ulp-riscv-i2c: Add ULP RISC-V I2C read/write timeout config option

The commit 88e4c06028 introduced a loop timeout for all ULP RISC-V I2C
transactions to avoid getting stuck in a forever loop. The loop timeout
was set to 500 msec by default. This commit improves on the concept by
making the loop timeout configurable via a Kconfig option in terms of
CPU ticks. If the timeout is set to -1 value then the transaction loops
will never timeout, therefore restoring the driver behavior before the
timeout was introduced.

The commit also updates the I2C Fast mode timings for esp32s2 which need
to be adjusted due to bus timing constraints.

Closes https://github.com/espressif/esp-idf/issues/11154
This commit is contained in:
Sudeep Mohanty 2023-04-26 11:19:18 +02:00
parent 3947688d54
commit 662e0812f4
4 changed files with 61 additions and 35 deletions

View File

@ -49,6 +49,19 @@ menu "Ultra Low Power (ULP) Co-processor"
help
The accuracy of the bitbanged UART driver is limited, it is not
recommend to increase the value above 19200.
config ULP_RISCV_I2C_RW_TIMEOUT
int
prompt "Set timeout for ULP RISC-V I2C transaction timeout in ticks."
default 500
range -1 4294967295
help
Set the ULP RISC-V I2C read/write timeout. Set this value to -1
if the ULP RISC-V I2C read and write APIs should wait forever.
Please note that the tick rate of the ULP co-processor would be
different than the OS tick rate of the main core and therefore
can have different timeout value depending on which core the API
is invoked on.
endmenu

View File

