summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/stm32/i2c.c38
1 files changed, 31 insertions, 7 deletions
diff --git a/chip/stm32/i2c.c b/chip/stm32/i2c.c
index 2d98d37e1e..42b6da1be4 100644
--- a/chip/stm32/i2c.c
+++ b/chip/stm32/i2c.c
@@ -57,8 +57,16 @@
#define I2C2 STM32_I2C2_PORT
enum {
- /* A stop condition should take 2 clocks, so allow 8 */
- TIMEOUT_STOP_SENT_US = I2C_PERIOD_US * 8,
+ /*
+ * A stop condition should take 2 clocks, but the process may need more
+ * time to notice if it is preempted, so we poll repeatedly for 8
+ * clocks, before backing off and only check once every
+ * STOP_SENT_RETRY_US for up to TIMEOUT_STOP_SENT clocks before
+ * giving up.
+ */
+ SLOW_STOP_SENT_US = I2C_PERIOD_US * 8,
+ TIMEOUT_STOP_SENT_US = I2C_PERIOD_US * 200,
+ STOP_SENT_RETRY_US = 150,
};
static uint16_t i2c_sr1[NUM_PORTS];
@@ -333,9 +341,9 @@ static void i2c_error_handler(int port)
/* ACK failed (NACK); expected when AP reads final byte.
* Software must clear AF bit. */
} else {
- CPRINTF("%s: I2C_SR1(%s): 0x%04x\n",
+ CPRINTF("%s: I2C_SR1(%d): 0x%04x\n",
__func__, port, i2c_sr1[port]);
- CPRINTF("%s: I2C_SR2(%s): 0x%04x\n",
+ CPRINTF("%s: I2C_SR2(%d): 0x%04x\n",
__func__, port, STM32_I2C_SR2(port));
}
@@ -573,16 +581,32 @@ static void master_stop(int port)
static int wait_until_stop_sent(int port)
{
timestamp_t deadline;
+ timestamp_t slow_cutoff;
+ uint8_t is_slow;
- deadline = get_time();
+ deadline = slow_cutoff = get_time();
deadline.val += TIMEOUT_STOP_SENT_US;
+ slow_cutoff.val += SLOW_STOP_SENT_US;
while (STM32_I2C_CR1(port) & (1 << 9)) {
if (timestamp_expired(deadline, NULL)) {
- ccprintf("Stop event deadline passed: CR1=%016b\n",
- STM32_I2C_CR1(port));
+ ccprintf("Stop event deadline passed:\ttask=%d"
+ "\tCR1=%016b\n",
+ (int)task_get_current(), STM32_I2C_CR1(port));
return EC_ERROR_TIMEOUT;
}
+
+ if (is_slow) {
+ /* If we haven't gotten a fast response, sleep */
+ usleep(STOP_SENT_RETRY_US);
+ } else {
+ /* Check to see if this request is taking a while */
+ if (timestamp_expired(slow_cutoff, NULL)) {
+ ccprintf("Stop event taking a while: task=%d",
+ (int)task_get_current());
+ is_slow = 1;
+ }
+ }
}
return EC_SUCCESS;