From 9f667bff0f48d01a4474fa879cb384331584a4ee Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 3 Oct 2012 08:30:12 +0900 Subject: spi/s3c64xx: use clk_prepare_enable and clk_disable_unprepare Convert clk_enable/clk_disable to clk_prepare_enable/clk_disable_unprepare calls as required by common clock framework. Signed-off-by: Thomas Abraham Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 1a81c90a4a71..06a5fe777434 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -516,7 +516,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) /* Disable Clock */ if (sdd->port_conf->clk_from_cmu) { - clk_disable(sdd->src_clk); + clk_disable_unprepare(sdd->src_clk); } else { val = readl(regs + S3C64XX_SPI_CLK_CFG); val &= ~S3C64XX_SPI_ENCLK_ENABLE; @@ -564,7 +564,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) /* There is half-multiplier before the SPI */ clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); /* Enable Clock */ - clk_enable(sdd->src_clk); + clk_prepare_enable(sdd->src_clk); } else { /* Configure Clock */ val = readl(regs + S3C64XX_SPI_CLK_CFG); @@ -1302,7 +1302,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) goto err3; } - if (clk_enable(sdd->clk)) { + if (clk_prepare_enable(sdd->clk)) { dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n"); ret = -EBUSY; goto err4; @@ -1317,7 +1317,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) goto err5; } - if (clk_enable(sdd->src_clk)) { + if (clk_prepare_enable(sdd->src_clk)) { dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name); ret = -EBUSY; goto err6; @@ -1361,11 +1361,11 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) err8: free_irq(irq, sdd); err7: - clk_disable(sdd->src_clk); + clk_disable_unprepare(sdd->src_clk); err6: clk_put(sdd->src_clk); err5: - clk_disable(sdd->clk); + clk_disable_unprepare(sdd->clk); err4: clk_put(sdd->clk); err3: @@ -1393,10 +1393,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), sdd); - clk_disable(sdd->src_clk); + clk_disable_unprepare(sdd->src_clk); clk_put(sdd->src_clk); - clk_disable(sdd->clk); + clk_disable_unprepare(sdd->clk); clk_put(sdd->clk); if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node) @@ -1417,8 +1417,8 @@ static int s3c64xx_spi_suspend(struct device *dev) spi_master_suspend(master); /* Disable the clock */ - clk_disable(sdd->src_clk); - clk_disable(sdd->clk); + clk_disable_unprepare(sdd->src_clk); + clk_disable_unprepare(sdd->clk); if (!sdd->cntrlr_info->cfg_gpio && dev->of_node) s3c64xx_spi_dt_gpio_free(sdd); @@ -1440,8 +1440,8 @@ static int s3c64xx_spi_resume(struct device *dev) sci->cfg_gpio(); /* Enable the clock */ - clk_enable(sdd->src_clk); - clk_enable(sdd->clk); + clk_prepare_enable(sdd->src_clk); + clk_prepare_enable(sdd->clk); s3c64xx_spi_hwinit(sdd, sdd->port_id); @@ -1457,8 +1457,8 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - clk_disable(sdd->clk); - clk_disable(sdd->src_clk); + clk_disable_unprepare(sdd->clk); + clk_disable_unprepare(sdd->src_clk); return 0; } @@ -1468,8 +1468,8 @@ static int s3c64xx_spi_runtime_resume(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - clk_enable(sdd->src_clk); - clk_enable(sdd->clk); + clk_prepare_enable(sdd->src_clk); + clk_prepare_enable(sdd->clk); return 0; } -- cgit v1.2.3 From 61d15963176d44ba898913389b65588e717b7c59 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 3 Oct 2012 11:56:53 +0200 Subject: spi/bcm63xx: remove driver version As Grant Likely reported, this does not make any sense in a mainline kernel remove that driver version string. Reported-by: Grant Likely Signed-off-by: Florian Fainelli Signed-off-by: Mark Brown --- drivers/spi/spi-bcm63xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index a9f4049c6769..af191f560136 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -36,7 +36,6 @@ #include #define PFX KBUILD_MODNAME -#define DRV_VER "0.1.2" struct bcm63xx_spi { struct completion done; @@ -441,8 +440,8 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) goto out_clk_disable; } - dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n", - r->start, irq, bs->fifo_size, DRV_VER); + dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n", + r->start, irq, bs->fifo_size); return 0; -- cgit v1.2.3 From 965199573a9a50dae82ada92eb2b808b739ce949 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 3 Oct 2012 11:56:54 +0200 Subject: spi/bcm63xx: add missing spi_master_{resume,suspend} calls to PM callbacks The PM callbacks implemented by the spi-bcm63xx driver don't call spi_master_{resume,suspend}, fix that. Signed-off-by: Florian Fainelli Signed-off-by: Mark Brown --- drivers/spi/spi-bcm63xx.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index af191f560136..c7b569567f12 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -484,6 +484,8 @@ static int bcm63xx_spi_suspend(struct device *dev) platform_get_drvdata(to_platform_device(dev)); struct bcm63xx_spi *bs = spi_master_get_devdata(master); + spi_master_suspend(master); + clk_disable(bs->clk); return 0; @@ -497,6 +499,8 @@ static int bcm63xx_spi_resume(struct device *dev) clk_enable(bs->clk); + spi_master_resume(master); + return 0; } -- cgit v1.2.3 From b308fb208e23c3e765cefa45eb868c40c5b9c3e7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 3 Oct 2012 11:56:55 +0200 Subject: spi/bcm63xx: remove useless call to bcm63xx_spi_check_transfer() Calling bcm63xx_spi_check_transfer() with a NULL argument does not do anything useful that the core spi code is not doing already, remove this superfluous call. Signed-off-by: Florian Fainelli Signed-off-by: Mark Brown --- drivers/spi/spi-bcm63xx.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index c7b569567f12..6d97047d9242 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -169,13 +169,6 @@ static int bcm63xx_spi_setup(struct spi_device *spi) return -EINVAL; } - ret = bcm63xx_spi_check_transfer(spi, NULL); - if (ret < 0) { - dev_err(&spi->dev, "setup: unsupported mode bits %x\n", - spi->mode & ~MODEBITS); - return ret; - } - dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n", __func__, spi->mode & MODEBITS, spi->bits_per_word, 0); -- cgit v1.2.3 From 0384e90b853357d24935c65ba0e1bdd27faa6e58 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 7 Oct 2012 18:19:44 +0200 Subject: spi/mcspi: allow configuration of pin directions Allow D0 to be an input and D1 to be an output, configurable via platform data and a new DT property. Based on a patch from Matus Ujhelyi Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3542fdc664b1..51046332677c 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -130,6 +130,7 @@ struct omap2_mcspi { struct omap2_mcspi_dma *dma_channels; struct device *dev; struct omap2_mcspi_regs ctx; + unsigned int pin_dir:1; }; struct omap2_mcspi_cs { @@ -765,8 +766,15 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ - l &= ~(OMAP2_MCSPI_CHCONF_IS|OMAP2_MCSPI_CHCONF_DPE1); - l |= OMAP2_MCSPI_CHCONF_DPE0; + if (mcspi->pin_dir == MCSPI_PINDIR_D0_OUT_D1_IN) { + l &= ~OMAP2_MCSPI_CHCONF_IS; + l &= ~OMAP2_MCSPI_CHCONF_DPE1; + l |= OMAP2_MCSPI_CHCONF_DPE0; + } else { + l |= OMAP2_MCSPI_CHCONF_IS; + l |= OMAP2_MCSPI_CHCONF_DPE1; + l &= ~OMAP2_MCSPI_CHCONF_DPE0; + } /* wordlength */ l &= ~OMAP2_MCSPI_CHCONF_WL_MASK; @@ -1167,6 +1175,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; + dev_set_drvdata(&pdev->dev, master); + + mcspi = spi_master_get_devdata(master); + mcspi->master = master; + match = of_match_device(omap_mcspi_of_match, &pdev->dev); if (match) { u32 num_cs = 1; /* default number of chipselect */ @@ -1175,19 +1188,17 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) of_property_read_u32(node, "ti,spi-num-cs", &num_cs); master->num_chipselect = num_cs; master->bus_num = bus_num++; + if (of_get_property(node, "ti,pindir-d0-in-d1-out", NULL)) + mcspi->pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT; } else { pdata = pdev->dev.platform_data; master->num_chipselect = pdata->num_cs; if (pdev->id != -1) master->bus_num = pdev->id; + mcspi->pin_dir = pdata->pin_dir; } regs_offset = pdata->regs_offset; - dev_set_drvdata(&pdev->dev, master); - - mcspi = spi_master_get_devdata(master); - mcspi->master = master; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { status = -ENODEV; -- cgit v1.2.3 From 5015a2e1679f9f56b574f3aab7006517106da83c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 5 Oct 2012 17:51:11 +0200 Subject: spi/pl022: Revert "spi/pl022: fix spi-pl022 pm enable at probe" This reverts commit 6887237cd7da904184dab2750504040c68f3a080. Commit "spi/pl022: enable runtime PM" introduced runtime PM issues. Due to that commit, "spi/pl022: fix spi-pl022 pm enable at probe" was merged to fix part of those issues. Instead of adding another fix, let's clean up and revert everything back to when it was already fine. Signed-off-by: Ulf Hansson Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 919464102d33..5cf0643ae74c 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2186,6 +2186,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n", adev->res.start, pl022->virtbase); + pm_runtime_enable(dev); pm_runtime_resume(dev); pl022->clk = devm_clk_get(&adev->dev, NULL); -- cgit v1.2.3 From 0df349945752bf04cee52babf266bbf1f4812193 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 17 Oct 2012 14:27:08 +0200 Subject: spi/pl022: Minor simplification for runtime pm In probe pm_runtime_put_autosuspend has the same effect as doing pm_runtime_put. This due to upper layer in driver core is preventing the device from being runtime suspended by a pm_runtime_get*. Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 919464102d33..a9106c936edb 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2248,10 +2248,9 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_set_autosuspend_delay(dev, platform_info->autosuspend_delay); pm_runtime_use_autosuspend(dev); - pm_runtime_put_autosuspend(dev); - } else { - pm_runtime_put(dev); } + pm_runtime_put(dev); + return 0; err_spi_register: -- cgit v1.2.3 From 4964a26df72c4a82e56c6b40818648d54bde93cc Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 17 Oct 2012 14:27:16 +0200 Subject: spi/pl022: Activate resourses before deactivate them in suspend To be able to deactivate resourses in suspend, the resourses must first be surely active. This is done with a pm_runtime_get_sync. Once the resourses are restored to active state again in resume, the runtime pm usage count can be decreased with a pm_runtime_put. Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index a9106c936edb..c220c2346276 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2349,6 +2349,8 @@ static int pl022_suspend(struct device *dev) dev_warn(dev, "cannot suspend master\n"); return ret; } + + pm_runtime_get_sync(dev); pl022_suspend_resources(pl022); dev_dbg(dev, "suspended\n"); @@ -2361,6 +2363,7 @@ static int pl022_resume(struct device *dev) int ret; pl022_resume_resources(pl022); + pm_runtime_put(dev); /* Start the queue running */ ret = spi_master_resume(pl022->master); -- cgit v1.2.3 From d8f18420c552de6d38e5464074133e0e9730abcd Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 17 Oct 2012 14:27:24 +0200 Subject: spi/pl022: add IDLE state pin management This commit allow to put pins in IDLE state in runtime_suspend and in SLEEP state in suspend, corresponding to defined semantics in . To do this, just add a boolean parameter runtime to pl022_resume_resources/pl022_suspend_resources which indicates if it's called from PM_RUNTIME callbacks or not. Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index c220c2346276..7e3c4166e8f8 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -371,6 +371,7 @@ struct pl022 { /* Two optional pin states - default & sleep */ struct pinctrl *pinctrl; struct pinctrl_state *pins_default; + struct pinctrl_state *pins_idle; struct pinctrl_state *pins_sleep; struct spi_master *master; struct pl022_ssp_controller *master_info; @@ -2116,6 +2117,11 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) } else dev_err(dev, "could not get default pinstate\n"); + pl022->pins_idle = pinctrl_lookup_state(pl022->pinctrl, + PINCTRL_STATE_IDLE); + if (IS_ERR(pl022->pins_idle)) + dev_dbg(dev, "could not get idle pinstate\n"); + pl022->pins_sleep = pinctrl_lookup_state(pl022->pinctrl, PINCTRL_STATE_SLEEP); if (IS_ERR(pl022->pins_sleep)) @@ -2305,35 +2311,47 @@ pl022_remove(struct amba_device *adev) * the runtime counterparts to handle external resources like * clocks, pins and regulators when going to sleep. */ -static void pl022_suspend_resources(struct pl022 *pl022) +static void pl022_suspend_resources(struct pl022 *pl022, bool runtime) { int ret; + struct pinctrl_state *pins_state; clk_disable(pl022->clk); + pins_state = runtime ? pl022->pins_idle : pl022->pins_sleep; /* Optionally let pins go into sleep states */ - if (!IS_ERR(pl022->pins_sleep)) { - ret = pinctrl_select_state(pl022->pinctrl, - pl022->pins_sleep); + if (!IS_ERR(pins_state)) { + ret = pinctrl_select_state(pl022->pinctrl, pins_state); if (ret) - dev_err(&pl022->adev->dev, - "could not set pins to sleep state\n"); + dev_err(&pl022->adev->dev, "could not set %s pins\n", + runtime ? "idle" : "sleep"); } } -static void pl022_resume_resources(struct pl022 *pl022) +static void pl022_resume_resources(struct pl022 *pl022, bool runtime) { int ret; /* Optionaly enable pins to be muxed in and configured */ + /* First go to the default state */ if (!IS_ERR(pl022->pins_default)) { - ret = pinctrl_select_state(pl022->pinctrl, - pl022->pins_default); + ret = pinctrl_select_state(pl022->pinctrl, pl022->pins_default); if (ret) dev_err(&pl022->adev->dev, "could not set default pins\n"); } + if (!runtime) { + /* Then let's idle the pins until the next transfer happens */ + if (!IS_ERR(pl022->pins_idle)) { + ret = pinctrl_select_state(pl022->pinctrl, + pl022->pins_idle); + if (ret) + dev_err(&pl022->adev->dev, + "could not set idle pins\n"); + } + } + clk_enable(pl022->clk); } #endif @@ -2351,7 +2369,7 @@ static int pl022_suspend(struct device *dev) } pm_runtime_get_sync(dev); - pl022_suspend_resources(pl022); + pl022_suspend_resources(pl022, false); dev_dbg(dev, "suspended\n"); return 0; @@ -2362,7 +2380,7 @@ static int pl022_resume(struct device *dev) struct pl022 *pl022 = dev_get_drvdata(dev); int ret; - pl022_resume_resources(pl022); + pl022_resume_resources(pl022, false); pm_runtime_put(dev); /* Start the queue running */ @@ -2381,7 +2399,7 @@ static int pl022_runtime_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - pl022_suspend_resources(pl022); + pl022_suspend_resources(pl022, true); return 0; } @@ -2389,7 +2407,7 @@ static int pl022_runtime_resume(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - pl022_resume_resources(pl022); + pl022_resume_resources(pl022, true); return 0; } #endif -- cgit v1.2.3 From dc4dc36056392c0b0b1ca9e81bebff964b9297e0 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 30 Oct 2012 12:34:05 +0530 Subject: spi: tegra: add spi driver for SLINK controller Tegra20/Tegra30 supports the spi interface through its SLINK controller. Add spi driver for SLINK controller. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 2 +- drivers/spi/spi-tegra20-slink.c | 1359 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1366 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi-tegra20-slink.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1acae359cabe..25290d9780b2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -385,6 +385,12 @@ config SPI_MXS help SPI driver for Freescale MXS devices. +config SPI_TEGRA20_SLINK + tristate "Nvidia Tegra20/Tegra30 SLINK Controller" + depends on ARCH_TEGRA && TEGRA20_APB_DMA + help + SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. + config SPI_TI_SSP tristate "TI Sequencer Serial Port - SPI Support" depends on MFD_TI_SSP diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c48df47e4b0f..f87c0f142e5a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,10 +60,10 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o +obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o - diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c new file mode 100644 index 000000000000..b8985be81d96 --- /dev/null +++ b/drivers/spi/spi-tegra20-slink.c @@ -0,0 +1,1359 @@ +/* + * SPI driver for Nvidia's Tegra20/Tegra30 SLINK Controller. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SLINK_COMMAND 0x000 +#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) +#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) +#define SLINK_BOTH_EN (1 << 10) +#define SLINK_CS_SW (1 << 11) +#define SLINK_CS_VALUE (1 << 12) +#define SLINK_CS_POLARITY (1 << 13) +#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16) +#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16) +#define SLINK_IDLE_SDA_PULL_LOW (2 << 16) +#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16) +#define SLINK_IDLE_SDA_MASK (3 << 16) +#define SLINK_CS_POLARITY1 (1 << 20) +#define SLINK_CK_SDA (1 << 21) +#define SLINK_CS_POLARITY2 (1 << 22) +#define SLINK_CS_POLARITY3 (1 << 23) +#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24) +#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24) +#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24) +#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24) +#define SLINK_IDLE_SCLK_MASK (3 << 24) +#define SLINK_M_S (1 << 28) +#define SLINK_WAIT (1 << 29) +#define SLINK_GO (1 << 30) +#define SLINK_ENB (1 << 31) + +#define SLINK_MODES (SLINK_IDLE_SCLK_MASK | SLINK_CK_SDA) + +#define SLINK_COMMAND2 0x004 +#define SLINK_LSBFE (1 << 0) +#define SLINK_SSOE (1 << 1) +#define SLINK_SPIE (1 << 4) +#define SLINK_BIDIROE (1 << 6) +#define SLINK_MODFEN (1 << 7) +#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8) +#define SLINK_CS_ACTIVE_BETWEEN (1 << 17) +#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18) +#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20) +#define SLINK_FIFO_REFILLS_0 (0 << 22) +#define SLINK_FIFO_REFILLS_1 (1 << 22) +#define SLINK_FIFO_REFILLS_2 (2 << 22) +#define SLINK_FIFO_REFILLS_3 (3 << 22) +#define SLINK_FIFO_REFILLS_MASK (3 << 22) +#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26) +#define SLINK_SPC0 (1 << 29) +#define SLINK_TXEN (1 << 30) +#define SLINK_RXEN (1 << 31) + +#define SLINK_STATUS 0x008 +#define SLINK_COUNT(val) (((val) >> 0) & 0x1f) +#define SLINK_WORD(val) (((val) >> 5) & 0x1f) +#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff) +#define SLINK_MODF (1 << 16) +#define SLINK_RX_UNF (1 << 18) +#define SLINK_TX_OVF (1 << 19) +#define SLINK_TX_FULL (1 << 20) +#define SLINK_TX_EMPTY (1 << 21) +#define SLINK_RX_FULL (1 << 22) +#define SLINK_RX_EMPTY (1 << 23) +#define SLINK_TX_UNF (1 << 24) +#define SLINK_RX_OVF (1 << 25) +#define SLINK_TX_FLUSH (1 << 26) +#define SLINK_RX_FLUSH (1 << 27) +#define SLINK_SCLK (1 << 28) +#define SLINK_ERR (1 << 29) +#define SLINK_RDY (1 << 30) +#define SLINK_BSY (1 << 31) +#define SLINK_FIFO_ERROR (SLINK_TX_OVF | SLINK_RX_UNF | \ + SLINK_TX_UNF | SLINK_RX_OVF) + +#define SLINK_FIFO_EMPTY (SLINK_TX_EMPTY | SLINK_RX_EMPTY) + +#define SLINK_MAS_DATA 0x010 +#define SLINK_SLAVE_DATA 0x014 + +#define SLINK_DMA_CTL 0x018 +#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0) +#define SLINK_TX_TRIG_1 (0 << 16) +#define SLINK_TX_TRIG_4 (1 << 16) +#define SLINK_TX_TRIG_8 (2 << 16) +#define SLINK_TX_TRIG_16 (3 << 16) +#define SLINK_TX_TRIG_MASK (3 << 16) +#define SLINK_RX_TRIG_1 (0 << 18) +#define SLINK_RX_TRIG_4 (1 << 18) +#define SLINK_RX_TRIG_8 (2 << 18) +#define SLINK_RX_TRIG_16 (3 << 18) +#define SLINK_RX_TRIG_MASK (3 << 18) +#define SLINK_PACKED (1 << 20) +#define SLINK_PACK_SIZE_4 (0 << 21) +#define SLINK_PACK_SIZE_8 (1 << 21) +#define SLINK_PACK_SIZE_16 (2 << 21) +#define SLINK_PACK_SIZE_32 (3 << 21) +#define SLINK_PACK_SIZE_MASK (3 << 21) +#define SLINK_IE_TXC (1 << 26) +#define SLINK_IE_RXC (1 << 27) +#define SLINK_DMA_EN (1 << 31) + +#define SLINK_STATUS2 0x01c +#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0) +#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f0000) >> 16) +#define SLINK_SS_HOLD_TIME(val) (((val) & 0xF) << 6) + +#define SLINK_TX_FIFO 0x100 +#define SLINK_RX_FIFO 0x180 + +#define DATA_DIR_TX (1 << 0) +#define DATA_DIR_RX (1 << 1) + +#define SLINK_DMA_TIMEOUT (msecs_to_jiffies(1000)) + +#define DEFAULT_SPI_DMA_BUF_LEN (16*1024) +#define TX_FIFO_EMPTY_COUNT_MAX SLINK_TX_FIFO_EMPTY_COUNT(0x20) +#define RX_FIFO_FULL_COUNT_ZERO SLINK_RX_FIFO_FULL_COUNT(0) + +#define SLINK_STATUS2_RESET \ + (TX_FIFO_EMPTY_COUNT_MAX | RX_FIFO_FULL_COUNT_ZERO << 16) + +#define MAX_CHIP_SELECT 4 +#define SLINK_FIFO_DEPTH 32 + +struct tegra_slink_chip_data { + bool cs_hold_time; +}; + +struct tegra_slink_data { + struct device *dev; + struct spi_master *master; + const struct tegra_slink_chip_data *chip_data; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + phys_addr_t phys; + unsigned irq; + int dma_req_sel; + u32 spi_max_frequency; + u32 cur_speed; + + struct spi_device *cur_spi; + unsigned cur_pos; + unsigned cur_len; + unsigned words_per_32bit; + unsigned bytes_per_word; + unsigned curr_dma_words; + unsigned cur_direction; + + unsigned cur_rx_pos; + unsigned cur_tx_pos; + + unsigned dma_buf_size; + unsigned max_buf_size; + bool is_curr_dma_xfer; + bool is_hw_based_cs; + + struct completion rx_dma_complete; + struct completion tx_dma_complete; + + u32 tx_status; + u32 rx_status; + u32 status_reg; + bool is_packed; + unsigned long packed_size; + + u32 command_reg; + u32 command2_reg; + u32 dma_control_reg; + u32 def_command_reg; + u32 def_command2_reg; + + struct completion xfer_completion; + struct spi_transfer *curr_xfer; + struct dma_chan *rx_dma_chan; + u32 *rx_dma_buf; + dma_addr_t rx_dma_phys; + struct dma_async_tx_descriptor *rx_dma_desc; + + struct dma_chan *tx_dma_chan; + u32 *tx_dma_buf; + dma_addr_t tx_dma_phys; + struct dma_async_tx_descriptor *tx_dma_desc; +}; + +static int tegra_slink_runtime_suspend(struct device *dev); +static int tegra_slink_runtime_resume(struct device *dev); + +static inline unsigned long tegra_slink_readl(struct tegra_slink_data *tspi, + unsigned long reg) +{ + return readl(tspi->base + reg); +} + +static inline void tegra_slink_writel(struct tegra_slink_data *tspi, + unsigned long val, unsigned long reg) +{ + writel(val, tspi->base + reg); + + /* Read back register to make sure that register writes completed */ + if (reg != SLINK_TX_FIFO) + readl(tspi->base + SLINK_MAS_DATA); +} + +static void tegra_slink_clear_status(struct tegra_slink_data *tspi) +{ + unsigned long val; + unsigned long val_write = 0; + + val = tegra_slink_readl(tspi, SLINK_STATUS); + + /* Write 1 to clear status register */ + val_write = SLINK_RDY | SLINK_FIFO_ERROR; + tegra_slink_writel(tspi, val_write, SLINK_STATUS); +} + +static unsigned long tegra_slink_get_packed_size(struct tegra_slink_data *tspi, + struct spi_transfer *t) +{ + unsigned long val; + + switch (tspi->bytes_per_word) { + case 0: + val = SLINK_PACK_SIZE_4; + break; + case 1: + val = SLINK_PACK_SIZE_8; + break; + case 2: + val = SLINK_PACK_SIZE_16; + break; + case 4: + val = SLINK_PACK_SIZE_32; + break; + default: + val = 0; + } + return val; +} + +static unsigned tegra_slink_calculate_curr_xfer_param( + struct spi_device *spi, struct tegra_slink_data *tspi, + struct spi_transfer *t) +{ + unsigned remain_len = t->len - tspi->cur_pos; + unsigned max_word; + unsigned bits_per_word ; + unsigned max_len; + unsigned total_fifo_words; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + tspi->bytes_per_word = (bits_per_word - 1) / 8 + 1; + + if (bits_per_word == 8 || bits_per_word == 16) { + tspi->is_packed = 1; + tspi->words_per_32bit = 32/bits_per_word; + } else { + tspi->is_packed = 0; + tspi->words_per_32bit = 1; + } + tspi->packed_size = tegra_slink_get_packed_size(tspi, t); + + if (tspi->is_packed) { + max_len = min(remain_len, tspi->max_buf_size); + tspi->curr_dma_words = max_len/tspi->bytes_per_word; + total_fifo_words = max_len/4; + } else { + max_word = (remain_len - 1) / tspi->bytes_per_word + 1; + max_word = min(max_word, tspi->max_buf_size/4); + tspi->curr_dma_words = max_word; + total_fifo_words = max_word; + } + return total_fifo_words; +} + +static unsigned tegra_slink_fill_tx_fifo_from_client_txbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned nbytes; + unsigned tx_empty_count; + unsigned long fifo_status; + unsigned max_n_32bit; + unsigned i, count; + unsigned long x; + unsigned int written_words; + unsigned fifo_words_left; + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; + + fifo_status = tegra_slink_readl(tspi, SLINK_STATUS2); + tx_empty_count = SLINK_TX_FIFO_EMPTY_COUNT(fifo_status); + + if (tspi->is_packed) { + fifo_words_left = tx_empty_count * tspi->words_per_32bit; + written_words = min(fifo_words_left, tspi->curr_dma_words); + nbytes = written_words * tspi->bytes_per_word; + max_n_32bit = DIV_ROUND_UP(nbytes, 4); + for (count = 0; count < max_n_32bit; count++) { + x = 0; + for (i = 0; (i < 4) && nbytes; i++, nbytes--) + x |= (*tx_buf++) << (i*8); + tegra_slink_writel(tspi, x, SLINK_TX_FIFO); + } + } else { + max_n_32bit = min(tspi->curr_dma_words, tx_empty_count); + written_words = max_n_32bit; + nbytes = written_words * tspi->bytes_per_word; + for (count = 0; count < max_n_32bit; count++) { + x = 0; + for (i = 0; nbytes && (i < tspi->bytes_per_word); + i++, nbytes--) + x |= ((*tx_buf++) << i*8); + tegra_slink_writel(tspi, x, SLINK_TX_FIFO); + } + } + tspi->cur_tx_pos += written_words * tspi->bytes_per_word; + return written_words; +} + +static unsigned int tegra_slink_read_rx_fifo_to_client_rxbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned rx_full_count; + unsigned long fifo_status; + unsigned i, count; + unsigned long x; + unsigned int read_words = 0; + unsigned len; + u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_rx_pos; + + fifo_status = tegra_slink_readl(tspi, SLINK_STATUS2); + rx_full_count = SLINK_RX_FIFO_FULL_COUNT(fifo_status); + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + for (count = 0; count < rx_full_count; count++) { + x = tegra_slink_readl(tspi, SLINK_RX_FIFO); + for (i = 0; len && (i < 4); i++, len--) + *rx_buf++ = (x >> i*8) & 0xFF; + } + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + read_words += tspi->curr_dma_words; + } else { + unsigned int bits_per_word; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + tspi->cur_spi->bits_per_word; + for (count = 0; count < rx_full_count; count++) { + x = tegra_slink_readl(tspi, SLINK_RX_FIFO); + for (i = 0; (i < tspi->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + } + tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word; + read_words += rx_full_count; + } + return read_words; +} + +static void tegra_slink_copy_client_txbuf_to_spi_txbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned len; + + /* Make the dma buffer to read by cpu */ + dma_sync_single_for_cpu(tspi->dev, tspi->tx_dma_phys, + tspi->dma_buf_size, DMA_TO_DEVICE); + + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len); + } else { + unsigned int i; + unsigned int count; + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; + unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word; + unsigned int x; + + for (count = 0; count < tspi->curr_dma_words; count++) { + x = 0; + for (i = 0; consume && (i < tspi->bytes_per_word); + i++, consume--) + x |= ((*tx_buf++) << i * 8); + tspi->tx_dma_buf[count] = x; + } + } + tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->tx_dma_phys, + tspi->dma_buf_size, DMA_TO_DEVICE); +} + +static void tegra_slink_copy_spi_rxbuf_to_client_rxbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned len; + + /* Make the dma buffer to read by cpu */ + dma_sync_single_for_cpu(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); + + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len); + } else { + unsigned int i; + unsigned int count; + unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos; + unsigned int x; + unsigned int rx_mask, bits_per_word; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + tspi->cur_spi->bits_per_word; + rx_mask = (1 << bits_per_word) - 1; + for (count = 0; count < tspi->curr_dma_words; count++) { + x = tspi->rx_dma_buf[count]; + x &= rx_mask; + for (i = 0; (i < tspi->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + } + } + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); +} + +static void tegra_slink_dma_complete(void *args) +{ + struct completion *dma_complete = args; + + complete(dma_complete); +} + +static int tegra_slink_start_tx_dma(struct tegra_slink_data *tspi, int len) +{ + INIT_COMPLETION(tspi->tx_dma_complete); + tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan, + tspi->tx_dma_phys, len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tspi->tx_dma_desc) { + dev_err(tspi->dev, "Not able to get desc for Tx\n"); + return -EIO; + } + + tspi->tx_dma_desc->callback = tegra_slink_dma_complete; + tspi->tx_dma_desc->callback_param = &tspi->tx_dma_complete; + + dmaengine_submit(tspi->tx_dma_desc); + dma_async_issue_pending(tspi->tx_dma_chan); + return 0; +} + +static int tegra_slink_start_rx_dma(struct tegra_slink_data *tspi, int len) +{ + INIT_COMPLETION(tspi->rx_dma_complete); + tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan, + tspi->rx_dma_phys, len, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tspi->rx_dma_desc) { + dev_err(tspi->dev, "Not able to get desc for Rx\n"); + return -EIO; + } + + tspi->rx_dma_desc->callback = tegra_slink_dma_complete; + tspi->rx_dma_desc->callback_param = &tspi->rx_dma_complete; + + dmaengine_submit(tspi->rx_dma_desc); + dma_async_issue_pending(tspi->rx_dma_chan); + return 0; +} + +static int tegra_slink_start_dma_based_transfer( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned long val; + unsigned long test_val; + unsigned int len; + int ret = 0; + unsigned long status; + + /* Make sure that Rx and Tx fifo are empty */ + status = tegra_slink_readl(tspi, SLINK_STATUS); + if ((status & SLINK_FIFO_EMPTY) != SLINK_FIFO_EMPTY) { + dev_err(tspi->dev, + "Rx/Tx fifo are not empty status 0x%08lx\n", status); + return -EIO; + } + + val = SLINK_DMA_BLOCK_SIZE(tspi->curr_dma_words - 1); + val |= tspi->packed_size; + if (tspi->is_packed) + len = DIV_ROUND_UP(tspi->curr_dma_words * tspi->bytes_per_word, + 4) * 4; + else + len = tspi->curr_dma_words * 4; + + /* Set attention level based on length of transfer */ + if (len & 0xF) + val |= SLINK_TX_TRIG_1 | SLINK_RX_TRIG_1; + else if (((len) >> 4) & 0x1) + val |= SLINK_TX_TRIG_4 | SLINK_RX_TRIG_4; + else + val |= SLINK_TX_TRIG_8 | SLINK_RX_TRIG_8; + + if (tspi->cur_direction & DATA_DIR_TX) + val |= SLINK_IE_TXC; + + if (tspi->cur_direction & DATA_DIR_RX) + val |= SLINK_IE_RXC; + + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + if (tspi->cur_direction & DATA_DIR_TX) { + tegra_slink_copy_client_txbuf_to_spi_txbuf(tspi, t); + wmb(); + ret = tegra_slink_start_tx_dma(tspi, len); + if (ret < 0) { + dev_err(tspi->dev, + "Starting tx dma failed, err %d\n", ret); + return ret; + } + + /* Wait for tx fifo to be fill before starting slink */ + test_val = tegra_slink_readl(tspi, SLINK_STATUS); + while (!(test_val & SLINK_TX_FULL)) + test_val = tegra_slink_readl(tspi, SLINK_STATUS); + } + + if (tspi->cur_direction & DATA_DIR_RX) { + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); + + ret = tegra_slink_start_rx_dma(tspi, len); + if (ret < 0) { + dev_err(tspi->dev, + "Starting rx dma failed, err %d\n", ret); + if (tspi->cur_direction & DATA_DIR_TX) + dmaengine_terminate_all(tspi->tx_dma_chan); + return ret; + } + } + tspi->is_curr_dma_xfer = true; + if (tspi->is_packed) { + val |= SLINK_PACKED; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + /* HW need small delay after settign Packed mode */ + udelay(1); + } + tspi->dma_control_reg = val; + + val |= SLINK_DMA_EN; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + return ret; +} + +static int tegra_slink_start_cpu_based_transfer( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned long val; + unsigned cur_words; + + val = tspi->packed_size; + if (tspi->cur_direction & DATA_DIR_TX) + val |= SLINK_IE_TXC; + + if (tspi->cur_direction & DATA_DIR_RX) + val |= SLINK_IE_RXC; + + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + if (tspi->cur_direction & DATA_DIR_TX) + cur_words = tegra_slink_fill_tx_fifo_from_client_txbuf(tspi, t); + else + cur_words = tspi->curr_dma_words; + val |= SLINK_DMA_BLOCK_SIZE(cur_words - 1); + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + tspi->is_curr_dma_xfer = false; + if (tspi->is_packed) { + val |= SLINK_PACKED; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + udelay(1); + wmb(); + } + tspi->dma_control_reg = val; + val |= SLINK_DMA_EN; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + return 0; +} + +static int tegra_slink_init_dma_param(struct tegra_slink_data *tspi, + bool dma_to_memory) +{ + struct dma_chan *dma_chan; + u32 *dma_buf; + dma_addr_t dma_phys; + int ret; + struct dma_slave_config dma_sconfig; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_chan = dma_request_channel(mask, NULL, NULL); + if (!dma_chan) { + dev_err(tspi->dev, + "Dma channel is not available, will try later\n"); + return -EPROBE_DEFER; + } + + dma_buf = dma_alloc_coherent(tspi->dev, tspi->dma_buf_size, + &dma_phys, GFP_KERNEL); + if (!dma_buf) { + dev_err(tspi->dev, " Not able to allocate the dma buffer\n"); + dma_release_channel(dma_chan); + return -ENOMEM; + } + + dma_sconfig.slave_id = tspi->dma_req_sel; + if (dma_to_memory) { + dma_sconfig.src_addr = tspi->phys + SLINK_RX_FIFO; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.src_maxburst = 0; + } else { + dma_sconfig.dst_addr = tspi->phys + SLINK_TX_FIFO; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.dst_maxburst = 0; + } + + ret = dmaengine_slave_config(dma_chan, &dma_sconfig); + if (ret) + goto scrub; + if (dma_to_memory) { + tspi->rx_dma_chan = dma_chan; + tspi->rx_dma_buf = dma_buf; + tspi->rx_dma_phys = dma_phys; + } else { + tspi->tx_dma_chan = dma_chan; + tspi->tx_dma_buf = dma_buf; + tspi->tx_dma_phys = dma_phys; + } + return 0; + +scrub: + dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); + dma_release_channel(dma_chan); + return ret; +} + +static void tegra_slink_deinit_dma_param(struct tegra_slink_data *tspi, + bool dma_to_memory) +{ + u32 *dma_buf; + dma_addr_t dma_phys; + struct dma_chan *dma_chan; + + if (dma_to_memory) { + dma_buf = tspi->rx_dma_buf; + dma_chan = tspi->rx_dma_chan; + dma_phys = tspi->rx_dma_phys; + tspi->rx_dma_chan = NULL; + tspi->rx_dma_buf = NULL; + } else { + dma_buf = tspi->tx_dma_buf; + dma_chan = tspi->tx_dma_chan; + dma_phys = tspi->tx_dma_phys; + tspi->tx_dma_buf = NULL; + tspi->tx_dma_chan = NULL; + } + if (!dma_chan) + return; + + dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); + dma_release_channel(dma_chan); +} + +static int tegra_slink_start_transfer_one(struct spi_device *spi, + struct spi_transfer *t, bool is_first_of_msg, + bool is_single_xfer) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master); + u32 speed; + u8 bits_per_word; + unsigned total_fifo_words; + int ret; + struct tegra_spi_device_controller_data *cdata = spi->controller_data; + unsigned long command; + unsigned long command2; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + if (!speed) + speed = tspi->spi_max_frequency; + if (speed != tspi->cur_speed) { + clk_set_rate(tspi->clk, speed * 4); + tspi->cur_speed = speed; + } + + tspi->cur_spi = spi; + tspi->cur_pos = 0; + tspi->cur_rx_pos = 0; + tspi->cur_tx_pos = 0; + tspi->curr_xfer = t; + total_fifo_words = tegra_slink_calculate_curr_xfer_param(spi, tspi, t); + + if (is_first_of_msg) { + tegra_slink_clear_status(tspi); + + command = tspi->def_command_reg; + command |= SLINK_BIT_LENGTH(bits_per_word - 1); + + command2 = tspi->def_command2_reg; + command2 |= SLINK_SS_EN_CS(spi->chip_select); + + /* possibly use the hw based chip select */ + tspi->is_hw_based_cs = false; + if (cdata && cdata->is_hw_based_cs && is_single_xfer && + ((tspi->curr_dma_words * tspi->bytes_per_word) == + (t->len - tspi->cur_pos))) { + int setup_count; + int sts2; + + setup_count = cdata->cs_setup_clk_count >> 1; + setup_count = max(setup_count, 3); + command2 |= SLINK_SS_SETUP(setup_count); + if (tspi->chip_data->cs_hold_time) { + int hold_count; + + hold_count = cdata->cs_hold_clk_count; + hold_count = max(hold_count, 0xF); + sts2 = tegra_slink_readl(tspi, SLINK_STATUS2); + sts2 &= ~SLINK_SS_HOLD_TIME(0xF); + sts2 |= SLINK_SS_HOLD_TIME(hold_count); + tegra_slink_writel(tspi, sts2, SLINK_STATUS2); + } + tspi->is_hw_based_cs = true; + } + + if (tspi->is_hw_based_cs) + command &= ~SLINK_CS_SW; + else + command |= SLINK_CS_SW | SLINK_CS_VALUE; + + command &= ~SLINK_MODES; + if (spi->mode & SPI_CPHA) + command |= SLINK_CK_SDA; + + if (spi->mode & SPI_CPOL) + command |= SLINK_IDLE_SCLK_DRIVE_HIGH; + else + command |= SLINK_IDLE_SCLK_DRIVE_LOW; + } else { + command = tspi->command_reg; + command &= ~SLINK_BIT_LENGTH(~0); + command |= SLINK_BIT_LENGTH(bits_per_word - 1); + + command2 = tspi->command2_reg; + command2 &= ~(SLINK_RXEN | SLINK_TXEN); + } + + tegra_slink_writel(tspi, command, SLINK_COMMAND); + tspi->command_reg = command; + + tspi->cur_direction = 0; + if (t->rx_buf) { + command2 |= SLINK_RXEN; + tspi->cur_direction |= DATA_DIR_RX; + } + if (t->tx_buf) { + command2 |= SLINK_TXEN; + tspi->cur_direction |= DATA_DIR_TX; + } + tegra_slink_writel(tspi, command2, SLINK_COMMAND2); + tspi->command2_reg = command2; + + if (total_fifo_words > SLINK_FIFO_DEPTH) + ret = tegra_slink_start_dma_based_transfer(tspi, t); + else + ret = tegra_slink_start_cpu_based_transfer(tspi, t); + return ret; +} + +static int tegra_slink_setup(struct spi_device *spi) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master); + unsigned long val; + unsigned long flags; + int ret; + unsigned int cs_pol_bit[MAX_CHIP_SELECT] = { + SLINK_CS_POLARITY, + SLINK_CS_POLARITY1, + SLINK_CS_POLARITY2, + SLINK_CS_POLARITY3, + }; + + dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", + spi->bits_per_word, + spi->mode & SPI_CPOL ? "" : "~", + spi->mode & SPI_CPHA ? "" : "~", + spi->max_speed_hz); + + BUG_ON(spi->chip_select >= MAX_CHIP_SELECT); + + ret = pm_runtime_get_sync(tspi->dev); + if (ret < 0) { + dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + + spin_lock_irqsave(&tspi->lock, flags); + val = tspi->def_command_reg; + if (spi->mode & SPI_CS_HIGH) + val |= cs_pol_bit[spi->chip_select]; + else + val &= ~cs_pol_bit[spi->chip_select]; + tspi->def_command_reg = val; + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + spin_unlock_irqrestore(&tspi->lock, flags); + + pm_runtime_put(tspi->dev); + return 0; +} + +static int tegra_slink_prepare_transfer(struct spi_master *master) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + return pm_runtime_get_sync(tspi->dev); +} + +static int tegra_slink_unprepare_transfer(struct spi_master *master) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + pm_runtime_put(tspi->dev); + return 0; +} + +static int tegra_slink_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + bool is_first_msg = true; + int single_xfer; + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + struct spi_transfer *xfer; + struct spi_device *spi = msg->spi; + int ret; + + msg->status = 0; + msg->actual_length = 0; + single_xfer = list_is_singular(&msg->transfers); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + INIT_COMPLETION(tspi->xfer_completion); + ret = tegra_slink_start_transfer_one(spi, xfer, + is_first_msg, single_xfer); + if (ret < 0) { + dev_err(tspi->dev, + "spi can not start transfer, err %d\n", ret); + goto exit; + } + is_first_msg = false; + ret = wait_for_completion_timeout(&tspi->xfer_completion, + SLINK_DMA_TIMEOUT); + if (WARN_ON(ret == 0)) { + dev_err(tspi->dev, + "spi trasfer timeout, err %d\n", ret); + ret = -EIO; + goto exit; + } + + if (tspi->tx_status || tspi->rx_status) { + dev_err(tspi->dev, "Error in Transfer\n"); + ret = -EIO; + goto exit; + } + msg->actual_length += xfer->len; + if (xfer->cs_change && xfer->delay_usecs) { + tegra_slink_writel(tspi, tspi->def_command_reg, + SLINK_COMMAND); + udelay(xfer->delay_usecs); + } + } + ret = 0; +exit: + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); + msg->status = ret; + spi_finalize_current_message(master); + return ret; +} + +static irqreturn_t handle_cpu_based_xfer(struct tegra_slink_data *tspi) +{ + struct spi_transfer *t = tspi->curr_xfer; + unsigned long flags; + + spin_lock_irqsave(&tspi->lock, flags); + if (tspi->tx_status || tspi->rx_status || + (tspi->status_reg & SLINK_BSY)) { + dev_err(tspi->dev, + "CpuXfer ERROR bit set 0x%x\n", tspi->status_reg); + dev_err(tspi->dev, + "CpuXfer 0x%08x:0x%08x:0x%08x\n", tspi->command_reg, + tspi->command2_reg, tspi->dma_control_reg); + tegra_periph_reset_assert(tspi->clk); + udelay(2); + tegra_periph_reset_deassert(tspi->clk); + complete(&tspi->xfer_completion); + goto exit; + } + + if (tspi->cur_direction & DATA_DIR_RX) + tegra_slink_read_rx_fifo_to_client_rxbuf(tspi, t); + + if (tspi->cur_direction & DATA_DIR_TX) + tspi->cur_pos = tspi->cur_tx_pos; + else + tspi->cur_pos = tspi->cur_rx_pos; + + if (tspi->cur_pos == t->len) { + complete(&tspi->xfer_completion); + goto exit; + } + + tegra_slink_calculate_curr_xfer_param(tspi->cur_spi, tspi, t); + tegra_slink_start_cpu_based_transfer(tspi, t); +exit: + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t handle_dma_based_xfer(struct tegra_slink_data *tspi) +{ + struct spi_transfer *t = tspi->curr_xfer; + long wait_status; + int err = 0; + unsigned total_fifo_words; + unsigned long flags; + + /* Abort dmas if any error */ + if (tspi->cur_direction & DATA_DIR_TX) { + if (tspi->tx_status) { + dmaengine_terminate_all(tspi->tx_dma_chan); + err += 1; + } else { + wait_status = wait_for_completion_interruptible_timeout( + &tspi->tx_dma_complete, SLINK_DMA_TIMEOUT); + if (wait_status <= 0) { + dmaengine_terminate_all(tspi->tx_dma_chan); + dev_err(tspi->dev, "TxDma Xfer failed\n"); + err += 1; + } + } + } + + if (tspi->cur_direction & DATA_DIR_RX) { + if (tspi->rx_status) { + dmaengine_terminate_all(tspi->rx_dma_chan); + err += 2; + } else { + wait_status = wait_for_completion_interruptible_timeout( + &tspi->rx_dma_complete, SLINK_DMA_TIMEOUT); + if (wait_status <= 0) { + dmaengine_terminate_all(tspi->rx_dma_chan); + dev_err(tspi->dev, "RxDma Xfer failed\n"); + err += 2; + } + } + } + + spin_lock_irqsave(&tspi->lock, flags); + if (err) { + dev_err(tspi->dev, + "DmaXfer: ERROR bit set 0x%x\n", tspi->status_reg); + dev_err(tspi->dev, + "DmaXfer 0x%08x:0x%08x:0x%08x\n", tspi->command_reg, + tspi->command2_reg, tspi->dma_control_reg); + tegra_periph_reset_assert(tspi->clk); + udelay(2); + tegra_periph_reset_deassert(tspi->clk); + complete(&tspi->xfer_completion); + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; + } + + if (tspi->cur_direction & DATA_DIR_RX) + tegra_slink_copy_spi_rxbuf_to_client_rxbuf(tspi, t); + + if (tspi->cur_direction & DATA_DIR_TX) + tspi->cur_pos = tspi->cur_tx_pos; + else + tspi->cur_pos = tspi->cur_rx_pos; + + if (tspi->cur_pos == t->len) { + complete(&tspi->xfer_completion); + goto exit; + } + + /* Continue transfer in current message */ + total_fifo_words = tegra_slink_calculate_curr_xfer_param(tspi->cur_spi, + tspi, t); + if (total_fifo_words > SLINK_FIFO_DEPTH) + err = tegra_slink_start_dma_based_transfer(tspi, t); + else + err = tegra_slink_start_cpu_based_transfer(tspi, t); + +exit: + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t tegra_slink_isr_thread(int irq, void *context_data) +{ + struct tegra_slink_data *tspi = context_data; + + if (!tspi->is_curr_dma_xfer) + return handle_cpu_based_xfer(tspi); + return handle_dma_based_xfer(tspi); +} + +static irqreturn_t tegra_slink_isr(int irq, void *context_data) +{ + struct tegra_slink_data *tspi = context_data; + + tspi->status_reg = tegra_slink_readl(tspi, SLINK_STATUS); + if (tspi->cur_direction & DATA_DIR_TX) + tspi->tx_status = tspi->status_reg & + (SLINK_TX_OVF | SLINK_TX_UNF); + + if (tspi->cur_direction & DATA_DIR_RX) + tspi->rx_status = tspi->status_reg & + (SLINK_RX_OVF | SLINK_RX_UNF); + tegra_slink_clear_status(tspi); + + return IRQ_WAKE_THREAD; +} + +static struct tegra_spi_platform_data *tegra_slink_parse_dt( + struct platform_device *pdev) +{ + struct tegra_spi_platform_data *pdata; + const unsigned int *prop; + struct device_node *np = pdev->dev.of_node; + u32 of_dma[2]; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Memory alloc for pdata failed\n"); + return NULL; + } + + if (of_property_read_u32_array(np, "nvidia,dma-request-selector", + of_dma, 2) >= 0) + pdata->dma_req_sel = of_dma[1]; + + prop = of_get_property(np, "spi-max-frequency", NULL); + if (prop) + pdata->spi_max_frequency = be32_to_cpup(prop); + + return pdata; +} + +const struct tegra_slink_chip_data tegra30_spi_cdata = { + .cs_hold_time = true, +}; + +const struct tegra_slink_chip_data tegra20_spi_cdata = { + .cs_hold_time = false, +}; + +static struct of_device_id tegra_slink_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, }, + { .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra_slink_of_match); + +static int __devinit tegra_slink_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct tegra_slink_data *tspi; + struct resource *r; + struct tegra_spi_platform_data *pdata = pdev->dev.platform_data; + int ret, spi_irq; + const struct tegra_slink_chip_data *cdata = NULL; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(tegra_slink_of_match), &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + cdata = match->data; + if (!pdata && pdev->dev.of_node) + pdata = tegra_slink_parse_dt(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting\n"); + return -ENODEV; + } + + if (!pdata->spi_max_frequency) + pdata->spi_max_frequency = 25000000; /* 25MHz */ + + master = spi_alloc_master(&pdev->dev, sizeof(*tspi)); + if (!master) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->setup = tegra_slink_setup; + master->prepare_transfer_hardware = tegra_slink_prepare_transfer; + master->transfer_one_message = tegra_slink_transfer_one_message; + master->unprepare_transfer_hardware = tegra_slink_unprepare_transfer; + master->num_chipselect = MAX_CHIP_SELECT; + master->bus_num = -1; + + dev_set_drvdata(&pdev->dev, master); + tspi = spi_master_get_devdata(master); + tspi->master = master; + tspi->dma_req_sel = pdata->dma_req_sel; + tspi->dev = &pdev->dev; + tspi->chip_data = cdata; + spin_lock_init(&tspi->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "No IO memory resource\n"); + ret = -ENODEV; + goto exit_free_master; + } + tspi->phys = r->start; + tspi->base = devm_request_and_ioremap(&pdev->dev, r); + if (!tspi->base) { + dev_err(&pdev->dev, + "Cannot request memregion/iomap dma address\n"); + ret = -EADDRNOTAVAIL; + goto exit_free_master; + } + + spi_irq = platform_get_irq(pdev, 0); + tspi->irq = spi_irq; + ret = request_threaded_irq(tspi->irq, tegra_slink_isr, + tegra_slink_isr_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), tspi); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + tspi->irq); + goto exit_free_master; + } + + tspi->clk = devm_clk_get(&pdev->dev, "slink"); + if (IS_ERR(tspi->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tspi->clk); + goto exit_free_irq; + } + + tspi->max_buf_size = SLINK_FIFO_DEPTH << 2; + tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN; + tspi->spi_max_frequency = pdata->spi_max_frequency; + + if (pdata->dma_req_sel) { + ret = tegra_slink_init_dma_param(tspi, true); + if (ret < 0) { + dev_err(&pdev->dev, "RxDma Init failed, err %d\n", ret); + goto exit_free_irq; + } + + ret = tegra_slink_init_dma_param(tspi, false); + if (ret < 0) { + dev_err(&pdev->dev, "TxDma Init failed, err %d\n", ret); + goto exit_rx_dma_free; + } + tspi->max_buf_size = tspi->dma_buf_size; + init_completion(&tspi->tx_dma_complete); + init_completion(&tspi->rx_dma_complete); + } + + init_completion(&tspi->xfer_completion); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_slink_runtime_resume(&pdev->dev); + if (ret) + goto exit_pm_disable; + } + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + goto exit_pm_disable; + } + tspi->def_command_reg = SLINK_M_S; + tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN; + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); + pm_runtime_put(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; + ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "can not register to master err %d\n", ret); + goto exit_pm_disable; + } + return ret; + +exit_pm_disable: + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_slink_runtime_suspend(&pdev->dev); + tegra_slink_deinit_dma_param(tspi, false); +exit_rx_dma_free: + tegra_slink_deinit_dma_param(tspi, true); +exit_free_irq: + free_irq(spi_irq, tspi); +exit_free_master: + spi_master_put(master); + return ret; +} + +static int __devexit tegra_slink_remove(struct platform_device *pdev) +{ + struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + free_irq(tspi->irq, tspi); + spi_unregister_master(master); + + if (tspi->tx_dma_chan) + tegra_slink_deinit_dma_param(tspi, false); + + if (tspi->rx_dma_chan) + tegra_slink_deinit_dma_param(tspi, true); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_slink_runtime_suspend(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_slink_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + + return spi_master_suspend(master); +} + +static int tegra_slink_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + tegra_slink_writel(tspi, tspi->command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->command2_reg, SLINK_COMMAND2); + pm_runtime_put(dev); + + return spi_master_resume(master); +} +#endif + +static int tegra_slink_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + /* Flush all write which are in PPSB queue by reading back */ + tegra_slink_readl(tspi, SLINK_MAS_DATA); + + clk_disable_unprepare(tspi->clk); + return 0; +} + +static int tegra_slink_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(tspi->clk); + if (ret < 0) { + dev_err(tspi->dev, "clk_prepare failed: %d\n", ret); + return ret; + } + return 0; +} + +static const struct dev_pm_ops slink_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_slink_runtime_suspend, + tegra_slink_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_slink_suspend, tegra_slink_resume) +}; +static struct platform_driver tegra_slink_driver = { + .driver = { + .name = "spi-tegra-slink", + .owner = THIS_MODULE, + .pm = &slink_pm_ops, + .of_match_table = of_match_ptr(tegra_slink_of_match), + }, + .probe = tegra_slink_probe, + .remove = __devexit_p(tegra_slink_remove), +}; +module_platform_driver(tegra_slink_driver); + +MODULE_ALIAS("platform:spi-tegra-slink"); +MODULE_DESCRIPTION("NVIDIA Tegra20/Tegra30 SLINK Controller Driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 880cfd43ea50b3c2d880d94935f07c77bf7c7116 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 31 Oct 2012 11:30:08 +0100 Subject: spi: spidev: Add device tree bindings This will allow to probe spidev from device tree by adding the compatible string of each device to this array. Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 830adbed1d7a..9fc5788f2095 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -642,10 +644,17 @@ static int __devexit spidev_remove(struct spi_device *spi) return 0; } +static const struct of_device_id spidev_dt_ids[] = { + {}, +}; + +MODULE_DEVICE_TABLE(of, spidev_dt_ids); + static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spidev_dt_ids), }, .probe = spidev_probe, .remove = __devexit_p(spidev_remove), -- cgit v1.2.3 From 8fad805bdc5229bf9787b01ca07061bb5967c2d9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 31 Oct 2012 11:30:09 +0100 Subject: spi: spidev: Add Rohm DH2228FV DAC compatible string Since we don't have a driver for it yet, use spidev instead. Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 9fc5788f2095..e44abc96cd05 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -645,6 +645,7 @@ static int __devexit spidev_remove(struct spi_device *spi) } static const struct of_device_id spidev_dt_ids[] = { + { .compatible = "rohm,dh2228fv" }, {}, }; -- cgit v1.2.3 From e47a682ace0cd56eb8e09b806c2b0f034b491520 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Tue, 6 Nov 2012 14:30:19 +0530 Subject: spi: omap2-mcspi: Reorder the wait_for_completion for tx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit d7b4394e[Cleanup the omap2_mcspi_txrx_dma function] changed the wait_for_completion order. Move the wait so that the rx doesnot wait for the tx to complete. Reported-and-tested-by: Sørensen, Stefan Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3542fdc664b1..b1a651ef9579 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -323,18 +323,13 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; unsigned int count; - u8 * rx; const u8 * tx; - void __iomem *chstat_reg; - struct omap2_mcspi_cs *cs = spi->controller_state; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; count = xfer->len; - rx = xfer->rx_buf; tx = xfer->tx_buf; - chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; @@ -359,19 +354,6 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, dma_async_issue_pending(mcspi_dma->dma_tx); omap2_mcspi_set_dma_req(spi, 0, 1); - wait_for_completion(&mcspi_dma->dma_tx_completion); - dma_unmap_single(mcspi->dev, xfer->tx_dma, count, - DMA_TO_DEVICE); - - /* for TX_ONLY mode, be sure all words have shifted out */ - if (rx == NULL) { - if (mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_TXS) < 0) - dev_err(&spi->dev, "TXS timed out\n"); - else if (mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_EOT) < 0) - dev_err(&spi->dev, "EOT timed out\n"); - } } static unsigned @@ -492,6 +474,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) struct dma_slave_config cfg; enum dma_slave_buswidth width; unsigned es; + void __iomem *chstat_reg; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; @@ -526,8 +509,24 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) omap2_mcspi_tx_dma(spi, xfer, cfg); if (rx != NULL) - return omap2_mcspi_rx_dma(spi, xfer, cfg, es); - + count = omap2_mcspi_rx_dma(spi, xfer, cfg, es); + + if (tx != NULL) { + chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; + wait_for_completion(&mcspi_dma->dma_tx_completion); + dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len, + DMA_TO_DEVICE); + + /* for TX_ONLY mode, be sure all words have shifted out */ + if (rx == NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) + dev_err(&spi->dev, "TXS timed out\n"); + else if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0) + dev_err(&spi->dev, "EOT timed out\n"); + } + } return count; } -- cgit v1.2.3 From 45e5033851acfbad1a69dea12caba3adbb162a0a Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Wed, 7 Nov 2012 11:40:12 +0530 Subject: spi: s3c64xx: Add fix for crash in spi suspend/resume The SPI driver makes a gpio_request during initialization. The requested gpios need to be populated as part of the s3c64xx_spi_driver_data so that they can be released during suspend and requested again during resume. Add the missing code to save the requested gpios as part of the driver data. This fixes a SPI driver suspend crash. Signed-off-by: Abhilash Kesavan Signed-off-by: Naveen Krishna Chatradhi Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 6e7a805d324d..824ea892db76 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1112,7 +1112,7 @@ static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd) dev_err(dev, "invalid gpio[%d]: %d\n", idx, gpio); goto free_gpio; } - + sdd->gpios[idx] = gpio; ret = gpio_request(gpio, "spi-bus"); if (ret) { dev_err(dev, "gpio [%d] request failed: %d\n", -- cgit v1.2.3 From e6811d1d7a6a38ee637fe219c3b67dbfe17e8b3f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 9 Nov 2012 14:36:45 +0530 Subject: spi: make sure all transfer has bits_per_word set When spi client does the spi transfer and does not sets the bits_per_word for each transfer then set it as default of spi device in spi core before calling low level transfer. Removing the similar code from spi-tegra20-slink driver as it is not required. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 3 +-- drivers/spi/spi.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index b8985be81d96..07dc7359fb12 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -727,8 +727,7 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi, unsigned long command; unsigned long command2; - bits_per_word = t->bits_per_word ? t->bits_per_word : - spi->bits_per_word; + bits_per_word = t->bits_per_word; speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; if (!speed) speed = tspi->spi_max_frequency; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 84c2861d6f4d..518e5955664c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1190,6 +1190,7 @@ EXPORT_SYMBOL_GPL(spi_setup); static int __spi_async(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; + struct spi_transfer *xfer; /* Half-duplex links include original MicroWire, and ones with * only one data pin like SPI_3WIRE (switches direction) or where @@ -1198,7 +1199,6 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) */ if ((master->flags & SPI_MASTER_HALF_DUPLEX) || (spi->mode & SPI_3WIRE)) { - struct spi_transfer *xfer; unsigned flags = master->flags; list_for_each_entry(xfer, &message->transfers, transfer_list) { @@ -1211,6 +1211,15 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) } } + /** + * Set transfer bits_per_word as spi device default if it is not + * set for this transfer. + */ + list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (!xfer->bits_per_word) + xfer->bits_per_word = spi->bits_per_word; + } + message->spi = spi; message->status = -EINPROGRESS; return master->transfer(spi, message); -- cgit v1.2.3 From 24bc89716163b4e30c1b71c9f4bd020387b13c96 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 9 Nov 2012 14:37:32 +0530 Subject: spi: tegra: sequence compatible strings as per preference Sequence compatible list for tegra20-slink driver to first look for Tegra30 and then Tegra20. Tegra30 have additional feature in HW which need to be utilize if it is provided from DT. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 07dc7359fb12..7882b50329e2 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1109,8 +1109,8 @@ const struct tegra_slink_chip_data tegra20_spi_cdata = { }; static struct of_device_id tegra_slink_of_match[] __devinitconst = { - { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, }, { .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, }, + { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, }, {} }; MODULE_DEVICE_TABLE(of, tegra_slink_of_match); -- cgit v1.2.3 From caae070c48f39b4f7312e7473f6d3576f426e7fb Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 9 Nov 2012 14:35:22 +0530 Subject: spi: Dont call master->setup if not populated Currently the master->setup() is called unconditionally. The assumption is that every driver need to implement this callback. This encourages drivers to populate empty functions to prevent crashing. This patch prevents the call of master->setup() if it is not populated. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/spi/spi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 84c2861d6f4d..619c7df0d27f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1156,7 +1156,7 @@ EXPORT_SYMBOL_GPL(spi_busnum_to_master); int spi_setup(struct spi_device *spi) { unsigned bad_bits; - int status; + int status = 0; /* help drivers fail *cleanly* when they need options * that aren't supported with their current master @@ -1171,7 +1171,8 @@ int spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - status = spi->master->setup(spi); + if (spi->master->setup) + status = spi->master->setup(spi); dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s" "%u bits/w, %u Hz max --> %d\n", -- cgit v1.2.3 From 8528547bcc33622176a27963dc8a2513d116b832 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 14 Nov 2012 05:54:47 +0530 Subject: spi: tegra: add spi driver for sflash controller NVIDIA's Tegra20 have the SPI (SFLASH) controller to interface with spi flash device which is used for system boot. Add the spi driver for this controller. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/spi-tegra20-sflash.c | 665 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 674 insertions(+) create mode 100644 drivers/spi/spi-tegra20-sflash.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 25290d9780b2..3cbc9fbe6d10 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -385,6 +385,14 @@ config SPI_MXS help SPI driver for Freescale MXS devices. +config SPI_TEGRA20_SFLASH + tristate "Nvidia Tegra20 Serial flash Controller" + depends on ARCH_TEGRA + help + SPI driver for Nvidia Tegra20 Serial flash Controller interface. + The main usecase of this controller is to use spi flash as boot + device. + config SPI_TEGRA20_SLINK tristate "Nvidia Tegra20/Tegra30 SLINK Controller" depends on ARCH_TEGRA && TEGRA20_APB_DMA diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index f87c0f142e5a..9f6b3d84545b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o +obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c new file mode 100644 index 000000000000..54eb9488fa5a --- /dev/null +++ b/drivers/spi/spi-tegra20-sflash.c @@ -0,0 +1,665 @@ +/* + * SPI driver for Nvidia's Tegra20 Serial Flash Controller. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPI_COMMAND 0x000 +#define SPI_GO BIT(30) +#define SPI_M_S BIT(28) +#define SPI_ACTIVE_SCLK_MASK (0x3 << 26) +#define SPI_ACTIVE_SCLK_DRIVE_LOW (0 << 26) +#define SPI_ACTIVE_SCLK_DRIVE_HIGH (1 << 26) +#define SPI_ACTIVE_SCLK_PULL_LOW (2 << 26) +#define SPI_ACTIVE_SCLK_PULL_HIGH (3 << 26) + +#define SPI_CK_SDA_FALLING (1 << 21) +#define SPI_CK_SDA_RISING (0 << 21) +#define SPI_CK_SDA_MASK (1 << 21) +#define SPI_ACTIVE_SDA (0x3 << 18) +#define SPI_ACTIVE_SDA_DRIVE_LOW (0 << 18) +#define SPI_ACTIVE_SDA_DRIVE_HIGH (1 << 18) +#define SPI_ACTIVE_SDA_PULL_LOW (2 << 18) +#define SPI_ACTIVE_SDA_PULL_HIGH (3 << 18) + +#define SPI_CS_POL_INVERT BIT(16) +#define SPI_TX_EN BIT(15) +#define SPI_RX_EN BIT(14) +#define SPI_CS_VAL_HIGH BIT(13) +#define SPI_CS_VAL_LOW 0x0 +#define SPI_CS_SW BIT(12) +#define SPI_CS_HW 0x0 +#define SPI_CS_DELAY_MASK (7 << 9) +#define SPI_CS3_EN BIT(8) +#define SPI_CS2_EN BIT(7) +#define SPI_CS1_EN BIT(6) +#define SPI_CS0_EN BIT(5) + +#define SPI_CS_MASK (SPI_CS3_EN | SPI_CS2_EN | \ + SPI_CS1_EN | SPI_CS0_EN) +#define SPI_BIT_LENGTH(x) (((x) & 0x1f) << 0) + +#define SPI_MODES (SPI_ACTIVE_SCLK_MASK | SPI_CK_SDA_MASK) + +#define SPI_STATUS 0x004 +#define SPI_BSY BIT(31) +#define SPI_RDY BIT(30) +#define SPI_TXF_FLUSH BIT(29) +#define SPI_RXF_FLUSH BIT(28) +#define SPI_RX_UNF BIT(27) +#define SPI_TX_OVF BIT(26) +#define SPI_RXF_EMPTY BIT(25) +#define SPI_RXF_FULL BIT(24) +#define SPI_TXF_EMPTY BIT(23) +#define SPI_TXF_FULL BIT(22) +#define SPI_BLK_CNT(count) (((count) & 0xffff) + 1) + +#define SPI_FIFO_ERROR (SPI_RX_UNF | SPI_TX_OVF) +#define SPI_FIFO_EMPTY (SPI_TX_EMPTY | SPI_RX_EMPTY) + +#define SPI_RX_CMP 0x8 +#define SPI_DMA_CTL 0x0C +#define SPI_DMA_EN BIT(31) +#define SPI_IE_RXC BIT(27) +#define SPI_IE_TXC BIT(26) +#define SPI_PACKED BIT(20) +#define SPI_RX_TRIG_MASK (0x3 << 18) +#define SPI_RX_TRIG_1W (0x0 << 18) +#define SPI_RX_TRIG_4W (0x1 << 18) +#define SPI_TX_TRIG_MASK (0x3 << 16) +#define SPI_TX_TRIG_1W (0x0 << 16) +#define SPI_TX_TRIG_4W (0x1 << 16) +#define SPI_DMA_BLK_COUNT(count) (((count) - 1) & 0xFFFF); + +#define SPI_TX_FIFO 0x10 +#define SPI_RX_FIFO 0x20 + +#define DATA_DIR_TX (1 << 0) +#define DATA_DIR_RX (1 << 1) + +#define MAX_CHIP_SELECT 4 +#define SPI_FIFO_DEPTH 4 +#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) + +struct tegra_sflash_data { + struct device *dev; + struct spi_master *master; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + unsigned irq; + u32 spi_max_frequency; + u32 cur_speed; + + struct spi_device *cur_spi; + unsigned cur_pos; + unsigned cur_len; + unsigned bytes_per_word; + unsigned cur_direction; + unsigned curr_xfer_words; + + unsigned cur_rx_pos; + unsigned cur_tx_pos; + + u32 tx_status; + u32 rx_status; + u32 status_reg; + + u32 def_command_reg; + u32 command_reg; + u32 dma_control_reg; + + struct completion xfer_completion; + struct spi_transfer *curr_xfer; +}; + +static int tegra_sflash_runtime_suspend(struct device *dev); +static int tegra_sflash_runtime_resume(struct device *dev); + +static inline unsigned long tegra_sflash_readl(struct tegra_sflash_data *tsd, + unsigned long reg) +{ + return readl(tsd->base + reg); +} + +static inline void tegra_sflash_writel(struct tegra_sflash_data *tsd, + unsigned long val, unsigned long reg) +{ + writel(val, tsd->base + reg); +} + +static void tegra_sflash_clear_status(struct tegra_sflash_data *tsd) +{ + /* Write 1 to clear status register */ + tegra_sflash_writel(tsd, SPI_RDY | SPI_FIFO_ERROR, SPI_STATUS); +} + +static unsigned tegra_sflash_calculate_curr_xfer_param( + struct spi_device *spi, struct tegra_sflash_data *tsd, + struct spi_transfer *t) +{ + unsigned remain_len = t->len - tsd->cur_pos; + unsigned max_word; + + tsd->bytes_per_word = (t->bits_per_word - 1) / 8 + 1; + max_word = remain_len / tsd->bytes_per_word; + if (max_word > SPI_FIFO_DEPTH) + max_word = SPI_FIFO_DEPTH; + tsd->curr_xfer_words = max_word; + return max_word; +} + +static unsigned tegra_sflash_fill_tx_fifo_from_client_txbuf( + struct tegra_sflash_data *tsd, struct spi_transfer *t) +{ + unsigned nbytes; + unsigned long status; + unsigned max_n_32bit = tsd->curr_xfer_words; + u8 *tx_buf = (u8 *)t->tx_buf + tsd->cur_tx_pos; + + if (max_n_32bit > SPI_FIFO_DEPTH) + max_n_32bit = SPI_FIFO_DEPTH; + nbytes = max_n_32bit * tsd->bytes_per_word; + + status = tegra_sflash_readl(tsd, SPI_STATUS); + while (!(status & SPI_TXF_FULL)) { + int i; + unsigned int x = 0; + + for (i = 0; nbytes && (i < tsd->bytes_per_word); + i++, nbytes--) + x |= ((*tx_buf++) << i*8); + tegra_sflash_writel(tsd, x, SPI_TX_FIFO); + if (!nbytes) + break; + + status = tegra_sflash_readl(tsd, SPI_STATUS); + } + tsd->cur_tx_pos += max_n_32bit * tsd->bytes_per_word; + return max_n_32bit; +} + +static int tegra_sflash_read_rx_fifo_to_client_rxbuf( + struct tegra_sflash_data *tsd, struct spi_transfer *t) +{ + unsigned long status; + unsigned int read_words = 0; + u8 *rx_buf = (u8 *)t->rx_buf + tsd->cur_rx_pos; + + status = tegra_sflash_readl(tsd, SPI_STATUS); + while (!(status & SPI_RXF_EMPTY)) { + int i; + unsigned long x; + + x = tegra_sflash_readl(tsd, SPI_RX_FIFO); + for (i = 0; (i < tsd->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + read_words++; + status = tegra_sflash_readl(tsd, SPI_STATUS); + } + tsd->cur_rx_pos += read_words * tsd->bytes_per_word; + return 0; +} + +static int tegra_sflash_start_cpu_based_transfer( + struct tegra_sflash_data *tsd, struct spi_transfer *t) +{ + unsigned long val = 0; + unsigned cur_words; + + if (tsd->cur_direction & DATA_DIR_TX) + val |= SPI_IE_TXC; + + if (tsd->cur_direction & DATA_DIR_RX) + val |= SPI_IE_RXC; + + tegra_sflash_writel(tsd, val, SPI_DMA_CTL); + tsd->dma_control_reg = val; + + if (tsd->cur_direction & DATA_DIR_TX) + cur_words = tegra_sflash_fill_tx_fifo_from_client_txbuf(tsd, t); + else + cur_words = tsd->curr_xfer_words; + val |= SPI_DMA_BLK_COUNT(cur_words); + tegra_sflash_writel(tsd, val, SPI_DMA_CTL); + tsd->dma_control_reg = val; + val |= SPI_DMA_EN; + tegra_sflash_writel(tsd, val, SPI_DMA_CTL); + return 0; +} + +static int tegra_sflash_start_transfer_one(struct spi_device *spi, + struct spi_transfer *t, bool is_first_of_msg, + bool is_single_xfer) +{ + struct tegra_sflash_data *tsd = spi_master_get_devdata(spi->master); + u32 speed; + unsigned long command; + + speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + if (!speed) + speed = tsd->spi_max_frequency; + if (speed != tsd->cur_speed) { + clk_set_rate(tsd->clk, speed); + tsd->cur_speed = speed; + } + + tsd->cur_spi = spi; + tsd->cur_pos = 0; + tsd->cur_rx_pos = 0; + tsd->cur_tx_pos = 0; + tsd->curr_xfer = t; + tegra_sflash_calculate_curr_xfer_param(spi, tsd, t); + if (is_first_of_msg) { + command = tsd->def_command_reg; + command |= SPI_BIT_LENGTH(t->bits_per_word - 1); + command |= SPI_CS_VAL_HIGH; + + command &= ~SPI_MODES; + if (spi->mode & SPI_CPHA) + command |= SPI_CK_SDA_FALLING; + + if (spi->mode & SPI_CPOL) + command |= SPI_ACTIVE_SCLK_DRIVE_HIGH; + else + command |= SPI_ACTIVE_SCLK_DRIVE_LOW; + command |= SPI_CS0_EN << spi->chip_select; + } else { + command = tsd->command_reg; + command &= ~SPI_BIT_LENGTH(~0); + command |= SPI_BIT_LENGTH(t->bits_per_word - 1); + command &= ~(SPI_RX_EN | SPI_TX_EN); + } + + tsd->cur_direction = 0; + if (t->rx_buf) { + command |= SPI_RX_EN; + tsd->cur_direction |= DATA_DIR_RX; + } + if (t->tx_buf) { + command |= SPI_TX_EN; + tsd->cur_direction |= DATA_DIR_TX; + } + tegra_sflash_writel(tsd, command, SPI_COMMAND); + tsd->command_reg = command; + + return tegra_sflash_start_cpu_based_transfer(tsd, t); +} + +static int tegra_sflash_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + bool is_first_msg = true; + int single_xfer; + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + struct spi_transfer *xfer; + struct spi_device *spi = msg->spi; + int ret; + + ret = pm_runtime_get_sync(tsd->dev); + if (ret < 0) { + dev_err(tsd->dev, "pm_runtime_get() failed, err = %d\n", ret); + return ret; + } + + msg->status = 0; + msg->actual_length = 0; + single_xfer = list_is_singular(&msg->transfers); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + INIT_COMPLETION(tsd->xfer_completion); + ret = tegra_sflash_start_transfer_one(spi, xfer, + is_first_msg, single_xfer); + if (ret < 0) { + dev_err(tsd->dev, + "spi can not start transfer, err %d\n", ret); + goto exit; + } + is_first_msg = false; + ret = wait_for_completion_timeout(&tsd->xfer_completion, + SPI_DMA_TIMEOUT); + if (WARN_ON(ret == 0)) { + dev_err(tsd->dev, + "spi trasfer timeout, err %d\n", ret); + ret = -EIO; + goto exit; + } + + if (tsd->tx_status || tsd->rx_status) { + dev_err(tsd->dev, "Error in Transfer\n"); + ret = -EIO; + goto exit; + } + msg->actual_length += xfer->len; + if (xfer->cs_change && xfer->delay_usecs) { + tegra_sflash_writel(tsd, tsd->def_command_reg, + SPI_COMMAND); + udelay(xfer->delay_usecs); + } + } + ret = 0; +exit: + tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND); + msg->status = ret; + spi_finalize_current_message(master); + pm_runtime_put(tsd->dev); + return ret; +} + +static irqreturn_t handle_cpu_based_xfer(struct tegra_sflash_data *tsd) +{ + struct spi_transfer *t = tsd->curr_xfer; + unsigned long flags; + + spin_lock_irqsave(&tsd->lock, flags); + if (tsd->tx_status || tsd->rx_status || (tsd->status_reg & SPI_BSY)) { + dev_err(tsd->dev, + "CpuXfer ERROR bit set 0x%x\n", tsd->status_reg); + dev_err(tsd->dev, + "CpuXfer 0x%08x:0x%08x\n", tsd->command_reg, + tsd->dma_control_reg); + tegra_periph_reset_assert(tsd->clk); + udelay(2); + tegra_periph_reset_deassert(tsd->clk); + complete(&tsd->xfer_completion); + goto exit; + } + + if (tsd->cur_direction & DATA_DIR_RX) + tegra_sflash_read_rx_fifo_to_client_rxbuf(tsd, t); + + if (tsd->cur_direction & DATA_DIR_TX) + tsd->cur_pos = tsd->cur_tx_pos; + else + tsd->cur_pos = tsd->cur_rx_pos; + + if (tsd->cur_pos == t->len) { + complete(&tsd->xfer_completion); + goto exit; + } + + tegra_sflash_calculate_curr_xfer_param(tsd->cur_spi, tsd, t); + tegra_sflash_start_cpu_based_transfer(tsd, t); +exit: + spin_unlock_irqrestore(&tsd->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t tegra_sflash_isr(int irq, void *context_data) +{ + struct tegra_sflash_data *tsd = context_data; + + tsd->status_reg = tegra_sflash_readl(tsd, SPI_STATUS); + if (tsd->cur_direction & DATA_DIR_TX) + tsd->tx_status = tsd->status_reg & SPI_TX_OVF; + + if (tsd->cur_direction & DATA_DIR_RX) + tsd->rx_status = tsd->status_reg & SPI_RX_UNF; + tegra_sflash_clear_status(tsd); + + return handle_cpu_based_xfer(tsd); +} + +static struct tegra_spi_platform_data *tegra_sflash_parse_dt( + struct platform_device *pdev) +{ + struct tegra_spi_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + u32 max_freq; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Memory alloc for pdata failed\n"); + return NULL; + } + + if (!of_property_read_u32(np, "spi-max-frequency", &max_freq)) + pdata->spi_max_frequency = max_freq; + + return pdata; +} + +static struct of_device_id tegra_sflash_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-sflash", }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra_sflash_of_match); + +static int __devinit tegra_sflash_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct tegra_sflash_data *tsd; + struct resource *r; + struct tegra_spi_platform_data *pdata = pdev->dev.platform_data; + int ret; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(tegra_sflash_of_match), + &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + + if (!pdata && pdev->dev.of_node) + pdata = tegra_sflash_parse_dt(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting\n"); + return -ENODEV; + } + + if (!pdata->spi_max_frequency) + pdata->spi_max_frequency = 25000000; /* 25MHz */ + + master = spi_alloc_master(&pdev->dev, sizeof(*tsd)); + if (!master) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->transfer_one_message = tegra_sflash_transfer_one_message; + master->num_chipselect = MAX_CHIP_SELECT; + master->bus_num = -1; + + dev_set_drvdata(&pdev->dev, master); + tsd = spi_master_get_devdata(master); + tsd->master = master; + tsd->dev = &pdev->dev; + spin_lock_init(&tsd->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "No IO memory resource\n"); + ret = -ENODEV; + goto exit_free_master; + } + tsd->base = devm_request_and_ioremap(&pdev->dev, r); + if (!tsd->base) { + dev_err(&pdev->dev, + "Cannot request memregion/iomap dma address\n"); + ret = -EADDRNOTAVAIL; + goto exit_free_master; + } + + tsd->irq = platform_get_irq(pdev, 0); + ret = request_irq(tsd->irq, tegra_sflash_isr, 0, + dev_name(&pdev->dev), tsd); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + tsd->irq); + goto exit_free_master; + } + + tsd->clk = devm_clk_get(&pdev->dev, "spi"); + if (IS_ERR(tsd->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tsd->clk); + goto exit_free_irq; + } + + tsd->spi_max_frequency = pdata->spi_max_frequency; + init_completion(&tsd->xfer_completion); + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_sflash_runtime_resume(&pdev->dev); + if (ret) + goto exit_pm_disable; + } + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + goto exit_pm_disable; + } + + /* Reset controller */ + tegra_periph_reset_assert(tsd->clk); + udelay(2); + tegra_periph_reset_deassert(tsd->clk); + + tsd->def_command_reg = SPI_M_S | SPI_CS_SW; + tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND); + pm_runtime_put(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; + ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "can not register to master err %d\n", ret); + goto exit_pm_disable; + } + return ret; + +exit_pm_disable: + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_sflash_runtime_suspend(&pdev->dev); +exit_free_irq: + free_irq(tsd->irq, tsd); +exit_free_master: + spi_master_put(master); + return ret; +} + +static int __devexit tegra_sflash_remove(struct platform_device *pdev) +{ + struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + + free_irq(tsd->irq, tsd); + spi_unregister_master(master); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_sflash_runtime_suspend(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_sflash_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + + return spi_master_suspend(master); +} + +static int tegra_sflash_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + tegra_sflash_writel(tsd, tsd->command_reg, SPI_COMMAND); + pm_runtime_put(dev); + + return spi_master_resume(master); +} +#endif + +static int tegra_sflash_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + + /* Flush all write which are in PPSB queue by reading back */ + tegra_sflash_readl(tsd, SPI_COMMAND); + + clk_disable_unprepare(tsd->clk); + return 0; +} + +static int tegra_sflash_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(tsd->clk); + if (ret < 0) { + dev_err(tsd->dev, "clk_prepare failed: %d\n", ret); + return ret; + } + return 0; +} + +static const struct dev_pm_ops slink_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_sflash_runtime_suspend, + tegra_sflash_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_sflash_suspend, tegra_sflash_resume) +}; +static struct platform_driver tegra_sflash_driver = { + .driver = { + .name = "spi-tegra-sflash", + .owner = THIS_MODULE, + .pm = &slink_pm_ops, + .of_match_table = of_match_ptr(tegra_sflash_of_match), + }, + .probe = tegra_sflash_probe, + .remove = __devexit_p(tegra_sflash_remove), +}; +module_platform_driver(tegra_sflash_driver); + +MODULE_ALIAS("platform:spi-tegra-sflash"); +MODULE_DESCRIPTION("NVIDIA Tegra20 Serial Flash Controller Driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 2cd451792db174382aaca96c75bc3bf47a6065fe Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 14 Nov 2012 11:14:26 +0800 Subject: spi/omap: fix D0/D1 direction confusion 0384e90b8 ("spi/mcspi: allow configuration of pin directions") did what it claimed to do the wrong way around. D0/D1 is configured as output by *clearing* the bits in the conf registers, hence also breaking the former default behaviour. Fix this before that change is merged to mainline. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 51046332677c..89f73c425c3d 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -766,7 +766,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ - if (mcspi->pin_dir == MCSPI_PINDIR_D0_OUT_D1_IN) { + if (mcspi->pin_dir == MCSPI_PINDIR_D0_IN_D1_OUT) { l &= ~OMAP2_MCSPI_CHCONF_IS; l &= ~OMAP2_MCSPI_CHCONF_DPE1; l |= OMAP2_MCSPI_CHCONF_DPE0; @@ -1188,8 +1188,8 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) of_property_read_u32(node, "ti,spi-num-cs", &num_cs); master->num_chipselect = num_cs; master->bus_num = bus_num++; - if (of_get_property(node, "ti,pindir-d0-in-d1-out", NULL)) - mcspi->pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT; + if (of_get_property(node, "ti,pindir-d0-out-d1-in", NULL)) + mcspi->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; } else { pdata = pdev->dev.platform_data; master->num_chipselect = pdata->num_cs; -- cgit v1.2.3 From b82b576bc7ffb9785ad3248825ab20496b018e0c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 21 May 2012 13:25:13 +0200 Subject: spi/bitbang: avoid needless loop flow manipulations This patch makes a loop look cleaner by replacing a "break" and a "continue" in its body by a single "if". Signed-off-by: Guennadi Liakhovetski Signed-off-by: Grant Likely --- drivers/spi/spi-bitbang.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index aef59b1a15f7..f5ae6e960c7e 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -346,17 +346,14 @@ static void bitbang_work(struct work_struct *work) if (t->delay_usecs) udelay(t->delay_usecs); - if (!cs_change) - continue; - if (t->transfer_list.next == &m->transfers) - break; - - /* sometimes a short mid-message deselect of the chip - * may be needed to terminate a mode or command - */ - ndelay(nsecs); - bitbang->chipselect(spi, BITBANG_CS_INACTIVE); - ndelay(nsecs); + if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) { + /* sometimes a short mid-message deselect of the chip + * may be needed to terminate a mode or command + */ + ndelay(nsecs); + bitbang->chipselect(spi, BITBANG_CS_INACTIVE); + ndelay(nsecs); + } } m->status = status; -- cgit v1.2.3 From edba9bf99dfb20d316abd164d9027479010dbf9d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 21 May 2012 13:25:17 +0200 Subject: spi/bitbang: (cosmetic) simplify list manipulation Use a proper list iterator instead of an open-coded loop and remove a superfluous list head initialisation. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Grant Likely --- drivers/spi/spi-bitbang.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index f5ae6e960c7e..8b3d8efafd3c 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -260,11 +260,11 @@ static void bitbang_work(struct work_struct *work) struct spi_bitbang *bitbang = container_of(work, struct spi_bitbang, work); unsigned long flags; + struct spi_message *m, *_m; spin_lock_irqsave(&bitbang->lock, flags); bitbang->busy = 1; - while (!list_empty(&bitbang->queue)) { - struct spi_message *m; + list_for_each_entry_safe(m, _m, &bitbang->queue, queue) { struct spi_device *spi; unsigned nsecs; struct spi_transfer *t = NULL; @@ -273,9 +273,7 @@ static void bitbang_work(struct work_struct *work) int status; int do_setup = -1; - m = container_of(bitbang->queue.next, struct spi_message, - queue); - list_del_init(&m->queue); + list_del(&m->queue); spin_unlock_irqrestore(&bitbang->lock, flags); /* FIXME this is made-up ... the correct value is known to -- cgit v1.2.3 From 15d0983f5cc862facc8453a592220598a36d7f9d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 7 Oct 2012 21:29:21 +0800 Subject: spi: omap2-mcspi: remove duplicated include from spi-omap2-mcspi.c Remove duplicated include. Signed-off-by: Wei Yongjun Signed-off-by: Grant Likely --- drivers/spi/spi-omap2-mcspi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3542fdc664b1..08a767047352 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -39,7 +39,6 @@ #include #include #include -#include #include -- cgit v1.2.3 From 743179849015dc71bb2ea63d8cd4bfa7fdfb4bc6 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 15 Nov 2012 20:19:57 +0100 Subject: of_spi: add generic binding support to specify cs gpio This will allow to use gpio for chip select with no modification in the driver binding When use the cs-gpios, the gpio number will be passed via the cs_gpio field and the number of chip select will automatically increased with max(hw cs, gpio cs). So if for example the controller has 2 CS lines, and the cs-gpios property looks like this: cs-gpios = <&gpio1 0 0> <0> <&gpio1 1 0> <&gpio1 2 0>; Then it should be configured so that num_chipselect = 4 with the following mapping: cs0 : &gpio1 0 0 cs1 : native cs2 : &gpio1 1 0 cs3 : &gpio1 2 0 Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Cc: devicetree-discuss@lists.ozlabs.org Cc: spi-devel-general@lists.sourceforge.net Signed-off-by: Richard Genoud [grant.likely: fixed up type of cs count so min() can do type checking] Signed-off-by: Grant Likely --- drivers/spi/spi.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 84c2861d6f4d..1587a4a5ff41 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -327,6 +328,7 @@ struct spi_device *spi_alloc_device(struct spi_master *master) spi->dev.parent = &master->dev; spi->dev.bus = &spi_bus_type; spi->dev.release = spidev_release; + spi->cs_gpio = -EINVAL; device_initialize(&spi->dev); return spi; } @@ -344,15 +346,16 @@ EXPORT_SYMBOL_GPL(spi_alloc_device); int spi_add_device(struct spi_device *spi) { static DEFINE_MUTEX(spi_add_lock); - struct device *dev = spi->master->dev.parent; + struct spi_master *master = spi->master; + struct device *dev = master->dev.parent; struct device *d; int status; /* Chipselects are numbered 0..max; validate. */ - if (spi->chip_select >= spi->master->num_chipselect) { + if (spi->chip_select >= master->num_chipselect) { dev_err(dev, "cs%d >= max %d\n", spi->chip_select, - spi->master->num_chipselect); + master->num_chipselect); return -EINVAL; } @@ -376,6 +379,9 @@ int spi_add_device(struct spi_device *spi) goto done; } + if (master->cs_gpios) + spi->cs_gpio = master->cs_gpios[spi->chip_select]; + /* Drivers may modify this initial i/o setup, but will * normally rely on the device being setup. Devices * using SPI_CS_HIGH can't coexist well otherwise... @@ -946,6 +952,44 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) } EXPORT_SYMBOL_GPL(spi_alloc_master); +#ifdef CONFIG_OF +static int of_spi_register_master(struct spi_master *master) +{ + u16 nb; + int i, *cs; + struct device_node *np = master->dev.of_node; + + if (!np) + return 0; + + nb = of_gpio_named_count(np, "cs-gpios"); + master->num_chipselect = max(nb, master->num_chipselect); + + if (nb < 1) + return 0; + + cs = devm_kzalloc(&master->dev, + sizeof(int) * master->num_chipselect, + GFP_KERNEL); + master->cs_gpios = cs; + + if (!master->cs_gpios) + return -ENOMEM; + + memset(cs, -EINVAL, master->num_chipselect); + + for (i = 0; i < nb; i++) + cs[i] = of_get_named_gpio(np, "cs-gpios", i); + + return 0; +} +#else +static int of_spi_register_master(struct spi_master *master) +{ + return 0; +} +#endif + /** * spi_register_master - register SPI master controller * @master: initialized master, originally from spi_alloc_master() @@ -977,6 +1021,10 @@ int spi_register_master(struct spi_master *master) if (!dev) return -ENODEV; + status = of_spi_register_master(master); + if (status) + return status; + /* even if it's just one always-selected device, there must * be at least one chipselect */ -- cgit v1.2.3 From ce3293058637ada3b1837a77c8f9c672a51b2434 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Thu, 22 Nov 2012 14:37:27 +0000 Subject: spi/sh-hspi: add CS manual control support The current HSPI driver used automatic CS control, leading to CS active for each byte transmitted. This patch changes the driver to manual CS control, and ensures CS is active thoughout a whole message. Additionally, it uses the cs_change field to determine if CS is disabled between transfers in the message. Signed-off-by: Phil Edworthy Signed-off-by: Grant Likely --- drivers/spi/spi-sh-hspi.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 796c077ef439..47c4beee8a0e 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -68,6 +68,16 @@ static u32 hspi_read(struct hspi_priv *hspi, int reg) return ioread32(hspi->addr + reg); } +static void hspi_bit_set(struct hspi_priv *hspi, int reg, u32 mask, u32 set) +{ + u32 val = hspi_read(hspi, reg); + + val &= ~mask; + val |= set & mask; + + hspi_write(hspi, reg, val); +} + /* * transfer function */ @@ -105,6 +115,13 @@ static int hspi_unprepare_transfer(struct spi_master *master) return 0; } +#define hspi_hw_cs_enable(hspi) hspi_hw_cs_ctrl(hspi, 0) +#define hspi_hw_cs_disable(hspi) hspi_hw_cs_ctrl(hspi, 1) +static void hspi_hw_cs_ctrl(struct hspi_priv *hspi, int hi) +{ + hspi_bit_set(hspi, SPSCR, (1 << 6), (hi) << 6); +} + static void hspi_hw_setup(struct hspi_priv *hspi, struct spi_message *msg, struct spi_transfer *t) @@ -155,7 +172,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi, hspi_write(hspi, SPCR, spcr); hspi_write(hspi, SPSR, 0x0); - hspi_write(hspi, SPSCR, 0x1); /* master mode */ + hspi_write(hspi, SPSCR, 0x21); /* master mode / CS control */ } static int hspi_transfer_one_message(struct spi_master *master, @@ -166,12 +183,21 @@ static int hspi_transfer_one_message(struct spi_master *master, u32 tx; u32 rx; int ret, i; + unsigned int cs_change; + const int nsecs = 50; dev_dbg(hspi->dev, "%s\n", __func__); + cs_change = 1; ret = 0; list_for_each_entry(t, &msg->transfers, transfer_list) { - hspi_hw_setup(hspi, msg, t); + + if (cs_change) { + hspi_hw_setup(hspi, msg, t); + hspi_hw_cs_enable(hspi); + ndelay(nsecs); + } + cs_change = t->cs_change; for (i = 0; i < t->len; i++) { @@ -198,9 +224,22 @@ static int hspi_transfer_one_message(struct spi_master *master, } msg->actual_length += t->len; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (cs_change) { + ndelay(nsecs); + hspi_hw_cs_disable(hspi); + ndelay(nsecs); + } } msg->status = ret; + if (!cs_change) { + ndelay(nsecs); + hspi_hw_cs_disable(hspi); + } spi_finalize_current_message(master); return ret; -- cgit v1.2.3 From 6a47d80308a824c32c47fbef6b8e8b59de08af94 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Mon, 19 Nov 2012 12:12:29 +0530 Subject: spi: omap2-mcspi: Fix the redifine warning Fix the below warning drivers/spi/spi-omap2-mcspi.c:336:34: warning: symbol 'tx' shadows an earlier one drivers/spi/spi-omap2-mcspi.c:327:12: originally declared here So delete the u8 tx as it is assigned and not used(resigned afterwards). Signed-off-by: Shubhrajyoti D Acked-by: Peter Korsgaard Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index cc4f2dd89b50..d1895f700196 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -324,14 +324,11 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; unsigned int count; - const u8 * tx; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; count = xfer->len; - tx = xfer->tx_buf; - if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; struct scatterlist sg; -- cgit v1.2.3 From 227c4ce6eac9fe6566d9718546a0e31b5b4633cd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 19 Nov 2012 17:07:52 +0530 Subject: spi: omap2-mcspi: remove duplicate inclusion of linux/err.h linux/err.h was included twice. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index d1895f700196..850138455259 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -39,7 +39,6 @@ #include #include #include -#include #include -- cgit v1.2.3 From 161b96c383c442f4d7dabbb8500a5fbd551b344d Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 7 Nov 2012 21:30:29 +0400 Subject: spi/clps711x: New SPI master driver This patch add new driver for CLPS711X SPI master controller. Due to platform limitations driver supports only 8 bit transfer mode. Chip select control is handled via GPIO. Signed-off-by: Alexander Shiyan Acked-by: Arnd Bergmann Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 7 ++ drivers/spi/Makefile | 1 + drivers/spi/spi-clps711x.c | 296 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+) create mode 100644 drivers/spi/spi-clps711x.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1acae359cabe..2a13e637e46b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -123,6 +123,13 @@ config SPI_BUTTERFLY inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_CLPS711X + tristate "CLPS711X host SPI controller" + depends on ARCH_CLPS711X + help + This enables dedicated general purpose SPI/Microwire1-compatible + master mode interface (SSI1) for CLPS711X-based CPUs. + config SPI_COLDFIRE_QSPI tristate "Freescale Coldfire QSPI controller" depends on (M520x || M523x || M5249 || M525x || M527x || M528x || M532x) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c48df47e4b0f..25ab4a1cd014 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o +obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c new file mode 100644 index 000000000000..59677eb30f94 --- /dev/null +++ b/drivers/spi/spi-clps711x.c @@ -0,0 +1,296 @@ +/* + * CLPS711X SPI bus driver + * + * Copyright (C) 2012 Alexander Shiyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "spi-clps711x" + +struct spi_clps711x_data { + struct completion done; + + struct clk *spi_clk; + u32 max_speed_hz; + + u8 *tx_buf; + u8 *rx_buf; + int count; + int len; + + int chipselect[0]; +}; + +static int spi_clps711x_setup(struct spi_device *spi) +{ + struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master); + + if (spi->bits_per_word != 8) { + dev_err(&spi->dev, "Unsupported master bus width %i\n", + spi->bits_per_word); + return -EINVAL; + } + + /* We are expect that SPI-device is not selected */ + gpio_direction_output(hw->chipselect[spi->chip_select], + !(spi->mode & SPI_CS_HIGH)); + + return 0; +} + +static void spi_clps711x_setup_mode(struct spi_device *spi) +{ + /* Setup edge for transfer */ + if (spi->mode & SPI_CPHA) + clps_writew(clps_readw(SYSCON3) | SYSCON3_ADCCKNSEN, SYSCON3); + else + clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCKNSEN, SYSCON3); +} + +static int spi_clps711x_setup_xfer(struct spi_device *spi, + struct spi_transfer *xfer) +{ + u32 speed = xfer->speed_hz ? : spi->max_speed_hz; + u8 bpw = xfer->bits_per_word ? : spi->bits_per_word; + struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master); + + if (bpw != 8) { + dev_err(&spi->dev, "Unsupported master bus width %i\n", bpw); + return -EINVAL; + } + + /* Setup SPI frequency divider */ + if (!speed || (speed >= hw->max_speed_hz)) + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) | + SYSCON1_ADCKSEL(3), SYSCON1); + else if (speed >= (hw->max_speed_hz / 2)) + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) | + SYSCON1_ADCKSEL(2), SYSCON1); + else if (speed >= (hw->max_speed_hz / 8)) + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) | + SYSCON1_ADCKSEL(1), SYSCON1); + else + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) | + SYSCON1_ADCKSEL(0), SYSCON1); + + return 0; +} + +static int spi_clps711x_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct spi_clps711x_data *hw = spi_master_get_devdata(master); + struct spi_transfer *xfer; + int status = 0, cs = hw->chipselect[msg->spi->chip_select]; + u32 data; + + spi_clps711x_setup_mode(msg->spi); + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (spi_clps711x_setup_xfer(msg->spi, xfer)) { + status = -EINVAL; + goto out_xfr; + } + + gpio_set_value(cs, !!(msg->spi->mode & SPI_CS_HIGH)); + + INIT_COMPLETION(hw->done); + + hw->count = 0; + hw->len = xfer->len; + hw->tx_buf = (u8 *)xfer->tx_buf; + hw->rx_buf = (u8 *)xfer->rx_buf; + + /* Initiate transfer */ + data = hw->tx_buf ? hw->tx_buf[hw->count] : 0; + clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO); + + wait_for_completion(&hw->done); + + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + if (xfer->cs_change || + list_is_last(&xfer->transfer_list, &msg->transfers)) + gpio_set_value(cs, !(msg->spi->mode & SPI_CS_HIGH)); + + msg->actual_length += xfer->len; + } + +out_xfr: + msg->status = status; + spi_finalize_current_message(master); + + return 0; +} + +static irqreturn_t spi_clps711x_isr(int irq, void *dev_id) +{ + struct spi_clps711x_data *hw = (struct spi_clps711x_data *)dev_id; + u32 data; + + /* Handle RX */ + data = clps_readb(SYNCIO); + if (hw->rx_buf) + hw->rx_buf[hw->count] = (u8)data; + + hw->count++; + + /* Handle TX */ + if (hw->count < hw->len) { + data = hw->tx_buf ? hw->tx_buf[hw->count] : 0; + clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO); + } else + complete(&hw->done); + + return IRQ_HANDLED; +} + +static int __devinit spi_clps711x_probe(struct platform_device *pdev) +{ + int i, ret; + struct spi_master *master; + struct spi_clps711x_data *hw; + struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data supplied\n"); + return -EINVAL; + } + + if (pdata->num_chipselect < 1) { + dev_err(&pdev->dev, "At least one CS must be defined\n"); + return -EINVAL; + } + + master = spi_alloc_master(&pdev->dev, + sizeof(struct spi_clps711x_data) + + sizeof(int) * pdata->num_chipselect); + if (!master) { + dev_err(&pdev->dev, "SPI allocating memory error\n"); + return -ENOMEM; + } + + master->bus_num = pdev->id; + master->mode_bits = SPI_CPHA | SPI_CS_HIGH; + master->num_chipselect = pdata->num_chipselect; + master->setup = spi_clps711x_setup; + master->transfer_one_message = spi_clps711x_transfer_one_message; + + hw = spi_master_get_devdata(master); + + for (i = 0; i < master->num_chipselect; i++) { + hw->chipselect[i] = pdata->chipselect[i]; + if (!gpio_is_valid(hw->chipselect[i])) { + dev_err(&pdev->dev, "Invalid CS GPIO %i\n", i); + ret = -EINVAL; + goto err_out; + } + if (gpio_request(hw->chipselect[i], DRIVER_NAME)) { + dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i); + ret = -EINVAL; + goto err_out; + } + } + + hw->spi_clk = devm_clk_get(&pdev->dev, "spi"); + if (IS_ERR(hw->spi_clk)) { + dev_err(&pdev->dev, "Can't get clocks\n"); + ret = PTR_ERR(hw->spi_clk); + goto err_out; + } + hw->max_speed_hz = clk_get_rate(hw->spi_clk); + + init_completion(&hw->done); + platform_set_drvdata(pdev, master); + + /* Disable extended mode due hardware problems */ + clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCON, SYSCON3); + + /* Clear possible pending interrupt */ + clps_readl(SYNCIO); + + ret = devm_request_irq(&pdev->dev, IRQ_SSEOTI, spi_clps711x_isr, 0, + dev_name(&pdev->dev), hw); + if (ret) { + dev_err(&pdev->dev, "Can't request IRQ\n"); + clk_put(hw->spi_clk); + goto clk_out; + } + + ret = spi_register_master(master); + if (!ret) { + dev_info(&pdev->dev, + "SPI bus driver initialized. Master clock %u Hz\n", + hw->max_speed_hz); + return 0; + } + + dev_err(&pdev->dev, "Failed to register master\n"); + devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw); + +clk_out: + devm_clk_put(&pdev->dev, hw->spi_clk); + +err_out: + while (--i >= 0) + if (gpio_is_valid(hw->chipselect[i])) + gpio_free(hw->chipselect[i]); + + platform_set_drvdata(pdev, NULL); + spi_master_put(master); + kfree(master); + + return ret; +} + +static int __devexit spi_clps711x_remove(struct platform_device *pdev) +{ + int i; + struct spi_master *master = platform_get_drvdata(pdev); + struct spi_clps711x_data *hw = spi_master_get_devdata(master); + + devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw); + + for (i = 0; i < master->num_chipselect; i++) + if (gpio_is_valid(hw->chipselect[i])) + gpio_free(hw->chipselect[i]); + + devm_clk_put(&pdev->dev, hw->spi_clk); + platform_set_drvdata(pdev, NULL); + spi_unregister_master(master); + kfree(master); + + return 0; +} + +static struct platform_driver clps711x_spi_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = spi_clps711x_probe, + .remove = __devexit_p(spi_clps711x_remove), +}; +module_platform_driver(clps711x_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexander Shiyan "); +MODULE_DESCRIPTION("CLPS711X SPI bus driver"); -- cgit v1.2.3 From 266904c77911fdc0a064d97e2f06034b824c23ac Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 4 Dec 2012 06:42:28 +0100 Subject: spi/stmp: remove obsolete driver This driver is obsolete and can't even be built anymore since the platform it depends has been removed. The STMP series is completely covered by the MXS platform these days, so spi-mxs can be used instead. Signed-off-by: Wolfram Sang Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 6 - drivers/spi/Makefile | 1 - drivers/spi/spi-stmp.c | 664 ------------------------------------------------- 3 files changed, 671 deletions(-) delete mode 100644 drivers/spi/spi-stmp.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 506584237355..45cb291287c9 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -379,12 +379,6 @@ config SPI_SIRF help SPI driver for CSR SiRFprimaII SoCs -config SPI_STMP3XXX - tristate "Freescale STMP37xx/378x SPI/SSP controller" - depends on ARCH_STMP3XXX - help - SPI driver for Freescale STMP37xx/378x SoC SSP interface - config SPI_MXS tristate "Freescale MXS SPI controller" depends on ARCH_MXS diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e0fca55cffe6..64e970ba261c 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,7 +60,6 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o -obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o diff --git a/drivers/spi/spi-stmp.c b/drivers/spi/spi-stmp.c deleted file mode 100644 index 911e904b3c84..000000000000 --- a/drivers/spi/spi-stmp.c +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Freescale STMP378X SPI master driver - * - * Author: dmitry pervushin - * - * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. - */ - -/* - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -/* 0 means DMA mode(recommended, default), !0 - PIO mode */ -static int pio; -static int clock; - -/* default timeout for busy waits is 2 seconds */ -#define STMP_SPI_TIMEOUT (2 * HZ) - -struct stmp_spi { - int id; - - void * __iomem regs; /* vaddr of the control registers */ - - int irq, err_irq; - u32 dma; - struct stmp3xxx_dma_descriptor d; - - u32 speed_khz; - u32 saved_timings; - u32 divider; - - struct clk *clk; - struct device *master_dev; - - struct work_struct work; - struct workqueue_struct *workqueue; - - /* lock protects queue access */ - spinlock_t lock; - struct list_head queue; - - struct completion done; -}; - -#define busy_wait(cond) \ - ({ \ - unsigned long end_jiffies = jiffies + STMP_SPI_TIMEOUT; \ - bool succeeded = false; \ - do { \ - if (cond) { \ - succeeded = true; \ - break; \ - } \ - cpu_relax(); \ - } while (time_before(jiffies, end_jiffies)); \ - succeeded; \ - }) - -/** - * stmp_spi_init_hw - * Initialize the SSP port - */ -static int stmp_spi_init_hw(struct stmp_spi *ss) -{ - int err = 0; - void *pins = ss->master_dev->platform_data; - - err = stmp3xxx_request_pin_group(pins, dev_name(ss->master_dev)); - if (err) - goto out; - - ss->clk = clk_get(NULL, "ssp"); - if (IS_ERR(ss->clk)) { - err = PTR_ERR(ss->clk); - goto out_free_pins; - } - clk_enable(ss->clk); - - stmp3xxx_reset_block(ss->regs, false); - stmp3xxx_dma_reset_channel(ss->dma); - - return 0; - -out_free_pins: - stmp3xxx_release_pin_group(pins, dev_name(ss->master_dev)); -out: - return err; -} - -static void stmp_spi_release_hw(struct stmp_spi *ss) -{ - void *pins = ss->master_dev->platform_data; - - if (ss->clk && !IS_ERR(ss->clk)) { - clk_disable(ss->clk); - clk_put(ss->clk); - } - stmp3xxx_release_pin_group(pins, dev_name(ss->master_dev)); -} - -static int stmp_spi_setup_transfer(struct spi_device *spi, - struct spi_transfer *t) -{ - u8 bits_per_word; - u32 hz; - struct stmp_spi *ss = spi_master_get_devdata(spi->master); - u16 rate; - - bits_per_word = spi->bits_per_word; - if (t && t->bits_per_word) - bits_per_word = t->bits_per_word; - - /* - * Calculate speed: - * - by default, use maximum speed from ssp clk - * - if device overrides it, use it - * - if transfer specifies other speed, use transfer's one - */ - hz = 1000 * ss->speed_khz / ss->divider; - if (spi->max_speed_hz) - hz = min(hz, spi->max_speed_hz); - if (t && t->speed_hz) - hz = min(hz, t->speed_hz); - - if (hz == 0) { - dev_err(&spi->dev, "Cannot continue with zero clock\n"); - return -EINVAL; - } - - if (bits_per_word != 8) { - dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, bits_per_word); - return -EINVAL; - } - - dev_dbg(&spi->dev, "Requested clk rate = %uHz, max = %uHz/%d = %uHz\n", - hz, ss->speed_khz, ss->divider, - ss->speed_khz * 1000 / ss->divider); - - if (ss->speed_khz * 1000 / ss->divider < hz) { - dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n", - __func__, hz); - return -EINVAL; - } - - rate = 1000 * ss->speed_khz/ss->divider/hz; - - writel(BF(ss->divider, SSP_TIMING_CLOCK_DIVIDE) | - BF(rate - 1, SSP_TIMING_CLOCK_RATE), - HW_SSP_TIMING + ss->regs); - - writel(BF(1 /* mode SPI */, SSP_CTRL1_SSP_MODE) | - BF(4 /* 8 bits */, SSP_CTRL1_WORD_LENGTH) | - ((spi->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | - ((spi->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0) | - (pio ? 0 : BM_SSP_CTRL1_DMA_ENABLE), - ss->regs + HW_SSP_CTRL1); - - return 0; -} - -static int stmp_spi_setup(struct spi_device *spi) -{ - /* spi_setup() does basic checks, - * stmp_spi_setup_transfer() does more later - */ - if (spi->bits_per_word != 8) { - dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, spi->bits_per_word); - return -EINVAL; - } - return 0; -} - -static inline u32 stmp_spi_cs(unsigned cs) -{ - return ((cs & 1) ? BM_SSP_CTRL0_WAIT_FOR_CMD : 0) | - ((cs & 2) ? BM_SSP_CTRL0_WAIT_FOR_IRQ : 0); -} - -static int stmp_spi_txrx_dma(struct stmp_spi *ss, int cs, - unsigned char *buf, dma_addr_t dma_buf, int len, - int first, int last, bool write) -{ - u32 c0 = 0; - dma_addr_t spi_buf_dma = dma_buf; - int status = 0; - enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - - c0 |= (first ? BM_SSP_CTRL0_LOCK_CS : 0); - c0 |= (last ? BM_SSP_CTRL0_IGNORE_CRC : 0); - c0 |= (write ? 0 : BM_SSP_CTRL0_READ); - c0 |= BM_SSP_CTRL0_DATA_XFER; - - c0 |= stmp_spi_cs(cs); - - c0 |= BF(len, SSP_CTRL0_XFER_COUNT); - - if (!dma_buf) - spi_buf_dma = dma_map_single(ss->master_dev, buf, len, dir); - - ss->d.command->cmd = - BF(len, APBH_CHn_CMD_XFER_COUNT) | - BF(1, APBH_CHn_CMD_CMDWORDS) | - BM_APBH_CHn_CMD_WAIT4ENDCMD | - BM_APBH_CHn_CMD_IRQONCMPLT | - BF(write ? BV_APBH_CHn_CMD_COMMAND__DMA_READ : - BV_APBH_CHn_CMD_COMMAND__DMA_WRITE, - APBH_CHn_CMD_COMMAND); - ss->d.command->pio_words[0] = c0; - ss->d.command->buf_ptr = spi_buf_dma; - - stmp3xxx_dma_reset_channel(ss->dma); - stmp3xxx_dma_clear_interrupt(ss->dma); - stmp3xxx_dma_enable_interrupt(ss->dma); - init_completion(&ss->done); - stmp3xxx_dma_go(ss->dma, &ss->d, 1); - wait_for_completion(&ss->done); - - if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & BM_SSP_CTRL0_RUN)) - status = -ETIMEDOUT; - - if (!dma_buf) - dma_unmap_single(ss->master_dev, spi_buf_dma, len, dir); - - return status; -} - -static inline void stmp_spi_enable(struct stmp_spi *ss) -{ - stmp3xxx_setl(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0); - stmp3xxx_clearl(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0); -} - -static inline void stmp_spi_disable(struct stmp_spi *ss) -{ - stmp3xxx_clearl(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0); - stmp3xxx_setl(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0); -} - -static int stmp_spi_txrx_pio(struct stmp_spi *ss, int cs, - unsigned char *buf, int len, - bool first, bool last, bool write) -{ - if (first) - stmp_spi_enable(ss); - - stmp3xxx_setl(stmp_spi_cs(cs), ss->regs + HW_SSP_CTRL0); - - while (len--) { - if (last && len <= 0) - stmp_spi_disable(ss); - - stmp3xxx_clearl(BM_SSP_CTRL0_XFER_COUNT, - ss->regs + HW_SSP_CTRL0); - stmp3xxx_setl(1, ss->regs + HW_SSP_CTRL0); - - if (write) - stmp3xxx_clearl(BM_SSP_CTRL0_READ, - ss->regs + HW_SSP_CTRL0); - else - stmp3xxx_setl(BM_SSP_CTRL0_READ, - ss->regs + HW_SSP_CTRL0); - - /* Run! */ - stmp3xxx_setl(BM_SSP_CTRL0_RUN, ss->regs + HW_SSP_CTRL0); - - if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & - BM_SSP_CTRL0_RUN)) - break; - - if (write) - writel(*buf, ss->regs + HW_SSP_DATA); - - /* Set TRANSFER */ - stmp3xxx_setl(BM_SSP_CTRL0_DATA_XFER, ss->regs + HW_SSP_CTRL0); - - if (!write) { - if (busy_wait((readl(ss->regs + HW_SSP_STATUS) & - BM_SSP_STATUS_FIFO_EMPTY))) - break; - *buf = readl(ss->regs + HW_SSP_DATA) & 0xFF; - } - - if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & - BM_SSP_CTRL0_RUN)) - break; - - /* advance to the next byte */ - buf++; - } - - return len < 0 ? 0 : -ETIMEDOUT; -} - -static int stmp_spi_handle_message(struct stmp_spi *ss, struct spi_message *m) -{ - bool first, last; - struct spi_transfer *t, *tmp_t; - int status = 0; - int cs; - - cs = m->spi->chip_select; - - list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { - - first = (&t->transfer_list == m->transfers.next); - last = (&t->transfer_list == m->transfers.prev); - - if (first || t->speed_hz || t->bits_per_word) - stmp_spi_setup_transfer(m->spi, t); - - /* reject "not last" transfers which request to change cs */ - if (t->cs_change && !last) { - dev_err(&m->spi->dev, - "Message with t->cs_change has been skipped\n"); - continue; - } - - if (t->tx_buf) { - status = pio ? - stmp_spi_txrx_pio(ss, cs, (void *)t->tx_buf, - t->len, first, last, true) : - stmp_spi_txrx_dma(ss, cs, (void *)t->tx_buf, - t->tx_dma, t->len, first, last, true); -#ifdef DEBUG - if (t->len < 0x10) - print_hex_dump_bytes("Tx ", - DUMP_PREFIX_OFFSET, - t->tx_buf, t->len); - else - pr_debug("Tx: %d bytes\n", t->len); -#endif - } - if (t->rx_buf) { - status = pio ? - stmp_spi_txrx_pio(ss, cs, t->rx_buf, - t->len, first, last, false) : - stmp_spi_txrx_dma(ss, cs, t->rx_buf, - t->rx_dma, t->len, first, last, false); -#ifdef DEBUG - if (t->len < 0x10) - print_hex_dump_bytes("Rx ", - DUMP_PREFIX_OFFSET, - t->rx_buf, t->len); - else - pr_debug("Rx: %d bytes\n", t->len); -#endif - } - - if (t->delay_usecs) - udelay(t->delay_usecs); - - if (status) - break; - - } - return status; -} - -/** - * stmp_spi_handle - handle messages from the queue - */ -static void stmp_spi_handle(struct work_struct *w) -{ - struct stmp_spi *ss = container_of(w, struct stmp_spi, work); - unsigned long flags; - struct spi_message *m; - - spin_lock_irqsave(&ss->lock, flags); - while (!list_empty(&ss->queue)) { - m = list_entry(ss->queue.next, struct spi_message, queue); - list_del_init(&m->queue); - spin_unlock_irqrestore(&ss->lock, flags); - - m->status = stmp_spi_handle_message(ss, m); - m->complete(m->context); - - spin_lock_irqsave(&ss->lock, flags); - } - spin_unlock_irqrestore(&ss->lock, flags); - - return; -} - -/** - * stmp_spi_transfer - perform message transfer. - * Called indirectly from spi_async, queues all the messages to - * spi_handle_message. - * @spi: spi device - * @m: message to be queued - */ -static int stmp_spi_transfer(struct spi_device *spi, struct spi_message *m) -{ - struct stmp_spi *ss = spi_master_get_devdata(spi->master); - unsigned long flags; - - m->status = -EINPROGRESS; - spin_lock_irqsave(&ss->lock, flags); - list_add_tail(&m->queue, &ss->queue); - queue_work(ss->workqueue, &ss->work); - spin_unlock_irqrestore(&ss->lock, flags); - return 0; -} - -static irqreturn_t stmp_spi_irq(int irq, void *dev_id) -{ - struct stmp_spi *ss = dev_id; - - stmp3xxx_dma_clear_interrupt(ss->dma); - complete(&ss->done); - return IRQ_HANDLED; -} - -static irqreturn_t stmp_spi_irq_err(int irq, void *dev_id) -{ - struct stmp_spi *ss = dev_id; - u32 c1, st; - - c1 = readl(ss->regs + HW_SSP_CTRL1); - st = readl(ss->regs + HW_SSP_STATUS); - dev_err(ss->master_dev, "%s: status = 0x%08X, c1 = 0x%08X\n", - __func__, st, c1); - stmp3xxx_clearl(c1 & 0xCCCC0000, ss->regs + HW_SSP_CTRL1); - - return IRQ_HANDLED; -} - -static int __devinit stmp_spi_probe(struct platform_device *dev) -{ - int err = 0; - struct spi_master *master; - struct stmp_spi *ss; - struct resource *r; - - master = spi_alloc_master(&dev->dev, sizeof(struct stmp_spi)); - if (master == NULL) { - err = -ENOMEM; - goto out0; - } - master->flags = SPI_MASTER_HALF_DUPLEX; - - ss = spi_master_get_devdata(master); - platform_set_drvdata(dev, master); - - /* Get resources(memory, IRQ) associated with the device */ - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (r == NULL) { - err = -ENODEV; - goto out_put_master; - } - ss->regs = ioremap(r->start, resource_size(r)); - if (!ss->regs) { - err = -EINVAL; - goto out_put_master; - } - - ss->master_dev = &dev->dev; - ss->id = dev->id; - - INIT_WORK(&ss->work, stmp_spi_handle); - INIT_LIST_HEAD(&ss->queue); - spin_lock_init(&ss->lock); - - ss->workqueue = create_singlethread_workqueue(dev_name(&dev->dev)); - if (!ss->workqueue) { - err = -ENXIO; - goto out_put_master; - } - master->transfer = stmp_spi_transfer; - master->setup = stmp_spi_setup; - - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA; - - ss->irq = platform_get_irq(dev, 0); - if (ss->irq < 0) { - err = ss->irq; - goto out_put_master; - } - ss->err_irq = platform_get_irq(dev, 1); - if (ss->err_irq < 0) { - err = ss->err_irq; - goto out_put_master; - } - - r = platform_get_resource(dev, IORESOURCE_DMA, 0); - if (r == NULL) { - err = -ENODEV; - goto out_put_master; - } - - ss->dma = r->start; - err = stmp3xxx_dma_request(ss->dma, &dev->dev, dev_name(&dev->dev)); - if (err) - goto out_put_master; - - err = stmp3xxx_dma_allocate_command(ss->dma, &ss->d); - if (err) - goto out_free_dma; - - master->bus_num = dev->id; - master->num_chipselect = 1; - - /* SPI controller initializations */ - err = stmp_spi_init_hw(ss); - if (err) { - dev_dbg(&dev->dev, "cannot initialize hardware\n"); - goto out_free_dma_desc; - } - - if (clock) { - dev_info(&dev->dev, "clock rate forced to %d\n", clock); - clk_set_rate(ss->clk, clock); - } - ss->speed_khz = clk_get_rate(ss->clk); - ss->divider = 2; - dev_info(&dev->dev, "max possible speed %d = %ld/%d kHz\n", - ss->speed_khz, clk_get_rate(ss->clk), ss->divider); - - /* Register for SPI interrupt */ - err = request_irq(ss->irq, stmp_spi_irq, 0, - dev_name(&dev->dev), ss); - if (err) { - dev_dbg(&dev->dev, "request_irq failed, %d\n", err); - goto out_release_hw; - } - - /* ..and shared interrupt for all SSP controllers */ - err = request_irq(ss->err_irq, stmp_spi_irq_err, IRQF_SHARED, - dev_name(&dev->dev), ss); - if (err) { - dev_dbg(&dev->dev, "request_irq(error) failed, %d\n", err); - goto out_free_irq; - } - - err = spi_register_master(master); - if (err) { - dev_dbg(&dev->dev, "cannot register spi master, %d\n", err); - goto out_free_irq_2; - } - dev_info(&dev->dev, "at (mapped) 0x%08X, irq=%d, bus %d, %s mode\n", - (u32)ss->regs, ss->irq, master->bus_num, - pio ? "PIO" : "DMA"); - return 0; - -out_free_irq_2: - free_irq(ss->err_irq, ss); -out_free_irq: - free_irq(ss->irq, ss); -out_free_dma_desc: - stmp3xxx_dma_free_command(ss->dma, &ss->d); -out_free_dma: - stmp3xxx_dma_release(ss->dma); -out_release_hw: - stmp_spi_release_hw(ss); -out_put_master: - if (ss->workqueue) - destroy_workqueue(ss->workqueue); - if (ss->regs) - iounmap(ss->regs); - platform_set_drvdata(dev, NULL); - spi_master_put(master); -out0: - return err; -} - -static int __devexit stmp_spi_remove(struct platform_device *dev) -{ - struct stmp_spi *ss; - struct spi_master *master; - - master = spi_master_get(platform_get_drvdata(dev)); - ss = spi_master_get_devdata(master); - - spi_unregister_master(master); - - free_irq(ss->err_irq, ss); - free_irq(ss->irq, ss); - stmp3xxx_dma_free_command(ss->dma, &ss->d); - stmp3xxx_dma_release(ss->dma); - stmp_spi_release_hw(ss); - destroy_workqueue(ss->workqueue); - iounmap(ss->regs); - spi_master_put(master); - return 0; -} - -#ifdef CONFIG_PM -static int stmp_spi_suspend(struct platform_device *pdev, pm_message_t pmsg) -{ - struct stmp_spi *ss; - struct spi_master *master; - - master = platform_get_drvdata(pdev); - ss = spi_master_get_devdata(master); - - ss->saved_timings = readl(HW_SSP_TIMING + ss->regs); - clk_disable(ss->clk); - - return 0; -} - -static int stmp_spi_resume(struct platform_device *pdev) -{ - struct stmp_spi *ss; - struct spi_master *master; - - master = platform_get_drvdata(pdev); - ss = spi_master_get_devdata(master); - - clk_enable(ss->clk); - stmp3xxx_reset_block(ss->regs, false); - writel(ss->saved_timings, ss->regs + HW_SSP_TIMING); - - return 0; -} - -#else -#define stmp_spi_suspend NULL -#define stmp_spi_resume NULL -#endif - -static struct platform_driver stmp_spi_driver = { - .probe = stmp_spi_probe, - .remove = __devexit_p(stmp_spi_remove), - .driver = { - .name = "stmp3xxx_ssp", - .owner = THIS_MODULE, - }, - .suspend = stmp_spi_suspend, - .resume = stmp_spi_resume, -}; -module_platform_driver(stmp_spi_driver); - -module_param(pio, int, S_IRUGO); -module_param(clock, int, S_IRUGO); -MODULE_AUTHOR("dmitry pervushin "); -MODULE_DESCRIPTION("STMP3xxx SPI/SSP driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b3a223ee2d1a4635b0643c547bc0096a37334ed1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 2 Dec 2012 12:54:25 +0900 Subject: spi: Remove SPI_BUFSIZ restriction on spi_write_then_read() In order to avoid constantly allocating and deallocating there is a fixed buffer which spi_write_then_read() uses for transfers, with an early error check to ensure that the transfer fits within the buffer. This limits the size of transfers to this size, currently max(32, SMP_CACHE_BYTES). Since we can dynamically allocate and in fact already have a fallback to do so when there is contention for the fixed buffer remove this restriction and instead dynamically allocate a suitably sized buffer if the transfer won't fit. Signed-off-by: Mark Brown Signed-off-by: Grant Likely --- drivers/spi/spi.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index f1217ae59f3d..22c71e238fdf 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1545,12 +1545,18 @@ int spi_write_then_read(struct spi_device *spi, struct spi_transfer x[2]; u8 *local_buf; - /* Use preallocated DMA-safe buffer. We can't avoid copying here, - * (as a pure convenience thing), but we can keep heap costs - * out of the hot path ... + /* Use preallocated DMA-safe buffer if we can. We can't avoid + * copying here, (as a pure convenience thing), but we can + * keep heap costs out of the hot path unless someone else is + * using the pre-allocated buffer or the transfer is too large. */ - if ((n_tx + n_rx) > SPI_BUFSIZ) - return -EINVAL; + if ((n_tx + n_rx) > SPI_BUFSIZ || !mutex_trylock(&lock)) { + local_buf = kmalloc(max(SPI_BUFSIZ, n_tx + n_rx), GFP_KERNEL); + if (!local_buf) + return -ENOMEM; + } else { + local_buf = buf; + } spi_message_init(&message); memset(x, 0, sizeof x); @@ -1563,14 +1569,6 @@ int spi_write_then_read(struct spi_device *spi, spi_message_add_tail(&x[1], &message); } - /* ... unless someone else is using the pre-allocated buffer */ - if (!mutex_trylock(&lock)) { - local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); - if (!local_buf) - return -ENOMEM; - } else - local_buf = buf; - memcpy(local_buf, txbuf, n_tx); x[0].tx_buf = local_buf; x[1].rx_buf = local_buf + n_tx; -- cgit v1.2.3 From 078b6ead72a6486c479f8b95c71dcb3b93abda90 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Wed, 7 Nov 2012 12:40:04 +0100 Subject: spi/sh-msiof: Remove unneeded clock name clk_get() no longer needs a character string for associating the right clock as this is done via the device struct now. Signed-off-by: Bastian Hecht Signed-off-by: Grant Likely --- drivers/spi/spi-sh-msiof.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 1f466bc66d9d..96358d0eabb7 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -597,7 +597,6 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) struct resource *r; struct spi_master *master; struct sh_msiof_spi_priv *p; - char clk_name[16]; int i; int ret; @@ -614,10 +613,9 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) p->info = pdev->dev.platform_data; init_completion(&p->done); - snprintf(clk_name, sizeof(clk_name), "msiof%d", pdev->id); - p->clk = clk_get(&pdev->dev, clk_name); + p->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(p->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + dev_err(&pdev->dev, "cannot get clock\n"); ret = PTR_ERR(p->clk); goto err1; } -- cgit v1.2.3 From 746aeffdd97263d8356870a5dfbbdb5f927378e2 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Wed, 7 Nov 2012 12:40:05 +0100 Subject: spi/sh: Add SH Mobile series as dependency to MSIOF controller The MSIOF hardware block is used in the SH Mobile series as well, so we add it here. Signed-off-by: Bastian Hecht Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 45cb291287c9..2e188e1127eb 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -348,10 +348,10 @@ config SPI_SC18IS602 config SPI_SH_MSIOF tristate "SuperH MSIOF SPI controller" - depends on SUPERH && HAVE_CLK + depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK select SPI_BITBANG help - SPI driver for SuperH MSIOF blocks. + SPI driver for SuperH and SH Mobile MSIOF blocks. config SPI_SH tristate "SuperH SPI controller" -- cgit v1.2.3 From b15d5d7004e25716c8b8dfe4e322a64551e2e6cc Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 21 Nov 2012 12:23:35 -0700 Subject: spi/orion: Add SPI_CHPA and SPI_CPOL support to kirkwood driver. Support these transfer modes from the SPI layer by setting the appropriate register bits before doing the transfer. This was tested on the Marvell kirkwood SOC that uses this driver. Reviewed-by: Jason Gunthorpe Signed-off-by: Rolf Manderscheid Signed-off-by: Grant Likely --- drivers/spi/spi-orion.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index b17c09cf0a05..011186d570fe 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -32,8 +32,12 @@ #define ORION_SPI_DATA_IN_REG 0x0c #define ORION_SPI_INT_CAUSE_REG 0x10 +#define ORION_SPI_MODE_CPOL (1 << 11) +#define ORION_SPI_MODE_CPHA (1 << 12) #define ORION_SPI_IF_8_16_BIT_MODE (1 << 5) #define ORION_SPI_CLK_PRESCALE_MASK 0x1F +#define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \ + ORION_SPI_MODE_CPHA) struct orion_spi { struct spi_master *master; @@ -123,6 +127,23 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) return 0; } +static void +orion_spi_mode_set(struct spi_device *spi) +{ + u32 reg; + struct orion_spi *orion_spi; + + orion_spi = spi_master_get_devdata(spi->master); + + reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); + reg &= ~ORION_SPI_MODE_MASK; + if (spi->mode & SPI_CPOL) + reg |= ORION_SPI_MODE_CPOL; + if (spi->mode & SPI_CPHA) + reg |= ORION_SPI_MODE_CPHA; + writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); +} + /* * called only when no transfer is active on the bus */ @@ -142,6 +163,8 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) if ((t != NULL) && t->bits_per_word) bits_per_word = t->bits_per_word; + orion_spi_mode_set(spi); + rc = orion_spi_baudrate_set(spi, speed); if (rc) return rc; @@ -399,7 +422,7 @@ static int __init orion_spi_probe(struct platform_device *pdev) } /* we support only mode 0, and no options */ - master->mode_bits = 0; + master->mode_bits = SPI_CPHA | SPI_CPOL; master->setup = orion_spi_setup; master->transfer_one_message = orion_spi_transfer_one_message; -- cgit v1.2.3 From 5323f49853ba5f37d9975fe6bd13546c2ec790c8 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 6 Dec 2012 23:28:36 +0000 Subject: spi: Fix comparison of different integer types Fix problem discovered with sparse: + drivers/spi/spi.c:1554:37: sparse: incompatible types in comparison expression (different signedness) drivers/spi/spi.c: In function 'spi_write_then_read': drivers/spi/spi.c:1554:23: warning: comparison of distinct pointer types lacks a cast [enabled by default] The change to SPI_BUFSIZ was introduced in commit b3a223ee2, "spi: Remove SPI_BUFSIZ restriction on spi_write_then_read()" Cc: Mark Brown Reported-by: kbuild test robot Signed-off-by: Grant Likely --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 22c71e238fdf..b370d292d19c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1551,7 +1551,7 @@ int spi_write_then_read(struct spi_device *spi, * using the pre-allocated buffer or the transfer is too large. */ if ((n_tx + n_rx) > SPI_BUFSIZ || !mutex_trylock(&lock)) { - local_buf = kmalloc(max(SPI_BUFSIZ, n_tx + n_rx), GFP_KERNEL); + local_buf = kmalloc(max((unsigned)SPI_BUFSIZ, n_tx + n_rx), GFP_KERNEL); if (!local_buf) return -ENOMEM; } else { -- cgit v1.2.3 From c20151dff8a6d503c0d0cc4387c33a618cdabcb7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 6 Dec 2012 16:55:33 +0100 Subject: spi: Add support for specifying 3-wire mode via device tree This patch allows to specify that a SPI device is connected in 3-wire mode via device tree. Signed-off-by: Lars-Peter Clausen Acked-by: Rob Herring Signed-off-by: Grant Likely --- drivers/spi/spi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b370d292d19c..2ca5f079ff01 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -861,6 +861,8 @@ static void of_register_spi_devices(struct spi_master *master) spi->mode |= SPI_CPOL; if (of_find_property(nc, "spi-cs-high", NULL)) spi->mode |= SPI_CS_HIGH; + if (of_find_property(nc, "spi-3wire", NULL)) + spi->mode |= SPI_3WIRE; /* Device speed */ prop = of_get_property(nc, "spi-max-frequency", &len); -- cgit v1.2.3 From fd4a319bc933ae93e68935b21924a9ca4ba2d060 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 7 Dec 2012 16:57:14 +0000 Subject: spi: Remove HOTPLUG section attributes CONFIG_HOTPLUG is going away as an option. As result the __dev* markings will be going away. Remove use of __devinit, __devexit_p, __devinitdata, __devinitconst, and __devexit. Bill Pemberton has done most of the legwork on this series. I've used his script to purge the attributes from the drivers/gpio tree. Reported-by: Bill Pemberton Signed-off-by: Grant Likely --- drivers/spi/spi-altera.c | 6 +++--- drivers/spi/spi-ath79.c | 6 +++--- drivers/spi/spi-atmel.c | 4 ++-- drivers/spi/spi-bcm63xx.c | 6 +++--- drivers/spi/spi-bfin-sport.c | 8 +++----- drivers/spi/spi-bfin5xx.c | 4 ++-- drivers/spi/spi-clps711x.c | 6 +++--- drivers/spi/spi-coldfire-qspi.c | 6 +++--- drivers/spi/spi-davinci.c | 6 +++--- drivers/spi/spi-dw-mmio.c | 6 +++--- drivers/spi/spi-dw-pci.c | 6 +++--- drivers/spi/spi-dw.c | 6 +++--- drivers/spi/spi-ep93xx.c | 6 +++--- drivers/spi/spi-falcon.c | 6 +++--- drivers/spi/spi-fsl-espi.c | 8 ++++---- drivers/spi/spi-fsl-lib.c | 4 ++-- drivers/spi/spi-fsl-spi.c | 14 +++++++------- drivers/spi/spi-gpio.c | 13 ++++++------- drivers/spi/spi-imx.c | 6 +++--- drivers/spi/spi-mpc512x-psc.c | 10 +++++----- drivers/spi/spi-mpc52xx-psc.c | 8 ++++---- drivers/spi/spi-mpc52xx.c | 8 ++++---- drivers/spi/spi-mxs.c | 6 +++--- drivers/spi/spi-nuc900.c | 6 +++--- drivers/spi/spi-oc-tiny.c | 10 +++++----- drivers/spi/spi-octeon.c | 6 +++--- drivers/spi/spi-omap-100k.c | 2 +- drivers/spi/spi-omap2-mcspi.c | 8 ++++---- drivers/spi/spi-orion.c | 2 +- drivers/spi/spi-pl022.c | 9 ++++----- drivers/spi/spi-pxa2xx-pci.c | 6 +++--- drivers/spi/spi-pxa2xx.c | 4 ++-- drivers/spi/spi-rspi.c | 10 +++++----- drivers/spi/spi-s3c24xx.c | 6 +++--- drivers/spi/spi-s3c64xx.c | 4 ++-- drivers/spi/spi-sh-hspi.c | 6 +++--- drivers/spi/spi-sh.c | 6 +++--- drivers/spi/spi-sirf.c | 6 +++--- drivers/spi/spi-tegra20-sflash.c | 8 ++++---- drivers/spi/spi-tegra20-slink.c | 8 ++++---- drivers/spi/spi-ti-ssp.c | 6 +++--- drivers/spi/spi-tle62x0.c | 6 +++--- drivers/spi/spi-topcliff-pch.c | 12 ++++++------ drivers/spi/spi-xcomm.c | 6 +++--- drivers/spi/spi-xilinx.c | 6 +++--- drivers/spi/spi.c | 3 +-- drivers/spi/spidev.c | 6 +++--- 47 files changed, 153 insertions(+), 158 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index f1fec2a19d10..5e7314ac51e9 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -215,7 +215,7 @@ static irqreturn_t altera_spi_irq(int irq, void *dev) return IRQ_HANDLED; } -static int __devinit altera_spi_probe(struct platform_device *pdev) +static int altera_spi_probe(struct platform_device *pdev) { struct altera_spi_platform_data *platp = pdev->dev.platform_data; struct altera_spi *hw; @@ -290,7 +290,7 @@ exit: return err; } -static int __devexit altera_spi_remove(struct platform_device *dev) +static int altera_spi_remove(struct platform_device *dev) { struct altera_spi *hw = platform_get_drvdata(dev); struct spi_master *master = hw->bitbang.master; @@ -311,7 +311,7 @@ MODULE_DEVICE_TABLE(of, altera_spi_match); static struct platform_driver altera_spi_driver = { .probe = altera_spi_probe, - .remove = __devexit_p(altera_spi_remove), + .remove = altera_spi_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 249077e5cc48..9a5d7791c5fb 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -192,7 +192,7 @@ static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs, return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS); } -static __devinit int ath79_spi_probe(struct platform_device *pdev) +static int ath79_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct ath79_spi *sp; @@ -251,7 +251,7 @@ err_put_master: return ret; } -static __devexit int ath79_spi_remove(struct platform_device *pdev) +static int ath79_spi_remove(struct platform_device *pdev) { struct ath79_spi *sp = platform_get_drvdata(pdev); @@ -265,7 +265,7 @@ static __devexit int ath79_spi_remove(struct platform_device *pdev) static struct platform_driver ath79_spi_driver = { .probe = ath79_spi_probe, - .remove = __devexit_p(ath79_spi_remove), + .remove = ath79_spi_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 16d6a839c7fa..364ee2eb74ec 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -907,7 +907,7 @@ static void atmel_spi_cleanup(struct spi_device *spi) /*-------------------------------------------------------------------------*/ -static int __devinit atmel_spi_probe(struct platform_device *pdev) +static int atmel_spi_probe(struct platform_device *pdev) { struct resource *regs; int irq; @@ -1003,7 +1003,7 @@ out_free: return ret; } -static int __devexit atmel_spi_remove(struct platform_device *pdev) +static int atmel_spi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 6d97047d9242..f44ab5508535 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -329,7 +329,7 @@ static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) } -static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) +static int bcm63xx_spi_probe(struct platform_device *pdev) { struct resource *r; struct device *dev = &pdev->dev; @@ -449,7 +449,7 @@ out: return ret; } -static int __devexit bcm63xx_spi_remove(struct platform_device *pdev) +static int bcm63xx_spi_remove(struct platform_device *pdev) { struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct bcm63xx_spi *bs = spi_master_get_devdata(master); @@ -514,7 +514,7 @@ static struct platform_driver bcm63xx_spi_driver = { .pm = BCM63XX_SPI_PM_OPS, }, .probe = bcm63xx_spi_probe, - .remove = __devexit_p(bcm63xx_spi_remove), + .remove = bcm63xx_spi_remove, }; module_platform_driver(bcm63xx_spi_driver); diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 6555ecd07302..ac7ffca7ba47 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -755,8 +755,7 @@ bfin_sport_spi_destroy_queue(struct bfin_sport_spi_master_data *drv_data) return 0; } -static int __devinit -bfin_sport_spi_probe(struct platform_device *pdev) +static int bfin_sport_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct bfin5xx_spi_master *platform_info; @@ -863,8 +862,7 @@ bfin_sport_spi_probe(struct platform_device *pdev) } /* stop hardware and remove the driver */ -static int __devexit -bfin_sport_spi_remove(struct platform_device *pdev) +static int bfin_sport_spi_remove(struct platform_device *pdev) { struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev); int status = 0; @@ -935,7 +933,7 @@ static struct platform_driver bfin_sport_spi_driver = { .owner = THIS_MODULE, }, .probe = bfin_sport_spi_probe, - .remove = __devexit_p(bfin_sport_spi_remove), + .remove = bfin_sport_spi_remove, .suspend = bfin_sport_spi_suspend, .resume = bfin_sport_spi_resume, }; diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 9bb4d4af8547..0429d833f75b 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -1387,7 +1387,7 @@ out_error_get_res: } /* stop hardware and remove the driver */ -static int __devexit bfin_spi_remove(struct platform_device *pdev) +static int bfin_spi_remove(struct platform_device *pdev) { struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); int status = 0; @@ -1477,7 +1477,7 @@ static struct platform_driver bfin_spi_driver = { }, .suspend = bfin_spi_suspend, .resume = bfin_spi_resume, - .remove = __devexit_p(bfin_spi_remove), + .remove = bfin_spi_remove, }; static int __init bfin_spi_init(void) diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 59677eb30f94..1366c4620d5d 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -163,7 +163,7 @@ static irqreturn_t spi_clps711x_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit spi_clps711x_probe(struct platform_device *pdev) +static int spi_clps711x_probe(struct platform_device *pdev) { int i, ret; struct spi_master *master; @@ -261,7 +261,7 @@ err_out: return ret; } -static int __devexit spi_clps711x_remove(struct platform_device *pdev) +static int spi_clps711x_remove(struct platform_device *pdev) { int i; struct spi_master *master = platform_get_drvdata(pdev); @@ -287,7 +287,7 @@ static struct platform_driver clps711x_spi_driver = { .owner = THIS_MODULE, }, .probe = spi_clps711x_probe, - .remove = __devexit_p(spi_clps711x_remove), + .remove = spi_clps711x_remove, }; module_platform_driver(clps711x_spi_driver); diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 764bfee75920..58466b810da4 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -401,7 +401,7 @@ static int mcfqspi_setup(struct spi_device *spi) return 0; } -static int __devinit mcfqspi_probe(struct platform_device *pdev) +static int mcfqspi_probe(struct platform_device *pdev) { struct spi_master *master; struct mcfqspi *mcfqspi; @@ -515,7 +515,7 @@ fail0: return status; } -static int __devexit mcfqspi_remove(struct platform_device *pdev) +static int mcfqspi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct mcfqspi *mcfqspi = spi_master_get_devdata(master); @@ -594,7 +594,7 @@ static struct platform_driver mcfqspi_driver = { .driver.owner = THIS_MODULE, .driver.pm = &mcfqspi_pm, .probe = mcfqspi_probe, - .remove = __devexit_p(mcfqspi_remove), + .remove = mcfqspi_remove, }; module_platform_driver(mcfqspi_driver); diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 147dfa87a64b..13661e129d96 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -769,7 +769,7 @@ rx_dma_failed: * It will invoke spi_bitbang_start to create work queue so that client driver * can register transfer method to work queue. */ -static int __devinit davinci_spi_probe(struct platform_device *pdev) +static int davinci_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct davinci_spi *dspi; @@ -952,7 +952,7 @@ err: * It will also call spi_bitbang_stop to destroy the work queue which was * created by spi_bitbang_start. */ -static int __devexit davinci_spi_remove(struct platform_device *pdev) +static int davinci_spi_remove(struct platform_device *pdev) { struct davinci_spi *dspi; struct spi_master *master; @@ -980,7 +980,7 @@ static struct platform_driver davinci_spi_driver = { .owner = THIS_MODULE, }, .probe = davinci_spi_probe, - .remove = __devexit_p(davinci_spi_remove), + .remove = davinci_spi_remove, }; module_platform_driver(davinci_spi_driver); diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index db2f1ba06eab..4a6d5c9057a4 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -26,7 +26,7 @@ struct dw_spi_mmio { struct clk *clk; }; -static int __devinit dw_spi_mmio_probe(struct platform_device *pdev) +static int dw_spi_mmio_probe(struct platform_device *pdev) { struct dw_spi_mmio *dwsmmio; struct dw_spi *dws; @@ -106,7 +106,7 @@ err_end: return ret; } -static int __devexit dw_spi_mmio_remove(struct platform_device *pdev) +static int dw_spi_mmio_remove(struct platform_device *pdev) { struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev); struct resource *mem; @@ -129,7 +129,7 @@ static int __devexit dw_spi_mmio_remove(struct platform_device *pdev) static struct platform_driver dw_spi_mmio_driver = { .probe = dw_spi_mmio_probe, - .remove = __devexit_p(dw_spi_mmio_remove), + .remove = dw_spi_mmio_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index ff81abbb3066..6055c8d9fdd7 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -32,7 +32,7 @@ struct dw_spi_pci { struct dw_spi dws; }; -static int __devinit spi_pci_probe(struct pci_dev *pdev, +static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct dw_spi_pci *dwpci; @@ -105,7 +105,7 @@ err_disable: return ret; } -static void __devexit spi_pci_remove(struct pci_dev *pdev) +static void spi_pci_remove(struct pci_dev *pdev) { struct dw_spi_pci *dwpci = pci_get_drvdata(pdev); @@ -159,7 +159,7 @@ static struct pci_driver dw_spi_driver = { .name = DRIVER_NAME, .id_table = pci_ids, .probe = spi_pci_probe, - .remove = __devexit_p(spi_pci_remove), + .remove = spi_pci_remove, .suspend = spi_suspend, .resume = spi_resume, }; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index d1a495f64e2d..c1abc06899e7 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -696,7 +696,7 @@ static void dw_spi_cleanup(struct spi_device *spi) kfree(chip); } -static int __devinit init_queue(struct dw_spi *dws) +static int init_queue(struct dw_spi *dws) { INIT_LIST_HEAD(&dws->queue); spin_lock_init(&dws->lock); @@ -795,7 +795,7 @@ static void spi_hw_init(struct dw_spi *dws) } } -int __devinit dw_spi_add_host(struct dw_spi *dws) +int dw_spi_add_host(struct dw_spi *dws) { struct spi_master *master; int ret; @@ -877,7 +877,7 @@ exit: } EXPORT_SYMBOL_GPL(dw_spi_add_host); -void __devexit dw_spi_remove_host(struct dw_spi *dws) +void dw_spi_remove_host(struct dw_spi *dws) { int status = 0; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 3a219599612a..acb1e1935c5a 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -1023,7 +1023,7 @@ static void ep93xx_spi_release_dma(struct ep93xx_spi *espi) free_page((unsigned long)espi->zeropage); } -static int __devinit ep93xx_spi_probe(struct platform_device *pdev) +static int ep93xx_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct ep93xx_spi_info *info; @@ -1138,7 +1138,7 @@ fail_release_master: return error; } -static int __devexit ep93xx_spi_remove(struct platform_device *pdev) +static int ep93xx_spi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct ep93xx_spi *espi = spi_master_get_devdata(master); @@ -1180,7 +1180,7 @@ static struct platform_driver ep93xx_spi_driver = { .owner = THIS_MODULE, }, .probe = ep93xx_spi_probe, - .remove = __devexit_p(ep93xx_spi_remove), + .remove = ep93xx_spi_remove, }; module_platform_driver(ep93xx_spi_driver); diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index 8f6aa735a24c..6a6f62ec2840 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -403,7 +403,7 @@ static int falcon_sflash_xfer_one(struct spi_master *master, return 0; } -static int __devinit falcon_sflash_probe(struct platform_device *pdev) +static int falcon_sflash_probe(struct platform_device *pdev) { struct falcon_sflash *priv; struct spi_master *master; @@ -438,7 +438,7 @@ static int __devinit falcon_sflash_probe(struct platform_device *pdev) return ret; } -static int __devexit falcon_sflash_remove(struct platform_device *pdev) +static int falcon_sflash_remove(struct platform_device *pdev) { struct falcon_sflash *priv = platform_get_drvdata(pdev); @@ -455,7 +455,7 @@ MODULE_DEVICE_TABLE(of, falcon_sflash_match); static struct platform_driver falcon_sflash_driver = { .probe = falcon_sflash_probe, - .remove = __devexit_p(falcon_sflash_remove), + .remove = falcon_sflash_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 27bdc47b5250..24610ca8955d 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -587,7 +587,7 @@ static void fsl_espi_remove(struct mpc8xxx_spi *mspi) iounmap(mspi->reg_base); } -static struct spi_master * __devinit fsl_espi_probe(struct device *dev, +static struct spi_master * fsl_espi_probe(struct device *dev, struct resource *mem, unsigned int irq) { struct fsl_spi_platform_data *pdata = dev->platform_data; @@ -686,7 +686,7 @@ static int of_fsl_espi_get_chipselects(struct device *dev) return 0; } -static int __devinit of_fsl_espi_probe(struct platform_device *ofdev) +static int of_fsl_espi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; @@ -725,7 +725,7 @@ err: return ret; } -static int __devexit of_fsl_espi_remove(struct platform_device *dev) +static int of_fsl_espi_remove(struct platform_device *dev) { return mpc8xxx_spi_remove(&dev->dev); } @@ -743,7 +743,7 @@ static struct platform_driver fsl_espi_driver = { .of_match_table = of_fsl_espi_match, }, .probe = of_fsl_espi_probe, - .remove = __devexit_p(of_fsl_espi_remove), + .remove = of_fsl_espi_remove, }; module_platform_driver(fsl_espi_driver); diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index 1503574b215a..8ade675a04f1 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -169,7 +169,7 @@ err: return ret; } -int __devexit mpc8xxx_spi_remove(struct device *dev) +int mpc8xxx_spi_remove(struct device *dev) { struct mpc8xxx_spi *mpc8xxx_spi; struct spi_master *master; @@ -189,7 +189,7 @@ int __devexit mpc8xxx_spi_remove(struct device *dev) return 0; } -int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev) +int of_mpc8xxx_spi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 6a62934ca74c..1a7f6359d998 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -843,7 +843,7 @@ static void fsl_spi_remove(struct mpc8xxx_spi *mspi) fsl_spi_cpm_free(mspi); } -static struct spi_master * __devinit fsl_spi_probe(struct device *dev, +static struct spi_master * fsl_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { struct fsl_spi_platform_data *pdata = dev->platform_data; @@ -1041,7 +1041,7 @@ static int of_fsl_spi_free_chipselects(struct device *dev) return 0; } -static int __devinit of_fsl_spi_probe(struct platform_device *ofdev) +static int of_fsl_spi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; @@ -1081,7 +1081,7 @@ err: return ret; } -static int __devexit of_fsl_spi_remove(struct platform_device *ofdev) +static int of_fsl_spi_remove(struct platform_device *ofdev) { int ret; @@ -1105,7 +1105,7 @@ static struct platform_driver of_fsl_spi_driver = { .of_match_table = of_fsl_spi_match, }, .probe = of_fsl_spi_probe, - .remove = __devexit_p(of_fsl_spi_remove), + .remove = of_fsl_spi_remove, }; #ifdef CONFIG_MPC832x_RDB @@ -1116,7 +1116,7 @@ static struct platform_driver of_fsl_spi_driver = { * tree can work with OpenFirmware driver. But for now we support old trees * as well. */ -static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev) +static int plat_mpc8xxx_spi_probe(struct platform_device *pdev) { struct resource *mem; int irq; @@ -1139,7 +1139,7 @@ static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev) return 0; } -static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev) +static int plat_mpc8xxx_spi_remove(struct platform_device *pdev) { return mpc8xxx_spi_remove(&pdev->dev); } @@ -1147,7 +1147,7 @@ static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev) MODULE_ALIAS("platform:mpc8xxx_spi"); static struct platform_driver mpc8xxx_spi_driver = { .probe = plat_mpc8xxx_spi_probe, - .remove = __devexit_p(plat_mpc8xxx_spi_remove), + .remove = plat_mpc8xxx_spi_remove, .driver = { .name = "mpc8xxx_spi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index a2b50c516b31..c7cf0b7a069b 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -287,7 +287,7 @@ static void spi_gpio_cleanup(struct spi_device *spi) spi_bitbang_cleanup(spi); } -static int __devinit spi_gpio_alloc(unsigned pin, const char *label, bool is_in) +static int spi_gpio_alloc(unsigned pin, const char *label, bool is_in) { int value; @@ -301,9 +301,8 @@ static int __devinit spi_gpio_alloc(unsigned pin, const char *label, bool is_in) return value; } -static int __devinit -spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label, - u16 *res_flags) +static int spi_gpio_request(struct spi_gpio_platform_data *pdata, + const char *label, u16 *res_flags) { int value; @@ -392,7 +391,7 @@ static inline int spi_gpio_probe_dt(struct platform_device *pdev) } #endif -static int __devinit spi_gpio_probe(struct platform_device *pdev) +static int spi_gpio_probe(struct platform_device *pdev) { int status; struct spi_master *master; @@ -485,7 +484,7 @@ gpio_free: return status; } -static int __devexit spi_gpio_remove(struct platform_device *pdev) +static int spi_gpio_remove(struct platform_device *pdev) { struct spi_gpio *spi_gpio; struct spi_gpio_platform_data *pdata; @@ -518,7 +517,7 @@ static struct platform_driver spi_gpio_driver = { .of_match_table = of_match_ptr(spi_gpio_dt_ids), }, .probe = spi_gpio_probe, - .remove = __devexit_p(spi_gpio_remove), + .remove = spi_gpio_remove, }; module_platform_driver(spi_gpio_driver); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index c9a0d8467de6..904913290aa5 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -750,7 +750,7 @@ static void spi_imx_cleanup(struct spi_device *spi) { } -static int __devinit spi_imx_probe(struct platform_device *pdev) +static int spi_imx_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id = @@ -906,7 +906,7 @@ out_gpio_free: return ret; } -static int __devexit spi_imx_remove(struct platform_device *pdev) +static int spi_imx_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -942,7 +942,7 @@ static struct platform_driver spi_imx_driver = { }, .id_table = spi_imx_devtype, .probe = spi_imx_probe, - .remove = __devexit_p(spi_imx_remove), + .remove = spi_imx_remove, }; module_platform_driver(spi_imx_driver); diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 0a1e39e94d06..cb3a3106bd4f 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -406,7 +406,7 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id) } /* bus_num is used only for the case dev->platform_data == NULL */ -static int __devinit mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, +static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, u32 size, unsigned int irq, s16 bus_num) { @@ -492,7 +492,7 @@ free_master: return ret; } -static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) +static int mpc512x_psc_spi_do_remove(struct device *dev) { struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); @@ -508,7 +508,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) return 0; } -static int __devinit mpc512x_psc_spi_of_probe(struct platform_device *op) +static int mpc512x_psc_spi_of_probe(struct platform_device *op) { const u32 *regaddr_p; u64 regaddr64, size64; @@ -539,7 +539,7 @@ static int __devinit mpc512x_psc_spi_of_probe(struct platform_device *op) irq_of_parse_and_map(op->dev.of_node, 0), id); } -static int __devexit mpc512x_psc_spi_of_remove(struct platform_device *op) +static int mpc512x_psc_spi_of_remove(struct platform_device *op) { return mpc512x_psc_spi_do_remove(&op->dev); } @@ -553,7 +553,7 @@ MODULE_DEVICE_TABLE(of, mpc512x_psc_spi_of_match); static struct platform_driver mpc512x_psc_spi_of_driver = { .probe = mpc512x_psc_spi_of_probe, - .remove = __devexit_p(mpc512x_psc_spi_of_remove), + .remove = mpc512x_psc_spi_of_remove, .driver = { .name = "mpc512x-psc-spi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index bd47d262d53f..291120b37dbb 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -363,7 +363,7 @@ static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id) } /* bus_num is used only for the case dev->platform_data == NULL */ -static int __devinit mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, +static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, u32 size, unsigned int irq, s16 bus_num) { struct fsl_spi_platform_data *pdata = dev->platform_data; @@ -450,7 +450,7 @@ free_master: return ret; } -static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op) +static int mpc52xx_psc_spi_of_probe(struct platform_device *op) { const u32 *regaddr_p; u64 regaddr64, size64; @@ -479,7 +479,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op) irq_of_parse_and_map(op->dev.of_node, 0), id); } -static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op) +static int mpc52xx_psc_spi_of_remove(struct platform_device *op) { struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); @@ -505,7 +505,7 @@ MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match); static struct platform_driver mpc52xx_psc_spi_of_driver = { .probe = mpc52xx_psc_spi_of_probe, - .remove = __devexit_p(mpc52xx_psc_spi_of_remove), + .remove = mpc52xx_psc_spi_of_remove, .driver = { .name = "mpc52xx-psc-spi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 045410650212..29f77056eedc 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -390,7 +390,7 @@ static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m) /* * OF Platform Bus Binding */ -static int __devinit mpc52xx_spi_probe(struct platform_device *op) +static int mpc52xx_spi_probe(struct platform_device *op) { struct spi_master *master; struct mpc52xx_spi *ms; @@ -527,7 +527,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) return rc; } -static int __devexit mpc52xx_spi_remove(struct platform_device *op) +static int mpc52xx_spi_remove(struct platform_device *op) { struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); struct mpc52xx_spi *ms = spi_master_get_devdata(master); @@ -547,7 +547,7 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op) return 0; } -static const struct of_device_id mpc52xx_spi_match[] __devinitconst = { +static const struct of_device_id mpc52xx_spi_match[] = { { .compatible = "fsl,mpc5200-spi", }, {} }; @@ -560,6 +560,6 @@ static struct platform_driver mpc52xx_spi_of_driver = { .of_match_table = mpc52xx_spi_match, }, .probe = mpc52xx_spi_probe, - .remove = __devexit_p(mpc52xx_spi_remove), + .remove = mpc52xx_spi_remove, }; module_platform_driver(mpc52xx_spi_of_driver); diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 86dd04d6bc87..a3ede249d05d 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -509,7 +509,7 @@ static const struct of_device_id mxs_spi_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mxs_spi_dt_ids); -static int __devinit mxs_spi_probe(struct platform_device *pdev) +static int mxs_spi_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(mxs_spi_dt_ids, &pdev->dev); @@ -636,7 +636,7 @@ out_master_free: return ret; } -static int __devexit mxs_spi_remove(struct platform_device *pdev) +static int mxs_spi_remove(struct platform_device *pdev) { struct spi_master *master; struct mxs_spi *spi; @@ -659,7 +659,7 @@ static int __devexit mxs_spi_remove(struct platform_device *pdev) static struct platform_driver mxs_spi_driver = { .probe = mxs_spi_probe, - .remove = __devexit_p(mxs_spi_remove), + .remove = mxs_spi_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index a6eca6ffdabe..b3f9ec83ef73 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -346,7 +346,7 @@ static void nuc900_init_spi(struct nuc900_spi *hw) nuc900_enable_int(hw); } -static int __devinit nuc900_spi_probe(struct platform_device *pdev) +static int nuc900_spi_probe(struct platform_device *pdev) { struct nuc900_spi *hw; struct spi_master *master; @@ -453,7 +453,7 @@ err_nomem: return err; } -static int __devexit nuc900_spi_remove(struct platform_device *dev) +static int nuc900_spi_remove(struct platform_device *dev) { struct nuc900_spi *hw = platform_get_drvdata(dev); @@ -477,7 +477,7 @@ static int __devexit nuc900_spi_remove(struct platform_device *dev) static struct platform_driver nuc900_spi_driver = { .probe = nuc900_spi_probe, - .remove = __devexit_p(nuc900_spi_remove), + .remove = nuc900_spi_remove, .driver = { .name = "nuc900-spi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index 9d9071b730be..432e66ec3088 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -243,7 +243,7 @@ static irqreturn_t tiny_spi_irq(int irq, void *dev) #ifdef CONFIG_OF #include -static int __devinit tiny_spi_of_probe(struct platform_device *pdev) +static int tiny_spi_of_probe(struct platform_device *pdev) { struct tiny_spi *hw = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; @@ -277,13 +277,13 @@ static int __devinit tiny_spi_of_probe(struct platform_device *pdev) return 0; } #else /* !CONFIG_OF */ -static int __devinit tiny_spi_of_probe(struct platform_device *pdev) +static int tiny_spi_of_probe(struct platform_device *pdev) { return 0; } #endif /* CONFIG_OF */ -static int __devinit tiny_spi_probe(struct platform_device *pdev) +static int tiny_spi_probe(struct platform_device *pdev) { struct tiny_spi_platform_data *platp = pdev->dev.platform_data; struct tiny_spi *hw; @@ -373,7 +373,7 @@ exit: return err; } -static int __devexit tiny_spi_remove(struct platform_device *pdev) +static int tiny_spi_remove(struct platform_device *pdev) { struct tiny_spi *hw = platform_get_drvdata(pdev); struct spi_master *master = hw->bitbang.master; @@ -399,7 +399,7 @@ MODULE_DEVICE_TABLE(of, tiny_spi_match); static struct platform_driver tiny_spi_driver = { .probe = tiny_spi_probe, - .remove = __devexit_p(tiny_spi_remove), + .remove = tiny_spi_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index ea8fb2efb0f8..24daf964a409 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -266,7 +266,7 @@ static int octeon_spi_nop_transfer_hardware(struct spi_master *master) return 0; } -static int __devinit octeon_spi_probe(struct platform_device *pdev) +static int octeon_spi_probe(struct platform_device *pdev) { struct resource *res_mem; @@ -326,7 +326,7 @@ fail: return err; } -static int __devexit octeon_spi_remove(struct platform_device *pdev) +static int octeon_spi_remove(struct platform_device *pdev) { struct octeon_spi *p = platform_get_drvdata(pdev); u64 register_base = p->register_base; @@ -352,7 +352,7 @@ static struct platform_driver octeon_spi_driver = { .of_match_table = octeon_spi_match, }, .probe = octeon_spi_probe, - .remove = __devexit_p(octeon_spi_remove), + .remove = octeon_spi_remove, }; module_platform_driver(octeon_spi_driver); diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index dfb4b7f448c5..3aef7fa7d5b8 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -486,7 +486,7 @@ static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k) return 0; } -static int __devinit omap1_spi100k_probe(struct platform_device *pdev) +static int omap1_spi100k_probe(struct platform_device *pdev) { struct spi_master *master; struct omap1_spi100k *spi100k; diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 850138455259..b610f522ca44 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1088,7 +1088,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, return 0; } -static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) +static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) { struct spi_master *master = mcspi->master; struct omap2_mcspi_regs *ctx = &mcspi->ctx; @@ -1141,7 +1141,7 @@ static const struct of_device_id omap_mcspi_of_match[] = { }; MODULE_DEVICE_TABLE(of, omap_mcspi_of_match); -static int __devinit omap2_mcspi_probe(struct platform_device *pdev) +static int omap2_mcspi_probe(struct platform_device *pdev) { struct spi_master *master; const struct omap2_mcspi_platform_config *pdata; @@ -1278,7 +1278,7 @@ free_master: return status; } -static int __devexit omap2_mcspi_remove(struct platform_device *pdev) +static int omap2_mcspi_remove(struct platform_device *pdev) { struct spi_master *master; struct omap2_mcspi *mcspi; @@ -1347,7 +1347,7 @@ static struct platform_driver omap2_mcspi_driver = { .of_match_table = omap_mcspi_of_match, }, .probe = omap2_mcspi_probe, - .remove = __devexit_p(omap2_mcspi_remove), + .remove = omap2_mcspi_remove, }; module_platform_driver(omap2_mcspi_driver); diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 011186d570fe..b7e718254b1d 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -501,7 +501,7 @@ static int __exit orion_spi_remove(struct platform_device *pdev) MODULE_ALIAS("platform:" DRIVER_NAME); -static const struct of_device_id orion_spi_of_match_table[] __devinitdata = { +static const struct of_device_id orion_spi_of_match_table[] = { { .compatible = "marvell,orion-spi", }, {} }; diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 1361868fced7..b0fe393c882c 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1089,7 +1089,7 @@ err_alloc_rx_sg: return -ENOMEM; } -static int __devinit pl022_dma_probe(struct pl022 *pl022) +static int pl022_dma_probe(struct pl022 *pl022) { dma_cap_mask_t mask; @@ -2058,8 +2058,7 @@ pl022_platform_data_dt_get(struct device *dev) return pd; } -static int __devinit -pl022_probe(struct amba_device *adev, const struct amba_id *id) +static int pl022_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; struct pl022_ssp_controller *platform_info = adev->dev.platform_data; @@ -2275,7 +2274,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) return status; } -static int __devexit +static int pl022_remove(struct amba_device *adev) { struct pl022 *pl022 = amba_get_drvdata(adev); @@ -2484,7 +2483,7 @@ static struct amba_driver pl022_driver = { }, .id_table = pl022_ids, .probe = pl022_probe, - .remove = __devexit_p(pl022_remove), + .remove = pl022_remove, }; static int __init pl022_init(void) diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 9f6ba34b172c..cf95587eefde 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -51,7 +51,7 @@ void pxa_ssp_free(struct ssp_device *ssp) } EXPORT_SYMBOL_GPL(pxa_ssp_free); -static int __devinit ce4100_spi_probe(struct pci_dev *dev, +static int ce4100_spi_probe(struct pci_dev *dev, const struct pci_device_id *ent) { int ret; @@ -129,7 +129,7 @@ err_nomem: return ret; } -static void __devexit ce4100_spi_remove(struct pci_dev *dev) +static void ce4100_spi_remove(struct pci_dev *dev) { struct ce4100_info *spi_info; struct ssp_device *ssp; @@ -161,7 +161,7 @@ static struct pci_driver ce4100_spi_driver = { .name = "ce4100_spi", .id_table = ce4100_spi_devices, .probe = ce4100_spi_probe, - .remove = __devexit_p(ce4100_spi_remove), + .remove = ce4100_spi_remove, }; module_pci_driver(ce4100_spi_driver); diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index dc25bee8d33f..5c8c4f5883c4 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1438,7 +1438,7 @@ static void cleanup(struct spi_device *spi) kfree(chip); } -static int __devinit init_queue(struct driver_data *drv_data) +static int init_queue(struct driver_data *drv_data) { INIT_LIST_HEAD(&drv_data->queue); spin_lock_init(&drv_data->lock); @@ -1526,7 +1526,7 @@ static int destroy_queue(struct driver_data *drv_data) return 0; } -static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) +static int pxa2xx_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pxa2xx_spi_master *platform_info; diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 30faf6d4ab91..902f2fb902db 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -661,7 +661,7 @@ static irqreturn_t rspi_irq(int irq, void *_sr) return ret; } -static int __devinit rspi_request_dma(struct rspi_data *rspi, +static int rspi_request_dma(struct rspi_data *rspi, struct platform_device *pdev) { struct rspi_plat_data *rspi_pd = pdev->dev.platform_data; @@ -709,7 +709,7 @@ static int __devinit rspi_request_dma(struct rspi_data *rspi, return 0; } -static void __devexit rspi_release_dma(struct rspi_data *rspi) +static void rspi_release_dma(struct rspi_data *rspi) { if (rspi->chan_tx) dma_release_channel(rspi->chan_tx); @@ -717,7 +717,7 @@ static void __devexit rspi_release_dma(struct rspi_data *rspi) dma_release_channel(rspi->chan_rx); } -static int __devexit rspi_remove(struct platform_device *pdev) +static int rspi_remove(struct platform_device *pdev) { struct rspi_data *rspi = dev_get_drvdata(&pdev->dev); @@ -731,7 +731,7 @@ static int __devexit rspi_remove(struct platform_device *pdev) return 0; } -static int __devinit rspi_probe(struct platform_device *pdev) +static int rspi_probe(struct platform_device *pdev) { struct resource *res; struct spi_master *master; @@ -827,7 +827,7 @@ error1: static struct platform_driver rspi_driver = { .probe = rspi_probe, - .remove = __devexit_p(rspi_remove), + .remove = rspi_remove, .driver = { .name = "rspi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index a2a080b7f42b..02d64603fcc5 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -506,7 +506,7 @@ static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw) } } -static int __devinit s3c24xx_spi_probe(struct platform_device *pdev) +static int s3c24xx_spi_probe(struct platform_device *pdev) { struct s3c2410_spi_info *pdata; struct s3c24xx_spi *hw; @@ -663,7 +663,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev) return err; } -static int __devexit s3c24xx_spi_remove(struct platform_device *dev) +static int s3c24xx_spi_remove(struct platform_device *dev) { struct s3c24xx_spi *hw = platform_get_drvdata(dev); @@ -722,7 +722,7 @@ static const struct dev_pm_ops s3c24xx_spi_pmops = { MODULE_ALIAS("platform:s3c2410-spi"); static struct platform_driver s3c24xx_spi_driver = { .probe = s3c24xx_spi_probe, - .remove = __devexit_p(s3c24xx_spi_remove), + .remove = s3c24xx_spi_remove, .driver = { .name = "s3c2410-spi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 57900a810bf2..4dd7b7ce5c5a 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1056,7 +1056,7 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) flush_fifo(sdd); } -static int __devinit s3c64xx_spi_get_dmares( +static int s3c64xx_spi_get_dmares( struct s3c64xx_spi_driver_data *sdd, bool tx) { struct platform_device *pdev = sdd->pdev; @@ -1135,7 +1135,7 @@ static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd) gpio_free(sdd->gpios[idx]); } -static struct __devinit s3c64xx_spi_info * s3c64xx_spi_parse_dt( +static struct s3c64xx_spi_info * s3c64xx_spi_parse_dt( struct device *dev) { struct s3c64xx_spi_info *sci; diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 47c4beee8a0e..32f7b55fce09 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -268,7 +268,7 @@ static void hspi_cleanup(struct spi_device *spi) dev_dbg(dev, "%s cleanup\n", spi->modalias); } -static int __devinit hspi_probe(struct platform_device *pdev) +static int hspi_probe(struct platform_device *pdev) { struct resource *res; struct spi_master *master; @@ -339,7 +339,7 @@ static int __devinit hspi_probe(struct platform_device *pdev) return ret; } -static int __devexit hspi_remove(struct platform_device *pdev) +static int hspi_remove(struct platform_device *pdev) { struct hspi_priv *hspi = dev_get_drvdata(&pdev->dev); @@ -353,7 +353,7 @@ static int __devexit hspi_remove(struct platform_device *pdev) static struct platform_driver hspi_driver = { .probe = hspi_probe, - .remove = __devexit_p(hspi_remove), + .remove = hspi_remove, .driver = { .name = "sh-hspi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index 79442c31bcd9..3c3600a994bd 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -432,7 +432,7 @@ static irqreturn_t spi_sh_irq(int irq, void *_ss) return IRQ_HANDLED; } -static int __devexit spi_sh_remove(struct platform_device *pdev) +static int spi_sh_remove(struct platform_device *pdev) { struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev); @@ -444,7 +444,7 @@ static int __devexit spi_sh_remove(struct platform_device *pdev) return 0; } -static int __devinit spi_sh_probe(struct platform_device *pdev) +static int spi_sh_probe(struct platform_device *pdev) { struct resource *res; struct spi_master *master; @@ -539,7 +539,7 @@ static int __devinit spi_sh_probe(struct platform_device *pdev) static struct platform_driver spi_sh_driver = { .probe = spi_sh_probe, - .remove = __devexit_p(spi_sh_remove), + .remove = spi_sh_remove, .driver = { .name = "sh_spi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index ecc3d9763d10..e0f43a512e84 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -479,7 +479,7 @@ static int spi_sirfsoc_setup(struct spi_device *spi) return spi_sirfsoc_setup_transfer(spi, NULL); } -static int __devinit spi_sirfsoc_probe(struct platform_device *pdev) +static int spi_sirfsoc_probe(struct platform_device *pdev) { struct sirfsoc_spi *sspi; struct spi_master *master; @@ -604,7 +604,7 @@ err_cs: return ret; } -static int __devexit spi_sirfsoc_remove(struct platform_device *pdev) +static int spi_sirfsoc_remove(struct platform_device *pdev) { struct spi_master *master; struct sirfsoc_spi *sspi; @@ -673,7 +673,7 @@ static struct platform_driver spi_sirfsoc_driver = { .of_match_table = spi_sirfsoc_of_match, }, .probe = spi_sirfsoc_probe, - .remove = __devexit_p(spi_sirfsoc_remove), + .remove = spi_sirfsoc_remove, }; module_platform_driver(spi_sirfsoc_driver); diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 54eb9488fa5a..448a8cc71df3 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -451,13 +451,13 @@ static struct tegra_spi_platform_data *tegra_sflash_parse_dt( return pdata; } -static struct of_device_id tegra_sflash_of_match[] __devinitconst = { +static struct of_device_id tegra_sflash_of_match[] = { { .compatible = "nvidia,tegra20-sflash", }, {} }; MODULE_DEVICE_TABLE(of, tegra_sflash_of_match); -static int __devinit tegra_sflash_probe(struct platform_device *pdev) +static int tegra_sflash_probe(struct platform_device *pdev) { struct spi_master *master; struct tegra_sflash_data *tsd; @@ -575,7 +575,7 @@ exit_free_master: return ret; } -static int __devexit tegra_sflash_remove(struct platform_device *pdev) +static int tegra_sflash_remove(struct platform_device *pdev) { struct spi_master *master = dev_get_drvdata(&pdev->dev); struct tegra_sflash_data *tsd = spi_master_get_devdata(master); @@ -655,7 +655,7 @@ static struct platform_driver tegra_sflash_driver = { .of_match_table = of_match_ptr(tegra_sflash_of_match), }, .probe = tegra_sflash_probe, - .remove = __devexit_p(tegra_sflash_remove), + .remove = tegra_sflash_remove, }; module_platform_driver(tegra_sflash_driver); diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 7882b50329e2..651167f2e0af 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1108,14 +1108,14 @@ const struct tegra_slink_chip_data tegra20_spi_cdata = { .cs_hold_time = false, }; -static struct of_device_id tegra_slink_of_match[] __devinitconst = { +static struct of_device_id tegra_slink_of_match[] = { { .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, }, { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, }, {} }; MODULE_DEVICE_TABLE(of, tegra_slink_of_match); -static int __devinit tegra_slink_probe(struct platform_device *pdev) +static int tegra_slink_probe(struct platform_device *pdev) { struct spi_master *master; struct tegra_slink_data *tspi; @@ -1261,7 +1261,7 @@ exit_free_master: return ret; } -static int __devexit tegra_slink_remove(struct platform_device *pdev) +static int tegra_slink_remove(struct platform_device *pdev) { struct spi_master *master = dev_get_drvdata(&pdev->dev); struct tegra_slink_data *tspi = spi_master_get_devdata(master); @@ -1348,7 +1348,7 @@ static struct platform_driver tegra_slink_driver = { .of_match_table = of_match_ptr(tegra_slink_of_match), }, .probe = tegra_slink_probe, - .remove = __devexit_p(tegra_slink_remove), + .remove = tegra_slink_remove, }; module_platform_driver(tegra_slink_driver); diff --git a/drivers/spi/spi-ti-ssp.c b/drivers/spi/spi-ti-ssp.c index 3f6f6e81c655..46992cab65f1 100644 --- a/drivers/spi/spi-ti-ssp.c +++ b/drivers/spi/spi-ti-ssp.c @@ -289,7 +289,7 @@ error_unlock: return error; } -static int __devinit ti_ssp_spi_probe(struct platform_device *pdev) +static int ti_ssp_spi_probe(struct platform_device *pdev) { const struct ti_ssp_spi_data *pdata; struct ti_ssp_spi *hw; @@ -357,7 +357,7 @@ error_wq: return error; } -static int __devexit ti_ssp_spi_remove(struct platform_device *pdev) +static int ti_ssp_spi_remove(struct platform_device *pdev) { struct ti_ssp_spi *hw = platform_get_drvdata(pdev); int error; @@ -378,7 +378,7 @@ static int __devexit ti_ssp_spi_remove(struct platform_device *pdev) static struct platform_driver ti_ssp_spi_driver = { .probe = ti_ssp_spi_probe, - .remove = __devexit_p(ti_ssp_spi_remove), + .remove = ti_ssp_spi_remove, .driver = { .name = "ti-ssp-spi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c index 24421024deaf..6b0874d782ed 100644 --- a/drivers/spi/spi-tle62x0.c +++ b/drivers/spi/spi-tle62x0.c @@ -240,7 +240,7 @@ static int to_gpio_num(struct device_attribute *attr) return -1; } -static int __devinit tle62x0_probe(struct spi_device *spi) +static int tle62x0_probe(struct spi_device *spi) { struct tle62x0_state *st; struct tle62x0_pdata *pdata; @@ -294,7 +294,7 @@ static int __devinit tle62x0_probe(struct spi_device *spi) return ret; } -static int __devexit tle62x0_remove(struct spi_device *spi) +static int tle62x0_remove(struct spi_device *spi) { struct tle62x0_state *st = spi_get_drvdata(spi); int ptr; @@ -313,7 +313,7 @@ static struct spi_driver tle62x0_driver = { .owner = THIS_MODULE, }, .probe = tle62x0_probe, - .remove = __devexit_p(tle62x0_remove), + .remove = tle62x0_remove, }; module_spi_driver(tle62x0_driver); diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 135f7406f4bf..f756481b0fea 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1401,7 +1401,7 @@ static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat, PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL); } -static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev) +static int pch_spi_pd_probe(struct platform_device *plat_dev) { int ret; struct spi_master *master; @@ -1498,7 +1498,7 @@ err_pci_iomap: return ret; } -static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev) +static int pch_spi_pd_remove(struct platform_device *plat_dev) { struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev); struct pch_spi_data *data = platform_get_drvdata(plat_dev); @@ -1619,12 +1619,12 @@ static struct platform_driver pch_spi_pd_driver = { .owner = THIS_MODULE, }, .probe = pch_spi_pd_probe, - .remove = __devexit_p(pch_spi_pd_remove), + .remove = pch_spi_pd_remove, .suspend = pch_spi_pd_suspend, .resume = pch_spi_pd_resume }; -static int __devinit pch_spi_probe(struct pci_dev *pdev, +static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct pch_spi_board_data *board_dat; @@ -1705,7 +1705,7 @@ err_no_mem: return retval; } -static void __devexit pch_spi_remove(struct pci_dev *pdev) +static void pch_spi_remove(struct pci_dev *pdev) { int i; struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev); @@ -1776,7 +1776,7 @@ static struct pci_driver pch_spi_pcidev_driver = { .name = "pch_spi", .id_table = pch_spi_pcidev_id, .probe = pch_spi_probe, - .remove = __devexit_p(pch_spi_remove), + .remove = pch_spi_remove, .suspend = pch_spi_suspend, .resume = pch_spi_resume, }; diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 266a847e2992..4d3ec8b9f479 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -217,7 +217,7 @@ static int spi_xcomm_setup(struct spi_device *spi) return 0; } -static int __devinit spi_xcomm_probe(struct i2c_client *i2c, +static int spi_xcomm_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct spi_xcomm *spi_xcomm; @@ -246,7 +246,7 @@ static int __devinit spi_xcomm_probe(struct i2c_client *i2c, return ret; } -static int __devexit spi_xcomm_remove(struct i2c_client *i2c) +static int spi_xcomm_remove(struct i2c_client *i2c) { struct spi_master *master = i2c_get_clientdata(i2c); @@ -267,7 +267,7 @@ static struct i2c_driver spi_xcomm_driver = { }, .id_table = spi_xcomm_ids, .probe = spi_xcomm_probe, - .remove = __devexit_p(spi_xcomm_remove), + .remove = spi_xcomm_remove, }; module_i2c_driver(spi_xcomm_driver); diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 4c5a663b9fa8..e1d769607425 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -462,7 +462,7 @@ void xilinx_spi_deinit(struct spi_master *master) } EXPORT_SYMBOL(xilinx_spi_deinit); -static int __devinit xilinx_spi_probe(struct platform_device *dev) +static int xilinx_spi_probe(struct platform_device *dev) { struct xspi_platform_data *pdata; struct resource *r; @@ -518,7 +518,7 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev) return 0; } -static int __devexit xilinx_spi_remove(struct platform_device *dev) +static int xilinx_spi_remove(struct platform_device *dev) { xilinx_spi_deinit(platform_get_drvdata(dev)); platform_set_drvdata(dev, 0); @@ -531,7 +531,7 @@ MODULE_ALIAS("platform:" XILINX_SPI_NAME); static struct platform_driver xilinx_spi_driver = { .probe = xilinx_spi_probe, - .remove = __devexit_p(xilinx_spi_remove), + .remove = xilinx_spi_remove, .driver = { .name = XILINX_SPI_NAME, .owner = THIS_MODULE, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 2ca5f079ff01..689eaf7485af 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -492,8 +492,7 @@ static void spi_match_master_to_boardinfo(struct spi_master *master, * The board info passed can safely be __initdata ... but be careful of * any embedded pointers (platform_data, etc), they're copied as-is. */ -int __devinit -spi_register_board_info(struct spi_board_info const *info, unsigned n) +int spi_register_board_info(struct spi_board_info const *info, unsigned n) { struct boardinfo *bi; int i; diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index e44abc96cd05..2e0655dbe070 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -573,7 +573,7 @@ static struct class *spidev_class; /*-------------------------------------------------------------------------*/ -static int __devinit spidev_probe(struct spi_device *spi) +static int spidev_probe(struct spi_device *spi) { struct spidev_data *spidev; int status; @@ -622,7 +622,7 @@ static int __devinit spidev_probe(struct spi_device *spi) return status; } -static int __devexit spidev_remove(struct spi_device *spi) +static int spidev_remove(struct spi_device *spi) { struct spidev_data *spidev = spi_get_drvdata(spi); @@ -658,7 +658,7 @@ static struct spi_driver spidev_spi_driver = { .of_match_table = of_match_ptr(spidev_dt_ids), }, .probe = spidev_probe, - .remove = __devexit_p(spidev_remove), + .remove = spidev_remove, /* NOTE: suspend/resume methods are not necessary here. * We don't do anything except pass the requests to/from -- cgit v1.2.3 From 7cb943615aabbd72624f77f0a84b8c5d627cf846 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Tue, 4 Dec 2012 15:09:38 +0100 Subject: spi/sparc: Allow of_register_spi_devices for sparc The spi support code works on SPARC too. No reason to exclude it from the party. Signed-off-by: Andreas Larsson Acked-by: David S. Miller Signed-off-by: Grant Likely --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 689eaf7485af..b8335a1f82ef 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -805,7 +805,7 @@ err_init_queue: /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_OF) && !defined(CONFIG_SPARC) +#if defined(CONFIG_OF) /** * of_register_spi_devices() - Register child devices onto the SPI bus * @master: Pointer to spi_master device -- cgit v1.2.3