From 0262abfaac71799e3688285f1f30bead42b8ff7e Mon Sep 17 00:00:00 2001
From: cbeytas <cbeytas@shaw.ca>
Date: Mon, 24 Jun 2013 00:05:40 -0400
Subject: [PATCH 020/114] Perform I2C combined transactions when possible

Perform I2C combined transactions whenever possible, within the
restrictions of the Broadcomm Serial Controller.

Disable DONE interrupt during TA poll

Prevent interrupt from being triggered if poll is missed and transfer
starts and finishes.

i2c: Make combined transactions optional and disabled by default
---
 drivers/i2c/busses/i2c-bcm2708.c | 31 ++++++++++++++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

--- a/drivers/i2c/busses/i2c-bcm2708.c
+++ b/drivers/i2c/busses/i2c-bcm2708.c
@@ -74,6 +74,9 @@ static unsigned int baudrate = CONFIG_I2
 module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 MODULE_PARM_DESC(baudrate, "The I2C baudrate");
 
+static bool combined = false;
+module_param(combined, bool, 0644);
+MODULE_PARM_DESC(combined, "Use combined transactions");
 
 struct bcm2708_i2c {
 	struct i2c_adapter adapter;
@@ -150,7 +153,7 @@ static inline void bcm2708_bsc_fifo_fill
 static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi)
 {
 	unsigned long bus_hz;
-	u32 cdiv;
+	u32 cdiv, s;
 	u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1;
 
 	bus_hz = clk_get_rate(bi->clk);
@@ -166,6 +169,32 @@ static inline void bcm2708_bsc_setup(str
 	bcm2708_wr(bi, BSC_DIV, cdiv);
 	bcm2708_wr(bi, BSC_A, bi->msg->addr);
 	bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
+	if (combined)
+	{
+		/* Do the next two messages meet combined transaction criteria?
+		   - Current message is a write, next message is a read
+		   - Both messages to same slave address
+		   - Write message can fit inside FIFO (16 bytes or less) */
+		if ( (bi->nmsgs > 1) &&
+		    !(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) &&
+		     (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) {
+			/* Fill FIFO with entire write message (16 byte FIFO) */
+			while (bi->pos < bi->msg->len)
+				bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
+			/* Start write transfer (no interrupts, don't clear FIFO) */
+			bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST);
+			/* poll for transfer start bit (should only take 1-20 polls) */
+			do {
+				s = bcm2708_rd(bi, BSC_S);
+			} while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE)));
+			/* Send next read message before the write transfer finishes. */
+			bi->nmsgs--;
+			bi->msg++;
+			bi->pos = 0;
+			bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
+			c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ;
+		}
+	}
 	bcm2708_wr(bi, BSC_C, c);
 }
 
