summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJes Klinke <jbk@google.com>2022-02-22 16:00:09 -0800
committerJulius Werner <jwerner@chromium.org>2022-03-15 22:06:27 +0000
commit19baa9d51e4f1b36473dc750735eb6e5345bebda (patch)
tree60aada7f006fd73797b5c566d704e2b0ed5b728f
parentca82e6161af7a453b512f35dd695a98084a1d7cf (diff)
downloadcoreboot-19baa9d51e4f1b36473dc750735eb6e5345bebda.tar.gz
coreboot-19baa9d51e4f1b36473dc750735eb6e5345bebda.tar.bz2
coreboot-19baa9d51e4f1b36473dc750735eb6e5345bebda.zip
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 <jbk@chromium.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/62278 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Julius Werner <jwerner@chromium.org>
-rw-r--r--src/device/Kconfig8
-rw-r--r--src/drivers/i2c/designware/dw_i2c.c10
-rw-r--r--src/soc/cavium/cn81xx/twsi.c27
-rw-r--r--src/soc/intel/quark/i2c.c2
-rw-r--r--src/soc/mediatek/common/i2c.c2
-rw-r--r--src/soc/qualcomm/common/include/soc/qup_se_handlers_common.h2
-rw-r--r--src/soc/qualcomm/common/qup_se_handler.c7
-rw-r--r--src/soc/qualcomm/common/qupv3_i2c.c4
-rw-r--r--src/soc/qualcomm/common/qupv3_spi.c4
-rw-r--r--src/soc/qualcomm/ipq40xx/qup.c32
-rw-r--r--src/soc/qualcomm/ipq806x/qup.c28
-rw-r--r--src/soc/qualcomm/qcs405/qup.c32
-rw-r--r--src/soc/rockchip/common/i2c.c6
-rw-r--r--src/soc/samsung/exynos5250/i2c.c21
-rw-r--r--src/soc/samsung/exynos5420/i2c.c20
15 files changed, 119 insertions, 86 deletions
diff --git a/src/device/Kconfig b/src/device/Kconfig
index 7f20d709ad39..388972ccf4f6 100644
--- a/src/device/Kconfig
+++ b/src/device/Kconfig
@@ -906,6 +906,14 @@ config SOFTWARE_I2C
I2C controller is not (yet) available. The platform code needs to
provide bindings to manually toggle I2C lines.
+config I2C_TRANSFER_TIMEOUT_US
+ int "I2C transfer timeout in microseconds"
+ default 500000
+ help
+ Timeout for a read/write transfers on the I2C bus, that is, the
+ maximum time a device could stretch clock bits before the transfer
+ is aborted and an error returned.
+
config RESOURCE_ALLOCATOR_V3
bool
default n
diff --git a/src/drivers/i2c/designware/dw_i2c.c b/src/drivers/i2c/designware/dw_i2c.c
index 2cc236e81b02..56f3f27c1d52 100644
--- a/src/drivers/i2c/designware/dw_i2c.c
+++ b/src/drivers/i2c/designware/dw_i2c.c
@@ -13,6 +13,8 @@
/* Use a ~10ms timeout for various operations */
#define DW_I2C_TIMEOUT_US 10000
+/* Timeout for waiting for FIFO to flush */
+#define DW_I2C_FLUSH_TIMEOUT_US 160000
/* High and low times in different speed modes (in ns) */
enum {
@@ -290,7 +292,7 @@ static enum cb_err dw_i2c_wait_for_bus_idle(struct dw_i2c_regs *regs)
struct stopwatch sw;
/* Start timeout for up to 16 bytes in FIFO */
- stopwatch_init_usecs_expire(&sw, 16 * DW_I2C_TIMEOUT_US);
+ stopwatch_init_usecs_expire(&sw, DW_I2C_FLUSH_TIMEOUT_US);
while (!stopwatch_expired(&sw)) {
uint32_t status = read32(&regs->status);
@@ -316,7 +318,7 @@ static enum cb_err dw_i2c_transfer_byte(struct dw_i2c_regs *regs,
struct stopwatch sw;
uint32_t cmd = CMD_DATA_CMD; /* Read op */
- stopwatch_init_usecs_expire(&sw, DW_I2C_TIMEOUT_US);
+ stopwatch_init_usecs_expire(&sw, CONFIG_I2C_TRANSFER_TIMEOUT_US);
if (!(segment->flags & I2C_M_RD)) {
/* Write op only: Wait for FIFO not full */
@@ -409,7 +411,7 @@ static enum cb_err _dw_i2c_transfer(unsigned int bus, const struct i2c_msg *segm
}
/* Wait for interrupt status to indicate transfer is complete */
- stopwatch_init_usecs_expire(&sw, DW_I2C_TIMEOUT_US);
+ stopwatch_init_usecs_expire(&sw, CONFIG_I2C_TRANSFER_TIMEOUT_US);
while (!(read32(&regs->raw_intr_stat) & INTR_STAT_STOP_DET)) {
if (stopwatch_expired(&sw)) {
printk(BIOS_ERR, "I2C stop bit not received\n");
@@ -436,7 +438,7 @@ static enum cb_err _dw_i2c_transfer(unsigned int bus, const struct i2c_msg *segm
}
/* Flush the RX FIFO in case it is not empty */
- stopwatch_init_usecs_expire(&sw, 16 * DW_I2C_TIMEOUT_US);
+ stopwatch_init_usecs_expire(&sw, DW_I2C_FLUSH_TIMEOUT_US);
while (read32(&regs->status) & STATUS_RX_FIFO_NOT_EMPTY) {
if (stopwatch_expired(&sw)) {
printk(BIOS_ERR, "I2C timeout flushing RX FIFO\n");
diff --git a/src/soc/cavium/cn81xx/twsi.c b/src/soc/cavium/cn81xx/twsi.c
index bb91b88ebb3b..b1c1e66fae8d 100644
--- a/src/soc/cavium/cn81xx/twsi.c
+++ b/src/soc/cavium/cn81xx/twsi.c
@@ -12,6 +12,7 @@
#include <delay.h>
#include <device/mmio.h>
#include <soc/addressmap.h>
+#include <timer.h>
#define TWSI_THP 24
@@ -348,17 +349,15 @@ static u8 twsi_read_status(void *baseaddr)
*
* @return 0 for success, 1 if timeout
*/
-static int twsi_wait(void *baseaddr)
+static int twsi_wait(void *baseaddr, struct stopwatch *timeout)
{
- unsigned long timeout = 500000;
u8 twsi_ctl;
printk(BIOS_SPEW, "%s(%p)\n", __func__, baseaddr);
do {
twsi_ctl = twsi_read_ctl(baseaddr);
twsi_ctl &= TWSI_CTL_IFLG;
- timeout--;
- } while (!twsi_ctl && timeout > 0);
+ } while (!twsi_ctl && !stopwatch_expired(timeout));
printk(BIOS_SPEW, " return: %u\n", !twsi_ctl);
return !twsi_ctl;
@@ -438,10 +437,12 @@ static int twsi_start(void *baseaddr)
{
int result;
u8 stat;
+ struct stopwatch timeout;
printk(BIOS_SPEW, "%s(%p)\n", __func__, baseaddr);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
twsi_write_ctl(baseaddr, TWSI_CTL_STA | TWSI_CTL_ENAB);
- result = twsi_wait(baseaddr);
+ result = twsi_wait(baseaddr, &timeout);
if (result) {
stat = twsi_read_status(baseaddr);
printk(BIOS_SPEW, "%s: result: 0x%x, status: 0x%x\n", __func__,
@@ -475,9 +476,11 @@ static int twsi_write_data(void *baseaddr, const u8 slave_addr,
union twsx_sw_twsi twsi_sw;
unsigned int curr = 0;
int result;
+ struct stopwatch timeout;
printk(BIOS_SPEW, "%s(%p, 0x%x, %p, 0x%x)\n", __func__, baseaddr,
slave_addr, buffer, length);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
result = twsi_start(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: Could not start BUS transaction\n",
@@ -485,7 +488,7 @@ static int twsi_write_data(void *baseaddr, const u8 slave_addr,
return -1;
}
- result = twsi_wait(baseaddr);
+ result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: wait failed\n", __func__);
return result;
@@ -500,7 +503,7 @@ static int twsi_write_data(void *baseaddr, const u8 slave_addr,
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
printk(BIOS_SPEW, "%s: Waiting\n", __func__);
- result = twsi_wait(baseaddr);
+ result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: Timed out writing slave address 0x%x\n",
__func__, slave_addr);
@@ -521,7 +524,7 @@ static int twsi_write_data(void *baseaddr, const u8 slave_addr,
twsi_write_sw(baseaddr, twsi_sw);
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
- result = twsi_wait(baseaddr);
+ result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: Timed out writing data to 0x%x\n",
__func__, slave_addr);
@@ -549,16 +552,18 @@ static int twsi_read_data(void *baseaddr, const u8 slave_addr,
union twsx_sw_twsi twsi_sw;
unsigned int curr = 0;
int result;
+ struct stopwatch timeout;
printk(BIOS_SPEW, "%s(%p, 0x%x, %p, %u)\n", __func__, baseaddr,
slave_addr, buffer, length);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
result = twsi_start(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: start failed\n", __func__);
return result;
}
- result = twsi_wait(baseaddr);
+ result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: wait failed\n", __func__);
return result;
@@ -574,7 +579,7 @@ static int twsi_read_data(void *baseaddr, const u8 slave_addr,
twsi_write_sw(baseaddr, twsi_sw);
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
- result = twsi_wait(baseaddr);
+ result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: waiting for sending addr failed\n", __func__);
return result;
@@ -590,7 +595,7 @@ static int twsi_read_data(void *baseaddr, const u8 slave_addr,
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB |
((curr < length - 1) ? TWSI_CTL_AAK : 0));
- result = twsi_wait(baseaddr);
+ result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: waiting for data failed\n",
__func__);
diff --git a/src/soc/intel/quark/i2c.c b/src/soc/intel/quark/i2c.c
index bb62a7ad0116..42ca25dbd9bb 100644
--- a/src/soc/intel/quark/i2c.c
+++ b/src/soc/intel/quark/i2c.c
@@ -243,7 +243,7 @@ int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segment,
status = regs->ic_clr_tx_abrt;
/* Start the timeout */
- stopwatch_init_msecs_expire(&timeout, 1000);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
/* Process each of the segments */
total_bytes = 0;
diff --git a/src/soc/mediatek/common/i2c.c b/src/soc/mediatek/common/i2c.c
index c47e08e8aa94..b4386b96fb6e 100644
--- a/src/soc/mediatek/common/i2c.c
+++ b/src/soc/mediatek/common/i2c.c
@@ -239,7 +239,7 @@ static int mtk_i2c_transfer(uint8_t bus, struct i2c_msg *seg,
/* start transfer transaction */
write32(&regs->start, 0x1);
- stopwatch_init_msecs_expire(&sw, 100);
+ stopwatch_init_usecs_expire(&sw, CONFIG_I2C_TRANSFER_TIMEOUT_US);
/* polling mode : see if transaction complete */
while (1) {
diff --git a/src/soc/qualcomm/common/include/soc/qup_se_handlers_common.h b/src/soc/qualcomm/common/include/soc/qup_se_handlers_common.h
index bab7434f0d58..a004c7944c53 100644
--- a/src/soc/qualcomm/common/include/soc/qup_se_handlers_common.h
+++ b/src/soc/qualcomm/common/include/soc/qup_se_handlers_common.h
@@ -463,6 +463,6 @@ u32 qup_wait_for_s_irq(unsigned int bus);
void qup_m_cancel_and_abort(unsigned int bus);
void qup_s_cancel_and_abort(unsigned int bus);
int qup_handle_transfer(unsigned int bus, const void *dout, void *din,
- int size);
+ int size, struct stopwatch *timeout);
#endif /* __SOC_COMMON_QCOM_QUP_SE_H__ */
diff --git a/src/soc/qualcomm/common/qup_se_handler.c b/src/soc/qualcomm/common/qup_se_handler.c
index bb7be37cf016..7dd4f452af40 100644
--- a/src/soc/qualcomm/common/qup_se_handler.c
+++ b/src/soc/qualcomm/common/qup_se_handler.c
@@ -149,15 +149,14 @@ void qup_s_cancel_and_abort(unsigned int bus)
}
}
-int qup_handle_transfer(unsigned int bus, const void *dout, void *din, int size)
+int qup_handle_transfer(unsigned int bus, const void *dout, void *din, int size,
+ struct stopwatch *timeout)
{
unsigned int m_irq;
- struct stopwatch sw;
unsigned int rx_rem_bytes = din ? size : 0;
unsigned int tx_rem_bytes = dout ? size : 0;
struct qup_regs *regs = qup[bus].regs;
- stopwatch_init_msecs_expire(&sw, 1000);
do {
m_irq = qup_wait_for_m_irq(bus);
if ((m_irq & M_RX_FIFO_WATERMARK_EN) ||
@@ -172,7 +171,7 @@ int qup_handle_transfer(unsigned int bus, const void *dout, void *din, int size)
break;
}
write32(&regs->geni_m_irq_clear, m_irq);
- } while (!stopwatch_expired(&sw));
+ } while (!stopwatch_expired(timeout));
if (!(m_irq & M_CMD_DONE_EN) || tx_rem_bytes || rx_rem_bytes) {
printk(BIOS_INFO, "%s:Error: Transfer failed\n", __func__);
diff --git a/src/soc/qualcomm/common/qupv3_i2c.c b/src/soc/qualcomm/common/qupv3_i2c.c
index 606b3bf9353d..8f0880e35d6a 100644
--- a/src/soc/qualcomm/common/qupv3_i2c.c
+++ b/src/soc/qualcomm/common/qupv3_i2c.c
@@ -117,6 +117,7 @@ static int i2c_do_xfer(unsigned int bus, struct i2c_msg segment,
unsigned int master_cmd_reg_val = (cmd << M_OPCODE_SHFT);
struct qup_regs *regs = qup[bus].regs;
void *dout = NULL, *din = NULL;
+ struct stopwatch timeout;
if (!(segment.flags & I2C_M_RD)) {
write32(&regs->i2c_tx_trans_len, segment.len);
@@ -130,7 +131,8 @@ static int i2c_do_xfer(unsigned int bus, struct i2c_msg segment,
master_cmd_reg_val |= (prams & M_PARAMS_MSK);
write32(&regs->geni_m_cmd0, master_cmd_reg_val);
- return qup_handle_transfer(bus, dout, din, segment.len);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
+ return qup_handle_transfer(bus, dout, din, segment.len, &timeout);
}
int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
diff --git a/src/soc/qualcomm/common/qupv3_spi.c b/src/soc/qualcomm/common/qupv3_spi.c
index c69ecf9eaa4e..1bb5c75e5e9e 100644
--- a/src/soc/qualcomm/common/qupv3_spi.c
+++ b/src/soc/qualcomm/common/qupv3_spi.c
@@ -83,6 +83,7 @@ int qup_spi_xfer(const struct spi_slave *slave, const void *dout,
int size;
unsigned int se_bus = slave->bus;
struct qup_regs *regs = qup[se_bus].regs;
+ struct stopwatch timeout;
if ((bytes_in == 0) && (bytes_out == 0))
return 0;
@@ -114,7 +115,8 @@ int qup_spi_xfer(const struct spi_slave *slave, const void *dout,
qup_setup_m_cmd(se_bus, m_cmd, m_param);
- if (qup_handle_transfer(se_bus, dout, din, size))
+ stopwatch_init_msecs_expire(&timeout, 1000);
+ if (qup_handle_transfer(se_bus, dout, din, size, &timeout))
return -1;
qup_spi_xfer(slave, dout + size, MAX((int)bytes_out - size, 0),
diff --git a/src/soc/qualcomm/ipq40xx/qup.c b/src/soc/qualcomm/ipq40xx/qup.c
index 76f079744cdb..88e9169ec73c 100644
--- a/src/soc/qualcomm/ipq40xx/qup.c
+++ b/src/soc/qualcomm/ipq40xx/qup.c
@@ -3,6 +3,7 @@
#include <device/mmio.h>
#include <console/console.h>
#include <delay.h>
+#include <timer.h>
#include <soc/iomap.h>
#include <soc/qup.h>
@@ -93,35 +94,33 @@ static qup_return_t qup_reset_master_status(blsp_qup_id_t id)
return QUP_SUCCESS;
}
-static qup_return_t qup_fifo_wait_for(blsp_qup_id_t id, uint32_t status)
+static qup_return_t qup_fifo_wait_for(blsp_qup_id_t id, uint32_t status,
+ struct stopwatch *timeout)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
- unsigned int count = TIMEOUT_CNT;
while (!(read32(QUP_ADDR(id, QUP_OPERATIONAL)) & status)) {
ret = qup_i2c_master_status(id);
if (ret)
return ret;
- if (count == 0)
+ if (stopwatch_expired(timeout))
return QUP_ERR_TIMEOUT;
- count--;
}
return QUP_SUCCESS;
}
-static qup_return_t qup_fifo_wait_while(blsp_qup_id_t id, uint32_t status)
+static qup_return_t qup_fifo_wait_while(blsp_qup_id_t id, uint32_t status,
+ struct stopwatch *timeout)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
- unsigned int count = TIMEOUT_CNT;
while (read32(QUP_ADDR(id, QUP_OPERATIONAL)) & status) {
ret = qup_i2c_master_status(id);
if (ret)
return ret;
- if (count == 0)
+ if (stopwatch_expired(timeout))
return QUP_ERR_TIMEOUT;
- count--;
}
return QUP_SUCCESS;
@@ -139,7 +138,8 @@ static inline uint32_t qup_i2c_create_output_tag(int stop, u8 data)
return tag;
}
-static inline qup_return_t qup_i2c_write_fifo_flush(blsp_qup_id_t id)
+static inline qup_return_t qup_i2c_write_fifo_flush(blsp_qup_id_t id,
+ struct stopwatch *timeout)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
@@ -147,7 +147,7 @@ static inline qup_return_t qup_i2c_write_fifo_flush(blsp_qup_id_t id)
mdelay(4); /* TPM seems to need this */
- ret = qup_fifo_wait_while(id, OUTPUT_FIFO_NOT_EMPTY);
+ ret = qup_fifo_wait_while(id, OUTPUT_FIFO_NOT_EMPTY, timeout);
if (ret)
return ret;
@@ -168,6 +168,7 @@ static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj,
unsigned int data_len = p_tx_obj->p.iic.data_len;
unsigned int idx = 0;
uint32_t tag, *fifo = QUP_ADDR(id, QUP_OUTPUT_FIFO);
+ struct stopwatch timeout;
qup_reset_master_status(id);
@@ -196,6 +197,7 @@ static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj,
qup_write32(fifo, tag);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
while (data_len) {
tag = qup_i2c_create_output_tag(data_len == 1 && stop_seq,
@@ -213,7 +215,7 @@ static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj,
qup_write32(fifo, tag);
- ret = qup_i2c_write_fifo_flush(id);
+ ret = qup_i2c_write_fifo_flush(id, &timeout);
if (ret) {
printk(QUPDBG "%s: error\n", __func__);
@@ -221,7 +223,7 @@ static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj,
}
}
- ret = qup_i2c_write_fifo_flush(id);
+ ret = qup_i2c_write_fifo_flush(id, &timeout);
qup_set_state(id, QUP_STATE_RESET);
@@ -285,6 +287,7 @@ static qup_return_t qup_i2c_read_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj)
unsigned int data_len = p_tx_obj->p.iic.data_len;
unsigned int idx = 0;
uint32_t *fifo = QUP_ADDR(id, QUP_OUTPUT_FIFO);
+ struct stopwatch timeout;
qup_reset_master_status(id);
@@ -303,13 +306,14 @@ static qup_return_t qup_i2c_read_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj)
(QUP_I2C_ADDR(addr) | QUP_I2C_SLAVE_READ)) |
((QUP_I2C_RECV_SEQ | data_len) << 16));
- ret = qup_i2c_write_fifo_flush(id);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
+ ret = qup_i2c_write_fifo_flush(id, &timeout);
if (ret) {
printk(QUPDBG "%s: OUTPUT_FIFO_NOT_EMPTY\n", __func__);
return ret;
}
- ret = qup_fifo_wait_for(id, INPUT_SERVICE_FLAG);
+ ret = qup_fifo_wait_for(id, INPUT_SERVICE_FLAG, &timeout);
if (ret) {
printk(QUPDBG "%s: INPUT_SERVICE_FLAG\n", __func__);
return ret;
diff --git a/src/soc/qualcomm/ipq806x/qup.c b/src/soc/qualcomm/ipq806x/qup.c
index e2acde3842ad..e7b45d6eca3e 100644
--- a/src/soc/qualcomm/ipq806x/qup.c
+++ b/src/soc/qualcomm/ipq806x/qup.c
@@ -3,6 +3,7 @@
#include <device/mmio.h>
#include <console/console.h>
#include <delay.h>
+#include <timer.h>
#include <soc/iomap.h>
#include <soc/qup.h>
@@ -87,35 +88,33 @@ static qup_return_t qup_reset_master_status(gsbi_id_t gsbi_id)
return QUP_SUCCESS;
}
-static qup_return_t qup_fifo_wait_for(gsbi_id_t gsbi_id, uint32_t status)
+static qup_return_t qup_fifo_wait_for(gsbi_id_t gsbi_id, uint32_t status,
+ struct stopwatch *timeout)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
- unsigned int count = TIMEOUT_CNT;
while (!(read32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & status)) {
ret = qup_i2c_master_status(gsbi_id);
if (ret)
return ret;
- if (count == 0)
+ if (stopwatch_expired(timeout))
return QUP_ERR_TIMEOUT;
- count--;
}
return QUP_SUCCESS;
}
-static qup_return_t qup_fifo_wait_while(gsbi_id_t gsbi_id, uint32_t status)
+static qup_return_t qup_fifo_wait_while(gsbi_id_t gsbi_id, uint32_t status,
+ struct stopwatch *timeout)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
- unsigned int count = TIMEOUT_CNT;
while (read32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & status) {
ret = qup_i2c_master_status(gsbi_id);
if (ret)
return ret;
- if (count == 0)
+ if (stopwatch_expired(timeout))
return QUP_ERR_TIMEOUT;
- count--;
}
return QUP_SUCCESS;
@@ -129,6 +128,7 @@ static qup_return_t qup_i2c_write_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
uint8_t *data_ptr = p_tx_obj->p.iic.data;
unsigned int data_len = p_tx_obj->p.iic.data_len;
unsigned int idx = 0;
+ struct stopwatch timeout;
qup_reset_master_status(gsbi_id);
qup_set_state(gsbi_id, QUP_STATE_RUN);
@@ -136,6 +136,7 @@ static qup_return_t qup_i2c_write_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO),
(QUP_I2C_START_SEQ | QUP_I2C_ADDR(addr)));
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
while (data_len) {
if (data_len == 1 && stop_seq) {
write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO),
@@ -147,7 +148,8 @@ static qup_return_t qup_i2c_write_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
data_len--;
idx++;
if (data_len) {
- ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_FULL);
+ ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_FULL,
+ &timeout);
if (ret)
return ret;
}
@@ -166,7 +168,7 @@ static qup_return_t qup_i2c_write_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
}
}
- ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY);
+ ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY, &timeout);
if (ret)
return ret;
@@ -202,6 +204,7 @@ static qup_return_t qup_i2c_read_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj)
uint8_t *data_ptr = p_tx_obj->p.iic.data;
unsigned int data_len = p_tx_obj->p.iic.data_len;
unsigned int idx = 0;
+ struct stopwatch timeout;
qup_reset_master_status(gsbi_id);
qup_set_state(gsbi_id, QUP_STATE_RUN);
@@ -212,7 +215,8 @@ static qup_return_t qup_i2c_read_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj)
write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO),
QUP_I2C_RECV_SEQ | data_len);
- ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
+ ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY, &timeout);
if (ret)
return ret;
@@ -221,7 +225,7 @@ static qup_return_t qup_i2c_read_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj)
while (data_len) {
uint32_t data;
- ret = qup_fifo_wait_for(gsbi_id, INPUT_SERVICE_FLAG);
+ ret = qup_fifo_wait_for(gsbi_id, INPUT_SERVICE_FLAG, &timeout);
if (ret)
return ret;
diff --git a/src/soc/qualcomm/qcs405/qup.c b/src/soc/qualcomm/qcs405/qup.c
index 316cd9fed050..e3a4e5e69eb9 100644
--- a/src/soc/qualcomm/qcs405/qup.c
+++ b/src/soc/qualcomm/qcs405/qup.c
@@ -3,6 +3,7 @@
#include <device/mmio.h>
#include <console/console.h>
#include <delay.h>
+#include <timer.h>
#include <soc/gpio.h>
#include <soc/iomap.h>
#include <soc/qup.h>
@@ -129,35 +130,33 @@ static qup_return_t qup_reset_master_status(blsp_qup_id_t id)
return QUP_SUCCESS;
}
-static qup_return_t qup_fifo_wait_for(blsp_qup_id_t id, uint32_t status)
+static qup_return_t qup_fifo_wait_for(blsp_qup_id_t id, uint32_t status,
+ struct stopwatch *timeout)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
- unsigned int count = TIMEOUT_CNT;
while (!(read32(QUP_ADDR(id, QUP_OPERATIONAL)) & status)) {
ret = qup_i2c_master_status(id);
if (ret)
return ret;
- if (count == 0)
+ if (stopwatch_expired(timeout))
return QUP_ERR_TIMEOUT;
- count--;
}
return QUP_SUCCESS;
}
-static qup_return_t qup_fifo_wait_while(blsp_qup_id_t id, uint32_t status)
+static qup_return_t qup_fifo_wait_while(blsp_qup_id_t id, uint32_t status,
+ struct stopwatch *timeout)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
- unsigned int count = TIMEOUT_CNT;
while (read32(QUP_ADDR(id, QUP_OPERATIONAL)) & status) {
ret = qup_i2c_master_status(id);
if (ret)
return ret;
- if (count == 0)
+ if (stopwatch_expired(timeout))
return QUP_ERR_TIMEOUT;
- count--;
}
return QUP_SUCCESS;
@@ -175,7 +174,8 @@ static inline uint32_t qup_i2c_create_output_tag(int stop, u8 data)
return tag;
}
-static inline qup_return_t qup_i2c_write_fifo_flush(blsp_qup_id_t id)
+static inline qup_return_t qup_i2c_write_fifo_flush(blsp_qup_id_t id,
+ struct stopwatch *timeout)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
@@ -183,7 +183,7 @@ static inline qup_return_t qup_i2c_write_fifo_flush(blsp_qup_id_t id)
mdelay(4); /* TPM seems to need this */
- ret = qup_fifo_wait_while(id, OUTPUT_FIFO_NOT_EMPTY);
+ ret = qup_fifo_wait_while(id, OUTPUT_FIFO_NOT_EMPTY, timeout);
if (ret)
return ret;
@@ -204,6 +204,7 @@ static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj,
unsigned int data_len = p_tx_obj->p.iic.data_len;
unsigned int idx = 0;
uint32_t tag, *fifo = QUP_ADDR(id, QUP_OUTPUT_FIFO);
+ struct stopwatch timeout;
qup_reset_master_status(id);
@@ -232,6 +233,7 @@ static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj,
qup_write32(fifo, tag);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
while (data_len) {
tag = qup_i2c_create_output_tag(data_len == 1 && stop_seq,
@@ -249,7 +251,7 @@ static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj,
qup_write32(fifo, tag);
- ret = qup_i2c_write_fifo_flush(id);
+ ret = qup_i2c_write_fifo_flush(id, &timeout);
if (ret) {
printk(QUPDBG "%s: error\n", __func__);
@@ -257,7 +259,7 @@ static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj,
}
}
- ret = qup_i2c_write_fifo_flush(id);
+ ret = qup_i2c_write_fifo_flush(id, &timeout);
qup_set_state(id, QUP_STATE_RESET);
@@ -321,6 +323,7 @@ static qup_return_t qup_i2c_read_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj)
unsigned int data_len = p_tx_obj->p.iic.data_len;
unsigned int idx = 0;
uint32_t *fifo = QUP_ADDR(id, QUP_OUTPUT_FIFO);
+ struct stopwatch timeout;
qup_reset_master_status(id);
@@ -339,13 +342,14 @@ static qup_return_t qup_i2c_read_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj)
(QUP_I2C_ADDR(addr) | QUP_I2C_SLAVE_READ)) |
((QUP_I2C_RECV_SEQ | data_len) << 16));
- ret = qup_i2c_write_fifo_flush(id);
+ stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
+ ret = qup_i2c_write_fifo_flush(id, &timeout);
if (ret) {
printk(QUPDBG "%s: OUTPUT_FIFO_NOT_EMPTY\n", __func__);
return ret;
}
- ret = qup_fifo_wait_for(id, INPUT_SERVICE_FLAG);
+ ret = qup_fifo_wait_for(id, INPUT_SERVICE_FLAG, &timeout);
if (ret) {
printk(QUPDBG "%s: INPUT_SERVICE_FLAG\n", __func__);
return ret;
diff --git a/src/soc/rockchip/common/i2c.c b/src/soc/rockchip/common/i2c.c
index dbbfd3e0410d..a0498a52c7fd 100644
--- a/src/soc/rockchip/common/i2c.c
+++ b/src/soc/rockchip/common/i2c.c
@@ -114,7 +114,6 @@ static int i2c_read(struct rk_i2c_regs *reg_addr, struct i2c_msg segment)
{
int res = 0;
uint8_t *data = segment.buf;
- int timeout = I2C_TIMEOUT_US;
unsigned int bytes_remaining = segment.len;
unsigned int con = 0;
@@ -122,6 +121,7 @@ static int i2c_read(struct rk_i2c_regs *reg_addr, struct i2c_msg segment)
write32(&reg_addr->i2c_mrxraddr, 0);
con = I2C_MODE_TRX | I2C_EN | I2C_ACT2NAK;
while (bytes_remaining) {
+ int timeout = CONFIG_I2C_TRANSFER_TIMEOUT_US;
size_t size = MIN(bytes_remaining, 32);
bytes_remaining -= size;
if (!bytes_remaining)
@@ -132,7 +132,6 @@ static int i2c_read(struct rk_i2c_regs *reg_addr, struct i2c_msg segment)
write32(&reg_addr->i2c_con, con);
write32(&reg_addr->i2c_mrxcnt, size);
- timeout = I2C_TIMEOUT_US;
while (timeout--) {
if (read32(&reg_addr->i2c_ipd) & I2C_NAKRCVI) {
write32(&reg_addr->i2c_mrxcnt, 0);
@@ -161,7 +160,6 @@ static int i2c_write(struct rk_i2c_regs *reg_addr, struct i2c_msg segment)
{
int res = 0;
uint8_t *data = segment.buf;
- int timeout = I2C_TIMEOUT_US;
int bytes_remaining = segment.len + 1;
/* Prepend one byte for the slave address to the transfer. */
@@ -169,6 +167,7 @@ static int i2c_write(struct rk_i2c_regs *reg_addr, struct i2c_msg segment)
int prefsz = 1;
while (bytes_remaining) {
+ int timeout = CONFIG_I2C_TRANSFER_TIMEOUT_US;
size_t size = MIN(bytes_remaining, 32);
buffer_to_fifo32_prefix(data, prefix, prefsz, size,
&reg_addr->txdata, 4, 4);
@@ -180,7 +179,6 @@ static int i2c_write(struct rk_i2c_regs *reg_addr, struct i2c_msg segment)
I2C_EN | I2C_MODE_TX | I2C_ACT2NAK);
write32(&reg_addr->i2c_mtxcnt, size);
- timeout = I2C_TIMEOUT_US;
while (timeout--) {
if (read32(&reg_addr->i2c_ipd) & I2C_NAKRCVI) {
write32(&reg_addr->i2c_mtxcnt, 0);
diff --git a/src/soc/samsung/exynos5250/i2c.c b/src/soc/samsung/exynos5250/i2c.c
index bc5570daad7f..5ce4c7d8146e 100644
--- a/src/soc/samsung/exynos5250/i2c.c
+++ b/src/soc/samsung/exynos5250/i2c.c
@@ -8,6 +8,9 @@
#include <soc/clk.h>
#include <soc/i2c.h>
#include <soc/periph.h>
+#include <timer.h>
+
+#define I2C_TIMEOUT_US (1000 * USECS_PER_MSEC)
struct __packed i2c_regs
{
@@ -119,9 +122,9 @@ static int i2c_got_ack(struct i2c_regs *regs)
return !(read8(&regs->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 = DIV_ROUND_UP(timeout_us, 10);
while (timeout--) {
if (!(read8(&regs->stat) & I2cStatBusy))
return 0;
@@ -131,9 +134,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 = DIV_ROUND_UP(timeout_us, 10);
while (timeout--) {
if (i2c_int_pending(regs))
return 0;
@@ -148,7 +151,7 @@ static int i2c_send_stop(struct i2c_regs *regs)
uint8_t mode = read8(&regs->stat) & (I2cStatModeMask);
write8(&regs->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)
@@ -158,7 +161,7 @@ static int i2c_send_start(struct i2c_regs *regs, int read, int chip)
write8(&regs->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)) {
@@ -180,7 +183,7 @@ static int i2c_xmit_buf(struct i2c_regs *regs, uint8_t *data, int len)
write8(&regs->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)) {
@@ -204,7 +207,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(&regs->ds);
@@ -220,7 +223,7 @@ int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
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(&regs->stat, I2cStatMasterXmit | I2cStatEnable);
diff --git a/src/soc/samsung/exynos5420/i2c.c b/src/soc/samsung/exynos5420/i2c.c
index ab17d52c4460..49505a0782af 100644
--- a/src/soc/samsung/exynos5420/i2c.c
+++ b/src/soc/samsung/exynos5420/i2c.c
@@ -11,6 +11,8 @@
#include <soc/pinmux.h>
#include <timer.h>
+#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(&regs->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(&regs->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(&regs->stat) & (I2cStatModeMask);
write8(&regs->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(&regs->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(&regs->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(&regs->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(&regs->stat, I2cStatMasterXmit | I2cStatEnable);