From 08600cb1a3a3428affc58c0ddccce4d0759e4c47 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Wed, 26 Apr 2023 11:19:18 +0200 Subject: [PATCH] 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 --- components/ulp/Kconfig | 13 +++++++ .../ulp/ulp_riscv/include/ulp_riscv_i2c.h | 13 ++++++- .../ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c | 36 +++++++++---------- components/ulp/ulp_riscv/ulp_riscv_i2c.c | 34 +++++++++--------- 4 files changed, 61 insertions(+), 35 deletions(-) diff --git a/components/ulp/Kconfig b/components/ulp/Kconfig index 5997ea5ae6..ad1fba677a 100644 --- a/components/ulp/Kconfig +++ b/components/ulp/Kconfig @@ -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 diff --git a/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h b/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h index 4ee4482a6a..503fa7b20c 100644 --- a/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h +++ b/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h @@ -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() \ diff --git a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c index bfde231551..99263d7c6b 100644 --- a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c +++ b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c @@ -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); diff --git a/components/ulp/ulp_riscv/ulp_riscv_i2c.c b/components/ulp/ulp_riscv/ulp_riscv_i2c.c index 0a23bef6d9..2a4b32669a 100644 --- a/components/ulp/ulp_riscv/ulp_riscv_i2c.c +++ b/components/ulp/ulp_riscv/ulp_riscv_i2c.c @@ -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) {