Skip to content

Commit

Permalink
ulp-riscv-i2c: Add ULP RISC-V I2C read/write timeout config option
Browse files Browse the repository at this point in the history
The commit 88e4c06 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 espressif#11154
  • Loading branch information
sudeep-mohanty authored and ilutchenko committed Jun 26, 2023
1 parent c76b407 commit 6dd5a1c
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 35 deletions.
13 changes: 13 additions & 0 deletions components/ulp/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
13 changes: 12 additions & 1 deletion components/ulp/ulp_riscv/include/ulp_riscv_i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,25 @@ 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, \
.i2c_timing_cfg.scl_high_period = 0.3, \
.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() \
Expand Down
36 changes: 18 additions & 18 deletions components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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);
Expand Down
34 changes: 18 additions & 16 deletions components/ulp/ulp_riscv/ulp_riscv_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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. */
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 6dd5a1c

Please sign in to comment.