feat(i2c_master): Support i2c sleep retention on esp32c5/p4/c61

pull/14733/head
C.S.M 2024-09-23 17:50:12 +08:00
rodzic 6dc8fe3db3
commit 3f061bd557
32 zmienionych plików z 216 dodań i 61 usunięć

Wyświetl plik

@ -280,7 +280,7 @@ static void i2c_hw_enable(i2c_port_t i2c_num)
static esp_err_t i2c_sleep_retention_init(void *arg)
{
i2c_port_t i2c_num = *(i2c_port_t *)arg;
esp_err_t ret = sleep_retention_entries_create(i2c_regs_retention[i2c_num].link_list, i2c_regs_retention[i2c_num].link_num, REGDMA_LINK_PRI_I2C, I2C_SLEEP_RETENTION_MODULE(i2c_num));
esp_err_t ret = sleep_retention_entries_create(i2c_regs_retention[i2c_num].link_list, i2c_regs_retention[i2c_num].link_num, REGDMA_LINK_PRI_I2C, i2c_regs_retention[i2c_num].module_id);
ESP_RETURN_ON_ERROR(ret, I2C_TAG, "failed to allocate mem for sleep retention");
return ret;
}
@ -428,9 +428,9 @@ esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_
sleep_retention_module_init_param_t init_param = {
.cbs = { .create = { .handle = i2c_sleep_retention_init, .arg = &i2c_num } }
};
ret = sleep_retention_module_init(I2C_SLEEP_RETENTION_MODULE(i2c_num), &init_param);
ret = sleep_retention_module_init(i2c_regs_retention[i2c_num].module_id, &init_param);
if (ret == ESP_OK) {
sleep_retention_module_allocate(I2C_SLEEP_RETENTION_MODULE(i2c_num));
sleep_retention_module_allocate(i2c_regs_retention[i2c_num].module_id);
}
#endif
return ESP_OK;
@ -486,9 +486,9 @@ esp_err_t i2c_driver_delete(i2c_port_t i2c_num)
p_i2c->intr_handle = NULL;
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_I2C_SUPPORT_SLEEP_RETENTION
esp_err_t err = sleep_retention_module_free(I2C_SLEEP_RETENTION_MODULE(i2c_num));
esp_err_t err = sleep_retention_module_free(i2c_regs_retention[i2c_num].module_id);
if (err == ESP_OK) {
err = sleep_retention_module_deinit(I2C_SLEEP_RETENTION_MODULE(i2c_num));
err = sleep_retention_module_deinit(i2c_regs_retention[i2c_num].module_id);
}
#endif

Wyświetl plik

