diff options
author | Jes Klinke <jbk@google.com> | 2022-02-22 16:00:09 -0800 |
---|---|---|
committer | Julius Werner <jwerner@chromium.org> | 2022-03-15 22:06:27 +0000 |
commit | 19baa9d51e4f1b36473dc750735eb6e5345bebda (patch) | |
tree | 60aada7f006fd73797b5c566d704e2b0ed5b728f /src/soc/qualcomm | |
parent | ca82e6161af7a453b512f35dd695a98084a1d7cf (diff) |
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>
Diffstat (limited to 'src/soc/qualcomm')
-rw-r--r-- | src/soc/qualcomm/common/include/soc/qup_se_handlers_common.h | 2 | ||||
-rw-r--r-- | src/soc/qualcomm/common/qup_se_handler.c | 7 | ||||
-rw-r--r-- | src/soc/qualcomm/common/qupv3_i2c.c | 4 | ||||
-rw-r--r-- | src/soc/qualcomm/common/qupv3_spi.c | 4 | ||||
-rw-r--r-- | src/soc/qualcomm/ipq40xx/qup.c | 32 | ||||
-rw-r--r-- | src/soc/qualcomm/ipq806x/qup.c | 28 | ||||
-rw-r--r-- | src/soc/qualcomm/qcs405/qup.c | 32 |
7 files changed, 62 insertions, 47 deletions
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 bab7434f0d..a004c7944c 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 bb7be37cf0..7dd4f452af 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(®s->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 606b3bf935..8f0880e35d 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(®s->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(®s->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 c69ecf9eaa..1bb5c75e5e 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 76f079744c..88e9169ec7 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 e2acde3842..e7b45d6eca 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 316cd9fed0..e3a4e5e69e 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; |