From 2575c0d49f730216b44f0b2cffec66b9f54359d2 Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Mon, 24 May 2021 10:51:38 +0800 Subject: [PATCH] i2c: fix I2C slave clock source selection In I2C slave mode, clock source can now be changed, according to the flags specified in `i2c_config_t` structure. Thus, ESP32-S2 can now act as an I2C slave, even with a 400KHz master clock. --- components/driver/i2c.c | 29 ++++++++++++++++----- components/driver/include/driver/i2c.h | 13 ++++----- components/hal/esp32s2/include/hal/i2c_ll.h | 4 +-- components/hal/include/hal/i2c_hal.h | 10 +++++++ 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index 466cf12cc6..1ac074a299 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -631,7 +631,7 @@ static esp_err_t i2c_hw_fsm_reset(i2c_port_t i2c_num) return ESP_OK; } -static i2c_sclk_t i2c_get_clk_src(const i2c_config_t *i2c_conf) +static i2c_sclk_t i2c_get_clk_src(const uint32_t clk_flags, const uint32_t clk_speed) { for (i2c_sclk_t clk = I2C_SCLK_DEFAULT + 1; clk < I2C_SCLK_MAX; clk++) { #if CONFIG_IDF_TARGET_ESP32S3 @@ -639,7 +639,8 @@ static i2c_sclk_t i2c_get_clk_src(const i2c_config_t *i2c_conf) continue; } #endif - if (((i2c_conf->clk_flags & i2c_clk_alloc[clk].character) == i2c_conf->clk_flags) && (i2c_conf->master.clk_speed <= i2c_clk_alloc[clk].clk_freq)) { + if ( ((clk_flags & i2c_clk_alloc[clk].character) == clk_flags) && + (clk_speed <= i2c_clk_alloc[clk].clk_freq) ) { return clk; } } @@ -648,15 +649,30 @@ static i2c_sclk_t i2c_get_clk_src(const i2c_config_t *i2c_conf) esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf) { + i2c_sclk_t src_clk = I2C_SCLK_DEFAULT; + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_NUM_ERROR_STR); ESP_RETURN_ON_FALSE(i2c_conf != NULL, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_ADDR_ERROR_STR); ESP_RETURN_ON_FALSE(i2c_conf->mode < I2C_MODE_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_MODE_ERR_STR); + if (i2c_conf->mode == I2C_MODE_MASTER) { - ESP_RETURN_ON_FALSE(i2c_get_clk_src(i2c_conf) != I2C_SCLK_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_CLK_FLAG_ERR_STR); + src_clk = i2c_get_clk_src(i2c_conf->clk_flags, i2c_conf->master.clk_speed); + ESP_RETURN_ON_FALSE(src_clk != I2C_SCLK_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_CLK_FLAG_ERR_STR); + } else { +#if CONFIG_IDF_TARGET_ESP32S2 + /* On ESP32-S2, APB clock shall always be used in slave mode as the + * other one, I2C_SCLK_REF_TICK, is too slow, even for sampling a + * 100KHz SCL. */ + src_clk = I2C_SCLK_APB; +#else + src_clk = i2c_get_clk_src(i2c_conf->clk_flags, i2c_conf->slave.maximum_speed); + ESP_RETURN_ON_FALSE(src_clk != I2C_SCLK_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_CLK_FLAG_ERR_STR); +#endif } - esp_err_t ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num, - i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode); + ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num, + i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode); if (ret != ESP_OK) { return ret; } @@ -666,6 +682,7 @@ esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf) i2c_hal_clr_intsts_mask(&(i2c_context[i2c_num].hal), I2C_LL_INTR_MASK); if (i2c_conf->mode == I2C_MODE_SLAVE) { //slave mode i2c_hal_slave_init(&(i2c_context[i2c_num].hal), i2c_num); + i2c_hal_set_source_clk(&(i2c_context[i2c_num].hal), src_clk); i2c_hal_set_slave_addr(&(i2c_context[i2c_num].hal), i2c_conf->slave.slave_addr, i2c_conf->slave.addr_10bit_en); i2c_hal_set_rxfifo_full_thr(&(i2c_context[i2c_num].hal), I2C_FIFO_FULL_THRESH_VAL); i2c_hal_set_txfifo_empty_thr(&(i2c_context[i2c_num].hal), I2C_FIFO_EMPTY_THRESH_VAL); @@ -678,7 +695,7 @@ esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf) i2c_hal_master_init(&(i2c_context[i2c_num].hal), i2c_num); //Default, we enable hardware filter i2c_hal_set_filter(&(i2c_context[i2c_num].hal), I2C_FILTER_CYC_NUM_DEF); - i2c_hal_set_bus_timing(&(i2c_context[i2c_num].hal), i2c_conf->master.clk_speed, i2c_get_clk_src(i2c_conf)); + i2c_hal_set_bus_timing(&(i2c_context[i2c_num].hal), i2c_conf->master.clk_speed, src_clk); } I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; diff --git a/components/driver/include/driver/i2c.h b/components/driver/include/driver/i2c.h index 4a5dcc2683..22dcc8ab24 100644 --- a/components/driver/include/driver/i2c.h +++ b/components/driver/include/driver/i2c.h @@ -74,14 +74,15 @@ typedef struct{ union { struct { - uint32_t clk_speed; /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */ - } master; /*!< I2C master config */ + uint32_t clk_speed; /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */ + } master; /*!< I2C master config */ struct { - uint8_t addr_10bit_en; /*!< I2C 10bit address mode enable for slave mode */ - uint16_t slave_addr; /*!< I2C address for slave mode */ - } slave; /*!< I2C slave config */ + uint8_t addr_10bit_en; /*!< I2C 10bit address mode enable for slave mode */ + uint16_t slave_addr; /*!< I2C address for slave mode */ + uint32_t maximum_speed; /*!< I2C expected clock speed from SCL. */ + } slave; /*!< I2C slave config */ }; - uint32_t clk_flags; /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/ + uint32_t clk_flags; /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/ } i2c_config_t; diff --git a/components/hal/esp32s2/include/hal/i2c_ll.h b/components/hal/esp32s2/include/hal/i2c_ll.h index e77fd18e04..81fb326b55 100644 --- a/components/hal/esp32s2/include/hal/i2c_ll.h +++ b/components/hal/esp32s2/include/hal/i2c_ll.h @@ -680,7 +680,7 @@ static inline void i2c_ll_master_clr_rx_it(i2c_dev_t *hw) } /** - * @brief + * @brief Enable I2C slave TX interrupt * * @param hw Beginning address of the peripheral registers * @@ -752,7 +752,7 @@ static inline void i2c_ll_slave_clr_rx_it(i2c_dev_t *hw) } /** - * @brief Reste I2C master FSM. When the master FSM is stuck, call this function to reset the FSM + * @brief Reset I2C master FSM. When the master FSM is stuck, call this function to reset the FSM * * @param hw Beginning address of the peripheral registers * diff --git a/components/hal/include/hal/i2c_hal.h b/components/hal/include/hal/i2c_hal.h index ae1c95be2c..b255878cc4 100644 --- a/components/hal/include/hal/i2c_hal.h +++ b/components/hal/include/hal/i2c_hal.h @@ -110,6 +110,16 @@ typedef struct { */ #define i2c_hal_slave_clr_rx_it(hal) i2c_ll_slave_clr_rx_it((hal)->dev) +/** + * @brief Set the source clock. This function is meant to be used in + * slave mode, in order to select a source clock abe to handle + * the expected SCL frequency. + * + * @param hal Context of the HAL layer + * @param src_clk Source clock to use choosen from `i2c_sclk_t` type + */ +#define i2c_hal_set_source_clk(hal, src_clk) i2c_ll_set_source_clk((hal)->dev, src_clk) + /** * @brief Init the I2C master. *