From a07b4c45bb38817611251bf8e4fcd9d5e78b336b Mon Sep 17 00:00:00 2001 From: cbeytas Date: Mon, 24 Jun 2013 00:05:40 -0400 Subject: [PATCH 1/3] Perform I2C combined transactions when possible Perform I2C combined transactions whenever possible, within the restrictions of the Broadcomm Serial Controller. --- drivers/i2c/busses/i2c-bcm2708.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c index b2b3fbacdb3b3c..379f3e3fffef90 100644 --- a/drivers/i2c/busses/i2c-bcm2708.c +++ b/drivers/i2c/busses/i2c-bcm2708.c @@ -149,7 +149,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); @@ -163,6 +163,29 @@ 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); + /* 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 TxW interrupts, don't clear FIFO) */ + bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_INTD | 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); } From 4a5f0c0a8e832326d994cd514e93a339aa8ba6e4 Mon Sep 17 00:00:00 2001 From: cbeytas Date: Thu, 27 Jun 2013 20:15:02 -0400 Subject: [PATCH 2/3] Disable DONE interrupt during TA poll Prevent interrupt from being triggered if poll is missed and transfer starts and finishes. --- drivers/i2c/busses/i2c-bcm2708.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c index 379f3e3fffef90..429da716451271 100644 --- a/drivers/i2c/busses/i2c-bcm2708.c +++ b/drivers/i2c/busses/i2c-bcm2708.c @@ -173,8 +173,8 @@ static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) /* 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 TxW interrupts, don't clear FIFO) */ - bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST); + /* 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); From e1d60ddc0c47448b9611787029b6a9ca2f6516fe Mon Sep 17 00:00:00 2001 From: cbeytas Date: Tue, 2 Jul 2013 00:09:10 -0400 Subject: [PATCH 3/3] Clear FIFO before starting transfer Fix driver hang problem. If previous transfer timed out, the next transfer could deadlock the interrupt handler due to extra bytes present in FIFO. --- drivers/i2c/busses/i2c-bcm2708.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c index 429da716451271..6b74f218cc2769 100644 --- a/drivers/i2c/busses/i2c-bcm2708.c +++ b/drivers/i2c/busses/i2c-bcm2708.c @@ -170,12 +170,13 @@ static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) 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) */ + /* Clear FIFO and fill with entire write message */ + bcm2708_wr(bi, BSC_C, BSC_C_CLEAR_1); 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) */ + /* Start write transfer (no interrupts) */ bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST); - /* poll for transfer start bit (should only take 1-20 polls) */ + /* poll for transfer start bit (should only take 1-60 polls) */ do { s = bcm2708_rd(bi, BSC_S); } while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE)));