@ -52,6 +52,7 @@ typedef struct {
.i2c_pin_cfg.sda_pullup_en = true, \
.i2c_pin_cfg.scl_pullup_en = true, \
#if CONFIG_IDF_TARGET_ESP32S3
/* Nominal I2C bus timing parameters for I2C fast mode. Max SCL freq of 400 KHz. */
#define ULP_RISCV_I2C_FAST_MODE_CONFIG() \
.i2c_timing_cfg.scl_low_period = 1.4, \
@ -59,7 +60,17 @@ typedef struct {
.i2c_timing_cfg.sda_duty_period = 1, \
.i2c_timing_cfg.scl_start_period = 2, \
.i2c_timing_cfg.scl_stop_period = 1.3, \
.i2c_timing_cfg.i2c_trans_timeout = 20, \
.i2c_timing_cfg.i2c_trans_timeout = 20,
#elif CONFIG_IDF_TARGET_ESP32S2
/* Nominal I2C bus timing parameters for I2C fast mode. Max SCL freq on S2 is about 233 KHz due to timing constraints. */
#define ULP_RISCV_I2C_FAST_MODE_CONFIG() \
.i2c_timing_cfg.scl_low_period = 2, \
.i2c_timing_cfg.scl_high_period = 0.7, \
.i2c_timing_cfg.sda_duty_period = 1.7, \
.i2c_timing_cfg.scl_start_period = 2.4, \
.i2c_timing_cfg.scl_stop_period = 1.3, \
.i2c_timing_cfg.i2c_trans_timeout = 20,
#endif
/* Nominal I2C bus timing parameters for I2C standard mode. Max SCL freq of 100 KHz. */
#define ULP_RISCV_I2C_STANDARD_MODE_CONFIG() \

View File

@ -10,6 +10,7 @@
#include "soc/rtc_io_reg.h"
#include "soc/sens_reg.h"
#include "hal/i2c_ll.h"
#include "sdkconfig.h"
#define I2C_CTRL_SLAVE_ADDR_MASK (0xFF << 0)
#define I2C_CTRL_SLAVE_REG_ADDR_MASK (0xFF << 11)
@ -30,7 +31,7 @@
#endif // CONFIG_IDF_TARGET_ESP32S3
/* Read/Write timeout (number of iterationis) */
#define ULP_RISCV_I2C_RW_TIMEOUT 500
#define ULP_RISCV_I2C_RW_TIMEOUT CONFIG_ULP_RISCV_I2C_RW_TIMEOUT
/*
* The RTC I2C controller follows the I2C command registers to perform read/write operations.
@ -65,19 +66,19 @@ static void ulp_riscv_i2c_format_cmd(uint32_t cmd_idx, uint8_t op_code, uint8_t
((byte_num & 0xFF) << 0)); // Byte Num
}
static inline int32_t ulp_riscv_i2c_wait_for_interrupt(uint32_t timeout)
static inline int32_t ulp_riscv_i2c_wait_for_interrupt(int32_t ticks_to_wait)
{
uint32_t status = 0;
uint32_t to = 0;
while (to < timeout) {
while (1) {
status = READ_PERI_REG(RTC_I2C_INT_ST_REG);
/* Return 0 if Tx or Rx data interrupt bits are set. -1 otherwise */
/* Return 0 if Tx or Rx data interrupt bits are set. */
if ((status & RTC_I2C_TX_DATA_INT_ST) ||
(status & RTC_I2C_RX_DATA_INT_ST)) {
return 0;
/* In case of errors return immidiately */
/* In case of error status, break and return -1 */
#if CONFIG_IDF_TARGET_ESP32S2
} else if ((status & RTC_I2C_TIMEOUT_INT_ST) ||
#elif CONFIG_IDF_TARGET_ESP32S3
@ -88,12 +89,17 @@ static inline int32_t ulp_riscv_i2c_wait_for_interrupt(uint32_t timeout)
return -1;
}
ulp_riscv_delay_cycles(ULP_RISCV_CYCLES_PER_MS);
to++;
if (ticks_to_wait > -1) {
/* If the ticks_to_wait value is not -1, keep track of ticks and
* break from the loop once the timeout is reached.
*/
ulp_riscv_delay_cycles(1);
to++;
if (to >= ticks_to_wait) {
return -1;
}
}
}
/* If we reach here, it is a timeout error */
return -1;
}
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr)
@ -169,10 +175,7 @@ void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size)
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
for (i = 0; i < size; i++) {
/* Poll for RTC I2C Rx Data interrupt bit to be set.
* Set a loop timeout of 500 msec to bail in case of any driver
* and/or hardware errors.
*/
/* Poll for RTC I2C Rx Data interrupt bit to be set */
if(!ulp_riscv_i2c_wait_for_interrupt(ULP_RISCV_I2C_RW_TIMEOUT)) {
/* Read the data
*
@ -253,10 +256,7 @@ void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size)
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
}
/* Poll for RTC I2C Tx Data interrupt bit to be set.
* Set a loop timeout of 500 msec to bail in case of any driver
* and/or hardware errors.
*/
/* Poll for RTC I2C Tx Data interrupt bit to be set */
if (!ulp_riscv_i2c_wait_for_interrupt(ULP_RISCV_I2C_RW_TIMEOUT)) {
/* Clear the Tx data interrupt bit */
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_TX_DATA_INT_CLR);

View File

@ -15,6 +15,7 @@
#include "driver/rtc_io.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
static const char *RTCI2C_TAG = "ulp_riscv_i2c";
@ -43,7 +44,7 @@ rtc_io_dev_t *rtc_io_dev = &RTCIO;
#define MICROSEC_TO_RTC_FAST_CLK(period) (period) * ((float)(SOC_CLK_RC_FAST_FREQ_APPROX) / (1000000.0))
/* Read/Write timeout (number of iterations)*/
#define ULP_RISCV_I2C_RW_TIMEOUT 500
#define ULP_RISCV_I2C_RW_TIMEOUT CONFIG_ULP_RISCV_I2C_RW_TIMEOUT
static esp_err_t i2c_gpio_is_cfg_valid(gpio_num_t sda_io_num, gpio_num_t scl_io_num)
{
@ -233,13 +234,13 @@ static void ulp_riscv_i2c_format_cmd(uint32_t cmd_idx, uint8_t op_code, uint8_t
#endif // CONFIG_IDF_TARGET_ESP32S2
}
static inline esp_err_t ulp_riscv_i2c_wait_for_interrupt(uint32_t timeout)
static inline esp_err_t ulp_riscv_i2c_wait_for_interrupt(int32_t ticks_to_wait)
{
uint32_t status = 0;
uint32_t to = 0;
esp_err_t ret = ESP_ERR_TIMEOUT;
esp_err_t ret = ESP_OK;
while (to < timeout) {
while (1) {
status = READ_PERI_REG(RTC_I2C_INT_ST_REG);
/* Return ESP_OK if Tx or Rx data interrupt bits are set. */
@ -259,10 +260,17 @@ static inline esp_err_t ulp_riscv_i2c_wait_for_interrupt(uint32_t timeout)
break;
}
vTaskDelay(1);
/* Loop timeout. If this expires, we return ESP_ERR_TIMEOUT */
to++;
if (ticks_to_wait > -1) {
/* If the ticks_to_wait value is not -1, keep track of ticks and
* break from the loop once the timeout is reached.
*/
vTaskDelay(1);
to++;
if (to >= ticks_to_wait) {
ret = ESP_ERR_TIMEOUT;
break;
}
}
}
return ret;
@ -339,10 +347,7 @@ void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size)
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
for (i = 0; i < size; i++) {
/* Poll for RTC I2C Rx Data interrupt bit to be set.
* Set a loop timeout of 500 iterations to bail in case of any driver
* and/or hardware errors.
*/
/* Poll for RTC I2C Rx Data interrupt bit to be set */
ret = ulp_riscv_i2c_wait_for_interrupt(ULP_RISCV_I2C_RW_TIMEOUT);
if (ret == ESP_OK) {
@ -426,10 +431,7 @@ void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size)
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
}
/* Poll for RTC I2C Tx Data interrupt bit to be set.
* Set a loop timeout of 500 iterations to bail in case of any driver
* and/or hardware errors.
*/
/* Poll for RTC I2C Tx Data interrupt bit to be set */
ret = ulp_riscv_i2c_wait_for_interrupt(ULP_RISCV_I2C_RW_TIMEOUT);
if (ret == ESP_OK) {