[PATCH] Fix OMAP McSPI DMA corruption

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux