From e1b0f0df6a08329ce2f123240a5218c3e5c43b74 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 20 Dec 2012 18:27:31 +0000 Subject: [PATCH 01/50] spi/s3c64xx: Complain if we fail to set a transfer speed Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index ad93231a8038..6495352bcc19 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -957,6 +957,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) if (spi->max_speed_hz >= speed) { spi->max_speed_hz = speed; } else { + dev_err(&spi->dev, "Can't set %dHz transfer speed\n", + spi->max_speed_hz); err = -EINVAL; goto setup_exit; } From 0202775bc3a28f2436ea6ee13ef3eb0e8f237857 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 7 Jan 2013 12:44:32 +0200 Subject: [PATCH 02/50] spi/pxa2xx-pci: switch to use pcim_* interfaces Instead of open-coding all the error management in the driver we can take advantage of the pcim_* interfaces that release the resources automatically. We also use platform_device_register_full() to register the platform device because it allows us to create and register the platform device at one go, simplifying the error management. This a preparatory step for getting rid of pxa_ssp_request()/free() which we will do in the next patch. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 90 +++++++++++++----------------------- 1 file changed, 32 insertions(+), 58 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index cf95587eefde..ece979e9c642 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -54,101 +54,75 @@ EXPORT_SYMBOL_GPL(pxa_ssp_free); static int ce4100_spi_probe(struct pci_dev *dev, const struct pci_device_id *ent) { + struct platform_device_info pi; int ret; - resource_size_t phys_beg; - resource_size_t phys_len; struct ce4100_info *spi_info; struct platform_device *pdev; struct pxa2xx_spi_master spi_pdata; struct ssp_device *ssp; - ret = pci_enable_device(dev); + ret = pcim_enable_device(dev); if (ret) return ret; - phys_beg = pci_resource_start(dev, 0); - phys_len = pci_resource_len(dev, 0); - - if (!request_mem_region(phys_beg, phys_len, - "CE4100 SPI")) { - dev_err(&dev->dev, "Can't request register space.\n"); - ret = -EBUSY; + ret = pcim_iomap_regions(dev, 1 << 0, "PXA2xx SPI"); + if (!ret) return ret; - } - pdev = platform_device_alloc("pxa2xx-spi", dev->devfn); - spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL); - if (!pdev || !spi_info ) { - ret = -ENOMEM; - goto err_nomem; - } + spi_info = devm_kzalloc(&dev->dev, sizeof(*spi_info), GFP_KERNEL); + if (!spi_info) + return -ENOMEM; + memset(&spi_pdata, 0, sizeof(spi_pdata)); spi_pdata.num_chipselect = dev->devfn; - ret = platform_device_add_data(pdev, &spi_pdata, sizeof(spi_pdata)); - if (ret) - goto err_nomem; - - pdev->dev.parent = &dev->dev; - pdev->dev.of_node = dev->dev.of_node; ssp = &spi_info->ssp; ssp->phys_base = pci_resource_start(dev, 0); - ssp->mmio_base = ioremap(phys_beg, phys_len); + ssp->mmio_base = pcim_iomap_table(dev)[0]; if (!ssp->mmio_base) { - dev_err(&pdev->dev, "failed to ioremap() registers\n"); - ret = -EIO; - goto err_nomem; + dev_err(&dev->dev, "failed to ioremap() registers\n"); + return -EIO; } ssp->irq = dev->irq; - ssp->port_id = pdev->id; + ssp->port_id = dev->devfn; ssp->type = PXA25x_SSP; mutex_lock(&ssp_lock); list_add(&ssp->node, &ssp_list); mutex_unlock(&ssp_lock); + memset(&pi, 0, sizeof(pi)); + pi.parent = &dev->dev; + pi.name = "pxa2xx-spi"; + pi.id = ssp->port_id; + pi.data = &spi_pdata; + pi.size_data = sizeof(spi_pdata); + + pdev = platform_device_register_full(&pi); + if (!pdev) { + mutex_lock(&ssp_lock); + list_del(&ssp->node); + mutex_unlock(&ssp_lock); + + return -ENOMEM; + } + + spi_info->spi_pdev = pdev; pci_set_drvdata(dev, spi_info); - ret = platform_device_add(pdev); - if (ret) - goto err_dev_add; - - return ret; - -err_dev_add: - pci_set_drvdata(dev, NULL); - mutex_lock(&ssp_lock); - list_del(&ssp->node); - mutex_unlock(&ssp_lock); - iounmap(ssp->mmio_base); - -err_nomem: - release_mem_region(phys_beg, phys_len); - platform_device_put(pdev); - kfree(spi_info); - return ret; + return 0; } static void ce4100_spi_remove(struct pci_dev *dev) { - struct ce4100_info *spi_info; - struct ssp_device *ssp; + struct ce4100_info *spi_info = pci_get_drvdata(dev); + struct ssp_device *ssp = &spi_info->ssp; - spi_info = pci_get_drvdata(dev); - ssp = &spi_info->ssp; platform_device_unregister(spi_info->spi_pdev); - iounmap(ssp->mmio_base); - release_mem_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); - mutex_lock(&ssp_lock); list_del(&ssp->node); mutex_unlock(&ssp_lock); - - pci_set_drvdata(dev, NULL); - pci_disable_device(dev); - kfree(spi_info); } static DEFINE_PCI_DEVICE_TABLE(ce4100_spi_devices) = { From 851bacf5902cad15f9bb789d278a1ee9608c8f25 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 7 Jan 2013 12:44:33 +0200 Subject: [PATCH 03/50] spi/pxa2xx: embed the ssp_device to platform data The spi-pxa2xx-pci glue driver had to implement pxa_ssp_request()/free() in order to support the spi-pxa2xx platform driver. Since the ACPI enabled platforms can use the same platform driver we would need to implement pxa_ssp_request()/free() in some central place that can be shared by the ACPI and PCI glue code. Instead of doing that we can make pxa_ssp_request()/free() to be available only when CONFIG_ARCH_PXA is set. On other arches these are being stubbed out in preference to passing the ssp_device from the platform data directly. We also change the SPI bus number to be taken from ssp->port_id instead of platform device id. This way the supporting code that passes the ssp can decide the number (or it can set it to the same as pdev->id). Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 73 +++------------------------------- drivers/spi/spi-pxa2xx.c | 15 +++++-- include/linux/pxa2xx_ssp.h | 9 +++++ include/linux/spi/pxa2xx_spi.h | 3 ++ 4 files changed, 28 insertions(+), 72 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index ece979e9c642..364964d2ed04 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -8,55 +8,11 @@ #include #include -struct ce4100_info { - struct ssp_device ssp; - struct platform_device *spi_pdev; -}; - -static DEFINE_MUTEX(ssp_lock); -static LIST_HEAD(ssp_list); - -struct ssp_device *pxa_ssp_request(int port, const char *label) -{ - struct ssp_device *ssp = NULL; - - mutex_lock(&ssp_lock); - - list_for_each_entry(ssp, &ssp_list, node) { - if (ssp->port_id == port && ssp->use_count == 0) { - ssp->use_count++; - ssp->label = label; - break; - } - } - - mutex_unlock(&ssp_lock); - - if (&ssp->node == &ssp_list) - return NULL; - - return ssp; -} -EXPORT_SYMBOL_GPL(pxa_ssp_request); - -void pxa_ssp_free(struct ssp_device *ssp) -{ - mutex_lock(&ssp_lock); - if (ssp->use_count) { - ssp->use_count--; - ssp->label = NULL; - } else - dev_err(&ssp->pdev->dev, "device already free\n"); - mutex_unlock(&ssp_lock); -} -EXPORT_SYMBOL_GPL(pxa_ssp_free); - static int ce4100_spi_probe(struct pci_dev *dev, const struct pci_device_id *ent) { struct platform_device_info pi; int ret; - struct ce4100_info *spi_info; struct platform_device *pdev; struct pxa2xx_spi_master spi_pdata; struct ssp_device *ssp; @@ -69,14 +25,10 @@ static int ce4100_spi_probe(struct pci_dev *dev, if (!ret) return ret; - spi_info = devm_kzalloc(&dev->dev, sizeof(*spi_info), GFP_KERNEL); - if (!spi_info) - return -ENOMEM; - memset(&spi_pdata, 0, sizeof(spi_pdata)); spi_pdata.num_chipselect = dev->devfn; - ssp = &spi_info->ssp; + ssp = &spi_pdata.ssp; ssp->phys_base = pci_resource_start(dev, 0); ssp->mmio_base = pcim_iomap_table(dev)[0]; if (!ssp->mmio_base) { @@ -87,10 +39,6 @@ static int ce4100_spi_probe(struct pci_dev *dev, ssp->port_id = dev->devfn; ssp->type = PXA25x_SSP; - mutex_lock(&ssp_lock); - list_add(&ssp->node, &ssp_list); - mutex_unlock(&ssp_lock); - memset(&pi, 0, sizeof(pi)); pi.parent = &dev->dev; pi.name = "pxa2xx-spi"; @@ -99,30 +47,19 @@ static int ce4100_spi_probe(struct pci_dev *dev, pi.size_data = sizeof(spi_pdata); pdev = platform_device_register_full(&pi); - if (!pdev) { - mutex_lock(&ssp_lock); - list_del(&ssp->node); - mutex_unlock(&ssp_lock); - + if (!pdev) return -ENOMEM; - } - spi_info->spi_pdev = pdev; - pci_set_drvdata(dev, spi_info); + pci_set_drvdata(dev, pdev); return 0; } static void ce4100_spi_remove(struct pci_dev *dev) { - struct ce4100_info *spi_info = pci_get_drvdata(dev); - struct ssp_device *ssp = &spi_info->ssp; + struct platform_device *pdev = pci_get_drvdata(dev); - platform_device_unregister(spi_info->spi_pdev); - - mutex_lock(&ssp_lock); - list_del(&ssp->node); - mutex_unlock(&ssp_lock); + platform_device_unregister(pdev); } static DEFINE_PCI_DEVICE_TABLE(ce4100_spi_devices) = { diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 5c8c4f5883c4..54097ad76356 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1535,11 +1535,18 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) struct ssp_device *ssp; int status; - platform_info = dev->platform_data; + platform_info = dev_get_platdata(dev); + if (!platform_info) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } ssp = pxa_ssp_request(pdev->id, pdev->name); - if (ssp == NULL) { - dev_err(&pdev->dev, "failed to request SSP%d\n", pdev->id); + if (!ssp) + ssp = &platform_info->ssp; + + if (!ssp->mmio_base) { + dev_err(&pdev->dev, "failed to get ssp\n"); return -ENODEV; } @@ -1561,7 +1568,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - master->bus_num = pdev->id; + master->bus_num = ssp->port_id; master->num_chipselect = platform_info->num_chipselect; master->dma_alignment = DMA_ALIGNMENT; master->cleanup = cleanup; diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index f36632061c66..065e7f6c3ad7 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -206,6 +206,15 @@ static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg) return __raw_readl(dev->mmio_base + reg); } +#ifdef CONFIG_ARCH_PXA struct ssp_device *pxa_ssp_request(int port, const char *label); void pxa_ssp_free(struct ssp_device *); +#else +static inline struct ssp_device *pxa_ssp_request(int port, const char *label) +{ + return NULL; +} +static inline void pxa_ssp_free(struct ssp_device *ssp) {} +#endif + #endif diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h index c73d1445c77e..6b99f09b717d 100644 --- a/include/linux/spi/pxa2xx_spi.h +++ b/include/linux/spi/pxa2xx_spi.h @@ -28,6 +28,9 @@ struct pxa2xx_spi_master { u32 clock_enable; u16 num_chipselect; u8 enable_dma; + + /* For non-PXA arches */ + struct ssp_device ssp; }; /* spi_board_info.controller_data for SPI slave devices, From 7a5d8ca12aece480e0fe5eda8ce4236dddf55363 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 10 Jan 2013 13:13:56 +0100 Subject: [PATCH 04/50] spi: bitbang: simplify pointer arithmetics Add a pointer variable to make spi_bitbang_start() look simpler. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mark Brown --- drivers/spi/spi-bitbang.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 8b3d8efafd3c..328c4dcd3d52 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -427,40 +427,41 @@ EXPORT_SYMBOL_GPL(spi_bitbang_transfer); */ int spi_bitbang_start(struct spi_bitbang *bitbang) { - int status; + struct spi_master *master = bitbang->master; + int status; - if (!bitbang->master || !bitbang->chipselect) + if (!master || !bitbang->chipselect) return -EINVAL; INIT_WORK(&bitbang->work, bitbang_work); spin_lock_init(&bitbang->lock); INIT_LIST_HEAD(&bitbang->queue); - if (!bitbang->master->mode_bits) - bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags; + if (!master->mode_bits) + master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags; - if (!bitbang->master->transfer) - bitbang->master->transfer = spi_bitbang_transfer; + if (!master->transfer) + master->transfer = spi_bitbang_transfer; if (!bitbang->txrx_bufs) { bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; - if (!bitbang->master->setup) { + if (!master->setup) { if (!bitbang->setup_transfer) bitbang->setup_transfer = spi_bitbang_setup_transfer; - bitbang->master->setup = spi_bitbang_setup; - bitbang->master->cleanup = spi_bitbang_cleanup; + master->setup = spi_bitbang_setup; + master->cleanup = spi_bitbang_cleanup; } - } else if (!bitbang->master->setup) + } else if (!master->setup) return -EINVAL; - if (bitbang->master->transfer == spi_bitbang_transfer && + if (master->transfer == spi_bitbang_transfer && !bitbang->setup_transfer) return -EINVAL; /* this task is the only thing to touch the SPI bits */ bitbang->busy = 0; bitbang->workqueue = create_singlethread_workqueue( - dev_name(bitbang->master->dev.parent)); + dev_name(master->dev.parent)); if (bitbang->workqueue == NULL) { status = -EBUSY; goto err1; @@ -469,7 +470,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - status = spi_register_master(bitbang->master); + status = spi_register_master(master); if (status < 0) goto err2; From f28037d2e9ee1fe34ede6211f1cce1d38d867cb8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 14 Jan 2013 11:17:19 +0900 Subject: [PATCH 05/50] MAINTAINERS: Add myself as a backup maintainer for SPI Grant said he would find it helpful for me to continue handling some of the legwork for SPI so add myself to MAINTAINERS so I get CCed on patches. Signed-off-by: Mark Brown Acked-by: Grant Likely --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index fa309ab7ccbf..e4ae50624f1b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7124,6 +7124,7 @@ F: drivers/clk/spear/ SPI SUBSYSTEM M: Grant Likely +M: Mark Brown L: spi-devel-general@lists.sourceforge.net Q: http://patchwork.kernel.org/project/spi-devel-general/list/ T: git git://git.secretlab.ca/git/linux-2.6.git From 2b49ebda39d66db1ae79a94c2bfec2d44f26ff94 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:24 +0200 Subject: [PATCH 06/50] spi/pxa2xx: allow building on a 64-bit kernel We are going to use it on 64-bit kernel on Intel Lynxpoint so make sure we can build it into such kernel. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 2e188e1127eb..a90393d7f106 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -299,7 +299,7 @@ config SPI_PPC4xx config SPI_PXA2XX tristate "PXA2xx SSP SPI master" - depends on (ARCH_PXA || (X86_32 && PCI)) && EXPERIMENTAL + depends on ARCH_PXA || PCI select PXA_SSP if ARCH_PXA help This enables using a PXA2xx or Sodaville SSP port as a SPI master @@ -307,7 +307,7 @@ config SPI_PXA2XX additional documentation can be found a Documentation/spi/pxa2xx. config SPI_PXA2XX_PCI - def_bool SPI_PXA2XX && X86_32 && PCI + def_tristate SPI_PXA2XX && PCI config SPI_RSPI tristate "Renesas RSPI controller" From 2b9b84f497638ebd60a762cb1c7fa7b4ff7dce4d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:25 +0200 Subject: [PATCH 07/50] spi/pxa2xx: fix warnings when compiling a 64-bit kernel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix following warnings seen when compiling 64-bit: drivers/spi/spi-pxa2xx.c: In function ‘map_dma_buffers’: drivers/spi/spi-pxa2xx.c:384:7: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/spi/spi-pxa2xx.c:384:40: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/spi/spi-pxa2xx.c: In function ‘pxa2xx_spi_probe’: drivers/spi/spi-pxa2xx.c:1572:34: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/spi/spi-pxa2xx.c:1572:34: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/spi/spi-pxa2xx.c:1572:34: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/spi/spi-pxa2xx.c:1572:27: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 54097ad76356..ef0a78650e96 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -47,7 +47,7 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) #define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) -#define IS_DMA_ALIGNED(x) ((((u32)(x)) & 0x07) == 0) +#define IS_DMA_ALIGNED(x) IS_ALIGNED((unsigned long)(x), DMA_ALIGNMENT) #define MAX_DMA_LEN 8191 #define DMA_ALIGNMENT 8 @@ -1576,8 +1576,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->transfer = transfer; drv_data->ssp_type = ssp->type; - drv_data->null_dma_buf = (u32 *)ALIGN((u32)(drv_data + - sizeof(struct driver_data)), 8); + drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT); drv_data->ioaddr = ssp->mmio_base; drv_data->ssdr_physical = ssp->phys_base + SSDR; From 7f86bde90e2e1f693d93922fc0f4d50f001ce1a5 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:26 +0200 Subject: [PATCH 08/50] spi/pxa2xx: convert to the pump message infrastructure The SPI core provides infrastructure for standard message queueing so use that instead of handling everything in the driver. This simplifies the driver. Signed-off-by: Mika Westerberg Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 212 +++------------------------------------ 1 file changed, 12 insertions(+), 200 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index ef0a78650e96..a241891355db 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -85,9 +85,6 @@ DEFINE_SSP_REG(SSPSP, 0x2c) #define DONE_STATE ((void*)2) #define ERROR_STATE ((void*)-1) -#define QUEUE_RUNNING 0 -#define QUEUE_STOPPED 1 - struct driver_data { /* Driver model hookup */ struct platform_device *pdev; @@ -117,14 +114,6 @@ struct driver_data { u32 clear_sr; u32 mask_sr; - /* Driver message queue */ - struct workqueue_struct *workqueue; - struct work_struct pump_messages; - spinlock_t lock; - struct list_head queue; - int busy; - int run; - /* Message Transfer pump */ struct tasklet_struct pump_transfers; @@ -173,8 +162,6 @@ struct chip_data { void (*cs_control)(u32 command); }; -static void pump_messages(struct work_struct *work); - static void cs_assert(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; @@ -444,15 +431,11 @@ static void unmap_dma_buffers(struct driver_data *drv_data) static void giveback(struct driver_data *drv_data) { struct spi_transfer* last_transfer; - unsigned long flags; struct spi_message *msg; - spin_lock_irqsave(&drv_data->lock, flags); msg = drv_data->cur_msg; drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; - queue_work(drv_data->workqueue, &drv_data->pump_messages); - spin_unlock_irqrestore(&drv_data->lock, flags); last_transfer = list_entry(msg->transfers.prev, struct spi_transfer, @@ -481,13 +464,7 @@ static void giveback(struct driver_data *drv_data) */ /* get a pointer to the next message, if any */ - spin_lock_irqsave(&drv_data->lock, flags); - if (list_empty(&drv_data->queue)) - next_msg = NULL; - else - next_msg = list_entry(drv_data->queue.next, - struct spi_message, queue); - spin_unlock_irqrestore(&drv_data->lock, flags); + next_msg = spi_get_next_queued_message(drv_data->master); /* see if the next and current messages point * to the same chip @@ -498,10 +475,7 @@ static void giveback(struct driver_data *drv_data) cs_deassert(drv_data); } - msg->state = NULL; - if (msg->complete) - msg->complete(msg->context); - + spi_finalize_current_message(drv_data->master); drv_data->cur_chip = NULL; } @@ -1176,31 +1150,12 @@ static void pump_transfers(unsigned long data) write_SSCR1(cr1, reg); } -static void pump_messages(struct work_struct *work) +static int pxa2xx_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) { - struct driver_data *drv_data = - container_of(work, struct driver_data, pump_messages); - unsigned long flags; - - /* Lock queue and check for queue work */ - spin_lock_irqsave(&drv_data->lock, flags); - if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) { - drv_data->busy = 0; - spin_unlock_irqrestore(&drv_data->lock, flags); - return; - } - - /* Make sure we are not already running a message */ - if (drv_data->cur_msg) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return; - } - - /* Extract head of queue */ - drv_data->cur_msg = list_entry(drv_data->queue.next, - struct spi_message, queue); - list_del_init(&drv_data->cur_msg->queue); + struct driver_data *drv_data = spi_master_get_devdata(master); + drv_data->cur_msg = msg; /* Initial message state*/ drv_data->cur_msg->state = START_STATE; drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, @@ -1213,34 +1168,6 @@ static void pump_messages(struct work_struct *work) /* Mark as busy and launch transfers */ tasklet_schedule(&drv_data->pump_transfers); - - drv_data->busy = 1; - spin_unlock_irqrestore(&drv_data->lock, flags); -} - -static int transfer(struct spi_device *spi, struct spi_message *msg) -{ - struct driver_data *drv_data = spi_master_get_devdata(spi->master); - unsigned long flags; - - spin_lock_irqsave(&drv_data->lock, flags); - - if (drv_data->run == QUEUE_STOPPED) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return -ESHUTDOWN; - } - - msg->actual_length = 0; - msg->status = -EINPROGRESS; - msg->state = START_STATE; - - list_add_tail(&msg->queue, &drv_data->queue); - - if (drv_data->run == QUEUE_RUNNING && !drv_data->busy) - queue_work(drv_data->workqueue, &drv_data->pump_messages); - - spin_unlock_irqrestore(&drv_data->lock, flags); - return 0; } @@ -1438,94 +1365,6 @@ static void cleanup(struct spi_device *spi) kfree(chip); } -static int init_queue(struct driver_data *drv_data) -{ - INIT_LIST_HEAD(&drv_data->queue); - spin_lock_init(&drv_data->lock); - - drv_data->run = QUEUE_STOPPED; - drv_data->busy = 0; - - tasklet_init(&drv_data->pump_transfers, - pump_transfers, (unsigned long)drv_data); - - INIT_WORK(&drv_data->pump_messages, pump_messages); - drv_data->workqueue = create_singlethread_workqueue( - dev_name(drv_data->master->dev.parent)); - if (drv_data->workqueue == NULL) - return -EBUSY; - - return 0; -} - -static int start_queue(struct driver_data *drv_data) -{ - unsigned long flags; - - spin_lock_irqsave(&drv_data->lock, flags); - - if (drv_data->run == QUEUE_RUNNING || drv_data->busy) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return -EBUSY; - } - - drv_data->run = QUEUE_RUNNING; - drv_data->cur_msg = NULL; - drv_data->cur_transfer = NULL; - drv_data->cur_chip = NULL; - spin_unlock_irqrestore(&drv_data->lock, flags); - - queue_work(drv_data->workqueue, &drv_data->pump_messages); - - return 0; -} - -static int stop_queue(struct driver_data *drv_data) -{ - unsigned long flags; - unsigned limit = 500; - int status = 0; - - spin_lock_irqsave(&drv_data->lock, flags); - - /* This is a bit lame, but is optimized for the common execution path. - * A wait_queue on the drv_data->busy could be used, but then the common - * execution path (pump_messages) would be required to call wake_up or - * friends on every SPI message. Do this instead */ - drv_data->run = QUEUE_STOPPED; - while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) { - spin_unlock_irqrestore(&drv_data->lock, flags); - msleep(10); - spin_lock_irqsave(&drv_data->lock, flags); - } - - if (!list_empty(&drv_data->queue) || drv_data->busy) - status = -EBUSY; - - spin_unlock_irqrestore(&drv_data->lock, flags); - - return status; -} - -static int destroy_queue(struct driver_data *drv_data) -{ - int status; - - status = stop_queue(drv_data); - /* we are unloading the module or failing to load (only two calls - * to this routine), and neither call can handle a return value. - * However, destroy_workqueue calls flush_workqueue, and that will - * block until all work is done. If the reason that stop_queue - * timed out is that the work will never finish, then it does no - * good to call destroy_workqueue, so return anyway. */ - if (status != 0) - return status; - - destroy_workqueue(drv_data->workqueue); - - return 0; -} - static int pxa2xx_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1573,7 +1412,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->dma_alignment = DMA_ALIGNMENT; master->cleanup = cleanup; master->setup = setup; - master->transfer = transfer; + master->transfer_one_message = pxa2xx_spi_transfer_one_message; drv_data->ssp_type = ssp->type; drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT); @@ -1646,31 +1485,19 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) write_SSTO(0, drv_data->ioaddr); write_SSPSP(0, drv_data->ioaddr); - /* Initial and start queue */ - status = init_queue(drv_data); - if (status != 0) { - dev_err(&pdev->dev, "problem initializing queue\n"); - goto out_error_clock_enabled; - } - status = start_queue(drv_data); - if (status != 0) { - dev_err(&pdev->dev, "problem starting queue\n"); - goto out_error_clock_enabled; - } + tasklet_init(&drv_data->pump_transfers, pump_transfers, + (unsigned long)drv_data); /* Register with the SPI framework */ platform_set_drvdata(pdev, drv_data); status = spi_register_master(master); if (status != 0) { dev_err(&pdev->dev, "problem registering spi master\n"); - goto out_error_queue_alloc; + goto out_error_clock_enabled; } return status; -out_error_queue_alloc: - destroy_queue(drv_data); - out_error_clock_enabled: clk_disable(ssp->clk); @@ -1693,26 +1520,11 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) { struct driver_data *drv_data = platform_get_drvdata(pdev); struct ssp_device *ssp; - int status = 0; if (!drv_data) return 0; ssp = drv_data->ssp; - /* Remove the queue */ - status = destroy_queue(drv_data); - if (status != 0) - /* the kernel does not check the return status of this - * this routine (mod->exit, within the kernel). Therefore - * nothing is gained by returning from here, the module is - * going away regardless, and we should not leave any more - * resources allocated than necessary. We cannot free the - * message memory in drv_data->queue, but we can release the - * resources below. I think the kernel should honor -EBUSY - * returns but... */ - dev_err(&pdev->dev, "pxa2xx_spi_remove: workqueue will not " - "complete, message memory not freed\n"); - /* Disable the SSP at the peripheral and SOC level */ write_SSCR0(0, drv_data->ioaddr); clk_disable(ssp->clk); @@ -1755,7 +1567,7 @@ static int pxa2xx_spi_suspend(struct device *dev) struct ssp_device *ssp = drv_data->ssp; int status = 0; - status = stop_queue(drv_data); + status = spi_master_suspend(drv_data->master); if (status != 0) return status; write_SSCR0(0, drv_data->ioaddr); @@ -1781,7 +1593,7 @@ static int pxa2xx_spi_resume(struct device *dev) clk_enable(ssp->clk); /* Start the queue running */ - status = start_queue(drv_data); + status = spi_master_resume(drv_data->master); if (status != 0) { dev_err(dev, "problem starting queue (%d)\n", status); return status; From 3343b7a6d2cd0a980d0c4a0ce02ef48b6bfcc12a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:27 +0200 Subject: [PATCH 09/50] spi/pxa2xx: convert to the common clk framework Convert clk_enable() to clk_prepare_enable() and clk_disable() to clk_disable_unprepare() respectively in order to support the common clk framework. Otherwise we get warnings on the console as the clock is not prepared before it is enabled. In addition we must cache the maximum clock rate to drv_data->max_clk_rate at probe time because clk_get_rate() cannot be called in tasklet context. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 33 ++++++++++++++++++++------------- include/linux/spi/pxa2xx_spi.h | 18 ------------------ 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index a241891355db..304cf6eb50e6 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -114,6 +115,9 @@ struct driver_data { u32 clear_sr; u32 mask_sr; + /* Maximun clock rate */ + unsigned long max_clk_rate; + /* Message Transfer pump */ struct tasklet_struct pump_transfers; @@ -891,9 +895,12 @@ static int set_dma_burst_and_threshold(struct chip_data *chip, return retval; } -static unsigned int ssp_get_clk_div(struct ssp_device *ssp, int rate) +static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) { - unsigned long ssp_clk = clk_get_rate(ssp->clk); + unsigned long ssp_clk = drv_data->max_clk_rate; + const struct ssp_device *ssp = drv_data->ssp; + + rate = min_t(int, ssp_clk, rate); if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP) return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8; @@ -908,7 +915,6 @@ static void pump_transfers(unsigned long data) struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct chip_data *chip = NULL; - struct ssp_device *ssp = drv_data->ssp; void __iomem *reg = drv_data->ioaddr; u32 clk_div = 0; u8 bits = 0; @@ -1005,7 +1011,7 @@ static void pump_transfers(unsigned long data) if (transfer->bits_per_word) bits = transfer->bits_per_word; - clk_div = ssp_get_clk_div(ssp, speed); + clk_div = ssp_get_clk_div(drv_data, speed); if (bits <= 8) { drv_data->n_bytes = 1; @@ -1214,7 +1220,6 @@ static int setup(struct spi_device *spi) struct pxa2xx_spi_chip *chip_info = NULL; struct chip_data *chip; struct driver_data *drv_data = spi_master_get_devdata(spi->master); - struct ssp_device *ssp = drv_data->ssp; unsigned int clk_div; uint tx_thres = TX_THRESH_DFLT; uint rx_thres = RX_THRESH_DFLT; @@ -1296,7 +1301,7 @@ static int setup(struct spi_device *spi) } } - clk_div = ssp_get_clk_div(ssp, spi->max_speed_hz); + clk_div = ssp_get_clk_div(drv_data, spi->max_speed_hz); chip->speed_hz = spi->max_speed_hz; chip->cr0 = clk_div @@ -1312,12 +1317,12 @@ static int setup(struct spi_device *spi) /* NOTE: PXA25x_SSP _could_ use external clocking ... */ if (!pxa25x_ssp_comp(drv_data)) dev_dbg(&spi->dev, "%ld Hz actual, %s\n", - clk_get_rate(ssp->clk) + drv_data->max_clk_rate / (1 + ((chip->cr0 & SSCR0_SCR(0xfff)) >> 8)), chip->enable_dma ? "DMA" : "PIO"); else dev_dbg(&spi->dev, "%ld Hz actual, %s\n", - clk_get_rate(ssp->clk) / 2 + drv_data->max_clk_rate / 2 / (1 + ((chip->cr0 & SSCR0_SCR(0x0ff)) >> 8)), chip->enable_dma ? "DMA" : "PIO"); @@ -1470,7 +1475,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) } /* Enable SOC clock */ - clk_enable(ssp->clk); + clk_prepare_enable(ssp->clk); + + drv_data->max_clk_rate = clk_get_rate(ssp->clk); /* Load default SSP configuration */ write_SSCR0(0, drv_data->ioaddr); @@ -1499,7 +1506,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) return status; out_error_clock_enabled: - clk_disable(ssp->clk); + clk_disable_unprepare(ssp->clk); out_error_dma_alloc: if (drv_data->tx_channel != -1) @@ -1527,7 +1534,7 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) /* Disable the SSP at the peripheral and SOC level */ write_SSCR0(0, drv_data->ioaddr); - clk_disable(ssp->clk); + clk_disable_unprepare(ssp->clk); /* Release DMA */ if (drv_data->master_info->enable_dma) { @@ -1571,7 +1578,7 @@ static int pxa2xx_spi_suspend(struct device *dev) if (status != 0) return status; write_SSCR0(0, drv_data->ioaddr); - clk_disable(ssp->clk); + clk_disable_unprepare(ssp->clk); return 0; } @@ -1590,7 +1597,7 @@ static int pxa2xx_spi_resume(struct device *dev) DRCMR_MAPVLD | drv_data->tx_channel; /* Enable the SSP clock */ - clk_enable(ssp->clk); + clk_prepare_enable(ssp->clk); /* Start the queue running */ status = spi_master_resume(drv_data->master); diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h index 6b99f09b717d..053b5ba51b25 100644 --- a/include/linux/spi/pxa2xx_spi.h +++ b/include/linux/spi/pxa2xx_spi.h @@ -133,23 +133,5 @@ static inline void pxa_free_dma(int dma_ch) { } -/* - * The CE4100 does not have the clk framework implemented and SPI clock can - * not be switched on/off or the divider changed. - */ -static inline void clk_disable(struct clk *clk) -{ -} - -static inline int clk_enable(struct clk *clk) -{ - return 0; -} - -static inline unsigned long clk_get_rate(struct clk *clk) -{ - return 3686400; -} - #endif #endif From 0202a32d5f4a129ced47db76001f958cd33adeb7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 25 Jan 2013 09:39:34 +0100 Subject: [PATCH 10/50] spi: spi-gpio: Add checks for the dt properties The bindings assumed that the gpios properties were always there, which made the NO_TX and NO_RX mode not usable from device tree. Add extra checks to make sure that the driver can work if either MOSI or MISO is not used. Signed-off-by: Maxime Ripard Cc: Mark Brown Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index c7cf0b7a069b..9ddef55a7165 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -365,9 +365,26 @@ static int spi_gpio_probe_dt(struct platform_device *pdev) if (!pdata) return -ENOMEM; - pdata->sck = of_get_named_gpio(np, "gpio-sck", 0); - pdata->miso = of_get_named_gpio(np, "gpio-miso", 0); - pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0); + ret = of_get_named_gpio(np, "gpio-sck", 0); + if (ret < 0) { + dev_err(&pdev->dev, "gpio-sck property not found\n"); + goto error_free; + } + pdata->sck = ret; + + ret = of_get_named_gpio(np, "gpio-miso", 0); + if (ret < 0) { + dev_info(&pdev->dev, "gpio-miso property not found, switching to no-rx mode\n"); + pdata->miso = SPI_GPIO_NO_MISO; + } else + pdata->miso = ret; + + ret = of_get_named_gpio(np, "gpio-mosi", 0); + if (ret < 0) { + dev_info(&pdev->dev, "gpio-mosi property not found, switching to no-tx mode\n"); + pdata->mosi = SPI_GPIO_NO_MOSI; + } else + pdata->mosi = ret; ret = of_property_read_u32(np, "num-chipselects", &tmp); if (ret < 0) { From 18dd6199fa4527229cede5784244b6eb37a21d62 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Thu, 24 Jan 2013 13:28:58 +0100 Subject: [PATCH 11/50] spi: spi-omap2-mcspi.c: fix coding style This patch fixes some indentation errors. Signed-off-by: Matthias Brugger Reviewed-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index b610f522ca44..31f6e842ea86 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1020,7 +1020,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) } static int omap2_mcspi_transfer_one_message(struct spi_master *master, - struct spi_message *m) + struct spi_message *m) { struct omap2_mcspi *mcspi; struct spi_transfer *t; @@ -1041,7 +1041,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, || (len && !(rx_buf || tx_buf)) || (t->bits_per_word && ( t->bits_per_word < 4 - || t->bits_per_word > 32))) { + || t->bits_per_word > 32))) { dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", t->speed_hz, len, @@ -1052,8 +1052,8 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, } if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) { dev_dbg(mcspi->dev, "speed_hz %d below minimum %d Hz\n", - t->speed_hz, - OMAP2_MCSPI_MAX_FREQ >> 15); + t->speed_hz, + OMAP2_MCSPI_MAX_FREQ >> 15); return -EINVAL; } @@ -1099,7 +1099,7 @@ static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) return ret; mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, - OMAP2_MCSPI_WAKEUPENABLE_WKEN); + OMAP2_MCSPI_WAKEUPENABLE_WKEN); ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN; omap2_mcspi_set_master_mode(master); @@ -1228,7 +1228,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) sprintf(dma_ch_name, "rx%d", i); dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, - dma_ch_name); + dma_ch_name); if (!dma_res) { dev_dbg(&pdev->dev, "cannot get DMA RX channel\n"); status = -ENODEV; @@ -1238,7 +1238,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start; sprintf(dma_ch_name, "tx%d", i); dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, - dma_ch_name); + dma_ch_name); if (!dma_res) { dev_dbg(&pdev->dev, "cannot get DMA TX channel\n"); status = -ENODEV; @@ -1254,7 +1254,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(pinctrl)) dev_warn(&pdev->dev, - "pins are not configured from the driver\n"); + "pins are not configured from the driver\n"); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); From 5cbc7ca987fb3f293203dc14a6c53b91b7c978a5 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Thu, 24 Jan 2013 13:40:41 +0100 Subject: [PATCH 12/50] spi: spi-omap2-mcspi.c: Toggle CS after each word This patch allows the board code to define SPI devices which needs to toggle the chip select after every word send. This is needed to get a better resolution reading e.g. an ADC data stream. Apart from that, as in the normal code CS is controlled by software, a transfer is done much faster. Signed-off-by: Matthias Brugger Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 18 ++++++++++++++++++ include/linux/platform_data/spi-omap2-mcspi.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 31f6e842ea86..12789fcfa980 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -927,6 +927,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) struct spi_device *spi; struct spi_transfer *t = NULL; + struct spi_master *master; int cs_active = 0; struct omap2_mcspi_cs *cs; struct omap2_mcspi_device_config *cd; @@ -935,6 +936,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) u32 chconf; spi = m->spi; + master = spi->master; cs = spi->controller_state; cd = spi->controller_data; @@ -952,6 +954,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) if (!t->speed_hz && !t->bits_per_word) par_override = 0; } + if (cd && cd->cs_per_word) { + chconf = mcspi->ctx.modulctrl; + chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE; + mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf); + mcspi->ctx.modulctrl = + mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); + } + if (!cs_active) { omap2_mcspi_force_cs(spi, 1); @@ -1013,6 +1023,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) if (cs_active) omap2_mcspi_force_cs(spi, 0); + if (cd && cd->cs_per_word) { + chconf = mcspi->ctx.modulctrl; + chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE; + mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf); + mcspi->ctx.modulctrl = + mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); + } + omap2_mcspi_set_enable(spi, 0); m->status = status; diff --git a/include/linux/platform_data/spi-omap2-mcspi.h b/include/linux/platform_data/spi-omap2-mcspi.h index a65572d53211..c100456eab17 100644 --- a/include/linux/platform_data/spi-omap2-mcspi.h +++ b/include/linux/platform_data/spi-omap2-mcspi.h @@ -22,6 +22,9 @@ struct omap2_mcspi_dev_attr { struct omap2_mcspi_device_config { unsigned turbo_mode:1; + + /* toggle chip select after every word */ + unsigned cs_per_word:1; }; #endif From d560040f7d6fbe0a2990b8f6edca1815e19e72f5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 31 Jan 2013 11:12:56 +0100 Subject: [PATCH 13/50] spi: spi-gpio: fix compilation warning on 64 bits systems SPI_GPIO_NO_MOSI and SPI_GPIO_NO_MISO flags are type casted to unsigned long, yet, they are to be stored in an unsigned int field in the spi_gpio_platform_data structure. This leads to the following warning during compilation on 64 bits systems: warning: large integer implicitly truncated to unsigned type [-Woverflow] Signed-off-by: Maxime Ripard Reported-by: Fengguang Wu Signed-off-by: Mark Brown --- include/linux/spi/spi_gpio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/spi/spi_gpio.h b/include/linux/spi/spi_gpio.h index 369b3d7d5b95..1634ce31c06d 100644 --- a/include/linux/spi/spi_gpio.h +++ b/include/linux/spi/spi_gpio.h @@ -62,8 +62,8 @@ */ struct spi_gpio_platform_data { unsigned sck; - unsigned mosi; - unsigned miso; + unsigned long mosi; + unsigned long miso; u16 num_chipselect; }; From 830379e04858098d65c59f07f68be3d73244ae54 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Dec 2012 10:45:59 +0200 Subject: [PATCH 14/50] spi/omap2: disable DMA requests before complete() No actual errors have been found for completing before disabling DMA request lines, but it just looks more semantically correct that on our DMA callback we quiesce the whole thing before stating transfer is finished. Signed-off-by: Felipe Balbi Signed-off-by: Grant Likely --- drivers/spi/spi-omap2-mcspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index b610f522ca44..68446dbd5545 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -298,10 +298,10 @@ static void omap2_mcspi_rx_callback(void *data) struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - complete(&mcspi_dma->dma_rx_completion); - /* We must disable the DMA RX request */ omap2_mcspi_set_dma_req(spi, 1, 0); + + complete(&mcspi_dma->dma_rx_completion); } static void omap2_mcspi_tx_callback(void *data) @@ -310,10 +310,10 @@ static void omap2_mcspi_tx_callback(void *data) struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - complete(&mcspi_dma->dma_tx_completion); - /* We must disable the DMA TX request */ omap2_mcspi_set_dma_req(spi, 0, 0); + + complete(&mcspi_dma->dma_tx_completion); } static void omap2_mcspi_tx_dma(struct spi_device *spi, From aae7147dfc522062b3448f67f5517d32fbdab1fb Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 11 Dec 2012 16:20:39 -0500 Subject: [PATCH 15/50] spi/davinci: add OF support for the spi controller This adds OF support to DaVinci SPI controller to configure platform data through device bindings. Also replaces clk_enable() with of clk_prepare_enable() as well as clk_disable() with clk_disable_unprepare(). Signed-off-by: Murali Karicheri Signed-off-by: Grant Likely --- drivers/spi/spi-davinci.c | 102 ++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 13661e129d96..50bd2cdc52de 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -135,7 +137,7 @@ struct davinci_spi { int dma_rx_chnum; int dma_tx_chnum; - struct davinci_spi_platform_data *pdata; + struct davinci_spi_platform_data pdata; void (*get_rx)(u32 rx_data, struct davinci_spi *); u32 (*get_tx)(struct davinci_spi *); @@ -213,7 +215,7 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) bool gpio_chipsel = false; dspi = spi_master_get_devdata(spi->master); - pdata = dspi->pdata; + pdata = &dspi->pdata; if (pdata->chip_sel && chip_sel < pdata->num_chipselect && pdata->chip_sel[chip_sel] != SPI_INTERN_CS) @@ -392,7 +394,7 @@ static int davinci_spi_setup(struct spi_device *spi) struct davinci_spi_platform_data *pdata; dspi = spi_master_get_devdata(spi->master); - pdata = dspi->pdata; + pdata = &dspi->pdata; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -534,7 +536,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) struct scatterlist sg_rx, sg_tx; dspi = spi_master_get_devdata(spi->master); - pdata = dspi->pdata; + pdata = &dspi->pdata; spicfg = (struct davinci_spi_config *)spi->controller_data; if (!spicfg) spicfg = &davinci_spi_default_cfg; @@ -758,6 +760,70 @@ rx_dma_failed: return r; } +#if defined(CONFIG_OF) +static const struct of_device_id davinci_spi_of_match[] = { + { + .compatible = "ti,dm644x-spi", + }, + { + .compatible = "ti,da8xx-spi", + .data = (void *)SPI_VERSION_2, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, davini_spi_of_match); + +/** + * spi_davinci_get_pdata - Get platform data from DTS binding + * @pdev: ptr to platform data + * @dspi: ptr to driver data + * + * Parses and populates pdata in dspi from device tree bindings. + * + * NOTE: Not all platform data params are supported currently. + */ +static int spi_davinci_get_pdata(struct platform_device *pdev, + struct davinci_spi *dspi) +{ + struct device_node *node = pdev->dev.of_node; + struct davinci_spi_platform_data *pdata; + unsigned int num_cs, intr_line = 0; + const struct of_device_id *match; + + pdata = &dspi->pdata; + + pdata->version = SPI_VERSION_1; + match = of_match_device(of_match_ptr(davinci_spi_of_match), + &pdev->dev); + if (!match) + return -ENODEV; + + /* match data has the SPI version number for SPI_VERSION_2 */ + if (match->data == (void *)SPI_VERSION_2) + pdata->version = SPI_VERSION_2; + + /* + * default num_cs is 1 and all chipsel are internal to the chip + * indicated by chip_sel being NULL. GPIO based CS is not + * supported yet in DT bindings. + */ + num_cs = 1; + of_property_read_u32(node, "num-cs", &num_cs); + pdata->num_chipselect = num_cs; + of_property_read_u32(node, "ti,davinci-spi-intr-line", &intr_line); + pdata->intr_line = intr_line; + return 0; +} +#else +#define davinci_spi_of_match NULL +static struct davinci_spi_platform_data + *spi_davinci_get_pdata(struct platform_device *pdev, + struct davinci_spi *dspi) +{ + return -ENODEV; +} +#endif + /** * davinci_spi_probe - probe function for SPI Master Controller * @pdev: platform_device structure which contains plateform specific data @@ -780,12 +846,6 @@ static int davinci_spi_probe(struct platform_device *pdev) int i = 0, ret = 0; u32 spipc0; - pdata = pdev->dev.platform_data; - if (pdata == NULL) { - ret = -ENODEV; - goto err; - } - master = spi_alloc_master(&pdev->dev, sizeof(struct davinci_spi)); if (master == NULL) { ret = -ENOMEM; @@ -800,6 +860,19 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } + if (pdev->dev.platform_data) { + pdata = pdev->dev.platform_data; + dspi->pdata = *pdata; + } else { + /* update dspi pdata with that from the DT */ + ret = spi_davinci_get_pdata(pdev, dspi); + if (ret < 0) + goto free_master; + } + + /* pdata in dspi is now updated and point pdata to that */ + pdata = &dspi->pdata; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { ret = -ENOENT; @@ -807,7 +880,6 @@ static int davinci_spi_probe(struct platform_device *pdev) } dspi->pbase = r->start; - dspi->pdata = pdata; mem = request_mem_region(r->start, resource_size(r), pdev->name); if (mem == NULL) { @@ -843,8 +915,9 @@ static int davinci_spi_probe(struct platform_device *pdev) ret = -ENODEV; goto put_master; } - clk_enable(dspi->clk); + clk_prepare_enable(dspi->clk); + master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; master->setup = davinci_spi_setup; @@ -927,7 +1000,7 @@ free_dma: dma_release_channel(dspi->dma_rx); dma_release_channel(dspi->dma_tx); free_clk: - clk_disable(dspi->clk); + clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); put_master: spi_master_put(master); @@ -963,7 +1036,7 @@ static int davinci_spi_remove(struct platform_device *pdev) spi_bitbang_stop(&dspi->bitbang); - clk_disable(dspi->clk); + clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); spi_master_put(master); free_irq(dspi->irq, dspi); @@ -978,6 +1051,7 @@ static struct platform_driver davinci_spi_driver = { .driver = { .name = "spi_davinci", .owner = THIS_MODULE, + .of_match_table = davinci_spi_of_match, }, .probe = davinci_spi_probe, .remove = davinci_spi_remove, From cf9c86efecf9510e62388fd174cf607671c59fa3 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Wed, 12 Dec 2012 12:54:48 +0100 Subject: [PATCH 16/50] spi/sh-msiof: Add device tree parsing to driver This adds the capability to retrieve setup data from the device tree node. The usage of platform data is still available. Signed-off-by: Bastian Hecht Signed-off-by: Grant Likely --- .../devicetree/bindings/spi/sh-msiof.txt | 12 ++++ drivers/spi/spi-sh-msiof.c | 56 ++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/spi/sh-msiof.txt diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt new file mode 100644 index 000000000000..e6222106ca36 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt @@ -0,0 +1,12 @@ +Renesas MSIOF spi controller + +Required properties: +- compatible : "renesas,sh-msiof" for SuperH or + "renesas,sh-mobile-msiof" for SH Mobile series +- reg : Offset and length of the register set for the device +- interrupts : interrupt line used by MSIOF + +Optional properties: +- num-cs : total number of chip-selects +- renesas,tx-fifo-size : Overrides the default tx fifo size given in words +- renesas,rx-fifo-size : Overrides the default rx fifo size given in words diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 96358d0eabb7..8b40d0884f8b 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -592,6 +593,37 @@ static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs, return 0; } +#ifdef CONFIG_OF +static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) +{ + struct sh_msiof_spi_info *info; + struct device_node *np = dev->of_node; + u32 num_cs = 0; + + info = devm_kzalloc(dev, sizeof(struct sh_msiof_spi_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "failed to allocate setup data\n"); + return NULL; + } + + /* Parse the MSIOF properties */ + of_property_read_u32(np, "num-cs", &num_cs); + of_property_read_u32(np, "renesas,tx-fifo-size", + &info->tx_fifo_override); + of_property_read_u32(np, "renesas,rx-fifo-size", + &info->rx_fifo_override); + + info->num_chipselect = num_cs; + + return info; +} +#else +static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) +{ + return NULL; +} +#endif + static int sh_msiof_spi_probe(struct platform_device *pdev) { struct resource *r; @@ -610,7 +642,17 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) p = spi_master_get_devdata(master); platform_set_drvdata(pdev, p); - p->info = pdev->dev.platform_data; + if (pdev->dev.of_node) + p->info = sh_msiof_spi_parse_dt(&pdev->dev); + else + p->info = pdev->dev.platform_data; + + if (!p->info) { + dev_err(&pdev->dev, "failed to obtain device info\n"); + ret = -ENXIO; + goto err1; + } + init_completion(&p->done); p->clk = clk_get(&pdev->dev, NULL); @@ -715,6 +757,17 @@ static int sh_msiof_spi_runtime_nop(struct device *dev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id sh_msiof_match[] = { + { .compatible = "renesas,sh-msiof", }, + { .compatible = "renesas,sh-mobile-msiof", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sh_msiof_match); +#else +#define sh_msiof_match NULL +#endif + static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = { .runtime_suspend = sh_msiof_spi_runtime_nop, .runtime_resume = sh_msiof_spi_runtime_nop, @@ -727,6 +780,7 @@ static struct platform_driver sh_msiof_spi_drv = { .name = "spi_sh_msiof", .owner = THIS_MODULE, .pm = &sh_msiof_spi_dev_pm_ops, + .of_match_table = sh_msiof_match, }, }; module_platform_driver(sh_msiof_spi_drv); From bb29785e0d6d150181704be2efcc3141044625e2 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 21 Dec 2012 19:32:09 +0000 Subject: [PATCH 17/50] spi/of: Use DT aliases for assigning bus number Linux assigns a number to each spi_master in the system, but when the platform used the device tree, the numbers are dynamically assigned and are not predictable. In general this shouldn't matter since the kernel doesn't use the bus number for anything other than matching a bus to spi_boardinfo (not used for DT). However, sometimes userspace needs to figure out which bus is which, so it makes sense to use the global /aliases namespace to choose a specific bus number. It is safe to derive the bus number from an alias because aliases will never cause two buses to try and use the same bus number. (At one time the cell-index property was used for this purpose, but cell-index has the risk of an id collision). Signed-off-by: Grant Likely Cc: Anatolij Gustschin Cc: Mark Brown --- drivers/spi/spi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 19ee901577da..08ff4acd5225 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1135,6 +1135,9 @@ int spi_register_master(struct spi_master *master) if (master->num_chipselect == 0) return -EINVAL; + if ((master->bus_num < 0) && master->dev.of_node) + master->bus_num = of_alias_get_id(master->dev.of_node, "spi"); + /* convention: dynamically assigned bus IDs count down from the max */ if (master->bus_num < 0) { /* FIXME switch to an IDR based scheme, something like From 766ed70447e0a9cfb23d068a4a929e18e54b0022 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 18 Dec 2012 14:25:43 +0530 Subject: [PATCH 18/50] spi: remove check for bits_per_word on transfer from low level driver The spi core make sure that each transfer structure have the proper setting for bits_per_word before calling low level transfer APIs. Hence it is no more require to check again in low level driver for this field whether this is set correct or not. Removing such code from low level driver. The txx9 change also removes a test for bits_per_word set to 0, and forcing it to 8 in that case. This can also be removed now since spi_setup() ensures spi->bits_per_word is not zero. if (!spi->bits_per_word) spi->bits_per_word = 8; Signed-off-by: Laxman Dewangan Signed-off-by: Grant Likely --- drivers/spi/spi-altera.c | 2 +- drivers/spi/spi-bfin-sport.c | 3 +-- drivers/spi/spi-bfin5xx.c | 3 +-- drivers/spi/spi-bitbang.c | 6 +++--- drivers/spi/spi-clps711x.c | 2 +- drivers/spi/spi-coldfire-qspi.c | 3 +-- drivers/spi/spi-ep93xx.c | 2 +- drivers/spi/spi-s3c64xx.c | 2 +- drivers/spi/spi-sirf.c | 3 +-- drivers/spi/spi-tegra20-slink.c | 9 +++------ drivers/spi/spi-txx9.c | 6 ++---- 11 files changed, 16 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 5e7314ac51e9..a537f8dffc09 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -134,7 +134,7 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) hw->tx = t->tx_buf; hw->rx = t->rx_buf; hw->count = 0; - hw->bytes_per_word = (t->bits_per_word ? : spi->bits_per_word) / 8; + hw->bytes_per_word = t->bits_per_word / 8; hw->len = t->len / hw->bytes_per_word; if (hw->irq >= 0) { diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index ac7ffca7ba47..39b0d1711b4e 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -416,8 +416,7 @@ bfin_sport_spi_pump_transfers(unsigned long data) drv_data->cs_change = transfer->cs_change; /* Bits per word setup */ - bits_per_word = transfer->bits_per_word ? : - message->spi->bits_per_word ? : 8; + bits_per_word = transfer->bits_per_word; if (bits_per_word % 16 == 0) drv_data->ops = &bfin_sport_transfer_ops_u16; else diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 0429d833f75b..7d7c9918fffe 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -642,8 +642,7 @@ static void bfin_spi_pump_transfers(unsigned long data) drv_data->cs_change = transfer->cs_change; /* Bits per word setup */ - bits_per_word = transfer->bits_per_word ? : - message->spi->bits_per_word ? : 8; + bits_per_word = transfer->bits_per_word; if (bits_per_word % 16 == 0) { drv_data->n_bytes = bits_per_word/8; drv_data->len = (transfer->len) >> 1; diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 8b3d8efafd3c..61beaec7cbed 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -69,7 +69,7 @@ static unsigned bitbang_txrx_8( unsigned ns, struct spi_transfer *t ) { - unsigned bits = t->bits_per_word ? : spi->bits_per_word; + unsigned bits = t->bits_per_word; unsigned count = t->len; const u8 *tx = t->tx_buf; u8 *rx = t->rx_buf; @@ -95,7 +95,7 @@ static unsigned bitbang_txrx_16( unsigned ns, struct spi_transfer *t ) { - unsigned bits = t->bits_per_word ? : spi->bits_per_word; + unsigned bits = t->bits_per_word; unsigned count = t->len; const u16 *tx = t->tx_buf; u16 *rx = t->rx_buf; @@ -121,7 +121,7 @@ static unsigned bitbang_txrx_32( unsigned ns, struct spi_transfer *t ) { - unsigned bits = t->bits_per_word ? : spi->bits_per_word; + unsigned bits = t->bits_per_word; unsigned count = t->len; const u32 *tx = t->tx_buf; u32 *rx = t->rx_buf; diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 1366c4620d5d..a11cbf02691a 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -68,7 +68,7 @@ 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; + u8 bpw = xfer->bits_per_word; struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master); if (bpw != 8) { diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 58466b810da4..7b5cc9e4e94d 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -329,8 +329,7 @@ static int mcfqspi_transfer_one_message(struct spi_master *master, mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high); mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE); - if ((t->bits_per_word ? t->bits_per_word : - spi->bits_per_word) == 8) + if (t->bits_per_word == 8) mcfqspi_transfer_msg8(mcfqspi, t->len, t->tx_buf, t->rx_buf); else diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index acb1e1935c5a..aecbff16ad60 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -446,7 +446,7 @@ static inline int bits_per_word(const struct ep93xx_spi *espi) struct spi_message *msg = espi->current_msg; struct spi_transfer *t = msg->state; - return t->bits_per_word ? t->bits_per_word : msg->spi->bits_per_word; + return t->bits_per_word; } static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index ad93231a8038..d77b6560b67f 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -697,7 +697,7 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, INIT_COMPLETION(sdd->xfer_completion); /* Only BPW and Speed may change across transfers */ - bpw = xfer->bits_per_word ? : spi->bits_per_word; + bpw = xfer->bits_per_word; speed = xfer->speed_hz ? : spi->max_speed_hz; if (xfer->len % (bpw / 8)) { diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index e0f43a512e84..750666751efd 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -382,8 +382,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) sspi = spi_master_get_devdata(spi->master); - bits_per_word = t && t->bits_per_word ? t->bits_per_word : - spi->bits_per_word; + bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; hz = t && t->speed_hz ? t->speed_hz : spi->max_speed_hz; /* Enable IO mode for RX, TX */ diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 651167f2e0af..7a95bddfb604 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -284,8 +284,7 @@ static unsigned tegra_slink_calculate_curr_xfer_param( unsigned max_len; unsigned total_fifo_words; - bits_per_word = t->bits_per_word ? t->bits_per_word : - spi->bits_per_word; + bits_per_word = t->bits_per_word; tspi->bytes_per_word = (bits_per_word - 1) / 8 + 1; if (bits_per_word == 8 || bits_per_word == 16) { @@ -378,8 +377,7 @@ static unsigned int tegra_slink_read_rx_fifo_to_client_rxbuf( } else { unsigned int bits_per_word; - bits_per_word = t->bits_per_word ? t->bits_per_word : - tspi->cur_spi->bits_per_word; + bits_per_word = t->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++) @@ -444,8 +442,7 @@ static void tegra_slink_copy_spi_rxbuf_to_client_rxbuf( 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; + bits_per_word = t->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]; diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index d5a3cbb646cb..550b5f48fd8f 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -189,9 +189,8 @@ static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m) unsigned int len = t->len; unsigned int wsize; u32 speed_hz = t->speed_hz ? : spi->max_speed_hz; - u8 bits_per_word = t->bits_per_word ? : spi->bits_per_word; + u8 bits_per_word = t->bits_per_word; - bits_per_word = bits_per_word ? : 8; wsize = bits_per_word >> 3; /* in bytes */ if (prev_speed_hz != speed_hz @@ -316,9 +315,8 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) /* check each transfer's parameters */ list_for_each_entry (t, &m->transfers, transfer_list) { u32 speed_hz = t->speed_hz ? : spi->max_speed_hz; - u8 bits_per_word = t->bits_per_word ? : spi->bits_per_word; + u8 bits_per_word = t->bits_per_word; - bits_per_word = bits_per_word ? : 8; if (!t->tx_buf && !t->rx_buf && t->len) return -EINVAL; if (bits_per_word != 8 && bits_per_word != 16) From e5118cd2c2881db18a92eec68d1b29db9bcbcaf3 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 26 Dec 2012 10:48:33 +0800 Subject: [PATCH 19/50] spi/sirf: 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: Barry Song Signed-off-by: Grant Likely --- drivers/spi/spi-sirf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 750666751efd..1f04979492aa 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -569,7 +569,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) ret = -EINVAL; goto free_pin; } - clk_enable(sspi->clk); + clk_prepare_enable(sspi->clk); sspi->ctrl_freq = clk_get_rate(sspi->clk); init_completion(&sspi->done); @@ -593,7 +593,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) return 0; free_clk: - clk_disable(sspi->clk); + clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); free_pin: pinctrl_put(sspi->p); @@ -617,7 +617,7 @@ static int spi_sirfsoc_remove(struct platform_device *pdev) if (sspi->chipselect[i] > 0) gpio_free(sspi->chipselect[i]); } - clk_disable(sspi->clk); + clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); pinctrl_put(sspi->p); spi_master_put(master); From f3b8a8ecc5922d9dff303ae2fadc1eae608a6f7c Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 26 Dec 2012 10:48:34 +0800 Subject: [PATCH 20/50] spi/sirf: add support for new SiRFmarco SMP SoC the driver is also compatible with SiRFmarco except SiRFprimaII, so simply add "sirf,marco-spi" to OF match table. Signed-off-by: Barry Song Signed-off-by: Grant Likely --- drivers/spi/spi-sirf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 1f04979492aa..6a5626d146b7 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -658,6 +658,7 @@ static const struct dev_pm_ops spi_sirfsoc_pm_ops = { static const struct of_device_id spi_sirfsoc_of_match[] = { { .compatible = "sirf,prima2-spi", }, + { .compatible = "sirf,marco-spi", }, {} }; MODULE_DEVICE_TABLE(of, sirfsoc_spi_of_match); From ba486a2a29b629413f89e0490edb02a8ccc3bb8f Mon Sep 17 00:00:00 2001 From: Juha Lumme Date: Wed, 26 Dec 2012 14:48:51 +0900 Subject: [PATCH 21/50] mxs/spi: clear XFER_COUNT in ctrl0 field in DMA descriptor On MX23 the XFER_COUNT part in ctrl0 field in DMA descriptor was improperly OR'd during the construction of DMA descriptor chain, instead of being freshly set. Because of that too many bytes were being expected from SPI during the last DMA cycle. This caused a timeout (SSP_TIMEOUT) to happen in the processing of the last DMA descriptor, and thus reads and writes were failing. This is a fix for the problem, by clearing XFER_COUNT bytes in ctrl0 before setting the new XFER_COUNT for DMA descriptor. Signed-off-by: Juha Lumme Acked-by: Marek Vasut Signed-off-by: Grant Likely --- drivers/spi/spi-mxs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index a3ede249d05d..e3d8b3197d22 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -241,6 +241,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, INIT_COMPLETION(spi->c); ctrl0 = readl(ssp->base + HW_SSP_CTRL0); + ctrl0 &= ~BM_SSP_CTRL0_XFER_COUNT; ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); if (*first) @@ -256,8 +257,10 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, if ((sg_count + 1 == sgs) && *last) ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC; - if (ssp->devid == IMX23_SSP) + if (ssp->devid == IMX23_SSP) { + ctrl0 &= ~BM_SSP_CTRL0_XFER_COUNT; ctrl0 |= min; + } dma_xfer[sg_count].pio[0] = ctrl0; dma_xfer[sg_count].pio[3] = min; From 440114fdb13cbc53ea734bcc05b86bcf5b1e430c Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 27 Dec 2012 10:42:24 +0100 Subject: [PATCH 22/50] spi/ath79: add delay between SCK changes The driver uses the "as fast as it can" approach to drive the SCK signal. However this does not work with certain low speed SPI chips (e.g. the PCF2123 RTC chip). The patch adds per-bit slowdowns in order to be able to use the driver with such chips as well. Signed-off-by: Gabor Juhos Signed-off-by: Grant Likely --- drivers/spi/spi-ath79.c | 44 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 9a5d7791c5fb..e02528244451 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -24,17 +24,24 @@ #include #include #include +#include +#include #include #include #define DRV_NAME "ath79-spi" +#define ATH79_SPI_RRW_DELAY_FACTOR 12000 +#define MHZ (1000 * 1000) + struct ath79_spi { struct spi_bitbang bitbang; u32 ioc_base; u32 reg_ctrl; void __iomem *base; + struct clk *clk; + unsigned rrw_delay; }; static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg) @@ -52,6 +59,12 @@ static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi) return spi_master_get_devdata(spi->master); } +static inline void ath79_spi_delay(struct ath79_spi *sp, unsigned nsecs) +{ + if (nsecs > sp->rrw_delay) + ndelay(nsecs - sp->rrw_delay); +} + static void ath79_spi_chipselect(struct spi_device *spi, int is_active) { struct ath79_spi *sp = ath79_spidev_to_sp(spi); @@ -184,7 +197,9 @@ static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs, /* setup MSB (to slave) on trailing edge */ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); + ath79_spi_delay(sp, nsecs); ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK); + ath79_spi_delay(sp, nsecs); word <<= 1; } @@ -198,6 +213,7 @@ static int ath79_spi_probe(struct platform_device *pdev) struct ath79_spi *sp; struct ath79_spi_platform_data *pdata; struct resource *r; + unsigned long rate; int ret; master = spi_alloc_master(&pdev->dev, sizeof(*sp)); @@ -236,12 +252,36 @@ static int ath79_spi_probe(struct platform_device *pdev) goto err_put_master; } + sp->clk = clk_get(&pdev->dev, "ahb"); + if (IS_ERR(sp->clk)) { + ret = PTR_ERR(sp->clk); + goto err_unmap; + } + + ret = clk_enable(sp->clk); + if (ret) + goto err_clk_put; + + rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ); + if (!rate) { + ret = -EINVAL; + goto err_clk_disable; + } + + sp->rrw_delay = ATH79_SPI_RRW_DELAY_FACTOR / rate; + dev_dbg(&pdev->dev, "register read/write delay is %u nsecs\n", + sp->rrw_delay); + ret = spi_bitbang_start(&sp->bitbang); if (ret) - goto err_unmap; + goto err_clk_disable; return 0; +err_clk_disable: + clk_disable(sp->clk); +err_clk_put: + clk_put(sp->clk); err_unmap: iounmap(sp->base); err_put_master: @@ -256,6 +296,8 @@ static int ath79_spi_remove(struct platform_device *pdev) struct ath79_spi *sp = platform_get_drvdata(pdev); spi_bitbang_stop(&sp->bitbang); + clk_disable(sp->clk); + clk_put(sp->clk); iounmap(sp->base); platform_set_drvdata(pdev, NULL); spi_master_put(sp->bitbang.master); From 72611db0eef21f4456d79ba302af4b34ea384f30 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 27 Dec 2012 10:42:25 +0100 Subject: [PATCH 23/50] spi/ath79: add missing HIGH->LOW SCK transition The 'ath79_spi_txrx_mode0' function does not set the SCK signal to LOW at the end of a word transfer. This causes communications errors with certain devices (e.g. the PCF2123 RTC chip). The patch ensures that the SCK signal will be LOW. Signed-off-by: Gabor Juhos Signed-off-by: Grant Likely --- drivers/spi/spi-ath79.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index e02528244451..d4b8e1236612 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -200,6 +200,8 @@ static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs, ath79_spi_delay(sp, nsecs); ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK); ath79_spi_delay(sp, nsecs); + if (bits == 1) + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); word <<= 1; } From f1e8fc9898fd8ca78b7438d3c2e60028d3ae2a34 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 27 Dec 2012 10:42:26 +0100 Subject: [PATCH 24/50] spi/ath79: remove superfluous chip select code The spi_bitbang driver calls the chipselect function of the driver from spi_bitbang_setup in order to deselect the given SPI chip, so we don't have to initialize the CS line here. Signed-off-by: Gabor Juhos Signed-off-by: Grant Likely --- drivers/spi/spi-ath79.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index d4b8e1236612..a725e62dfa90 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -128,12 +128,6 @@ static int ath79_spi_setup_cs(struct spi_device *spi) gpio_free(cdata->gpio); return status; } - } else { - if (spi->mode & SPI_CS_HIGH) - sp->ioc_base |= AR71XX_SPI_IOC_CS0; - else - sp->ioc_base &= ~AR71XX_SPI_IOC_CS0; - ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); } return 0; From 95d79419feffb326a3d5cb50e2248129dec06bb0 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 27 Dec 2012 10:42:27 +0100 Subject: [PATCH 25/50] spi/ath79: use gpio_request_one Use gpio_request_one() instead of multiple gpiolib calls. Signed-off-by: Gabor Juhos Signed-off-by: Grant Likely --- drivers/spi/spi-ath79.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index a725e62dfa90..19d539e36300 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -100,6 +100,7 @@ static int ath79_spi_setup_cs(struct spi_device *spi) { struct ath79_spi *sp = ath79_spidev_to_sp(spi); struct ath79_spi_controller_data *cdata; + int status; cdata = spi->controller_data; if (spi->chip_select && !cdata) @@ -115,22 +116,21 @@ static int ath79_spi_setup_cs(struct spi_device *spi) /* TODO: setup speed? */ ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43); + status = 0; if (spi->chip_select) { - int status = 0; + unsigned long flags; - status = gpio_request(cdata->gpio, dev_name(&spi->dev)); - if (status) - return status; + flags = GPIOF_DIR_OUT; + if (spi->mode & SPI_CS_HIGH) + flags |= GPIOF_INIT_HIGH; + else + flags |= GPIOF_INIT_LOW; - status = gpio_direction_output(cdata->gpio, - spi->mode & SPI_CS_HIGH); - if (status) { - gpio_free(cdata->gpio); - return status; - } + status = gpio_request_one(cdata->gpio, flags, + dev_name(&spi->dev)); } - return 0; + return status; } static void ath79_spi_cleanup_cs(struct spi_device *spi) From c4a31f43005512b366e8bfc346e7f14c1a7a1ba7 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 27 Dec 2012 10:42:28 +0100 Subject: [PATCH 26/50] spi/ath79: avoid multiple initialization of the SPI controller Currently we are initializing the SPI controller in the chip select line function, and that function is called once for each SPI device on the bus. If a board has multiple SPI devices, the controller will be initialized multiple times. Introduce ath79_spi_{en,dis}able helper functions, and call those from probe/response in order to avoid the mutliple initialization of the controller. Signed-off-by: Gabor Juhos Signed-off-by: Grant Likely --- drivers/spi/spi-ath79.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 19d539e36300..842acd8983e6 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -96,16 +96,8 @@ static void ath79_spi_chipselect(struct spi_device *spi, int is_active) } -static int ath79_spi_setup_cs(struct spi_device *spi) +static void ath79_spi_enable(struct ath79_spi *sp) { - struct ath79_spi *sp = ath79_spidev_to_sp(spi); - struct ath79_spi_controller_data *cdata; - int status; - - cdata = spi->controller_data; - if (spi->chip_select && !cdata) - return -EINVAL; - /* enable GPIO mode */ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); @@ -115,6 +107,24 @@ static int ath79_spi_setup_cs(struct spi_device *spi) /* TODO: setup speed? */ ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43); +} + +static void ath79_spi_disable(struct ath79_spi *sp) +{ + /* restore CTRL register */ + ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, sp->reg_ctrl); + /* disable GPIO mode */ + ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0); +} + +static int ath79_spi_setup_cs(struct spi_device *spi) +{ + struct ath79_spi_controller_data *cdata; + int status; + + cdata = spi->controller_data; + if (spi->chip_select && !cdata) + return -EINVAL; status = 0; if (spi->chip_select) { @@ -135,17 +145,10 @@ static int ath79_spi_setup_cs(struct spi_device *spi) static void ath79_spi_cleanup_cs(struct spi_device *spi) { - struct ath79_spi *sp = ath79_spidev_to_sp(spi); - if (spi->chip_select) { struct ath79_spi_controller_data *cdata = spi->controller_data; gpio_free(cdata->gpio); } - - /* restore CTRL register */ - ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, sp->reg_ctrl); - /* disable GPIO mode */ - ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0); } static int ath79_spi_setup(struct spi_device *spi) @@ -268,12 +271,15 @@ static int ath79_spi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "register read/write delay is %u nsecs\n", sp->rrw_delay); + ath79_spi_enable(sp); ret = spi_bitbang_start(&sp->bitbang); if (ret) - goto err_clk_disable; + goto err_disable; return 0; +err_disable: + ath79_spi_disable(sp); err_clk_disable: clk_disable(sp->clk); err_clk_put: @@ -292,6 +298,7 @@ static int ath79_spi_remove(struct platform_device *pdev) struct ath79_spi *sp = platform_get_drvdata(pdev); spi_bitbang_stop(&sp->bitbang); + ath79_spi_disable(sp); clk_disable(sp->clk); clk_put(sp->clk); iounmap(sp->base); From 059b8ffeee5b427949872bb6ed5db5ae0788054e Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sat, 5 Jan 2013 00:17:14 +0530 Subject: [PATCH 27/50] spi: make sure all transfer has proper speed set When spi client does the spi transfer and if it does not set the speed for each transfer then set it as default of spi device in spi core before calling low level transfer. This will remove the extra check in low level driver for setting speed. Signed-off-by: Laxman Dewangan Signed-off-by: Grant Likely --- drivers/spi/spi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 08ff4acd5225..2edbe1903804 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1369,12 +1369,14 @@ 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. + * Set transfer bits_per_word and max speed 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; + if (!xfer->speed_hz) + xfer->speed_hz = spi->max_speed_hz; } message->spi = spi; From beb96c2ad4cd5012e39587b960aa7e41661c78ca Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sat, 5 Jan 2013 00:17:15 +0530 Subject: [PATCH 28/50] spi/tegra: remove checks for valid speed SPI core make sure that all transfer has proper speed set before calling low level spi transfer. Hence, it is not require to have check in spi driver. Remove the check for speed validity from transfer and use it directly. Signed-off-by: Laxman Dewangan Signed-off-by: Grant Likely --- drivers/spi/spi-tegra20-sflash.c | 14 +++++++++++--- drivers/spi/spi-tegra20-slink.c | 6 +++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 448a8cc71df3..9a42c158e245 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -269,9 +269,7 @@ static int tegra_sflash_start_transfer_one(struct spi_device *spi, u32 speed; unsigned long command; - speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; - if (!speed) - speed = tsd->spi_max_frequency; + speed = t->speed_hz; if (speed != tsd->cur_speed) { clk_set_rate(tsd->clk, speed); tsd->cur_speed = speed; @@ -319,6 +317,15 @@ static int tegra_sflash_start_transfer_one(struct spi_device *spi, return tegra_sflash_start_cpu_based_transfer(tsd, t); } +static int tegra_sflash_setup(struct spi_device *spi) +{ + struct tegra_sflash_data *tsd = spi_master_get_devdata(spi->master); + + /* Set speed to the spi max fequency if spi device has not set */ + spi->max_speed_hz = spi->max_speed_hz ? : tsd->spi_max_frequency; + return 0; +} + static int tegra_sflash_transfer_one_message(struct spi_master *master, struct spi_message *msg) { @@ -492,6 +499,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA; + master->setup = tegra_sflash_setup; master->transfer_one_message = tegra_sflash_transfer_one_message; master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 7a95bddfb604..8d51db8dea6d 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -725,9 +725,7 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi, unsigned long command2; 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; + speed = t->speed_hz; if (speed != tspi->cur_speed) { clk_set_rate(tspi->clk, speed * 4); tspi->cur_speed = speed; @@ -838,6 +836,8 @@ static int tegra_slink_setup(struct spi_device *spi) BUG_ON(spi->chip_select >= MAX_CHIP_SELECT); + /* Set speed to the spi max fequency if spi device has not set */ + spi->max_speed_hz = spi->max_speed_hz ? : tspi->spi_max_frequency; ret = pm_runtime_get_sync(tspi->dev); if (ret < 0) { dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); From 3133fba3bb3a37cc3d589cb8435e0c0976fb75cd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 7 Jan 2013 20:42:55 -0200 Subject: [PATCH 29/50] spi/imx: Add MODULE_ALIAS() Add an entry for MODULE_ALIAS(). Signed-off-by: Fabio Estevam Signed-off-by: Grant Likely --- drivers/spi/spi-imx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 904913290aa5..0befeeb522f4 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -949,3 +949,4 @@ module_platform_driver(spi_imx_driver); MODULE_DESCRIPTION("SPI Master Controller driver"); MODULE_AUTHOR("Sascha Hauer, Pengutronix"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); From 4eb770067f3ad616d5cc37be4c58eb918fce5062 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 10 Jan 2013 11:04:21 +0900 Subject: [PATCH 30/50] spi/s3c64xx: Use devm_clk_get() and devm_request_irq() Use devm_clk_get() and devm_request_irq() rather than clk_get() and request_irq() to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Grant Likely --- drivers/spi/spi-s3c64xx.c | 43 +++++++++++++++------------------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index a82bfa4af601..1f2f7f8329f1 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1282,7 +1282,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) if (sdd->regs == NULL) { dev_err(&pdev->dev, "Unable to remap IO\n"); ret = -ENXIO; - goto err1; + goto err0; } if (!sci->cfg_gpio && pdev->dev.of_node) { @@ -1291,36 +1291,36 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) } else if (sci->cfg_gpio == NULL || sci->cfg_gpio()) { dev_err(&pdev->dev, "Unable to config gpio\n"); ret = -EBUSY; - goto err2; + goto err0; } /* Setup clocks */ - sdd->clk = clk_get(&pdev->dev, "spi"); + sdd->clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(sdd->clk)) { dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n"); ret = PTR_ERR(sdd->clk); - goto err3; + goto err1; } if (clk_prepare_enable(sdd->clk)) { dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n"); ret = -EBUSY; - goto err4; + goto err1; } sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr); - sdd->src_clk = clk_get(&pdev->dev, clk_name); + sdd->src_clk = devm_clk_get(&pdev->dev, clk_name); if (IS_ERR(sdd->src_clk)) { dev_err(&pdev->dev, "Unable to acquire clock '%s'\n", clk_name); ret = PTR_ERR(sdd->src_clk); - goto err5; + goto err2; } if (clk_prepare_enable(sdd->src_clk)) { dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name); ret = -EBUSY; - goto err6; + goto err2; } /* Setup Deufult Mode */ @@ -1330,11 +1330,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) init_completion(&sdd->xfer_completion); INIT_LIST_HEAD(&sdd->queue); - ret = request_irq(irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd); + ret = devm_request_irq(&pdev->dev, irq, s3c64xx_spi_irq, 0, + "spi-s3c64xx", sdd); if (ret != 0) { dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret); - goto err7; + goto err3; } writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | @@ -1344,7 +1345,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) if (spi_register_master(master)) { dev_err(&pdev->dev, "cannot register SPI master\n"); ret = -EBUSY; - goto err8; + goto err3; } dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " @@ -1358,21 +1359,13 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) return 0; -err8: - free_irq(irq, sdd); -err7: - clk_disable_unprepare(sdd->src_clk); -err6: - clk_put(sdd->src_clk); -err5: - clk_disable_unprepare(sdd->clk); -err4: - clk_put(sdd->clk); err3: + clk_disable_unprepare(sdd->src_clk); +err2: + clk_disable_unprepare(sdd->clk); +err1: if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node) s3c64xx_spi_dt_gpio_free(sdd); -err2: -err1: err0: platform_set_drvdata(pdev, NULL); spi_master_put(master); @@ -1391,13 +1384,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) writel(0, sdd->regs + S3C64XX_SPI_INT_EN); - free_irq(platform_get_irq(pdev, 0), sdd); - clk_disable_unprepare(sdd->src_clk); - clk_put(sdd->src_clk); clk_disable_unprepare(sdd->clk); - clk_put(sdd->clk); if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node) s3c64xx_spi_dt_gpio_free(sdd); From 75bf3361104157b6dcdadc895329a67b1c4f9617 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 31 Jan 2013 15:25:01 +0900 Subject: [PATCH 31/50] spi/s3c64xx: fix checkpatch warnings and error Fix checkpatch warnings and error as below: ERROR: "foo * bar" should be "foo *bar" WARNING: please, no space before tabs WARNING: quoted string split across lines WARNING: msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt Signed-off-by: Jingoo Han Signed-off-by: Grant Likely --- drivers/spi/spi-s3c64xx.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 1f2f7f8329f1..d8928d0592df 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -62,7 +62,7 @@ #define S3C64XX_SPI_CLKSEL_SRCMSK (3<<9) #define S3C64XX_SPI_CLKSEL_SRCSHFT 9 #define S3C64XX_SPI_ENCLK_ENABLE (1<<8) -#define S3C64XX_SPI_PSR_MASK 0xff +#define S3C64XX_SPI_PSR_MASK 0xff #define S3C64XX_SPI_MODE_CH_TSZ_BYTE (0<<29) #define S3C64XX_SPI_MODE_CH_TSZ_HALFWORD (1<<29) @@ -743,8 +743,7 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, sdd->regs + S3C64XX_SPI_SLAVE_SEL); if (status) { - dev_err(&spi->dev, "I/O Error: " - "rx-%d tx-%d res:rx-%c tx-%c len-%d\n", + dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n", xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, (sdd->state & RXBUSY) ? 'f' : 'p', (sdd->state & TXBUSY) ? 'f' : 'p', @@ -799,7 +798,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) /* Acquire DMA channels */ while (!acquire_dma(sdd)) - msleep(10); + usleep_range(10000, 11000); pm_runtime_get_sync(&sdd->pdev->dev); @@ -841,16 +840,14 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( cs = kzalloc(sizeof(*cs), GFP_KERNEL); if (!cs) { - dev_err(&spi->dev, "could not allocate memory for controller" - " data\n"); + dev_err(&spi->dev, "could not allocate memory for controller data\n"); of_node_put(data_np); return ERR_PTR(-ENOMEM); } cs->line = of_get_named_gpio(data_np, "cs-gpio", 0); if (!gpio_is_valid(cs->line)) { - dev_err(&spi->dev, "chip select gpio is not specified or " - "invalid\n"); + dev_err(&spi->dev, "chip select gpio is not specified or invalid\n"); kfree(cs); of_node_put(data_np); return ERR_PTR(-EINVAL); @@ -1078,8 +1075,8 @@ static int s3c64xx_spi_get_dmares( if (!sdd->pdev->dev.of_node) { res = platform_get_resource(pdev, IORESOURCE_DMA, tx ? 0 : 1); if (!res) { - dev_err(&pdev->dev, "Unable to get SPI-%s dma " - "resource\n", chan_str); + dev_err(&pdev->dev, "Unable to get SPI-%s dma resource\n", + chan_str); return -ENXIO; } dma_data->dmach = res->start; @@ -1135,8 +1132,7 @@ static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd) gpio_free(sdd->gpios[idx]); } -static struct s3c64xx_spi_info * s3c64xx_spi_parse_dt( - struct device *dev) +static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) { struct s3c64xx_spi_info *sci; u32 temp; @@ -1148,16 +1144,14 @@ static struct s3c64xx_spi_info * s3c64xx_spi_parse_dt( } if (of_property_read_u32(dev->of_node, "samsung,spi-src-clk", &temp)) { - dev_warn(dev, "spi bus clock parent not specified, using " - "clock at index 0 as parent\n"); + dev_warn(dev, "spi bus clock parent not specified, using clock at index 0 as parent\n"); sci->src_clk_nr = 0; } else { sci->src_clk_nr = temp; } if (of_property_read_u32(dev->of_node, "num-cs", &temp)) { - dev_warn(dev, "number of chip select lines not specified, " - "assuming 1 chip select line\n"); + dev_warn(dev, "number of chip select lines not specified, assuming 1 chip select line\n"); sci->num_cs = 1; } else { sci->num_cs = temp; @@ -1247,8 +1241,8 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) if (pdev->dev.of_node) { ret = of_alias_get_id(pdev->dev.of_node, "spi"); if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id, " - "errno %d\n", ret); + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", + ret); goto err0; } sdd->port_id = ret; @@ -1348,8 +1342,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) goto err3; } - dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " - "with %d Slaves attached\n", + dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", sdd->port_id, master->num_chipselect); dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n", mem_res->end, mem_res->start, From 2deff8d602e8c9a2cab4b070be829294e1211f2c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 5 Feb 2013 13:27:35 +0000 Subject: [PATCH 32/50] spi: Remove erroneous __init, __exit and __exit_p() references in drivers Some of the spi driver module remove hooks were annotated with __exit and referenced with __exit_p(). Presumably these were supposed to be __devinit, __devexit and __devexit_p() since __init/__exit for a probe/remove hook has never been correct. They also got missed during the big __devinit/__devexit purge since they didn't match the pattern. Remove then now to be rid of it. v2: purge __init also Reported-by: Arnd Bergmann [Arnd set a patch cleaning up one, and then I found more] Signed-off-by: Grant Likely --- drivers/spi/spi-atmel.c | 2 +- drivers/spi/spi-au1550.c | 8 ++++---- drivers/spi/spi-bfin5xx.c | 2 +- drivers/spi/spi-omap-100k.c | 6 +++--- drivers/spi/spi-omap-uwire.c | 6 +++--- drivers/spi/spi-orion.c | 8 ++++---- drivers/spi/spi-ppc4xx.c | 6 +++--- drivers/spi/spi-s3c64xx.c | 2 +- drivers/spi/spi-txx9.c | 6 +++--- 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index ab34497bcfee..656d137db253 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1088,7 +1088,7 @@ static struct platform_driver atmel_spi_driver = { .suspend = atmel_spi_suspend, .resume = atmel_spi_resume, .probe = atmel_spi_probe, - .remove = __exit_p(atmel_spi_remove), + .remove = atmel_spi_remove, }; module_platform_driver(atmel_spi_driver); diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 4de66d1cfe51..44dd34b6ad09 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -717,7 +717,7 @@ static void au1550_spi_bits_handlers_set(struct au1550_spi *hw, int bpw) } } -static void __init au1550_spi_setup_psc_as_spi(struct au1550_spi *hw) +static void au1550_spi_setup_psc_as_spi(struct au1550_spi *hw) { u32 stat, cfg; @@ -766,7 +766,7 @@ static void __init au1550_spi_setup_psc_as_spi(struct au1550_spi *hw) } -static int __init au1550_spi_probe(struct platform_device *pdev) +static int au1550_spi_probe(struct platform_device *pdev) { struct au1550_spi *hw; struct spi_master *master; @@ -968,7 +968,7 @@ err_nomem: return err; } -static int __exit au1550_spi_remove(struct platform_device *pdev) +static int au1550_spi_remove(struct platform_device *pdev) { struct au1550_spi *hw = platform_get_drvdata(pdev); @@ -997,7 +997,7 @@ static int __exit au1550_spi_remove(struct platform_device *pdev) MODULE_ALIAS("platform:au1550-spi"); static struct platform_driver au1550_spi_drv = { - .remove = __exit_p(au1550_spi_remove), + .remove = au1550_spi_remove, .driver = { .name = "au1550-spi", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 7d7c9918fffe..317f564c899c 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -1273,7 +1273,7 @@ static int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data) return 0; } -static int __init bfin_spi_probe(struct platform_device *pdev) +static int bfin_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct bfin5xx_spi_master *platform_info; diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 3aef7fa7d5b8..78d29a18dcc4 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -481,7 +481,7 @@ static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) return 0; } -static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k) +static int omap1_spi100k_reset(struct omap1_spi100k *spi100k) { return 0; } @@ -560,7 +560,7 @@ err1: return status; } -static int __exit omap1_spi100k_remove(struct platform_device *pdev) +static int omap1_spi100k_remove(struct platform_device *pdev) { struct spi_master *master; struct omap1_spi100k *spi100k; @@ -604,7 +604,7 @@ static struct platform_driver omap1_spi100k_driver = { .name = "omap1_spi100k", .owner = THIS_MODULE, }, - .remove = __exit_p(omap1_spi100k_remove), + .remove = omap1_spi100k_remove, }; diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index 0a94d9dc9c31..102b233b50c4 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -476,7 +476,7 @@ static void uwire_off(struct uwire_spi *uwire) spi_master_put(uwire->bitbang.master); } -static int __init uwire_probe(struct platform_device *pdev) +static int uwire_probe(struct platform_device *pdev) { struct spi_master *master; struct uwire_spi *uwire; @@ -536,7 +536,7 @@ static int __init uwire_probe(struct platform_device *pdev) return status; } -static int __exit uwire_remove(struct platform_device *pdev) +static int uwire_remove(struct platform_device *pdev) { struct uwire_spi *uwire = dev_get_drvdata(&pdev->dev); int status; @@ -557,7 +557,7 @@ static struct platform_driver uwire_driver = { .name = "omap_uwire", .owner = THIS_MODULE, }, - .remove = __exit_p(uwire_remove), + .remove = uwire_remove, // suspend ... unuse ck // resume ... use ck }; diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index b7e718254b1d..6a3cdc7cfeaf 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -366,7 +366,7 @@ msg_done: return 0; } -static int __init orion_spi_reset(struct orion_spi *orion_spi) +static int orion_spi_reset(struct orion_spi *orion_spi) { /* Verify that the CS is deasserted */ orion_spi_set_cs(orion_spi, 0); @@ -396,7 +396,7 @@ static int orion_spi_setup(struct spi_device *spi) return 0; } -static int __init orion_spi_probe(struct platform_device *pdev) +static int orion_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct orion_spi *spi; @@ -479,7 +479,7 @@ out: } -static int __exit orion_spi_remove(struct platform_device *pdev) +static int orion_spi_remove(struct platform_device *pdev) { struct spi_master *master; struct resource *r; @@ -513,7 +513,7 @@ static struct platform_driver orion_spi_driver = { .owner = THIS_MODULE, .of_match_table = of_match_ptr(orion_spi_of_match_table), }, - .remove = __exit_p(orion_spi_remove), + .remove = orion_spi_remove, }; static int __init orion_spi_init(void) diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 7a85f22b6474..424b333fab18 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -389,7 +389,7 @@ static void free_gpios(struct ppc4xx_spi *hw) /* * platform_device layer stuff... */ -static int __init spi_ppc4xx_of_probe(struct platform_device *op) +static int spi_ppc4xx_of_probe(struct platform_device *op) { struct ppc4xx_spi *hw; struct spi_master *master; @@ -560,7 +560,7 @@ free_master: return ret; } -static int __exit spi_ppc4xx_of_remove(struct platform_device *op) +static int spi_ppc4xx_of_remove(struct platform_device *op) { struct spi_master *master = dev_get_drvdata(&op->dev); struct ppc4xx_spi *hw = spi_master_get_devdata(master); @@ -583,7 +583,7 @@ MODULE_DEVICE_TABLE(of, spi_ppc4xx_of_match); static struct platform_driver spi_ppc4xx_of_driver = { .probe = spi_ppc4xx_of_probe, - .remove = __exit_p(spi_ppc4xx_of_remove), + .remove = spi_ppc4xx_of_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index d8928d0592df..8f492ed317cc 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1191,7 +1191,7 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( platform_get_device_id(pdev)->driver_data; } -static int __init s3c64xx_spi_probe(struct platform_device *pdev) +static int s3c64xx_spi_probe(struct platform_device *pdev) { struct resource *mem_res; struct s3c64xx_spi_driver_data *sdd; diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index 550b5f48fd8f..adb853047926 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -335,7 +335,7 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) return 0; } -static int __init txx9spi_probe(struct platform_device *dev) +static int txx9spi_probe(struct platform_device *dev) { struct spi_master *master; struct txx9spi *c; @@ -430,7 +430,7 @@ exit: return ret; } -static int __exit txx9spi_remove(struct platform_device *dev) +static int txx9spi_remove(struct platform_device *dev) { struct spi_master *master = spi_master_get(platform_get_drvdata(dev)); struct txx9spi *c = spi_master_get_devdata(master); @@ -448,7 +448,7 @@ static int __exit txx9spi_remove(struct platform_device *dev) MODULE_ALIAS("platform:spi_txx9"); static struct platform_driver txx9spi_driver = { - .remove = __exit_p(txx9spi_remove), + .remove = txx9spi_remove, .driver = { .name = "spi_txx9", .owner = THIS_MODULE, From 9d15a3bac39e0a7945c463c26fd25c1c58280c68 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 11 Jan 2013 01:05:48 +0100 Subject: [PATCH 33/50] spi/mpc512x-psc: don't use obsolet cell-index property Remove deprecated cell-index property and use spi alias to obtain the SPI PSC number used for SPI bus id. Signed-off-by: Anatolij Gustschin Signed-off-by: Grant Likely --- drivers/spi/spi-mpc512x-psc.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index cb3a3106bd4f..88e544151f6f 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -522,17 +522,11 @@ static int mpc512x_psc_spi_of_probe(struct platform_device *op) regaddr64 = of_translate_address(op->dev.of_node, regaddr_p); /* get PSC id (0..11, used by port_config) */ - if (op->dev.platform_data == NULL) { - const u32 *psc_nump; - - psc_nump = of_get_property(op->dev.of_node, "cell-index", NULL); - if (!psc_nump || *psc_nump > 11) { - dev_err(&op->dev, "mpc512x_psc_spi: Device node %s " - "has invalid cell-index property\n", - op->dev.of_node->full_name); - return -EINVAL; - } - id = *psc_nump; + id = of_alias_get_id(op->dev.of_node, "spi"); + if (id < 0) { + dev_err(&op->dev, "no alias id for %s\n", + op->dev.of_node->full_name); + return id; } return mpc512x_psc_spi_do_probe(&op->dev, (u32) regaddr64, (u32) size64, From c88dd349b5a83c9efa4030176a5184a9a1440475 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 14 Jan 2013 21:27:00 +0100 Subject: [PATCH 34/50] spi/spi-mpc512x-psc: init mode bits supported by the driver The driver should setup mode bits it supports, otherwise adding an SPI device might fail even if the driver supports the requested SPI mode. Signed-off-by: Anatolij Gustschin Signed-off-by: Grant Likely --- drivers/spi/spi-mpc512x-psc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 88e544151f6f..89480b281d74 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -438,6 +438,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, master->num_chipselect = pdata->max_chipselect; } + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; master->setup = mpc512x_psc_spi_setup; master->transfer = mpc512x_psc_spi_transfer; master->cleanup = mpc512x_psc_spi_cleanup; From 2cd94c8a1b4184b48ecbc16b353dcbb513053285 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 14:35:04 +0800 Subject: [PATCH 35/50] spi: Ensure memory used for spi_write_then_read() is DMA safe Use GFP_DMA in order to ensure that the memory we allocate for transfers in spi_write_then_read() can be DMAed. On most platforms this will have no effect. Signed-off-by: Mark Brown Signed-off-by: Grant Likely --- drivers/spi/spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 2edbe1903804..d1e0a316826d 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1661,7 +1661,8 @@ 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((unsigned)SPI_BUFSIZ, n_tx + n_rx), GFP_KERNEL); + local_buf = kmalloc(max((unsigned)SPI_BUFSIZ, n_tx + n_rx), + GFP_KERNEL | GFP_DMA); if (!local_buf) return -ENOMEM; } else { From c0fde3ba0e03af30f9fa9cdf180442189b3f7e9c Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sun, 3 Feb 2013 15:15:12 +0100 Subject: [PATCH 36/50] spi/bcm63xx: reject transfers unable to transfer The hardware does not support keeping CS asserted after sending one FIFO buffer worth of data, so reject transfers requiring CS being kept asserted, either between transers or for a certain time after it, or exceeding the FIFO size. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Grant Likely --- drivers/spi/spi-bcm63xx.c | 93 ++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index f44ab5508535..27667c17ce90 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -49,16 +49,10 @@ struct bcm63xx_spi { unsigned int msg_type_shift; unsigned int msg_ctl_width; - /* Data buffers */ - const unsigned char *tx_ptr; - unsigned char *rx_ptr; - /* data iomem */ u8 __iomem *tx_io; const u8 __iomem *rx_io; - int remaining_bytes; - struct clk *clk; struct platform_device *pdev; }; @@ -175,24 +169,13 @@ static int bcm63xx_spi_setup(struct spi_device *spi) return 0; } -/* Fill the TX FIFO with as many bytes as possible */ -static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) -{ - u8 size; - - /* Fill the Tx FIFO with as many bytes as possible */ - size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes : - bs->fifo_size; - memcpy_toio(bs->tx_io, bs->tx_ptr, size); - bs->remaining_bytes -= size; -} - -static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, - struct spi_transfer *t) +static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) { struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); u16 msg_ctl; u16 cmd; + u8 rx_tail; + unsigned int timeout = 0; /* Disable the CMD_DONE interrupt */ bcm_spi_writeb(bs, 0, SPI_INT_MASK); @@ -200,14 +183,8 @@ static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf, t->len); - /* Transmitter is inhibited */ - bs->tx_ptr = t->tx_buf; - bs->rx_ptr = t->rx_buf; - - if (t->tx_buf) { - bs->remaining_bytes = t->len; - bcm63xx_spi_fill_tx_fifo(bs); - } + if (t->tx_buf) + memcpy_toio(bs->tx_io, t->tx_buf, t->len); init_completion(&bs->done); @@ -239,7 +216,18 @@ static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, /* Enable the CMD_DONE interrupt */ bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); - return t->len - bs->remaining_bytes; + timeout = wait_for_completion_timeout(&bs->done, HZ); + if (!timeout) + return -ETIMEDOUT; + + /* read out all data */ + rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); + + /* Read out all the data */ + if (rx_tail) + memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail); + + return 0; } static int bcm63xx_spi_prepare_transfer(struct spi_master *master) @@ -267,36 +255,41 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master, struct spi_transfer *t; struct spi_device *spi = m->spi; int status = 0; - unsigned int timeout = 0; list_for_each_entry(t, &m->transfers, transfer_list) { - unsigned int len = t->len; - u8 rx_tail; - status = bcm63xx_spi_check_transfer(spi, t); if (status < 0) goto exit; + /* we can only transfer one fifo worth of data */ + if (t->len > bs->fifo_size) { + dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", + t->len, bs->fifo_size); + status = -EINVAL; + goto exit; + } + + /* CS will be deasserted directly after transfer */ + if (t->delay_usecs) { + dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); + status = -EINVAL; + goto exit; + } + + if (!t->cs_change && + !list_is_last(&t->transfer_list, &m->transfers)) { + dev_err(&spi->dev, "unable to keep CS asserted between transfers\n"); + status = -EINVAL; + goto exit; + } + /* configure adapter for a new transfer */ bcm63xx_spi_setup_transfer(spi, t); - while (len) { - /* send the data */ - len -= bcm63xx_txrx_bufs(spi, t); - - timeout = wait_for_completion_timeout(&bs->done, HZ); - if (!timeout) { - status = -ETIMEDOUT; - goto exit; - } - - /* read out all data */ - rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); - - /* Read out all the data */ - if (rx_tail) - memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); - } + /* send the data */ + status = bcm63xx_txrx_bufs(spi, t); + if (status) + goto exit; m->actual_length += t->len; } From 41ab724aac00cae42e649496c2567e9ef45dd753 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 4 Feb 2013 09:26:26 -0300 Subject: [PATCH 37/50] spi/orion: Use module_platform_driver() This patch reduces and simplifies initalization code by using module_platform_driver(). With this change it's necessary to remove the __init annotation to avoid section mismatch warnings. Signed-off-by: Ezequiel Garcia Signed-off-by: Grant Likely --- drivers/spi/spi-orion.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 6a3cdc7cfeaf..66a5f82cf138 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -513,20 +513,11 @@ static struct platform_driver orion_spi_driver = { .owner = THIS_MODULE, .of_match_table = of_match_ptr(orion_spi_of_match_table), }, + .probe = orion_spi_probe, .remove = orion_spi_remove, }; -static int __init orion_spi_init(void) -{ - return platform_driver_probe(&orion_spi_driver, orion_spi_probe); -} -module_init(orion_spi_init); - -static void __exit orion_spi_exit(void) -{ - platform_driver_unregister(&orion_spi_driver); -} -module_exit(orion_spi_exit); +module_platform_driver(orion_spi_driver); MODULE_DESCRIPTION("Orion SPI driver"); MODULE_AUTHOR("Shadi Ammouri "); From 32310aaf5c410dd3dc701556cc9bbeff0847c7c9 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Fri, 21 Dec 2012 15:13:26 -0500 Subject: [PATCH 38/50] spi/davinci: use request_threaded_irq() to fix deadlock With RT pre-empt patch applied to Linux kernel, the irq handler will be force converted to an irq thread. spi driver can get back to back messages from the slave device. In such cases, IRQ thread doesn't get a chance to run to read the slave data. Hence the irq handler must be run in hard irq context to read/write data from slave device. Otherwise, the kernel goes into a deadlock. This patch fixes this issue when PREEMPT_RT_FULL is enabled in the kernel. A dummy thread function is provided to satisfy the request_threaded_irq() API. Passing a NULL for function also causes the irq handler to be executed in the thread context. Signed-off-by: Murali Karicheri Signed-off-by: Grant Likely --- drivers/spi/spi-davinci.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 50bd2cdc52de..8234d2259722 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -701,6 +701,19 @@ err_alloc_dummy_buf: return ret; } +/** + * dummy_thread_fn - dummy thread function + * @irq: IRQ number for this SPI Master + * @context_data: structure for SPI Master controller davinci_spi + * + * This is to satisfy the request_threaded_irq() API so that the irq + * handler is called in interrupt context. + */ +static irqreturn_t dummy_thread_fn(s32 irq, void *data) +{ + return IRQ_HANDLED; +} + /** * davinci_spi_irq - Interrupt handler for SPI Master Controller * @irq: IRQ number for this SPI Master @@ -899,8 +912,8 @@ static int davinci_spi_probe(struct platform_device *pdev) goto unmap_io; } - ret = request_irq(dspi->irq, davinci_spi_irq, 0, dev_name(&pdev->dev), - dspi); + ret = request_threaded_irq(dspi->irq, davinci_spi_irq, dummy_thread_fn, + 0, dev_name(&pdev->dev), dspi); if (ret) goto unmap_io; From b17de076062a677b11ec5054d61184d0134e851f Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sun, 3 Feb 2013 15:15:13 +0100 Subject: [PATCH 39/50] spi/bcm63xx: work around inability to keep CS up This SPI controller does not support keeping CS asserted after sending a transfer. Since messages expected on this SPI controller are rather short, we can work around it for normal use cases by sending all transfers at once in a big full duplex stream. This means that we cannot change the speed between transfers if they require CS to be kept asserted, but these would have been rejected before anyway because of the inability of keeping CS asserted. Signed-off-by: Jonas Gorski Signed-off-by: Grant Likely --- drivers/spi/spi-bcm63xx.c | 136 ++++++++++++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 29 deletions(-) diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 27667c17ce90..9578af782a77 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -37,6 +37,8 @@ #define PFX KBUILD_MODNAME +#define BCM63XX_SPI_MAX_PREPEND 15 + struct bcm63xx_spi { struct completion done; @@ -169,13 +171,17 @@ static int bcm63xx_spi_setup(struct spi_device *spi) return 0; } -static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, + unsigned int num_transfers) { struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); u16 msg_ctl; u16 cmd; u8 rx_tail; - unsigned int timeout = 0; + unsigned int i, timeout = 0, prepend_len = 0, len = 0; + struct spi_transfer *t = first; + bool do_rx = false; + bool do_tx = false; /* Disable the CMD_DONE interrupt */ bcm_spi_writeb(bs, 0, SPI_INT_MASK); @@ -183,19 +189,45 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf, t->len); - if (t->tx_buf) - memcpy_toio(bs->tx_io, t->tx_buf, t->len); + if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) + prepend_len = t->len; + + /* prepare the buffer */ + for (i = 0; i < num_transfers; i++) { + if (t->tx_buf) { + do_tx = true; + memcpy_toio(bs->tx_io + len, t->tx_buf, t->len); + + /* don't prepend more than one tx */ + if (t != first) + prepend_len = 0; + } + + if (t->rx_buf) { + do_rx = true; + /* prepend is half-duplex write only */ + if (t == first) + prepend_len = 0; + } + + len += t->len; + + t = list_entry(t->transfer_list.next, struct spi_transfer, + transfer_list); + } + + len -= prepend_len; init_completion(&bs->done); /* Fill in the Message control register */ - msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); + msg_ctl = (len << SPI_BYTE_CNT_SHIFT); - if (t->rx_buf && t->tx_buf) + if (do_rx && do_tx && prepend_len == 0) msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); - else if (t->rx_buf) + else if (do_rx) msg_ctl |= (SPI_HD_R << bs->msg_type_shift); - else if (t->tx_buf) + else if (do_tx) msg_ctl |= (SPI_HD_W << bs->msg_type_shift); switch (bs->msg_ctl_width) { @@ -209,7 +241,7 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) /* Issue the transfer */ cmd = SPI_CMD_START_IMMEDIATE; - cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); + cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); bcm_spi_writew(bs, cmd, SPI_CMD); @@ -223,9 +255,25 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) /* read out all data */ rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); + if (do_rx && rx_tail != len) + return -EIO; + + if (!rx_tail) + return 0; + + len = 0; + t = first; /* Read out all the data */ - if (rx_tail) - memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail); + for (i = 0; i < num_transfers; i++) { + if (t->rx_buf) + memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len); + + if (t != first || prepend_len == 0) + len += t->len; + + t = list_entry(t->transfer_list.next, struct spi_transfer, + transfer_list); + } return 0; } @@ -252,19 +300,48 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master, struct spi_message *m) { struct bcm63xx_spi *bs = spi_master_get_devdata(master); - struct spi_transfer *t; + struct spi_transfer *t, *first = NULL; struct spi_device *spi = m->spi; int status = 0; + unsigned int n_transfers = 0, total_len = 0; + bool can_use_prepend = false; + /* + * This SPI controller does not support keeping CS active after a + * transfer. + * Work around this by merging as many transfers we can into one big + * full-duplex transfers. + */ list_for_each_entry(t, &m->transfers, transfer_list) { status = bcm63xx_spi_check_transfer(spi, t); if (status < 0) goto exit; + if (!first) + first = t; + + n_transfers++; + total_len += t->len; + + if (n_transfers == 2 && !first->rx_buf && !t->tx_buf && + first->len <= BCM63XX_SPI_MAX_PREPEND) + can_use_prepend = true; + else if (can_use_prepend && t->tx_buf) + can_use_prepend = false; + /* we can only transfer one fifo worth of data */ - if (t->len > bs->fifo_size) { + if ((can_use_prepend && + total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || + (!can_use_prepend && total_len > bs->fifo_size)) { dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", - t->len, bs->fifo_size); + total_len, bs->fifo_size); + status = -EINVAL; + goto exit; + } + + /* all combined transfers have to have the same speed */ + if (t->speed_hz != first->speed_hz) { + dev_err(&spi->dev, "unable to change speed between transfers\n"); status = -EINVAL; goto exit; } @@ -276,22 +353,23 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master, goto exit; } - if (!t->cs_change && - !list_is_last(&t->transfer_list, &m->transfers)) { - dev_err(&spi->dev, "unable to keep CS asserted between transfers\n"); - status = -EINVAL; - goto exit; + if (t->cs_change || + list_is_last(&t->transfer_list, &m->transfers)) { + /* configure adapter for a new transfer */ + bcm63xx_spi_setup_transfer(spi, first); + + /* send the data */ + status = bcm63xx_txrx_bufs(spi, first, n_transfers); + if (status) + goto exit; + + m->actual_length += total_len; + + first = NULL; + n_transfers = 0; + total_len = 0; + can_use_prepend = false; } - - /* configure adapter for a new transfer */ - bcm63xx_spi_setup_transfer(spi, t); - - /* send the data */ - status = bcm63xx_txrx_bufs(spi, t); - if (status) - goto exit; - - m->actual_length += t->len; } exit: m->status = status; From 737a7c43807b9d01bc01e741b2095c8b4dcc1b13 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 30 Jan 2013 21:33:30 +0100 Subject: [PATCH 40/50] spi/mips-lantiq: make use of spi_finalize_current_message Rather than calling m->complete() directly we choose the sane way and call spi_finalize_current_message instead. Signed-off-by: Thomas Langer Signed-off-by: John Crispin Signed-off-by: Grant Likely --- drivers/spi/spi-falcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index 6a6f62ec2840..f9c66c224a17 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -398,7 +398,7 @@ static int falcon_sflash_xfer_one(struct spi_master *master, } m->status = ret; - m->complete(m->context); + spi_finalize_current_message(master); return 0; } From ec3eaeca916ae6e86925712741559c78ff9c5c49 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 30 Jan 2013 21:33:31 +0100 Subject: [PATCH 41/50] spi/mips-lantiq: set SPI_MASTER_HALF_DUPLEX flag Due to hardware limitations of the spi/flash frontend of the EBU we need to set the SPI_MASTER_HALF_DUPLEX flag. Signed-off-by: Thomas Langer Signed-off-by: John Crispin Signed-off-by: Grant Likely --- drivers/spi/spi-falcon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index f9c66c224a17..c7a74f0ef892 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -423,6 +423,7 @@ static int falcon_sflash_probe(struct platform_device *pdev) master->mode_bits = SPI_MODE_3; master->num_chipselect = 1; + master->flags = SPI_MASTER_HALF_DUPLEX; master->bus_num = -1; master->setup = falcon_sflash_setup; master->prepare_transfer_hardware = falcon_sflash_prepare_xfer; From 7410e848583f9120dd5f9414629f01bb76b5ee5f Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 5 Feb 2013 20:57:55 +0100 Subject: [PATCH 42/50] spi/ath79: add shutdown handler The SPI controller of the AR7xxx/AR9xxx SoCs have a special mode which allows the SoC to directly read data from SPI flash chips. In this mode, the content of the SPI flash chip can be accessed via a memory mapped region. During early init time, the kernel expects that the flash chip is accessible through that memory region because it reads board specific values (e.g. MAC address, WiFi calibration data) from the flash on various boards. This is working if the kernel is loaded directly by the bootloader because that leaves the SPI controller in the special mode. However it is not working in a kexec'd kernel because the SPI driver does not restore the special mode during shutdown. The patch adds a shutdown handler to fix this issue. Signed-off-by: Gabor Juhos Signed-off-by: Grant Likely --- drivers/spi/spi-ath79.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 842acd8983e6..e504b7636058 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -308,9 +308,15 @@ static int ath79_spi_remove(struct platform_device *pdev) return 0; } +static void ath79_spi_shutdown(struct platform_device *pdev) +{ + ath79_spi_remove(pdev); +} + static struct platform_driver ath79_spi_driver = { .probe = ath79_spi_probe, .remove = ath79_spi_remove, + .shutdown = ath79_spi_shutdown, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, From cd7bed00340475ee72a013a070e200e065085ef3 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:28 +0200 Subject: [PATCH 43/50] spi/pxa2xx: break out the private DMA API usage into a separate file The PXA SPI driver uses PXA platform specific private DMA implementation which does not work on non-PXA platforms. In order to use this driver on other platforms we break out the private DMA implementation into a separate file that gets compiled only when CONFIG_SPI_PXA2XX_PXADMA is set. The DMA functions are stubbed out if there is no DMA implementation selected (i.e we are building on non-PXA platform). While we are there we can kill the dummy DMA bits in pxa2xx_spi.h as they are not needed anymore for CE4100. Once this is done we can add the generic DMA engine support to the driver that allows usage of any DMA controller that implements DMA engine API. Signed-off-by: Mika Westerberg Acked-by: Linus Walleij Tested-by: Lu Cao Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 4 +- drivers/spi/spi-pxa2xx-pxadma.c | 490 ++++++++++++++++++++++++++ drivers/spi/spi-pxa2xx.c | 593 ++------------------------------ drivers/spi/spi-pxa2xx.h | 185 ++++++++++ include/linux/spi/pxa2xx_spi.h | 80 ----- 6 files changed, 711 insertions(+), 647 deletions(-) create mode 100644 drivers/spi/spi-pxa2xx-pxadma.c create mode 100644 drivers/spi/spi-pxa2xx.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a90393d7f106..f1878666e917 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -297,6 +297,12 @@ config SPI_PPC4xx help This selects a driver for the PPC4xx SPI Controller. +config SPI_PXA2XX_PXADMA + bool "PXA2xx SSP legacy PXA DMA API support" + depends on SPI_PXA2XX && ARCH_PXA + help + Enable PXA private legacy DMA API support. + config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on ARCH_PXA || PCI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 64e970ba261c..2e3cdd3caba9 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -47,7 +47,9 @@ obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o obj-$(CONFIG_SPI_ORION) += spi-orion.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o -obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o +spi-pxa2xx-platform-objs := spi-pxa2xx.o +spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o +obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c new file mode 100644 index 000000000000..2916efc7cfe5 --- /dev/null +++ b/drivers/spi/spi-pxa2xx-pxadma.c @@ -0,0 +1,490 @@ +/* + * PXA2xx SPI private DMA support. + * + * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * + * 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. + * + * This program is distributed in the hope that 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "spi-pxa2xx.h" + +#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) +#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) + +bool pxa2xx_spi_dma_is_possible(size_t len) +{ + /* Try to map dma buffer and do a dma transfer if successful, but + * only if the length is non-zero and less than MAX_DMA_LEN. + * + * Zero-length non-descriptor DMA is illegal on PXA2xx; force use + * of PIO instead. Care is needed above because the transfer may + * have have been passed with buffers that are already dma mapped. + * A zero-length transfer in PIO mode will not try to write/read + * to/from the buffers + * + * REVISIT large transfers are exactly where we most want to be + * using DMA. If this happens much, split those transfers into + * multiple DMA segments rather than forcing PIO. + */ + return len > 0 && len <= MAX_DMA_LEN; +} + +int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + struct device *dev = &msg->spi->dev; + + if (!drv_data->cur_chip->enable_dma) + return 0; + + if (msg->is_dma_mapped) + return drv_data->rx_dma && drv_data->tx_dma; + + if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) + return 0; + + /* Modify setup if rx buffer is null */ + if (drv_data->rx == NULL) { + *drv_data->null_dma_buf = 0; + drv_data->rx = drv_data->null_dma_buf; + drv_data->rx_map_len = 4; + } else + drv_data->rx_map_len = drv_data->len; + + + /* Modify setup if tx buffer is null */ + if (drv_data->tx == NULL) { + *drv_data->null_dma_buf = 0; + drv_data->tx = drv_data->null_dma_buf; + drv_data->tx_map_len = 4; + } else + drv_data->tx_map_len = drv_data->len; + + /* Stream map the tx buffer. Always do DMA_TO_DEVICE first + * so we flush the cache *before* invalidating it, in case + * the tx and rx buffers overlap. + */ + drv_data->tx_dma = dma_map_single(dev, drv_data->tx, + drv_data->tx_map_len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, drv_data->tx_dma)) + return 0; + + /* Stream map the rx buffer */ + drv_data->rx_dma = dma_map_single(dev, drv_data->rx, + drv_data->rx_map_len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, drv_data->rx_dma)) { + dma_unmap_single(dev, drv_data->tx_dma, + drv_data->tx_map_len, DMA_TO_DEVICE); + return 0; + } + + return 1; +} + +static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data) +{ + struct device *dev; + + if (!drv_data->dma_mapped) + return; + + if (!drv_data->cur_msg->is_dma_mapped) { + dev = &drv_data->cur_msg->spi->dev; + dma_unmap_single(dev, drv_data->rx_dma, + drv_data->rx_map_len, DMA_FROM_DEVICE); + dma_unmap_single(dev, drv_data->tx_dma, + drv_data->tx_map_len, DMA_TO_DEVICE); + } + + drv_data->dma_mapped = 0; +} + +static int wait_ssp_rx_stall(void const __iomem *ioaddr) +{ + unsigned long limit = loops_per_jiffy << 1; + + while ((read_SSSR(ioaddr) & SSSR_BSY) && --limit) + cpu_relax(); + + return limit; +} + +static int wait_dma_channel_stop(int channel) +{ + unsigned long limit = loops_per_jiffy << 1; + + while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit) + cpu_relax(); + + return limit; +} + +static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data, + const char *msg) +{ + void __iomem *reg = drv_data->ioaddr; + + /* Stop and reset */ + DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; + DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; + write_SSSR_CS(drv_data, drv_data->clear_sr); + write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + if (!pxa25x_ssp_comp(drv_data)) + write_SSTO(0, reg); + pxa2xx_spi_flush(drv_data); + write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + + pxa2xx_spi_unmap_dma_buffers(drv_data); + + dev_err(&drv_data->pdev->dev, "%s\n", msg); + + drv_data->cur_msg->state = ERROR_STATE; + tasklet_schedule(&drv_data->pump_transfers); +} + +static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data) +{ + void __iomem *reg = drv_data->ioaddr; + struct spi_message *msg = drv_data->cur_msg; + + /* Clear and disable interrupts on SSP and DMA channels*/ + write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); + DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; + DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; + + if (wait_dma_channel_stop(drv_data->rx_channel) == 0) + dev_err(&drv_data->pdev->dev, + "dma_handler: dma rx channel stop failed\n"); + + if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) + dev_err(&drv_data->pdev->dev, + "dma_transfer: ssp rx stall failed\n"); + + pxa2xx_spi_unmap_dma_buffers(drv_data); + + /* update the buffer pointer for the amount completed in dma */ + drv_data->rx += drv_data->len - + (DCMD(drv_data->rx_channel) & DCMD_LENGTH); + + /* read trailing data from fifo, it does not matter how many + * bytes are in the fifo just read until buffer is full + * or fifo is empty, which ever occurs first */ + drv_data->read(drv_data); + + /* return count of what was actually read */ + msg->actual_length += drv_data->len - + (drv_data->rx_end - drv_data->rx); + + /* Transfer delays and chip select release are + * handled in pump_transfers or giveback + */ + + /* Move to next transfer */ + msg->state = pxa2xx_spi_next_transfer(drv_data); + + /* Schedule transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); +} + +void pxa2xx_spi_dma_handler(int channel, void *data) +{ + struct driver_data *drv_data = data; + u32 irq_status = DCSR(channel) & DMA_INT_MASK; + + if (irq_status & DCSR_BUSERR) { + + if (channel == drv_data->tx_channel) + pxa2xx_spi_dma_error_stop(drv_data, + "dma_handler: bad bus address on tx channel"); + else + pxa2xx_spi_dma_error_stop(drv_data, + "dma_handler: bad bus address on rx channel"); + return; + } + + /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */ + if ((channel == drv_data->tx_channel) + && (irq_status & DCSR_ENDINTR) + && (drv_data->ssp_type == PXA25x_SSP)) { + + /* Wait for rx to stall */ + if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) + dev_err(&drv_data->pdev->dev, + "dma_handler: ssp rx stall failed\n"); + + /* finish this transfer, start the next */ + pxa2xx_spi_dma_transfer_complete(drv_data); + } +} + +irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) +{ + u32 irq_status; + void __iomem *reg = drv_data->ioaddr; + + irq_status = read_SSSR(reg) & drv_data->mask_sr; + if (irq_status & SSSR_ROR) { + pxa2xx_spi_dma_error_stop(drv_data, + "dma_transfer: fifo overrun"); + return IRQ_HANDLED; + } + + /* Check for false positive timeout */ + if ((irq_status & SSSR_TINT) + && (DCSR(drv_data->tx_channel) & DCSR_RUN)) { + write_SSSR(SSSR_TINT, reg); + return IRQ_HANDLED; + } + + if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) { + + /* Clear and disable timeout interrupt, do the rest in + * dma_transfer_complete */ + if (!pxa25x_ssp_comp(drv_data)) + write_SSTO(0, reg); + + /* finish this transfer, start the next */ + pxa2xx_spi_dma_transfer_complete(drv_data); + + return IRQ_HANDLED; + } + + /* Opps problem detected */ + return IRQ_NONE; +} + +int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst) +{ + u32 dma_width; + + switch (drv_data->n_bytes) { + case 1: + dma_width = DCMD_WIDTH1; + break; + case 2: + dma_width = DCMD_WIDTH2; + break; + default: + dma_width = DCMD_WIDTH4; + break; + } + + /* Setup rx DMA Channel */ + DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; + DSADR(drv_data->rx_channel) = drv_data->ssdr_physical; + DTADR(drv_data->rx_channel) = drv_data->rx_dma; + if (drv_data->rx == drv_data->null_dma_buf) + /* No target address increment */ + DCMD(drv_data->rx_channel) = DCMD_FLOWSRC + | dma_width + | dma_burst + | drv_data->len; + else + DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR + | DCMD_FLOWSRC + | dma_width + | dma_burst + | drv_data->len; + + /* Setup tx DMA Channel */ + DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; + DSADR(drv_data->tx_channel) = drv_data->tx_dma; + DTADR(drv_data->tx_channel) = drv_data->ssdr_physical; + if (drv_data->tx == drv_data->null_dma_buf) + /* No source address increment */ + DCMD(drv_data->tx_channel) = DCMD_FLOWTRG + | dma_width + | dma_burst + | drv_data->len; + else + DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR + | DCMD_FLOWTRG + | dma_width + | dma_burst + | drv_data->len; + + /* Enable dma end irqs on SSP to detect end of transfer */ + if (drv_data->ssp_type == PXA25x_SSP) + DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN; + + return 0; +} + +void pxa2xx_spi_dma_start(struct driver_data *drv_data) +{ + DCSR(drv_data->rx_channel) |= DCSR_RUN; + DCSR(drv_data->tx_channel) |= DCSR_RUN; +} + +int pxa2xx_spi_dma_setup(struct driver_data *drv_data) +{ + struct device *dev = &drv_data->pdev->dev; + struct ssp_device *ssp = drv_data->ssp; + + /* Get two DMA channels (rx and tx) */ + drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx", + DMA_PRIO_HIGH, + pxa2xx_spi_dma_handler, + drv_data); + if (drv_data->rx_channel < 0) { + dev_err(dev, "problem (%d) requesting rx channel\n", + drv_data->rx_channel); + return -ENODEV; + } + drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx", + DMA_PRIO_MEDIUM, + pxa2xx_spi_dma_handler, + drv_data); + if (drv_data->tx_channel < 0) { + dev_err(dev, "problem (%d) requesting tx channel\n", + drv_data->tx_channel); + pxa_free_dma(drv_data->rx_channel); + return -ENODEV; + } + + DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel; + DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel; + + return 0; +} + +void pxa2xx_spi_dma_release(struct driver_data *drv_data) +{ + struct ssp_device *ssp = drv_data->ssp; + + DRCMR(ssp->drcmr_rx) = 0; + DRCMR(ssp->drcmr_tx) = 0; + + if (drv_data->tx_channel != 0) + pxa_free_dma(drv_data->tx_channel); + if (drv_data->rx_channel != 0) + pxa_free_dma(drv_data->rx_channel); +} + +void pxa2xx_spi_dma_resume(struct driver_data *drv_data) +{ + if (drv_data->rx_channel != -1) + DRCMR(drv_data->ssp->drcmr_rx) = + DRCMR_MAPVLD | drv_data->rx_channel; + if (drv_data->tx_channel != -1) + DRCMR(drv_data->ssp->drcmr_tx) = + DRCMR_MAPVLD | drv_data->tx_channel; +} + +int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, + struct spi_device *spi, + u8 bits_per_word, u32 *burst_code, + u32 *threshold) +{ + struct pxa2xx_spi_chip *chip_info = + (struct pxa2xx_spi_chip *)spi->controller_data; + int bytes_per_word; + int burst_bytes; + int thresh_words; + int req_burst_size; + int retval = 0; + + /* Set the threshold (in registers) to equal the same amount of data + * as represented by burst size (in bytes). The computation below + * is (burst_size rounded up to nearest 8 byte, word or long word) + * divided by (bytes/register); the tx threshold is the inverse of + * the rx, so that there will always be enough data in the rx fifo + * to satisfy a burst, and there will always be enough space in the + * tx fifo to accept a burst (a tx burst will overwrite the fifo if + * there is not enough space), there must always remain enough empty + * space in the rx fifo for any data loaded to the tx fifo. + * Whenever burst_size (in bytes) equals bits/word, the fifo threshold + * will be 8, or half the fifo; + * The threshold can only be set to 2, 4 or 8, but not 16, because + * to burst 16 to the tx fifo, the fifo would have to be empty; + * however, the minimum fifo trigger level is 1, and the tx will + * request service when the fifo is at this level, with only 15 spaces. + */ + + /* find bytes/word */ + if (bits_per_word <= 8) + bytes_per_word = 1; + else if (bits_per_word <= 16) + bytes_per_word = 2; + else + bytes_per_word = 4; + + /* use struct pxa2xx_spi_chip->dma_burst_size if available */ + if (chip_info) + req_burst_size = chip_info->dma_burst_size; + else { + switch (chip->dma_burst_size) { + default: + /* if the default burst size is not set, + * do it now */ + chip->dma_burst_size = DCMD_BURST8; + case DCMD_BURST8: + req_burst_size = 8; + break; + case DCMD_BURST16: + req_burst_size = 16; + break; + case DCMD_BURST32: + req_burst_size = 32; + break; + } + } + if (req_burst_size <= 8) { + *burst_code = DCMD_BURST8; + burst_bytes = 8; + } else if (req_burst_size <= 16) { + if (bytes_per_word == 1) { + /* don't burst more than 1/2 the fifo */ + *burst_code = DCMD_BURST8; + burst_bytes = 8; + retval = 1; + } else { + *burst_code = DCMD_BURST16; + burst_bytes = 16; + } + } else { + if (bytes_per_word == 1) { + /* don't burst more than 1/2 the fifo */ + *burst_code = DCMD_BURST8; + burst_bytes = 8; + retval = 1; + } else if (bytes_per_word == 2) { + /* don't burst more than 1/2 the fifo */ + *burst_code = DCMD_BURST16; + burst_bytes = 16; + retval = 1; + } else { + *burst_code = DCMD_BURST32; + burst_bytes = 32; + } + } + + thresh_words = burst_bytes / bytes_per_word; + + /* thresh_words will be between 2 and 8 */ + *threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT) + | (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT); + + return retval; +} diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 304cf6eb50e6..5b7c2a4ba828 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +35,7 @@ #include #include +#include "spi-pxa2xx.h" MODULE_AUTHOR("Stephen Street"); MODULE_DESCRIPTION("PXA2xx SSP SPI Controller"); @@ -46,12 +46,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define TIMOUT_DFLT 1000 -#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) -#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) -#define IS_DMA_ALIGNED(x) IS_ALIGNED((unsigned long)(x), DMA_ALIGNMENT) -#define MAX_DMA_LEN 8191 -#define DMA_ALIGNMENT 8 - /* * for testing SSCR1 changes that require SSP restart, basically * everything except the service and interrupt enables, the pxa270 developer @@ -66,106 +60,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); | SSCR1_RFT | SSCR1_TFT | SSCR1_MWDS \ | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) -#define DEFINE_SSP_REG(reg, off) \ -static inline u32 read_##reg(void const __iomem *p) \ -{ return __raw_readl(p + (off)); } \ -\ -static inline void write_##reg(u32 v, void __iomem *p) \ -{ __raw_writel(v, p + (off)); } - -DEFINE_SSP_REG(SSCR0, 0x00) -DEFINE_SSP_REG(SSCR1, 0x04) -DEFINE_SSP_REG(SSSR, 0x08) -DEFINE_SSP_REG(SSITR, 0x0c) -DEFINE_SSP_REG(SSDR, 0x10) -DEFINE_SSP_REG(SSTO, 0x28) -DEFINE_SSP_REG(SSPSP, 0x2c) - -#define START_STATE ((void*)0) -#define RUNNING_STATE ((void*)1) -#define DONE_STATE ((void*)2) -#define ERROR_STATE ((void*)-1) - -struct driver_data { - /* Driver model hookup */ - struct platform_device *pdev; - - /* SSP Info */ - struct ssp_device *ssp; - - /* SPI framework hookup */ - enum pxa_ssp_type ssp_type; - struct spi_master *master; - - /* PXA hookup */ - struct pxa2xx_spi_master *master_info; - - /* DMA setup stuff */ - int rx_channel; - int tx_channel; - u32 *null_dma_buf; - - /* SSP register addresses */ - void __iomem *ioaddr; - u32 ssdr_physical; - - /* SSP masks*/ - u32 dma_cr1; - u32 int_cr1; - u32 clear_sr; - u32 mask_sr; - - /* Maximun clock rate */ - unsigned long max_clk_rate; - - /* Message Transfer pump */ - struct tasklet_struct pump_transfers; - - /* Current message transfer state info */ - struct spi_message* cur_msg; - struct spi_transfer* cur_transfer; - struct chip_data *cur_chip; - size_t len; - void *tx; - void *tx_end; - void *rx; - void *rx_end; - int dma_mapped; - dma_addr_t rx_dma; - dma_addr_t tx_dma; - size_t rx_map_len; - size_t tx_map_len; - u8 n_bytes; - u32 dma_width; - int (*write)(struct driver_data *drv_data); - int (*read)(struct driver_data *drv_data); - irqreturn_t (*transfer_handler)(struct driver_data *drv_data); - void (*cs_control)(u32 command); -}; - -struct chip_data { - u32 cr0; - u32 cr1; - u32 psp; - u32 timeout; - u8 n_bytes; - u32 dma_width; - u32 dma_burst_size; - u32 threshold; - u32 dma_threshold; - u8 enable_dma; - u8 bits_per_word; - u32 speed_hz; - union { - int gpio_cs; - unsigned int frm; - }; - int gpio_cs_inverted; - int (*write)(struct driver_data *drv_data); - int (*read)(struct driver_data *drv_data); - void (*cs_control)(u32 command); -}; - static void cs_assert(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; @@ -200,26 +94,7 @@ static void cs_deassert(struct driver_data *drv_data) gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted); } -static void write_SSSR_CS(struct driver_data *drv_data, u32 val) -{ - void __iomem *reg = drv_data->ioaddr; - - if (drv_data->ssp_type == CE4100_SSP) - val |= read_SSSR(reg) & SSSR_ALT_FRM_MASK; - - write_SSSR(val, reg); -} - -static int pxa25x_ssp_comp(struct driver_data *drv_data) -{ - if (drv_data->ssp_type == PXA25x_SSP) - return 1; - if (drv_data->ssp_type == CE4100_SSP) - return 1; - return 0; -} - -static int flush(struct driver_data *drv_data) +int pxa2xx_spi_flush(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; @@ -345,7 +220,7 @@ static int u32_reader(struct driver_data *drv_data) return drv_data->rx == drv_data->rx_end; } -static void *next_transfer(struct driver_data *drv_data) +void *pxa2xx_spi_next_transfer(struct driver_data *drv_data) { struct spi_message *msg = drv_data->cur_msg; struct spi_transfer *trans = drv_data->cur_transfer; @@ -361,76 +236,6 @@ static void *next_transfer(struct driver_data *drv_data) return DONE_STATE; } -static int map_dma_buffers(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - struct device *dev = &msg->spi->dev; - - if (!drv_data->cur_chip->enable_dma) - return 0; - - if (msg->is_dma_mapped) - return drv_data->rx_dma && drv_data->tx_dma; - - if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) - return 0; - - /* Modify setup if rx buffer is null */ - if (drv_data->rx == NULL) { - *drv_data->null_dma_buf = 0; - drv_data->rx = drv_data->null_dma_buf; - drv_data->rx_map_len = 4; - } else - drv_data->rx_map_len = drv_data->len; - - - /* Modify setup if tx buffer is null */ - if (drv_data->tx == NULL) { - *drv_data->null_dma_buf = 0; - drv_data->tx = drv_data->null_dma_buf; - drv_data->tx_map_len = 4; - } else - drv_data->tx_map_len = drv_data->len; - - /* Stream map the tx buffer. Always do DMA_TO_DEVICE first - * so we flush the cache *before* invalidating it, in case - * the tx and rx buffers overlap. - */ - drv_data->tx_dma = dma_map_single(dev, drv_data->tx, - drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(dev, drv_data->tx_dma)) - return 0; - - /* Stream map the rx buffer */ - drv_data->rx_dma = dma_map_single(dev, drv_data->rx, - drv_data->rx_map_len, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, drv_data->rx_dma)) { - dma_unmap_single(dev, drv_data->tx_dma, - drv_data->tx_map_len, DMA_TO_DEVICE); - return 0; - } - - return 1; -} - -static void unmap_dma_buffers(struct driver_data *drv_data) -{ - struct device *dev; - - if (!drv_data->dma_mapped) - return; - - if (!drv_data->cur_msg->is_dma_mapped) { - dev = &drv_data->cur_msg->spi->dev; - dma_unmap_single(dev, drv_data->rx_dma, - drv_data->rx_map_len, DMA_FROM_DEVICE); - dma_unmap_single(dev, drv_data->tx_dma, - drv_data->tx_map_len, DMA_TO_DEVICE); - } - - drv_data->dma_mapped = 0; -} - /* caller already set message->status; dma and pio irqs are blocked */ static void giveback(struct driver_data *drv_data) { @@ -483,161 +288,6 @@ static void giveback(struct driver_data *drv_data) drv_data->cur_chip = NULL; } -static int wait_ssp_rx_stall(void const __iomem *ioaddr) -{ - unsigned long limit = loops_per_jiffy << 1; - - while ((read_SSSR(ioaddr) & SSSR_BSY) && --limit) - cpu_relax(); - - return limit; -} - -static int wait_dma_channel_stop(int channel) -{ - unsigned long limit = loops_per_jiffy << 1; - - while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit) - cpu_relax(); - - return limit; -} - -static void dma_error_stop(struct driver_data *drv_data, const char *msg) -{ - void __iomem *reg = drv_data->ioaddr; - - /* Stop and reset */ - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - write_SSSR_CS(drv_data, drv_data->clear_sr); - write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); - if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); - flush(drv_data); - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); - - unmap_dma_buffers(drv_data); - - dev_err(&drv_data->pdev->dev, "%s\n", msg); - - drv_data->cur_msg->state = ERROR_STATE; - tasklet_schedule(&drv_data->pump_transfers); -} - -static void dma_transfer_complete(struct driver_data *drv_data) -{ - void __iomem *reg = drv_data->ioaddr; - struct spi_message *msg = drv_data->cur_msg; - - /* Clear and disable interrupts on SSP and DMA channels*/ - write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); - write_SSSR_CS(drv_data, drv_data->clear_sr); - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - - if (wait_dma_channel_stop(drv_data->rx_channel) == 0) - dev_err(&drv_data->pdev->dev, - "dma_handler: dma rx channel stop failed\n"); - - if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) - dev_err(&drv_data->pdev->dev, - "dma_transfer: ssp rx stall failed\n"); - - unmap_dma_buffers(drv_data); - - /* update the buffer pointer for the amount completed in dma */ - drv_data->rx += drv_data->len - - (DCMD(drv_data->rx_channel) & DCMD_LENGTH); - - /* read trailing data from fifo, it does not matter how many - * bytes are in the fifo just read until buffer is full - * or fifo is empty, which ever occurs first */ - drv_data->read(drv_data); - - /* return count of what was actually read */ - msg->actual_length += drv_data->len - - (drv_data->rx_end - drv_data->rx); - - /* Transfer delays and chip select release are - * handled in pump_transfers or giveback - */ - - /* Move to next transfer */ - msg->state = next_transfer(drv_data); - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); -} - -static void dma_handler(int channel, void *data) -{ - struct driver_data *drv_data = data; - u32 irq_status = DCSR(channel) & DMA_INT_MASK; - - if (irq_status & DCSR_BUSERR) { - - if (channel == drv_data->tx_channel) - dma_error_stop(drv_data, - "dma_handler: " - "bad bus address on tx channel"); - else - dma_error_stop(drv_data, - "dma_handler: " - "bad bus address on rx channel"); - return; - } - - /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */ - if ((channel == drv_data->tx_channel) - && (irq_status & DCSR_ENDINTR) - && (drv_data->ssp_type == PXA25x_SSP)) { - - /* Wait for rx to stall */ - if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) - dev_err(&drv_data->pdev->dev, - "dma_handler: ssp rx stall failed\n"); - - /* finish this transfer, start the next */ - dma_transfer_complete(drv_data); - } -} - -static irqreturn_t dma_transfer(struct driver_data *drv_data) -{ - u32 irq_status; - void __iomem *reg = drv_data->ioaddr; - - irq_status = read_SSSR(reg) & drv_data->mask_sr; - if (irq_status & SSSR_ROR) { - dma_error_stop(drv_data, "dma_transfer: fifo overrun"); - return IRQ_HANDLED; - } - - /* Check for false positive timeout */ - if ((irq_status & SSSR_TINT) - && (DCSR(drv_data->tx_channel) & DCSR_RUN)) { - write_SSSR(SSSR_TINT, reg); - return IRQ_HANDLED; - } - - if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) { - - /* Clear and disable timeout interrupt, do the rest in - * dma_transfer_complete */ - if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); - - /* finish this transfer, start the next */ - dma_transfer_complete(drv_data); - - return IRQ_HANDLED; - } - - /* Opps problem detected */ - return IRQ_NONE; -} - static void reset_sccr1(struct driver_data *drv_data) { void __iomem *reg = drv_data->ioaddr; @@ -659,7 +309,7 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); - flush(drv_data); + pxa2xx_spi_flush(drv_data); write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); dev_err(&drv_data->pdev->dev, "%s\n", msg); @@ -687,7 +337,7 @@ static void int_transfer_complete(struct driver_data *drv_data) */ /* Move to next transfer */ - drv_data->cur_msg->state = next_transfer(drv_data); + drv_data->cur_msg->state = pxa2xx_spi_next_transfer(drv_data); /* Schedule transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); @@ -798,103 +448,6 @@ static irqreturn_t ssp_int(int irq, void *dev_id) return drv_data->transfer_handler(drv_data); } -static int set_dma_burst_and_threshold(struct chip_data *chip, - struct spi_device *spi, - u8 bits_per_word, u32 *burst_code, - u32 *threshold) -{ - struct pxa2xx_spi_chip *chip_info = - (struct pxa2xx_spi_chip *)spi->controller_data; - int bytes_per_word; - int burst_bytes; - int thresh_words; - int req_burst_size; - int retval = 0; - - /* Set the threshold (in registers) to equal the same amount of data - * as represented by burst size (in bytes). The computation below - * is (burst_size rounded up to nearest 8 byte, word or long word) - * divided by (bytes/register); the tx threshold is the inverse of - * the rx, so that there will always be enough data in the rx fifo - * to satisfy a burst, and there will always be enough space in the - * tx fifo to accept a burst (a tx burst will overwrite the fifo if - * there is not enough space), there must always remain enough empty - * space in the rx fifo for any data loaded to the tx fifo. - * Whenever burst_size (in bytes) equals bits/word, the fifo threshold - * will be 8, or half the fifo; - * The threshold can only be set to 2, 4 or 8, but not 16, because - * to burst 16 to the tx fifo, the fifo would have to be empty; - * however, the minimum fifo trigger level is 1, and the tx will - * request service when the fifo is at this level, with only 15 spaces. - */ - - /* find bytes/word */ - if (bits_per_word <= 8) - bytes_per_word = 1; - else if (bits_per_word <= 16) - bytes_per_word = 2; - else - bytes_per_word = 4; - - /* use struct pxa2xx_spi_chip->dma_burst_size if available */ - if (chip_info) - req_burst_size = chip_info->dma_burst_size; - else { - switch (chip->dma_burst_size) { - default: - /* if the default burst size is not set, - * do it now */ - chip->dma_burst_size = DCMD_BURST8; - case DCMD_BURST8: - req_burst_size = 8; - break; - case DCMD_BURST16: - req_burst_size = 16; - break; - case DCMD_BURST32: - req_burst_size = 32; - break; - } - } - if (req_burst_size <= 8) { - *burst_code = DCMD_BURST8; - burst_bytes = 8; - } else if (req_burst_size <= 16) { - if (bytes_per_word == 1) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST8; - burst_bytes = 8; - retval = 1; - } else { - *burst_code = DCMD_BURST16; - burst_bytes = 16; - } - } else { - if (bytes_per_word == 1) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST8; - burst_bytes = 8; - retval = 1; - } else if (bytes_per_word == 2) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST16; - burst_bytes = 16; - retval = 1; - } else { - *burst_code = DCMD_BURST32; - burst_bytes = 32; - } - } - - thresh_words = burst_bytes / bytes_per_word; - - /* thresh_words will be between 2 and 8 */ - *threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT) - | (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT); - - return retval; -} - static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) { unsigned long ssp_clk = drv_data->max_clk_rate; @@ -956,8 +509,8 @@ static void pump_transfers(unsigned long data) cs_deassert(drv_data); } - /* Check for transfers that need multiple DMA segments */ - if (transfer->len > MAX_DMA_LEN && chip->enable_dma) { + /* Check if we can DMA this transfer */ + if (!pxa2xx_spi_dma_is_possible(transfer->len) && chip->enable_dma) { /* reject already-mapped transfers; PIO won't always work */ if (message->is_dma_mapped @@ -980,21 +533,20 @@ static void pump_transfers(unsigned long data) } /* Setup the transfer state based on the type of transfer */ - if (flush(drv_data) == 0) { + if (pxa2xx_spi_flush(drv_data) == 0) { dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n"); message->status = -EIO; giveback(drv_data); return; } drv_data->n_bytes = chip->n_bytes; - drv_data->dma_width = chip->dma_width; drv_data->tx = (void *)transfer->tx_buf; drv_data->tx_end = drv_data->tx + transfer->len; drv_data->rx = transfer->rx_buf; drv_data->rx_end = drv_data->rx + transfer->len; drv_data->rx_dma = transfer->rx_dma; drv_data->tx_dma = transfer->tx_dma; - drv_data->len = transfer->len & DCMD_LENGTH; + drv_data->len = transfer->len; drv_data->write = drv_data->tx ? chip->write : null_writer; drv_data->read = drv_data->rx ? chip->read : null_reader; @@ -1015,21 +567,18 @@ static void pump_transfers(unsigned long data) if (bits <= 8) { drv_data->n_bytes = 1; - drv_data->dma_width = DCMD_WIDTH1; drv_data->read = drv_data->read != null_reader ? u8_reader : null_reader; drv_data->write = drv_data->write != null_writer ? u8_writer : null_writer; } else if (bits <= 16) { drv_data->n_bytes = 2; - drv_data->dma_width = DCMD_WIDTH2; drv_data->read = drv_data->read != null_reader ? u16_reader : null_reader; drv_data->write = drv_data->write != null_writer ? u16_writer : null_writer; } else if (bits <= 32) { drv_data->n_bytes = 4; - drv_data->dma_width = DCMD_WIDTH4; drv_data->read = drv_data->read != null_reader ? u32_reader : null_reader; drv_data->write = drv_data->write != null_writer ? @@ -1038,7 +587,8 @@ static void pump_transfers(unsigned long data) /* if bits/word is changed in dma mode, then must check the * thresholds and burst also */ if (chip->enable_dma) { - if (set_dma_burst_and_threshold(chip, message->spi, + if (pxa2xx_spi_set_dma_burst_and_threshold(chip, + message->spi, bits, &dma_burst, &dma_thresh)) if (printk_ratelimit()) @@ -1057,70 +607,21 @@ static void pump_transfers(unsigned long data) message->state = RUNNING_STATE; - /* Try to map dma buffer and do a dma transfer if successful, but - * only if the length is non-zero and less than MAX_DMA_LEN. - * - * Zero-length non-descriptor DMA is illegal on PXA2xx; force use - * of PIO instead. Care is needed above because the transfer may - * have have been passed with buffers that are already dma mapped. - * A zero-length transfer in PIO mode will not try to write/read - * to/from the buffers - * - * REVISIT large transfers are exactly where we most want to be - * using DMA. If this happens much, split those transfers into - * multiple DMA segments rather than forcing PIO. - */ drv_data->dma_mapped = 0; - if (drv_data->len > 0 && drv_data->len <= MAX_DMA_LEN) - drv_data->dma_mapped = map_dma_buffers(drv_data); + if (pxa2xx_spi_dma_is_possible(drv_data->len)) + drv_data->dma_mapped = pxa2xx_spi_map_dma_buffers(drv_data); if (drv_data->dma_mapped) { /* Ensure we have the correct interrupt handler */ - drv_data->transfer_handler = dma_transfer; + drv_data->transfer_handler = pxa2xx_spi_dma_transfer; - /* Setup rx DMA Channel */ - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - DSADR(drv_data->rx_channel) = drv_data->ssdr_physical; - DTADR(drv_data->rx_channel) = drv_data->rx_dma; - if (drv_data->rx == drv_data->null_dma_buf) - /* No target address increment */ - DCMD(drv_data->rx_channel) = DCMD_FLOWSRC - | drv_data->dma_width - | dma_burst - | drv_data->len; - else - DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR - | DCMD_FLOWSRC - | drv_data->dma_width - | dma_burst - | drv_data->len; - - /* Setup tx DMA Channel */ - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - DSADR(drv_data->tx_channel) = drv_data->tx_dma; - DTADR(drv_data->tx_channel) = drv_data->ssdr_physical; - if (drv_data->tx == drv_data->null_dma_buf) - /* No source address increment */ - DCMD(drv_data->tx_channel) = DCMD_FLOWTRG - | drv_data->dma_width - | dma_burst - | drv_data->len; - else - DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR - | DCMD_FLOWTRG - | drv_data->dma_width - | dma_burst - | drv_data->len; - - /* Enable dma end irqs on SSP to detect end of transfer */ - if (drv_data->ssp_type == PXA25x_SSP) - DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN; + pxa2xx_spi_dma_prepare(drv_data, dma_burst); /* Clear status and start DMA engine */ cr1 = chip->cr1 | dma_thresh | drv_data->dma_cr1; write_SSSR(drv_data->clear_sr, reg); - DCSR(drv_data->rx_channel) |= DCSR_RUN; - DCSR(drv_data->tx_channel) |= DCSR_RUN; + + pxa2xx_spi_dma_start(drv_data); } else { /* Ensure we have the correct interrupt handler */ drv_data->transfer_handler = interrupt_transfer; @@ -1262,8 +763,6 @@ static int setup(struct spi_device *spi) chip->gpio_cs = -1; chip->enable_dma = 0; chip->timeout = TIMOUT_DFLT; - chip->dma_burst_size = drv_data->master_info->enable_dma ? - DCMD_BURST8 : 0; } /* protocol drivers may change the chip settings, so... @@ -1293,7 +792,8 @@ static int setup(struct spi_device *spi) * burst and threshold can still respond to changes in bits_per_word */ if (chip->enable_dma) { /* set up legal burst and threshold for dma */ - if (set_dma_burst_and_threshold(chip, spi, spi->bits_per_word, + if (pxa2xx_spi_set_dma_burst_and_threshold(chip, spi, + spi->bits_per_word, &chip->dma_burst_size, &chip->dma_threshold)) { dev_warn(&spi->dev, "in setup: DMA burst size reduced " @@ -1328,18 +828,15 @@ static int setup(struct spi_device *spi) if (spi->bits_per_word <= 8) { chip->n_bytes = 1; - chip->dma_width = DCMD_WIDTH1; chip->read = u8_reader; chip->write = u8_writer; } else if (spi->bits_per_word <= 16) { chip->n_bytes = 2; - chip->dma_width = DCMD_WIDTH2; chip->read = u16_reader; chip->write = u16_writer; } else if (spi->bits_per_word <= 32) { chip->cr0 |= SSCR0_EDSS; chip->n_bytes = 4; - chip->dma_width = DCMD_WIDTH4; chip->read = u32_reader; chip->write = u32_writer; } else { @@ -1447,31 +944,11 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) drv_data->tx_channel = -1; drv_data->rx_channel = -1; if (platform_info->enable_dma) { - - /* Get two DMA channels (rx and tx) */ - drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx", - DMA_PRIO_HIGH, - dma_handler, - drv_data); - if (drv_data->rx_channel < 0) { - dev_err(dev, "problem (%d) requesting rx channel\n", - drv_data->rx_channel); - status = -ENODEV; - goto out_error_irq_alloc; + status = pxa2xx_spi_dma_setup(drv_data); + if (status) { + dev_warn(dev, "failed to setup DMA, using PIO\n"); + platform_info->enable_dma = false; } - drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx", - DMA_PRIO_MEDIUM, - dma_handler, - drv_data); - if (drv_data->tx_channel < 0) { - dev_err(dev, "problem (%d) requesting tx channel\n", - drv_data->tx_channel); - status = -ENODEV; - goto out_error_dma_alloc; - } - - DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel; - DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel; } /* Enable SOC clock */ @@ -1507,14 +984,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) out_error_clock_enabled: clk_disable_unprepare(ssp->clk); - -out_error_dma_alloc: - if (drv_data->tx_channel != -1) - pxa_free_dma(drv_data->tx_channel); - if (drv_data->rx_channel != -1) - pxa_free_dma(drv_data->rx_channel); - -out_error_irq_alloc: + pxa2xx_spi_dma_release(drv_data); free_irq(ssp->irq, drv_data); out_error_master_alloc: @@ -1537,12 +1007,8 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) clk_disable_unprepare(ssp->clk); /* Release DMA */ - if (drv_data->master_info->enable_dma) { - DRCMR(ssp->drcmr_rx) = 0; - DRCMR(ssp->drcmr_tx) = 0; - pxa_free_dma(drv_data->tx_channel); - pxa_free_dma(drv_data->rx_channel); - } + if (drv_data->master_info->enable_dma) + pxa2xx_spi_dma_release(drv_data); /* Release IRQ */ free_irq(ssp->irq, drv_data); @@ -1589,12 +1055,7 @@ static int pxa2xx_spi_resume(struct device *dev) struct ssp_device *ssp = drv_data->ssp; int status = 0; - if (drv_data->rx_channel != -1) - DRCMR(drv_data->ssp->drcmr_rx) = - DRCMR_MAPVLD | drv_data->rx_channel; - if (drv_data->tx_channel != -1) - DRCMR(drv_data->ssp->drcmr_tx) = - DRCMR_MAPVLD | drv_data->tx_channel; + pxa2xx_spi_dma_resume(drv_data); /* Enable the SSP clock */ clk_prepare_enable(ssp->clk); diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h new file mode 100644 index 000000000000..0a98905c916e --- /dev/null +++ b/drivers/spi/spi-pxa2xx.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * Copyright (C) 2013, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SPI_PXA2XX_H +#define SPI_PXA2XX_H + +#include +#include +#include +#include +#include +#include +#include + +struct driver_data { + /* Driver model hookup */ + struct platform_device *pdev; + + /* SSP Info */ + struct ssp_device *ssp; + + /* SPI framework hookup */ + enum pxa_ssp_type ssp_type; + struct spi_master *master; + + /* PXA hookup */ + struct pxa2xx_spi_master *master_info; + + /* PXA private DMA setup stuff */ + int rx_channel; + int tx_channel; + u32 *null_dma_buf; + + /* SSP register addresses */ + void __iomem *ioaddr; + u32 ssdr_physical; + + /* SSP masks*/ + u32 dma_cr1; + u32 int_cr1; + u32 clear_sr; + u32 mask_sr; + + /* Maximun clock rate */ + unsigned long max_clk_rate; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message transfer state info */ + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct chip_data *cur_chip; + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + int dma_mapped; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + size_t rx_map_len; + size_t tx_map_len; + u8 n_bytes; + int (*write)(struct driver_data *drv_data); + int (*read)(struct driver_data *drv_data); + irqreturn_t (*transfer_handler)(struct driver_data *drv_data); + void (*cs_control)(u32 command); +}; + +struct chip_data { + u32 cr0; + u32 cr1; + u32 psp; + u32 timeout; + u8 n_bytes; + u32 dma_burst_size; + u32 threshold; + u32 dma_threshold; + u8 enable_dma; + u8 bits_per_word; + u32 speed_hz; + union { + int gpio_cs; + unsigned int frm; + }; + int gpio_cs_inverted; + int (*write)(struct driver_data *drv_data); + int (*read)(struct driver_data *drv_data); + void (*cs_control)(u32 command); +}; + +#define DEFINE_SSP_REG(reg, off) \ +static inline u32 read_##reg(void const __iomem *p) \ +{ return __raw_readl(p + (off)); } \ +\ +static inline void write_##reg(u32 v, void __iomem *p) \ +{ __raw_writel(v, p + (off)); } + +DEFINE_SSP_REG(SSCR0, 0x00) +DEFINE_SSP_REG(SSCR1, 0x04) +DEFINE_SSP_REG(SSSR, 0x08) +DEFINE_SSP_REG(SSITR, 0x0c) +DEFINE_SSP_REG(SSDR, 0x10) +DEFINE_SSP_REG(SSTO, 0x28) +DEFINE_SSP_REG(SSPSP, 0x2c) + +#define START_STATE ((void *)0) +#define RUNNING_STATE ((void *)1) +#define DONE_STATE ((void *)2) +#define ERROR_STATE ((void *)-1) + +#define MAX_DMA_LEN 8191 +#define IS_DMA_ALIGNED(x) IS_ALIGNED((unsigned long)(x), DMA_ALIGNMENT) +#define DMA_ALIGNMENT 8 + +static inline int pxa25x_ssp_comp(struct driver_data *drv_data) +{ + if (drv_data->ssp_type == PXA25x_SSP) + return 1; + if (drv_data->ssp_type == CE4100_SSP) + return 1; + return 0; +} + +static inline void write_SSSR_CS(struct driver_data *drv_data, u32 val) +{ + void __iomem *reg = drv_data->ioaddr; + + if (drv_data->ssp_type == CE4100_SSP) + val |= read_SSSR(reg) & SSSR_ALT_FRM_MASK; + + write_SSSR(val, reg); +} + +extern int pxa2xx_spi_flush(struct driver_data *drv_data); +extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data); + +#if defined(CONFIG_SPI_PXA2XX_PXADMA) +extern bool pxa2xx_spi_dma_is_possible(size_t len); +extern int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data); +extern irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data); +extern int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst); +extern void pxa2xx_spi_dma_start(struct driver_data *drv_data); +extern int pxa2xx_spi_dma_setup(struct driver_data *drv_data); +extern void pxa2xx_spi_dma_release(struct driver_data *drv_data); +extern void pxa2xx_spi_dma_resume(struct driver_data *drv_data); +extern int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, + struct spi_device *spi, + u8 bits_per_word, + u32 *burst_code, + u32 *threshold); +#else +static inline bool pxa2xx_spi_dma_is_possible(size_t len) { return false; } +static inline int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data) +{ + return 0; +} +#define pxa2xx_spi_dma_transfer NULL +static inline void pxa2xx_spi_dma_prepare(struct driver_data *drv_data, + u32 dma_burst) {} +static inline void pxa2xx_spi_dma_start(struct driver_data *drv_data) {} +static inline int pxa2xx_spi_dma_setup(struct driver_data *drv_data) +{ + return 0; +} +static inline void pxa2xx_spi_dma_release(struct driver_data *drv_data) {} +static inline void pxa2xx_spi_dma_resume(struct driver_data *drv_data) {} +static inline int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, + struct spi_device *spi, + u8 bits_per_word, + u32 *burst_code, + u32 *threshold) +{ + return -ENODEV; +} +#endif + +#endif /* SPI_PXA2XX_H */ diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h index 053b5ba51b25..d6d2b4d557f8 100644 --- a/include/linux/spi/pxa2xx_spi.h +++ b/include/linux/spi/pxa2xx_spi.h @@ -53,85 +53,5 @@ struct pxa2xx_spi_chip { extern void pxa2xx_set_spi_info(unsigned id, struct pxa2xx_spi_master *info); -#else -/* - * This is the implemtation for CE4100 on x86. ARM defines them in mach/ or - * plat/ include path. - * The CE4100 does not provide DMA support. This bits are here to let the driver - * compile and will never be used. Maybe we get DMA support at a later point in - * time. - */ - -#define DCSR(n) (n) -#define DSADR(n) (n) -#define DTADR(n) (n) -#define DCMD(n) (n) -#define DRCMR(n) (n) - -#define DCSR_RUN (1 << 31) /* Run Bit */ -#define DCSR_NODESC (1 << 30) /* No-Descriptor Fetch */ -#define DCSR_STOPIRQEN (1 << 29) /* Stop Interrupt Enable */ -#define DCSR_REQPEND (1 << 8) /* Request Pending (read-only) */ -#define DCSR_STOPSTATE (1 << 3) /* Stop State (read-only) */ -#define DCSR_ENDINTR (1 << 2) /* End Interrupt */ -#define DCSR_STARTINTR (1 << 1) /* Start Interrupt */ -#define DCSR_BUSERR (1 << 0) /* Bus Error Interrupt */ - -#define DCSR_EORIRQEN (1 << 28) /* End of Receive Interrupt Enable */ -#define DCSR_EORJMPEN (1 << 27) /* Jump to next descriptor on EOR */ -#define DCSR_EORSTOPEN (1 << 26) /* STOP on an EOR */ -#define DCSR_SETCMPST (1 << 25) /* Set Descriptor Compare Status */ -#define DCSR_CLRCMPST (1 << 24) /* Clear Descriptor Compare Status */ -#define DCSR_CMPST (1 << 10) /* The Descriptor Compare Status */ -#define DCSR_EORINTR (1 << 9) /* The end of Receive */ - -#define DRCMR_MAPVLD (1 << 7) /* Map Valid */ -#define DRCMR_CHLNUM 0x1f /* mask for Channel Number */ - -#define DDADR_DESCADDR 0xfffffff0 /* Address of next descriptor */ -#define DDADR_STOP (1 << 0) /* Stop */ - -#define DCMD_INCSRCADDR (1 << 31) /* Source Address Increment Setting. */ -#define DCMD_INCTRGADDR (1 << 30) /* Target Address Increment Setting. */ -#define DCMD_FLOWSRC (1 << 29) /* Flow Control by the source. */ -#define DCMD_FLOWTRG (1 << 28) /* Flow Control by the target. */ -#define DCMD_STARTIRQEN (1 << 22) /* Start Interrupt Enable */ -#define DCMD_ENDIRQEN (1 << 21) /* End Interrupt Enable */ -#define DCMD_ENDIAN (1 << 18) /* Device Endian-ness. */ -#define DCMD_BURST8 (1 << 16) /* 8 byte burst */ -#define DCMD_BURST16 (2 << 16) /* 16 byte burst */ -#define DCMD_BURST32 (3 << 16) /* 32 byte burst */ -#define DCMD_WIDTH1 (1 << 14) /* 1 byte width */ -#define DCMD_WIDTH2 (2 << 14) /* 2 byte width (HalfWord) */ -#define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */ -#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */ - -/* - * Descriptor structure for PXA's DMA engine - * Note: this structure must always be aligned to a 16-byte boundary. - */ - -typedef enum { - DMA_PRIO_HIGH = 0, - DMA_PRIO_MEDIUM = 1, - DMA_PRIO_LOW = 2 -} pxa_dma_prio; - -/* - * DMA registration - */ - -static inline int pxa_request_dma(char *name, - pxa_dma_prio prio, - void (*irq_handler)(int, void *), - void *data) -{ - return -ENODEV; -} - -static inline void pxa_free_dma(int dma_ch) -{ -} - #endif #endif From 5928808ef623347e0d4aa22327b992e9e125b6ad Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:29 +0200 Subject: [PATCH 44/50] spi/pxa2xx: add support for DMA engine To be able to use DMA with this driver on non-PXA platforms we implement support for the generic DMA engine API. This lets user to use different DMA engines with little or no modification to the driver. Request lines and channel numbers can be passed to the driver from the platform specific data. The DMA engine implementation will be selected by default even on PXA platform. User can select the legacy DMA API by enabling Kconfig option CONFIG_SPI_PXA2XX_PXADMA. Signed-off-by: Mika Westerberg Acked-by: Linus Walleij Tested-by: Lu Cao Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 +- drivers/spi/Makefile | 1 + drivers/spi/spi-pxa2xx-dma.c | 392 +++++++++++++++++++++++++++++++++ drivers/spi/spi-pxa2xx.c | 2 +- drivers/spi/spi-pxa2xx.h | 32 ++- include/linux/spi/pxa2xx_spi.h | 6 + 6 files changed, 437 insertions(+), 3 deletions(-) create mode 100644 drivers/spi/spi-pxa2xx-dma.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f1878666e917..8913d3dd5724 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -301,7 +301,12 @@ config SPI_PXA2XX_PXADMA bool "PXA2xx SSP legacy PXA DMA API support" depends on SPI_PXA2XX && ARCH_PXA help - Enable PXA private legacy DMA API support. + Enable PXA private legacy DMA API support. Note that this is + deprecated in favor of generic DMA engine API. + +config SPI_PXA2XX_DMA + def_bool y + depends on SPI_PXA2XX && !SPI_PXA2XX_PXADMA config SPI_PXA2XX tristate "PXA2xx SSP SPI master" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 2e3cdd3caba9..e53c30941340 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o +spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c new file mode 100644 index 000000000000..c735c5a008a2 --- /dev/null +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -0,0 +1,392 @@ +/* + * PXA2xx SPI DMA engine support. + * + * Copyright (C) 2013, Intel Corporation + * Author: Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-pxa2xx.h" + +static int pxa2xx_spi_map_dma_buffer(struct driver_data *drv_data, + enum dma_data_direction dir) +{ + int i, nents, len = drv_data->len; + struct scatterlist *sg; + struct device *dmadev; + struct sg_table *sgt; + void *buf, *pbuf; + + /* + * Some DMA controllers have problems transferring buffers that are + * not multiple of 4 bytes. So we truncate the transfer so that it + * is suitable for such controllers, and handle the trailing bytes + * manually after the DMA completes. + * + * REVISIT: It would be better if this information could be + * retrieved directly from the DMA device in a similar way than + * ->copy_align etc. is done. + */ + len = ALIGN(drv_data->len, 4); + + if (dir == DMA_TO_DEVICE) { + dmadev = drv_data->tx_chan->device->dev; + sgt = &drv_data->tx_sgt; + buf = drv_data->tx; + drv_data->tx_map_len = len; + } else { + dmadev = drv_data->rx_chan->device->dev; + sgt = &drv_data->rx_sgt; + buf = drv_data->rx; + drv_data->rx_map_len = len; + } + + nents = DIV_ROUND_UP(len, SZ_2K); + if (nents != sgt->nents) { + int ret; + + sg_free_table(sgt); + ret = sg_alloc_table(sgt, nents, GFP_KERNEL); + if (ret) + return ret; + } + + pbuf = buf; + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + size_t bytes = min_t(size_t, len, SZ_2K); + + if (buf) + sg_set_buf(sg, pbuf, bytes); + else + sg_set_buf(sg, drv_data->dummy, bytes); + + pbuf += bytes; + len -= bytes; + } + + nents = dma_map_sg(dmadev, sgt->sgl, sgt->nents, dir); + if (!nents) + return -ENOMEM; + + return nents; +} + +static void pxa2xx_spi_unmap_dma_buffer(struct driver_data *drv_data, + enum dma_data_direction dir) +{ + struct device *dmadev; + struct sg_table *sgt; + + if (dir == DMA_TO_DEVICE) { + dmadev = drv_data->tx_chan->device->dev; + sgt = &drv_data->tx_sgt; + } else { + dmadev = drv_data->rx_chan->device->dev; + sgt = &drv_data->rx_sgt; + } + + dma_unmap_sg(dmadev, sgt->sgl, sgt->nents, dir); +} + +static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data) +{ + if (!drv_data->dma_mapped) + return; + + pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_FROM_DEVICE); + pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_TO_DEVICE); + + drv_data->dma_mapped = 0; +} + +static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, + bool error) +{ + struct spi_message *msg = drv_data->cur_msg; + + /* + * It is possible that one CPU is handling ROR interrupt and other + * just gets DMA completion. Calling pump_transfers() twice for the + * same transfer leads to problems thus we prevent concurrent calls + * by using ->dma_running. + */ + if (atomic_dec_and_test(&drv_data->dma_running)) { + void __iomem *reg = drv_data->ioaddr; + + /* + * If the other CPU is still handling the ROR interrupt we + * might not know about the error yet. So we re-check the + * ROR bit here before we clear the status register. + */ + if (!error) { + u32 status = read_SSSR(reg) & drv_data->mask_sr; + error = status & SSSR_ROR; + } + + /* Clear status & disable interrupts */ + write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); + if (!pxa25x_ssp_comp(drv_data)) + write_SSTO(0, reg); + + if (!error) { + pxa2xx_spi_unmap_dma_buffers(drv_data); + + /* Handle the last bytes of unaligned transfer */ + drv_data->tx += drv_data->tx_map_len; + drv_data->write(drv_data); + + drv_data->rx += drv_data->rx_map_len; + drv_data->read(drv_data); + + msg->actual_length += drv_data->len; + msg->state = pxa2xx_spi_next_transfer(drv_data); + } else { + /* In case we got an error we disable the SSP now */ + write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + + msg->state = ERROR_STATE; + } + + tasklet_schedule(&drv_data->pump_transfers); + } +} + +static void pxa2xx_spi_dma_callback(void *data) +{ + pxa2xx_spi_dma_transfer_complete(data, false); +} + +static struct dma_async_tx_descriptor * +pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data, + enum dma_transfer_direction dir) +{ + struct pxa2xx_spi_master *pdata = drv_data->master_info; + struct chip_data *chip = drv_data->cur_chip; + enum dma_slave_buswidth width; + struct dma_slave_config cfg; + struct dma_chan *chan; + struct sg_table *sgt; + int nents, ret; + + switch (drv_data->n_bytes) { + case 1: + width = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 2: + width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + default: + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = drv_data->ssdr_physical; + cfg.dst_addr_width = width; + cfg.dst_maxburst = chip->dma_burst_size; + cfg.slave_id = pdata->tx_slave_id; + + sgt = &drv_data->tx_sgt; + nents = drv_data->tx_nents; + chan = drv_data->tx_chan; + } else { + cfg.src_addr = drv_data->ssdr_physical; + cfg.src_addr_width = width; + cfg.src_maxburst = chip->dma_burst_size; + cfg.slave_id = pdata->rx_slave_id; + + sgt = &drv_data->rx_sgt; + nents = drv_data->rx_nents; + chan = drv_data->rx_chan; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_warn(&drv_data->pdev->dev, "DMA slave config failed\n"); + return NULL; + } + + return dmaengine_prep_slave_sg(chan, sgt->sgl, nents, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +} + +static bool pxa2xx_spi_dma_filter(struct dma_chan *chan, void *param) +{ + const struct pxa2xx_spi_master *pdata = param; + + return chan->chan_id == pdata->tx_chan_id || + chan->chan_id == pdata->rx_chan_id; +} + +bool pxa2xx_spi_dma_is_possible(size_t len) +{ + return len <= MAX_DMA_LEN; +} + +int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data) +{ + const struct chip_data *chip = drv_data->cur_chip; + int ret; + + if (!chip->enable_dma) + return 0; + + /* Don't bother with DMA if we can't do even a single burst */ + if (drv_data->len < chip->dma_burst_size) + return 0; + + ret = pxa2xx_spi_map_dma_buffer(drv_data, DMA_TO_DEVICE); + if (ret <= 0) { + dev_warn(&drv_data->pdev->dev, "failed to DMA map TX\n"); + return 0; + } + + drv_data->tx_nents = ret; + + ret = pxa2xx_spi_map_dma_buffer(drv_data, DMA_FROM_DEVICE); + if (ret <= 0) { + pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_TO_DEVICE); + dev_warn(&drv_data->pdev->dev, "failed to DMA map RX\n"); + return 0; + } + + drv_data->rx_nents = ret; + return 1; +} + +irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) +{ + u32 status; + + status = read_SSSR(drv_data->ioaddr) & drv_data->mask_sr; + if (status & SSSR_ROR) { + dev_err(&drv_data->pdev->dev, "FIFO overrun\n"); + + dmaengine_terminate_all(drv_data->rx_chan); + dmaengine_terminate_all(drv_data->tx_chan); + + pxa2xx_spi_dma_transfer_complete(drv_data, true); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst) +{ + struct dma_async_tx_descriptor *tx_desc, *rx_desc; + + tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV); + if (!tx_desc) { + dev_err(&drv_data->pdev->dev, + "failed to get DMA TX descriptor\n"); + return -EBUSY; + } + + rx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_DEV_TO_MEM); + if (!rx_desc) { + dev_err(&drv_data->pdev->dev, + "failed to get DMA RX descriptor\n"); + return -EBUSY; + } + + /* We are ready when RX completes */ + rx_desc->callback = pxa2xx_spi_dma_callback; + rx_desc->callback_param = drv_data; + + dmaengine_submit(rx_desc); + dmaengine_submit(tx_desc); + return 0; +} + +void pxa2xx_spi_dma_start(struct driver_data *drv_data) +{ + dma_async_issue_pending(drv_data->rx_chan); + dma_async_issue_pending(drv_data->tx_chan); + + atomic_set(&drv_data->dma_running, 1); +} + +int pxa2xx_spi_dma_setup(struct driver_data *drv_data) +{ + struct pxa2xx_spi_master *pdata = drv_data->master_info; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + drv_data->dummy = devm_kzalloc(&drv_data->pdev->dev, SZ_2K, GFP_KERNEL); + if (!drv_data->dummy) + return -ENOMEM; + + drv_data->tx_chan = dma_request_channel(mask, pxa2xx_spi_dma_filter, + pdata); + if (!drv_data->tx_chan) + return -ENODEV; + + drv_data->rx_chan = dma_request_channel(mask, pxa2xx_spi_dma_filter, + pdata); + if (!drv_data->rx_chan) { + dma_release_channel(drv_data->tx_chan); + drv_data->tx_chan = NULL; + return -ENODEV; + } + + return 0; +} + +void pxa2xx_spi_dma_release(struct driver_data *drv_data) +{ + if (drv_data->rx_chan) { + dmaengine_terminate_all(drv_data->rx_chan); + dma_release_channel(drv_data->rx_chan); + sg_free_table(&drv_data->rx_sgt); + drv_data->rx_chan = NULL; + } + if (drv_data->tx_chan) { + dmaengine_terminate_all(drv_data->tx_chan); + dma_release_channel(drv_data->tx_chan); + sg_free_table(&drv_data->tx_sgt); + drv_data->tx_chan = NULL; + } +} + +void pxa2xx_spi_dma_resume(struct driver_data *drv_data) +{ +} + +int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, + struct spi_device *spi, + u8 bits_per_word, u32 *burst_code, + u32 *threshold) +{ + struct pxa2xx_spi_chip *chip_info = spi->controller_data; + + /* + * If the DMA burst size is given in chip_info we use that, + * otherwise we use the default. Also we use the default FIFO + * thresholds for now. + */ + *burst_code = chip_info ? chip_info->dma_burst_size : 16; + *threshold = SSCR1_RxTresh(RX_THRESH_DFLT) + | SSCR1_TxTresh(TX_THRESH_DFLT); + + return 0; +} diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 5b7c2a4ba828..c3d78073d1ad 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -928,7 +928,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) drv_data->mask_sr = SSSR_RFS | SSSR_TFS | SSSR_ROR; } else { drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; - drv_data->dma_cr1 = SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE; + drv_data->dma_cr1 = DEFAULT_DMA_CR1; drv_data->clear_sr = SSSR_ROR | SSSR_TINT; drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR; } diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 0a98905c916e..97ff4717e6ac 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -10,11 +10,15 @@ #ifndef SPI_PXA2XX_H #define SPI_PXA2XX_H +#include +#include #include #include #include #include #include +#include +#include #include #include @@ -53,6 +57,16 @@ struct driver_data { /* Message Transfer pump */ struct tasklet_struct pump_transfers; + /* DMA engine support */ + struct dma_chan *rx_chan; + struct dma_chan *tx_chan; + struct sg_table rx_sgt; + struct sg_table tx_sgt; + int rx_nents; + int tx_nents; + void *dummy; + atomic_t dma_running; + /* Current message transfer state info */ struct spi_message *cur_msg; struct spi_transfer *cur_transfer; @@ -116,7 +130,6 @@ DEFINE_SSP_REG(SSPSP, 0x2c) #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) -#define MAX_DMA_LEN 8191 #define IS_DMA_ALIGNED(x) IS_ALIGNED((unsigned long)(x), DMA_ALIGNMENT) #define DMA_ALIGNMENT 8 @@ -142,7 +155,24 @@ static inline void write_SSSR_CS(struct driver_data *drv_data, u32 val) extern int pxa2xx_spi_flush(struct driver_data *drv_data); extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data); +/* + * Select the right DMA implementation. + */ #if defined(CONFIG_SPI_PXA2XX_PXADMA) +#define SPI_PXA2XX_USE_DMA 1 +#define MAX_DMA_LEN 8191 +#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE) +#elif defined(CONFIG_SPI_PXA2XX_DMA) +#define SPI_PXA2XX_USE_DMA 1 +#define MAX_DMA_LEN SZ_64K +#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) +#else +#undef SPI_PXA2XX_USE_DMA +#define MAX_DMA_LEN 0 +#define DEFAULT_DMA_CR1 0 +#endif + +#ifdef SPI_PXA2XX_USE_DMA extern bool pxa2xx_spi_dma_is_possible(size_t len); extern int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data); extern irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data); diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h index d6d2b4d557f8..e5cbbc4c57f7 100644 --- a/include/linux/spi/pxa2xx_spi.h +++ b/include/linux/spi/pxa2xx_spi.h @@ -29,6 +29,12 @@ struct pxa2xx_spi_master { u16 num_chipselect; u8 enable_dma; + /* DMA engine specific config */ + int rx_chan_id; + int tx_chan_id; + int rx_slave_id; + int tx_slave_id; + /* For non-PXA arches */ struct ssp_device ssp; }; From 7d94a50585884180a487ce17e8ddbfa37f7694b6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:30 +0200 Subject: [PATCH 45/50] spi/pxa2xx: add support for runtime PM Drivers should put the device into low power states proactively whenever the device is not in use. Thus implement support for runtime PM and use the autosuspend feature to make sure that we can still perform well in case we see lots of SPI traffic within short period of time. Signed-off-by: Mika Westerberg Tested-by: Lu Cao Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 73 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index c3d78073d1ad..7f8f939835fd 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -417,10 +418,20 @@ static irqreturn_t ssp_int(int irq, void *dev_id) { struct driver_data *drv_data = dev_id; void __iomem *reg = drv_data->ioaddr; - u32 sccr1_reg = read_SSCR1(reg); + u32 sccr1_reg; u32 mask = drv_data->mask_sr; u32 status; + /* + * The IRQ might be shared with other peripherals so we must first + * check that are we RPM suspended or not. If we are we assume that + * the IRQ was not for us (we shouldn't be RPM suspended when the + * interrupt is enabled). + */ + if (pm_runtime_suspended(&drv_data->pdev->dev)) + return IRQ_NONE; + + sccr1_reg = read_SSCR1(reg); status = read_SSSR(reg); /* Ignore possible writes if we don't need to write */ @@ -678,6 +689,27 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master, return 0; } +static int pxa2xx_spi_prepare_transfer(struct spi_master *master) +{ + struct driver_data *drv_data = spi_master_get_devdata(master); + + pm_runtime_get_sync(&drv_data->pdev->dev); + return 0; +} + +static int pxa2xx_spi_unprepare_transfer(struct spi_master *master) +{ + struct driver_data *drv_data = spi_master_get_devdata(master); + + /* Disable the SSP now */ + write_SSCR0(read_SSCR0(drv_data->ioaddr) & ~SSCR0_SSE, + drv_data->ioaddr); + + pm_runtime_mark_last_busy(&drv_data->pdev->dev); + pm_runtime_put_autosuspend(&drv_data->pdev->dev); + return 0; +} + static int setup_cs(struct spi_device *spi, struct chip_data *chip, struct pxa2xx_spi_chip *chip_info) { @@ -915,6 +947,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->cleanup = cleanup; master->setup = setup; master->transfer_one_message = pxa2xx_spi_transfer_one_message; + master->prepare_transfer_hardware = pxa2xx_spi_prepare_transfer; + master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; drv_data->ssp_type = ssp->type; drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT); @@ -980,6 +1014,11 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) goto out_error_clock_enabled; } + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return status; out_error_clock_enabled: @@ -1002,6 +1041,8 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) return 0; ssp = drv_data->ssp; + pm_runtime_get_sync(&pdev->dev); + /* Disable the SSP at the peripheral and SOC level */ write_SSCR0(0, drv_data->ioaddr); clk_disable_unprepare(ssp->clk); @@ -1010,6 +1051,9 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) if (drv_data->master_info->enable_dma) pxa2xx_spi_dma_release(drv_data); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); + /* Release IRQ */ free_irq(ssp->irq, drv_data); @@ -1069,20 +1113,37 @@ static int pxa2xx_spi_resume(struct device *dev) return 0; } +#endif + +#ifdef CONFIG_PM_RUNTIME +static int pxa2xx_spi_runtime_suspend(struct device *dev) +{ + struct driver_data *drv_data = dev_get_drvdata(dev); + + clk_disable_unprepare(drv_data->ssp->clk); + return 0; +} + +static int pxa2xx_spi_runtime_resume(struct device *dev) +{ + struct driver_data *drv_data = dev_get_drvdata(dev); + + clk_prepare_enable(drv_data->ssp->clk); + return 0; +} +#endif static const struct dev_pm_ops pxa2xx_spi_pm_ops = { - .suspend = pxa2xx_spi_suspend, - .resume = pxa2xx_spi_resume, + SET_SYSTEM_SLEEP_PM_OPS(pxa2xx_spi_suspend, pxa2xx_spi_resume) + SET_RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend, + pxa2xx_spi_runtime_resume, NULL) }; -#endif static struct platform_driver driver = { .driver = { .name = "pxa2xx-spi", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &pxa2xx_spi_pm_ops, -#endif }, .probe = pxa2xx_spi_probe, .remove = pxa2xx_spi_remove, From b833172fd8f44fb56e0b3cb810155a6baecc65dc Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:31 +0200 Subject: [PATCH 46/50] spi/pxa2xx: add support for SPI_LOOP This is useful when testing the functionality of the controller from userspace and there aren't any real SPI slave devices connected to the bus. Signed-off-by: Mika Westerberg Tested-by: Lu Cao Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 7f8f939835fd..8a5ba0aa095a 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -846,6 +846,9 @@ static int setup(struct spi_device *spi) chip->cr1 |= (((spi->mode & SPI_CPHA) != 0) ? SSCR1_SPH : 0) | (((spi->mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0); + if (spi->mode & SPI_LOOP) + chip->cr1 |= SSCR1_LBM; + /* NOTE: PXA25x_SSP _could_ use external clocking ... */ if (!pxa25x_ssp_comp(drv_data)) dev_dbg(&spi->dev, "%ld Hz actual, %s\n", @@ -939,7 +942,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->dev.parent = &pdev->dev; master->dev.of_node = pdev->dev.of_node; /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; master->bus_num = ssp->port_id; master->num_chipselect = platform_info->num_chipselect; From a0d2642e9296882cda3ad03ff3d9a6649cd70439 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:32 +0200 Subject: [PATCH 47/50] spi/pxa2xx: add support for Intel Low Power Subsystem SPI Intel LPSS SPI is pretty much the same as the PXA27xx SPI except that it has few additional features over the original: o FIFO depth is 256 entries o RX FIFO has one watermark o TX FIFO has two watermarks, low and high o chip select can be controlled by writing to a register The new FIFO registers follow immediately the PXA27xx registers but then there are some additional LPSS private registers at offset 1k or 2k from the base address. For these private registers we add new accessors that take advantage of drv_data->lpss_base once it is resolved. We add a new type LPSS_SSP that can be used to distinguish the LPSS devices from others. Signed-off-by: Mika Westerberg Tested-by: Lu Cao Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 133 ++++++++++++++++++++++++++++++++- drivers/spi/spi-pxa2xx.h | 6 ++ include/linux/pxa2xx_ssp.h | 9 +++ include/linux/spi/pxa2xx_spi.h | 1 + 4 files changed, 145 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 8a5ba0aa095a..4bd6b729f710 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * Copyright (C) 2013, Intel Corporation * * 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 @@ -61,6 +62,98 @@ MODULE_ALIAS("platform:pxa2xx-spi"); | SSCR1_RFT | SSCR1_TFT | SSCR1_MWDS \ | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) +#define LPSS_RX_THRESH_DFLT 64 +#define LPSS_TX_LOTHRESH_DFLT 160 +#define LPSS_TX_HITHRESH_DFLT 224 + +/* Offset from drv_data->lpss_base */ +#define SPI_CS_CONTROL 0x18 +#define SPI_CS_CONTROL_SW_MODE BIT(0) +#define SPI_CS_CONTROL_CS_HIGH BIT(1) + +static bool is_lpss_ssp(const struct driver_data *drv_data) +{ + return drv_data->ssp_type == LPSS_SSP; +} + +/* + * Read and write LPSS SSP private registers. Caller must first check that + * is_lpss_ssp() returns true before these can be called. + */ +static u32 __lpss_ssp_read_priv(struct driver_data *drv_data, unsigned offset) +{ + WARN_ON(!drv_data->lpss_base); + return readl(drv_data->lpss_base + offset); +} + +static void __lpss_ssp_write_priv(struct driver_data *drv_data, + unsigned offset, u32 value) +{ + WARN_ON(!drv_data->lpss_base); + writel(value, drv_data->lpss_base + offset); +} + +/* + * lpss_ssp_setup - perform LPSS SSP specific setup + * @drv_data: pointer to the driver private data + * + * Perform LPSS SSP specific setup. This function must be called first if + * one is going to use LPSS SSP private registers. + */ +static void lpss_ssp_setup(struct driver_data *drv_data) +{ + unsigned offset = 0x400; + u32 value, orig; + + if (!is_lpss_ssp(drv_data)) + return; + + /* + * Perform auto-detection of the LPSS SSP private registers. They + * can be either at 1k or 2k offset from the base address. + */ + orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); + + value = orig | SPI_CS_CONTROL_SW_MODE; + writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL); + value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); + if (value != (orig | SPI_CS_CONTROL_SW_MODE)) { + offset = 0x800; + goto detection_done; + } + + value &= ~SPI_CS_CONTROL_SW_MODE; + writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL); + value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); + if (value != orig) { + offset = 0x800; + goto detection_done; + } + +detection_done: + /* Now set the LPSS base */ + drv_data->lpss_base = drv_data->ioaddr + offset; + + /* Enable software chip select control */ + value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH; + __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); +} + +static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) +{ + u32 value; + + if (!is_lpss_ssp(drv_data)) + return; + + value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL); + if (enable) + value &= ~SPI_CS_CONTROL_CS_HIGH; + else + value |= SPI_CS_CONTROL_CS_HIGH; + __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); +} + static void cs_assert(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; @@ -75,8 +168,12 @@ static void cs_assert(struct driver_data *drv_data) return; } - if (gpio_is_valid(chip->gpio_cs)) + if (gpio_is_valid(chip->gpio_cs)) { gpio_set_value(chip->gpio_cs, chip->gpio_cs_inverted); + return; + } + + lpss_ssp_cs_control(drv_data, true); } static void cs_deassert(struct driver_data *drv_data) @@ -91,8 +188,12 @@ static void cs_deassert(struct driver_data *drv_data) return; } - if (gpio_is_valid(chip->gpio_cs)) + if (gpio_is_valid(chip->gpio_cs)) { gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted); + return; + } + + lpss_ssp_cs_control(drv_data, false); } int pxa2xx_spi_flush(struct driver_data *drv_data) @@ -642,6 +743,13 @@ static void pump_transfers(unsigned long data) write_SSSR_CS(drv_data, drv_data->clear_sr); } + if (is_lpss_ssp(drv_data)) { + if ((read_SSIRF(reg) & 0xff) != chip->lpss_rx_threshold) + write_SSIRF(chip->lpss_rx_threshold, reg); + if ((read_SSITF(reg) & 0xffff) != chip->lpss_tx_threshold) + write_SSITF(chip->lpss_tx_threshold, reg); + } + /* see if we need to reload the config registers */ if ((read_SSCR0(reg) != cr0) || (read_SSCR1(reg) & SSCR1_CHANGE_MASK) != @@ -754,8 +862,17 @@ static int setup(struct spi_device *spi) struct chip_data *chip; struct driver_data *drv_data = spi_master_get_devdata(spi->master); unsigned int clk_div; - uint tx_thres = TX_THRESH_DFLT; - uint rx_thres = RX_THRESH_DFLT; + uint tx_thres, tx_hi_thres, rx_thres; + + if (is_lpss_ssp(drv_data)) { + tx_thres = LPSS_TX_LOTHRESH_DFLT; + tx_hi_thres = LPSS_TX_HITHRESH_DFLT; + rx_thres = LPSS_RX_THRESH_DFLT; + } else { + tx_thres = TX_THRESH_DFLT; + tx_hi_thres = 0; + rx_thres = RX_THRESH_DFLT; + } if (!pxa25x_ssp_comp(drv_data) && (spi->bits_per_word < 4 || spi->bits_per_word > 32)) { @@ -808,6 +925,8 @@ static int setup(struct spi_device *spi) chip->timeout = chip_info->timeout; if (chip_info->tx_threshold) tx_thres = chip_info->tx_threshold; + if (chip_info->tx_hi_threshold) + tx_hi_thres = chip_info->tx_hi_threshold; if (chip_info->rx_threshold) rx_thres = chip_info->rx_threshold; chip->enable_dma = drv_data->master_info->enable_dma; @@ -819,6 +938,10 @@ static int setup(struct spi_device *spi) chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) | (SSCR1_TxTresh(tx_thres) & SSCR1_TFT); + chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres); + chip->lpss_tx_threshold = SSITF_TxLoThresh(tx_thres) + | SSITF_TxHiThresh(tx_hi_thres); + /* set dma burst and threshold outside of chip_info path so that if * chip_info goes away after setting chip->enable_dma, the * burst and threshold can still respond to changes in bits_per_word */ @@ -1006,6 +1129,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) write_SSTO(0, drv_data->ioaddr); write_SSPSP(0, drv_data->ioaddr); + lpss_ssp_setup(drv_data); + tasklet_init(&drv_data->pump_transfers, pump_transfers, (unsigned long)drv_data); diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 97ff4717e6ac..5adc2a11c7bc 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -86,6 +86,8 @@ struct driver_data { int (*read)(struct driver_data *drv_data); irqreturn_t (*transfer_handler)(struct driver_data *drv_data); void (*cs_control)(u32 command); + + void __iomem *lpss_base; }; struct chip_data { @@ -97,6 +99,8 @@ struct chip_data { u32 dma_burst_size; u32 threshold; u32 dma_threshold; + u16 lpss_rx_threshold; + u16 lpss_tx_threshold; u8 enable_dma; u8 bits_per_word; u32 speed_hz; @@ -124,6 +128,8 @@ DEFINE_SSP_REG(SSITR, 0x0c) DEFINE_SSP_REG(SSDR, 0x10) DEFINE_SSP_REG(SSTO, 0x28) DEFINE_SSP_REG(SSPSP, 0x2c) +DEFINE_SSP_REG(SSITF, SSITF) +DEFINE_SSP_REG(SSIRF, SSIRF) #define START_STATE ((void *)0) #define RUNNING_STATE ((void *)1) diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 065e7f6c3ad7..467cc6307b62 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -155,6 +155,14 @@ #define SSACD_ACDS(x) ((x) << 0) /* Audio clock divider select */ #define SSACD_SCDX8 (1 << 7) /* SYSCLK division ratio select */ +/* LPSS SSP */ +#define SSITF 0x44 /* TX FIFO trigger level */ +#define SSITF_TxLoThresh(x) (((x) - 1) << 8) +#define SSITF_TxHiThresh(x) ((x) - 1) + +#define SSIRF 0x48 /* RX FIFO trigger level */ +#define SSIRF_RxThresh(x) ((x) - 1) + enum pxa_ssp_type { SSP_UNDEFINED = 0, PXA25x_SSP, /* pxa 210, 250, 255, 26x */ @@ -164,6 +172,7 @@ enum pxa_ssp_type { PXA168_SSP, PXA910_SSP, CE4100_SSP, + LPSS_SSP, }; struct ssp_device { diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h index e5cbbc4c57f7..82d5111cd0c2 100644 --- a/include/linux/spi/pxa2xx_spi.h +++ b/include/linux/spi/pxa2xx_spi.h @@ -44,6 +44,7 @@ struct pxa2xx_spi_master { */ struct pxa2xx_spi_chip { u8 tx_threshold; + u8 tx_hi_threshold; u8 rx_threshold; u8 dma_burst_size; u32 timeout; From a3496855d9f1948d1b977afe8bd922725ded05d5 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 22 Jan 2013 12:26:33 +0200 Subject: [PATCH 48/50] spi/pxa2xx: add support for Lynxpoint SPI controllers Intel Lynxpoint PCH Low Power Subsystem has two general purpose SPI controllers that are LPSS_SSP compatible. These controllers are enumerated from ACPI namespace with ACPI IDs INT33C0 and INT33C1. Signed-off-by: Mika Westerberg Tested-by: Lu Cao Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- drivers/spi/spi-pxa2xx.c | 110 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 3 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8913d3dd5724..e79884e997ae 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -310,7 +310,7 @@ config SPI_PXA2XX_DMA config SPI_PXA2XX tristate "PXA2xx SSP SPI master" - depends on ARCH_PXA || PCI + depends on ARCH_PXA || PCI || ACPI select PXA_SSP if ARCH_PXA help This enables using a PXA2xx or Sodaville SSP port as a SPI master diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 4bd6b729f710..90b27a3508a6 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -933,6 +934,13 @@ static int setup(struct spi_device *spi) chip->dma_threshold = 0; if (chip_info->enable_loopback) chip->cr1 = SSCR1_LBM; + } else if (ACPI_HANDLE(&spi->dev)) { + /* + * Slave devices enumerated from ACPI namespace don't + * usually have chip_info but we still might want to use + * DMA with them. + */ + chip->enable_dma = drv_data->master_info->enable_dma; } chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) | @@ -1025,6 +1033,99 @@ static void cleanup(struct spi_device *spi) kfree(chip); } +#ifdef CONFIG_ACPI +static int pxa2xx_spi_acpi_add_dma(struct acpi_resource *res, void *data) +{ + struct pxa2xx_spi_master *pdata = data; + + if (res->type == ACPI_RESOURCE_TYPE_FIXED_DMA) { + const struct acpi_resource_fixed_dma *dma; + + dma = &res->data.fixed_dma; + if (pdata->tx_slave_id < 0) { + pdata->tx_slave_id = dma->request_lines; + pdata->tx_chan_id = dma->channels; + } else if (pdata->rx_slave_id < 0) { + pdata->rx_slave_id = dma->request_lines; + pdata->rx_chan_id = dma->channels; + } + } + + /* Tell the ACPI core to skip this resource */ + return 1; +} + +static struct pxa2xx_spi_master * +pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) +{ + struct pxa2xx_spi_master *pdata; + struct list_head resource_list; + struct acpi_device *adev; + struct ssp_device *ssp; + struct resource *res; + int devid; + + if (!ACPI_HANDLE(&pdev->dev) || + acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) + return NULL; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*ssp), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, + "failed to allocate memory for platform data\n"); + return NULL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return NULL; + + ssp = &pdata->ssp; + + ssp->phys_base = res->start; + ssp->mmio_base = devm_request_and_ioremap(&pdev->dev, res); + if (!ssp->mmio_base) { + dev_err(&pdev->dev, "failed to ioremap mmio_base\n"); + return NULL; + } + + ssp->clk = devm_clk_get(&pdev->dev, NULL); + ssp->irq = platform_get_irq(pdev, 0); + ssp->type = LPSS_SSP; + ssp->pdev = pdev; + + ssp->port_id = -1; + if (adev->pnp.unique_id && !kstrtoint(adev->pnp.unique_id, 0, &devid)) + ssp->port_id = devid; + + pdata->num_chipselect = 1; + pdata->rx_slave_id = -1; + pdata->tx_slave_id = -1; + + INIT_LIST_HEAD(&resource_list); + acpi_dev_get_resources(adev, &resource_list, pxa2xx_spi_acpi_add_dma, + pdata); + acpi_dev_free_resource_list(&resource_list); + + pdata->enable_dma = pdata->rx_slave_id >= 0 && pdata->tx_slave_id >= 0; + + return pdata; +} + +static struct acpi_device_id pxa2xx_spi_acpi_match[] = { + { "INT33C0", 0 }, + { "INT33C1", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); +#else +static inline struct pxa2xx_spi_master * +pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) +{ + return NULL; +} +#endif + static int pxa2xx_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1036,8 +1137,11 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) platform_info = dev_get_platdata(dev); if (!platform_info) { - dev_err(&pdev->dev, "missing platform data\n"); - return -ENODEV; + platform_info = pxa2xx_spi_acpi_get_pdata(pdev); + if (!platform_info) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } } ssp = pxa_ssp_request(pdev->id, pdev->name); @@ -1064,6 +1168,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->dev.parent = &pdev->dev; master->dev.of_node = pdev->dev.of_node; + ACPI_HANDLE_SET(&master->dev, ACPI_HANDLE(&pdev->dev)); /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; @@ -1272,6 +1377,7 @@ static struct platform_driver driver = { .name = "pxa2xx-spi", .owner = THIS_MODULE, .pm = &pxa2xx_spi_pm_ops, + .acpi_match_table = ACPI_PTR(pxa2xx_spi_acpi_match), }, .probe = pxa2xx_spi_probe, .remove = pxa2xx_spi_remove, From 0da83bb1c9d2690465d27cb8887918e6664f647e Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Tue, 29 Jan 2013 15:53:40 +0100 Subject: [PATCH 49/50] spi/of: Fix initialization of cs_gpios array Using memset does not set an array of integers properly. Replace with a loop to set each element properly. Signed-off-by: Andreas Larsson Signed-off-by: Grant Likely --- drivers/spi/spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index d1e0a316826d..6707cb2f4fa4 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1080,7 +1080,8 @@ static int of_spi_register_master(struct spi_master *master) if (!master->cs_gpios) return -ENOMEM; - memset(cs, -EINVAL, master->num_chipselect); + for (i = 0; i < master->num_chipselect; i++) + cs[i] = -EINVAL; for (i = 0; i < nb; i++) cs[i] = of_get_named_gpio(np, "cs-gpios", i); From 095c3752e673c0ba039a2f67fd867297fde75ae7 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Tue, 29 Jan 2013 15:53:41 +0100 Subject: [PATCH 50/50] spi: Document cs_gpios and cs_gpio in kernel-doc This adds missing kernel-doc entries for cs_gpios in struct spi_master and cs_gpio in struct spi_device. Signed-off-by: Andreas Larsson [grant.likely: tweaked the language of the descriptions] Signed-off-by: Grant Likely --- include/linux/spi/spi.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index f62918946d86..30e9c50a5e20 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -57,6 +57,8 @@ extern struct bus_type spi_bus_type; * @modalias: Name of the driver to use with this device, or an alias * for that name. This appears in the sysfs "modalias" attribute * for driver coldplugging, and in uevents used for hotplugging + * @cs_gpio: gpio number of the chipselect line (optional, -EINVAL when + * when not using a GPIO line) * * A @spi_device is used to interchange data between an SPI slave * (usually a discrete chip) and CPU memory. @@ -258,6 +260,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @unprepare_transfer_hardware: there are currently no more messages on the * queue so the subsystem notifies the driver that it may relax the * hardware by issuing this call + * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS + * number. Any individual value may be -EINVAL for CS lines that + * are not GPIOs (driven by the SPI controller itself). * * Each SPI master controller can communicate with one or more @spi_device * children. These make a small bus, sharing MOSI, MISO and SCK signals