diff options
Diffstat (limited to 'target/linux/pistachio/patches-4.9/106-spi-img-spfi-finish-every-transfer-cleanly.patch')
-rw-r--r-- | target/linux/pistachio/patches-4.9/106-spi-img-spfi-finish-every-transfer-cleanly.patch | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/target/linux/pistachio/patches-4.9/106-spi-img-spfi-finish-every-transfer-cleanly.patch b/target/linux/pistachio/patches-4.9/106-spi-img-spfi-finish-every-transfer-cleanly.patch new file mode 100644 index 0000000000..f75f0fbc57 --- /dev/null +++ b/target/linux/pistachio/patches-4.9/106-spi-img-spfi-finish-every-transfer-cleanly.patch @@ -0,0 +1,125 @@ +From 5fcca3fd4b621d7b5bdeca18d36dfc6ca6cfe383 Mon Sep 17 00:00:00 2001 +From: Ionela Voinescu <ionela.voinescu@imgtec.com> +Date: Wed, 10 Aug 2016 11:42:26 +0100 +Subject: spi: img-spfi: finish every transfer cleanly + +Before this change, the interrupt status bit that signaled +the end of a tranfers was cleared in the wait_all_done +function. That functionality triggered issues for DMA +duplex transactions where the wait function was called +twice, in both the TX and RX callbacks. + +In order to fix the issue, clear all interrupt data bits +at the end of a PIO transfer or at the end of both TX and RX +duplex transfers, if the transfer is not a pending tranfer +(command waiting for data). After that, the status register +is checked for new incoming data or new data requests to be +signaled. If SPFI finished cleanly, no new interrupt data +bits should be set. + +Signed-off-by: Ionela Voinescu <ionela.voinescu@imgtec.com> +--- + drivers/spi/spi-img-spfi.c | 49 +++++++++++++++++++++++++++++++++------------- + 1 file changed, 35 insertions(+), 14 deletions(-) + +diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c +index 8ad6c75..a124423 100644 +--- a/drivers/spi/spi-img-spfi.c ++++ b/drivers/spi/spi-img-spfi.c +@@ -83,6 +83,14 @@ + #define SPFI_INTERRUPT_SDE BIT(1) + #define SPFI_INTERRUPT_SDTRIG BIT(0) + ++#define SPFI_INTERRUPT_DATA_BITS (SPFI_INTERRUPT_SDHF |\ ++ SPFI_INTERRUPT_SDFUL |\ ++ SPFI_INTERRUPT_GDEX32BIT |\ ++ SPFI_INTERRUPT_GDHF |\ ++ SPFI_INTERRUPT_GDFUL |\ ++ SPFI_INTERRUPT_ALLDONETRIG |\ ++ SPFI_INTERRUPT_GDEX8BIT) ++ + /* + * There are four parallel FIFOs of 16 bytes each. The word buffer + * (*_32BIT_VALID_DATA) accesses all four FIFOs at once, resulting in an +@@ -144,6 +152,23 @@ static inline void spfi_reset(struct img_spfi *spfi) + spfi_writel(spfi, 0, SPFI_CONTROL); + } + ++static inline void spfi_finish(struct img_spfi *spfi) ++{ ++ if (!(spfi->complete)) ++ return; ++ ++ /* Clear data bits as all transfers(TX and RX) have finished */ ++ spfi_writel(spfi, SPFI_INTERRUPT_DATA_BITS, SPFI_INTERRUPT_CLEAR); ++ if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) & SPFI_INTERRUPT_DATA_BITS) { ++ dev_err(spfi->dev, "SPFI did not finish transfer cleanly.\n"); ++ spfi_reset(spfi); ++ } ++ /* Disable SPFI for it not to interfere with pending transactions */ ++ spfi_writel(spfi, ++ spfi_readl(spfi, SPFI_CONTROL) & ~SPFI_CONTROL_SPFI_EN, ++ SPFI_CONTROL); ++} ++ + static int spfi_wait_all_done(struct img_spfi *spfi) + { + unsigned long timeout = jiffies + msecs_to_jiffies(50); +@@ -152,19 +177,9 @@ static int spfi_wait_all_done(struct img_spfi *spfi) + return 0; + + while (time_before(jiffies, timeout)) { +- u32 status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); +- +- if (status & SPFI_INTERRUPT_ALLDONETRIG) { +- spfi_writel(spfi, SPFI_INTERRUPT_ALLDONETRIG, +- SPFI_INTERRUPT_CLEAR); +- /* +- * Disable SPFI for it not to interfere with +- * pending transactions +- */ +- spfi_writel(spfi, spfi_readl(spfi, SPFI_CONTROL) +- & ~SPFI_CONTROL_SPFI_EN, SPFI_CONTROL); ++ if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) & ++ SPFI_INTERRUPT_ALLDONETRIG) + return 0; +- } + cpu_relax(); + } + +@@ -296,6 +311,8 @@ static int img_spfi_start_pio(struct spi_master *master, + } + + ret = spfi_wait_all_done(spfi); ++ spfi_finish(spfi); ++ + if (ret < 0) + return ret; + +@@ -311,8 +328,10 @@ static void img_spfi_dma_rx_cb(void *data) + + spin_lock_irqsave(&spfi->lock, flags); + spfi->rx_dma_busy = false; +- if (!spfi->tx_dma_busy) ++ if (!spfi->tx_dma_busy) { ++ spfi_finish(spfi); + spi_finalize_current_transfer(spfi->master); ++ } + spin_unlock_irqrestore(&spfi->lock, flags); + } + +@@ -325,8 +344,10 @@ static void img_spfi_dma_tx_cb(void *data) + + spin_lock_irqsave(&spfi->lock, flags); + spfi->tx_dma_busy = false; +- if (!spfi->rx_dma_busy) ++ if (!spfi->rx_dma_busy) { ++ spfi_finish(spfi); + spi_finalize_current_transfer(spfi->master); ++ } + spin_unlock_irqrestore(&spfi->lock, flags); + } + +-- +2.7.4 + |