@ -33,7 +33,7 @@
#include "soc/rtc_io_channel.h"
#include "driver/lp_io.h"
#endif
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
#if I2C_USE_RETENTION_LINK
#include "esp_private/sleep_retention.h"
#endif
@ -47,15 +47,31 @@ typedef struct i2c_platform_t {
static i2c_platform_t s_i2c_platform = {}; // singleton platform
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_I2C_SUPPORT_SLEEP_RETENTION
#if I2C_USE_RETENTION_LINK
static esp_err_t s_i2c_sleep_retention_init(void *arg)
{
i2c_bus_t *bus = (i2c_bus_t *)arg;
i2c_port_num_t port_num = bus->port_num;
esp_err_t ret = sleep_retention_entries_create(i2c_regs_retention[port_num].link_list, i2c_regs_retention[port_num].link_num, REGDMA_LINK_PRI_I2C, I2C_SLEEP_RETENTION_MODULE(port_num));
esp_err_t ret = sleep_retention_entries_create(i2c_regs_retention[port_num].link_list, i2c_regs_retention[port_num].link_num, REGDMA_LINK_PRI_I2C, i2c_regs_retention[port_num].module_id);
ESP_RETURN_ON_ERROR(ret, TAG, "failed to allocate mem for sleep retention");
return ret;
}
void i2c_create_retention_module(i2c_bus_handle_t handle)
{
i2c_port_num_t port_num = handle->port_num;
_lock_acquire(&s_i2c_platform.mutex);
if (handle->retention_link_created == false) {
if (sleep_retention_module_allocate(i2c_regs_retention[port_num].module_id) != ESP_OK) {
// even though the sleep retention module create failed, I2C driver should still work, so just warning here
ESP_LOGW(TAG, "create retention module failed, power domain can't turn off");
} else {
handle->retention_link_created = true;
}
}
_lock_release(&s_i2c_platform.mutex);
}
#endif
static esp_err_t s_i2c_bus_handle_acquire(i2c_port_num_t port_num, i2c_bus_handle_t *i2c_new_bus, i2c_bus_mode_t mode)
@ -77,14 +93,14 @@ static esp_err_t s_i2c_bus_handle_acquire(i2c_port_num_t port_num, i2c_bus_handl
bus->bus_mode = mode;
bus->is_lp_i2c = (bus->port_num < SOC_HP_I2C_NUM) ? false : true;
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_I2C_SUPPORT_SLEEP_RETENTION
#if I2C_USE_RETENTION_LINK
if (bus->is_lp_i2c == false) {
sleep_retention_module_init_param_t init_param = {
.cbs = { .create = { .handle = s_i2c_sleep_retention_init, .arg = (void *)bus } }
};
ret = sleep_retention_module_init(I2C_SLEEP_RETENTION_MODULE(port_num), &init_param);
if (ret == ESP_OK) {
sleep_retention_module_allocate(I2C_SLEEP_RETENTION_MODULE(port_num));
esp_err_t err = sleep_retention_module_init(i2c_regs_retention[port_num].module_id, &init_param);
if (err != ESP_OK) {
ESP_LOGW(TAG, "init sleep retention failed on bus %d, power domain may be turned off during sleep", port_num);
}
} else {
ESP_LOGW(TAG, "Detected PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP is enabled while LP_I2C is used. Sleep retention is not supported on LP I2C. Please use it properly");
@ -175,12 +191,12 @@ esp_err_t i2c_release_bus_handle(i2c_bus_handle_t i2c_bus)
if (s_i2c_platform.count[port_num] == 0) {
do_deinitialize = true;
s_i2c_platform.buses[port_num] = NULL;
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_I2C_SUPPORT_SLEEP_RETENTION
#if I2C_USE_RETENTION_LINK
if (i2c_bus->is_lp_i2c == false) {
esp_err_t err = sleep_retention_module_free(I2C_SLEEP_RETENTION_MODULE(port_num));
if (err == ESP_OK) {
err = sleep_retention_module_deinit(I2C_SLEEP_RETENTION_MODULE(port_num));
if (i2c_bus->retention_link_created) {
sleep_retention_module_free(i2c_regs_retention[port_num].module_id);
}
sleep_retention_module_deinit(i2c_regs_retention[port_num].module_id);
}
#endif
if (i2c_bus->intr_handle) {

Wyświetl plik

@ -932,6 +932,10 @@ esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config, i2c_mast
ESP_RETURN_ON_FALSE((bus_config->i2c_port < SOC_I2C_NUM || bus_config->i2c_port == -1), ESP_ERR_INVALID_ARG, TAG, "invalid i2c port number");
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(bus_config->sda_io_num) && GPIO_IS_VALID_GPIO(bus_config->scl_io_num), ESP_ERR_INVALID_ARG, TAG, "invalid SDA/SCL pin number");
#if !SOC_I2C_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(bus_config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#endif // SOC_I2C_SUPPORT_SLEEP_RETENTION
i2c_master = heap_caps_calloc(1, sizeof(i2c_master_bus_t) + 20 * sizeof(i2c_transaction_t), I2C_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(i2c_master, ESP_ERR_NO_MEM, TAG, "no memory for i2c master bus");
@ -980,6 +984,12 @@ esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config, i2c_mast
ESP_RETURN_ON_FALSE(1 << (bus_config->intr_priority) & I2C_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", bus_config->intr_priority);
}
#if I2C_USE_RETENTION_LINK
if (bus_config->flags.allow_pd != 0) {
i2c_create_retention_module(i2c_master->base);
}
#endif // I2C_USE_RETENTION_LINK
xSemaphoreTake(i2c_master->bus_lock_mux, portMAX_DELAY);
SLIST_INIT(&i2c_master->device_list);
xSemaphoreGive(i2c_master->bus_lock_mux);

Wyświetl plik

@ -54,6 +54,9 @@ extern "C" {
#define I2C_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED)
#endif
// Use retention link only when the target supports sleep retention and PM is enabled
#define I2C_USE_RETENTION_LINK (SOC_I2C_SUPPORT_SLEEP_RETENTION && CONFIG_PM_ENABLE && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP)
#define I2C_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
#define I2C_PM_LOCK_NAME_LEN_MAX 16
@ -119,6 +122,9 @@ struct i2c_bus_t {
char pm_lock_name[I2C_PM_LOCK_NAME_LEN_MAX]; // pm lock name
#endif
i2c_bus_mode_t bus_mode; // I2C bus mode
#if SOC_I2C_SUPPORT_SLEEP_RETENTION
bool retention_link_created; // mark if the retention link is created.
#endif
};
typedef struct i2c_master_device_list {
@ -260,6 +266,13 @@ esp_err_t i2c_common_set_pins(i2c_bus_handle_t handle);
*/
bool i2c_bus_occupied(i2c_port_num_t port_num);
/**
* @brief Create sleep retention link
*
* @param handle I2C bus handle
*/
void i2c_create_retention_module(i2c_bus_handle_t handle);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -235,6 +235,12 @@ esp_err_t i2c_new_slave_device(const i2c_slave_config_t *slave_config, i2c_slave
ret = esp_intr_alloc_intrstatus(i2c_periph_signal[i2c_port_num].irq, isr_flags, (uint32_t)i2c_ll_get_interrupt_status_reg(hal->dev), I2C_LL_SLAVE_EVENT_INTR, s_slave_isr_handle_default, i2c_slave, &i2c_slave->base->intr_handle);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install i2c slave interrupt failed");
#if I2C_USE_RETENTION_LINK
if (slave_config->flags.allow_pd != 0) {
i2c_create_retention_module(i2c_slave->base);
}
#endif // I2C_USE_RETENTION_LINK
portENTER_CRITICAL(&i2c_slave->base->spinlock);
i2c_ll_clear_intr_mask(hal->dev, I2C_LL_SLAVE_EVENT_INTR);
i2c_hal_slave_init(hal);

Wyświetl plik

@ -33,6 +33,9 @@ typedef struct {
size_t trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background, only valid in asynchronous transaction. (Typically max_device_num * per_transaction)*/
struct {
uint32_t enable_internal_pullup: 1; /*!< Enable internal pullups. Note: This is not strong enough to pullup buses under high-speed frequency. Recommend proper external pull-up if possible */
uint32_t allow_pd: 1; /*!< If set, the driver will backup/restore the I2C registers before/after entering/exist sleep mode.
By this approach, the system can power off I2C's power domain.
This can save power, but at the expense of more RAM being consumed */
} flags; /*!< I2C master config flags */
} i2c_master_bus_config_t;

Wyświetl plik

@ -40,6 +40,9 @@ typedef struct {
#if SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH
uint32_t slave_unmatch_en: 1; /*!< Can trigger unmatch interrupt when slave address does not match what master sends*/
#endif
uint32_t allow_pd: 1; /*!< If set, the driver will backup/restore the I2C registers before/after entering/exist sleep mode.
By this approach, the system can power off I2C's power domain.
This can save power, but at the expense of more RAM being consumed */
} flags; /*!< I2C slave config flags */
} i2c_slave_config_t;

Wyświetl plik

@ -5,8 +5,3 @@ components/esp_driver_i2c/test_apps/i2c_test_apps:
- if: SOC_I2C_SUPPORTED != 1
depends_components:
- esp_driver_i2c
# Following dependency is needed because they might increase lazy installed memory
# that can cause sleep retention memory leak check failed.
- components/ieee802154/**
- components/esp_coex/**
- components/esp_phy/**

Wyświetl plik

@ -25,13 +25,10 @@ if(CONFIG_SOC_LP_I2C_SUPPORTED)
list(APPEND srcs "test_lp_i2c.c")
endif()
# Only build this file with `CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP` and `CONFIG_IEEE802154_ENABLED` enabled
# Enable `CONFIG_IEEE802154_ENABLED` is for modem domain really power down.
# This reliable can be removed if the sleep retention got finished.
if(CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP AND CONFIG_IEEE802154_ENABLED)
if(CONFIG_SOC_I2C_SUPPORT_SLEEP_RETENTION)
list(APPEND srcs "test_i2c_sleep_retention.c")
endif()
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES unity driver test_utils ieee802154
PRIV_REQUIRES unity driver test_utils
WHOLE_ARCHIVE)

Wyświetl plik

@ -43,6 +43,7 @@ static void i2c_master_write_sleep_retention_test(void)
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_io_num = I2C_MASTER_SDA_IO,
.flags.enable_internal_pullup = true,
.flags.allow_pd = true,
};
i2c_master_bus_handle_t bus_handle;
@ -68,7 +69,9 @@ static void i2c_master_write_sleep_retention_test(void)
TEST_ESP_OK(i2c_master_transmit(dev_handle, data_wr, DATA_LENGTH, -1));
unity_wait_for_signal("i2c slave receive once, master to sleep");
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(true));
#endif
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(3 * 1000 * 1000));
TEST_ESP_OK(esp_light_sleep_start());
@ -84,7 +87,9 @@ static void i2c_master_write_sleep_retention_test(void)
unity_send_signal("master write again");
unity_wait_for_signal("ready to delete");
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(false));
#endif
TEST_ESP_OK(i2c_master_bus_rm_device(dev_handle));
TEST_ESP_OK(i2c_del_master_bus(bus_handle));
@ -103,6 +108,7 @@ static void i2c_slave_read_sleep_retention_test(void)
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_io_num = I2C_SLAVE_SDA_IO,
.slave_addr = 0x58,
.flags.allow_pd = true,
};
i2c_slave_dev_handle_t slave_handle;
@ -128,7 +134,9 @@ static void i2c_slave_read_sleep_retention_test(void)
unity_send_signal("i2c slave receive once, master to sleep");
// Slave sleep as well..
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(true));
#endif
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
TEST_ESP_OK(esp_light_sleep_start());
@ -147,7 +155,9 @@ static void i2c_slave_read_sleep_retention_test(void)
vQueueDelete(s_receive_queue);
unity_send_signal("ready to delete");
#if ESP_SLEEP_POWER_DOWN_CPU
TEST_ESP_OK(sleep_cpu_configure(false));
#endif
TEST_ESP_OK(i2c_del_slave_device(slave_handle));
}

Wyświetl plik

@ -31,6 +31,8 @@ def test_i2c(dut: Dut) -> None:
'count, config',
[
(2, 'defaults',),
(2, 'release',),
(2, 'iram_safe',),
],
indirect=True
)
@ -38,19 +40,3 @@ def test_i2c_multi_device(case_tester) -> None: # type: ignore
for case in case_tester.test_menu:
if case.attributes.get('test_env', 'generic_multi_device') == 'generic_multi_device':
case_tester.run_multi_dev_case(case=case, reset=True)
@pytest.mark.esp32c6
@pytest.mark.esp32h2
@pytest.mark.generic_multi_device
@pytest.mark.parametrize(
'count, config',
[
(2, 'sleep_retention',),
],
indirect=True
)
def test_i2c_sleep_retention(case_tester) -> None: # type: ignore
for case in case_tester.test_menu:
if case.attributes.get('test_env', 'generic_multi_device') == 'generic_multi_device':
case_tester.run_multi_dev_case(case=case, reset=True, timeout=250)

Wyświetl plik

@ -2,5 +2,6 @@ CONFIG_PM_ENABLE=y
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_I2C_ISR_IRAM_SAFE=y

Wyświetl plik

@ -1,4 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y

Wyświetl plik

@ -1,5 +0,0 @@
CONFIG_PM_ENABLE=y
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_IEEE802154_ENABLED=y
CONFIG_IEEE802154_SLEEP_ENABLE=y

Wyświetl plik

@ -85,9 +85,6 @@ typedef enum {
#define LP_I2C_SDA_IOMUX_PAD 6
#define LP_I2C_SCL_IOMUX_PAD 7
// I2C sleep retention module
#define I2C_SLEEP_RETENTION_MODULE(i2c_num) (SLEEP_RETENTION_MODULE_I2C0)
/**
* @brief Calculate I2C bus frequency
* Note that the clock accuracy is affected by the external pull-up resistor,

Wyświetl plik

@ -76,9 +76,6 @@ typedef enum {
#define I2C_LL_RESET_SLV_SCL_PULSE_NUM_DEFAULT (9)
#define I2C_LL_SCL_WAIT_US_VAL_DEFAULT (2500) // Approximate value for SCL timeout regs (in us).
// I2C sleep retention module
#define I2C_SLEEP_RETENTION_MODULE(i2c_num) ((i2c_num == 0) ? SLEEP_RETENTION_MODULE_I2C0 : SLEEP_RETENTION_MODULE_I2C1)
/**
* @brief Calculate I2C bus frequency
* Note that the clock accuracy is affected by the external pull-up resistor,

Wyświetl plik

@ -40,3 +40,32 @@ const i2c_signal_conn_t i2c_periph_signal[SOC_I2C_NUM] = {
.irq = ETS_LP_I2C_INTR_SOURCE,
},
};
// I2C sleep retention entries
// I2C registers require set the reg_update bit to make the configuration take effect
/* I2C Registers Context
Include: I2C_SCL_LOW_PERIOD_REG /
I2C_CTR_REG / I2C_TO_REG / I2C_SLAVE_ADDR_REG / I2C_FIFO_CONF_REG
I2C_INT_ENA_REG / I2C_SDA_HOLD_REG / I2C_SDA_SAMPLE_REG / I2C_SCL_START_HOLD_REG
I2C_SCL_RSTART_SETUP_REG / I2C_SCL_STOP_HOLD_REG / I2C_SCL_STOP_SETUP_REG /I2C_FILTER_CFG_REG / I2C_CLK_CONF_REG / I2C_SCL_ST_TIME_OUT_REG / I2C_SCL_MAIN_ST_TIME_OUT_REG / I2C_SCL_SP_CONF_REG / I2C_SCL_STRETCH_CONF_REG
*/
#define I2C0_RETENTION_REGS_CNT 18
#define I2C0_RETENTION_MAP_BASE I2C_SCL_LOW_PERIOD_REG(0)
static const uint32_t i2c0_regs_map[4] = {0xc03f345b, 0x3, 0, 0};
static const regdma_entries_config_t i2c0_regs_retention[] = {
[0] = {.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_I2C_LINK(0x00), I2C0_RETENTION_MAP_BASE, I2C0_RETENTION_MAP_BASE, I2C0_RETENTION_REGS_CNT, 0, 0, i2c0_regs_map[0], i2c0_regs_map[1], i2c0_regs_map[2], i2c0_regs_map[3]), \
.owner = ENTRY(0) | ENTRY(2) }, \
[1] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x01), I2C_CTR_REG(0), I2C_FSM_RST, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[2] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x02), I2C_CTR_REG(0), 0x0, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[3] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x03), I2C_CTR_REG(0), I2C_CONF_UPGATE, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[4] = {.config = REGDMA_LINK_WAIT_INIT(REGDMA_I2C_LINK(0x04), I2C_CTR_REG(0), 0x0, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
};
const i2c_reg_ctx_link_t i2c_regs_retention[SOC_HP_I2C_NUM] = {
{i2c0_regs_retention, ARRAY_SIZE(i2c0_regs_retention), SLEEP_RETENTION_MODULE_I2C0},
};

Wyświetl plik

@ -615,6 +615,10 @@ config SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH
bool
default y
config SOC_I2C_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_LP_I2C_NUM
int
default 1

Wyświetl plik

@ -265,7 +265,7 @@
#define SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS (1)
#define SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH (1)
// #define SOC_I2C_SUPPORT_SLEEP_RETENTION (1) // TODO: IDF-9693
#define SOC_I2C_SUPPORT_SLEEP_RETENTION (1)
/*-------------------------- LP_I2C CAPS -------------------------------------*/
// ESP32-C5 has 1 LP_I2C

Wyświetl plik

@ -50,7 +50,7 @@ static const regdma_entries_config_t i2c0_regs_retention[] = {
[1] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x01), I2C_CTR_REG(0), I2C_FSM_RST, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[2] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x02), I2C_CTR_REG(0), 0x0, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
.owner = ENTRY(0) | ENTRY(2) }, \
[3] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x03), I2C_CTR_REG(0), I2C_CONF_UPGATE, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[4] = {.config = REGDMA_LINK_WAIT_INIT(REGDMA_I2C_LINK(0x04), I2C_CTR_REG(0), 0x0, I2C_CONF_UPGATE_M, 1, 0), \
@ -58,5 +58,5 @@ static const regdma_entries_config_t i2c0_regs_retention[] = {
};
const i2c_reg_ctx_link_t i2c_regs_retention[SOC_HP_I2C_NUM] = {
{i2c0_regs_retention, ARRAY_SIZE(i2c0_regs_retention)},
{i2c0_regs_retention, ARRAY_SIZE(i2c0_regs_retention), SLEEP_RETENTION_MODULE_I2C0},
};

Wyświetl plik

@ -21,3 +21,32 @@ const i2c_signal_conn_t i2c_periph_signal[SOC_I2C_NUM] = {
.irq = ETS_I2C_EXT0_INTR_SOURCE,
},
};
// I2C sleep retention entries
// I2C registers require set the reg_update bit to make the configuration take effect
/* I2C Registers Context
Include: I2C_SCL_LOW_PERIOD_REG /
I2C_CTR_REG / I2C_TO_REG / I2C_SLAVE_ADDR_REG / I2C_FIFO_CONF_REG
I2C_INT_ENA_REG / I2C_SDA_HOLD_REG / I2C_SDA_SAMPLE_REG / I2C_SCL_START_HOLD_REG
I2C_SCL_RSTART_SETUP_REG / I2C_SCL_STOP_HOLD_REG / I2C_SCL_STOP_SETUP_REG /I2C_FILTER_CFG_REG / I2C_CLK_CONF_REG / I2C_SCL_ST_TIME_OUT_REG / I2C_SCL_MAIN_ST_TIME_OUT_REG / I2C_SCL_SP_CONF_REG / I2C_SCL_STRETCH_CONF_REG
*/
#define I2C0_RETENTION_REGS_CNT 18
#define I2C0_RETENTION_MAP_BASE I2C_SCL_LOW_PERIOD_REG
static const uint32_t i2c0_regs_map[4] = {0xc03f345b, 0x3, 0, 0};
static const regdma_entries_config_t i2c0_regs_retention[] = {
[0] = {.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_I2C_LINK(0x00), I2C0_RETENTION_MAP_BASE, I2C0_RETENTION_MAP_BASE, I2C0_RETENTION_REGS_CNT, 0, 0, i2c0_regs_map[0], i2c0_regs_map[1], i2c0_regs_map[2], i2c0_regs_map[3]), \
.owner = ENTRY(0) | ENTRY(2) }, \
[1] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x01), I2C_CTR_REG, I2C_FSM_RST, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[2] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x02), I2C_CTR_REG, 0x0, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[3] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x03), I2C_CTR_REG, I2C_CONF_UPGATE, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[4] = {.config = REGDMA_LINK_WAIT_INIT(REGDMA_I2C_LINK(0x04), I2C_CTR_REG, 0x0, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
};
const i2c_reg_ctx_link_t i2c_regs_retention[SOC_HP_I2C_NUM] = {
{i2c0_regs_retention, ARRAY_SIZE(i2c0_regs_retention), SLEEP_RETENTION_MODULE_I2C0},
};

Wyświetl plik

@ -423,6 +423,10 @@ config SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH
bool
default y
config SOC_I2C_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_LEDC_SUPPORT_PLL_DIV_CLOCK
bool
default y

Wyświetl plik

@ -228,6 +228,7 @@
#define SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE (1)
#define SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS (1)
#define SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH (1)
#define SOC_I2C_SUPPORT_SLEEP_RETENTION (1)
// /*-------------------------- I2S CAPS ----------------------------------------*/
// #define SOC_I2S_NUM (1U)

Wyświetl plik

@ -47,7 +47,7 @@ static const regdma_entries_config_t i2c0_regs_retention[] = {
[1] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x01), I2C_CTR_REG(0), I2C_FSM_RST, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[2] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x02), I2C_CTR_REG(0), 0x0, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
.owner = ENTRY(0) | ENTRY(2) }, \
[3] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x03), I2C_CTR_REG(0), I2C_CONF_UPGATE, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) | ENTRY(2) }, \
[4] = {.config = REGDMA_LINK_WAIT_INIT(REGDMA_I2C_LINK(0x04), I2C_CTR_REG(0), 0x0, I2C_CONF_UPGATE_M, 1, 0), \
@ -71,6 +71,6 @@ static const regdma_entries_config_t i2c1_regs_retention[] = {
};
const i2c_reg_ctx_link_t i2c_regs_retention[SOC_HP_I2C_NUM] = {
{i2c0_regs_retention, ARRAY_SIZE(i2c0_regs_retention)},
{i2c1_regs_retention, ARRAY_SIZE(i2c1_regs_retention)},
{i2c0_regs_retention, ARRAY_SIZE(i2c0_regs_retention), SLEEP_RETENTION_MODULE_I2C0},
{i2c1_regs_retention, ARRAY_SIZE(i2c1_regs_retention), SLEEP_RETENTION_MODULE_I2C1},
};

Wyświetl plik

@ -34,3 +34,49 @@ const i2c_signal_conn_t i2c_periph_signal[SOC_I2C_NUM] = {
.irq = ETS_LP_I2C_INTR_SOURCE,
},
};
// I2C sleep retention entries
// I2C registers require set the reg_update bit to make the configuration take effect
/* I2C Registers Context
Include: I2C_SCL_LOW_PERIOD_REG /
I2C_CTR_REG / I2C_TO_REG / I2C_SLAVE_ADDR_REG / I2C_FIFO_CONF_REG
I2C_INT_ENA_REG / I2C_SDA_HOLD_REG / I2C_SDA_SAMPLE_REG / I2C_SCL_START_HOLD_REG
I2C_SCL_RSTART_SETUP_REG / I2C_SCL_STOP_HOLD_REG / I2C_SCL_STOP_SETUP_REG /I2C_FILTER_CFG_REG / I2C_CLK_CONF_REG / I2C_SCL_ST_TIME_OUT_REG / I2C_SCL_MAIN_ST_TIME_OUT_REG / I2C_SCL_SP_CONF_REG / I2C_SCL_STRETCH_CONF_REG
*/
#define I2C0_RETENTION_REGS_CNT 18
#define I2C0_RETENTION_MAP_BASE I2C_SCL_LOW_PERIOD_REG(0)
static const uint32_t i2c0_regs_map[4] = {0xc03f345b, 0x3, 0, 0};
static const regdma_entries_config_t i2c0_regs_retention[] = {
[0] = {.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_I2C_LINK(0x00), I2C0_RETENTION_MAP_BASE, I2C0_RETENTION_MAP_BASE, I2C0_RETENTION_REGS_CNT, 0, 0, i2c0_regs_map[0], i2c0_regs_map[1], i2c0_regs_map[2], i2c0_regs_map[3]), \
.owner = ENTRY(0) }, \
[1] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x01), I2C_CTR_REG(0), I2C_FSM_RST, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) }, \
[2] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x02), I2C_CTR_REG(0), 0x0, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) }, \
[3] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x03), I2C_CTR_REG(0), I2C_CONF_UPGATE, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) }, \
[4] = {.config = REGDMA_LINK_WAIT_INIT(REGDMA_I2C_LINK(0x04), I2C_CTR_REG(0), 0x0, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) }, \
};
#define I2C1_RETENTION_REGS_CNT 18
#define I2C1_RETENTION_MAP_BASE I2C_SCL_LOW_PERIOD_REG(1)
static const uint32_t i2c1_regs_map[4] = {0xc03f345b, 0x3, 0, 0};
static const regdma_entries_config_t i2c1_regs_retention[] = {
[0] = {.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_I2C_LINK(0x00), I2C1_RETENTION_MAP_BASE, I2C1_RETENTION_MAP_BASE, I2C1_RETENTION_REGS_CNT, 0, 0, i2c1_regs_map[0], i2c1_regs_map[1], i2c1_regs_map[2], i2c1_regs_map[3]), \
.owner = ENTRY(0) }, \
[1] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x01), I2C_CTR_REG(1), I2C_FSM_RST, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) }, \
[2] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x02), I2C_CTR_REG(1), 0x0, I2C_FSM_RST_M, 1, 0), \
.owner = ENTRY(0) }, \
[3] = {.config = REGDMA_LINK_WRITE_INIT(REGDMA_I2C_LINK(0x03), I2C_CTR_REG(1), I2C_CONF_UPGATE, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) }, \
[4] = {.config = REGDMA_LINK_WAIT_INIT(REGDMA_I2C_LINK(0x04), I2C_CTR_REG(1), 0x0, I2C_CONF_UPGATE_M, 1, 0), \
.owner = ENTRY(0) }, \
};
const i2c_reg_ctx_link_t i2c_regs_retention[SOC_HP_I2C_NUM] = {
{i2c0_regs_retention, ARRAY_SIZE(i2c0_regs_retention), SLEEP_RETENTION_MODULE_I2C0},
{i2c1_regs_retention, ARRAY_SIZE(i2c1_regs_retention), SLEEP_RETENTION_MODULE_I2C1},
};

