From 608fa5efd868325ce85fca1f777a89c0909ea032 Mon Sep 17 00:00:00 2001 From: Alec Berg Date: Thu, 21 May 2015 13:29:03 -0700 Subject: [PATCH] stm32f0: i2c: fix master i2c sending partial transfers Fix master i2c when sending partial transfers using I2C_XFER_START and I2C_XFER_STOP. BUG=none BRANCH=none TEST=Tested i2c transfers on oak. tested transfers with I2C_XFER_SINGLE (I2C_XFER_START | I2C_XFER_STOP) and tested partial transfers with just one flag. For partial transfers I tested two different types: - i2c_xfer START only transmitting, then another i2c_xfer with more trasnmitting followed by a STOP. verified with logic analyzer that there is not restart in the middle. - i2c_xfer START with transmitting and receiving, then another i2c_xfer with more receiving followed by a STOP. verified with logic analyzer that there is one restart in between transmitting and receving and no restart in between the two calls to i2c_xfer. Change-Id: Ie4146d1cf7d39f7dc56fd02e65add6bf02772e67 Signed-off-by: Alec Berg Reviewed-on: https://chromium-review.googlesource.com/272690 Reviewed-by: Vincent Palatin --- chip/stm32/i2c-stm32f0.c | 34 +++++++++++++++++++++++++++------- chip/stm32/registers.h | 1 + 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/chip/stm32/i2c-stm32f0.c b/chip/stm32/i2c-stm32f0.c index 9d43f9cb27..4e599efa9a 100644 --- a/chip/stm32/i2c-stm32f0.c +++ b/chip/stm32/i2c-stm32f0.c @@ -340,13 +340,19 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes, } if (out_bytes || !in_bytes) { - /* Configure the write transfer */ + /* + * Configure the write transfer: if we are stopping then set + * AUTOEND bit to automatically set STOP bit after NBYTES. + * if we are not stopping, set RELOAD bit so that we can load + * NBYTES again. if we are starting, then set START bit. + */ STM32_I2C_CR2(port) = ((out_bytes & 0xFF) << 16) | slave_addr | ((in_bytes == 0 && xfer_stop) ? - STM32_I2C_CR2_AUTOEND : 0); - /* let's go ... */ - STM32_I2C_CR2(port) |= STM32_I2C_CR2_START; + STM32_I2C_CR2_AUTOEND : 0) + | ((in_bytes == 0 && !xfer_stop) ? + STM32_I2C_CR2_RELOAD : 0) + | (xfer_start ? STM32_I2C_CR2_START : 0); for (i = 0; i < out_bytes; i++) { rv = wait_isr(port, STM32_I2C_ISR_TXIS); @@ -362,11 +368,18 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes, if (rv) goto xfer_exit; } - /* Configure the read transfer and (re)start */ + /* + * Configure the read transfer: if we are stopping then set + * AUTOEND bit to automatically set STOP bit after NBYTES. + * if we are not stopping, set RELOAD bit so that we can load + * NBYTES again. if we were just transmitting, we need to + * set START bit to send (re)start and begin read transaction. + */ STM32_I2C_CR2(port) = ((in_bytes & 0xFF) << 16) | STM32_I2C_CR2_RD_WRN | slave_addr | (xfer_stop ? STM32_I2C_CR2_AUTOEND : 0) - | STM32_I2C_CR2_START; + | (!xfer_stop ? STM32_I2C_CR2_RELOAD : 0) + | (out_bytes ? STM32_I2C_CR2_START : 0); for (i = 0; i < in_bytes; i++) { /* Wait for receive buffer not empty */ @@ -377,7 +390,14 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes, in[i] = STM32_I2C_RXDR(port); } } - rv = wait_isr(port, xfer_stop ? STM32_I2C_ISR_STOP : STM32_I2C_ISR_TC); + + /* + * If we are stopping, then we already set AUTOEND and we should + * wait for the stop bit to be transmitted. Otherwise, we set + * the RELOAD bit and we should wait for transfer complete + * reload (TCR). + */ + rv = wait_isr(port, xfer_stop ? STM32_I2C_ISR_STOP : STM32_I2C_ISR_TCR); if (rv) goto xfer_exit; diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index 52c74ef64f..f9ff3d1c69 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -474,6 +474,7 @@ typedef volatile struct timer_ctlr timer_ctlr_t; #define STM32_I2C_ISR_NACK (1 << 4) #define STM32_I2C_ISR_STOP (1 << 5) #define STM32_I2C_ISR_TC (1 << 6) +#define STM32_I2C_ISR_TCR (1 << 7) #define STM32_I2C_ISR_BERR (1 << 8) #define STM32_I2C_ISR_ARLO (1 << 9) #define STM32_I2C_ISR_OVR (1 << 10)