| From 56c1723426d3cfd4723bfbfce531d7b38bae6266 Mon Sep 17 00:00:00 2001 |
| From: Lukas Wunner <lukas@wunner.de> |
| Date: Thu, 8 Nov 2018 08:06:10 +0100 |
| Subject: spi: bcm2835: Avoid finishing transfer prematurely in IRQ mode |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Lukas Wunner <lukas@wunner.de> |
| |
| commit 56c1723426d3cfd4723bfbfce531d7b38bae6266 upstream. |
| |
| The IRQ handler bcm2835_spi_interrupt() first reads as much as possible |
| from the RX FIFO, then writes as much as possible to the TX FIFO. |
| Afterwards it decides whether the transfer is finished by checking if |
| the TX FIFO is empty. |
| |
| If very few bytes were written to the TX FIFO, they may already have |
| been transmitted by the time the FIFO's emptiness is checked. As a |
| result, the transfer will be declared finished and the chip will be |
| reset without reading the corresponding received bytes from the RX FIFO. |
| |
| The odds of this happening increase with a high clock frequency (such |
| that the TX FIFO drains quickly) and either passing "threadirqs" on the |
| command line or enabling CONFIG_PREEMPT_RT_BASE (such that the IRQ |
| handler may be preempted between filling the TX FIFO and checking its |
| emptiness). |
| |
| Fix by instead checking whether rx_len has reached zero, which means |
| that the transfer has been received in full. This is also more |
| efficient as it avoids one bus read access per interrupt. Note that |
| bcm2835_spi_transfer_one_poll() likewise uses rx_len to determine |
| whether the transfer has finished. |
| |
| Signed-off-by: Lukas Wunner <lukas@wunner.de> |
| Fixes: e34ff011c70e ("spi: bcm2835: move to the transfer_one driver model") |
| Cc: stable@vger.kernel.org # v4.1+ |
| Cc: Mathias Duckeck <m.duckeck@kunbus.de> |
| Cc: Frank Pavlic <f.pavlic@kunbus.de> |
| Cc: Martin Sperl <kernel@martin.sperl.org> |
| Cc: Noralf Trønnes <noralf@tronnes.org> |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/spi/spi-bcm2835.c | 3 +-- |
| 1 file changed, 1 insertion(+), 2 deletions(-) |
| |
| --- a/drivers/spi/spi-bcm2835.c |
| +++ b/drivers/spi/spi-bcm2835.c |
| @@ -155,8 +155,7 @@ static irqreturn_t bcm2835_spi_interrupt |
| /* Write as many bytes as possible to FIFO */ |
| bcm2835_wr_fifo(bs); |
| |
| - /* based on flags decide if we can finish the transfer */ |
| - if (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_DONE) { |
| + if (!bs->rx_len) { |
| /* Transfer complete - reset SPI HW */ |
| bcm2835_spi_reset_hw(master); |
| /* wake up the framework */ |