summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Anderson <dianders@chromium.org>2013-05-02 18:24:21 -0700
committerChromeBot <chrome-bot@google.com>2013-05-08 15:39:38 -0700
commit07d772db29c88711428b8adb00d042c35a367a03 (patch)
treeae70332d5841bd99e5e97740cd1c917d9c2b9f24
parent5df7913825f426935e3fabc1b03ddc9d66ac947e (diff)
downloadchrome-ec-07d772db29c88711428b8adb00d042c35a367a03.tar.gz
i2c: stm32l: Fix i2c reads of sizes other than 1
The STM32L manual has a whole section on i2c master reads and describes the correct method for receiving exactly 2 bytes and more than 2 bytes. We weren't following those instructions and thus larger transfers weren't working. BUG=chrome-os-partner:19265 BRANCH=none TEST=i2cxfer r16 0x90 0 ...doesn't fail TEST=i2cxfer r 0x90 0 ...doesn't fail TEST=Use pydevi2c and test some commands: >>> tps = I2CDevice(20, 0x48) >>> [hex(x) for x in tps.Get(0, 20)] ['0x1e', '0x0', '0x3e', '0x0', '0x12', '0x20', '0x4b', '0xbf', '0xff', '0xff', '0x20', '0x12', '0x1e', '0x1e', '0x1e', '0x1f', '0x1f', '0x1f', '0x1f', '0x1f'] Change-Id: Ifaab6d8b700e099bcd9c374c70fca0983858ed3f Signed-off-by: Doug Anderson <dianders@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/50229 Reviewed-by: Simon Glass <sjg@chromium.org> Tested-by: Simon Glass <sjg@chromium.org>
-rw-r--r--chip/stm32/i2c-stm32l15x.c70
-rw-r--r--chip/stm32/registers.h1
2 files changed, 60 insertions, 11 deletions
diff --git a/chip/stm32/i2c-stm32l15x.c b/chip/stm32/i2c-stm32l15x.c
index e3ff2cb0d1..dc138ba107 100644
--- a/chip/stm32/i2c-stm32l15x.c
+++ b/chip/stm32/i2c-stm32l15x.c
@@ -148,9 +148,11 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes,
*/
STM32_I2C_SR1(port) = 0;
- /* Clear start and stop bits */
- STM32_I2C_CR1(port) &= ~(STM32_I2C_CR1_START | STM32_I2C_CR1_STOP);
-
+ /* Clear start, stop, POS, ACK bits to get us in a known state */
+ STM32_I2C_CR1(port) &= ~(STM32_I2C_CR1_START |
+ STM32_I2C_CR1_STOP |
+ STM32_I2C_CR1_POS |
+ STM32_I2C_CR1_ACK);
if (out_bytes) {
if (!started) {
rv = send_start(port, slave_addr);
@@ -178,27 +180,73 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes,
}
if (in_bytes) {
+ /* Setup ACK/POS before sending start as per user manual */
+ if (in_bytes == 2)
+ STM32_I2C_CR1(port) |= STM32_I2C_CR1_POS;
+ else if (in_bytes != 1)
+ STM32_I2C_CR1(port) |= STM32_I2C_CR1_ACK;
+
if (!started) {
rv = send_start(port, slave_addr | 0x01);
if (rv)
goto xfer_exit;
}
- /* Read data, if any */
- for (i = 0; i < in_bytes; i++) {
- /* Wait for receive buffer not empty */
+ if (in_bytes == 1) {
+ /* Set stop immediately after ADDR cleared */
+ if (flags & I2C_XFER_STOP)
+ STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP;
+
rv = wait_sr1(port, STM32_I2C_SR1_RXNE);
if (rv)
- return rv;
+ goto xfer_exit;
+
+ in[0] = STM32_I2C_DR(port);
+ } else if (in_bytes == 2) {
+ /* Wait till the shift register is full */
+ rv = wait_sr1(port, STM32_I2C_SR1_BTF);
+ if (rv)
+ goto xfer_exit;
- dump_i2c_reg(port, "read data");
+ if (flags & I2C_XFER_STOP)
+ STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP;
+
+ in[0] = STM32_I2C_DR(port);
+ in[1] = STM32_I2C_DR(port);
+ } else {
+ /* Read all but last three */
+ for (i = 0; i < in_bytes - 3; i++) {
+ /* Wait for receive buffer not empty */
+ rv = wait_sr1(port, STM32_I2C_SR1_RXNE);
+ if (rv)
+ goto xfer_exit;
+
+ dump_i2c_reg(port, "read data");
+ in[i] = STM32_I2C_DR(port);
+ dump_i2c_reg(port, "post read data");
+ }
+
+ /* Wait for BTF (data N-2 in DR, N-1 in shift) */
+ rv = wait_sr1(port, STM32_I2C_SR1_BTF);
+ if (rv)
+ goto xfer_exit;
+
+ /* No more acking */
+ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_ACK;
+ in[i++] = STM32_I2C_DR(port);
+
+ /* Wait for BTF (data N-1 in DR, N in shift) */
+ rv = wait_sr1(port, STM32_I2C_SR1_BTF);
+ if (rv)
+ goto xfer_exit;
/* If this is the last byte, queue stop condition */
- if (i == in_bytes - 1 && (flags & I2C_XFER_STOP))
+ if (flags & I2C_XFER_STOP)
STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP;
- in[i] = STM32_I2C_DR(port);
- dump_i2c_reg(port, "post read data");
+ /* Read the last two bytes */
+ in[i++] = STM32_I2C_DR(port);
+ in[i++] = STM32_I2C_DR(port);
}
}
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index cb02973399..2edc9d2fb2 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -283,6 +283,7 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define STM32_I2C_CR1_START (1 << 8)
#define STM32_I2C_CR1_STOP (1 << 9)
#define STM32_I2C_CR1_ACK (1 << 10)
+#define STM32_I2C_CR1_POS (1 << 11)
#define STM32_I2C_CR1_SWRST (1 << 15)
#define STM32_I2C_CR2(n) REG16(stm32_i2c_reg(n, 0x04))
#define STM32_I2C_OAR1(n) REG16(stm32_i2c_reg(n, 0x08))