From 19baa9d51e4f1b36473dc750735eb6e5345bebda Mon Sep 17 00:00:00 2001 From: Jes Klinke Date: Tue, 22 Feb 2022 16:00:09 -0800 Subject: i2c: Add configurable I2C transfer timeout This patch introduces CONFIG_I2C_TRANSFER_TIMEOUT_US, which controls how long to wait for an I2C devices to produce/accept all the data bytes in a single transfer. (The device can delay transfer by stretching the clock of the ack bit.) The default value of this new setting is 500ms. Existing code had timeouts anywhere from tens of milliseconds to a full second beween various drivers. Drivers can still have their own shorter timeouts for setup/communication with the I2C host controller (as opposed to transactions with I2C devices on the bus.) In general, the timeout is not meant to be reached except in situations where there is already serious problem with the boot, and serves to make sure that some useful diagnostic output is produced on the console. Change-Id: I6423122f32aad1dbcee0bfe240cdaa8cb512791f Signed-off-by: Jes B. Klinke Reviewed-on: https://review.coreboot.org/c/coreboot/+/62278 Tested-by: build bot (Jenkins) Reviewed-by: Julius Werner --- src/soc/samsung/exynos5420/i2c.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src/soc/samsung/exynos5420') diff --git a/src/soc/samsung/exynos5420/i2c.c b/src/soc/samsung/exynos5420/i2c.c index ab17d52c44..49505a0782 100644 --- a/src/soc/samsung/exynos5420/i2c.c +++ b/src/soc/samsung/exynos5420/i2c.c @@ -11,6 +11,8 @@ #include #include +#define I2C_TIMEOUT_US (1000 * USECS_PER_MSEC) + struct __packed i2c_regs { uint8_t con; @@ -508,9 +510,9 @@ static int i2c_got_ack(struct i2c_regs *regs) return !(read8(®s->stat) & I2cStatAck); } -static int i2c_wait_for_idle(struct i2c_regs *regs) +static int i2c_wait_for_idle(struct i2c_regs *regs, int timeout_us) { - int timeout = 1000 * 100; // 1s. + int timeout = timeout_us / 10; while (timeout--) { if (!(read8(®s->stat) & I2cStatBusy)) return 0; @@ -520,9 +522,9 @@ static int i2c_wait_for_idle(struct i2c_regs *regs) return 1; } -static int i2c_wait_for_int(struct i2c_regs *regs) +static int i2c_wait_for_int(struct i2c_regs *regs, int timeout_us) { - int timeout = 1000 * 100; // 1s. + int timeout = timeout_us / 10; while (timeout--) { if (i2c_int_pending(regs)) return 0; @@ -537,7 +539,7 @@ static int i2c_send_stop(struct i2c_regs *regs) uint8_t mode = read8(®s->stat) & (I2cStatModeMask); write8(®s->stat, mode | I2cStatEnable); i2c_clear_int(regs); - return i2c_wait_for_idle(regs); + return i2c_wait_for_idle(regs, I2C_TIMEOUT_US); } static int i2c_send_start(struct i2c_regs *regs, int read, int chip) @@ -547,7 +549,7 @@ static int i2c_send_start(struct i2c_regs *regs, int read, int chip) write8(®s->stat, mode | I2cStatStartStop | I2cStatEnable); i2c_clear_int(regs); - if (i2c_wait_for_int(regs)) + if (i2c_wait_for_int(regs, I2C_TIMEOUT_US)) return 1; if (!i2c_got_ack(regs)) { @@ -569,7 +571,7 @@ static int i2c_xmit_buf(struct i2c_regs *regs, uint8_t *data, int len) write8(®s->ds, data[i]); i2c_clear_int(regs); - if (i2c_wait_for_int(regs)) + if (i2c_wait_for_int(regs, CONFIG_I2C_TRANSFER_TIMEOUT_US)) return 1; if (!i2c_got_ack(regs)) { @@ -593,7 +595,7 @@ static int i2c_recv_buf(struct i2c_regs *regs, uint8_t *data, int len) i2c_ack_disable(regs); i2c_clear_int(regs); - if (i2c_wait_for_int(regs)) + if (i2c_wait_for_int(regs, CONFIG_I2C_TRANSFER_TIMEOUT_US)) return 1; data[i] = read8(®s->ds); @@ -611,7 +613,7 @@ int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments, int count) struct i2c_regs *regs = i2c->regs; int res = 0; - if (!regs || i2c_wait_for_idle(regs)) + if (!regs || i2c_wait_for_idle(regs, I2C_TIMEOUT_US)) return 1; write8(®s->stat, I2cStatMasterXmit | I2cStatEnable); -- cgit v1.2.3