Wyświetl plik

@ -775,6 +775,10 @@ config SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH
bool
default y
config SOC_I2C_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_LP_I2C_NUM
int
default 1

Wyświetl plik

@ -40,6 +40,8 @@ typedef enum periph_retention_module {
SLEEP_RETENTION_MODULE_AXI_DMA_CH0 = 16,
SLEEP_RETENTION_MODULE_AXI_DMA_CH1 = 17,
SLEEP_RETENTION_MODULE_AXI_DMA_CH2 = 18,
SLEEP_RETENTION_MODULE_I2C0 = 19,
SLEEP_RETENTION_MODULE_I2C1 = 20,
SLEEP_RETENTION_MODULE_I2S0 = 19,
SLEEP_RETENTION_MODULE_I2S1 = 20,

Wyświetl plik

@ -304,7 +304,7 @@
#define SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS (1)
#define SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH (1)
// #define SOC_I2C_SUPPORT_SLEEP_RETENTION (1) // TODO: IDF-9353
#define SOC_I2C_SUPPORT_SLEEP_RETENTION (1)
/*-------------------------- LP_I2C CAPS -------------------------------------*/
// ESP32-P4 has 1 LP_I2C

Wyświetl plik

@ -11,6 +11,9 @@
#if SOC_I2C_SUPPORTED
#include "soc/regdma.h"
#include "soc/interrupts.h"
#if SOC_I2C_SUPPORT_SLEEP_RETENTION
#include "soc/retention_periph_defs.h"
#endif
#ifdef __cplusplus
extern "C" {
@ -31,6 +34,7 @@ extern const i2c_signal_conn_t i2c_periph_signal[SOC_I2C_NUM];
typedef struct {
const regdma_entries_config_t *link_list;
uint32_t link_num;
periph_retention_module_t module_id;
} i2c_reg_ctx_link_t;
extern const i2c_reg_ctx_link_t i2c_regs_retention[SOC_HP_I2C_NUM];

Wyświetl plik

@ -111,7 +111,7 @@ I2C master bus requires the configuration that specified by :cpp:type:`i2c_maste
- :cpp:member:`i2c_master_bus_config_t::intr_priority` sets the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2 or 3), otherwise use the priority indicated by :cpp:member:`i2c_master_bus_config_t::intr_priority`. Please use the number form (1, 2, 3) , not the bitmask form ((1<<1), (1<<2), (1<<3)).
- :cpp:member:`i2c_master_bus_config_t::trans_queue_depth` sets the depth of internal transfer queue. Only valid in asynchronous transaction.
- :cpp:member:`i2c_master_bus_config_t::enable_internal_pullup` enables internal pullups. Note: This is not strong enough to pullup buses under high-speed frequency. A suitable external pullup is recommended.
- :cpp:member:`i2c_master_bus_config_t::allow_pd` configures if the driver allows the system to power down the peripheral in light sleep mode. Before entering sleep, the system will backup the I2C register context, which will be restored later when the system exit the sleep mode. Powering down the peripheral can save more power, but at the cost of more memory consumed to save the register context. It's a tradeoff between power consumption and memory consumption. This configuration option relies on specific hardware feature, if you enable it on an unsupported chip, you will see error message like ``not able to power down in light sleep``.
If the configurations in :cpp:type:`i2c_master_bus_config_t` is specified, then :cpp:func:`i2c_new_master_bus` can be called to allocate and initialize an I2C master bus. This function will return an I2C bus handle if it runs correctly. Specifically, when there are no more I2C port available, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error.

Wyświetl plik

@ -111,6 +111,7 @@ I2C 主机总线需要 :cpp:type:`i2c_master_bus_config_t` 指定的配置:
- :cpp:member:`i2c_master_bus_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0``,则驱动程序将使用低或中优先级的中断(优先级可设为 1、2 或 3 中的一个),若未设置,则将使用 :cpp:member:`i2c_master_bus_config_t::intr_priority` 指示的优先级。请使用数字形式1、2、3不要用位掩码形式(1<<1)、(1<<2)、(1<<3))。
- :cpp:member:`i2c_master_bus_config_t::trans_queue_depth` 设置内部传输队列的深度,但仅在异步传输中有效。
- :cpp:member:`i2c_master_bus_config_t::enable_internal_pullup` 启用内部上拉电阻。注意:该设置无法在高速频率下拉高总线,此时建议使用合适的外部上拉电阻。
- :cpp:member:`i2c_master_bus_config_t::allow_pd` 配置驱动程序是否允许系统在睡眠模式下关闭外设电源。在进入睡眠之前,系统将备份 I2C 寄存器上下文,当系统退出睡眠模式时,这些上下文将被恢复。关闭外设可以节省更多功耗,但代价是消耗更多内存来保存寄存器上下文。你需要在功耗和内存消耗之间做权衡。此配置选项依赖于特定的硬件功能,如果在不支持的芯片上启用它,你将看到类似 ``not able to power down in light sleep`` 的错误消息。
如果在 :cpp:type:`i2c_master_bus_config_t` 中指定了配置,则可调用 :cpp:func:`i2c_new_master_bus` 来分配和初始化 I2C 主机总线。如果函数运行正确,则将返回一个 I2C 总线句柄。若没有可用的 I2C 端口,此函数将返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误。

Wyświetl plik

@ -52,6 +52,7 @@ void test_i2c_lcd_apis(void)
.trans_queue_depth = 4,
.flags = {
.enable_internal_pullup = true,
.allow_pd = false,
}
};