Commit 393df744 authored by Ned Forrester's avatar Ned Forrester Committed by Linus Torvalds

pxa2xx_spi: bugfix full duplex dma data corruption

Fixes a data corruption bug in pxa2xx_spi.c when operating in full duplex
mode with DMA and using buffers that overlap.

SPI transmit and receive buffers are allowed to be the same or to overlap.
 However, this driver fails if such overlap is attempted in DMA mode
because it maps the rx and tx buffers in the wrong order.  By mapping
DMA_FROM_DEVICE (read) before DMA_TO_DEVICE (write), it invalidates the
cache before flushing it, thus discarding data which should have been
transmitted.

The patch corrects the order of mapping.  This bug exists in all versions
of pxa2xx_spi.c; similar bugs are in the drivers for two other SPI
controllers (au1500, imx).

A version of this patch has been tested on kernel 2.6.20 using
verification of loopback data with: random transfer length, random
bits-per-word, random positive offsets (both larger and smaller than
transfer length) between the start of the rx and tx buffers, and varying
clock rates.
Signed-off-by: default avatarNed Forrester <nforrester@whoi.edu>
Cc: Vernon Sauder <vernoninhand@gmail.com>
Cc: J. Scott Merritt <merrij3@rpi.edu>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Cc: <stable@kernel.org>		[2.6.27.x]
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f652c521
......@@ -352,21 +352,21 @@ static int map_dma_buffers(struct driver_data *drv_data)
} else
drv_data->tx_map_len = drv_data->len;
/* Stream map the rx buffer */
drv_data->rx_dma = dma_map_single(dev, drv_data->rx,
drv_data->rx_map_len,
DMA_FROM_DEVICE);
if (dma_mapping_error(dev, drv_data->rx_dma))
return 0;
/* Stream map the tx buffer */
/* Stream map the tx buffer. Always do DMA_TO_DEVICE first
* so we flush the cache *before* invalidating it, in case
* the tx and rx buffers overlap.
*/
drv_data->tx_dma = dma_map_single(dev, drv_data->tx,
drv_data->tx_map_len,
DMA_TO_DEVICE);
drv_data->tx_map_len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, drv_data->tx_dma))
return 0;
if (dma_mapping_error(dev, drv_data->tx_dma)) {
dma_unmap_single(dev, drv_data->rx_dma,
/* Stream map the rx buffer */
drv_data->rx_dma = dma_map_single(dev, drv_data->rx,
drv_data->rx_map_len, DMA_FROM_DEVICE);
if (dma_mapping_error(dev, drv_data->rx_dma)) {
dma_unmap_single(dev, drv_data->tx_dma,
drv_data->tx_map_len, DMA_TO_DEVICE);
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment