diff options
author | Doug Anderson <dianders@chromium.org> | 2013-05-02 18:24:21 -0700 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-05-08 15:39:38 -0700 |
commit | 07d772db29c88711428b8adb00d042c35a367a03 (patch) | |
tree | ae70332d5841bd99e5e97740cd1c917d9c2b9f24 | |
parent | 5df7913825f426935e3fabc1b03ddc9d66ac947e (diff) | |
download | chrome-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.c | 70 | ||||
-rw-r--r-- | chip/stm32/registers.h | 1 |
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)) |