summaryrefslogtreecommitdiff
path: root/target/linux/brcm2708/patches-4.9/950-0110-i2c-bcm2835-Add-support-for-Repeated-Start-Condition.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-4.9/950-0110-i2c-bcm2835-Add-support-for-Repeated-Start-Condition.patch')
-rw-r--r--target/linux/brcm2708/patches-4.9/950-0110-i2c-bcm2835-Add-support-for-Repeated-Start-Condition.patch181
1 files changed, 181 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.9/950-0110-i2c-bcm2835-Add-support-for-Repeated-Start-Condition.patch b/target/linux/brcm2708/patches-4.9/950-0110-i2c-bcm2835-Add-support-for-Repeated-Start-Condition.patch
new file mode 100644
index 0000000..0cd5aa4
--- /dev/null
+++ b/target/linux/brcm2708/patches-4.9/950-0110-i2c-bcm2835-Add-support-for-Repeated-Start-Condition.patch
@@ -0,0 +1,181 @@
+From db656eb11aebb0d7e4b833f9b452503ddb1351f1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
+Date: Fri, 23 Sep 2016 04:54:27 +0200
+Subject: [PATCH] i2c: bcm2835: Add support for Repeated Start Condition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Documentation/i2c/i2c-protocol states that Combined transactions should
+separate messages with a Start bit and end the whole transaction with a
+Stop bit. This patch adds support for issuing only a Start between
+messages instead of a Stop followed by a Start.
+
+This implementation differs from downstream i2c-bcm2708 in 2 respects:
+- it uses an interrupt to detect that the transfer is active instead
+ of using polling. There is no interrupt for Transfer Active, but by
+ not prefilling the FIFO it's possible to use the TXW interrupt.
+- when resetting/disabling the controller between transfers it writes
+ CLEAR to the control register instead of just zero.
+ Using just zero gave many errors. This might be the reason why
+ downstream had to disable this feature and make it available with a
+ module parameter.
+
+I have run thousands of transfers to a DS1307 (rtc), MMA8451 (accel)
+and AT24C32 (eeprom) in parallel without problems.
+
+Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
+Acked-by: Eric Anholt <eric@anholt.net>
+---
+ drivers/i2c/busses/i2c-bcm2835.c | 101 ++++++++++++++++++++++++---------------
+ 1 file changed, 63 insertions(+), 38 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-bcm2835.c
++++ b/drivers/i2c/busses/i2c-bcm2835.c
+@@ -63,6 +63,7 @@ struct bcm2835_i2c_dev {
+ struct i2c_adapter adapter;
+ struct completion completion;
+ struct i2c_msg *curr_msg;
++ int num_msgs;
+ u32 msg_err;
+ u8 *msg_buf;
+ size_t msg_buf_remaining;
+@@ -110,6 +111,45 @@ static void bcm2835_drain_rxfifo(struct
+ }
+
+ /*
++ * Repeated Start Condition (Sr)
++ * The BCM2835 ARM Peripherals datasheet mentions a way to trigger a Sr when it
++ * talks about reading from a slave with 10 bit address. This is achieved by
++ * issuing a write, poll the I2CS.TA flag and wait for it to be set, and then
++ * issue a read.
++ * A comment in https://github.com/raspberrypi/linux/issues/254 shows how the
++ * firmware actually does it using polling and says that it's a workaround for
++ * a problem in the state machine.
++ * It turns out that it is possible to use the TXW interrupt to know when the
++ * transfer is active, provided the FIFO has not been prefilled.
++ */
++
++static void bcm2835_i2c_start_transfer(struct bcm2835_i2c_dev *i2c_dev)
++{
++ u32 c = BCM2835_I2C_C_ST | BCM2835_I2C_C_I2CEN;
++ struct i2c_msg *msg = i2c_dev->curr_msg;
++ bool last_msg = (i2c_dev->num_msgs == 1);
++
++ if (!i2c_dev->num_msgs)
++ return;
++
++ i2c_dev->num_msgs--;
++ i2c_dev->msg_buf = msg->buf;
++ i2c_dev->msg_buf_remaining = msg->len;
++
++ if (msg->flags & I2C_M_RD)
++ c |= BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR;
++ else
++ c |= BCM2835_I2C_C_INTT;
++
++ if (last_msg)
++ c |= BCM2835_I2C_C_INTD;
++
++ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
++ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
++ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
++}
++
++/*
+ * Note about I2C_C_CLEAR on error:
+ * The I2C_C_CLEAR on errors will take some time to resolve -- if you were in
+ * non-idle state and I2C_C_READ, it sets an abort_rx flag and runs through
+@@ -151,6 +191,12 @@ static irqreturn_t bcm2835_i2c_isr(int t
+ }
+
+ bcm2835_fill_txfifo(i2c_dev);
++
++ if (i2c_dev->num_msgs && !i2c_dev->msg_buf_remaining) {
++ i2c_dev->curr_msg++;
++ bcm2835_i2c_start_transfer(i2c_dev);
++ }
++
+ return IRQ_HANDLED;
+ }
+
+@@ -175,30 +221,25 @@ complete:
+ return IRQ_HANDLED;
+ }
+
+-static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
+- struct i2c_msg *msg)
++static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
++ int num)
+ {
+- u32 c;
++ struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ unsigned long time_left;
++ int i;
+
+- i2c_dev->curr_msg = msg;
+- i2c_dev->msg_buf = msg->buf;
+- i2c_dev->msg_buf_remaining = msg->len;
+- reinit_completion(&i2c_dev->completion);
+-
+- bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
++ for (i = 0; i < (num - 1); i++)
++ if (msgs[i].flags & I2C_M_RD) {
++ dev_warn_once(i2c_dev->dev,
++ "only one read message supported, has to be last\n");
++ return -EOPNOTSUPP;
++ }
+
+- if (msg->flags & I2C_M_RD) {
+- c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR;
+- } else {
+- c = BCM2835_I2C_C_INTT;
+- bcm2835_fill_txfifo(i2c_dev);
+- }
+- c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN;
++ i2c_dev->curr_msg = msgs;
++ i2c_dev->num_msgs = num;
++ reinit_completion(&i2c_dev->completion);
+
+- bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
+- bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
+- bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
++ bcm2835_i2c_start_transfer(i2c_dev);
+
+ time_left = wait_for_completion_timeout(&i2c_dev->completion,
+ BCM2835_I2C_TIMEOUT);
+@@ -209,31 +250,15 @@ static int bcm2835_i2c_xfer_msg(struct b
+ return -ETIMEDOUT;
+ }
+
+- if (likely(!i2c_dev->msg_err))
+- return 0;
++ if (!i2c_dev->msg_err)
++ return num;
+
+ dev_dbg(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
+
+ if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
+ return -EREMOTEIO;
+- else
+- return -EIO;
+-}
+-
+-static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+- int num)
+-{
+- struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+- int i;
+- int ret = 0;
+-
+- for (i = 0; i < num; i++) {
+- ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]);
+- if (ret)
+- break;
+- }
+
+- return ret ?: i;
++ return -EIO;
+ }
+
+ static u32 bcm2835_i2c_func(struct i2c_adapter *adap)