From 6e0050ec8e909a5ed7d062552cd4fd223d286687 Mon Sep 17 00:00:00 2001 From: Sifan Naeem Date: Mon, 2 Mar 2015 16:01:58 +0000 Subject: [PATCH 1/6] spi: img-spfi: Remove udelay in soft reset Removing the udelay between setting and clearing the soft reset bit in the spfi control register as it is not required. Signed-off-by: Sifan Naeem Reviewed-by: Andrew Bresticker Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index c01567d53581..fde4b5dd40f2 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -134,7 +134,6 @@ static inline void spfi_stop(struct img_spfi *spfi) static inline void spfi_reset(struct img_spfi *spfi) { spfi_writel(spfi, SPFI_CONTROL_SOFT_RESET, SPFI_CONTROL); - udelay(1); spfi_writel(spfi, 0, SPFI_CONTROL); } From b6fe39770aa63d14129bc7e061c95cfc3cb1419a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 6 Apr 2015 14:29:04 -0700 Subject: [PATCH 2/6] spi: img-spfi: Implement a prepare_message() callback In preparation for switching to using the SPI core's CS GPIO handling, move setup of the PORT_STATE register, which must be configured before CS is asserted, to a prepare_message() callback. Signed-off-by: Ezequiel Garcia Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index fde4b5dd40f2..642259e88429 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -396,6 +396,25 @@ stop_dma: return -EIO; } +static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg) +{ + struct img_spfi *spfi = spi_master_get_devdata(master); + u32 val; + + val = spfi_readl(spfi, SPFI_PORT_STATE); + if (msg->spi->mode & SPI_CPHA) + val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select); + else + val &= ~SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select); + if (msg->spi->mode & SPI_CPOL) + val |= SPFI_PORT_STATE_CK_POL(msg->spi->chip_select); + else + val &= ~SPFI_PORT_STATE_CK_POL(msg->spi->chip_select); + spfi_writel(spfi, val, SPFI_PORT_STATE); + + return 0; +} + static void img_spfi_config(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { @@ -433,18 +452,6 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi, &master->cur_msg->transfers)) val |= SPFI_CONTROL_CONTINUE; spfi_writel(spfi, val, SPFI_CONTROL); - - val = spfi_readl(spfi, SPFI_PORT_STATE); - if (spi->mode & SPI_CPHA) - val |= SPFI_PORT_STATE_CK_PHASE(spi->chip_select); - else - val &= ~SPFI_PORT_STATE_CK_PHASE(spi->chip_select); - if (spi->mode & SPI_CPOL) - val |= SPFI_PORT_STATE_CK_POL(spi->chip_select); - else - val &= ~SPFI_PORT_STATE_CK_POL(spi->chip_select); - spfi_writel(spfi, val, SPFI_PORT_STATE); - spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT, SPFI_TRANSACTION); } @@ -591,6 +598,7 @@ static int img_spfi_probe(struct platform_device *pdev) master->set_cs = img_spfi_set_cs; master->transfer_one = img_spfi_transfer_one; + master->prepare_message = img_spfi_prepare; spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx"); spfi->rx_ch = dma_request_slave_channel(spfi->dev, "rx"); From ede8342bf63166e8d8fc3c05fc0985b27cc8186b Mon Sep 17 00:00:00 2001 From: Sifan Naeem Date: Mon, 6 Apr 2015 14:29:06 -0700 Subject: [PATCH 3/6] spi: img-spfi: Setup TRANSACTION register before CONTROL register Setting the transfer length in the TRANSACTION register after the CONTROL register is programmed causes intermittent timeout issues in SPFI transfers when using the SPI framework to control the CS GPIO lines. To avoid this issue, set transfer length before programming the CONTROL register. Signed-off-by: Sifan Naeem Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index 642259e88429..6c6ae4a9401a 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -434,6 +434,9 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi, val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT; spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select)); + spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT, + SPFI_TRANSACTION); + val = spfi_readl(spfi, SPFI_CONTROL); val &= ~(SPFI_CONTROL_SEND_DMA | SPFI_CONTROL_GET_DMA); if (xfer->tx_buf) @@ -452,8 +455,6 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi, &master->cur_msg->transfers)) val |= SPFI_CONTROL_CONTINUE; spfi_writel(spfi, val, SPFI_CONTROL); - spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT, - SPFI_TRANSACTION); } static int img_spfi_transfer_one(struct spi_master *master, From 824ab37df006ea2d06e4c4817344c0e8c056b744 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 8 Apr 2015 10:03:14 -0700 Subject: [PATCH 4/6] spi: img-spfi: Implement a handle_err() callback The driver can be greatly simplified by moving the transfer timeout handling to a handle_err() callback. Signed-off-by: Ezequiel Garcia Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 44 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index c9f3bca988d7..3fd7b07c3eef 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -270,7 +270,6 @@ static int img_spfi_start_pio(struct spi_master *master, if (rx_bytes > 0 || tx_bytes > 0) { dev_err(spfi->dev, "PIO transfer timed out\n"); - spfi_reset(spfi); return -ETIMEDOUT; } @@ -396,6 +395,29 @@ stop_dma: return -EIO; } +static void img_spfi_handle_err(struct spi_master *master, + struct spi_message *msg) +{ + struct img_spfi *spfi = spi_master_get_devdata(master); + unsigned long flags; + + /* + * Stop all DMA and reset the controller if the previous transaction + * timed-out and never completed it's DMA. + */ + spin_lock_irqsave(&spfi->lock, flags); + if (spfi->tx_dma_busy || spfi->rx_dma_busy) { + spfi->tx_dma_busy = false; + spfi->rx_dma_busy = false; + + dmaengine_terminate_all(spfi->tx_ch); + dmaengine_terminate_all(spfi->rx_ch); + } + spin_unlock_irqrestore(&spfi->lock, flags); + + spfi_reset(spfi); +} + static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg) { struct img_spfi *spfi = spi_master_get_devdata(master); @@ -462,8 +484,6 @@ static int img_spfi_transfer_one(struct spi_master *master, struct spi_transfer *xfer) { struct img_spfi *spfi = spi_master_get_devdata(spi->master); - bool dma_reset = false; - unsigned long flags; int ret; if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) { @@ -473,23 +493,6 @@ static int img_spfi_transfer_one(struct spi_master *master, return -EINVAL; } - /* - * Stop all DMA and reset the controller if the previous transaction - * timed-out and never completed it's DMA. - */ - spin_lock_irqsave(&spfi->lock, flags); - if (spfi->tx_dma_busy || spfi->rx_dma_busy) { - dev_err(spfi->dev, "SPI DMA still busy\n"); - dma_reset = true; - } - spin_unlock_irqrestore(&spfi->lock, flags); - - if (dma_reset) { - dmaengine_terminate_all(spfi->tx_ch); - dmaengine_terminate_all(spfi->rx_ch); - spfi_reset(spfi); - } - img_spfi_config(master, spi, xfer); if (master->can_dma && master->can_dma(master, spi, xfer)) ret = img_spfi_start_dma(master, spi, xfer); @@ -607,6 +610,7 @@ static int img_spfi_probe(struct platform_device *pdev) master->set_cs = img_spfi_set_cs; master->transfer_one = img_spfi_transfer_one; master->prepare_message = img_spfi_prepare; + master->handle_err = img_spfi_handle_err; spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx"); spfi->rx_ch = dma_request_slave_channel(spfi->dev, "rx"); From ba33d8ac0c93fdfbdbc825ef690caab5d57e22c2 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Wed, 8 Apr 2015 10:03:15 -0700 Subject: [PATCH 5/6] spi: img-spfi: Reset controller after each message Imagination has recommended that the SPFI controller be reset after each message, regardless of success or failure. Do this in an unprepare_message() callback. Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index 3fd7b07c3eef..c37f6e50096e 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -414,8 +414,6 @@ static void img_spfi_handle_err(struct spi_master *master, dmaengine_terminate_all(spfi->rx_ch); } spin_unlock_irqrestore(&spfi->lock, flags); - - spfi_reset(spfi); } static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg) @@ -437,6 +435,16 @@ static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg) return 0; } +static int img_spfi_unprepare(struct spi_master *master, + struct spi_message *msg) +{ + struct img_spfi *spfi = spi_master_get_devdata(master); + + spfi_reset(spfi); + + return 0; +} + static void img_spfi_config(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { @@ -610,6 +618,7 @@ static int img_spfi_probe(struct platform_device *pdev) master->set_cs = img_spfi_set_cs; master->transfer_one = img_spfi_transfer_one; master->prepare_message = img_spfi_prepare; + master->unprepare_message = img_spfi_unprepare; master->handle_err = img_spfi_handle_err; spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx"); From 8c2c8c03cdcb9b0a75b5585e611715fdd8096c38 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 8 Apr 2015 10:03:16 -0700 Subject: [PATCH 6/6] spi: img-spfi: Control CS lines with GPIO When the CONTINUE bit is set, the interrupt status we are polling to identify if a transaction has finished can be sporadic. Even though the transfer has finished, the interrupt status may erroneously indicate that there is still data in the FIFO. This behaviour causes random timeouts in large PIO transfers. Instead of using the CONTINUE bit to control the CS lines, use the SPI core's CS GPIO handling. Also, now that the CONTINUE bit is not being used, we can poll for the ALLDONE interrupt to indicate transfer completion. Signed-off-by: Sifan Naeem Signed-off-by: Ezequiel Garcia Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-img-spfi.txt | 1 + drivers/spi/spi-img-spfi.c | 92 +++++++++---------- 2 files changed, 45 insertions(+), 48 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-img-spfi.txt b/Documentation/devicetree/bindings/spi/spi-img-spfi.txt index c7dd50fb8eb2..e02fbf18c82c 100644 --- a/Documentation/devicetree/bindings/spi/spi-img-spfi.txt +++ b/Documentation/devicetree/bindings/spi/spi-img-spfi.txt @@ -14,6 +14,7 @@ Required properties: - dma-names: Must include the following entries: - rx - tx +- cs-gpios: Must specify the GPIOs used for chipselect lines. - #address-cells: Must be 1. - #size-cells: Must be 0. diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index dedb7d880ccc..788e2b176a4f 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -122,35 +123,31 @@ static inline void spfi_start(struct img_spfi *spfi) spfi_writel(spfi, val, SPFI_CONTROL); } -static inline void spfi_stop(struct img_spfi *spfi) -{ - u32 val; - - val = spfi_readl(spfi, SPFI_CONTROL); - val &= ~SPFI_CONTROL_SPFI_EN; - spfi_writel(spfi, val, SPFI_CONTROL); -} - static inline void spfi_reset(struct img_spfi *spfi) { spfi_writel(spfi, SPFI_CONTROL_SOFT_RESET, SPFI_CONTROL); spfi_writel(spfi, 0, SPFI_CONTROL); } -static void spfi_flush_tx_fifo(struct img_spfi *spfi) +static int spfi_wait_all_done(struct img_spfi *spfi) { - unsigned long timeout = jiffies + msecs_to_jiffies(10); + unsigned long timeout = jiffies + msecs_to_jiffies(50); - spfi_writel(spfi, SPFI_INTERRUPT_SDE, SPFI_INTERRUPT_CLEAR); while (time_before(jiffies, timeout)) { - if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) & - SPFI_INTERRUPT_SDE) - return; + u32 status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); + + if (status & SPFI_INTERRUPT_ALLDONETRIG) { + spfi_writel(spfi, SPFI_INTERRUPT_ALLDONETRIG, + SPFI_INTERRUPT_CLEAR); + return 0; + } cpu_relax(); } - dev_err(spfi->dev, "Timed out waiting for FIFO to drain\n"); + dev_err(spfi->dev, "Timed out waiting for transaction to complete\n"); spfi_reset(spfi); + + return -ETIMEDOUT; } static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf, @@ -236,6 +233,7 @@ static int img_spfi_start_pio(struct spi_master *master, const void *tx_buf = xfer->tx_buf; void *rx_buf = xfer->rx_buf; unsigned long timeout; + int ret; if (tx_buf) tx_bytes = xfer->len; @@ -268,15 +266,15 @@ static int img_spfi_start_pio(struct spi_master *master, cpu_relax(); } + ret = spfi_wait_all_done(spfi); + if (ret < 0) + return ret; + if (rx_bytes > 0 || tx_bytes > 0) { dev_err(spfi->dev, "PIO transfer timed out\n"); return -ETIMEDOUT; } - if (tx_buf) - spfi_flush_tx_fifo(spfi); - spfi_stop(spfi); - return 0; } @@ -285,14 +283,12 @@ static void img_spfi_dma_rx_cb(void *data) struct img_spfi *spfi = data; unsigned long flags; + spfi_wait_all_done(spfi); + spin_lock_irqsave(&spfi->lock, flags); - spfi->rx_dma_busy = false; - if (!spfi->tx_dma_busy) { - spfi_stop(spfi); + if (!spfi->tx_dma_busy) spi_finalize_current_transfer(spfi->master); - } - spin_unlock_irqrestore(&spfi->lock, flags); } @@ -301,16 +297,12 @@ static void img_spfi_dma_tx_cb(void *data) struct img_spfi *spfi = data; unsigned long flags; - spfi_flush_tx_fifo(spfi); + spfi_wait_all_done(spfi); spin_lock_irqsave(&spfi->lock, flags); - spfi->tx_dma_busy = false; - if (!spfi->rx_dma_busy) { - spfi_stop(spfi); + if (!spfi->rx_dma_busy) spi_finalize_current_transfer(spfi->master); - } - spin_unlock_irqrestore(&spfi->lock, flags); } @@ -445,6 +437,25 @@ static int img_spfi_unprepare(struct spi_master *master, return 0; } +static int img_spfi_setup(struct spi_device *spi) +{ + int ret; + + ret = gpio_request_one(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ? + GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, + dev_name(&spi->dev)); + if (ret) + dev_err(&spi->dev, "can't request chipselect gpio %d\n", + spi->cs_gpio); + + return ret; +} + +static void img_spfi_cleanup(struct spi_device *spi) +{ + gpio_free(spi->cs_gpio); +} + static void img_spfi_config(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { @@ -480,10 +491,6 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi, else if (xfer->tx_nbits == SPI_NBITS_QUAD && xfer->rx_nbits == SPI_NBITS_QUAD) val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT; - val &= ~SPFI_CONTROL_CONTINUE; - if (!xfer->cs_change && !list_is_last(&xfer->transfer_list, - &master->cur_msg->transfers)) - val |= SPFI_CONTROL_CONTINUE; spfi_writel(spfi, val, SPFI_CONTROL); } @@ -510,17 +517,6 @@ static int img_spfi_transfer_one(struct spi_master *master, return ret; } -static void img_spfi_set_cs(struct spi_device *spi, bool enable) -{ - struct img_spfi *spfi = spi_master_get_devdata(spi->master); - u32 val; - - val = spfi_readl(spfi, SPFI_PORT_STATE); - val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK << SPFI_PORT_STATE_DEV_SEL_SHIFT); - val |= spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT; - spfi_writel(spfi, val, SPFI_PORT_STATE); -} - static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { @@ -609,13 +605,13 @@ static int img_spfi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL; if (of_property_read_bool(spfi->dev->of_node, "img,supports-quad-mode")) master->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD; - master->num_chipselect = 5; master->dev.of_node = pdev->dev.of_node; master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(8); master->max_speed_hz = clk_get_rate(spfi->spfi_clk) / 4; master->min_speed_hz = clk_get_rate(spfi->spfi_clk) / 512; - master->set_cs = img_spfi_set_cs; + master->setup = img_spfi_setup; + master->cleanup = img_spfi_cleanup; master->transfer_one = img_spfi_transfer_one; master->prepare_message = img_spfi_prepare; master->unprepare_message = img_spfi_unprepare;