Because the dma_map_single() on ARM only flushes and invalidates the cache-lines occupied by the transfer buffer, care should be taken not to touch memory that share the same cache-lines before starting the DMA transfer. In the McSPI driver dma_map_single() was called before the driver was finished touching the spi_message and spi_transfer structures, causing potential corruption of the transfer buffer. With a layout like this: struct spi_message read_msg; u16 data[4]; - my (touchscreen) driver would get [ 5.875000] 0 0 1002 3334 [ 6.695312] 0 0 1363 3234 after playing with the layout so that data[] starts on a new cache-line I would get: [ 5.890625] 1507 788 1278 2720 [ 5.890625] 1507 819 1242 2779 This patch solves the problem by moving the dma_map_single() calls to just before the DMA is started. Signed-off-by: Klaus Pedersen <klaus.k.pedersen@xxxxxxxxx> --- drivers/spi/omap2_mcspi.c | 48 +++++++++++++++++++------------------------- 1 files changed, 21 insertions(+), 27 deletions(-) diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index a6ba11a..987a472 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -251,6 +251,15 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) } if (tx != NULL) { + xfer->tx_dma = dma_map_single(&spi->dev, (void *)tx, count, + DMA_TO_DEVICE); + if (dma_mapping_error(xfer->tx_dma)) { + dev_err(&spi->dev, + "%s(): Couldn't DMA map a %d bytes %cX buffer\n", + __FUNCTION__, 'T', count); + return 0; + } + omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel, data_type, element_count, 1, OMAP_DMA_SYNC_ELEMENT, @@ -266,6 +275,18 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) } if (rx != NULL) { + xfer->rx_dma = dma_map_single(&spi->dev, rx, count, + DMA_FROM_DEVICE); + if (dma_mapping_error(xfer->rx_dma)) { + dev_err(&spi->dev, + "%s(): Couldn't DMA map a %d bytes %cX buffer\n", + __FUNCTION__, 'T', count); + if (tx != NULL) + dma_unmap_single(NULL, xfer->tx_dma, + count, DMA_TO_DEVICE); + return 0; + } + omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, data_type, element_count, 1, OMAP_DMA_SYNC_ELEMENT, @@ -827,33 +848,6 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) if (m->is_dma_mapped || len < DMA_MIN_BYTES) continue; - - /* Do DMA mapping "early" for better error reporting and - * dcache use. Note that if dma_unmap_single() ever starts - * to do real work on ARM, we'd need to clean up mappings - * for previous transfers on *ALL* exits of this loop... - */ - if (tx_buf != NULL) { - t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf, - len, DMA_TO_DEVICE); - if (dma_mapping_error(t->tx_dma)) { - dev_dbg(&spi->dev, "dma %cX %d bytes error\n", - 'T', len); - return -EINVAL; - } - } - if (rx_buf != NULL) { - t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(t->rx_dma)) { - dev_dbg(&spi->dev, "dma %cX %d bytes error\n", - 'R', len); - if (tx_buf != NULL) - dma_unmap_single(NULL, t->tx_dma, - len, DMA_TO_DEVICE); - return -EINVAL; - } - } } mcspi = spi_master_get_devdata(spi->master); -- 1.5.3.3 - To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html