spi: pl022: Fix broken spidev when DMA is enabled

Message ID 1412743127-4523-1-git-send-email-rjui@broadcom.com
State New
Headers show

Commit Message

Ray Jui Oct. 8, 2014, 4:38 a.m.
The PL022 SPI driver maps the DMA RX buffer before the DMA TX buffer. In
most cases, the sequence of the mapping does not matter. But in cases
where TX and RX happen to use the same buffer, e.g., spidev, it causes
the cached TX data not written to memory, because the same memory has
been marked invalid when dma_map_sg on the RX buffer is called

The solution is to reverse the sequence so it maps the TX buffer before
the RX buffer

Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com>
Tested-by: Scott Branden <sbranden@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
 drivers/spi/spi-pl022.c |   20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

Comments

Mark Brown Oct. 8, 2014, 11:21 a.m. | #1
On Tue, Oct 07, 2014 at 09:38:47PM -0700, Ray Jui wrote:

> The PL022 SPI driver maps the DMA RX buffer before the DMA TX buffer. In
> most cases, the sequence of the mapping does not matter. But in cases
> where TX and RX happen to use the same buffer, e.g., spidev, it causes
> the cached TX data not written to memory, because the same memory has
> been marked invalid when dma_map_sg on the RX buffer is called

This seems like it is a bug in spidev, using the same buffer simultaneously
for both directions isn't something I'd think would be expected to work
reliably unless it was explicitly mapped as bidirectional.
Ray Jui Oct. 8, 2014, 4:14 p.m. | #2
On 10/8/2014 4:21 AM, Mark Brown wrote:
> On Tue, Oct 07, 2014 at 09:38:47PM -0700, Ray Jui wrote:
>
>> The PL022 SPI driver maps the DMA RX buffer before the DMA TX buffer. In
>> most cases, the sequence of the mapping does not matter. But in cases
>> where TX and RX happen to use the same buffer, e.g., spidev, it causes
>> the cached TX data not written to memory, because the same memory has
>> been marked invalid when dma_map_sg on the RX buffer is called
>
> This seems like it is a bug in spidev, using the same buffer simultaneously
> for both directions isn't something I'd think would be expected to work
> reliably unless it was explicitly mapped as bidirectional.
>
Hi Mark,

Thanks for the reply. It looks like you are also the maintainer of 
spidev. In this case, could you please help to confirm that you expect 
spidev to use separate buffers for TX and RX? If so, I can go ahead and 
make the change in spidev.

+ Grant

Thanks,

Ray
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Mark Brown Oct. 8, 2014, 6:21 p.m. | #3
On Wed, Oct 08, 2014 at 09:14:35AM -0700, Ray Jui wrote:

> Thanks for the reply. It looks like you are also the maintainer of spidev.
> In this case, could you please help to confirm that you expect spidev to use
> separate buffers for TX and RX? If so, I can go ahead and make the change in
> spidev.

Yes, that would be my expectation for maximum robustness (or if it is
going to use one buffer it explicitly maps it for mixed use but I'd
expect that to be asking for trouble).
Ray Jui Oct. 8, 2014, 6:31 p.m. | #4
On 10/8/2014 11:21 AM, Mark Brown wrote:
> On Wed, Oct 08, 2014 at 09:14:35AM -0700, Ray Jui wrote:
>
>> Thanks for the reply. It looks like you are also the maintainer of spidev.
>> In this case, could you please help to confirm that you expect spidev to use
>> separate buffers for TX and RX? If so, I can go ahead and make the change in
>> spidev.
>
> Yes, that would be my expectation for maximum robustness (or if it is
> going to use one buffer it explicitly maps it for mixed use but I'd
> expect that to be asking for trouble).
>
Okay, Mark. I'm going to make the change in the spidev and submit a new 
patch.

Thanks for the feedback.

Ray
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Geert Uytterhoeven Oct. 9, 2014, 1:59 p.m. | #5
On Wed, Oct 8, 2014 at 8:21 PM, Mark Brown <broonie@kernel.org> wrote:
> On Wed, Oct 08, 2014 at 09:14:35AM -0700, Ray Jui wrote:
>> Thanks for the reply. It looks like you are also the maintainer of spidev.
>> In this case, could you please help to confirm that you expect spidev to use
>> separate buffers for TX and RX? If so, I can go ahead and make the change in
>> spidev.
>
> Yes, that would be my expectation for maximum robustness (or if it is
> going to use one buffer it explicitly maps it for mixed use but I'd
> expect that to be asking for trouble).

Having two separate buffers avoids false successes when running
"spidev_test --loop" on a buggy SPI master driver.
Been there, done that ;-)

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch hide | download patch | download mbox

diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 1189cfd..edb7298 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -773,10 +773,10 @@  static void *next_transfer(struct pl022 *pl022)
 static void unmap_free_dma_scatter(struct pl022 *pl022)
 {
 	/* Unmap and free the SG tables */
-	dma_unmap_sg(pl022->dma_tx_channel->device->dev, pl022->sgt_tx.sgl,
-		     pl022->sgt_tx.nents, DMA_TO_DEVICE);
 	dma_unmap_sg(pl022->dma_rx_channel->device->dev, pl022->sgt_rx.sgl,
 		     pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+	dma_unmap_sg(pl022->dma_tx_channel->device->dev, pl022->sgt_tx.sgl,
+		     pl022->sgt_tx.nents, DMA_TO_DEVICE);
 	sg_free_table(&pl022->sgt_rx);
 	sg_free_table(&pl022->sgt_tx);
 }
@@ -1026,16 +1026,16 @@  static int configure_dma(struct pl022 *pl022)
 			  pl022->cur_transfer->len, &pl022->sgt_tx);
 
 	/* Map DMA buffers */
-	rx_sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
-			   pl022->sgt_rx.nents, DMA_FROM_DEVICE);
-	if (!rx_sglen)
-		goto err_rx_sgmap;
-
 	tx_sglen = dma_map_sg(txchan->device->dev, pl022->sgt_tx.sgl,
 			   pl022->sgt_tx.nents, DMA_TO_DEVICE);
 	if (!tx_sglen)
 		goto err_tx_sgmap;
 
+	rx_sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
+			   pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+	if (!rx_sglen)
+		goto err_rx_sgmap;
+
 	/* Send both scatterlists */
 	rxdesc = dmaengine_prep_slave_sg(rxchan,
 				      pl022->sgt_rx.sgl,
@@ -1070,12 +1070,12 @@  err_txdesc:
 	dmaengine_terminate_all(txchan);
 err_rxdesc:
 	dmaengine_terminate_all(rxchan);
+	dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
+		     pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+err_rx_sgmap:
 	dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl,
 		     pl022->sgt_tx.nents, DMA_TO_DEVICE);
 err_tx_sgmap:
-	dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
-		     pl022->sgt_tx.nents, DMA_FROM_DEVICE);
-err_rx_sgmap:
 	sg_free_table(&pl022->sgt_tx);
 err_alloc_tx_sg:
 	sg_free_table(&pl022->sgt_rx);