drivers: i2c: esp32: set timeout to allow clock stretching
The ESP32 series MCUs allow to set a timeout which triggers an error if the SCL line is unchanged for the specified amount of time. By default, the ESP-IDF HAL sets the timeout to an arbitrary value of 10 times the bus cycle. This is not sufficient for chips like the TI bq76952, which pulls the SCL line low (clock stretching) for several 100 µs. The timeout should also not be dependent on the chosen bitrate, as it is defined by the time a chip needs for internal calculation before it can provide requested data or continue communication. This commit adds a property to devicetree to allow configuration of the scl timeout. This value is set via direct register access, as the ESP-IDF HAL does not provide access to the enable bit and does not give any information about the maximum size of the timeout (defined in I2C clock cycles in the register). Fixes #51351 Signed-off-by: Martin Jäger <martin@libre.solar>
This commit is contained in:
parent
1529968884
commit
d5168a8d96
|
@ -91,6 +91,7 @@ struct i2c_esp32_config {
|
|||
|
||||
const uint32_t default_config;
|
||||
const uint32_t bitrate;
|
||||
const uint32_t scl_timeout;
|
||||
};
|
||||
|
||||
/* I2C clock characteristic, The order is the same as i2c_sclk_t. */
|
||||
|
@ -238,6 +239,27 @@ static int i2c_esp32_recover(const struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR i2c_esp32_configure_timeout(const struct device *dev)
|
||||
{
|
||||
const struct i2c_esp32_config *config = dev->config;
|
||||
|
||||
if (config->scl_timeout > 0) {
|
||||
i2c_sclk_t sclk = i2c_get_clk_src(config->bitrate);
|
||||
uint32_t clk_freq_mhz = I2C_LL_CLK_SRC_FREQ(sclk);
|
||||
uint32_t timeout_cycles = MIN(I2C_TIME_OUT_REG_V,
|
||||
clk_freq_mhz / MHZ(1) * config->scl_timeout);
|
||||
sys_clear_bits(I2C_TO_REG(config->index), I2C_TIME_OUT_REG);
|
||||
sys_set_bits(I2C_TO_REG(config->index), timeout_cycles << I2C_TIME_OUT_REG_S);
|
||||
LOG_DBG("SCL timeout: %d us, value: %d", config->scl_timeout, timeout_cycles);
|
||||
} else {
|
||||
/* Disabling the timeout by clearing the I2C_TIME_OUT_EN bit does not seem to work,
|
||||
* at least for ESP32-C3 (tested with communication to bq76952 chip). So we set the
|
||||
* timeout to maximum supported value instead.
|
||||
*/
|
||||
sys_set_bits(I2C_TO_REG(config->index), I2C_TIME_OUT_REG);
|
||||
}
|
||||
}
|
||||
|
||||
static int i2c_esp32_configure(const struct device *dev, uint32_t dev_config)
|
||||
{
|
||||
const struct i2c_esp32_config *config = dev->config;
|
||||
|
@ -272,6 +294,7 @@ static int i2c_esp32_configure(const struct device *dev, uint32_t dev_config)
|
|||
}
|
||||
|
||||
i2c_hal_set_bus_timing(&data->hal, config->bitrate, i2c_get_clk_src(config->bitrate));
|
||||
i2c_esp32_configure_timeout(dev);
|
||||
i2c_hal_update_config(&data->hal);
|
||||
|
||||
return 0;
|
||||
|
@ -709,6 +732,10 @@ static int IRAM_ATTR i2c_esp32_init(const struct device *dev)
|
|||
#define I2C_ESP32_GET_PIN_INFO(idx)
|
||||
#endif /* SOC_I2C_SUPPORT_HW_CLR_BUS */
|
||||
|
||||
#define I2C_ESP32_TIMEOUT(inst) \
|
||||
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, scl_timeout_us), \
|
||||
(DT_INST_PROP(inst, scl_timeout_us)), (0))
|
||||
|
||||
#define I2C_ESP32_FREQUENCY(bitrate) \
|
||||
(bitrate == I2C_BITRATE_STANDARD ? KHZ(100) \
|
||||
: bitrate == I2C_BITRATE_FAST ? KHZ(400) \
|
||||
|
@ -741,6 +768,7 @@ static int IRAM_ATTR i2c_esp32_init(const struct device *dev)
|
|||
}, \
|
||||
.irq_source = ETS_I2C_EXT##idx##_INTR_SOURCE, \
|
||||
.bitrate = I2C_FREQUENCY(idx), \
|
||||
.scl_timeout = I2C_ESP32_TIMEOUT(idx), \
|
||||
.default_config = I2C_MODE_CONTROLLER, \
|
||||
}; \
|
||||
I2C_DEVICE_DT_DEFINE(I2C(idx), i2c_esp32_init, NULL, &i2c_esp32_data_##idx, \
|
||||
|
|
|
@ -46,3 +46,13 @@ properties:
|
|||
type: boolean
|
||||
required: false
|
||||
description: Set I2C RX data as LSB
|
||||
|
||||
scl-timeout-us:
|
||||
type: int
|
||||
required: false
|
||||
description: |
|
||||
Timeout for unchanged SCL during clock stretching of the I2C target in
|
||||
microseconds.
|
||||
|
||||
If not set or 0, the timeout is disabled or set to the maximum possible
|
||||
value if the MCU does not support disabling the timeout.
|
||||
|
|
Loading…
Reference in a new issue