From 0c3b97487d15a452f1fa7bfd9b6f1bd1a31d538d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 27 Mar 2014 09:26:38 +0800 Subject: spi: atmel: Make current_remaining_bytes to be int Don't use unsigned for current_remaining_bytes so we can check current_remaining_bytes < 0 case. Use int is enough for current_remaining_bytes. Signed-off-by: Axel Lin Signed-off-by: Wenyou Yang Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 8005f9869481..485e6cce94bf 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -224,7 +224,7 @@ struct atmel_spi { struct platform_device *pdev; struct spi_transfer *current_transfer; - unsigned long current_remaining_bytes; + int current_remaining_bytes; int done_status; struct completion xfer_completion; @@ -1110,6 +1110,8 @@ static int atmel_spi_one_transfer(struct spi_master *master, atmel_spi_next_xfer_pio(master, xfer); } else { as->current_remaining_bytes -= len; + if (as->current_remaining_bytes < 0) + as->current_remaining_bytes = 0; } } else { atmel_spi_next_xfer_pio(master, xfer); -- cgit v1.2.3 From eee668a92bc775030178c8a7f0d0abe9cce441d7 Mon Sep 17 00:00:00 2001 From: Harini Katakam Date: Fri, 11 Apr 2014 12:06:28 +0530 Subject: spi: core: Increase timeout value The existing timeout value in wait_for_completion_timeout is calculated from the transfer length and speed with tolerance of 10msec. This is too low because this is used for error conditions such as hardware hang etc. The xfer->speed_hz considered may not be the actual speed set because the best clock divisor is chosen from a limited set such that the actual speed <= requested speed. This will lead to timeout being less than actual transfer time. Considering acceptable latencies, this timeout can be set to a value double the expected transfer plus 100 msecs. This patch adds the same in the core. Signed-off-by: Harini Katakam Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4eb9bf02996c..f01cbb41e6ca 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -775,7 +775,7 @@ static int spi_transfer_one_message(struct spi_master *master, if (ret > 0) { ret = 0; ms = xfer->len * 8 * 1000 / xfer->speed_hz; - ms += 10; /* some tolerance */ + ms += ms + 100; /* some tolerance */ ms = wait_for_completion_timeout(&master->xfer_completion, msecs_to_jiffies(ms)); -- cgit v1.2.3 From cd6339e6ced387ad67b5551dd2931cfd7e8b970b Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Tue, 1 Apr 2014 17:10:50 +0800 Subject: spi: add "spi-lsb-first" to devicetree add optional property devicetree for SPI slave nodes into devicetree so that LSB mode can be enabled by devicetree. Signed-off-by: Zhao Qiang Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-bus.txt | 2 ++ drivers/spi/spi.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index e5a4d1b4acfe..22d57404fdc3 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -55,6 +55,8 @@ contain the following properties. chip select active high - spi-3wire - (optional) Empty property indicating device requires 3-wire mode. +- spi-lsb-first - (optional) Empty property indicating device requires + LSB first mode. - spi-tx-bus-width - (optional) The bus width(number of data wires) that used for MOSI. Defaults to 1 if not present. - spi-rx-bus-width - (optional) The bus width(number of data wires) that diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4eb9bf02996c..214045c04705 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1234,6 +1234,8 @@ static void of_register_spi_devices(struct spi_master *master) spi->mode |= SPI_CS_HIGH; if (of_find_property(nc, "spi-3wire", NULL)) spi->mode |= SPI_3WIRE; + if (of_find_property(nc, "spi-lsb-first", NULL)) + spi->mode |= SPI_LSB_FIRST; /* Device DUAL/QUAD mode */ if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) { -- cgit v1.2.3 From abcadeb255074d1351d110ef696b647859fa3d5b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Mar 2014 16:43:23 +0800 Subject: spi: fsl: Kill mpc8xxx_spi_cleanup and convert fsl_espi_setup to use devm_kzalloc In current code, master->cleanup and master->setup are not set in the same function. This makes it hard to read and not good for code maintain. One example is in fsl-spi.c, master->cleanup is overrided in mpc8xxx_spi_probe() which leads to a memory leak. This patch removes mpc8xxx_spi_cleanup() and converts fsl_espi_setup to use devm_kzalloc so we don't need to take care of freeing memory. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 2 +- drivers/spi/spi-fsl-lib.c | 6 ------ drivers/spi/spi-fsl-lib.h | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index e767f5831b9c..3cf7d65bc739 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -458,7 +458,7 @@ static int fsl_espi_setup(struct spi_device *spi) return -EINVAL; if (!cs) { - cs = kzalloc(sizeof *cs, GFP_KERNEL); + cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; spi->controller_state = cs; diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index e5d45fca3551..95212ea96c8d 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -99,11 +99,6 @@ int mpc8xxx_spi_transfer(struct spi_device *spi, return 0; } -void mpc8xxx_spi_cleanup(struct spi_device *spi) -{ - kfree(spi->controller_state); -} - const char *mpc8xxx_spi_strmode(unsigned int flags) { if (flags & SPI_QE_CPU_MODE) { @@ -134,7 +129,6 @@ int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, | SPI_LSB_FIRST | SPI_LOOP; master->transfer = mpc8xxx_spi_transfer; - master->cleanup = mpc8xxx_spi_cleanup; master->dev.of_node = dev->of_node; mpc8xxx_spi = spi_master_get_devdata(master); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 52db6936778e..2fcbfd01d109 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -124,7 +124,6 @@ extern struct mpc8xxx_spi_probe_info *to_of_pinfo( extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, unsigned int len); extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m); -extern void mpc8xxx_spi_cleanup(struct spi_device *spi); extern const char *mpc8xxx_spi_strmode(unsigned int flags); extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq); -- cgit v1.2.3 From 7a40054361162d2f78f332aa868fed137beb7901 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Mar 2014 16:42:57 +0800 Subject: spi: fsl-spi: Fix memory leak mpc8xxx_spi_probe() has set master->cleanup = mpc8xxx_spi_cleanup, however current code overrides the setting in fsl_spi_probe() and set master->cleanup = fsl_spi_cleanup. Thus the memory allocated for cs is not freed anywhere. Convert to use devm_kzalloc to fix the memory leak. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index b3e7775034db..98ccd231bf00 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -431,7 +431,7 @@ static int fsl_spi_setup(struct spi_device *spi) return -EINVAL; if (!cs) { - cs = kzalloc(sizeof *cs, GFP_KERNEL); + cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; spi->controller_state = cs; -- cgit v1.2.3 From c586feba880d78cab1882e1faee0395bba2b884f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 31 Mar 2014 11:37:29 +0800 Subject: spi: s3c24xx: Convert to use devm_kzalloc Simplify the cleanup code. Signed-off-by: Axel Lin Reviewed-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index bed23384dfab..6db14e53af17 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -183,7 +183,9 @@ static int s3c24xx_spi_setup(struct spi_device *spi) /* allocate settings on the first call */ if (!cs) { - cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL); + cs = devm_kzalloc(&spi->dev, + sizeof(struct s3c24xx_spi_devstate), + GFP_KERNEL); if (!cs) { dev_err(&spi->dev, "no memory for controller state\n"); return -ENOMEM; @@ -209,11 +211,6 @@ static int s3c24xx_spi_setup(struct spi_device *spi) return 0; } -static void s3c24xx_spi_cleanup(struct spi_device *spi) -{ - kfree(spi->controller_state); -} - static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) { return hw->tx ? hw->tx[count] : 0; @@ -543,7 +540,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; hw->master->setup = s3c24xx_spi_setup; - hw->master->cleanup = s3c24xx_spi_cleanup; dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); -- cgit v1.2.3 From e4745fef5595df7405ea24d6b21ba0e65332bb8e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 28 Mar 2014 11:05:55 +0800 Subject: spi: Remove unneeded include of linux/workqueue.h Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-ath79.c | 1 - drivers/spi/spi-bcm63xx-hsspi.c | 1 - drivers/spi/spi-bcm63xx.c | 1 - drivers/spi/spi-falcon.c | 1 - drivers/spi/spi-nuc900.c | 1 - drivers/spi/spi-omap-uwire.c | 1 - drivers/spi/spi-pxa2xx.c | 1 - drivers/spi/spi-s3c24xx.c | 1 - drivers/spi/spi-s3c64xx.c | 1 - drivers/spi/spi-sh-sci.c | 1 - drivers/spi/spi-sun4i.c | 1 - drivers/spi/spi-sun6i.c | 1 - 12 files changed, 12 deletions(-) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 3898b0b9ee77..058db0fe8dc7 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index 5a211e98383b..86f5a98aa7a2 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #define HSSPI_GLOBAL_CTRL_REG 0x0 diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 0250fa721cea..8510400e7867 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index 09965f069a1c..ba441ad9a007 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index 16e30de650b0..73e91d5a43df 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index be2a2e108e2f..0f5a0aa3b871 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 41185d0557fa..d0fc8e06bf43 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index bed23384dfab..1f3ef978c1d3 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index f19cd97855e8..153b4bec21c1 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index 8b44b71f5024..1f56ef651d1a 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index d266a8702067..85204c93f3d3 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -19,7 +19,6 @@ #include #include #include -#include #include diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index b3e3498a7e6f..bd24093f4038 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -20,7 +20,6 @@ #include #include #include -#include #include -- cgit v1.2.3 From 766e3721990d2c78e0d614b57753f105adbaa8c5 Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Fri, 4 Apr 2014 16:27:17 +0800 Subject: spi: convert spi-bfin-v3.c to a multiplatform driver Spi v3 controller is not only used on Blackfin. So rename it and use ioread/iowrite api to make it work on other platform. Signed-off-by: Scott Jiang Signed-off-by: Mark Brown --- arch/blackfin/include/asm/bfin_spi3.h | 258 --------- arch/blackfin/mach-bf609/boards/ezkit.c | 22 +- drivers/spi/Kconfig | 4 +- drivers/spi/Makefile | 2 +- drivers/spi/spi-adi-v3.c | 985 ++++++++++++++++++++++++++++++++ drivers/spi/spi-bfin-v3.c | 965 ------------------------------- include/linux/spi/adi_spi3.h | 254 ++++++++ 7 files changed, 1253 insertions(+), 1237 deletions(-) delete mode 100644 arch/blackfin/include/asm/bfin_spi3.h create mode 100644 drivers/spi/spi-adi-v3.c delete mode 100644 drivers/spi/spi-bfin-v3.c create mode 100644 include/linux/spi/adi_spi3.h diff --git a/arch/blackfin/include/asm/bfin_spi3.h b/arch/blackfin/include/asm/bfin_spi3.h deleted file mode 100644 index 0957e65a54be..000000000000 --- a/arch/blackfin/include/asm/bfin_spi3.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Analog Devices SPI3 controller driver - * - * Copyright (c) 2011 Analog Devices Inc. - * - * 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. - * - * 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. - */ - -#ifndef _SPI_CHANNEL_H_ -#define _SPI_CHANNEL_H_ - -#include - -/* SPI_CONTROL */ -#define SPI_CTL_EN 0x00000001 /* Enable */ -#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */ -#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */ -#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */ -#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */ -#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */ -#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */ -#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in-between transfers */ -#define SPI_CTL_EMISO 0x00000100 /* Enable MISO */ -#define SPI_CTL_SIZE 0x00000600 /* Word Transfer Size */ -#define SPI_CTL_SIZE08 0x00000000 /* SIZE: 8 bits */ -#define SPI_CTL_SIZE16 0x00000200 /* SIZE: 16 bits */ -#define SPI_CTL_SIZE32 0x00000400 /* SIZE: 32 bits */ -#define SPI_CTL_LSBF 0x00001000 /* LSB First */ -#define SPI_CTL_FCEN 0x00002000 /* Flow-Control Enable */ -#define SPI_CTL_FCCH 0x00004000 /* Flow-Control Channel Selection */ -#define SPI_CTL_FCPL 0x00008000 /* Flow-Control Polarity */ -#define SPI_CTL_FCWM 0x00030000 /* Flow-Control Water-Mark */ -#define SPI_CTL_FIFO0 0x00000000 /* FCWM: TFIFO empty or RFIFO Full */ -#define SPI_CTL_FIFO1 0x00010000 /* FCWM: TFIFO 75% or more empty or RFIFO 75% or more full */ -#define SPI_CTL_FIFO2 0x00020000 /* FCWM: TFIFO 50% or more empty or RFIFO 50% or more full */ -#define SPI_CTL_FMODE 0x00040000 /* Fast-mode Enable */ -#define SPI_CTL_MIOM 0x00300000 /* Multiple I/O Mode */ -#define SPI_CTL_MIO_DIS 0x00000000 /* MIOM: Disable */ -#define SPI_CTL_MIO_DUAL 0x00100000 /* MIOM: Enable DIOM (Dual I/O Mode) */ -#define SPI_CTL_MIO_QUAD 0x00200000 /* MIOM: Enable QUAD (Quad SPI Mode) */ -#define SPI_CTL_SOSI 0x00400000 /* Start on MOSI */ -/* SPI_RX_CONTROL */ -#define SPI_RXCTL_REN 0x00000001 /* Receive Channel Enable */ -#define SPI_RXCTL_RTI 0x00000004 /* Receive Transfer Initiate */ -#define SPI_RXCTL_RWCEN 0x00000008 /* Receive Word Counter Enable */ -#define SPI_RXCTL_RDR 0x00000070 /* Receive Data Request */ -#define SPI_RXCTL_RDR_DIS 0x00000000 /* RDR: Disabled */ -#define SPI_RXCTL_RDR_NE 0x00000010 /* RDR: RFIFO not empty */ -#define SPI_RXCTL_RDR_25 0x00000020 /* RDR: RFIFO 25% full */ -#define SPI_RXCTL_RDR_50 0x00000030 /* RDR: RFIFO 50% full */ -#define SPI_RXCTL_RDR_75 0x00000040 /* RDR: RFIFO 75% full */ -#define SPI_RXCTL_RDR_FULL 0x00000050 /* RDR: RFIFO full */ -#define SPI_RXCTL_RDO 0x00000100 /* Receive Data Over-Run */ -#define SPI_RXCTL_RRWM 0x00003000 /* FIFO Regular Water-Mark */ -#define SPI_RXCTL_RWM_0 0x00000000 /* RRWM: RFIFO Empty */ -#define SPI_RXCTL_RWM_25 0x00001000 /* RRWM: RFIFO 25% full */ -#define SPI_RXCTL_RWM_50 0x00002000 /* RRWM: RFIFO 50% full */ -#define SPI_RXCTL_RWM_75 0x00003000 /* RRWM: RFIFO 75% full */ -#define SPI_RXCTL_RUWM 0x00070000 /* FIFO Urgent Water-Mark */ -#define SPI_RXCTL_UWM_DIS 0x00000000 /* RUWM: Disabled */ -#define SPI_RXCTL_UWM_25 0x00010000 /* RUWM: RFIFO 25% full */ -#define SPI_RXCTL_UWM_50 0x00020000 /* RUWM: RFIFO 50% full */ -#define SPI_RXCTL_UWM_75 0x00030000 /* RUWM: RFIFO 75% full */ -#define SPI_RXCTL_UWM_FULL 0x00040000 /* RUWM: RFIFO full */ -/* SPI_TX_CONTROL */ -#define SPI_TXCTL_TEN 0x00000001 /* Transmit Channel Enable */ -#define SPI_TXCTL_TTI 0x00000004 /* Transmit Transfer Initiate */ -#define SPI_TXCTL_TWCEN 0x00000008 /* Transmit Word Counter Enable */ -#define SPI_TXCTL_TDR 0x00000070 /* Transmit Data Request */ -#define SPI_TXCTL_TDR_DIS 0x00000000 /* TDR: Disabled */ -#define SPI_TXCTL_TDR_NF 0x00000010 /* TDR: TFIFO not full */ -#define SPI_TXCTL_TDR_25 0x00000020 /* TDR: TFIFO 25% empty */ -#define SPI_TXCTL_TDR_50 0x00000030 /* TDR: TFIFO 50% empty */ -#define SPI_TXCTL_TDR_75 0x00000040 /* TDR: TFIFO 75% empty */ -#define SPI_TXCTL_TDR_EMPTY 0x00000050 /* TDR: TFIFO empty */ -#define SPI_TXCTL_TDU 0x00000100 /* Transmit Data Under-Run */ -#define SPI_TXCTL_TRWM 0x00003000 /* FIFO Regular Water-Mark */ -#define SPI_TXCTL_RWM_FULL 0x00000000 /* TRWM: TFIFO full */ -#define SPI_TXCTL_RWM_25 0x00001000 /* TRWM: TFIFO 25% empty */ -#define SPI_TXCTL_RWM_50 0x00002000 /* TRWM: TFIFO 50% empty */ -#define SPI_TXCTL_RWM_75 0x00003000 /* TRWM: TFIFO 75% empty */ -#define SPI_TXCTL_TUWM 0x00070000 /* FIFO Urgent Water-Mark */ -#define SPI_TXCTL_UWM_DIS 0x00000000 /* TUWM: Disabled */ -#define SPI_TXCTL_UWM_25 0x00010000 /* TUWM: TFIFO 25% empty */ -#define SPI_TXCTL_UWM_50 0x00020000 /* TUWM: TFIFO 50% empty */ -#define SPI_TXCTL_UWM_75 0x00030000 /* TUWM: TFIFO 75% empty */ -#define SPI_TXCTL_UWM_EMPTY 0x00040000 /* TUWM: TFIFO empty */ -/* SPI_CLOCK */ -#define SPI_CLK_BAUD 0x0000FFFF /* Baud Rate */ -/* SPI_DELAY */ -#define SPI_DLY_STOP 0x000000FF /* Transfer delay time in multiples of SCK period */ -#define SPI_DLY_LEADX 0x00000100 /* Extended (1 SCK) LEAD Control */ -#define SPI_DLY_LAGX 0x00000200 /* Extended (1 SCK) LAG control */ -/* SPI_SSEL */ -#define SPI_SLVSEL_SSE1 0x00000002 /* SPISSEL1 Enable */ -#define SPI_SLVSEL_SSE2 0x00000004 /* SPISSEL2 Enable */ -#define SPI_SLVSEL_SSE3 0x00000008 /* SPISSEL3 Enable */ -#define SPI_SLVSEL_SSE4 0x00000010 /* SPISSEL4 Enable */ -#define SPI_SLVSEL_SSE5 0x00000020 /* SPISSEL5 Enable */ -#define SPI_SLVSEL_SSE6 0x00000040 /* SPISSEL6 Enable */ -#define SPI_SLVSEL_SSE7 0x00000080 /* SPISSEL7 Enable */ -#define SPI_SLVSEL_SSEL1 0x00000200 /* SPISSEL1 Value */ -#define SPI_SLVSEL_SSEL2 0x00000400 /* SPISSEL2 Value */ -#define SPI_SLVSEL_SSEL3 0x00000800 /* SPISSEL3 Value */ -#define SPI_SLVSEL_SSEL4 0x00001000 /* SPISSEL4 Value */ -#define SPI_SLVSEL_SSEL5 0x00002000 /* SPISSEL5 Value */ -#define SPI_SLVSEL_SSEL6 0x00004000 /* SPISSEL6 Value */ -#define SPI_SLVSEL_SSEL7 0x00008000 /* SPISSEL7 Value */ -/* SPI_RWC */ -#define SPI_RWC_VALUE 0x0000FFFF /* Received Word-Count */ -/* SPI_RWCR */ -#define SPI_RWCR_VALUE 0x0000FFFF /* Received Word-Count Reload */ -/* SPI_TWC */ -#define SPI_TWC_VALUE 0x0000FFFF /* Transmitted Word-Count */ -/* SPI_TWCR */ -#define SPI_TWCR_VALUE 0x0000FFFF /* Transmitted Word-Count Reload */ -/* SPI_IMASK */ -#define SPI_IMSK_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_IMASKCL */ -#define SPI_IMSK_CLR_RUW 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_CLR_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_CLR_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_CLR_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_CLR_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_CLR_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_CLR_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_CLR_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_CLR_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_CLR_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_IMASKST */ -#define SPI_IMSK_SET_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_SET_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_SET_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_SET_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_SET_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_SET_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_SET_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_SET_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_SET_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_SET_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_STATUS */ -#define SPI_STAT_SPIF 0x00000001 /* SPI Finished */ -#define SPI_STAT_RUWM 0x00000002 /* Receive Urgent Water-Mark Breached */ -#define SPI_STAT_TUWM 0x00000004 /* Transmit Urgent Water-Mark Breached */ -#define SPI_STAT_ROE 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_STAT_TUE 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_STAT_TCE 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_STAT_MODF 0x00000080 /* Mode Fault Error Indication */ -#define SPI_STAT_RS 0x00000100 /* Receive Start Indication */ -#define SPI_STAT_TS 0x00000200 /* Transmit Start Indication */ -#define SPI_STAT_RF 0x00000400 /* Receive Finish Indication */ -#define SPI_STAT_TF 0x00000800 /* Transmit Finish Indication */ -#define SPI_STAT_RFS 0x00007000 /* SPI_RFIFO status */ -#define SPI_STAT_RFIFO_EMPTY 0x00000000 /* RFS: RFIFO Empty */ -#define SPI_STAT_RFIFO_25 0x00001000 /* RFS: RFIFO 25% Full */ -#define SPI_STAT_RFIFO_50 0x00002000 /* RFS: RFIFO 50% Full */ -#define SPI_STAT_RFIFO_75 0x00003000 /* RFS: RFIFO 75% Full */ -#define SPI_STAT_RFIFO_FULL 0x00004000 /* RFS: RFIFO Full */ -#define SPI_STAT_TFS 0x00070000 /* SPI_TFIFO status */ -#define SPI_STAT_TFIFO_FULL 0x00000000 /* TFS: TFIFO full */ -#define SPI_STAT_TFIFO_25 0x00010000 /* TFS: TFIFO 25% empty */ -#define SPI_STAT_TFIFO_50 0x00020000 /* TFS: TFIFO 50% empty */ -#define SPI_STAT_TFIFO_75 0x00030000 /* TFS: TFIFO 75% empty */ -#define SPI_STAT_TFIFO_EMPTY 0x00040000 /* TFS: TFIFO empty */ -#define SPI_STAT_FCS 0x00100000 /* Flow-Control Stall Indication */ -#define SPI_STAT_RFE 0x00400000 /* SPI_RFIFO Empty */ -#define SPI_STAT_TFF 0x00800000 /* SPI_TFIFO Full */ -/* SPI_ILAT */ -#define SPI_ILAT_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ -#define SPI_ILAT_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ -#define SPI_ILAT_ROI 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_ILAT_TUI 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_ILAT_TCI 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_ILAT_MFI 0x00000080 /* Mode Fault Error Indication */ -#define SPI_ILAT_RSI 0x00000100 /* Receive Start Indication */ -#define SPI_ILAT_TSI 0x00000200 /* Transmit Start Indication */ -#define SPI_ILAT_RFI 0x00000400 /* Receive Finish Indication */ -#define SPI_ILAT_TFI 0x00000800 /* Transmit Finish Indication */ -/* SPI_ILATCL */ -#define SPI_ILAT_CLR_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ -#define SPI_ILAT_CLR_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ -#define SPI_ILAT_CLR_ROI 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_ILAT_CLR_TUI 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_ILAT_CLR_TCI 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_ILAT_CLR_MFI 0x00000080 /* Mode Fault Error Indication */ -#define SPI_ILAT_CLR_RSI 0x00000100 /* Receive Start Indication */ -#define SPI_ILAT_CLR_TSI 0x00000200 /* Transmit Start Indication */ -#define SPI_ILAT_CLR_RFI 0x00000400 /* Receive Finish Indication */ -#define SPI_ILAT_CLR_TFI 0x00000800 /* Transmit Finish Indication */ - -/* - * bfin spi3 registers layout - */ -struct bfin_spi_regs { - u32 revid; - u32 control; - u32 rx_control; - u32 tx_control; - u32 clock; - u32 delay; - u32 ssel; - u32 rwc; - u32 rwcr; - u32 twc; - u32 twcr; - u32 reserved0; - u32 emask; - u32 emaskcl; - u32 emaskst; - u32 reserved1; - u32 status; - u32 elat; - u32 elatcl; - u32 reserved2; - u32 rfifo; - u32 reserved3; - u32 tfifo; -}; - -#define MAX_CTRL_CS 8 /* cs in spi controller */ - -/* device.platform_data for SSP controller devices */ -struct bfin_spi3_master { - u16 num_chipselect; - u16 pin_req[7]; -}; - -/* spi_board_info.controller_data for SPI slave devices, - * copied to spi_device.platform_data ... mostly for dma tuning - */ -struct bfin_spi3_chip { - u32 control; - u16 cs_chg_udelay; /* Some devices require 16-bit delays */ - u32 tx_dummy_val; /* tx value for rx only transfer */ - bool enable_dma; -}; - -#endif /* _SPI_CHANNEL_H_ */ diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c index 943f7e95ec15..1ba4600de69f 100644 --- a/arch/blackfin/mach-bf609/boards/ezkit.c +++ b/arch/blackfin/mach-bf609/boards/ezkit.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -767,13 +767,13 @@ static struct flash_platform_data bfin_spi_flash_data = { .type = "w25q32", }; -static struct bfin_spi3_chip spi_flash_chip_info = { +static struct adi_spi3_chip spi_flash_chip_info = { .enable_dma = true, /* use dma transfer with this chip*/ }; #endif #if IS_ENABLED(CONFIG_SPI_SPIDEV) -static struct bfin_spi3_chip spidev_chip_info = { +static struct adi_spi3_chip spidev_chip_info = { .enable_dma = true, }; #endif @@ -1736,7 +1736,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { }, #endif }; -#if IS_ENABLED(CONFIG_SPI_BFIN_V3) +#if IS_ENABLED(CONFIG_SPI_ADI_V3) /* SPI (0) */ static struct resource bfin_spi0_resource[] = { { @@ -1777,13 +1777,13 @@ static struct resource bfin_spi1_resource[] = { }; /* SPI controller data */ -static struct bfin_spi3_master bf60x_spi_master_info0 = { +static struct adi_spi3_master bf60x_spi_master_info0 = { .num_chipselect = MAX_CTRL_CS + MAX_BLACKFIN_GPIOS, .pin_req = {P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0}, }; static struct platform_device bf60x_spi_master0 = { - .name = "bfin-spi3", + .name = "adi-spi3", .id = 0, /* Bus number */ .num_resources = ARRAY_SIZE(bfin_spi0_resource), .resource = bfin_spi0_resource, @@ -1792,13 +1792,13 @@ static struct platform_device bf60x_spi_master0 = { }, }; -static struct bfin_spi3_master bf60x_spi_master_info1 = { +static struct adi_spi3_master bf60x_spi_master_info1 = { .num_chipselect = MAX_CTRL_CS + MAX_BLACKFIN_GPIOS, .pin_req = {P_SPI1_SCK, P_SPI1_MISO, P_SPI1_MOSI, 0}, }; static struct platform_device bf60x_spi_master1 = { - .name = "bfin-spi3", + .name = "adi-spi3", .id = 1, /* Bus number */ .num_resources = ARRAY_SIZE(bfin_spi1_resource), .resource = bfin_spi1_resource, @@ -1990,7 +1990,7 @@ static struct platform_device *ezkit_devices[] __initdata = { &bfin_sdh_device, #endif -#if IS_ENABLED(CONFIG_SPI_BFIN_V3) +#if IS_ENABLED(CONFIG_SPI_ADI_V3) &bf60x_spi_master0, &bf60x_spi_master1, #endif @@ -2051,8 +2051,8 @@ static struct pinctrl_map __initdata bfin_pinmux_map[] = { PIN_MAP_MUX_GROUP_DEFAULT("bfin_sir.1", "pinctrl-adi2.0", NULL, "uart1"), PIN_MAP_MUX_GROUP_DEFAULT("bfin-sdh.0", "pinctrl-adi2.0", NULL, "rsi0"), PIN_MAP_MUX_GROUP_DEFAULT("stmmaceth.0", "pinctrl-adi2.0", NULL, "eth0"), - PIN_MAP_MUX_GROUP_DEFAULT("bfin-spi3.0", "pinctrl-adi2.0", NULL, "spi0"), - PIN_MAP_MUX_GROUP_DEFAULT("bfin-spi3.1", "pinctrl-adi2.0", NULL, "spi1"), + PIN_MAP_MUX_GROUP_DEFAULT("adi-spi3.0", "pinctrl-adi2.0", NULL, "spi0"), + PIN_MAP_MUX_GROUP_DEFAULT("adi-spi3.1", "pinctrl-adi2.0", NULL, "spi1"), PIN_MAP_MUX_GROUP_DEFAULT("i2c-bfin-twi.0", "pinctrl-adi2.0", NULL, "twi0"), PIN_MAP_MUX_GROUP_DEFAULT("i2c-bfin-twi.1", "pinctrl-adi2.0", NULL, "twi1"), PIN_MAP_MUX_GROUP_DEFAULT("bfin-rotary", "pinctrl-adi2.0", NULL, "rotary"), diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60f2b41c7310..a52e0edb7146 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -91,8 +91,8 @@ config SPI_BFIN5XX help This is the SPI controller master driver for Blackfin 5xx processor. -config SPI_BFIN_V3 - tristate "SPI controller v3 for Blackfin" +config SPI_ADI_V3 + tristate "SPI controller v3 for ADI" depends on BF60x help This is the SPI controller v3 master driver diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bd792669e563..71e65dfc0ea3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o -obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o +obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o diff --git a/drivers/spi/spi-adi-v3.c b/drivers/spi/spi-adi-v3.c new file mode 100644 index 000000000000..0c2914cfcdb5 --- /dev/null +++ b/drivers/spi/spi-adi-v3.c @@ -0,0 +1,985 @@ +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2014 Analog Devices Inc. + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +enum adi_spi_state { + START_STATE, + RUNNING_STATE, + DONE_STATE, + ERROR_STATE +}; + +struct adi_spi_master; + +struct adi_spi_transfer_ops { + void (*write) (struct adi_spi_master *); + void (*read) (struct adi_spi_master *); + void (*duplex) (struct adi_spi_master *); +}; + +/* runtime info for spi master */ +struct adi_spi_master { + /* SPI framework hookup */ + struct spi_master *master; + + /* Regs base of SPI controller */ + struct adi_spi_regs __iomem *regs; + + /* Pin request list */ + u16 *pin_req; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message transfer state info */ + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct adi_spi_device *cur_chip; + unsigned transfer_len; + + /* transfer buffer */ + void *tx; + void *tx_end; + void *rx; + void *rx_end; + + /* dma info */ + unsigned int tx_dma; + unsigned int rx_dma; + dma_addr_t tx_dma_addr; + dma_addr_t rx_dma_addr; + unsigned long dummy_buffer; /* used in unidirectional transfer */ + unsigned long tx_dma_size; + unsigned long rx_dma_size; + int tx_num; + int rx_num; + + /* store register value for suspend/resume */ + u32 control; + u32 ssel; + + unsigned long sclk; + enum adi_spi_state state; + + const struct adi_spi_transfer_ops *ops; +}; + +struct adi_spi_device { + u32 control; + u32 clock; + u32 ssel; + + u8 cs; + u16 cs_chg_udelay; /* Some devices require > 255usec delay */ + u32 cs_gpio; + u32 tx_dummy_val; /* tx value for rx only transfer */ + bool enable_dma; + const struct adi_spi_transfer_ops *ops; +}; + +static void adi_spi_enable(struct adi_spi_master *drv_data) +{ + u32 ctl; + + ctl = ioread32(&drv_data->regs->control); + ctl |= SPI_CTL_EN; + iowrite32(ctl, &drv_data->regs->control); +} + +static void adi_spi_disable(struct adi_spi_master *drv_data) +{ + u32 ctl; + + ctl = ioread32(&drv_data->regs->control); + ctl &= ~SPI_CTL_EN; + iowrite32(ctl, &drv_data->regs->control); +} + +/* Caculate the SPI_CLOCK register value based on input HZ */ +static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz) +{ + u32 spi_clock = sclk / speed_hz; + + if (spi_clock) + spi_clock--; + return spi_clock; +} + +static int adi_spi_flush(struct adi_spi_master *drv_data) +{ + unsigned long limit = loops_per_jiffy << 1; + + /* wait for stop and clear stat */ + while (!(ioread32(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit) + cpu_relax(); + + iowrite32(0xFFFFFFFF, &drv_data->regs->status); + + return limit; +} + +/* Chip select operation functions for cs_change flag */ +static void adi_spi_cs_active(struct adi_spi_master *drv_data, struct adi_spi_device *chip) +{ + if (likely(chip->cs < MAX_CTRL_CS)) { + u32 reg; + reg = ioread32(&drv_data->regs->ssel); + reg &= ~chip->ssel; + iowrite32(reg, &drv_data->regs->ssel); + } else { + gpio_set_value(chip->cs_gpio, 0); + } +} + +static void adi_spi_cs_deactive(struct adi_spi_master *drv_data, + struct adi_spi_device *chip) +{ + if (likely(chip->cs < MAX_CTRL_CS)) { + u32 reg; + reg = ioread32(&drv_data->regs->ssel); + reg |= chip->ssel; + iowrite32(reg, &drv_data->regs->ssel); + } else { + gpio_set_value(chip->cs_gpio, 1); + } + + /* Move delay here for consistency */ + if (chip->cs_chg_udelay) + udelay(chip->cs_chg_udelay); +} + +/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ +static inline void adi_spi_cs_enable(struct adi_spi_master *drv_data, + struct adi_spi_device *chip) +{ + if (chip->cs < MAX_CTRL_CS) { + u32 reg; + reg = ioread32(&drv_data->regs->ssel); + reg |= chip->ssel >> 8; + iowrite32(reg, &drv_data->regs->ssel); + } +} + +static inline void adi_spi_cs_disable(struct adi_spi_master *drv_data, + struct adi_spi_device *chip) +{ + if (chip->cs < MAX_CTRL_CS) { + u32 reg; + reg = ioread32(&drv_data->regs->ssel); + reg &= ~(chip->ssel >> 8); + iowrite32(reg, &drv_data->regs->ssel); + } +} + +/* stop controller and re-config current chip*/ +static void adi_spi_restore_state(struct adi_spi_master *drv_data) +{ + struct adi_spi_device *chip = drv_data->cur_chip; + + /* Clear status and disable clock */ + iowrite32(0xFFFFFFFF, &drv_data->regs->status); + iowrite32(0x0, &drv_data->regs->rx_control); + iowrite32(0x0, &drv_data->regs->tx_control); + adi_spi_disable(drv_data); + + /* Load the registers */ + iowrite32(chip->control, &drv_data->regs->control); + iowrite32(chip->clock, &drv_data->regs->clock); + + adi_spi_enable(drv_data); + drv_data->tx_num = drv_data->rx_num = 0; + /* we always choose tx transfer initiate */ + iowrite32(SPI_RXCTL_REN, &drv_data->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &drv_data->regs->tx_control); + adi_spi_cs_active(drv_data, chip); +} + +/* discard invalid rx data and empty rfifo */ +static inline void dummy_read(struct adi_spi_master *drv_data) +{ + while (!(ioread32(&drv_data->regs->status) & SPI_STAT_RFE)) + ioread32(&drv_data->regs->rfifo); +} + +static void adi_spi_u8_write(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + iowrite32(*(u8 *)(drv_data->tx++), &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv_data->regs->rfifo); + } +} + +static void adi_spi_u8_read(struct adi_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(tx_val, &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(drv_data->rx++) = ioread32(&drv_data->regs->rfifo); + } +} + +static void adi_spi_u8_duplex(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(*(u8 *)(drv_data->tx++), &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(drv_data->rx++) = ioread32(&drv_data->regs->rfifo); + } +} + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u8 = { + .write = adi_spi_u8_write, + .read = adi_spi_u8_read, + .duplex = adi_spi_u8_duplex, +}; + +static void adi_spi_u16_write(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + iowrite32(*(u16 *)drv_data->tx, &drv_data->regs->tfifo); + drv_data->tx += 2; + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv_data->regs->rfifo); + } +} + +static void adi_spi_u16_read(struct adi_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(tx_val, &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)drv_data->rx = ioread32(&drv_data->regs->rfifo); + drv_data->rx += 2; + } +} + +static void adi_spi_u16_duplex(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(*(u16 *)drv_data->tx, &drv_data->regs->tfifo); + drv_data->tx += 2; + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)drv_data->rx = ioread32(&drv_data->regs->rfifo); + drv_data->rx += 2; + } +} + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u16 = { + .write = adi_spi_u16_write, + .read = adi_spi_u16_read, + .duplex = adi_spi_u16_duplex, +}; + +static void adi_spi_u32_write(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + iowrite32(*(u32 *)drv_data->tx, &drv_data->regs->tfifo); + drv_data->tx += 4; + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv_data->regs->rfifo); + } +} + +static void adi_spi_u32_read(struct adi_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(tx_val, &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)drv_data->rx = ioread32(&drv_data->regs->rfifo); + drv_data->rx += 4; + } +} + +static void adi_spi_u32_duplex(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(*(u32 *)drv_data->tx, &drv_data->regs->tfifo); + drv_data->tx += 4; + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)drv_data->rx = ioread32(&drv_data->regs->rfifo); + drv_data->rx += 4; + } +} + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u32 = { + .write = adi_spi_u32_write, + .read = adi_spi_u32_read, + .duplex = adi_spi_u32_duplex, +}; + + +/* test if there is more transfer to be done */ +static void adi_spi_next_transfer(struct adi_spi_master *drv) +{ + struct spi_message *msg = drv->cur_msg; + struct spi_transfer *t = drv->cur_transfer; + + /* Move to next transfer */ + if (t->transfer_list.next != &msg->transfers) { + drv->cur_transfer = list_entry(t->transfer_list.next, + struct spi_transfer, transfer_list); + drv->state = RUNNING_STATE; + } else { + drv->state = DONE_STATE; + drv->cur_transfer = NULL; + } +} + +static void adi_spi_giveback(struct adi_spi_master *drv_data) +{ + struct adi_spi_device *chip = drv_data->cur_chip; + + adi_spi_cs_deactive(drv_data, chip); + spi_finalize_current_message(drv_data->master); +} + +static int adi_spi_setup_transfer(struct adi_spi_master *drv) +{ + struct spi_transfer *t = drv->cur_transfer; + u32 cr, cr_width; + + if (t->tx_buf) { + drv->tx = (void *)t->tx_buf; + drv->tx_end = drv->tx + t->len; + } else { + drv->tx = NULL; + } + + if (t->rx_buf) { + drv->rx = t->rx_buf; + drv->rx_end = drv->rx + t->len; + } else { + drv->rx = NULL; + } + + drv->transfer_len = t->len; + + /* bits per word setup */ + switch (t->bits_per_word) { + case 8: + cr_width = SPI_CTL_SIZE08; + drv->ops = &adi_spi_transfer_ops_u8; + break; + case 16: + cr_width = SPI_CTL_SIZE16; + drv->ops = &adi_spi_transfer_ops_u16; + break; + case 32: + cr_width = SPI_CTL_SIZE32; + drv->ops = &adi_spi_transfer_ops_u32; + break; + default: + return -EINVAL; + } + cr = ioread32(&drv->regs->control) & ~SPI_CTL_SIZE; + cr |= cr_width; + iowrite32(cr, &drv->regs->control); + + /* speed setup */ + iowrite32(hz_to_spi_clock(drv->sclk, t->speed_hz), &drv->regs->clock); + return 0; +} + +static int adi_spi_dma_xfer(struct adi_spi_master *drv_data) +{ + struct spi_transfer *t = drv_data->cur_transfer; + struct spi_message *msg = drv_data->cur_msg; + struct adi_spi_device *chip = drv_data->cur_chip; + u32 dma_config; + unsigned long word_count, word_size; + void *tx_buf, *rx_buf; + + switch (t->bits_per_word) { + case 8: + dma_config = WDSIZE_8 | PSIZE_8; + word_count = drv_data->transfer_len; + word_size = 1; + break; + case 16: + dma_config = WDSIZE_16 | PSIZE_16; + word_count = drv_data->transfer_len / 2; + word_size = 2; + break; + default: + dma_config = WDSIZE_32 | PSIZE_32; + word_count = drv_data->transfer_len / 4; + word_size = 4; + break; + } + + if (!drv_data->rx) { + tx_buf = drv_data->tx; + rx_buf = &drv_data->dummy_buffer; + drv_data->tx_dma_size = drv_data->transfer_len; + drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer); + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, 0); + } else if (!drv_data->tx) { + drv_data->dummy_buffer = chip->tx_dummy_val; + tx_buf = &drv_data->dummy_buffer; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer); + drv_data->rx_dma_size = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, 0); + set_dma_x_modify(drv_data->rx_dma, word_size); + } else { + tx_buf = drv_data->tx; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = drv_data->rx_dma_size + = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, word_size); + } + + drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)tx_buf, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->tx_dma_addr)) + return -ENOMEM; + + drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)rx_buf, + drv_data->rx_dma_size, + DMA_FROM_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->rx_dma_addr)) { + dma_unmap_single(&msg->spi->dev, + drv_data->tx_dma_addr, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + return -ENOMEM; + } + + dummy_read(drv_data); + set_dma_x_count(drv_data->tx_dma, word_count); + set_dma_x_count(drv_data->rx_dma, word_count); + set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr); + set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr); + dma_config |= DMAFLOW_STOP | RESTART | DI_EN; + set_dma_config(drv_data->tx_dma, dma_config); + set_dma_config(drv_data->rx_dma, dma_config | WNR); + enable_dma(drv_data->tx_dma); + enable_dma(drv_data->rx_dma); + + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RDR_NE, + &drv_data->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF, + &drv_data->regs->tx_control); + + return 0; +} + +static int adi_spi_pio_xfer(struct adi_spi_master *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + + if (!drv_data->rx) { + /* write only half duplex */ + drv_data->ops->write(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; + } else if (!drv_data->tx) { + /* read only half duplex */ + drv_data->ops->read(drv_data); + if (drv_data->rx != drv_data->rx_end) + return -EIO; + } else { + /* full duplex mode */ + drv_data->ops->duplex(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; + } + + if (!adi_spi_flush(drv_data)) + return -EIO; + msg->actual_length += drv_data->transfer_len; + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +static void adi_spi_pump_transfers(unsigned long data) +{ + struct adi_spi_master *drv_data = (struct adi_spi_master *)data; + struct spi_message *msg = NULL; + struct spi_transfer *t = NULL; + struct adi_spi_device *chip = NULL; + int ret; + + /* Get current state information */ + msg = drv_data->cur_msg; + t = drv_data->cur_transfer; + chip = drv_data->cur_chip; + + /* Handle for abort */ + if (drv_data->state == ERROR_STATE) { + msg->status = -EIO; + adi_spi_giveback(drv_data); + return; + } + + if (drv_data->state == RUNNING_STATE) { + if (t->delay_usecs) + udelay(t->delay_usecs); + if (t->cs_change) + adi_spi_cs_deactive(drv_data, chip); + adi_spi_next_transfer(drv_data); + t = drv_data->cur_transfer; + } + /* Handle end of message */ + if (drv_data->state == DONE_STATE) { + msg->status = 0; + adi_spi_giveback(drv_data); + return; + } + + if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) { + /* Schedule next transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + return; + } + + ret = adi_spi_setup_transfer(drv_data); + if (ret) { + msg->status = ret; + adi_spi_giveback(drv_data); + } + + iowrite32(0xFFFFFFFF, &drv_data->regs->status); + adi_spi_cs_active(drv_data, chip); + drv_data->state = RUNNING_STATE; + + if (chip->enable_dma) + ret = adi_spi_dma_xfer(drv_data); + else + ret = adi_spi_pio_xfer(drv_data); + if (ret) { + msg->status = ret; + adi_spi_giveback(drv_data); + } +} + +static int adi_spi_transfer_one_message(struct spi_master *master, + struct spi_message *m) +{ + struct adi_spi_master *drv_data = spi_master_get_devdata(master); + + drv_data->cur_msg = m; + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + adi_spi_restore_state(drv_data); + + drv_data->state = START_STATE; + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + struct spi_transfer, transfer_list); + + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +#define MAX_SPI_SSEL 7 + +static const u16 ssel[][MAX_SPI_SSEL] = { + {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3, + P_SPI0_SSEL4, P_SPI0_SSEL5, + P_SPI0_SSEL6, P_SPI0_SSEL7}, + + {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3, + P_SPI1_SSEL4, P_SPI1_SSEL5, + P_SPI1_SSEL6, P_SPI1_SSEL7}, + + {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3, + P_SPI2_SSEL4, P_SPI2_SSEL5, + P_SPI2_SSEL6, P_SPI2_SSEL7}, +}; + +static int adi_spi_setup(struct spi_device *spi) +{ + struct adi_spi_master *drv_data = spi_master_get_devdata(spi->master); + struct adi_spi_device *chip = spi_get_ctldata(spi); + u32 ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE; + int ret = -EINVAL; + + if (!chip) { + struct adi_spi3_chip *chip_info = spi->controller_data; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + dev_err(&spi->dev, "can not allocate chip data\n"); + return -ENOMEM; + } + if (chip_info) { + if (chip_info->control & ~ctl_reg) { + dev_err(&spi->dev, + "do not set bits that the SPI framework manages\n"); + goto error; + } + chip->control = chip_info->control; + chip->cs_chg_udelay = chip_info->cs_chg_udelay; + chip->tx_dummy_val = chip_info->tx_dummy_val; + chip->enable_dma = chip_info->enable_dma; + } + chip->cs = spi->chip_select; + + if (chip->cs < MAX_CTRL_CS) { + chip->ssel = (1 << chip->cs) << 8; + ret = peripheral_request(ssel[spi->master->bus_num] + [chip->cs-1], dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "peripheral_request() error\n"); + goto error; + } + } else { + chip->cs_gpio = chip->cs - MAX_CTRL_CS; + ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH, + dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "gpio_request_one() error\n"); + goto error; + } + } + spi_set_ctldata(spi, chip); + } + + /* force a default base state */ + chip->control &= ctl_reg; + + if (spi->mode & SPI_CPOL) + chip->control |= SPI_CTL_CPOL; + if (spi->mode & SPI_CPHA) + chip->control |= SPI_CTL_CPHA; + if (spi->mode & SPI_LSB_FIRST) + chip->control |= SPI_CTL_LSBF; + chip->control |= SPI_CTL_MSTR; + /* we choose software to controll cs */ + chip->control &= ~SPI_CTL_ASSEL; + + chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz); + + adi_spi_cs_enable(drv_data, chip); + adi_spi_cs_deactive(drv_data, chip); + + return 0; +error: + if (chip) { + kfree(chip); + spi_set_ctldata(spi, NULL); + } + + return ret; +} + +static void adi_spi_cleanup(struct spi_device *spi) +{ + struct adi_spi_device *chip = spi_get_ctldata(spi); + struct adi_spi_master *drv_data = spi_master_get_devdata(spi->master); + + if (!chip) + return; + + if (chip->cs < MAX_CTRL_CS) { + peripheral_free(ssel[spi->master->bus_num] + [chip->cs-1]); + adi_spi_cs_disable(drv_data, chip); + } else { + gpio_free(chip->cs_gpio); + } + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + +static irqreturn_t adi_spi_tx_dma_isr(int irq, void *dev_id) +{ + struct adi_spi_master *drv_data = dev_id; + u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma); + u32 tx_ctl; + + clear_dma_irqstat(drv_data->tx_dma); + if (dma_stat & DMA_DONE) { + drv_data->tx_num++; + } else { + dev_err(&drv_data->master->dev, + "spi tx dma error: %d\n", dma_stat); + if (drv_data->tx) + drv_data->state = ERROR_STATE; + } + tx_ctl = ioread32(&drv_data->regs->tx_control); + tx_ctl &= ~SPI_TXCTL_TDR_NF; + iowrite32(tx_ctl, &drv_data->regs->tx_control); + return IRQ_HANDLED; +} + +static irqreturn_t adi_spi_rx_dma_isr(int irq, void *dev_id) +{ + struct adi_spi_master *drv_data = dev_id; + struct spi_message *msg = drv_data->cur_msg; + u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma); + + clear_dma_irqstat(drv_data->rx_dma); + if (dma_stat & DMA_DONE) { + drv_data->rx_num++; + /* we may fail on tx dma */ + if (drv_data->state != ERROR_STATE) + msg->actual_length += drv_data->transfer_len; + } else { + drv_data->state = ERROR_STATE; + dev_err(&drv_data->master->dev, + "spi rx dma error: %d\n", dma_stat); + } + iowrite32(0, &drv_data->regs->tx_control); + iowrite32(0, &drv_data->regs->rx_control); + if (drv_data->rx_num != drv_data->tx_num) + dev_dbg(&drv_data->master->dev, + "dma interrupt missing: tx=%d,rx=%d\n", + drv_data->tx_num, drv_data->rx_num); + tasklet_schedule(&drv_data->pump_transfers); + return IRQ_HANDLED; +} + +static int adi_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_spi3_master *info = dev_get_platdata(dev); + struct spi_master *master; + struct adi_spi_master *drv_data; + struct resource *mem, *res; + unsigned int tx_dma, rx_dma; + unsigned long sclk; + int ret; + + if (!info) { + dev_err(dev, "platform data missing!\n"); + return -ENODEV; + } + + sclk = get_sclk1(); + if (!sclk) { + dev_err(dev, "can not get sclk1\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(dev, "can not get tx dma resource\n"); + return -ENXIO; + } + tx_dma = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(dev, "can not get rx dma resource\n"); + return -ENXIO; + } + rx_dma = res->start; + + /* allocate master with space for drv_data */ + master = spi_alloc_master(dev, sizeof(*drv_data)); + if (!master) { + dev_err(dev, "can not alloc spi_master\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, master); + + /* the mode bits supported by this driver */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + + master->bus_num = pdev->id; + master->num_chipselect = info->num_chipselect; + master->cleanup = adi_spi_cleanup; + master->setup = adi_spi_setup; + master->transfer_one_message = adi_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(8); + + drv_data = spi_master_get_devdata(master); + drv_data->master = master; + drv_data->tx_dma = tx_dma; + drv_data->rx_dma = rx_dma; + drv_data->pin_req = info->pin_req; + drv_data->sclk = sclk; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drv_data->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(drv_data->regs)) { + ret = PTR_ERR(drv_data->regs); + goto err_put_master; + } + + /* request tx and rx dma */ + ret = request_dma(tx_dma, "SPI_TX_DMA"); + if (ret) { + dev_err(dev, "can not request SPI TX DMA channel\n"); + goto err_put_master; + } + set_dma_callback(tx_dma, adi_spi_tx_dma_isr, drv_data); + + ret = request_dma(rx_dma, "SPI_RX_DMA"); + if (ret) { + dev_err(dev, "can not request SPI RX DMA channel\n"); + goto err_free_tx_dma; + } + set_dma_callback(drv_data->rx_dma, adi_spi_rx_dma_isr, drv_data); + + /* request CLK, MOSI and MISO */ + ret = peripheral_request_list(drv_data->pin_req, "adi-spi3"); + if (ret < 0) { + dev_err(dev, "can not request spi pins\n"); + goto err_free_rx_dma; + } + + iowrite32(SPI_CTL_MSTR | SPI_CTL_CPHA, &drv_data->regs->control); + iowrite32(0x0000FE00, &drv_data->regs->ssel); + iowrite32(0x0, &drv_data->regs->delay); + + tasklet_init(&drv_data->pump_transfers, + adi_spi_pump_transfers, (unsigned long)drv_data); + /* register with the SPI framework */ + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(dev, "can not register spi master\n"); + goto err_free_peripheral; + } + + return ret; + +err_free_peripheral: + peripheral_free_list(drv_data->pin_req); +err_free_rx_dma: + free_dma(rx_dma); +err_free_tx_dma: + free_dma(tx_dma); +err_put_master: + spi_master_put(master); + + return ret; +} + +static int adi_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct adi_spi_master *drv_data = spi_master_get_devdata(master); + + adi_spi_disable(drv_data); + peripheral_free_list(drv_data->pin_req); + free_dma(drv_data->rx_dma); + free_dma(drv_data->tx_dma); + return 0; +} + +#ifdef CONFIG_PM +static int adi_spi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct adi_spi_master *drv_data = spi_master_get_devdata(master); + + spi_master_suspend(master); + + drv_data->control = ioread32(&drv_data->regs->control); + drv_data->ssel = ioread32(&drv_data->regs->ssel); + + iowrite32(SPI_CTL_MSTR | SPI_CTL_CPHA, &drv_data->regs->control); + iowrite32(0x0000FE00, &drv_data->regs->ssel); + dma_disable_irq(drv_data->rx_dma); + dma_disable_irq(drv_data->tx_dma); + + return 0; +} + +static int adi_spi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct adi_spi_master *drv_data = spi_master_get_devdata(master); + int ret = 0; + + /* bootrom may modify spi and dma status when resume in spi boot mode */ + disable_dma(drv_data->rx_dma); + + dma_enable_irq(drv_data->rx_dma); + dma_enable_irq(drv_data->tx_dma); + iowrite32(drv_data->control, &drv_data->regs->control); + iowrite32(drv_data->ssel, &drv_data->regs->ssel); + + ret = spi_master_resume(master); + if (ret) { + free_dma(drv_data->rx_dma); + free_dma(drv_data->tx_dma); + } + + return ret; +} +#endif +static const struct dev_pm_ops adi_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(adi_spi_suspend, adi_spi_resume) +}; + +MODULE_ALIAS("platform:adi-spi3"); +static struct platform_driver adi_spi_driver = { + .driver = { + .name = "adi-spi3", + .owner = THIS_MODULE, + .pm = &adi_spi_pm_ops, + }, + .remove = adi_spi_remove, +}; + +module_platform_driver_probe(adi_spi_driver, adi_spi_probe); + +MODULE_DESCRIPTION("Analog Devices SPI3 controller driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-bfin-v3.c b/drivers/spi/spi-bfin-v3.c deleted file mode 100644 index 4089d0e0d84e..000000000000 --- a/drivers/spi/spi-bfin-v3.c +++ /dev/null @@ -1,965 +0,0 @@ -/* - * Analog Devices SPI3 controller driver - * - * Copyright (c) 2013 Analog Devices Inc. - * - * 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. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -enum bfin_spi_state { - START_STATE, - RUNNING_STATE, - DONE_STATE, - ERROR_STATE -}; - -struct bfin_spi_master; - -struct bfin_spi_transfer_ops { - void (*write) (struct bfin_spi_master *); - void (*read) (struct bfin_spi_master *); - void (*duplex) (struct bfin_spi_master *); -}; - -/* runtime info for spi master */ -struct bfin_spi_master { - /* SPI framework hookup */ - struct spi_master *master; - - /* Regs base of SPI controller */ - struct bfin_spi_regs __iomem *regs; - - /* Pin request list */ - u16 *pin_req; - - /* Message Transfer pump */ - struct tasklet_struct pump_transfers; - - /* Current message transfer state info */ - struct spi_message *cur_msg; - struct spi_transfer *cur_transfer; - struct bfin_spi_device *cur_chip; - unsigned transfer_len; - - /* transfer buffer */ - void *tx; - void *tx_end; - void *rx; - void *rx_end; - - /* dma info */ - unsigned int tx_dma; - unsigned int rx_dma; - dma_addr_t tx_dma_addr; - dma_addr_t rx_dma_addr; - unsigned long dummy_buffer; /* used in unidirectional transfer */ - unsigned long tx_dma_size; - unsigned long rx_dma_size; - int tx_num; - int rx_num; - - /* store register value for suspend/resume */ - u32 control; - u32 ssel; - - unsigned long sclk; - enum bfin_spi_state state; - - const struct bfin_spi_transfer_ops *ops; -}; - -struct bfin_spi_device { - u32 control; - u32 clock; - u32 ssel; - - u8 cs; - u16 cs_chg_udelay; /* Some devices require > 255usec delay */ - u32 cs_gpio; - u32 tx_dummy_val; /* tx value for rx only transfer */ - bool enable_dma; - const struct bfin_spi_transfer_ops *ops; -}; - -static void bfin_spi_enable(struct bfin_spi_master *drv_data) -{ - bfin_write_or(&drv_data->regs->control, SPI_CTL_EN); -} - -static void bfin_spi_disable(struct bfin_spi_master *drv_data) -{ - bfin_write_and(&drv_data->regs->control, ~SPI_CTL_EN); -} - -/* Caculate the SPI_CLOCK register value based on input HZ */ -static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz) -{ - u32 spi_clock = sclk / speed_hz; - - if (spi_clock) - spi_clock--; - return spi_clock; -} - -static int bfin_spi_flush(struct bfin_spi_master *drv_data) -{ - unsigned long limit = loops_per_jiffy << 1; - - /* wait for stop and clear stat */ - while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit) - cpu_relax(); - - bfin_write(&drv_data->regs->status, 0xFFFFFFFF); - - return limit; -} - -/* Chip select operation functions for cs_change flag */ -static void bfin_spi_cs_active(struct bfin_spi_master *drv_data, struct bfin_spi_device *chip) -{ - if (likely(chip->cs < MAX_CTRL_CS)) - bfin_write_and(&drv_data->regs->ssel, ~chip->ssel); - else - gpio_set_value(chip->cs_gpio, 0); -} - -static void bfin_spi_cs_deactive(struct bfin_spi_master *drv_data, - struct bfin_spi_device *chip) -{ - if (likely(chip->cs < MAX_CTRL_CS)) - bfin_write_or(&drv_data->regs->ssel, chip->ssel); - else - gpio_set_value(chip->cs_gpio, 1); - - /* Move delay here for consistency */ - if (chip->cs_chg_udelay) - udelay(chip->cs_chg_udelay); -} - -/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ -static inline void bfin_spi_cs_enable(struct bfin_spi_master *drv_data, - struct bfin_spi_device *chip) -{ - if (chip->cs < MAX_CTRL_CS) - bfin_write_or(&drv_data->regs->ssel, chip->ssel >> 8); -} - -static inline void bfin_spi_cs_disable(struct bfin_spi_master *drv_data, - struct bfin_spi_device *chip) -{ - if (chip->cs < MAX_CTRL_CS) - bfin_write_and(&drv_data->regs->ssel, ~(chip->ssel >> 8)); -} - -/* stop controller and re-config current chip*/ -static void bfin_spi_restore_state(struct bfin_spi_master *drv_data) -{ - struct bfin_spi_device *chip = drv_data->cur_chip; - - /* Clear status and disable clock */ - bfin_write(&drv_data->regs->status, 0xFFFFFFFF); - bfin_write(&drv_data->regs->rx_control, 0x0); - bfin_write(&drv_data->regs->tx_control, 0x0); - bfin_spi_disable(drv_data); - - SSYNC(); - - /* Load the registers */ - bfin_write(&drv_data->regs->control, chip->control); - bfin_write(&drv_data->regs->clock, chip->clock); - - bfin_spi_enable(drv_data); - drv_data->tx_num = drv_data->rx_num = 0; - /* we always choose tx transfer initiate */ - bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN); - bfin_write(&drv_data->regs->tx_control, - SPI_TXCTL_TEN | SPI_TXCTL_TTI); - bfin_spi_cs_active(drv_data, chip); -} - -/* discard invalid rx data and empty rfifo */ -static inline void dummy_read(struct bfin_spi_master *drv_data) -{ - while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)) - bfin_read(&drv_data->regs->rfifo); -} - -static void bfin_spi_u8_write(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->tx < drv_data->tx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++))); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - bfin_read(&drv_data->regs->rfifo); - } -} - -static void bfin_spi_u8_read(struct bfin_spi_master *drv_data) -{ - u32 tx_val = drv_data->cur_chip->tx_dummy_val; - - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, tx_val); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo); - } -} - -static void bfin_spi_u8_duplex(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++))); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo); - } -} - -static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = { - .write = bfin_spi_u8_write, - .read = bfin_spi_u8_read, - .duplex = bfin_spi_u8_duplex, -}; - -static void bfin_spi_u16_write(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->tx < drv_data->tx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx)); - drv_data->tx += 2; - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - bfin_read(&drv_data->regs->rfifo); - } -} - -static void bfin_spi_u16_read(struct bfin_spi_master *drv_data) -{ - u32 tx_val = drv_data->cur_chip->tx_dummy_val; - - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, tx_val); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); - drv_data->rx += 2; - } -} - -static void bfin_spi_u16_duplex(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx)); - drv_data->tx += 2; - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); - drv_data->rx += 2; - } -} - -static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = { - .write = bfin_spi_u16_write, - .read = bfin_spi_u16_read, - .duplex = bfin_spi_u16_duplex, -}; - -static void bfin_spi_u32_write(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->tx < drv_data->tx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx)); - drv_data->tx += 4; - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - bfin_read(&drv_data->regs->rfifo); - } -} - -static void bfin_spi_u32_read(struct bfin_spi_master *drv_data) -{ - u32 tx_val = drv_data->cur_chip->tx_dummy_val; - - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, tx_val); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); - drv_data->rx += 4; - } -} - -static void bfin_spi_u32_duplex(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx)); - drv_data->tx += 4; - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); - drv_data->rx += 4; - } -} - -static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u32 = { - .write = bfin_spi_u32_write, - .read = bfin_spi_u32_read, - .duplex = bfin_spi_u32_duplex, -}; - - -/* test if there is more transfer to be done */ -static void bfin_spi_next_transfer(struct bfin_spi_master *drv) -{ - struct spi_message *msg = drv->cur_msg; - struct spi_transfer *t = drv->cur_transfer; - - /* Move to next transfer */ - if (t->transfer_list.next != &msg->transfers) { - drv->cur_transfer = list_entry(t->transfer_list.next, - struct spi_transfer, transfer_list); - drv->state = RUNNING_STATE; - } else { - drv->state = DONE_STATE; - drv->cur_transfer = NULL; - } -} - -static void bfin_spi_giveback(struct bfin_spi_master *drv_data) -{ - struct bfin_spi_device *chip = drv_data->cur_chip; - - bfin_spi_cs_deactive(drv_data, chip); - spi_finalize_current_message(drv_data->master); -} - -static int bfin_spi_setup_transfer(struct bfin_spi_master *drv) -{ - struct spi_transfer *t = drv->cur_transfer; - u32 cr, cr_width; - - if (t->tx_buf) { - drv->tx = (void *)t->tx_buf; - drv->tx_end = drv->tx + t->len; - } else { - drv->tx = NULL; - } - - if (t->rx_buf) { - drv->rx = t->rx_buf; - drv->rx_end = drv->rx + t->len; - } else { - drv->rx = NULL; - } - - drv->transfer_len = t->len; - - /* bits per word setup */ - switch (t->bits_per_word) { - case 8: - cr_width = SPI_CTL_SIZE08; - drv->ops = &bfin_bfin_spi_transfer_ops_u8; - break; - case 16: - cr_width = SPI_CTL_SIZE16; - drv->ops = &bfin_bfin_spi_transfer_ops_u16; - break; - case 32: - cr_width = SPI_CTL_SIZE32; - drv->ops = &bfin_bfin_spi_transfer_ops_u32; - break; - default: - return -EINVAL; - } - cr = bfin_read(&drv->regs->control) & ~SPI_CTL_SIZE; - cr |= cr_width; - bfin_write(&drv->regs->control, cr); - - /* speed setup */ - bfin_write(&drv->regs->clock, - hz_to_spi_clock(drv->sclk, t->speed_hz)); - return 0; -} - -static int bfin_spi_dma_xfer(struct bfin_spi_master *drv_data) -{ - struct spi_transfer *t = drv_data->cur_transfer; - struct spi_message *msg = drv_data->cur_msg; - struct bfin_spi_device *chip = drv_data->cur_chip; - u32 dma_config; - unsigned long word_count, word_size; - void *tx_buf, *rx_buf; - - switch (t->bits_per_word) { - case 8: - dma_config = WDSIZE_8 | PSIZE_8; - word_count = drv_data->transfer_len; - word_size = 1; - break; - case 16: - dma_config = WDSIZE_16 | PSIZE_16; - word_count = drv_data->transfer_len / 2; - word_size = 2; - break; - default: - dma_config = WDSIZE_32 | PSIZE_32; - word_count = drv_data->transfer_len / 4; - word_size = 4; - break; - } - - if (!drv_data->rx) { - tx_buf = drv_data->tx; - rx_buf = &drv_data->dummy_buffer; - drv_data->tx_dma_size = drv_data->transfer_len; - drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer); - set_dma_x_modify(drv_data->tx_dma, word_size); - set_dma_x_modify(drv_data->rx_dma, 0); - } else if (!drv_data->tx) { - drv_data->dummy_buffer = chip->tx_dummy_val; - tx_buf = &drv_data->dummy_buffer; - rx_buf = drv_data->rx; - drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer); - drv_data->rx_dma_size = drv_data->transfer_len; - set_dma_x_modify(drv_data->tx_dma, 0); - set_dma_x_modify(drv_data->rx_dma, word_size); - } else { - tx_buf = drv_data->tx; - rx_buf = drv_data->rx; - drv_data->tx_dma_size = drv_data->rx_dma_size - = drv_data->transfer_len; - set_dma_x_modify(drv_data->tx_dma, word_size); - set_dma_x_modify(drv_data->rx_dma, word_size); - } - - drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev, - (void *)tx_buf, - drv_data->tx_dma_size, - DMA_TO_DEVICE); - if (dma_mapping_error(&msg->spi->dev, - drv_data->tx_dma_addr)) - return -ENOMEM; - - drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev, - (void *)rx_buf, - drv_data->rx_dma_size, - DMA_FROM_DEVICE); - if (dma_mapping_error(&msg->spi->dev, - drv_data->rx_dma_addr)) { - dma_unmap_single(&msg->spi->dev, - drv_data->tx_dma_addr, - drv_data->tx_dma_size, - DMA_TO_DEVICE); - return -ENOMEM; - } - - dummy_read(drv_data); - set_dma_x_count(drv_data->tx_dma, word_count); - set_dma_x_count(drv_data->rx_dma, word_count); - set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr); - set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr); - dma_config |= DMAFLOW_STOP | RESTART | DI_EN; - set_dma_config(drv_data->tx_dma, dma_config); - set_dma_config(drv_data->rx_dma, dma_config | WNR); - enable_dma(drv_data->tx_dma); - enable_dma(drv_data->rx_dma); - SSYNC(); - - bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN | SPI_RXCTL_RDR_NE); - SSYNC(); - bfin_write(&drv_data->regs->tx_control, - SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF); - - return 0; -} - -static int bfin_spi_pio_xfer(struct bfin_spi_master *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - - if (!drv_data->rx) { - /* write only half duplex */ - drv_data->ops->write(drv_data); - if (drv_data->tx != drv_data->tx_end) - return -EIO; - } else if (!drv_data->tx) { - /* read only half duplex */ - drv_data->ops->read(drv_data); - if (drv_data->rx != drv_data->rx_end) - return -EIO; - } else { - /* full duplex mode */ - drv_data->ops->duplex(drv_data); - if (drv_data->tx != drv_data->tx_end) - return -EIO; - } - - if (!bfin_spi_flush(drv_data)) - return -EIO; - msg->actual_length += drv_data->transfer_len; - tasklet_schedule(&drv_data->pump_transfers); - return 0; -} - -static void bfin_spi_pump_transfers(unsigned long data) -{ - struct bfin_spi_master *drv_data = (struct bfin_spi_master *)data; - struct spi_message *msg = NULL; - struct spi_transfer *t = NULL; - struct bfin_spi_device *chip = NULL; - int ret; - - /* Get current state information */ - msg = drv_data->cur_msg; - t = drv_data->cur_transfer; - chip = drv_data->cur_chip; - - /* Handle for abort */ - if (drv_data->state == ERROR_STATE) { - msg->status = -EIO; - bfin_spi_giveback(drv_data); - return; - } - - if (drv_data->state == RUNNING_STATE) { - if (t->delay_usecs) - udelay(t->delay_usecs); - if (t->cs_change) - bfin_spi_cs_deactive(drv_data, chip); - bfin_spi_next_transfer(drv_data); - t = drv_data->cur_transfer; - } - /* Handle end of message */ - if (drv_data->state == DONE_STATE) { - msg->status = 0; - bfin_spi_giveback(drv_data); - return; - } - - if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) { - /* Schedule next transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); - return; - } - - ret = bfin_spi_setup_transfer(drv_data); - if (ret) { - msg->status = ret; - bfin_spi_giveback(drv_data); - } - - bfin_write(&drv_data->regs->status, 0xFFFFFFFF); - bfin_spi_cs_active(drv_data, chip); - drv_data->state = RUNNING_STATE; - - if (chip->enable_dma) - ret = bfin_spi_dma_xfer(drv_data); - else - ret = bfin_spi_pio_xfer(drv_data); - if (ret) { - msg->status = ret; - bfin_spi_giveback(drv_data); - } -} - -static int bfin_spi_transfer_one_message(struct spi_master *master, - struct spi_message *m) -{ - struct bfin_spi_master *drv_data = spi_master_get_devdata(master); - - drv_data->cur_msg = m; - drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); - bfin_spi_restore_state(drv_data); - - drv_data->state = START_STATE; - drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, - struct spi_transfer, transfer_list); - - tasklet_schedule(&drv_data->pump_transfers); - return 0; -} - -#define MAX_SPI_SSEL 7 - -static const u16 ssel[][MAX_SPI_SSEL] = { - {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3, - P_SPI0_SSEL4, P_SPI0_SSEL5, - P_SPI0_SSEL6, P_SPI0_SSEL7}, - - {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3, - P_SPI1_SSEL4, P_SPI1_SSEL5, - P_SPI1_SSEL6, P_SPI1_SSEL7}, - - {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3, - P_SPI2_SSEL4, P_SPI2_SSEL5, - P_SPI2_SSEL6, P_SPI2_SSEL7}, -}; - -static int bfin_spi_setup(struct spi_device *spi) -{ - struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master); - struct bfin_spi_device *chip = spi_get_ctldata(spi); - u32 bfin_ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE; - int ret = -EINVAL; - - if (!chip) { - struct bfin_spi3_chip *chip_info = spi->controller_data; - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) { - dev_err(&spi->dev, "can not allocate chip data\n"); - return -ENOMEM; - } - if (chip_info) { - if (chip_info->control & ~bfin_ctl_reg) { - dev_err(&spi->dev, - "do not set bits that the SPI framework manages\n"); - goto error; - } - chip->control = chip_info->control; - chip->cs_chg_udelay = chip_info->cs_chg_udelay; - chip->tx_dummy_val = chip_info->tx_dummy_val; - chip->enable_dma = chip_info->enable_dma; - } - chip->cs = spi->chip_select; - if (chip->cs < MAX_CTRL_CS) { - chip->ssel = (1 << chip->cs) << 8; - ret = peripheral_request(ssel[spi->master->bus_num] - [chip->cs-1], dev_name(&spi->dev)); - if (ret) { - dev_err(&spi->dev, "peripheral_request() error\n"); - goto error; - } - } else { - chip->cs_gpio = chip->cs - MAX_CTRL_CS; - ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH, - dev_name(&spi->dev)); - if (ret) { - dev_err(&spi->dev, "gpio_request_one() error\n"); - goto error; - } - } - spi_set_ctldata(spi, chip); - } - - /* force a default base state */ - chip->control &= bfin_ctl_reg; - - if (spi->mode & SPI_CPOL) - chip->control |= SPI_CTL_CPOL; - if (spi->mode & SPI_CPHA) - chip->control |= SPI_CTL_CPHA; - if (spi->mode & SPI_LSB_FIRST) - chip->control |= SPI_CTL_LSBF; - chip->control |= SPI_CTL_MSTR; - /* we choose software to controll cs */ - chip->control &= ~SPI_CTL_ASSEL; - - chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz); - - bfin_spi_cs_enable(drv_data, chip); - bfin_spi_cs_deactive(drv_data, chip); - - return 0; -error: - if (chip) { - kfree(chip); - spi_set_ctldata(spi, NULL); - } - - return ret; -} - -static void bfin_spi_cleanup(struct spi_device *spi) -{ - struct bfin_spi_device *chip = spi_get_ctldata(spi); - struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master); - - if (!chip) - return; - - if (chip->cs < MAX_CTRL_CS) { - peripheral_free(ssel[spi->master->bus_num] - [chip->cs-1]); - bfin_spi_cs_disable(drv_data, chip); - } else { - gpio_free(chip->cs_gpio); - } - - kfree(chip); - spi_set_ctldata(spi, NULL); -} - -static irqreturn_t bfin_spi_tx_dma_isr(int irq, void *dev_id) -{ - struct bfin_spi_master *drv_data = dev_id; - u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma); - - clear_dma_irqstat(drv_data->tx_dma); - if (dma_stat & DMA_DONE) { - drv_data->tx_num++; - } else { - dev_err(&drv_data->master->dev, - "spi tx dma error: %d\n", dma_stat); - if (drv_data->tx) - drv_data->state = ERROR_STATE; - } - bfin_write_and(&drv_data->regs->tx_control, ~SPI_TXCTL_TDR_NF); - return IRQ_HANDLED; -} - -static irqreturn_t bfin_spi_rx_dma_isr(int irq, void *dev_id) -{ - struct bfin_spi_master *drv_data = dev_id; - struct spi_message *msg = drv_data->cur_msg; - u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma); - - clear_dma_irqstat(drv_data->rx_dma); - if (dma_stat & DMA_DONE) { - drv_data->rx_num++; - /* we may fail on tx dma */ - if (drv_data->state != ERROR_STATE) - msg->actual_length += drv_data->transfer_len; - } else { - drv_data->state = ERROR_STATE; - dev_err(&drv_data->master->dev, - "spi rx dma error: %d\n", dma_stat); - } - bfin_write(&drv_data->regs->tx_control, 0); - bfin_write(&drv_data->regs->rx_control, 0); - if (drv_data->rx_num != drv_data->tx_num) - dev_dbg(&drv_data->master->dev, - "dma interrupt missing: tx=%d,rx=%d\n", - drv_data->tx_num, drv_data->rx_num); - tasklet_schedule(&drv_data->pump_transfers); - return IRQ_HANDLED; -} - -static int bfin_spi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct bfin_spi3_master *info = dev_get_platdata(dev); - struct spi_master *master; - struct bfin_spi_master *drv_data; - struct resource *mem, *res; - unsigned int tx_dma, rx_dma; - unsigned long sclk; - int ret; - - if (!info) { - dev_err(dev, "platform data missing!\n"); - return -ENODEV; - } - - sclk = get_sclk1(); - if (!sclk) { - dev_err(dev, "can not get sclk1\n"); - return -ENXIO; - } - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(dev, "can not get tx dma resource\n"); - return -ENXIO; - } - tx_dma = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(dev, "can not get rx dma resource\n"); - return -ENXIO; - } - rx_dma = res->start; - - /* allocate master with space for drv_data */ - master = spi_alloc_master(dev, sizeof(*drv_data)); - if (!master) { - dev_err(dev, "can not alloc spi_master\n"); - return -ENOMEM; - } - platform_set_drvdata(pdev, master); - - /* the mode bits supported by this driver */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; - - master->bus_num = pdev->id; - master->num_chipselect = info->num_chipselect; - master->cleanup = bfin_spi_cleanup; - master->setup = bfin_spi_setup; - master->transfer_one_message = bfin_spi_transfer_one_message; - master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | - SPI_BPW_MASK(8); - - drv_data = spi_master_get_devdata(master); - drv_data->master = master; - drv_data->tx_dma = tx_dma; - drv_data->rx_dma = rx_dma; - drv_data->pin_req = info->pin_req; - drv_data->sclk = sclk; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - drv_data->regs = devm_ioremap_resource(dev, mem); - if (IS_ERR(drv_data->regs)) { - ret = PTR_ERR(drv_data->regs); - goto err_put_master; - } - - /* request tx and rx dma */ - ret = request_dma(tx_dma, "SPI_TX_DMA"); - if (ret) { - dev_err(dev, "can not request SPI TX DMA channel\n"); - goto err_put_master; - } - set_dma_callback(tx_dma, bfin_spi_tx_dma_isr, drv_data); - - ret = request_dma(rx_dma, "SPI_RX_DMA"); - if (ret) { - dev_err(dev, "can not request SPI RX DMA channel\n"); - goto err_free_tx_dma; - } - set_dma_callback(drv_data->rx_dma, bfin_spi_rx_dma_isr, drv_data); - - /* request CLK, MOSI and MISO */ - ret = peripheral_request_list(drv_data->pin_req, "bfin-spi3"); - if (ret < 0) { - dev_err(dev, "can not request spi pins\n"); - goto err_free_rx_dma; - } - - bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA); - bfin_write(&drv_data->regs->ssel, 0x0000FE00); - bfin_write(&drv_data->regs->delay, 0x0); - - tasklet_init(&drv_data->pump_transfers, - bfin_spi_pump_transfers, (unsigned long)drv_data); - /* register with the SPI framework */ - ret = devm_spi_register_master(dev, master); - if (ret) { - dev_err(dev, "can not register spi master\n"); - goto err_free_peripheral; - } - - return ret; - -err_free_peripheral: - peripheral_free_list(drv_data->pin_req); -err_free_rx_dma: - free_dma(rx_dma); -err_free_tx_dma: - free_dma(tx_dma); -err_put_master: - spi_master_put(master); - - return ret; -} - -static int bfin_spi_remove(struct platform_device *pdev) -{ - struct spi_master *master = platform_get_drvdata(pdev); - struct bfin_spi_master *drv_data = spi_master_get_devdata(master); - - bfin_spi_disable(drv_data); - - peripheral_free_list(drv_data->pin_req); - free_dma(drv_data->rx_dma); - free_dma(drv_data->tx_dma); - - return 0; -} - -#ifdef CONFIG_PM -static int bfin_spi_suspend(struct device *dev) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct bfin_spi_master *drv_data = spi_master_get_devdata(master); - - spi_master_suspend(master); - - drv_data->control = bfin_read(&drv_data->regs->control); - drv_data->ssel = bfin_read(&drv_data->regs->ssel); - - bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA); - bfin_write(&drv_data->regs->ssel, 0x0000FE00); - dma_disable_irq(drv_data->rx_dma); - dma_disable_irq(drv_data->tx_dma); - - return 0; -} - -static int bfin_spi_resume(struct device *dev) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct bfin_spi_master *drv_data = spi_master_get_devdata(master); - int ret = 0; - - /* bootrom may modify spi and dma status when resume in spi boot mode */ - disable_dma(drv_data->rx_dma); - - dma_enable_irq(drv_data->rx_dma); - dma_enable_irq(drv_data->tx_dma); - bfin_write(&drv_data->regs->control, drv_data->control); - bfin_write(&drv_data->regs->ssel, drv_data->ssel); - - ret = spi_master_resume(master); - if (ret) { - free_dma(drv_data->rx_dma); - free_dma(drv_data->tx_dma); - } - - return ret; -} -#endif -static const struct dev_pm_ops bfin_spi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(bfin_spi_suspend, bfin_spi_resume) -}; - -MODULE_ALIAS("platform:bfin-spi3"); -static struct platform_driver bfin_spi_driver = { - .driver = { - .name = "bfin-spi3", - .owner = THIS_MODULE, - .pm = &bfin_spi_pm_ops, - }, - .remove = bfin_spi_remove, -}; - -module_platform_driver_probe(bfin_spi_driver, bfin_spi_probe); - -MODULE_DESCRIPTION("Analog Devices SPI3 controller driver"); -MODULE_AUTHOR("Scott Jiang "); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi/adi_spi3.h b/include/linux/spi/adi_spi3.h new file mode 100644 index 000000000000..c84123aa1d06 --- /dev/null +++ b/include/linux/spi/adi_spi3.h @@ -0,0 +1,254 @@ +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2014 Analog Devices Inc. + * + * 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. + * + * 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. + */ + +#ifndef _ADI_SPI3_H_ +#define _ADI_SPI3_H_ + +#include + +/* SPI_CONTROL */ +#define SPI_CTL_EN 0x00000001 /* Enable */ +#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */ +#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */ +#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */ +#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */ +#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */ +#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */ +#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in-between transfers */ +#define SPI_CTL_EMISO 0x00000100 /* Enable MISO */ +#define SPI_CTL_SIZE 0x00000600 /* Word Transfer Size */ +#define SPI_CTL_SIZE08 0x00000000 /* SIZE: 8 bits */ +#define SPI_CTL_SIZE16 0x00000200 /* SIZE: 16 bits */ +#define SPI_CTL_SIZE32 0x00000400 /* SIZE: 32 bits */ +#define SPI_CTL_LSBF 0x00001000 /* LSB First */ +#define SPI_CTL_FCEN 0x00002000 /* Flow-Control Enable */ +#define SPI_CTL_FCCH 0x00004000 /* Flow-Control Channel Selection */ +#define SPI_CTL_FCPL 0x00008000 /* Flow-Control Polarity */ +#define SPI_CTL_FCWM 0x00030000 /* Flow-Control Water-Mark */ +#define SPI_CTL_FIFO0 0x00000000 /* FCWM: TFIFO empty or RFIFO Full */ +#define SPI_CTL_FIFO1 0x00010000 /* FCWM: TFIFO 75% or more empty or RFIFO 75% or more full */ +#define SPI_CTL_FIFO2 0x00020000 /* FCWM: TFIFO 50% or more empty or RFIFO 50% or more full */ +#define SPI_CTL_FMODE 0x00040000 /* Fast-mode Enable */ +#define SPI_CTL_MIOM 0x00300000 /* Multiple I/O Mode */ +#define SPI_CTL_MIO_DIS 0x00000000 /* MIOM: Disable */ +#define SPI_CTL_MIO_DUAL 0x00100000 /* MIOM: Enable DIOM (Dual I/O Mode) */ +#define SPI_CTL_MIO_QUAD 0x00200000 /* MIOM: Enable QUAD (Quad SPI Mode) */ +#define SPI_CTL_SOSI 0x00400000 /* Start on MOSI */ +/* SPI_RX_CONTROL */ +#define SPI_RXCTL_REN 0x00000001 /* Receive Channel Enable */ +#define SPI_RXCTL_RTI 0x00000004 /* Receive Transfer Initiate */ +#define SPI_RXCTL_RWCEN 0x00000008 /* Receive Word Counter Enable */ +#define SPI_RXCTL_RDR 0x00000070 /* Receive Data Request */ +#define SPI_RXCTL_RDR_DIS 0x00000000 /* RDR: Disabled */ +#define SPI_RXCTL_RDR_NE 0x00000010 /* RDR: RFIFO not empty */ +#define SPI_RXCTL_RDR_25 0x00000020 /* RDR: RFIFO 25% full */ +#define SPI_RXCTL_RDR_50 0x00000030 /* RDR: RFIFO 50% full */ +#define SPI_RXCTL_RDR_75 0x00000040 /* RDR: RFIFO 75% full */ +#define SPI_RXCTL_RDR_FULL 0x00000050 /* RDR: RFIFO full */ +#define SPI_RXCTL_RDO 0x00000100 /* Receive Data Over-Run */ +#define SPI_RXCTL_RRWM 0x00003000 /* FIFO Regular Water-Mark */ +#define SPI_RXCTL_RWM_0 0x00000000 /* RRWM: RFIFO Empty */ +#define SPI_RXCTL_RWM_25 0x00001000 /* RRWM: RFIFO 25% full */ +#define SPI_RXCTL_RWM_50 0x00002000 /* RRWM: RFIFO 50% full */ +#define SPI_RXCTL_RWM_75 0x00003000 /* RRWM: RFIFO 75% full */ +#define SPI_RXCTL_RUWM 0x00070000 /* FIFO Urgent Water-Mark */ +#define SPI_RXCTL_UWM_DIS 0x00000000 /* RUWM: Disabled */ +#define SPI_RXCTL_UWM_25 0x00010000 /* RUWM: RFIFO 25% full */ +#define SPI_RXCTL_UWM_50 0x00020000 /* RUWM: RFIFO 50% full */ +#define SPI_RXCTL_UWM_75 0x00030000 /* RUWM: RFIFO 75% full */ +#define SPI_RXCTL_UWM_FULL 0x00040000 /* RUWM: RFIFO full */ +/* SPI_TX_CONTROL */ +#define SPI_TXCTL_TEN 0x00000001 /* Transmit Channel Enable */ +#define SPI_TXCTL_TTI 0x00000004 /* Transmit Transfer Initiate */ +#define SPI_TXCTL_TWCEN 0x00000008 /* Transmit Word Counter Enable */ +#define SPI_TXCTL_TDR 0x00000070 /* Transmit Data Request */ +#define SPI_TXCTL_TDR_DIS 0x00000000 /* TDR: Disabled */ +#define SPI_TXCTL_TDR_NF 0x00000010 /* TDR: TFIFO not full */ +#define SPI_TXCTL_TDR_25 0x00000020 /* TDR: TFIFO 25% empty */ +#define SPI_TXCTL_TDR_50 0x00000030 /* TDR: TFIFO 50% empty */ +#define SPI_TXCTL_TDR_75 0x00000040 /* TDR: TFIFO 75% empty */ +#define SPI_TXCTL_TDR_EMPTY 0x00000050 /* TDR: TFIFO empty */ +#define SPI_TXCTL_TDU 0x00000100 /* Transmit Data Under-Run */ +#define SPI_TXCTL_TRWM 0x00003000 /* FIFO Regular Water-Mark */ +#define SPI_TXCTL_RWM_FULL 0x00000000 /* TRWM: TFIFO full */ +#define SPI_TXCTL_RWM_25 0x00001000 /* TRWM: TFIFO 25% empty */ +#define SPI_TXCTL_RWM_50 0x00002000 /* TRWM: TFIFO 50% empty */ +#define SPI_TXCTL_RWM_75 0x00003000 /* TRWM: TFIFO 75% empty */ +#define SPI_TXCTL_TUWM 0x00070000 /* FIFO Urgent Water-Mark */ +#define SPI_TXCTL_UWM_DIS 0x00000000 /* TUWM: Disabled */ +#define SPI_TXCTL_UWM_25 0x00010000 /* TUWM: TFIFO 25% empty */ +#define SPI_TXCTL_UWM_50 0x00020000 /* TUWM: TFIFO 50% empty */ +#define SPI_TXCTL_UWM_75 0x00030000 /* TUWM: TFIFO 75% empty */ +#define SPI_TXCTL_UWM_EMPTY 0x00040000 /* TUWM: TFIFO empty */ +/* SPI_CLOCK */ +#define SPI_CLK_BAUD 0x0000FFFF /* Baud Rate */ +/* SPI_DELAY */ +#define SPI_DLY_STOP 0x000000FF /* Transfer delay time in multiples of SCK period */ +#define SPI_DLY_LEADX 0x00000100 /* Extended (1 SCK) LEAD Control */ +#define SPI_DLY_LAGX 0x00000200 /* Extended (1 SCK) LAG control */ +/* SPI_SSEL */ +#define SPI_SLVSEL_SSE1 0x00000002 /* SPISSEL1 Enable */ +#define SPI_SLVSEL_SSE2 0x00000004 /* SPISSEL2 Enable */ +#define SPI_SLVSEL_SSE3 0x00000008 /* SPISSEL3 Enable */ +#define SPI_SLVSEL_SSE4 0x00000010 /* SPISSEL4 Enable */ +#define SPI_SLVSEL_SSE5 0x00000020 /* SPISSEL5 Enable */ +#define SPI_SLVSEL_SSE6 0x00000040 /* SPISSEL6 Enable */ +#define SPI_SLVSEL_SSE7 0x00000080 /* SPISSEL7 Enable */ +#define SPI_SLVSEL_SSEL1 0x00000200 /* SPISSEL1 Value */ +#define SPI_SLVSEL_SSEL2 0x00000400 /* SPISSEL2 Value */ +#define SPI_SLVSEL_SSEL3 0x00000800 /* SPISSEL3 Value */ +#define SPI_SLVSEL_SSEL4 0x00001000 /* SPISSEL4 Value */ +#define SPI_SLVSEL_SSEL5 0x00002000 /* SPISSEL5 Value */ +#define SPI_SLVSEL_SSEL6 0x00004000 /* SPISSEL6 Value */ +#define SPI_SLVSEL_SSEL7 0x00008000 /* SPISSEL7 Value */ +/* SPI_RWC */ +#define SPI_RWC_VALUE 0x0000FFFF /* Received Word-Count */ +/* SPI_RWCR */ +#define SPI_RWCR_VALUE 0x0000FFFF /* Received Word-Count Reload */ +/* SPI_TWC */ +#define SPI_TWC_VALUE 0x0000FFFF /* Transmitted Word-Count */ +/* SPI_TWCR */ +#define SPI_TWCR_VALUE 0x0000FFFF /* Transmitted Word-Count Reload */ +/* SPI_IMASK */ +#define SPI_IMSK_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_IMASKCL */ +#define SPI_IMSK_CLR_RUW 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_CLR_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_CLR_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_CLR_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_CLR_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_CLR_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_CLR_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_CLR_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_IMASKST */ +#define SPI_IMSK_SET_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_SET_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_SET_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_SET_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_SET_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_SET_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_SET_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_SET_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_STATUS */ +#define SPI_STAT_SPIF 0x00000001 /* SPI Finished */ +#define SPI_STAT_RUWM 0x00000002 /* Receive Urgent Water-Mark Breached */ +#define SPI_STAT_TUWM 0x00000004 /* Transmit Urgent Water-Mark Breached */ +#define SPI_STAT_ROE 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_STAT_TUE 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_STAT_TCE 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_STAT_MODF 0x00000080 /* Mode Fault Error Indication */ +#define SPI_STAT_RS 0x00000100 /* Receive Start Indication */ +#define SPI_STAT_TS 0x00000200 /* Transmit Start Indication */ +#define SPI_STAT_RF 0x00000400 /* Receive Finish Indication */ +#define SPI_STAT_TF 0x00000800 /* Transmit Finish Indication */ +#define SPI_STAT_RFS 0x00007000 /* SPI_RFIFO status */ +#define SPI_STAT_RFIFO_EMPTY 0x00000000 /* RFS: RFIFO Empty */ +#define SPI_STAT_RFIFO_25 0x00001000 /* RFS: RFIFO 25% Full */ +#define SPI_STAT_RFIFO_50 0x00002000 /* RFS: RFIFO 50% Full */ +#define SPI_STAT_RFIFO_75 0x00003000 /* RFS: RFIFO 75% Full */ +#define SPI_STAT_RFIFO_FULL 0x00004000 /* RFS: RFIFO Full */ +#define SPI_STAT_TFS 0x00070000 /* SPI_TFIFO status */ +#define SPI_STAT_TFIFO_FULL 0x00000000 /* TFS: TFIFO full */ +#define SPI_STAT_TFIFO_25 0x00010000 /* TFS: TFIFO 25% empty */ +#define SPI_STAT_TFIFO_50 0x00020000 /* TFS: TFIFO 50% empty */ +#define SPI_STAT_TFIFO_75 0x00030000 /* TFS: TFIFO 75% empty */ +#define SPI_STAT_TFIFO_EMPTY 0x00040000 /* TFS: TFIFO empty */ +#define SPI_STAT_FCS 0x00100000 /* Flow-Control Stall Indication */ +#define SPI_STAT_RFE 0x00400000 /* SPI_RFIFO Empty */ +#define SPI_STAT_TFF 0x00800000 /* SPI_TFIFO Full */ +/* SPI_ILAT */ +#define SPI_ILAT_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ +#define SPI_ILAT_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ +#define SPI_ILAT_ROI 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_ILAT_TUI 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_ILAT_TCI 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_ILAT_MFI 0x00000080 /* Mode Fault Error Indication */ +#define SPI_ILAT_RSI 0x00000100 /* Receive Start Indication */ +#define SPI_ILAT_TSI 0x00000200 /* Transmit Start Indication */ +#define SPI_ILAT_RFI 0x00000400 /* Receive Finish Indication */ +#define SPI_ILAT_TFI 0x00000800 /* Transmit Finish Indication */ +/* SPI_ILATCL */ +#define SPI_ILAT_CLR_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ +#define SPI_ILAT_CLR_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ +#define SPI_ILAT_CLR_ROI 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_ILAT_CLR_TUI 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_ILAT_CLR_TCI 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_ILAT_CLR_MFI 0x00000080 /* Mode Fault Error Indication */ +#define SPI_ILAT_CLR_RSI 0x00000100 /* Receive Start Indication */ +#define SPI_ILAT_CLR_TSI 0x00000200 /* Transmit Start Indication */ +#define SPI_ILAT_CLR_RFI 0x00000400 /* Receive Finish Indication */ +#define SPI_ILAT_CLR_TFI 0x00000800 /* Transmit Finish Indication */ + +/* + * adi spi3 registers layout + */ +struct adi_spi_regs { + u32 revid; + u32 control; + u32 rx_control; + u32 tx_control; + u32 clock; + u32 delay; + u32 ssel; + u32 rwc; + u32 rwcr; + u32 twc; + u32 twcr; + u32 reserved0; + u32 emask; + u32 emaskcl; + u32 emaskst; + u32 reserved1; + u32 status; + u32 elat; + u32 elatcl; + u32 reserved2; + u32 rfifo; + u32 reserved3; + u32 tfifo; +}; + +#define MAX_CTRL_CS 8 /* cs in spi controller */ + +/* device.platform_data for SSP controller devices */ +struct adi_spi3_master { + u16 num_chipselect; + u16 pin_req[7]; +}; + +/* spi_board_info.controller_data for SPI slave devices, + * copied to spi_device.platform_data ... mostly for dma tuning + */ +struct adi_spi3_chip { + u32 control; + u16 cs_chg_udelay; /* Some devices require 16-bit delays */ + u32 tx_dummy_val; /* tx value for rx only transfer */ + bool enable_dma; +}; + +#endif /* _ADI_SPI3_H_ */ -- cgit v1.2.3 From c34cc2487d5e6c225609696db3f49d40259787e2 Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Sat, 5 Apr 2014 11:29:09 +0800 Subject: spi: spi-adi-v3: convert to use common clk framework Use common clk api to get spi clock. Signed-off-by: Scott Jiang Signed-off-by: Mark Brown --- arch/blackfin/mach-bf609/clock.c | 7 +++++++ drivers/spi/spi-adi-v3.c | 13 +++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/arch/blackfin/mach-bf609/clock.c b/arch/blackfin/mach-bf609/clock.c index 56200f37cfc8..244fa4ab4c56 100644 --- a/arch/blackfin/mach-bf609/clock.c +++ b/arch/blackfin/mach-bf609/clock.c @@ -363,6 +363,12 @@ static struct clk ethclk = { .ops = &dummy_clk_ops, }; +static struct clk spiclk = { + .name = "spi", + .parent = &sclk1, + .ops = &dummy_clk_ops, +}; + static struct clk_lookup bf609_clks[] = { CLK(sys_clkin, NULL, "SYS_CLKIN"), CLK(pll_clk, NULL, "PLLCLK"), @@ -375,6 +381,7 @@ static struct clk_lookup bf609_clks[] = { CLK(dclk, NULL, "DCLK"), CLK(oclk, NULL, "OCLK"), CLK(ethclk, NULL, "stmmaceth"), + CLK(spiclk, NULL, "spi"), }; int __init clk_init(void) diff --git a/drivers/spi/spi-adi-v3.c b/drivers/spi/spi-adi-v3.c index 0c2914cfcdb5..dcb2287c7f8a 100644 --- a/drivers/spi/spi-adi-v3.c +++ b/drivers/spi/spi-adi-v3.c @@ -13,6 +13,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -800,7 +801,7 @@ static int adi_spi_probe(struct platform_device *pdev) struct adi_spi_master *drv_data; struct resource *mem, *res; unsigned int tx_dma, rx_dma; - unsigned long sclk; + struct clk *sclk; int ret; if (!info) { @@ -808,10 +809,10 @@ static int adi_spi_probe(struct platform_device *pdev) return -ENODEV; } - sclk = get_sclk1(); - if (!sclk) { - dev_err(dev, "can not get sclk1\n"); - return -ENXIO; + sclk = devm_clk_get(dev, "spi"); + if (IS_ERR(sclk)) { + dev_err(dev, "can not get spi clock\n"); + return PTR_ERR(sclk); } res = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -852,7 +853,7 @@ static int adi_spi_probe(struct platform_device *pdev) drv_data->tx_dma = tx_dma; drv_data->rx_dma = rx_dma; drv_data->pin_req = info->pin_req; - drv_data->sclk = sclk; + drv_data->sclk = clk_get_rate(sclk); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); drv_data->regs = devm_ioremap_resource(dev, mem); -- cgit v1.2.3 From c474b38665463d9017cd60cd3a8f8226cf8d562f Mon Sep 17 00:00:00 2001 From: Harini Katakam Date: Mon, 14 Apr 2014 14:36:53 +0530 Subject: spi: Add driver for Cadence SPI controller Add driver for Cadence SPI controller. This is used in Xilinx Zynq. Signed-off-by: Harini Katakam Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-cadence.c | 673 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 681 insertions(+) create mode 100644 drivers/spi/spi-cadence.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60f2b41c7310..5f11568660c6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -148,6 +148,13 @@ config SPI_BUTTERFLY inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_CADENCE + tristate "Cadence SPI controller" + depends on SPI_MASTER + help + This selects the Cadence SPI controller master driver + used by Xilinx Zynq. + config SPI_CLPS711X tristate "CLPS711X host SPI controller" depends on ARCH_CLPS711X || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bd792669e563..3e503d6d3df3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o +obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c new file mode 100644 index 000000000000..bb758978465d --- /dev/null +++ b/drivers/spi/spi-cadence.c @@ -0,0 +1,673 @@ +/* + * Cadence SPI controller driver (master mode only) + * + * Copyright (C) 2008 - 2014 Xilinx, Inc. + * + * based on Blackfin On-Chip SPI Driver (spi_bfin5xx.c) + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Name of this driver */ +#define CDNS_SPI_NAME "cdns-spi" + +/* Register offset definitions */ +#define CDNS_SPI_CR_OFFSET 0x00 /* Configuration Register, RW */ +#define CDNS_SPI_ISR_OFFSET 0x04 /* Interrupt Status Register, RO */ +#define CDNS_SPI_IER_OFFSET 0x08 /* Interrupt Enable Register, WO */ +#define CDNS_SPI_IDR_OFFSET 0x0c /* Interrupt Disable Register, WO */ +#define CDNS_SPI_IMR_OFFSET 0x10 /* Interrupt Enabled Mask Register, RO */ +#define CDNS_SPI_ER_OFFSET 0x14 /* Enable/Disable Register, RW */ +#define CDNS_SPI_DR_OFFSET 0x18 /* Delay Register, RW */ +#define CDNS_SPI_TXD_OFFSET 0x1C /* Data Transmit Register, WO */ +#define CDNS_SPI_RXD_OFFSET 0x20 /* Data Receive Register, RO */ +#define CDNS_SPI_SICR_OFFSET 0x24 /* Slave Idle Count Register, RW */ +#define CDNS_SPI_THLD_OFFSET 0x28 /* Transmit FIFO Watermark Register,RW */ + +/* + * SPI Configuration Register bit Masks + * + * This register contains various control bits that affect the operation + * of the SPI controller + */ +#define CDNS_SPI_CR_MANSTRT_MASK 0x00010000 /* Manual TX Start */ +#define CDNS_SPI_CR_CPHA_MASK 0x00000004 /* Clock Phase Control */ +#define CDNS_SPI_CR_CPOL_MASK 0x00000002 /* Clock Polarity Control */ +#define CDNS_SPI_CR_SSCTRL_MASK 0x00003C00 /* Slave Select Mask */ +#define CDNS_SPI_CR_BAUD_DIV_MASK 0x00000038 /* Baud Rate Divisor Mask */ +#define CDNS_SPI_CR_MSTREN_MASK 0x00000001 /* Master Enable Mask */ +#define CDNS_SPI_CR_MANSTRTEN_MASK 0x00008000 /* Manual TX Enable Mask */ +#define CDNS_SPI_CR_SSFORCE_MASK 0x00004000 /* Manual SS Enable Mask */ +#define CDNS_SPI_CR_BAUD_DIV_4_MASK 0x00000008 /* Default Baud Div Mask */ +#define CDNS_SPI_CR_DEFAULT_MASK (CDNS_SPI_CR_MSTREN_MASK | \ + CDNS_SPI_CR_SSCTRL_MASK | \ + CDNS_SPI_CR_SSFORCE_MASK | \ + CDNS_SPI_CR_BAUD_DIV_4_MASK) + +/* + * SPI Configuration Register - Baud rate and slave select + * + * These are the values used in the calculation of baud rate divisor and + * setting the slave select. + */ + +#define CDNS_SPI_BAUD_DIV_MAX 7 /* Baud rate divisor maximum */ +#define CDNS_SPI_BAUD_DIV_MIN 1 /* Baud rate divisor minimum */ +#define CDNS_SPI_BAUD_DIV_SHIFT 3 /* Baud rate divisor shift in CR */ +#define CDNS_SPI_SS_SHIFT 10 /* Slave Select field shift in CR */ +#define CDNS_SPI_SS0 0x1 /* Slave Select zero */ + +/* + * SPI Interrupt Registers bit Masks + * + * All the four interrupt registers (Status/Mask/Enable/Disable) have the same + * bit definitions. + */ +#define CDNS_SPI_IXR_TXOW_MASK 0x00000004 /* SPI TX FIFO Overwater */ +#define CDNS_SPI_IXR_MODF_MASK 0x00000002 /* SPI Mode Fault */ +#define CDNS_SPI_IXR_RXNEMTY_MASK 0x00000010 /* SPI RX FIFO Not Empty */ +#define CDNS_SPI_IXR_DEFAULT_MASK (CDNS_SPI_IXR_TXOW_MASK | \ + CDNS_SPI_IXR_MODF_MASK) +#define CDNS_SPI_IXR_TXFULL_MASK 0x00000008 /* SPI TX Full */ +#define CDNS_SPI_IXR_ALL_MASK 0x0000007F /* SPI all interrupts */ + +/* + * SPI Enable Register bit Masks + * + * This register is used to enable or disable the SPI controller + */ +#define CDNS_SPI_ER_ENABLE_MASK 0x00000001 /* SPI Enable Bit Mask */ +#define CDNS_SPI_ER_DISABLE_MASK 0x0 /* SPI Disable Bit Mask */ + +/* SPI FIFO depth in bytes */ +#define CDNS_SPI_FIFO_DEPTH 128 + +/* Default number of chip select lines */ +#define CDNS_SPI_DEFAULT_NUM_CS 4 + +/** + * struct cdns_spi - This definition defines spi driver instance + * @regs: Virtual address of the SPI controller registers + * @ref_clk: Pointer to the peripheral clock + * @pclk: Pointer to the APB clock + * @speed_hz: Current SPI bus clock speed in Hz + * @txbuf: Pointer to the TX buffer + * @rxbuf: Pointer to the RX buffer + * @tx_bytes: Number of bytes left to transfer + * @rx_bytes: Number of bytes requested + * @dev_busy: Device busy flag + * @is_decoded_cs: Flag for decoder property set or not + */ +struct cdns_spi { + void __iomem *regs; + struct clk *ref_clk; + struct clk *pclk; + u32 speed_hz; + const u8 *txbuf; + u8 *rxbuf; + int tx_bytes; + int rx_bytes; + u8 dev_busy; + u32 is_decoded_cs; +}; + +/* Macros for the SPI controller read/write */ +static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset) +{ + return readl_relaxed(xspi->regs + offset); +} + +static inline void cdns_spi_write(struct cdns_spi *xspi, u32 offset, u32 val) +{ + writel_relaxed(val, xspi->regs + offset); +} + +/** + * cdns_spi_init_hw - Initialize the hardware and configure the SPI controller + * @xspi: Pointer to the cdns_spi structure + * + * On reset the SPI controller is configured to be in master mode, baud rate + * divisor is set to 4, threshold value for TX FIFO not full interrupt is set + * to 1 and size of the word to be transferred as 8 bit. + * This function initializes the SPI controller to disable and clear all the + * interrupts, enable manual slave select and manual start, deselect all the + * chip select lines, and enable the SPI controller. + */ +static void cdns_spi_init_hw(struct cdns_spi *xspi) +{ + cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET, + CDNS_SPI_ER_DISABLE_MASK); + cdns_spi_write(xspi, CDNS_SPI_IDR_OFFSET, + CDNS_SPI_IXR_ALL_MASK); + + /* Clear the RX FIFO */ + while (cdns_spi_read(xspi, CDNS_SPI_ISR_OFFSET) & + CDNS_SPI_IXR_RXNEMTY_MASK) + cdns_spi_read(xspi, CDNS_SPI_RXD_OFFSET); + + cdns_spi_write(xspi, CDNS_SPI_ISR_OFFSET, + CDNS_SPI_IXR_ALL_MASK); + cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET, + CDNS_SPI_CR_DEFAULT_MASK); + cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET, + CDNS_SPI_ER_ENABLE_MASK); +} + +/** + * cdns_spi_chipselect - Select or deselect the chip select line + * @spi: Pointer to the spi_device structure + * @is_on: Select(0) or deselect (1) the chip select line + */ +static void cdns_spi_chipselect(struct spi_device *spi, bool is_high) +{ + struct cdns_spi *xspi = spi_master_get_devdata(spi->master); + u32 ctrl_reg; + + ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR_OFFSET); + + if (is_high) { + /* Deselect the slave */ + ctrl_reg |= CDNS_SPI_CR_SSCTRL_MASK; + } else { + /* Select the slave */ + ctrl_reg &= ~CDNS_SPI_CR_SSCTRL_MASK; + if (!(xspi->is_decoded_cs)) + ctrl_reg |= ((~(CDNS_SPI_SS0 << spi->chip_select)) << + CDNS_SPI_SS_SHIFT) & + CDNS_SPI_CR_SSCTRL_MASK; + else + ctrl_reg |= (spi->chip_select << CDNS_SPI_SS_SHIFT) & + CDNS_SPI_CR_SSCTRL_MASK; + } + + cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET, ctrl_reg); +} + +/** + * cdns_spi_config_clock_mode - Sets clock polarity and phase + * @spi: Pointer to the spi_device structure + * + * Sets the requested clock polarity and phase. + */ +static void cdns_spi_config_clock_mode(struct spi_device *spi) +{ + struct cdns_spi *xspi = spi_master_get_devdata(spi->master); + u32 ctrl_reg; + + ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR_OFFSET); + + /* Set the SPI clock phase and clock polarity */ + ctrl_reg &= ~(CDNS_SPI_CR_CPHA_MASK | CDNS_SPI_CR_CPOL_MASK); + if (spi->mode & SPI_CPHA) + ctrl_reg |= CDNS_SPI_CR_CPHA_MASK; + if (spi->mode & SPI_CPOL) + ctrl_reg |= CDNS_SPI_CR_CPOL_MASK; + + cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET, ctrl_reg); +} + +/** + * cdns_spi_config_clock_freq - Sets clock frequency + * @spi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provides + * information about next transfer setup parameters + * + * Sets the requested clock frequency. + * Note: If the requested frequency is not an exact match with what can be + * obtained using the prescalar value the driver sets the clock frequency which + * is lower than the requested frequency (maximum lower) for the transfer. If + * the requested frequency is higher or lower than that is supported by the SPI + * controller the driver will set the highest or lowest frequency supported by + * controller. + */ +static void cdns_spi_config_clock_freq(struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct cdns_spi *xspi = spi_master_get_devdata(spi->master); + u32 ctrl_reg, baud_rate_val; + unsigned long frequency; + + frequency = clk_get_rate(xspi->ref_clk); + + ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR_OFFSET); + + /* Set the clock frequency */ + if (xspi->speed_hz != transfer->speed_hz) { + /* first valid value is 1 */ + baud_rate_val = CDNS_SPI_BAUD_DIV_MIN; + while ((baud_rate_val < CDNS_SPI_BAUD_DIV_MAX) && + (frequency / (2 << baud_rate_val)) > transfer->speed_hz) + baud_rate_val++; + + ctrl_reg &= ~CDNS_SPI_CR_BAUD_DIV_MASK; + ctrl_reg |= baud_rate_val << CDNS_SPI_BAUD_DIV_SHIFT; + + xspi->speed_hz = frequency / (2 << baud_rate_val); + } + cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET, ctrl_reg); +} + +/** + * cdns_spi_setup_transfer - Configure SPI controller for specified transfer + * @spi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provides + * information about next transfer setup parameters + * + * Sets the operational mode of SPI controller for the next SPI transfer and + * sets the requested clock frequency. + * + * Return: Always 0 + */ +static int cdns_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct cdns_spi *xspi = spi_master_get_devdata(spi->master); + + cdns_spi_config_clock_freq(spi, transfer); + + dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u clock speed\n", + __func__, spi->mode, spi->bits_per_word, + xspi->speed_hz); + + return 0; +} + +/** + * cdns_spi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible + * @xspi: Pointer to the cdns_spi structure + */ +static void cdns_spi_fill_tx_fifo(struct cdns_spi *xspi) +{ + unsigned long trans_cnt = 0; + + while ((trans_cnt < CDNS_SPI_FIFO_DEPTH) && + (xspi->tx_bytes > 0)) { + if (xspi->txbuf) + cdns_spi_write(xspi, CDNS_SPI_TXD_OFFSET, + *xspi->txbuf++); + else + cdns_spi_write(xspi, CDNS_SPI_TXD_OFFSET, 0); + + xspi->tx_bytes--; + trans_cnt++; + } +} + +/** + * cdns_spi_irq - Interrupt service routine of the SPI controller + * @irq: IRQ number + * @dev_id: Pointer to the xspi structure + * + * This function handles TX empty and Mode Fault interrupts only. + * On TX empty interrupt this function reads the received data from RX FIFO and + * fills the TX FIFO if there is any data remaining to be transferred. + * On Mode Fault interrupt this function indicates that transfer is completed, + * the SPI subsystem will identify the error as the remaining bytes to be + * transferred is non-zero. + * + * Return: IRQ_HANDLED when handled; IRQ_NONE otherwise. + */ +static irqreturn_t cdns_spi_irq(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct cdns_spi *xspi = spi_master_get_devdata(master); + u32 intr_status, status; + + status = IRQ_NONE; + intr_status = cdns_spi_read(xspi, CDNS_SPI_ISR_OFFSET); + cdns_spi_write(xspi, CDNS_SPI_ISR_OFFSET, intr_status); + + if (intr_status & CDNS_SPI_IXR_MODF_MASK) { + /* Indicate that transfer is completed, the SPI subsystem will + * identify the error as the remaining bytes to be + * transferred is non-zero + */ + cdns_spi_write(xspi, CDNS_SPI_IDR_OFFSET, + CDNS_SPI_IXR_DEFAULT_MASK); + spi_finalize_current_transfer(master); + status = IRQ_HANDLED; + } else if (intr_status & CDNS_SPI_IXR_TXOW_MASK) { + unsigned long trans_cnt; + + trans_cnt = xspi->rx_bytes - xspi->tx_bytes; + + /* Read out the data from the RX FIFO */ + while (trans_cnt) { + u8 data; + + data = cdns_spi_read(xspi, CDNS_SPI_RXD_OFFSET); + if (xspi->rxbuf) + *xspi->rxbuf++ = data; + + xspi->rx_bytes--; + trans_cnt--; + } + + if (xspi->tx_bytes) { + /* There is more data to send */ + cdns_spi_fill_tx_fifo(xspi); + } else { + /* Transfer is completed */ + cdns_spi_write(xspi, CDNS_SPI_IDR_OFFSET, + CDNS_SPI_IXR_DEFAULT_MASK); + spi_finalize_current_transfer(master); + } + status = IRQ_HANDLED; + } + + return status; +} + +/** + * cdns_transfer_one - Initiates the SPI transfer + * @master: Pointer to spi_master structure + * @spi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provides + * information about next transfer parameters + * + * This function fills the TX FIFO, starts the SPI transfer and + * returns a positive transfer count so that core will wait for completion. + * + * Return: Number of bytes transferred in the last transfer + */ +static int cdns_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct cdns_spi *xspi = spi_master_get_devdata(master); + + xspi->txbuf = transfer->tx_buf; + xspi->rxbuf = transfer->rx_buf; + xspi->tx_bytes = transfer->len; + xspi->rx_bytes = transfer->len; + + cdns_spi_setup_transfer(spi, transfer); + + cdns_spi_fill_tx_fifo(xspi); + + cdns_spi_write(xspi, CDNS_SPI_IER_OFFSET, + CDNS_SPI_IXR_DEFAULT_MASK); + return transfer->len; +} + +/** + * cdns_prepare_transfer_hardware - Prepares hardware for transfer. + * @master: Pointer to the spi_master structure which provides + * information about the controller. + * + * This function enables SPI master controller. + * + * Return: 0 always + */ +static int cdns_prepare_transfer_hardware(struct spi_master *master) +{ + struct cdns_spi *xspi = spi_master_get_devdata(master); + + cdns_spi_config_clock_mode(master->cur_msg->spi); + + cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET, + CDNS_SPI_ER_ENABLE_MASK); + + return 0; +} + +/** + * cdns_unprepare_transfer_hardware - Relaxes hardware after transfer + * @master: Pointer to the spi_master structure which provides + * information about the controller. + * + * This function disables the SPI master controller. + * + * Return: 0 always + */ +static int cdns_unprepare_transfer_hardware(struct spi_master *master) +{ + struct cdns_spi *xspi = spi_master_get_devdata(master); + + cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET, + CDNS_SPI_ER_DISABLE_MASK); + + return 0; +} + +/** + * cdns_spi_probe - Probe method for the SPI driver + * @pdev: Pointer to the platform_device structure + * + * This function initializes the driver data structures and the hardware. + * + * Return: 0 on success and error value on error + */ +static int cdns_spi_probe(struct platform_device *pdev) +{ + int ret = 0, irq; + struct spi_master *master; + struct cdns_spi *xspi; + struct resource *res; + u32 num_cs; + + master = spi_alloc_master(&pdev->dev, sizeof(*xspi)); + if (master == NULL) + return -ENOMEM; + + xspi = spi_master_get_devdata(master); + master->dev.of_node = pdev->dev.of_node; + platform_set_drvdata(pdev, master); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xspi->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xspi->regs)) { + ret = PTR_ERR(xspi->regs); + goto remove_master; + } + + xspi->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(xspi->pclk)) { + dev_err(&pdev->dev, "pclk clock not found.\n"); + ret = PTR_ERR(xspi->pclk); + goto remove_master; + } + + xspi->ref_clk = devm_clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(xspi->ref_clk)) { + dev_err(&pdev->dev, "ref_clk clock not found.\n"); + ret = PTR_ERR(xspi->ref_clk); + goto remove_master; + } + + ret = clk_prepare_enable(xspi->pclk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable APB clock.\n"); + goto remove_master; + } + + ret = clk_prepare_enable(xspi->ref_clk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable device clock.\n"); + goto clk_dis_apb; + } + + /* SPI controller initializations */ + cdns_spi_init_hw(xspi); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + ret = -ENXIO; + dev_err(&pdev->dev, "irq number is invalid\n"); + goto remove_master; + } + + ret = devm_request_irq(&pdev->dev, irq, cdns_spi_irq, + 0, pdev->name, master); + if (ret != 0) { + ret = -ENXIO; + dev_err(&pdev->dev, "request_irq failed\n"); + goto remove_master; + } + + ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); + + if (ret < 0) + master->num_chipselect = CDNS_SPI_DEFAULT_NUM_CS; + else + master->num_chipselect = num_cs; + + ret = of_property_read_u32(pdev->dev.of_node, "is-decoded-cs", + &xspi->is_decoded_cs); + + if (ret < 0) + xspi->is_decoded_cs = 0; + + master->prepare_transfer_hardware = cdns_prepare_transfer_hardware; + master->transfer_one = cdns_transfer_one; + master->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware; + master->set_cs = cdns_spi_chipselect; + master->mode_bits = SPI_CPOL | SPI_CPHA; + + /* Set to default valid value */ + master->max_speed_hz = clk_get_rate(xspi->ref_clk) / 4; + xspi->speed_hz = master->max_speed_hz; + + master->bits_per_word_mask = SPI_BPW_MASK(8); + + ret = spi_register_master(master); + if (ret) { + dev_err(&pdev->dev, "spi_register_master failed\n"); + goto clk_dis_all; + } + + return ret; + +clk_dis_all: + clk_disable_unprepare(xspi->ref_clk); +clk_dis_apb: + clk_disable_unprepare(xspi->pclk); +remove_master: + spi_master_put(master); + return ret; +} + +/** + * cdns_spi_remove - Remove method for the SPI driver + * @pdev: Pointer to the platform_device structure + * + * This function is called if a device is physically removed from the system or + * if the driver module is being unloaded. It frees all resources allocated to + * the device. + * + * Return: 0 on success and error value on error + */ +static int cdns_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct cdns_spi *xspi = spi_master_get_devdata(master); + + cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET, + CDNS_SPI_ER_DISABLE_MASK); + + clk_disable_unprepare(xspi->ref_clk); + clk_disable_unprepare(xspi->pclk); + + spi_unregister_master(master); + + return 0; +} + +/** + * cdns_spi_suspend - Suspend method for the SPI driver + * @dev: Address of the platform_device structure + * + * This function disables the SPI controller and + * changes the driver state to "suspend" + * + * Return: Always 0 + */ +static int __maybe_unused cdns_spi_suspend(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct spi_master *master = platform_get_drvdata(pdev); + struct cdns_spi *xspi = spi_master_get_devdata(master); + + spi_master_suspend(master); + + clk_disable_unprepare(xspi->ref_clk); + + clk_disable_unprepare(xspi->pclk); + + return 0; +} + +/** + * cdns_spi_resume - Resume method for the SPI driver + * @dev: Address of the platform_device structure + * + * This function changes the driver state to "ready" + * + * Return: 0 on success and error value on error + */ +static int __maybe_unused cdns_spi_resume(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct spi_master *master = platform_get_drvdata(pdev); + struct cdns_spi *xspi = spi_master_get_devdata(master); + int ret = 0; + + ret = clk_prepare_enable(xspi->pclk); + if (ret) { + dev_err(dev, "Cannot enable APB clock.\n"); + return ret; + } + + ret = clk_prepare_enable(xspi->ref_clk); + if (ret) { + dev_err(dev, "Cannot enable device clock.\n"); + clk_disable(xspi->pclk); + return ret; + } + spi_master_resume(master); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cdns_spi_dev_pm_ops, cdns_spi_suspend, + cdns_spi_resume); + +static struct of_device_id cdns_spi_of_match[] = { + { .compatible = "xlnx,zynq-spi-r1p6" }, + { .compatible = "cdns,spi-r1p6" }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, cdns_spi_of_match); + +/* cdns_spi_driver - This structure defines the SPI subsystem platform driver */ +static struct platform_driver cdns_spi_driver = { + .probe = cdns_spi_probe, + .remove = cdns_spi_remove, + .driver = { + .name = CDNS_SPI_NAME, + .owner = THIS_MODULE, + .of_match_table = cdns_spi_of_match, + .pm = &cdns_spi_dev_pm_ops, + }, +}; + +module_platform_driver(cdns_spi_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Cadence SPI driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 205d52756f4091ffcf526f9d14853b18e3ca8d6f Mon Sep 17 00:00:00 2001 From: Harini Katakam Date: Mon, 14 Apr 2014 14:36:54 +0530 Subject: spi: Add devicetree bindings documentation for Cadence SPI Add spi-cadence bindings documentation. Signed-off-by: Harini Katakam Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-cadence.txt | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-cadence.txt diff --git a/Documentation/devicetree/bindings/spi/spi-cadence.txt b/Documentation/devicetree/bindings/spi/spi-cadence.txt new file mode 100644 index 000000000000..94f09141a4f0 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-cadence.txt @@ -0,0 +1,31 @@ +Cadence SPI controller Device Tree Bindings +------------------------------------------- + +Required properties: +- compatible : Should be "cdns,spi-r1p6" or "xlnx,zynq-spi-r1p6". +- reg : Physical base address and size of SPI registers map. +- interrupts : Property with a value describing the interrupt + number. +- interrupt-parent : Must be core interrupt controller +- clock-names : List of input clock names - "ref_clk", "pclk" + (See clock bindings for details). +- clocks : Clock phandles (see clock bindings for details). + +Optional properties: +- num-cs : Number of chip selects used. + If a decoder is used, this will be the number of + chip selects after the decoder. +- is-decoded-cs : Flag to indicate whether decoder is used or not. + +Example: + + spi@e0007000 { + compatible = "xlnx,zynq-spi-r1p6"; + clock-names = "ref_clk", "pclk"; + clocks = <&clkc 26>, <&clkc 35>; + interrupt-parent = <&intc>; + interrupts = <0 49 4>; + num-cs = <4>; + is-decoded-cs = <0>; + reg = <0xe0007000 0x1000>; + } ; -- cgit v1.2.3 From d77ec5df47696300b9498e6973dcc34b40de8d27 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Mon, 14 Apr 2014 14:30:00 +0800 Subject: spi: sirf: fix line over 80 characters style issue fix a lot of "line over 80 characters" checkpatch issues, on which the users of the driver, key customers care about this very much. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown --- drivers/spi/spi-sirf.c | 51 +++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 67d8909dcf39..3c12f396d96a 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -382,14 +382,16 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) if (IS_DMA_VALID(t)) { struct dma_async_tx_descriptor *rx_desc, *tx_desc; - sspi->dst_start = dma_map_single(&spi->dev, sspi->rx, t->len, DMA_FROM_DEVICE); + sspi->dst_start = dma_map_single(&spi->dev, + sspi->rx, t->len, DMA_FROM_DEVICE); rx_desc = dmaengine_prep_slave_single(sspi->rx_chan, sspi->dst_start, t->len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); rx_desc->callback = spi_sirfsoc_dma_fini_callback; rx_desc->callback_param = &sspi->rx_done; - sspi->src_start = dma_map_single(&spi->dev, (void *)sspi->tx, t->len, DMA_TO_DEVICE); + sspi->src_start = dma_map_single(&spi->dev, + (void *)sspi->tx, t->len, DMA_TO_DEVICE); tx_desc = dmaengine_prep_slave_single(sspi->tx_chan, sspi->src_start, t->len, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -404,13 +406,18 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) /* Send the first word to trigger the whole tx/rx process */ sspi->tx_word(sspi); - writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN | - SIRFSOC_SPI_RXFIFO_THD_INT_EN | SIRFSOC_SPI_TXFIFO_THD_INT_EN | - SIRFSOC_SPI_FRM_END_INT_EN | SIRFSOC_SPI_RXFIFO_FULL_INT_EN | - SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | + SIRFSOC_SPI_TX_UFLOW_INT_EN | + SIRFSOC_SPI_RXFIFO_THD_INT_EN | + SIRFSOC_SPI_TXFIFO_THD_INT_EN | + SIRFSOC_SPI_FRM_END_INT_EN | + SIRFSOC_SPI_RXFIFO_FULL_INT_EN | + SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, + sspi->base + SIRFSOC_SPI_INT_EN); } - writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, sspi->base + SIRFSOC_SPI_TX_RX_EN); + writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, + sspi->base + SIRFSOC_SPI_TX_RX_EN); if (!IS_DMA_VALID(t)) { /* for PIO */ if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) @@ -434,8 +441,10 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) } if (IS_DMA_VALID(t)) { - dma_unmap_single(&spi->dev, sspi->src_start, t->len, DMA_TO_DEVICE); - dma_unmap_single(&spi->dev, sspi->dst_start, t->len, DMA_FROM_DEVICE); + dma_unmap_single(&spi->dev, + sspi->src_start, t->len, DMA_TO_DEVICE); + dma_unmap_single(&spi->dev, + sspi->dst_start, t->len, DMA_FROM_DEVICE); } /* TX, RX FIFO stop */ @@ -512,7 +521,8 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) break; case 12: case 16: - regval |= (bits_per_word == 12) ? SIRFSOC_SPI_TRAN_DAT_FORMAT_12 : + regval |= (bits_per_word == 12) ? + SIRFSOC_SPI_TRAN_DAT_FORMAT_12 : SIRFSOC_SPI_TRAN_DAT_FORMAT_16; sspi->rx_word = spi_sirfsoc_rx_word_u16; sspi->tx_word = spi_sirfsoc_tx_word_u16; @@ -540,8 +550,8 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) regval |= SIRFSOC_SPI_CLK_IDLE_STAT; /* - * Data should be driven at least 1/2 cycle before the fetch edge to make - * sure that data gets stable at the fetch edge. + * Data should be driven at least 1/2 cycle before the fetch edge + * to make sure that data gets stable at the fetch edge. */ if (((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) || (!(spi->mode & SPI_CPOL) && !(spi->mode & SPI_CPHA))) @@ -578,11 +588,14 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) if (IS_DMA_VALID(t)) { /* Enable DMA mode for RX, TX */ writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL); - writel(SIRFSOC_SPI_RX_DMA_FLUSH, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); + writel(SIRFSOC_SPI_RX_DMA_FLUSH, + sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); } else { /* Enable IO mode for RX, TX */ - writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL); - writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); + writel(SIRFSOC_SPI_IO_MODE_SEL, + sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL); + writel(SIRFSOC_SPI_IO_MODE_SEL, + sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); } return 0; @@ -612,7 +625,8 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) goto err_cs; } - master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs); + master = spi_alloc_master(&pdev->dev, + sizeof(*sspi) + sizeof(int) * num_cs); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI master\n"); return -ENOMEM; @@ -808,8 +822,7 @@ static struct platform_driver spi_sirfsoc_driver = { .remove = spi_sirfsoc_remove, }; module_platform_driver(spi_sirfsoc_driver); - MODULE_DESCRIPTION("SiRF SoC SPI master driver"); -MODULE_AUTHOR("Zhiwu Song , " - "Barry Song "); +MODULE_AUTHOR("Zhiwu Song "); +MODULE_AUTHOR("Barry Song "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bf83fd6402a856eeb9a22c364c50ccf9bbdf9b17 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Mon, 14 Apr 2014 14:30:01 +0800 Subject: spi: sirf: fix spi full-duplex DMA transferring issue sometimes t->tx can be equal with t->rx. for example, spidev will make tx and rx point to spidev->buffer at the same time. currently, for this case, we map the buffer BIDIRECTION to fix the cache consistency. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown --- drivers/spi/spi-sirf.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 3c12f396d96a..0c039d4542a5 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -383,7 +383,8 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) struct dma_async_tx_descriptor *rx_desc, *tx_desc; sspi->dst_start = dma_map_single(&spi->dev, - sspi->rx, t->len, DMA_FROM_DEVICE); + sspi->rx, t->len, (t->tx_buf != t->rx_buf) ? + DMA_FROM_DEVICE : DMA_BIDIRECTIONAL); rx_desc = dmaengine_prep_slave_single(sspi->rx_chan, sspi->dst_start, t->len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -391,7 +392,9 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) rx_desc->callback_param = &sspi->rx_done; sspi->src_start = dma_map_single(&spi->dev, - (void *)sspi->tx, t->len, DMA_TO_DEVICE); + (void *)sspi->tx, t->len, + (t->tx_buf != t->rx_buf) ? + DMA_TO_DEVICE : DMA_BIDIRECTIONAL); tx_desc = dmaengine_prep_slave_single(sspi->tx_chan, sspi->src_start, t->len, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -- cgit v1.2.3 From 25dbe04a24516cdfd6bd215c1caaf28916b91a82 Mon Sep 17 00:00:00 2001 From: Harini Katakam Date: Tue, 15 Apr 2014 16:42:09 +0530 Subject: spi: cadence: Add dependency on ARM Add dependency on ARM in Kconfig. This is to fix the build error related to _relaxed IO. Remove dependency on SPI_MASTER because this is already defined under if SPI_MASTER in Kconfig. Signed-off-by: Harini Katakam Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5f11568660c6..32b1d0f1416c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -150,7 +150,7 @@ config SPI_BUTTERFLY config SPI_CADENCE tristate "Cadence SPI controller" - depends on SPI_MASTER + depends on ARM help This selects the Cadence SPI controller master driver used by Xilinx Zynq. -- cgit v1.2.3 From c908ef345d2c314ec25cfac113a8f9bb2b6b3a25 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Tue, 15 Apr 2014 15:24:59 +0800 Subject: spi: sirf: refactor spi transfer functions split sirfsoc_spi_transfer function into 3 sub-functions: spi_sirfsoc_cmd_transfer, spi_sirfsoc_pio_transfer and spi_sirfsoc_dma_transfer. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown --- drivers/spi/spi-sirf.c | 232 ++++++++++++++++++++++++++----------------------- 1 file changed, 123 insertions(+), 109 deletions(-) diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 0c039d4542a5..2d238990115c 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -309,59 +310,51 @@ static void spi_sirfsoc_dma_fini_callback(void *data) complete(dma_complete); } -static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) +static int spi_sirfsoc_cmd_transfer(struct spi_device *spi, + struct spi_transfer *t) { struct sirfsoc_spi *sspi; int timeout = t->len * 10; - sspi = spi_master_get_devdata(spi->master); + u32 cmd; - sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage; - sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage; - sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width; - reinit_completion(&sspi->rx_done); - reinit_completion(&sspi->tx_done); - - writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS); - - /* - * fill tx_buf into command register and wait for its completion - */ - if (sspi->tx_by_cmd) { - u32 cmd; - memcpy(&cmd, sspi->tx, t->len); - - if (sspi->word_width == 1 && !(spi->mode & SPI_LSB_FIRST)) - cmd = cpu_to_be32(cmd) >> - ((SIRFSOC_MAX_CMD_BYTES - t->len) * 8); - if (sspi->word_width == 2 && t->len == 4 && - (!(spi->mode & SPI_LSB_FIRST))) - cmd = ((cmd & 0xffff) << 16) | (cmd >> 16); - - writel(cmd, sspi->base + SIRFSOC_SPI_CMD); - writel(SIRFSOC_SPI_FRM_END_INT_EN, - sspi->base + SIRFSOC_SPI_INT_EN); - writel(SIRFSOC_SPI_CMD_TX_EN, - sspi->base + SIRFSOC_SPI_TX_RX_EN); + sspi = spi_master_get_devdata(spi->master); + memcpy(&cmd, sspi->tx, t->len); + if (sspi->word_width == 1 && !(spi->mode & SPI_LSB_FIRST)) + cmd = cpu_to_be32(cmd) >> + ((SIRFSOC_MAX_CMD_BYTES - t->len) * 8); + if (sspi->word_width == 2 && t->len == 4 && + (!(spi->mode & SPI_LSB_FIRST))) + cmd = ((cmd & 0xffff) << 16) | (cmd >> 16); + writel(cmd, sspi->base + SIRFSOC_SPI_CMD); + writel(SIRFSOC_SPI_FRM_END_INT_EN, + sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_CMD_TX_EN, + sspi->base + SIRFSOC_SPI_TX_RX_EN); + if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) { + dev_err(&spi->dev, "cmd transfer timeout\n"); + return 0; + } - if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) { - dev_err(&spi->dev, "transfer timeout\n"); - return 0; - } + return t->len; +} - return t->len; - } +static void spi_sirfsoc_dma_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct sirfsoc_spi *sspi; + struct dma_async_tx_descriptor *rx_desc, *tx_desc; + int timeout = t->len * 10; - if (sspi->left_tx_word == 1) { - writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | - SIRFSOC_SPI_ENA_AUTO_CLR, - sspi->base + SIRFSOC_SPI_CTRL); - writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); - writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); - } else if ((sspi->left_tx_word > 1) && (sspi->left_tx_word < - SIRFSOC_SPI_DAT_FRM_LEN_MAX)) { + sspi = spi_master_get_devdata(spi->master); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); + writel(0, sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS); + if (sspi->left_tx_word < SIRFSOC_SPI_DAT_FRM_LEN_MAX) { writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | - SIRFSOC_SPI_MUL_DAT_MODE | - SIRFSOC_SPI_ENA_AUTO_CLR, + SIRFSOC_SPI_ENA_AUTO_CLR | SIRFSOC_SPI_MUL_DAT_MODE, sspi->base + SIRFSOC_SPI_CTRL); writel(sspi->left_tx_word - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); @@ -373,88 +366,109 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); } - - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - - if (IS_DMA_VALID(t)) { - struct dma_async_tx_descriptor *rx_desc, *tx_desc; - - sspi->dst_start = dma_map_single(&spi->dev, - sspi->rx, t->len, (t->tx_buf != t->rx_buf) ? - DMA_FROM_DEVICE : DMA_BIDIRECTIONAL); - rx_desc = dmaengine_prep_slave_single(sspi->rx_chan, - sspi->dst_start, t->len, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - rx_desc->callback = spi_sirfsoc_dma_fini_callback; - rx_desc->callback_param = &sspi->rx_done; - - sspi->src_start = dma_map_single(&spi->dev, - (void *)sspi->tx, t->len, - (t->tx_buf != t->rx_buf) ? - DMA_TO_DEVICE : DMA_BIDIRECTIONAL); - tx_desc = dmaengine_prep_slave_single(sspi->tx_chan, - sspi->src_start, t->len, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - tx_desc->callback = spi_sirfsoc_dma_fini_callback; - tx_desc->callback_param = &sspi->tx_done; - - dmaengine_submit(tx_desc); - dmaengine_submit(rx_desc); - dma_async_issue_pending(sspi->tx_chan); - dma_async_issue_pending(sspi->rx_chan); - } else { - /* Send the first word to trigger the whole tx/rx process */ - sspi->tx_word(sspi); - - writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | - SIRFSOC_SPI_TX_UFLOW_INT_EN | - SIRFSOC_SPI_RXFIFO_THD_INT_EN | - SIRFSOC_SPI_TXFIFO_THD_INT_EN | - SIRFSOC_SPI_FRM_END_INT_EN | - SIRFSOC_SPI_RXFIFO_FULL_INT_EN | - SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, - sspi->base + SIRFSOC_SPI_INT_EN); - } - + sspi->dst_start = dma_map_single(&spi->dev, sspi->rx, t->len, + (t->tx_buf != t->rx_buf) ? + DMA_FROM_DEVICE : DMA_BIDIRECTIONAL); + rx_desc = dmaengine_prep_slave_single(sspi->rx_chan, + sspi->dst_start, t->len, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + rx_desc->callback = spi_sirfsoc_dma_fini_callback; + rx_desc->callback_param = &sspi->rx_done; + + sspi->src_start = dma_map_single(&spi->dev, (void *)sspi->tx, t->len, + (t->tx_buf != t->rx_buf) ? + DMA_TO_DEVICE : DMA_BIDIRECTIONAL); + tx_desc = dmaengine_prep_slave_single(sspi->tx_chan, + sspi->src_start, t->len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx_desc->callback = spi_sirfsoc_dma_fini_callback; + tx_desc->callback_param = &sspi->tx_done; + + dmaengine_submit(tx_desc); + dmaengine_submit(rx_desc); + dma_async_issue_pending(sspi->tx_chan); + dma_async_issue_pending(sspi->rx_chan); writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, sspi->base + SIRFSOC_SPI_TX_RX_EN); - - if (!IS_DMA_VALID(t)) { /* for PIO */ - if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) - dev_err(&spi->dev, "transfer timeout\n"); - } else if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) { + if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) { dev_err(&spi->dev, "transfer timeout\n"); dmaengine_terminate_all(sspi->rx_chan); } else sspi->left_rx_word = 0; - /* * we only wait tx-done event if transferring by DMA. for PIO, * we get rx data by writing tx data, so if rx is done, tx has * done earlier */ - if (IS_DMA_VALID(t)) { - if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) { - dev_err(&spi->dev, "transfer timeout\n"); - dmaengine_terminate_all(sspi->tx_chan); - } + if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) { + dev_err(&spi->dev, "transfer timeout\n"); + dmaengine_terminate_all(sspi->tx_chan); } + dma_unmap_single(&spi->dev, sspi->src_start, t->len, DMA_TO_DEVICE); + dma_unmap_single(&spi->dev, sspi->dst_start, t->len, DMA_FROM_DEVICE); + /* TX, RX FIFO stop */ + writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP); + writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP); + if (sspi->left_tx_word >= SIRFSOC_SPI_DAT_FRM_LEN_MAX) + writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN); +} - if (IS_DMA_VALID(t)) { - dma_unmap_single(&spi->dev, - sspi->src_start, t->len, DMA_TO_DEVICE); - dma_unmap_single(&spi->dev, - sspi->dst_start, t->len, DMA_FROM_DEVICE); - } +static void spi_sirfsoc_pio_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct sirfsoc_spi *sspi; + int timeout = t->len * 10; - /* TX, RX FIFO stop */ + sspi = spi_master_get_devdata(spi->master); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); + writel(0, sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS); + writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | SIRFSOC_SPI_MUL_DAT_MODE | + SIRFSOC_SPI_ENA_AUTO_CLR, sspi->base + SIRFSOC_SPI_CTRL); + writel(sspi->left_tx_word - 1, + sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); + writel(sspi->left_rx_word - 1, + sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); + sspi->tx_word(sspi); + writel(SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN | + SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_RXFIFO_THD_INT_EN | + SIRFSOC_SPI_TXFIFO_THD_INT_EN | SIRFSOC_SPI_FRM_END_INT_EN| + SIRFSOC_SPI_RXFIFO_FULL_INT_EN, + sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, + sspi->base + SIRFSOC_SPI_TX_RX_EN); + if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) + dev_err(&spi->dev, "transfer timeout\n"); writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP); writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP); writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN); writel(0, sspi->base + SIRFSOC_SPI_INT_EN); +} + +static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct sirfsoc_spi *sspi; + sspi = spi_master_get_devdata(spi->master); + + sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage; + sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage; + sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width; + reinit_completion(&sspi->rx_done); + reinit_completion(&sspi->tx_done); + /* + * in the transfer, if transfer data using command register with rx_buf + * null, just fill command data into command register and wait for its + * completion. + */ + if (sspi->tx_by_cmd) + spi_sirfsoc_cmd_transfer(spi, t); + else if (IS_DMA_VALID(t)) + spi_sirfsoc_dma_transfer(spi, t); + else + spi_sirfsoc_pio_transfer(spi, t); return t->len - sspi->left_rx_word * sspi->word_width; } -- cgit v1.2.3 From d0fb47a5237d8b9576113568bacfd27892308b62 Mon Sep 17 00:00:00 2001 From: Jane Wan Date: Wed, 16 Apr 2014 13:09:39 -0700 Subject: spi: fsl-espi: Configure FSL eSPI CSBEF and CSAFT Make FSL eSPI CSnBEF and CSnAFT fields in ESPI_SPMODEn registers (n=0,1,2,3) configurable through device tree. CSnBEF is the chip select setup time. It's the delay in bits from the activation of chip select pin to the first clock for data frame. CSnAFT is the chip select hold time. It's the delay in bits from the last clock for data frame to the deactivation of chip select pin. The FSL eSPI driver hardcodes CSnBEF and CSnAFT to 0. Need to set them to a different value for some device. Signed-off-by: Jane Wan Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/fsl-spi.txt | 6 ++++ drivers/spi/spi-fsl-espi.c | 34 ++++++++++++++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/fsl-spi.txt b/Documentation/devicetree/bindings/spi/fsl-spi.txt index b032dd76e9d2..a2331372068c 100644 --- a/Documentation/devicetree/bindings/spi/fsl-spi.txt +++ b/Documentation/devicetree/bindings/spi/fsl-spi.txt @@ -42,6 +42,10 @@ Required properties: - interrupts : should contain eSPI interrupt, the device has one interrupt. - fsl,espi-num-chipselects : the number of the chipselect signals. +Optional properties: +- fsl,csbef: chip select assertion time in bits before frame starts +- fsl,csaft: chip select negation time in bits after frame ends + Example: spi@110000 { #address-cells = <1>; @@ -51,4 +55,6 @@ Example: interrupts = <53 0x2>; interrupt-parent = <&mpic>; fsl,espi-num-chipselects = <4>; + fsl,csbef = <1>; + fsl,csaft = <1>; }; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index e767f5831b9c..24096c84e9c4 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -586,8 +586,10 @@ static struct spi_master * fsl_espi_probe(struct device *dev, struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; struct fsl_espi_reg *reg_base; - u32 regval; - int i, ret = 0; + struct device_node *nc; + const __be32 *prop; + u32 regval, csmode; + int i, len, ret = 0; master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); if (!master) { @@ -634,8 +636,32 @@ static struct spi_master * fsl_espi_probe(struct device *dev, mpc8xxx_spi_write_reg(®_base->event, 0xffffffff); /* Init eSPI CS mode register */ - for (i = 0; i < pdata->max_chipselect; i++) - mpc8xxx_spi_write_reg(®_base->csmode[i], CSMODE_INIT_VAL); + for_each_available_child_of_node(master->dev.of_node, nc) { + /* get chip select */ + prop = of_get_property(nc, "reg", &len); + if (!prop || len < sizeof(*prop)) + continue; + i = be32_to_cpup(prop); + if (i < 0 || i >= pdata->max_chipselect) + continue; + + csmode = CSMODE_INIT_VAL; + /* check if CSBEF is set in device tree */ + prop = of_get_property(nc, "fsl,csbef", &len); + if (prop && len >= sizeof(*prop)) { + csmode &= ~(CSMODE_BEF(0xf)); + csmode |= CSMODE_BEF(be32_to_cpup(prop)); + } + /* check if CSAFT is set in device tree */ + prop = of_get_property(nc, "fsl,csaft", &len); + if (prop && len >= sizeof(*prop)) { + csmode &= ~(CSMODE_AFT(0xf)); + csmode |= CSMODE_AFT(be32_to_cpup(prop)); + } + mpc8xxx_spi_write_reg(®_base->csmode[i], csmode); + + dev_info(dev, "cs=%d, init_csmode=0x%x\n", i, csmode); + } /* Enable SPI interface */ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; -- cgit v1.2.3 From d6ba32d5c60f569d252ec9dcd96cd46b19785b60 Mon Sep 17 00:00:00 2001 From: "Chew, Chiau Ee" Date: Fri, 18 Apr 2014 00:26:06 +0800 Subject: spi/pxa2xx-pci: Add PCI mode support for BayTrail LPSS SPI Similar to CE4100, BayTrail LPSS SPI can be PCI enumerated as well. Thus, the functions are renamed from ce4100_xxx to pxa2xx_spi_pci_xxx to clarify that this is a generic PCI glue layer. Also, added required infrastructure to support SPI hosts with different configurations. This patch is based on Mika Westerberg's previous work. Signed-off-by: Chew, Chiau Ee Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 76 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 3f006d3ed2a8..c1865c92ccb9 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -8,7 +8,43 @@ #include #include -static int ce4100_spi_probe(struct pci_dev *dev, +enum { + PORT_CE4100, + PORT_BYT, +}; + +struct pxa_spi_info { + enum pxa_ssp_type type; + int port_id; + int num_chipselect; + int tx_slave_id; + int tx_chan_id; + int rx_slave_id; + int rx_chan_id; +}; + +static struct pxa_spi_info spi_info_configs[] = { + [PORT_CE4100] = { + .type = PXA25x_SSP, + .port_id = -1, + .num_chipselect = -1, + .tx_slave_id = -1, + .tx_chan_id = -1, + .rx_slave_id = -1, + .rx_chan_id = -1, + }, + [PORT_BYT] = { + .type = LPSS_SSP, + .port_id = 0, + .num_chipselect = 1, + .tx_slave_id = 0, + .tx_chan_id = 0, + .rx_slave_id = 1, + .rx_chan_id = 1, + }, +}; + +static int pxa2xx_spi_pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) { struct platform_device_info pi; @@ -16,6 +52,7 @@ static int ce4100_spi_probe(struct pci_dev *dev, struct platform_device *pdev; struct pxa2xx_spi_master spi_pdata; struct ssp_device *ssp; + struct pxa_spi_info *c; ret = pcim_enable_device(dev); if (ret) @@ -25,8 +62,16 @@ static int ce4100_spi_probe(struct pci_dev *dev, if (ret) return ret; + c = &spi_info_configs[ent->driver_data]; + memset(&spi_pdata, 0, sizeof(spi_pdata)); - spi_pdata.num_chipselect = dev->devfn; + spi_pdata.num_chipselect = (c->num_chipselect > 0) ? + c->num_chipselect : dev->devfn; + spi_pdata.tx_slave_id = c->tx_slave_id; + spi_pdata.tx_chan_id = c->tx_chan_id; + spi_pdata.rx_slave_id = c->rx_slave_id; + spi_pdata.rx_chan_id = c->rx_chan_id; + spi_pdata.enable_dma = c->rx_slave_id >= 0 && c->tx_slave_id >= 0; ssp = &spi_pdata.ssp; ssp->phys_base = pci_resource_start(dev, 0); @@ -36,8 +81,8 @@ static int ce4100_spi_probe(struct pci_dev *dev, return -EIO; } ssp->irq = dev->irq; - ssp->port_id = dev->devfn; - ssp->type = PXA25x_SSP; + ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn; + ssp->type = c->type; memset(&pi, 0, sizeof(pi)); pi.parent = &dev->dev; @@ -55,28 +100,29 @@ static int ce4100_spi_probe(struct pci_dev *dev, return 0; } -static void ce4100_spi_remove(struct pci_dev *dev) +static void pxa2xx_spi_pci_remove(struct pci_dev *dev) { struct platform_device *pdev = pci_get_drvdata(dev); platform_device_unregister(pdev); } -static const struct pci_device_id ce4100_spi_devices[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, +static const struct pci_device_id pxa2xx_spi_pci_devices[] = { + { PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0f0e), PORT_BYT }, { }, }; -MODULE_DEVICE_TABLE(pci, ce4100_spi_devices); +MODULE_DEVICE_TABLE(pci, pxa2xx_spi_pci_devices); -static struct pci_driver ce4100_spi_driver = { - .name = "ce4100_spi", - .id_table = ce4100_spi_devices, - .probe = ce4100_spi_probe, - .remove = ce4100_spi_remove, +static struct pci_driver pxa2xx_spi_pci_driver = { + .name = "pxa2xx_spi_pci", + .id_table = pxa2xx_spi_pci_devices, + .probe = pxa2xx_spi_pci_probe, + .remove = pxa2xx_spi_pci_remove, }; -module_pci_driver(ce4100_spi_driver); +module_pci_driver(pxa2xx_spi_pci_driver); -MODULE_DESCRIPTION("CE4100 PCI-SPI glue code for PXA's driver"); +MODULE_DESCRIPTION("CE4100/LPSS PCI-SPI glue code for PXA's driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Sebastian Andrzej Siewior "); -- cgit v1.2.3 From ec37e8e1f0e376776dde5e9539af414d2ec31f71 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Fri, 31 Jan 2014 12:07:44 +0200 Subject: spi: dw: migrate to generic queue infrastructure Signed-off-by: Baruch Siach Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 183 +++++---------------------------------------------- drivers/spi/spi-dw.h | 8 --- 2 files changed, 15 insertions(+), 176 deletions(-) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 712ac5629cd4..357869a704d6 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -36,9 +36,6 @@ #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) -#define QUEUE_RUNNING 0 -#define QUEUE_STOPPED 1 - #define MRST_SPI_DEASSERT 0 #define MRST_SPI_ASSERT 1 @@ -263,18 +260,14 @@ static int map_dma_buffers(struct dw_spi *dws) static void giveback(struct dw_spi *dws) { struct spi_transfer *last_transfer; - unsigned long flags; struct spi_message *msg; - spin_lock_irqsave(&dws->lock, flags); msg = dws->cur_msg; dws->cur_msg = NULL; dws->cur_transfer = NULL; dws->prev_chip = dws->cur_chip; dws->cur_chip = NULL; dws->dma_mapped = 0; - queue_work(dws->workqueue, &dws->pump_messages); - spin_unlock_irqrestore(&dws->lock, flags); last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, transfer_list); @@ -282,9 +275,7 @@ static void giveback(struct dw_spi *dws) if (!last_transfer->cs_change && dws->cs_control) dws->cs_control(MRST_SPI_DEASSERT); - msg->state = NULL; - if (msg->complete) - msg->complete(msg->context); + spi_finalize_current_message(dws->master); } static void int_error_stop(struct dw_spi *dws, const char *msg) @@ -529,30 +520,12 @@ early_exit: return; } -static void pump_messages(struct work_struct *work) +static int dw_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) { - struct dw_spi *dws = - container_of(work, struct dw_spi, pump_messages); - unsigned long flags; - - /* Lock queue and check for queue work */ - spin_lock_irqsave(&dws->lock, flags); - if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) { - dws->busy = 0; - spin_unlock_irqrestore(&dws->lock, flags); - return; - } - - /* Make sure we are not already running a message */ - if (dws->cur_msg) { - spin_unlock_irqrestore(&dws->lock, flags); - return; - } - - /* Extract head of queue */ - dws->cur_msg = list_entry(dws->queue.next, struct spi_message, queue); - list_del_init(&dws->cur_msg->queue); + struct dw_spi *dws = spi_master_get_devdata(master); + dws->cur_msg = msg; /* Initial message state*/ dws->cur_msg->state = START_STATE; dws->cur_transfer = list_entry(dws->cur_msg->transfers.next, @@ -560,46 +533,9 @@ static void pump_messages(struct work_struct *work) transfer_list); dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi); - /* Mark as busy and launch transfers */ + /* Launch transfers */ tasklet_schedule(&dws->pump_transfers); - dws->busy = 1; - spin_unlock_irqrestore(&dws->lock, flags); -} - -/* spi_device use this to queue in their spi_msg */ -static int dw_spi_transfer(struct spi_device *spi, struct spi_message *msg) -{ - struct dw_spi *dws = spi_master_get_devdata(spi->master); - unsigned long flags; - - spin_lock_irqsave(&dws->lock, flags); - - if (dws->run == QUEUE_STOPPED) { - spin_unlock_irqrestore(&dws->lock, flags); - return -ESHUTDOWN; - } - - msg->actual_length = 0; - msg->status = -EINPROGRESS; - msg->state = START_STATE; - - list_add_tail(&msg->queue, &dws->queue); - - if (dws->run == QUEUE_RUNNING && !dws->busy) { - - if (dws->cur_transfer || dws->cur_msg) - queue_work(dws->workqueue, - &dws->pump_messages); - else { - /* If no other data transaction in air, just go */ - spin_unlock_irqrestore(&dws->lock, flags); - pump_messages(&dws->pump_messages); - return 0; - } - } - - spin_unlock_irqrestore(&dws->lock, flags); return 0; } @@ -664,79 +600,10 @@ static int dw_spi_setup(struct spi_device *spi) return 0; } -static int init_queue(struct dw_spi *dws) -{ - INIT_LIST_HEAD(&dws->queue); - spin_lock_init(&dws->lock); - - dws->run = QUEUE_STOPPED; - dws->busy = 0; - - tasklet_init(&dws->pump_transfers, - pump_transfers, (unsigned long)dws); - - INIT_WORK(&dws->pump_messages, pump_messages); - dws->workqueue = create_singlethread_workqueue( - dev_name(dws->master->dev.parent)); - if (dws->workqueue == NULL) - return -EBUSY; - - return 0; -} - -static int start_queue(struct dw_spi *dws) +static void dw_spi_cleanup(struct spi_device *spi) { - unsigned long flags; - - spin_lock_irqsave(&dws->lock, flags); - - if (dws->run == QUEUE_RUNNING || dws->busy) { - spin_unlock_irqrestore(&dws->lock, flags); - return -EBUSY; - } - - dws->run = QUEUE_RUNNING; - dws->cur_msg = NULL; - dws->cur_transfer = NULL; - dws->cur_chip = NULL; - dws->prev_chip = NULL; - spin_unlock_irqrestore(&dws->lock, flags); - - queue_work(dws->workqueue, &dws->pump_messages); - - return 0; -} - -static int stop_queue(struct dw_spi *dws) -{ - unsigned long flags; - unsigned limit = 50; - int status = 0; - - spin_lock_irqsave(&dws->lock, flags); - dws->run = QUEUE_STOPPED; - while ((!list_empty(&dws->queue) || dws->busy) && limit--) { - spin_unlock_irqrestore(&dws->lock, flags); - msleep(10); - spin_lock_irqsave(&dws->lock, flags); - } - - if (!list_empty(&dws->queue) || dws->busy) - status = -EBUSY; - spin_unlock_irqrestore(&dws->lock, flags); - - return status; -} - -static int destroy_queue(struct dw_spi *dws) -{ - int status; - - status = stop_queue(dws); - if (status != 0) - return status; - destroy_workqueue(dws->workqueue); - return 0; + struct chip_data *chip = spi_get_ctldata(spi); + kfree(chip); } /* Restart the controller, disable all interrupts, clean rx fifo */ @@ -794,7 +661,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->bus_num = dws->bus_num; master->num_chipselect = dws->num_cs; master->setup = dw_spi_setup; - master->transfer = dw_spi_transfer; + master->transfer_one_message = dw_spi_transfer_one_message; master->max_speed_hz = dws->max_freq; /* Basic HW init */ @@ -808,33 +675,21 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) } } - /* Initial and start queue */ - ret = init_queue(dws); - if (ret) { - dev_err(&master->dev, "problem initializing queue\n"); - goto err_diable_hw; - } - ret = start_queue(dws); - if (ret) { - dev_err(&master->dev, "problem starting queue\n"); - goto err_diable_hw; - } + tasklet_init(&dws->pump_transfers, pump_transfers, (unsigned long)dws); spi_master_set_devdata(master, dws); ret = devm_spi_register_master(dev, master); if (ret) { dev_err(&master->dev, "problem registering spi master\n"); - goto err_queue_alloc; + goto err_dma_exit; } mrst_spi_debugfs_init(dws); return 0; -err_queue_alloc: - destroy_queue(dws); +err_dma_exit: if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); -err_diable_hw: spi_enable_chip(dws, 0); err_free_master: spi_master_put(master); @@ -844,18 +699,10 @@ EXPORT_SYMBOL_GPL(dw_spi_add_host); void dw_spi_remove_host(struct dw_spi *dws) { - int status = 0; - if (!dws) return; mrst_spi_debugfs_remove(dws); - /* Remove the queue */ - status = destroy_queue(dws); - if (status != 0) - dev_err(&dws->master->dev, - "dw_spi_remove: workqueue will not complete, message memory not freed\n"); - if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); spi_enable_chip(dws, 0); @@ -868,7 +715,7 @@ int dw_spi_suspend_host(struct dw_spi *dws) { int ret = 0; - ret = stop_queue(dws); + ret = spi_master_suspend(dws->master); if (ret) return ret; spi_enable_chip(dws, 0); @@ -882,7 +729,7 @@ int dw_spi_resume_host(struct dw_spi *dws) int ret; spi_hw_init(dws); - ret = start_queue(dws); + ret = spi_master_resume(dws->master); if (ret) dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret); return ret; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 587643dae11e..3fd7ab599ab4 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -104,14 +104,6 @@ struct dw_spi { u16 bus_num; u16 num_cs; /* supported slave numbers */ - /* 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; -- cgit v1.2.3 From 20e5ea191524b482e1b4aa12b5ff4684717f44b8 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Fri, 31 Jan 2014 12:07:45 +0200 Subject: spi: dw: document device tree binding Signed-off-by: Baruch Siach Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-dw.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-dw.txt diff --git a/Documentation/devicetree/bindings/spi/spi-dw.txt b/Documentation/devicetree/bindings/spi/spi-dw.txt new file mode 100644 index 000000000000..7b63ed601990 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-dw.txt @@ -0,0 +1,24 @@ +Synopsys DesignWare SPI master + +Required properties: +- compatible: should be "snps,designware-spi" +- #address-cells: see spi-bus.txt +- #size-cells: see spi-bus.txt +- reg: address and length of the spi master registers +- interrupts: should contain one interrupt +- clocks: spi clock phandle +- num-cs: see spi-bus.txt + +Optional properties: +- cs-gpios: see spi-bus.txt + +Example: + +spi: spi@4020a000 { + compatible = "snps,designware-spi"; + interrupts = <11 1>; + reg = <0x4020a000 0x1000>; + clocks = <&pclk>; + num-cs = <2>; + cs-gpios = <&banka 0 0>; +}; -- cgit v1.2.3 From d9c73bb8a3a5e4b76d2ad55da00d9ea776475c81 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Fri, 31 Jan 2014 12:07:47 +0200 Subject: spi: dw: add support for gpio controlled chip select Also, use this opportunity to let spi_chip_sel() handle chip-select deactivation as well. Signed-off-by: Baruch Siach Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 22 ++++++++++++++++++++++ drivers/spi/spi-dw.c | 18 ++++++++++++------ drivers/spi/spi-dw.h | 16 +++++++++++----- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 1492f5ee9aaa..a5cba14ac3d2 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "spi-dw.h" @@ -70,6 +71,27 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) dws->num_cs = 4; dws->max_freq = clk_get_rate(dwsmmio->clk); + if (pdev->dev.of_node) { + int i; + + for (i = 0; i < dws->num_cs; i++) { + int cs_gpio = of_get_named_gpio(pdev->dev.of_node, + "cs-gpios", i); + + if (cs_gpio == -EPROBE_DEFER) { + ret = cs_gpio; + goto out; + } + + if (gpio_is_valid(cs_gpio)) { + ret = devm_gpio_request(&pdev->dev, cs_gpio, + dev_name(&pdev->dev)); + if (ret) + goto out; + } + } + } + ret = dw_spi_add_host(&pdev->dev, dws); if (ret) goto out; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 357869a704d6..9965e1b84832 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "spi-dw.h" @@ -36,9 +37,6 @@ #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) -#define MRST_SPI_DEASSERT 0 -#define MRST_SPI_ASSERT 1 - /* Slave spi_dev related */ struct chip_data { u16 cr0; @@ -272,8 +270,8 @@ static void giveback(struct dw_spi *dws) last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, transfer_list); - if (!last_transfer->cs_change && dws->cs_control) - dws->cs_control(MRST_SPI_DEASSERT); + if (!last_transfer->cs_change) + spi_chip_sel(dws, dws->cur_msg->spi, 0); spi_finalize_current_message(dws->master); } @@ -493,7 +491,7 @@ static void pump_transfers(unsigned long data) dw_writew(dws, DW_SPI_CTRL0, cr0); spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); - spi_chip_sel(dws, spi->chip_select); + spi_chip_sel(dws, spi, 1); /* Set the interrupt mask, for poll mode just disable all int */ spi_mask_intr(dws, 0xff); @@ -544,6 +542,7 @@ static int dw_spi_setup(struct spi_device *spi) { struct dw_spi_chip *chip_info = NULL; struct chip_data *chip; + int ret; /* Only alloc on first setup */ chip = spi_get_ctldata(spi); @@ -597,6 +596,13 @@ static int dw_spi_setup(struct spi_device *spi) | (spi->mode << SPI_MODE_OFFSET) | (chip->tmode << SPI_TMOD_OFFSET); + if (gpio_is_valid(spi->cs_gpio)) { + ret = gpio_direction_output(spi->cs_gpio, + !(spi->mode & SPI_CS_HIGH)); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 3fd7ab599ab4..6d2acad34f64 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -3,6 +3,7 @@ #include #include +#include /* Register offsets */ #define DW_SPI_CTRL0 0x00 @@ -178,15 +179,20 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div) dw_writel(dws, DW_SPI_BAUDR, div); } -static inline void spi_chip_sel(struct dw_spi *dws, u16 cs) +static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi, + int active) { - if (cs > dws->num_cs) - return; + u16 cs = spi->chip_select; + int gpio_val = active ? (spi->mode & SPI_CS_HIGH) : + !(spi->mode & SPI_CS_HIGH); if (dws->cs_control) - dws->cs_control(1); + dws->cs_control(active); + if (gpio_is_valid(spi->cs_gpio)) + gpio_set_value(spi->cs_gpio, gpio_val); - dw_writel(dws, DW_SPI_SER, 1 << cs); + if (active) + dw_writel(dws, DW_SPI_SER, 1 << cs); } /* Disable IRQ bits */ -- cgit v1.2.3 From 77538f4a98dc542ad25a1c0fa3b79c5f90c47619 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 29 Apr 2014 17:19:01 +0900 Subject: spi: pl022: remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 51d99779682f..66d2ae21e78e 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1111,10 +1111,8 @@ static int pl022_dma_probe(struct pl022 *pl022) } pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!pl022->dummypage) { - dev_dbg(&pl022->adev->dev, "no DMA dummypage!\n"); + if (!pl022->dummypage) goto err_no_dummypage; - } dev_info(&pl022->adev->dev, "setup for DMA on RX %s, TX %s\n", dma_chan_name(pl022->dma_rx_channel), @@ -1809,11 +1807,8 @@ static int pl022_setup(struct spi_device *spi) if (chip == NULL) { chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); - if (!chip) { - dev_err(&spi->dev, - "cannot allocate controller state\n"); + if (!chip) return -ENOMEM; - } dev_dbg(&spi->dev, "allocated memory for controller's runtime state\n"); } @@ -2050,10 +2045,8 @@ pl022_platform_data_dt_get(struct device *dev) } pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL); - if (!pd) { - dev_err(dev, "cannot allocate platform data memory\n"); + if (!pd) return NULL; - } pd->bus_id = -1; pd->enable_dma = 1; -- cgit v1.2.3 From 9deae4592b66c0b2a7832fefa192e7a506ce047e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 29 Apr 2014 17:19:38 +0900 Subject: spi: pxa2xx: remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 41185d0557fa..54e4e6593980 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -886,11 +886,8 @@ static int setup(struct spi_device *spi) chip = spi_get_ctldata(spi); if (!chip) { chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); - if (!chip) { - dev_err(&spi->dev, - "failed setup: can't allocate chip data\n"); + if (!chip) return -ENOMEM; - } if (drv_data->ssp_type == CE4100_SSP) { if (spi->chip_select > 4) { @@ -1037,11 +1034,8 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) return NULL; pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(&pdev->dev, - "failed to allocate memory for platform data\n"); + if (!pdata) return NULL; - } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) -- cgit v1.2.3 From 0375cff5d8c9da86787167da2740b6e5c50963da Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 29 Apr 2014 17:20:02 +0900 Subject: spi: s3c24xx: remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 6db14e53af17..381d4af149fc 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -186,10 +186,8 @@ static int s3c24xx_spi_setup(struct spi_device *spi) cs = devm_kzalloc(&spi->dev, sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL); - if (!cs) { - dev_err(&spi->dev, "no memory for controller state\n"); + if (!cs) return -ENOMEM; - } cs->spcon = SPCON_DEFAULT; cs->hz = -1; -- cgit v1.2.3 From 1273eb0506c474a128d06025de98c5ca4a11cf14 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 29 Apr 2014 17:20:20 +0900 Subject: spi: s3c64xx: remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index f19cd97855e8..affcfd6fe76b 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -773,7 +773,6 @@ 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"); of_node_put(data_np); return ERR_PTR(-ENOMEM); } @@ -987,10 +986,8 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) u32 temp; sci = devm_kzalloc(dev, sizeof(*sci), GFP_KERNEL); - if (!sci) { - dev_err(dev, "memory allocation for spi_info failed\n"); + if (!sci) return ERR_PTR(-ENOMEM); - } 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"); -- cgit v1.2.3 From 1e8231b79ccbb3e6e7b3f6af9f998dca16b00efe Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 29 Apr 2014 17:21:25 +0900 Subject: spi: sh-msiof: remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index e850d03e7190..45b09142afe2 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -642,10 +642,8 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) u32 num_cs = 1; info = devm_kzalloc(dev, sizeof(struct sh_msiof_spi_info), GFP_KERNEL); - if (!info) { - dev_err(dev, "failed to allocate setup data\n"); + if (!info) return NULL; - } /* Parse the MSIOF properties */ of_property_read_u32(np, "num-cs", &num_cs); -- cgit v1.2.3 From 9f48e54bf70018547b0c146b4187489c615bd3a2 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 29 Apr 2014 17:22:32 +0900 Subject: spi: tle62x0: remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-tle62x0.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c index 2d4010d80824..daf5aa1c24c3 100644 --- a/drivers/spi/spi-tle62x0.c +++ b/drivers/spi/spi-tle62x0.c @@ -253,10 +253,8 @@ static int tle62x0_probe(struct spi_device *spi) } st = kzalloc(sizeof(struct tle62x0_state), GFP_KERNEL); - if (st == NULL) { - dev_err(&spi->dev, "no memory for device state\n"); + if (st == NULL) return -ENOMEM; - } st->us = spi; st->nr_gpio = pdata->gpio_count; -- cgit v1.2.3 From fe75cbc1bd94d9e7d837b31f4373d55d7dd91019 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 29 Apr 2014 17:23:10 +0900 Subject: spi: topcliff-pch: remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-topcliff-pch.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index f406b30af961..f05abf89c067 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1578,14 +1578,11 @@ static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct pch_pd_dev_save *pd_dev_save; pd_dev_save = kzalloc(sizeof(struct pch_pd_dev_save), GFP_KERNEL); - if (!pd_dev_save) { - dev_err(&pdev->dev, "%s Can't allocate pd_dev_sav\n", __func__); + if (!pd_dev_save) return -ENOMEM; - } board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL); if (!board_dat) { - dev_err(&pdev->dev, "%s Can't allocate board_dat\n", __func__); retval = -ENOMEM; goto err_no_mem; } -- cgit v1.2.3 From b112f0585edd1c10e4986373d93ab0d95d12da82 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 6 May 2014 17:44:41 +0200 Subject: spi: atmel: fix incorrect comparison Found using smatch: drivers/spi/spi-atmel.c:878 atmel_spi_pump_pio_data() warn: unsigned 'as->current_remaining_bytes' is never less than zero. Signed-off-by: Alexandre Belloni Acked-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 485e6cce94bf..e6fe837d49fb 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -874,8 +874,9 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) spi_readl(as, RDR); } if (xfer->bits_per_word > 8) { - as->current_remaining_bytes -= 2; - if (as->current_remaining_bytes < 0) + if (as->current_remaining_bytes > 2) + as->current_remaining_bytes -= 2; + else as->current_remaining_bytes = 0; } else { as->current_remaining_bytes--; -- cgit v1.2.3 From 790d190257b339bba9ff821d7d49f4567146f4ad Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 May 2014 16:45:41 +0900 Subject: spi: fsl-dspi: Make of_device_id array const Make of_device_id array const, because all OF functions handle it as const. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index d565eeee3bd8..5021ddf03f60 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -406,7 +406,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct of_device_id fsl_dspi_dt_ids[] = { +static const struct of_device_id fsl_dspi_dt_ids[] = { { .compatible = "fsl,vf610-dspi", .data = NULL, }, { /* sentinel */ } }; -- cgit v1.2.3 From d9e15281896481df1a2566477287210c210357e3 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 May 2014 16:48:59 +0900 Subject: spi: spi-gpio: Make of_device_id array const Make of_device_id array const, because all OF functions handle it as const. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 09823076df88..9f595535cf27 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -340,7 +340,7 @@ done: } #ifdef CONFIG_OF -static struct of_device_id spi_gpio_dt_ids[] = { +static const struct of_device_id spi_gpio_dt_ids[] = { { .compatible = "spi-gpio" }, {} }; -- cgit v1.2.3 From 113b1a071583aff4750e95f3373fac18ac319bee Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 May 2014 16:50:04 +0900 Subject: spi: qup: Make of_device_id array const Make of_device_id array const, because all OF functions handle it as const. Signed-off-by: Jingoo Han Reviewed-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index b032e8885e24..17c5f6001000 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -749,7 +749,7 @@ static int spi_qup_remove(struct platform_device *pdev) return 0; } -static struct of_device_id spi_qup_dt_match[] = { +static const struct of_device_id spi_qup_dt_match[] = { { .compatible = "qcom,spi-qup-v2.1.1", }, { .compatible = "qcom,spi-qup-v2.2.1", }, { } -- cgit v1.2.3 From 0ac83f396d90a275316be0d67932d56b298792f1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 May 2014 16:51:02 +0900 Subject: spi: tegra114: Make of_device_id array const Make of_device_id array const, because all OF functions handle it as const. Signed-off-by: Jingoo Han Acked-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 400649595505..e4a85ada861d 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1012,7 +1012,7 @@ static irqreturn_t tegra_spi_isr(int irq, void *context_data) return IRQ_WAKE_THREAD; } -static struct of_device_id tegra_spi_of_match[] = { +static const struct of_device_id tegra_spi_of_match[] = { { .compatible = "nvidia,tegra114-spi", }, {} }; -- cgit v1.2.3 From bff15edc0be070df60228564da4aedaffb35e7dd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 May 2014 16:51:52 +0900 Subject: spi: tegra20-sflash: Make of_device_id array const Make of_device_id array const, because all OF functions handle it as const. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-sflash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 47869ea636e1..3548ce25c08f 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -419,7 +419,7 @@ static irqreturn_t tegra_sflash_isr(int irq, void *context_data) return handle_cpu_based_xfer(tsd); } -static struct of_device_id tegra_sflash_of_match[] = { +static const struct of_device_id tegra_sflash_of_match[] = { { .compatible = "nvidia,tegra20-sflash", }, {} }; -- cgit v1.2.3 From b2fb1872d3d912dcf81ec71a345fd3dcf81a066d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 May 2014 16:52:36 +0900 Subject: spi: tegra20-slink: Make of_device_id array const Make of_device_id array const, because all OF functions handle it as const. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index e3c1b93e45d1..0b9e32e9f493 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1001,7 +1001,7 @@ static const struct tegra_slink_chip_data tegra20_spi_cdata = { .cs_hold_time = false, }; -static struct of_device_id tegra_slink_of_match[] = { +static const struct of_device_id tegra_slink_of_match[] = { { .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, }, { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, }, {} -- cgit v1.2.3 From bf61fb805ce8f60a02c69abf249832bb306e2fe2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 8 May 2014 19:17:07 +0800 Subject: spi: dw: Remove unused dw_spi_cleanup function The dw_spi_cleanup() function was removed by commit c63f5da00845 "spi: dw: Don't call kfree for memory allocated by devm_kzalloc". commit ec37e8e1f0e3 "spi: dw: migrate to generic queue infrastructure" added dw_spi_cleanup() but never use it. So now I got below build warning: CC [M] drivers/spi/spi-dw.o drivers/spi/spi-dw.c:609:13: warning: 'dw_spi_cleanup' defined but not used [-Wunused-function] Signed-off-by: Axel Lin Acked-by: Baruch Siach Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 9965e1b84832..29f33143b795 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -606,12 +606,6 @@ static int dw_spi_setup(struct spi_device *spi) return 0; } -static void dw_spi_cleanup(struct spi_device *spi) -{ - struct chip_data *chip = spi_get_ctldata(spi); - kfree(chip); -} - /* Restart the controller, disable all interrupts, clean rx fifo */ static void spi_hw_init(struct dw_spi *dws) { -- cgit v1.2.3 From 8f391222c06c27e072e306e7eefd41d39b68ec46 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Tue, 13 May 2014 16:34:42 -0500 Subject: spi: qup: Correct selection of FIFO/Block mode This patch fixes the calculation for determining whether to use FIFO or BLOCK mode. Signed-off-by: Andy Gross Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 78c66e3c53ed..943f973a59aa 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -287,7 +287,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); if (!xfer) { - dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n", + dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", qup_err, spi_err, opflags); return IRQ_HANDLED; } @@ -366,7 +366,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) n_words = xfer->len / w_size; controller->w_size = w_size; - if (n_words <= controller->in_fifo_sz) { + if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { mode = QUP_IO_M_MODE_FIFO; writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); -- cgit v1.2.3 From 2b16069a7af4a4e6f6c2e599afe1301d0655487e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 16 May 2014 16:47:18 +0200 Subject: spi/topcliff-pch: Fix Kconfig dependencies The spi-topcliff-pch driver is for a companion chip to the Intel Atom E600 series processors. These are 32-bit x86 processors so the driver is only needed on X86_32. Add COMPILE_TEST as an alternative, so that the driver can still be build-tested elsewhere. Signed-off-by: Jean Delvare Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60f2b41c7310..722312fe19be 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -505,7 +505,7 @@ config SPI_TEGRA20_SLINK config SPI_TOPCLIFF_PCH tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) help SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus used in some x86 embedded processors. -- cgit v1.2.3 From 41148c3a728222a885bc2f3ba3ce66831de0b244 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Sun, 4 May 2014 14:32:36 +0800 Subject: spi: sirf: decrease the interrupt count and latency of PIO mode current PIO tranfer method be described as follows: 1. fill as much as bytes but no more than 256 bytes(fifo size) 2. enable oflow/uflow/txfifo_empty interrupt 3. isr process 3 interrupt signal, do complete works. 4. after isr done, if there are left bytes go into 1 else go into 5 5. transfer end by current PIO transfer method: 1. reduce interrupt counts in spi interrupt line. 2. reduce interrupt latency because no do data fill/fetch in isr. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown --- drivers/spi/spi-sirf.c | 101 ++++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 2d238990115c..95ac276eaafe 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -86,6 +86,7 @@ #define SIRFSOC_SPI_TX_DONE BIT(1) #define SIRFSOC_SPI_RX_OFLOW BIT(2) #define SIRFSOC_SPI_TX_UFLOW BIT(3) +#define SIRFSOC_SPI_RX_IO_DMA BIT(4) #define SIRFSOC_SPI_RX_FIFO_FULL BIT(6) #define SIRFSOC_SPI_TXFIFO_EMPTY BIT(7) #define SIRFSOC_SPI_RXFIFO_THD_REACH BIT(8) @@ -265,41 +266,34 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) { struct sirfsoc_spi *sspi = dev_id; u32 spi_stat = readl(sspi->base + SIRFSOC_SPI_INT_STATUS); - - writel(spi_stat, sspi->base + SIRFSOC_SPI_INT_STATUS); - if (sspi->tx_by_cmd && (spi_stat & SIRFSOC_SPI_FRM_END)) { complete(&sspi->tx_done); writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_INT_MASK_ALL, + sspi->base + SIRFSOC_SPI_INT_STATUS); return IRQ_HANDLED; } /* Error Conditions */ if (spi_stat & SIRFSOC_SPI_RX_OFLOW || spi_stat & SIRFSOC_SPI_TX_UFLOW) { + complete(&sspi->tx_done); complete(&sspi->rx_done); writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_INT_MASK_ALL, + sspi->base + SIRFSOC_SPI_INT_STATUS); + return IRQ_HANDLED; } + if (spi_stat & SIRFSOC_SPI_TXFIFO_EMPTY) + complete(&sspi->tx_done); + while (!(readl(sspi->base + SIRFSOC_SPI_INT_STATUS) & + SIRFSOC_SPI_RX_IO_DMA)) + cpu_relax(); + complete(&sspi->rx_done); + writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_INT_MASK_ALL, + sspi->base + SIRFSOC_SPI_INT_STATUS); - if (spi_stat & (SIRFSOC_SPI_FRM_END - | SIRFSOC_SPI_RXFIFO_THD_REACH)) - while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS) - & SIRFSOC_SPI_FIFO_EMPTY)) && - sspi->left_rx_word) - sspi->rx_word(sspi); - - if (spi_stat & (SIRFSOC_SPI_TXFIFO_EMPTY | - SIRFSOC_SPI_TXFIFO_THD_REACH)) - while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) - & SIRFSOC_SPI_FIFO_FULL)) && - sspi->left_tx_word) - sspi->tx_word(sspi); - - /* Received all words */ - if ((sspi->left_rx_word == 0) && (sspi->left_tx_word == 0)) { - complete(&sspi->rx_done); - writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); - } return IRQ_HANDLED; } @@ -420,32 +414,45 @@ static void spi_sirfsoc_pio_transfer(struct spi_device *spi, int timeout = t->len * 10; sspi = spi_master_get_devdata(spi->master); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(0, sspi->base + SIRFSOC_SPI_INT_EN); - writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS); - writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | SIRFSOC_SPI_MUL_DAT_MODE | - SIRFSOC_SPI_ENA_AUTO_CLR, sspi->base + SIRFSOC_SPI_CTRL); - writel(sspi->left_tx_word - 1, - sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); - writel(sspi->left_rx_word - 1, - sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); - sspi->tx_word(sspi); - writel(SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN | - SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_RXFIFO_THD_INT_EN | - SIRFSOC_SPI_TXFIFO_THD_INT_EN | SIRFSOC_SPI_FRM_END_INT_EN| - SIRFSOC_SPI_RXFIFO_FULL_INT_EN, - sspi->base + SIRFSOC_SPI_INT_EN); - writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, + do { + writel(SIRFSOC_SPI_FIFO_RESET, + sspi->base + SIRFSOC_SPI_RXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_RESET, + sspi->base + SIRFSOC_SPI_TXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + SIRFSOC_SPI_RXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + SIRFSOC_SPI_TXFIFO_OP); + writel(0, sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_INT_MASK_ALL, + sspi->base + SIRFSOC_SPI_INT_STATUS); + writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | + SIRFSOC_SPI_MUL_DAT_MODE | SIRFSOC_SPI_ENA_AUTO_CLR, + sspi->base + SIRFSOC_SPI_CTRL); + writel(min(sspi->left_tx_word, (u32)(256 / sspi->word_width)) + - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); + writel(min(sspi->left_rx_word, (u32)(256 / sspi->word_width)) + - 1, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); + while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) + & SIRFSOC_SPI_FIFO_FULL)) && sspi->left_tx_word) + sspi->tx_word(sspi); + writel(SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN | + SIRFSOC_SPI_TX_UFLOW_INT_EN | + SIRFSOC_SPI_RX_OFLOW_INT_EN, + sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, sspi->base + SIRFSOC_SPI_TX_RX_EN); - if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) - dev_err(&spi->dev, "transfer timeout\n"); - writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN); - writel(0, sspi->base + SIRFSOC_SPI_INT_EN); + if (!wait_for_completion_timeout(&sspi->tx_done, timeout) || + !wait_for_completion_timeout(&sspi->rx_done, timeout)) { + dev_err(&spi->dev, "transfer timeout\n"); + break; + } + while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS) + & SIRFSOC_SPI_FIFO_EMPTY)) && sspi->left_rx_word) + sspi->rx_word(sspi); + writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP); + writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP); + } while (sspi->left_tx_word != 0 || sspi->left_rx_word != 0); } static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) -- cgit v1.2.3 From 3beb61dbfcf188399cbc36ce1eeb8b2ba724de38 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 22 May 2014 20:07:35 +0200 Subject: spi: rspi: Round up division to avoid slave overclocking The calculation of the bit rate divider used a standard C division, which rounds down the quotient. This may lead to a higher bitrate than requested. Round up to avoid this. E.g. on Koelsch, the SPI flash (configured for 30 MHz) was driven at 48.75 MHz. After this patch it's driven at a safe 24.375 MHz. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 1fb0ad213324..5639f9529e0b 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -266,7 +266,8 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ - spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; + spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk), + 2 * rspi->max_speed_hz) - 1; rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); /* Disable dummy transmission, set 16-bit word access, 1 frame */ @@ -302,7 +303,8 @@ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ - spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; + spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk), + 2 * rspi->max_speed_hz) - 1; rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); /* Disable dummy transmission, set byte access */ @@ -335,7 +337,7 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ - spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz); + spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk), 2 * rspi->max_speed_hz); rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); /* Disable dummy transmission, set byte access */ -- cgit v1.2.3 From 80874d8c9e9cbfa195572dfea46f2314098b3869 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 26 May 2014 14:05:25 +0200 Subject: spi: core: Ignore unsupported spi-[tr]x-bus-width property values Rejecting unsupported values of spi-tx-bus-width and spi-rx-bus-width may break compatibility with future DTs. Just ignore them, falling back to Single SPI Transfers. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index f01cbb41e6ca..147e0707f43f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1247,11 +1247,10 @@ static void of_register_spi_devices(struct spi_master *master) spi->mode |= SPI_TX_QUAD; break; default: - dev_err(&master->dev, - "spi-tx-bus-width %d not supported\n", - value); - spi_dev_put(spi); - continue; + dev_warn(&master->dev, + "spi-tx-bus-width %d not supported\n", + value); + break; } } @@ -1266,11 +1265,10 @@ static void of_register_spi_devices(struct spi_master *master) spi->mode |= SPI_RX_QUAD; break; default: - dev_err(&master->dev, - "spi-rx-bus-width %d not supported\n", - value); - spi_dev_put(spi); - continue; + dev_warn(&master->dev, + "spi-rx-bus-width %d not supported\n", + value); + break; } } -- cgit v1.2.3 From a2cb1be18254fd1479d87f7860af7a8413508e16 Mon Sep 17 00:00:00 2001 From: Valentin Longchamp Date: Fri, 16 May 2014 16:46:21 +0200 Subject: spi/fsl-espi: fix rx_buf in fsl_espi_cmd_trans()/fsl_espi_rw_trans() By default for every espi transfer, the rx_buf is placed right after the tx_buf. This can lead to a buffer overflow when the size of both the TX and RX data cumulated is higher than the allocated 64K buffer for the transfer (this is the case when sending for instance a read command and reading 64K back, please see: http://article.gmane.org/gmane.linux.drivers.mtd/53411 ) This gets fixed by always setting the RX buffer pointer at the begining of the transfer buffer. [The driver shouldn't be doing the copy in the first place and instead sending directly from the supplied buffer but this is at least not worse than what's there -- broonie] Signed-off-by: Valentin Longchamp Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 24096c84e9c4..f0d7662e4d45 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -348,7 +348,7 @@ static void fsl_espi_cmd_trans(struct spi_message *m, } espi_trans->tx_buf = local_buf; - espi_trans->rx_buf = local_buf + espi_trans->n_tx; + espi_trans->rx_buf = local_buf; fsl_espi_do_trans(m, espi_trans); espi_trans->actual_length = espi_trans->len; @@ -397,7 +397,7 @@ static void fsl_espi_rw_trans(struct spi_message *m, espi_trans->n_rx = trans_len; espi_trans->len = trans_len + n_tx; espi_trans->tx_buf = local_buf; - espi_trans->rx_buf = local_buf + n_tx; + espi_trans->rx_buf = local_buf; fsl_espi_do_trans(m, espi_trans); memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len); -- cgit v1.2.3 From 836d1a22db9ee0a466301465e873bc94da86cb15 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Fri, 30 May 2014 18:18:09 +0200 Subject: spi/pxa2xx: fix runtime PM enabling order In commit 7dd62787334ac6e0e2a0ef3f20bb1936ac431b04 (spi/pxa2xx: Convert to core runtime PM) master->auto_runtime_pm was set to true. In this case pm_runtime_enable() must be called *before* spi_register_master(), otherwise the kernel hangs with this error message: spi_master spi0: Failed to power device: -13 A similar fix, but for spi/hspi, was applied in 268d76430d1b68c340687357ffd18b4b12d02269. Signed-off-by: Antonio Ospite Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 41185d0557fa..a07b75814442 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1202,6 +1202,11 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) tasklet_init(&drv_data->pump_transfers, pump_transfers, (unsigned long)drv_data); + 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); + /* Register with the SPI framework */ platform_set_drvdata(pdev, drv_data); status = devm_spi_register_master(&pdev->dev, master); @@ -1210,11 +1215,6 @@ 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: -- cgit v1.2.3 From 5f684c34fc82be84ece158aa5c5c7c5072daa9a0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:03 +0200 Subject: spi: rspi: Extract rspi_wait_for_{tx_empty,rx_full}() Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 5639f9529e0b..d04a4acce231 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -405,11 +405,22 @@ static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask, return 0; } +static inline int rspi_wait_for_tx_empty(struct rspi_data *rspi) +{ + return rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); +} + +static inline int rspi_wait_for_rx_full(struct rspi_data *rspi) +{ + return rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE); +} + static int rspi_data_out(struct rspi_data *rspi, u8 data) { - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { + int error = rspi_wait_for_tx_empty(rspi); + if (error < 0) { dev_err(&rspi->master->dev, "transmit timeout\n"); - return -ETIMEDOUT; + return error; } rspi_write_data(rspi, data); return 0; @@ -417,11 +428,13 @@ static int rspi_data_out(struct rspi_data *rspi, u8 data) static int rspi_data_in(struct rspi_data *rspi) { + int error; u8 data; - if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { + error = rspi_wait_for_rx_full(rspi); + if (error < 0) { dev_err(&rspi->master->dev, "receive timeout\n"); - return -ETIMEDOUT; + return error; } data = rspi_read_data(rspi); return data; @@ -737,7 +750,7 @@ static int rspi_transfer_out_in(struct rspi_data *rspi, } /* Wait for the last transmission */ - rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + rspi_wait_for_tx_empty(rspi); return 0; } @@ -783,7 +796,7 @@ static int rspi_rz_transfer_out_in(struct rspi_data *rspi, } /* Wait for the last transmission */ - rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + rspi_wait_for_tx_empty(rspi); return 0; } @@ -818,7 +831,7 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, } /* Wait for the last transmission */ - rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + rspi_wait_for_tx_empty(rspi); return 0; } @@ -836,7 +849,7 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) } /* Wait for the last transmission */ - rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + rspi_wait_for_tx_empty(rspi); return 0; } -- cgit v1.2.3 From 32c64261c6f50a4e71ec7546f7e2f48eba91c985 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:04 +0200 Subject: spi: rspi: Do not call rspi_receive_init() for TX-only Since commit 8449fd76deb9ac67a15a6fb8ead7bb4595d019d2 ("spi: rspi: Merge rspi_send_pio() and rspi_receive_pio()"), rspi_receive_init() is called for transmit-only transfers too, while this is not needed. Only call rspi_receive_init() when receiving, to preserve behavior on RSPI on SH. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index d04a4acce231..57beda209599 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -726,13 +726,13 @@ static int rspi_transfer_out_in(struct rspi_data *rspi, u8 *rx_buf = xfer->rx_buf; u8 spcr, data; - rspi_receive_init(rspi); - spcr = rspi_read8(rspi, RSPI_SPCR); - if (rx_buf) + if (rx_buf) { + rspi_receive_init(rspi); spcr &= ~SPCR_TXMD; - else + } else { spcr |= SPCR_TXMD; + } rspi_write8(rspi, spcr, RSPI_SPCR); while (remain > 0) { -- cgit v1.2.3 From 9c5de2c1754c2bb3c69c4d7bf0d0edc0a61d8232 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:05 +0200 Subject: spi: rspi: Remove unused 16-bit DMA support The 16-bit DMA support doesn't fit well within the SPI core DMA framework, as it needs to manage its own double-sized temporary buffers, for handling the interleaved data. Remove it, as there is no in-tree board code that sets rspi_plat_data.dma_width_16bit. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 84 ++++-------------------------------------------- include/linux/spi/rspi.h | 2 -- 2 files changed, 6 insertions(+), 80 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 57beda209599..3bd06fd9af47 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -201,7 +201,6 @@ struct rspi_data { struct dma_chan *chan_tx; struct dma_chan *chan_rx; - unsigned dma_width_16bit:1; unsigned dma_callbacked:1; unsigned byte_access:1; }; @@ -475,60 +474,17 @@ static void rspi_dma_unmap_sg(struct scatterlist *sg, struct dma_chan *chan, dma_unmap_sg(chan->device->dev, sg, 1, dir); } -static void rspi_memory_to_8bit(void *buf, const void *data, unsigned len) -{ - u16 *dst = buf; - const u8 *src = data; - - while (len) { - *dst++ = (u16)(*src++); - len--; - } -} - -static void rspi_memory_from_8bit(void *buf, const void *data, unsigned len) -{ - u8 *dst = buf; - const u16 *src = data; - - while (len) { - *dst++ = (u8)*src++; - len--; - } -} - static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) { struct scatterlist sg; - const void *buf = NULL; + const void *buf = t->tx_buf; struct dma_async_tx_descriptor *desc; - unsigned int len; + unsigned int len = t->len; int ret = 0; - if (rspi->dma_width_16bit) { - void *tmp; - /* - * If DMAC bus width is 16-bit, the driver allocates a dummy - * buffer. And, the driver converts original data into the - * DMAC data as the following format: - * original data: 1st byte, 2nd byte ... - * DMAC data: 1st byte, dummy, 2nd byte, dummy ... - */ - len = t->len * 2; - tmp = kmalloc(len, GFP_KERNEL); - if (!tmp) - return -ENOMEM; - rspi_memory_to_8bit(tmp, t->tx_buf, t->len); - buf = tmp; - } else { - len = t->len; - buf = t->tx_buf; - } + if (!rspi_dma_map_sg(&sg, buf, len, rspi->chan_tx, DMA_TO_DEVICE)) + return -EFAULT; - if (!rspi_dma_map_sg(&sg, buf, len, rspi->chan_tx, DMA_TO_DEVICE)) { - ret = -EFAULT; - goto end_nomap; - } desc = dmaengine_prep_slave_sg(rspi->chan_tx, &sg, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { @@ -563,10 +519,6 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) end: rspi_dma_unmap_sg(&sg, rspi->chan_tx, DMA_TO_DEVICE); -end_nomap: - if (rspi->dma_width_16bit) - kfree(buf); - return ret; } @@ -603,28 +555,11 @@ static void qspi_receive_init(const struct rspi_data *rspi) static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) { struct scatterlist sg, sg_dummy; - void *dummy = NULL, *rx_buf = NULL; + void *dummy = NULL, *rx_buf = t->rx_buf; struct dma_async_tx_descriptor *desc, *desc_dummy; - unsigned int len; + unsigned int len = t->len; int ret = 0; - if (rspi->dma_width_16bit) { - /* - * If DMAC bus width is 16-bit, the driver allocates a dummy - * buffer. And, finally the driver converts the DMAC data into - * actual data as the following format: - * DMAC data: 1st byte, dummy, 2nd byte, dummy ... - * actual data: 1st byte, 2nd byte ... - */ - len = t->len * 2; - rx_buf = kmalloc(len, GFP_KERNEL); - if (!rx_buf) - return -ENOMEM; - } else { - len = t->len; - rx_buf = t->rx_buf; - } - /* prepare dummy transfer to generate SPI clocks */ dummy = kzalloc(len, GFP_KERNEL); if (!dummy) { @@ -697,11 +632,6 @@ end: end_dummy_mapped: rspi_dma_unmap_sg(&sg_dummy, rspi->chan_tx, DMA_TO_DEVICE); end_nomap: - if (rspi->dma_width_16bit) { - if (!ret) - rspi_memory_from_8bit(t->rx_buf, rx_buf, t->len); - kfree(rx_buf); - } kfree(dummy); return ret; @@ -1073,8 +1003,6 @@ static int rspi_request_dma(struct rspi_data *rspi, if (!res || !rspi_pd) return 0; /* The driver assumes no error. */ - rspi->dma_width_16bit = rspi_pd->dma_width_16bit; - /* If the module receives data by DMAC, it also needs TX DMAC */ if (rspi_pd->dma_rx_id && rspi_pd->dma_tx_id) { dma_cap_zero(mask); diff --git a/include/linux/spi/rspi.h b/include/linux/spi/rspi.h index a25bd6f65e7f..e546b2ceb623 100644 --- a/include/linux/spi/rspi.h +++ b/include/linux/spi/rspi.h @@ -25,8 +25,6 @@ struct rspi_plat_data { unsigned int dma_tx_id; unsigned int dma_rx_id; - unsigned dma_width_16bit:1; /* DMAC read/write width = 16-bit */ - u16 num_chipselect; }; -- cgit v1.2.3 From b42e03596db3d45980c976c8124fdc323f031dc4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:06 +0200 Subject: spi: rspi: Use core SPI_MASTER_MUST_[RT]X handling RSPI needs dummy transfers to generate the SPI clock on receive. RSPI-RZ and QSPI always do both transmit and receive. Use the SPI core SPI_MASTER_MUST_RX/SPI_MASTER_MUST_TX infrastructure instead of checking for the presence of buffers and providing dummy data ourselves (for PIO), or providing a dummy buffer (for DMA). rspi_receive_dma() now provides full duplex DMA transfers on RSPI, and is renamed to rspi_send_receive_dma(). As the SPI core will always provide a TX buffer, the logic to choose between DMA send and DMA send/receive in rspi_transfer_one() now has to check for the presence of an RX buffer. Likewise for the DMA availability tests in rspi_is_dma(). The buffer tests in qspi_transfer_one() are now always true, so they're removed. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 132 ++++++++++++++++++++++--------------------------- 1 file changed, 58 insertions(+), 74 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 3bd06fd9af47..ece8f6037943 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -183,8 +183,6 @@ #define SPBFCR_TXTRG_MASK 0x30 /* Transmit Buffer Data Triggering Number */ #define SPBFCR_RXTRG_MASK 0x07 /* Receive Buffer Data Triggering Number */ -#define DUMMY_DATA 0x00 - struct rspi_data { void __iomem *addr; u32 max_speed_hz; @@ -252,6 +250,7 @@ struct spi_ops { int (*transfer_one)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer); u16 mode_bits; + u16 flags; }; /* @@ -552,42 +551,38 @@ static void qspi_receive_init(const struct rspi_data *rspi) rspi_write8(rspi, 0, QSPI_SPBFCR); } -static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) +static int rspi_send_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) { - struct scatterlist sg, sg_dummy; - void *dummy = NULL, *rx_buf = t->rx_buf; - struct dma_async_tx_descriptor *desc, *desc_dummy; + struct scatterlist sg_rx, sg_tx; + const void *tx_buf = t->tx_buf; + void *rx_buf = t->rx_buf; + struct dma_async_tx_descriptor *desc_tx, *desc_rx; unsigned int len = t->len; int ret = 0; - /* prepare dummy transfer to generate SPI clocks */ - dummy = kzalloc(len, GFP_KERNEL); - if (!dummy) { - ret = -ENOMEM; - goto end_nomap; - } - if (!rspi_dma_map_sg(&sg_dummy, dummy, len, rspi->chan_tx, - DMA_TO_DEVICE)) { - ret = -EFAULT; - goto end_nomap; - } - desc_dummy = dmaengine_prep_slave_sg(rspi->chan_tx, &sg_dummy, 1, + /* prepare transmit transfer */ + if (!rspi_dma_map_sg(&sg_tx, tx_buf, len, rspi->chan_tx, + DMA_TO_DEVICE)) + return -EFAULT; + + desc_tx = dmaengine_prep_slave_sg(rspi->chan_tx, &sg_tx, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc_dummy) { + if (!desc_tx) { ret = -EIO; - goto end_dummy_mapped; + goto end_tx_mapped; } /* prepare receive transfer */ - if (!rspi_dma_map_sg(&sg, rx_buf, len, rspi->chan_rx, + if (!rspi_dma_map_sg(&sg_rx, rx_buf, len, rspi->chan_rx, DMA_FROM_DEVICE)) { ret = -EFAULT; - goto end_dummy_mapped; + goto end_tx_mapped; } - desc = dmaengine_prep_slave_sg(rspi->chan_rx, &sg, 1, DMA_FROM_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { + desc_rx = dmaengine_prep_slave_sg(rspi->chan_rx, &sg_rx, 1, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_rx) { ret = -EIO; goto end; } @@ -606,13 +601,13 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) rspi_enable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE); rspi->dma_callbacked = 0; - desc->callback = rspi_dma_complete; - desc->callback_param = rspi; - dmaengine_submit(desc); + desc_rx->callback = rspi_dma_complete; + desc_rx->callback_param = rspi; + dmaengine_submit(desc_rx); dma_async_issue_pending(rspi->chan_rx); - desc_dummy->callback = NULL; /* No callback */ - dmaengine_submit(desc_dummy); + desc_tx->callback = NULL; /* No callback */ + dmaengine_submit(desc_tx); dma_async_issue_pending(rspi->chan_tx); ret = wait_event_interruptible_timeout(rspi->wait, @@ -628,21 +623,19 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) enable_irq(rspi->rx_irq); end: - rspi_dma_unmap_sg(&sg, rspi->chan_rx, DMA_FROM_DEVICE); -end_dummy_mapped: - rspi_dma_unmap_sg(&sg_dummy, rspi->chan_tx, DMA_TO_DEVICE); -end_nomap: - kfree(dummy); - + rspi_dma_unmap_sg(&sg_rx, rspi->chan_rx, DMA_FROM_DEVICE); +end_tx_mapped: + rspi_dma_unmap_sg(&sg_tx, rspi->chan_tx, DMA_TO_DEVICE); return ret; } static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t) { - if (t->tx_buf && rspi->chan_tx) - return 1; /* If the module receives data by DMAC, it also needs TX DMAC */ - if (t->rx_buf && rspi->chan_tx && rspi->chan_rx) + if (t->rx_buf) + return rspi->chan_tx && rspi->chan_rx; + + if (rspi->chan_tx) return 1; return 0; @@ -654,7 +647,7 @@ static int rspi_transfer_out_in(struct rspi_data *rspi, int remain = xfer->len, ret; const u8 *tx_buf = xfer->tx_buf; u8 *rx_buf = xfer->rx_buf; - u8 spcr, data; + u8 spcr; spcr = rspi_read8(rspi, RSPI_SPCR); if (rx_buf) { @@ -666,8 +659,7 @@ static int rspi_transfer_out_in(struct rspi_data *rspi, rspi_write8(rspi, spcr, RSPI_SPCR); while (remain > 0) { - data = tx_buf ? *tx_buf++ : DUMMY_DATA; - ret = rspi_data_out(rspi, data); + ret = rspi_data_out(rspi, *tx_buf++); if (ret < 0) return ret; if (rx_buf) { @@ -689,20 +681,14 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct rspi_data *rspi = spi_master_get_devdata(master); - int ret; if (!rspi_is_dma(rspi, xfer)) return rspi_transfer_out_in(rspi, xfer); - if (xfer->tx_buf) { - ret = rspi_send_dma(rspi, xfer); - if (ret < 0) - return ret; - } if (xfer->rx_buf) - return rspi_receive_dma(rspi, xfer); - - return 0; + return rspi_send_receive_dma(rspi, xfer); + else + return rspi_send_dma(rspi, xfer); } static int rspi_rz_transfer_out_in(struct rspi_data *rspi, @@ -711,17 +697,14 @@ static int rspi_rz_transfer_out_in(struct rspi_data *rspi, int remain = xfer->len, ret; const u8 *tx_buf = xfer->tx_buf; u8 *rx_buf = xfer->rx_buf; - u8 data; rspi_rz_receive_init(rspi); while (remain > 0) { - data = tx_buf ? *tx_buf++ : DUMMY_DATA; - ret = rspi_data_out_in(rspi, data); + ret = rspi_data_out_in(rspi, *tx_buf++); if (ret < 0) return ret; - if (rx_buf) - *rx_buf++ = ret; + *rx_buf++ = ret; remain--; } @@ -746,17 +729,14 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, int remain = xfer->len, ret; const u8 *tx_buf = xfer->tx_buf; u8 *rx_buf = xfer->rx_buf; - u8 data; qspi_receive_init(rspi); while (remain > 0) { - data = tx_buf ? *tx_buf++ : DUMMY_DATA; - ret = rspi_data_out_in(rspi, data); + ret = rspi_data_out_in(rspi, *tx_buf++); if (ret < 0) return ret; - if (rx_buf) - *rx_buf++ = ret; + *rx_buf++ = ret; remain--; } @@ -807,10 +787,10 @@ static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, if (spi->mode & SPI_LOOP) { return qspi_transfer_out_in(rspi, xfer); - } else if (xfer->tx_buf && xfer->tx_nbits > SPI_NBITS_SINGLE) { + } else if (xfer->tx_nbits > SPI_NBITS_SINGLE) { /* Quad or Dual SPI Write */ return qspi_transfer_out(rspi, xfer); - } else if (xfer->rx_buf && xfer->rx_nbits > SPI_NBITS_SINGLE) { + } else if (xfer->rx_nbits > SPI_NBITS_SINGLE) { /* Quad or Dual SPI Read */ return qspi_transfer_in(rspi, xfer); } else { @@ -1061,23 +1041,26 @@ static int rspi_remove(struct platform_device *pdev) } static const struct spi_ops rspi_ops = { - .set_config_register = rspi_set_config_register, - .transfer_one = rspi_transfer_one, - .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, + .set_config_register = rspi_set_config_register, + .transfer_one = rspi_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, + .flags = SPI_MASTER_MUST_TX, }; static const struct spi_ops rspi_rz_ops = { - .set_config_register = rspi_rz_set_config_register, - .transfer_one = rspi_rz_transfer_one, - .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, + .set_config_register = rspi_rz_set_config_register, + .transfer_one = rspi_rz_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, + .flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX, }; static const struct spi_ops qspi_ops = { - .set_config_register = qspi_set_config_register, - .transfer_one = qspi_transfer_one, - .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP | - SPI_TX_DUAL | SPI_TX_QUAD | - SPI_RX_DUAL | SPI_RX_QUAD, + .set_config_register = qspi_set_config_register, + .transfer_one = qspi_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP | + SPI_TX_DUAL | SPI_TX_QUAD | + SPI_RX_DUAL | SPI_RX_QUAD, + .flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX, }; #ifdef CONFIG_OF @@ -1197,6 +1180,7 @@ static int rspi_probe(struct platform_device *pdev) master->prepare_message = rspi_prepare_message; master->unprepare_message = rspi_unprepare_message; master->mode_bits = ops->mode_bits; + master->flags = ops->flags; master->dev.of_node = pdev->dev.of_node; ret = platform_get_irq_byname(pdev, "rx"); -- cgit v1.2.3 From 6837b8e91d2a080293c30d5fe42d9692390091fa Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:07 +0200 Subject: spi: rspi: Extract rspi_pio_transfer() The various PIO loops are very similar. Consolidate into a single function rspi_pio_transfer(). Both buffer pointers can be NULL, as RSPI supports TX-only mode, and Dual/Quad SPI Transfers are unidirectional. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 95 ++++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 62 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index ece8f6037943..fdbd46d0c570 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -438,15 +438,24 @@ static int rspi_data_in(struct rspi_data *rspi) return data; } -static int rspi_data_out_in(struct rspi_data *rspi, u8 data) +static int rspi_pio_transfer(struct rspi_data *rspi, const u8 *tx, u8 *rx, + unsigned int n) { - int ret; - - ret = rspi_data_out(rspi, data); - if (ret < 0) - return ret; + while (n-- > 0) { + if (tx) { + int ret = rspi_data_out(rspi, *tx++); + if (ret < 0) + return ret; + } + if (rx) { + int ret = rspi_data_in(rspi); + if (ret < 0) + return ret; + *rx++ = ret; + } + } - return rspi_data_in(rspi); + return 0; } static void rspi_dma_complete(void *arg) @@ -644,13 +653,11 @@ static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t) static int rspi_transfer_out_in(struct rspi_data *rspi, struct spi_transfer *xfer) { - int remain = xfer->len, ret; - const u8 *tx_buf = xfer->tx_buf; - u8 *rx_buf = xfer->rx_buf; u8 spcr; + int ret; spcr = rspi_read8(rspi, RSPI_SPCR); - if (rx_buf) { + if (xfer->rx_buf) { rspi_receive_init(rspi); spcr &= ~SPCR_TXMD; } else { @@ -658,18 +665,9 @@ static int rspi_transfer_out_in(struct rspi_data *rspi, } rspi_write8(rspi, spcr, RSPI_SPCR); - while (remain > 0) { - ret = rspi_data_out(rspi, *tx_buf++); - if (ret < 0) - return ret; - if (rx_buf) { - ret = rspi_data_in(rspi); - if (ret < 0) - return ret; - *rx_buf++ = ret; - } - remain--; - } + ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); + if (ret < 0) + return ret; /* Wait for the last transmission */ rspi_wait_for_tx_empty(rspi); @@ -694,19 +692,13 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, static int rspi_rz_transfer_out_in(struct rspi_data *rspi, struct spi_transfer *xfer) { - int remain = xfer->len, ret; - const u8 *tx_buf = xfer->tx_buf; - u8 *rx_buf = xfer->rx_buf; + int ret; rspi_rz_receive_init(rspi); - while (remain > 0) { - ret = rspi_data_out_in(rspi, *tx_buf++); - if (ret < 0) - return ret; - *rx_buf++ = ret; - remain--; - } + ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); + if (ret < 0) + return ret; /* Wait for the last transmission */ rspi_wait_for_tx_empty(rspi); @@ -726,19 +718,13 @@ static int rspi_rz_transfer_one(struct spi_master *master, static int qspi_transfer_out_in(struct rspi_data *rspi, struct spi_transfer *xfer) { - int remain = xfer->len, ret; - const u8 *tx_buf = xfer->tx_buf; - u8 *rx_buf = xfer->rx_buf; + int ret; qspi_receive_init(rspi); - while (remain > 0) { - ret = rspi_data_out_in(rspi, *tx_buf++); - if (ret < 0) - return ret; - *rx_buf++ = ret; - remain--; - } + ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); + if (ret < 0) + return ret; /* Wait for the last transmission */ rspi_wait_for_tx_empty(rspi); @@ -748,15 +734,11 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) { - const u8 *buf = xfer->tx_buf; - unsigned int i; int ret; - for (i = 0; i < xfer->len; i++) { - ret = rspi_data_out(rspi, *buf++); - if (ret < 0) - return ret; - } + ret = rspi_pio_transfer(rspi, xfer->tx_buf, NULL, xfer->len); + if (ret < 0) + return ret; /* Wait for the last transmission */ rspi_wait_for_tx_empty(rspi); @@ -766,18 +748,7 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer) { - u8 *buf = xfer->rx_buf; - unsigned int i; - int ret; - - for (i = 0; i < xfer->len; i++) { - ret = rspi_data_in(rspi); - if (ret < 0) - return ret; - *buf++ = ret; - } - - return 0; + return rspi_pio_transfer(rspi, NULL, xfer->rx_buf, xfer->len); } static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, -- cgit v1.2.3 From 27e105a6006b8ce1b55709c5e24f63959981475d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:08 +0200 Subject: spi: rspi: Don't consider DMA configuration failures fatal Fall back to PIO if DMA configuration failed. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index fdbd46d0c570..94a99ec7d989 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1190,10 +1190,8 @@ static int rspi_probe(struct platform_device *pdev) } ret = rspi_request_dma(rspi, pdev); - if (ret < 0) { - dev_err(&pdev->dev, "rspi_request_dma failed.\n"); - goto error3; - } + if (ret < 0) + dev_warn(&pdev->dev, "DMA not available, using PIO\n"); ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { -- cgit v1.2.3 From 65bf220571f131a7c3a564a88793bd0f16fd7c96 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:09 +0200 Subject: spi: rspi: Extract rspi_request_dma_chan() Setup of the receive and transmit DMA channels is very similar, so let's consolidate. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 89 +++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 94a99ec7d989..0a7a2d618f0f 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -942,52 +942,73 @@ static irqreturn_t rspi_irq_tx(int irq, void *_sr) return 0; } -static int rspi_request_dma(struct rspi_data *rspi, - struct platform_device *pdev) +static struct dma_chan *rspi_request_dma_chan(struct device *dev, + enum dma_transfer_direction dir, + unsigned int id, + dma_addr_t port_addr) { - const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dma_cap_mask_t mask; + struct dma_chan *chan; struct dma_slave_config cfg; int ret; + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, shdma_chan_filter, + (void *)(unsigned long)id); + if (!chan) { + dev_warn(dev, "dma_request_channel failed\n"); + return NULL; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.slave_id = id; + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) + cfg.dst_addr = port_addr; + else + cfg.src_addr = port_addr; + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_warn(dev, "dmaengine_slave_config failed %d\n", ret); + dma_release_channel(chan); + return NULL; + } + + return chan; +} + +static int rspi_request_dma(struct rspi_data *rspi, + struct platform_device *pdev) +{ + const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res || !rspi_pd) return 0; /* The driver assumes no error. */ /* If the module receives data by DMAC, it also needs TX DMAC */ if (rspi_pd->dma_rx_id && rspi_pd->dma_tx_id) { - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - rspi->chan_rx = dma_request_channel(mask, shdma_chan_filter, - (void *)rspi_pd->dma_rx_id); - if (rspi->chan_rx) { - cfg.slave_id = rspi_pd->dma_rx_id; - cfg.direction = DMA_DEV_TO_MEM; - cfg.dst_addr = 0; - cfg.src_addr = res->start + RSPI_SPDR; - ret = dmaengine_slave_config(rspi->chan_rx, &cfg); - if (!ret) - dev_info(&pdev->dev, "Use DMA when rx.\n"); - else - return ret; - } + rspi->chan_rx = rspi_request_dma_chan(&pdev->dev, + DMA_DEV_TO_MEM, + rspi_pd->dma_rx_id, + res->start + RSPI_SPDR); + if (!rspi->chan_rx) + return -ENODEV; + + dev_info(&pdev->dev, "Use DMA when rx.\n"); } if (rspi_pd->dma_tx_id) { - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - rspi->chan_tx = dma_request_channel(mask, shdma_chan_filter, - (void *)rspi_pd->dma_tx_id); - if (rspi->chan_tx) { - cfg.slave_id = rspi_pd->dma_tx_id; - cfg.direction = DMA_MEM_TO_DEV; - cfg.dst_addr = res->start + RSPI_SPDR; - cfg.src_addr = 0; - ret = dmaengine_slave_config(rspi->chan_tx, &cfg); - if (!ret) - dev_info(&pdev->dev, "Use DMA when tx\n"); - else - return ret; - } + rspi->chan_tx = rspi_request_dma_chan(&pdev->dev, + DMA_MEM_TO_DEV, + rspi_pd->dma_tx_id, + res->start + RSPI_SPDR); + if (!rspi->chan_tx) + return -ENODEV; + + dev_info(&pdev->dev, "Use DMA when tx\n"); } return 0; -- cgit v1.2.3 From fcdc49ae53dc8cfc1a7758607bca68935f568ca2 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:10 +0200 Subject: spi: rspi: Remove unneeded resource test in DMA setup The resource is know to exist, as rspi_probe() already mapped it. Remove the test, and just pass the resource. Pass the device pointer instead of the platform device pointer, as the latter is no longer needed. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 0a7a2d618f0f..1ec51cb00203 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -980,35 +980,32 @@ static struct dma_chan *rspi_request_dma_chan(struct device *dev, return chan; } -static int rspi_request_dma(struct rspi_data *rspi, - struct platform_device *pdev) +static int rspi_request_dma(struct device *dev, struct rspi_data *rspi, + const struct resource *res) { - const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + const struct rspi_plat_data *rspi_pd = dev_get_platdata(dev); - if (!res || !rspi_pd) + if (!rspi_pd) return 0; /* The driver assumes no error. */ /* If the module receives data by DMAC, it also needs TX DMAC */ if (rspi_pd->dma_rx_id && rspi_pd->dma_tx_id) { - rspi->chan_rx = rspi_request_dma_chan(&pdev->dev, - DMA_DEV_TO_MEM, + rspi->chan_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, rspi_pd->dma_rx_id, res->start + RSPI_SPDR); if (!rspi->chan_rx) return -ENODEV; - dev_info(&pdev->dev, "Use DMA when rx.\n"); + dev_info(dev, "Use DMA when rx.\n"); } if (rspi_pd->dma_tx_id) { - rspi->chan_tx = rspi_request_dma_chan(&pdev->dev, - DMA_MEM_TO_DEV, + rspi->chan_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, rspi_pd->dma_tx_id, res->start + RSPI_SPDR); if (!rspi->chan_tx) return -ENODEV; - dev_info(&pdev->dev, "Use DMA when tx\n"); + dev_info(dev, "Use DMA when tx\n"); } return 0; @@ -1210,7 +1207,7 @@ static int rspi_probe(struct platform_device *pdev) goto error2; } - ret = rspi_request_dma(rspi, pdev); + ret = rspi_request_dma(&pdev->dev, rspi, res); if (ret < 0) dev_warn(&pdev->dev, "DMA not available, using PIO\n"); -- cgit v1.2.3 From 5f338d0ce0b4c9e6c554b92cfb288789a41bfbc1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:11 +0200 Subject: spi: rspi: SPI DMA core needs both RX and TX DMA to function The SPI DMA core framework needs both RX and TX DMA to function. As a preparation for converting the driver to use this framework, fall back to PIO if no DMA channel or only one DMA channel is available. This affects only RSPI, which could do DMA transfers for TX-only before. RSPI-RZ and QSPI (at least for Single SPI Transfers) will need both RX and TX DMA anyway. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 1ec51cb00203..7b993f75a3cf 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -640,10 +640,6 @@ end_tx_mapped: static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t) { - /* If the module receives data by DMAC, it also needs TX DMAC */ - if (t->rx_buf) - return rspi->chan_tx && rspi->chan_rx; - if (rspi->chan_tx) return 1; @@ -985,29 +981,25 @@ static int rspi_request_dma(struct device *dev, struct rspi_data *rspi, { const struct rspi_plat_data *rspi_pd = dev_get_platdata(dev); - if (!rspi_pd) + if (!rspi_pd || !rspi_pd->dma_rx_id || !rspi_pd->dma_tx_id) return 0; /* The driver assumes no error. */ - /* If the module receives data by DMAC, it also needs TX DMAC */ - if (rspi_pd->dma_rx_id && rspi_pd->dma_tx_id) { - rspi->chan_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, - rspi_pd->dma_rx_id, - res->start + RSPI_SPDR); - if (!rspi->chan_rx) - return -ENODEV; + rspi->chan_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, + rspi_pd->dma_rx_id, + res->start + RSPI_SPDR); + if (!rspi->chan_rx) + return -ENODEV; - dev_info(dev, "Use DMA when rx.\n"); - } - if (rspi_pd->dma_tx_id) { - rspi->chan_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, - rspi_pd->dma_tx_id, - res->start + RSPI_SPDR); - if (!rspi->chan_tx) - return -ENODEV; - - dev_info(dev, "Use DMA when tx\n"); + rspi->chan_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, + rspi_pd->dma_tx_id, + res->start + RSPI_SPDR); + if (!rspi->chan_tx) { + dma_release_channel(rspi->chan_rx); + rspi->chan_rx = NULL; + return -ENODEV; } + dev_info(dev, "DMA available"); return 0; } -- cgit v1.2.3 From 2f777ec91aa0623e058c43dd4aaf0b3325d3c3e8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:12 +0200 Subject: spi: rspi: Use SPI core DMA mapping framework Use the SPI core DMA mapping framework instead of our own. If available, DMA is used for transfers larger than the FIFO size (8 or 32 bytes). Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 140 ++++++++++++++++++------------------------------- 1 file changed, 50 insertions(+), 90 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 7b993f75a3cf..753ac7bdfd55 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -195,10 +195,6 @@ struct rspi_data { int rx_irq, tx_irq; const struct spi_ops *ops; - /* for dmaengine */ - struct dma_chan *chan_tx; - struct dma_chan *chan_rx; - unsigned dma_callbacked:1; unsigned byte_access:1; }; @@ -251,6 +247,7 @@ struct spi_ops { struct spi_transfer *xfer); u16 mode_bits; u16 flags; + u16 fifo_size; }; /* @@ -466,39 +463,16 @@ static void rspi_dma_complete(void *arg) wake_up_interruptible(&rspi->wait); } -static int rspi_dma_map_sg(struct scatterlist *sg, const void *buf, - unsigned len, struct dma_chan *chan, - enum dma_transfer_direction dir) -{ - sg_init_table(sg, 1); - sg_set_buf(sg, buf, len); - sg_dma_len(sg) = len; - return dma_map_sg(chan->device->dev, sg, 1, dir); -} - -static void rspi_dma_unmap_sg(struct scatterlist *sg, struct dma_chan *chan, - enum dma_transfer_direction dir) -{ - dma_unmap_sg(chan->device->dev, sg, 1, dir); -} - static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) { - struct scatterlist sg; - const void *buf = t->tx_buf; struct dma_async_tx_descriptor *desc; - unsigned int len = t->len; - int ret = 0; - - if (!rspi_dma_map_sg(&sg, buf, len, rspi->chan_tx, DMA_TO_DEVICE)) - return -EFAULT; + int ret; - desc = dmaengine_prep_slave_sg(rspi->chan_tx, &sg, 1, DMA_TO_DEVICE, + desc = dmaengine_prep_slave_sg(rspi->master->dma_tx, t->tx_sg.sgl, + t->tx_sg.nents, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - ret = -EIO; - goto end; - } + if (!desc) + return -EIO; /* * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be @@ -513,7 +487,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) desc->callback = rspi_dma_complete; desc->callback_param = rspi; dmaengine_submit(desc); - dma_async_issue_pending(rspi->chan_tx); + dma_async_issue_pending(rspi->master->dma_tx); ret = wait_event_interruptible_timeout(rspi->wait, rspi->dma_callbacked, HZ); @@ -524,9 +498,6 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) rspi_disable_irq(rspi, SPCR_SPTIE); enable_irq(rspi->tx_irq); - -end: - rspi_dma_unmap_sg(&sg, rspi->chan_tx, DMA_TO_DEVICE); return ret; } @@ -562,39 +533,22 @@ static void qspi_receive_init(const struct rspi_data *rspi) static int rspi_send_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) { - struct scatterlist sg_rx, sg_tx; - const void *tx_buf = t->tx_buf; - void *rx_buf = t->rx_buf; struct dma_async_tx_descriptor *desc_tx, *desc_rx; - unsigned int len = t->len; - int ret = 0; + int ret; /* prepare transmit transfer */ - if (!rspi_dma_map_sg(&sg_tx, tx_buf, len, rspi->chan_tx, - DMA_TO_DEVICE)) - return -EFAULT; - - desc_tx = dmaengine_prep_slave_sg(rspi->chan_tx, &sg_tx, 1, - DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc_tx) { - ret = -EIO; - goto end_tx_mapped; - } + desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx, t->tx_sg.sgl, + t->tx_sg.nents, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_tx) + return -EIO; /* prepare receive transfer */ - if (!rspi_dma_map_sg(&sg_rx, rx_buf, len, rspi->chan_rx, - DMA_FROM_DEVICE)) { - ret = -EFAULT; - goto end_tx_mapped; - - } - desc_rx = dmaengine_prep_slave_sg(rspi->chan_rx, &sg_rx, 1, - DMA_FROM_DEVICE, + desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx, t->rx_sg.sgl, + t->rx_sg.nents, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc_rx) { - ret = -EIO; - goto end; - } + if (!desc_rx) + return -EIO; rspi_receive_init(rspi); @@ -613,11 +567,11 @@ static int rspi_send_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) desc_rx->callback = rspi_dma_complete; desc_rx->callback_param = rspi; dmaengine_submit(desc_rx); - dma_async_issue_pending(rspi->chan_rx); + dma_async_issue_pending(rspi->master->dma_rx); desc_tx->callback = NULL; /* No callback */ dmaengine_submit(desc_tx); - dma_async_issue_pending(rspi->chan_tx); + dma_async_issue_pending(rspi->master->dma_tx); ret = wait_event_interruptible_timeout(rspi->wait, rspi->dma_callbacked, HZ); @@ -631,19 +585,21 @@ static int rspi_send_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) if (rspi->rx_irq != rspi->tx_irq) enable_irq(rspi->rx_irq); -end: - rspi_dma_unmap_sg(&sg_rx, rspi->chan_rx, DMA_FROM_DEVICE); -end_tx_mapped: - rspi_dma_unmap_sg(&sg_tx, rspi->chan_tx, DMA_TO_DEVICE); return ret; } -static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t) +static bool __rspi_can_dma(const struct rspi_data *rspi, + const struct spi_transfer *xfer) { - if (rspi->chan_tx) - return 1; + return xfer->len > rspi->ops->fifo_size; +} - return 0; +static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + + return __rspi_can_dma(rspi, xfer); } static int rspi_transfer_out_in(struct rspi_data *rspi, @@ -676,7 +632,7 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, { struct rspi_data *rspi = spi_master_get_devdata(master); - if (!rspi_is_dma(rspi, xfer)) + if (!master->can_dma || !__rspi_can_dma(rspi, xfer)) return rspi_transfer_out_in(rspi, xfer); if (xfer->rx_buf) @@ -976,7 +932,7 @@ static struct dma_chan *rspi_request_dma_chan(struct device *dev, return chan; } -static int rspi_request_dma(struct device *dev, struct rspi_data *rspi, +static int rspi_request_dma(struct device *dev, struct spi_master *master, const struct resource *res) { const struct rspi_plat_data *rspi_pd = dev_get_platdata(dev); @@ -984,31 +940,32 @@ static int rspi_request_dma(struct device *dev, struct rspi_data *rspi, if (!rspi_pd || !rspi_pd->dma_rx_id || !rspi_pd->dma_tx_id) return 0; /* The driver assumes no error. */ - rspi->chan_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, - rspi_pd->dma_rx_id, - res->start + RSPI_SPDR); - if (!rspi->chan_rx) + master->dma_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, + rspi_pd->dma_rx_id, + res->start + RSPI_SPDR); + if (!master->dma_rx) return -ENODEV; - rspi->chan_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, - rspi_pd->dma_tx_id, - res->start + RSPI_SPDR); - if (!rspi->chan_tx) { - dma_release_channel(rspi->chan_rx); - rspi->chan_rx = NULL; + master->dma_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, + rspi_pd->dma_tx_id, + res->start + RSPI_SPDR); + if (!master->dma_tx) { + dma_release_channel(master->dma_rx); + master->dma_rx = NULL; return -ENODEV; } + master->can_dma = rspi_can_dma; dev_info(dev, "DMA available"); return 0; } static void rspi_release_dma(struct rspi_data *rspi) { - if (rspi->chan_tx) - dma_release_channel(rspi->chan_tx); - if (rspi->chan_rx) - dma_release_channel(rspi->chan_rx); + if (rspi->master->dma_tx) + dma_release_channel(rspi->master->dma_tx); + if (rspi->master->dma_rx) + dma_release_channel(rspi->master->dma_rx); } static int rspi_remove(struct platform_device *pdev) @@ -1026,6 +983,7 @@ static const struct spi_ops rspi_ops = { .transfer_one = rspi_transfer_one, .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, .flags = SPI_MASTER_MUST_TX, + .fifo_size = 8, }; static const struct spi_ops rspi_rz_ops = { @@ -1033,6 +991,7 @@ static const struct spi_ops rspi_rz_ops = { .transfer_one = rspi_rz_transfer_one, .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, .flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX, + .fifo_size = 8, /* 8 for TX, 32 for RX */ }; static const struct spi_ops qspi_ops = { @@ -1042,6 +1001,7 @@ static const struct spi_ops qspi_ops = { SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD, .flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX, + .fifo_size = 32, }; #ifdef CONFIG_OF @@ -1199,7 +1159,7 @@ static int rspi_probe(struct platform_device *pdev) goto error2; } - ret = rspi_request_dma(&pdev->dev, rspi, res); + ret = rspi_request_dma(&pdev->dev, master, res); if (ret < 0) dev_warn(&pdev->dev, "DMA not available, using PIO\n"); -- cgit v1.2.3 From 8393fa787bf63c05cb6f9cf0a58ba1ea213c3f01 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:13 +0200 Subject: spi: rspi: Move RSPI-specific setup out of DMA routines Refactor RSPI (on SH) DMA handling to make it reusable for other RSPI implementations: - Call the DMA routines after configuring the TX Mode bit and after calling rspi_receive_init(), so these RSPI-specific operations can be removed from the DMA routines, - Absorb rspi_transfer_out_in() into rspi_transfer_one(). Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 753ac7bdfd55..3dea8adfdcf3 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -480,7 +480,6 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) */ disable_irq(rspi->tx_irq); - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, RSPI_SPCR); rspi_enable_irq(rspi, SPCR_SPTIE); rspi->dma_callbacked = 0; @@ -550,8 +549,6 @@ static int rspi_send_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) if (!desc_rx) return -EIO; - rspi_receive_init(rspi); - /* * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be * called. So, this driver disables the IRQ while DMA transfer. @@ -560,7 +557,6 @@ static int rspi_send_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) if (rspi->rx_irq != rspi->tx_irq) disable_irq(rspi->rx_irq); - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD, RSPI_SPCR); rspi_enable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE); rspi->dma_callbacked = 0; @@ -602,9 +598,10 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi, return __rspi_can_dma(rspi, xfer); } -static int rspi_transfer_out_in(struct rspi_data *rspi, - struct spi_transfer *xfer) +static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) { + struct rspi_data *rspi = spi_master_get_devdata(master); u8 spcr; int ret; @@ -617,6 +614,13 @@ static int rspi_transfer_out_in(struct rspi_data *rspi, } rspi_write8(rspi, spcr, RSPI_SPCR); + if (master->can_dma && __rspi_can_dma(rspi, xfer)) { + if (xfer->rx_buf) + return rspi_send_receive_dma(rspi, xfer); + else + return rspi_send_dma(rspi, xfer); + } + ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); if (ret < 0) return ret; @@ -627,20 +631,6 @@ static int rspi_transfer_out_in(struct rspi_data *rspi, return 0; } -static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, - struct spi_transfer *xfer) -{ - struct rspi_data *rspi = spi_master_get_devdata(master); - - if (!master->can_dma || !__rspi_can_dma(rspi, xfer)) - return rspi_transfer_out_in(rspi, xfer); - - if (xfer->rx_buf) - return rspi_send_receive_dma(rspi, xfer); - else - return rspi_send_dma(rspi, xfer); -} - static int rspi_rz_transfer_out_in(struct rspi_data *rspi, struct spi_transfer *xfer) { -- cgit v1.2.3 From e4b52dc4625ee739195189d40c6ddc7d55ddf312 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:14 +0200 Subject: spi: rspi: Pass sg_tables instead of spi_tranfer to rspi_*_dma() The DMA routines only need access to the scatter-gather tables inside the spi_transfer structures, hence just pass those. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 3dea8adfdcf3..bfa5e7e5df5a 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -463,13 +463,13 @@ static void rspi_dma_complete(void *arg) wake_up_interruptible(&rspi->wait); } -static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) +static int rspi_send_dma(struct rspi_data *rspi, struct sg_table *tx) { struct dma_async_tx_descriptor *desc; int ret; - desc = dmaengine_prep_slave_sg(rspi->master->dma_tx, t->tx_sg.sgl, - t->tx_sg.nents, DMA_TO_DEVICE, + desc = dmaengine_prep_slave_sg(rspi->master->dma_tx, tx->sgl, + tx->nents, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) return -EIO; @@ -530,21 +530,22 @@ static void qspi_receive_init(const struct rspi_data *rspi) rspi_write8(rspi, 0, QSPI_SPBFCR); } -static int rspi_send_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) +static int rspi_send_receive_dma(struct rspi_data *rspi, struct sg_table *tx, + struct sg_table *rx) { struct dma_async_tx_descriptor *desc_tx, *desc_rx; int ret; /* prepare transmit transfer */ - desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx, t->tx_sg.sgl, - t->tx_sg.nents, DMA_TO_DEVICE, + desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx, tx->sgl, + tx->nents, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) return -EIO; /* prepare receive transfer */ - desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx, t->rx_sg.sgl, - t->rx_sg.nents, DMA_FROM_DEVICE, + desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx, rx->sgl, + rx->nents, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_rx) return -EIO; @@ -616,9 +617,10 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, if (master->can_dma && __rspi_can_dma(rspi, xfer)) { if (xfer->rx_buf) - return rspi_send_receive_dma(rspi, xfer); + return rspi_send_receive_dma(rspi, &xfer->tx_sg, + &xfer->rx_sg); else - return rspi_send_dma(rspi, xfer); + return rspi_send_dma(rspi, &xfer->tx_sg); } ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); -- cgit v1.2.3 From c52fb6d63425248bd4152451a2cc74b7df8fa989 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:15 +0200 Subject: spi: rspi: Merge rspi_*_dma() into rspi_dma_transfer() rspi_send_dma() and rspi_send_receive_dma() are very similar. Consolidate into a single function rspi_dma_transfer(), and add missing checks for dmaengine_submit() failures. Both sg_table pointer parameters can be NULL, as RSPI supports TX-only mode, and unidirectional DMA transfers will also be needed later for Dual/Quad DMA support. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 139 ++++++++++++++++++++++--------------------------- 1 file changed, 61 insertions(+), 78 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index bfa5e7e5df5a..c77cfe654b0e 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -463,30 +463,67 @@ static void rspi_dma_complete(void *arg) wake_up_interruptible(&rspi->wait); } -static int rspi_send_dma(struct rspi_data *rspi, struct sg_table *tx) +static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx, + struct sg_table *rx) { - struct dma_async_tx_descriptor *desc; + struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL; + u8 irq_mask = 0; + unsigned int other_irq = 0; + dma_cookie_t cookie; int ret; - desc = dmaengine_prep_slave_sg(rspi->master->dma_tx, tx->sgl, - tx->nents, DMA_TO_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EIO; + if (tx) { + desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx, + tx->sgl, tx->nents, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_tx) + return -EIO; + + irq_mask |= SPCR_SPTIE; + } + if (rx) { + desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx, + rx->sgl, rx->nents, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_rx) + return -EIO; + + irq_mask |= SPCR_SPRIE; + } /* - * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be + * DMAC needs SPxIE, but if SPxIE is set, the IRQ routine will be * called. So, this driver disables the IRQ while DMA transfer. */ - disable_irq(rspi->tx_irq); + if (tx) + disable_irq(other_irq = rspi->tx_irq); + if (rx && rspi->rx_irq != other_irq) + disable_irq(rspi->rx_irq); - rspi_enable_irq(rspi, SPCR_SPTIE); + rspi_enable_irq(rspi, irq_mask); rspi->dma_callbacked = 0; - desc->callback = rspi_dma_complete; - desc->callback_param = rspi; - dmaengine_submit(desc); - dma_async_issue_pending(rspi->master->dma_tx); + if (rx) { + desc_rx->callback = rspi_dma_complete; + desc_rx->callback_param = rspi; + cookie = dmaengine_submit(desc_rx); + if (dma_submit_error(cookie)) + return cookie; + dma_async_issue_pending(rspi->master->dma_rx); + } + if (tx) { + if (rx) { + /* No callback */ + desc_tx->callback = NULL; + } else { + desc_tx->callback = rspi_dma_complete; + desc_tx->callback_param = rspi; + } + cookie = dmaengine_submit(desc_tx); + if (dma_submit_error(cookie)) + return cookie; + dma_async_issue_pending(rspi->master->dma_tx); + } ret = wait_event_interruptible_timeout(rspi->wait, rspi->dma_callbacked, HZ); @@ -494,9 +531,14 @@ static int rspi_send_dma(struct rspi_data *rspi, struct sg_table *tx) ret = 0; else if (!ret) ret = -ETIMEDOUT; - rspi_disable_irq(rspi, SPCR_SPTIE); - enable_irq(rspi->tx_irq); + rspi_disable_irq(rspi, irq_mask); + + if (tx) + enable_irq(rspi->tx_irq); + if (rx && rspi->rx_irq != other_irq) + enable_irq(rspi->rx_irq); + return ret; } @@ -530,61 +572,6 @@ static void qspi_receive_init(const struct rspi_data *rspi) rspi_write8(rspi, 0, QSPI_SPBFCR); } -static int rspi_send_receive_dma(struct rspi_data *rspi, struct sg_table *tx, - struct sg_table *rx) -{ - struct dma_async_tx_descriptor *desc_tx, *desc_rx; - int ret; - - /* prepare transmit transfer */ - desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx, tx->sgl, - tx->nents, DMA_TO_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc_tx) - return -EIO; - - /* prepare receive transfer */ - desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx, rx->sgl, - rx->nents, DMA_FROM_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc_rx) - return -EIO; - - /* - * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be - * called. So, this driver disables the IRQ while DMA transfer. - */ - disable_irq(rspi->tx_irq); - if (rspi->rx_irq != rspi->tx_irq) - disable_irq(rspi->rx_irq); - - rspi_enable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE); - rspi->dma_callbacked = 0; - - desc_rx->callback = rspi_dma_complete; - desc_rx->callback_param = rspi; - dmaengine_submit(desc_rx); - dma_async_issue_pending(rspi->master->dma_rx); - - desc_tx->callback = NULL; /* No callback */ - dmaengine_submit(desc_tx); - dma_async_issue_pending(rspi->master->dma_tx); - - ret = wait_event_interruptible_timeout(rspi->wait, - rspi->dma_callbacked, HZ); - if (ret > 0 && rspi->dma_callbacked) - ret = 0; - else if (!ret) - ret = -ETIMEDOUT; - rspi_disable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE); - - enable_irq(rspi->tx_irq); - if (rspi->rx_irq != rspi->tx_irq) - enable_irq(rspi->rx_irq); - - return ret; -} - static bool __rspi_can_dma(const struct rspi_data *rspi, const struct spi_transfer *xfer) { @@ -615,13 +602,9 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, } rspi_write8(rspi, spcr, RSPI_SPCR); - if (master->can_dma && __rspi_can_dma(rspi, xfer)) { - if (xfer->rx_buf) - return rspi_send_receive_dma(rspi, &xfer->tx_sg, - &xfer->rx_sg); - else - return rspi_send_dma(rspi, &xfer->tx_sg); - } + if (master->can_dma && __rspi_can_dma(rspi, xfer)) + return rspi_dma_transfer(rspi, &xfer->tx_sg, + xfer->rx_buf ? &xfer->rx_sg : NULL); ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); if (ret < 0) -- cgit v1.2.3 From 03e627c55752fa434d5b3eba5ee3e489c51672b6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:16 +0200 Subject: spi: rspi: Absorb rspi_rz_transfer_out_in() into rspi_rz_transfer_one() Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index c77cfe654b0e..b7f8be81423b 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -616,9 +616,11 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, return 0; } -static int rspi_rz_transfer_out_in(struct rspi_data *rspi, - struct spi_transfer *xfer) +static int rspi_rz_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) { + struct rspi_data *rspi = spi_master_get_devdata(master); int ret; rspi_rz_receive_init(rspi); @@ -633,15 +635,6 @@ static int rspi_rz_transfer_out_in(struct rspi_data *rspi, return 0; } -static int rspi_rz_transfer_one(struct spi_master *master, - struct spi_device *spi, - struct spi_transfer *xfer) -{ - struct rspi_data *rspi = spi_master_get_devdata(master); - - return rspi_rz_transfer_out_in(rspi, xfer); -} - static int qspi_transfer_out_in(struct rspi_data *rspi, struct spi_transfer *xfer) { -- cgit v1.2.3 From 4f12b5e529e4ff274eb478ec1c2ef41358ed9577 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:17 +0200 Subject: spi: rspi: Add DMA support for QSPI on R-Car Gen2 Enable DMA support for QSPI on R-Car Gen2, for Single, Dual, and Quad SPI Transfers. Performance figures for reading from a QSPI FLASH driven at 24.375 MHz on r8a7791/koelsch: - Single: 1.1 Mbps PIO, 23 Mbps DMA - Dual : 12.7 Mbps PIO, 48 Mbps DMA - Quad : 13 Mbps PIO, 70 Mbps DMA Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index b7f8be81423b..bec81470dd9c 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -642,6 +642,9 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, qspi_receive_init(rspi); + if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) + return rspi_dma_transfer(rspi, &xfer->tx_sg, &xfer->rx_sg); + ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); if (ret < 0) return ret; @@ -656,6 +659,9 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) { int ret; + if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) + return rspi_dma_transfer(rspi, &xfer->tx_sg, NULL); + ret = rspi_pio_transfer(rspi, xfer->tx_buf, NULL, xfer->len); if (ret < 0) return ret; @@ -668,6 +674,9 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer) { + if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) + return rspi_dma_transfer(rspi, NULL, &xfer->rx_sg); + return rspi_pio_transfer(rspi, NULL, xfer->rx_buf, xfer->len); } -- cgit v1.2.3 From e7fb921d9f62df05240ad1a74b5a0f623e503c9c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:18 +0200 Subject: spi: rspi: Add DMA support for RSPI on RZ/A1H Enable DMA support for RSPI on r7s72100 (RZ/A1H). Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index bec81470dd9c..4bc4138e002b 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -625,6 +625,9 @@ static int rspi_rz_transfer_one(struct spi_master *master, rspi_rz_receive_init(rspi); + if (master->can_dma && __rspi_can_dma(rspi, xfer)) + return rspi_dma_transfer(rspi, &xfer->tx_sg, &xfer->rx_sg); + ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); if (ret < 0) return ret; -- cgit v1.2.3 From 8b983e90ea1a3dd82070f96c062ad521a06b7cc0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Jun 2014 15:38:19 +0200 Subject: spi: rspi: Extract rspi_common_transfer() Extract the common parts of rspi_transfer_one(), rspi_rz_transfer_one(), and qspi_transfer_out_in() into the new function rspi_common_transfer(). Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 61 ++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 4bc4138e002b..10112745bb17 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -586,12 +586,32 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi, return __rspi_can_dma(rspi, xfer); } +static int rspi_common_transfer(struct rspi_data *rspi, + struct spi_transfer *xfer) +{ + int ret; + + if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { + /* rx_buf can be NULL on RSPI on SH in TX-only Mode */ + return rspi_dma_transfer(rspi, &xfer->tx_sg, + xfer->rx_buf ? &xfer->rx_sg : NULL); + } + + ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); + if (ret < 0) + return ret; + + /* Wait for the last transmission */ + rspi_wait_for_tx_empty(rspi); + + return 0; +} + static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct rspi_data *rspi = spi_master_get_devdata(master); u8 spcr; - int ret; spcr = rspi_read8(rspi, RSPI_SPCR); if (xfer->rx_buf) { @@ -602,18 +622,7 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, } rspi_write8(rspi, spcr, RSPI_SPCR); - if (master->can_dma && __rspi_can_dma(rspi, xfer)) - return rspi_dma_transfer(rspi, &xfer->tx_sg, - xfer->rx_buf ? &xfer->rx_sg : NULL); - - ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); - if (ret < 0) - return ret; - - /* Wait for the last transmission */ - rspi_wait_for_tx_empty(rspi); - - return 0; + return rspi_common_transfer(rspi, xfer); } static int rspi_rz_transfer_one(struct spi_master *master, @@ -625,37 +634,15 @@ static int rspi_rz_transfer_one(struct spi_master *master, rspi_rz_receive_init(rspi); - if (master->can_dma && __rspi_can_dma(rspi, xfer)) - return rspi_dma_transfer(rspi, &xfer->tx_sg, &xfer->rx_sg); - - ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); - if (ret < 0) - return ret; - - /* Wait for the last transmission */ - rspi_wait_for_tx_empty(rspi); - - return 0; + return rspi_common_transfer(rspi, xfer); } static int qspi_transfer_out_in(struct rspi_data *rspi, struct spi_transfer *xfer) { - int ret; - qspi_receive_init(rspi); - if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) - return rspi_dma_transfer(rspi, &xfer->tx_sg, &xfer->rx_sg); - - ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); - if (ret < 0) - return ret; - - /* Wait for the last transmission */ - rspi_wait_for_tx_empty(rspi); - - return 0; + return rspi_common_transfer(rspi, xfer); } static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) -- cgit v1.2.3