summaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-3.18/0020-Perform-I2C-combined-transactions-when-possible.patch
blob: 236f9c60309681e7f458d13d72603fe8aadd3f27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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(-)

diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c
index 09203c0..7d385a3 100644
--- a/drivers/i2c/busses/i2c-bcm2708.c
+++ b/drivers/i2c/busses/i2c-bcm2708.c
@@ -74,6 +74,9 @@ static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE;
 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(struct bcm2708_i2c *bi)
 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(struct bcm2708_i2c *bi)
 	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);
 }
 
-- 
1.8.3.2