summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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))