[PATCH] ads7846: allocate separate cache lines for tx and rx data

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

 



From: Alessandro Rubini <rubini@xxxxxxxxx>

Since the SPI master might use DMA, tx and rx buffers must live on
different cache lines.  Here one cache line is used for all tx and
another for all rx.  This will work whether the SPI master maps/unmaps
one buffer at a time or all together.  The patch only fixes the analog
inputs, as touch screen operation is not affected, although I didn't
check why.  I tested on at91sam9263, where buffers are all mapped
initially and unmapped individually at irq time.

The issue was discussed with Russell King on linux-arm-kernel.

Signed-off-by: Alessandro Rubini <rubini@xxxxxxxxx>
Cc: Russell King <linux@xxxxxxxxxxxxxxxx>
Cc: David Brownell <dbrownell@xxxxxxxxxxxxxxxxxxxxx>
---
 drivers/input/touchscreen/ads7846.c |   53 +++++++++++++++++++++++++----------
 1 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index ba9d38c..6c940c9 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -27,6 +27,7 @@
 #include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
+#include <linux/dma-mapping.h>
 #include <asm/irq.h>
 
 
@@ -196,13 +197,20 @@ struct ads7846 {
  */
 
 struct ser_req {
+	struct spi_message	msg;
+	struct spi_transfer	xfer[6];
+	/* data buffers must live on different cache lines, for DMA access */
+	struct tx_data		*tx;
+	struct rx_data		*rx;
+};
+struct tx_data {
 	u8			ref_on;
 	u8			command;
 	u8			ref_off;
-	u16			scratch;
+};
+struct rx_data {
 	__be16			sample;
-	struct spi_message	msg;
-	struct spi_transfer	xfer[6];
+	__be16			scratch;
 };
 
 static void ads7846_enable(struct ads7846 *ts);
@@ -218,12 +226,27 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
 {
 	struct spi_device	*spi = to_spi_device(dev);
 	struct ads7846		*ts = dev_get_drvdata(dev);
-	struct ser_req		*req = kzalloc(sizeof *req, GFP_KERNEL);
+	struct ser_req		*req;
+	void			*ptr;
 	int			status;
 	int			use_internal;
+	int			cacheline = dma_get_cache_alignment();
+	int			size;
+
+	/*
+	 * allocate the request and two different cache lines for tx and rx.
+	 * Use a single kmalloc and some simple quasi-constant aritmetics
+	 */
+	size = ALIGN(sizeof *req, cacheline)
+		+ ALIGN(sizeof *req->tx, cacheline)
+		+ ALIGN(sizeof *req->rx, cacheline);
 
-	if (!req)
+	ptr = kzalloc(size, GFP_KERNEL);
+	if (!ptr)
 		return -ENOMEM;
+	req = ptr;
+	req->tx = ptr + ALIGN(sizeof *req, cacheline);
+	req->rx = (void *)req->tx + ALIGN(sizeof *req->tx, cacheline);
 
 	spi_message_init(&req->msg);
 
@@ -232,12 +255,12 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
 
 	/* maybe turn on internal vREF, and let it settle */
 	if (use_internal) {
-		req->ref_on = REF_ON;
-		req->xfer[0].tx_buf = &req->ref_on;
+		req->tx->ref_on = REF_ON;
+		req->xfer[0].tx_buf = &req->tx->ref_on;
 		req->xfer[0].len = 1;
 		spi_message_add_tail(&req->xfer[0], &req->msg);
 
-		req->xfer[1].rx_buf = &req->scratch;
+		req->xfer[1].rx_buf = &req->rx->scratch;
 		req->xfer[1].len = 2;
 
 		/* for 1uF, settle for 800 usec; no cap, 100 usec.  */
@@ -246,24 +269,24 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
 	}
 
 	/* take sample */
-	req->command = (u8) command;
-	req->xfer[2].tx_buf = &req->command;
+	req->tx->command = (u8) command;
+	req->xfer[2].tx_buf = &req->tx->command;
 	req->xfer[2].len = 1;
 	spi_message_add_tail(&req->xfer[2], &req->msg);
 
-	req->xfer[3].rx_buf = &req->sample;
+	req->xfer[3].rx_buf = &req->rx->sample;
 	req->xfer[3].len = 2;
 	spi_message_add_tail(&req->xfer[3], &req->msg);
 
 	/* REVISIT:  take a few more samples, and compare ... */
 
 	/* converter in low power mode & enable PENIRQ */
-	req->ref_off = PWRDOWN;
-	req->xfer[4].tx_buf = &req->ref_off;
+	req->tx->ref_off = PWRDOWN;
+	req->xfer[4].tx_buf = &req->tx->ref_off;
 	req->xfer[4].len = 1;
 	spi_message_add_tail(&req->xfer[4], &req->msg);
 
-	req->xfer[5].rx_buf = &req->scratch;
+	req->xfer[5].rx_buf = &req->rx->scratch;
 	req->xfer[5].len = 2;
 	CS_CHANGE(req->xfer[5]);
 	spi_message_add_tail(&req->xfer[5], &req->msg);
@@ -276,7 +299,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
 
 	if (status == 0) {
 		/* on-wire is a must-ignore bit, a BE12 value, then padding */
-		status = be16_to_cpu(req->sample);
+		status = be16_to_cpu(req->rx->sample);
 		status = status >> 3;
 		status &= 0x0fff;
 	}
-- 
1.6.0.2
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux