From 9b17b8470e81021354ac21c0b488acb6bc73787f Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Mon, 26 May 2025 16:30:29 +0800 Subject: [PATCH] feat(i3c): Add example for i2c mode in i3c peripheral --- Kconfig | 1 + components/esp_driver_i3c/CMakeLists.txt | 2 +- components/esp_driver_i3c/Kconfig | 7 +- components/esp_driver_i3c/i3c_master.c | 514 +++++++++--------- .../esp_driver_i3c/i3c_master_private.h | 58 +- .../include/driver/i3c_master.h | 59 +- .../include/driver/i3c_master_i2c.h | 12 +- .../include/driver/i3c_master_types.h | 13 +- components/esp_driver_i3c/linker.lf | 12 +- .../test_apps/i3c_test_apps/CMakeLists.txt | 19 +- .../i3c_test_apps/main/test_app_main.c | 15 +- .../main/test_i3c_master_common.c | 32 +- .../i3c_test_apps/sdkconfig.ci.cache_safe | 8 +- .../i3c_test_apps/sdkconfig.ci.release | 1 + .../i3c_test_apps/sdkconfig.defaults | 1 + .../hal/esp32p4/include/hal/i3c_master_ll.h | 69 +-- components/soc/esp32p4/i3c_master_periph.c | 1 + .../esp32p4/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32p4/include/soc/soc_caps.h | 2 + .../soc/include/soc/i3c_master_periph.h | 1 + examples/peripherals/.build-test-rules.yml | 6 + .../i3c/i3c_i2c_basic/CMakeLists.txt | 8 + .../peripherals/i3c/i3c_i2c_basic/README.md | 53 ++ .../i3c/i3c_i2c_basic/main/CMakeLists.txt | 5 + .../i3c/i3c_i2c_basic/main/Kconfig.projbuild | 18 + .../i3c_i2c_basic/main/i3c_i2c_basic_main.c | 120 ++++ .../i3c/i3c_i2c_basic/sdkconfig.defaults | 1 + tools/idf_py_actions/hints.yml | 4 + 28 files changed, 639 insertions(+), 411 deletions(-) create mode 100644 examples/peripherals/i3c/i3c_i2c_basic/CMakeLists.txt create mode 100644 examples/peripherals/i3c/i3c_i2c_basic/README.md create mode 100644 examples/peripherals/i3c/i3c_i2c_basic/main/CMakeLists.txt create mode 100644 examples/peripherals/i3c/i3c_i2c_basic/main/Kconfig.projbuild create mode 100644 examples/peripherals/i3c/i3c_i2c_basic/main/i3c_i2c_basic_main.c create mode 100644 examples/peripherals/i3c/i3c_i2c_basic/sdkconfig.defaults diff --git a/Kconfig b/Kconfig index 1aa7dd4816..79415b4457 100644 --- a/Kconfig +++ b/Kconfig @@ -717,3 +717,4 @@ mainmenu "Espressif IoT Development Framework Configuration" - CONFIG_USB_HOST_EXT_PORT_RESET_ATTEMPTS - CONFIG_LIBC_PICOLIBC - CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION + - CONFIG_I3C_MASTER_ENABLED diff --git a/components/esp_driver_i3c/CMakeLists.txt b/components/esp_driver_i3c/CMakeLists.txt index d399530f0a..2d0caf3272 100644 --- a/components/esp_driver_i3c/CMakeLists.txt +++ b/components/esp_driver_i3c/CMakeLists.txt @@ -4,7 +4,7 @@ set(srcs) set(include "include") # I3C related source files. -if(CONFIG_SOC_I3C_MASTER_SUPPORTED) +if(CONFIG_I3C_MASTER_ENABLED) list(APPEND srcs "i3c_master.c" ) endif() diff --git a/components/esp_driver_i3c/Kconfig b/components/esp_driver_i3c/Kconfig index 3aa3980efb..4e64453cce 100644 --- a/components/esp_driver_i3c/Kconfig +++ b/components/esp_driver_i3c/Kconfig @@ -1,5 +1,10 @@ menu "ESP-Driver:I3C Master Configurations" - depends on SOC_I3C_MASTER_SUPPORTED + + config I3C_MASTER_ENABLED + bool + default y if SOC_I3C_MASTER_SUPPORTED && IDF_EXPERIMENTAL_FEATURES + default n + config I3C_MASTER_ISR_CACHE_SAFE bool "I3C ISR Cache-Safe" select I3C_MASTER_ISR_HANDLER_IN_IRAM diff --git a/components/esp_driver_i3c/i3c_master.c b/components/esp_driver_i3c/i3c_master.c index 52120eccfa..06e18f035b 100644 --- a/components/esp_driver_i3c/i3c_master.c +++ b/components/esp_driver_i3c/i3c_master.c @@ -11,41 +11,35 @@ #include "esp_types.h" #include "esp_attr.h" #include "esp_check.h" -#if CONFIG_I3C_MASTER_ENABLE_DEBUG_LOG -// The local log level must be defined before including esp_log.h -// Set the maximum log level for this source file -#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG -#endif #include "esp_log.h" #include "esp_intr_alloc.h" +#include "esp_clk_tree.h" +#include "esp_cache.h" +#include "esp_pm.h" +#include "esp_memory_utils.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" +#include "freertos/idf_additions.h" #include "esp_private/periph_ctrl.h" #include "esp_private/esp_clk.h" -#include "esp_rom_gpio.h" -#include "driver/gpio.h" -#include "hal/gpio_hal.h" -#include "freertos/idf_additions.h" -#include "hal/i3c_master_ll.h" -#include "driver/i3c_master.h" -#include "driver/i3c_master_i2c.h" #include "esp_private/gpio.h" -#include "i3c_master_private.h" -#include "hal/i3c_master_hal.h" -#include "soc/clk_tree_defs.h" -#include "esp_clk_tree.h" -#include "hal/dma_types.h" -#include "soc/i3c_master_periph.h" #include "esp_private/esp_clk_tree_common.h" #include "esp_private/gdma.h" #include "esp_private/gdma_link.h" #include "esp_private/esp_dma_utils.h" -#include "esp_cache.h" +#include "esp_private/esp_cache_private.h" +#include "driver/gpio.h" +#include "driver/i3c_master.h" +#include "driver/i3c_master_i2c.h" +#include "hal/gpio_hal.h" +#include "hal/i3c_master_hal.h" +#include "hal/dma_types.h" #include "hal/cache_hal.h" #include "hal/cache_ll.h" -#include "esp_pm.h" -#include "esp_memory_utils.h" -#include "esp_private/esp_cache_private.h" +#include "hal/i3c_master_ll.h" +#include "i3c_master_private.h" +#include "soc/clk_tree_defs.h" +#include "soc/i3c_master_periph.h" static const char *TAG = "i3c.master"; @@ -61,70 +55,47 @@ static bool i3c_master_bus_occupied(i3c_master_bus_num_t bus_num) return s_i3c_master_platform.buses[bus_num] != NULL; } -static esp_err_t s_do_dma_transaction(i3c_master_bus_handle_t bus_handle, i3c_i2c_transaction_desc_t *trans) +// Forward declaration for DMA transaction handler +static esp_err_t do_dma_transaction_handler(i3c_master_bus_handle_t bus_handle, i3c_i2c_transaction_desc_t *trans); + +static esp_err_t do_fifo_transaction_handler(i3c_master_bus_handle_t bus_handle, i3c_i2c_transaction_desc_t *trans) { - esp_err_t err = ESP_OK; bus_handle->cur_trans = trans; + bus_handle->read_buffer_left_size = trans->read_buffer ? trans->command_table[0].cmd_h.regular.dl : 0; + bus_handle->read_fifo_buffer_pointer = trans->read_buffer; - portENTER_CRITICAL_SAFE(&bus_handle->spinlock); - i3c_master_ll_set_i2c_fast_mode_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_speed_hz); - i3c_master_ll_set_address_device_table(bus_handle->hal.dev, trans->addr_table, trans->addr_table_num); - i3c_master_ll_set_command(bus_handle->hal.dev, trans->command_table, trans->command_table_num); - portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); + size_t actual_write_size = 0; - for (int i = 0; i < trans->command_table_num; i++) { - if (trans->command_table[i].cmd_l.regular.rnw == I3C_MASTER_WRITE_TRANSFER) { - size_t write_size = trans->command_table[i].cmd_h.regular.dl; - size_t dma_aligned_size = I3C_ALIGN_UP(write_size, I3C_MASTER_DMA_INTERFACE_ALIGNMENT); - gdma_buffer_mount_config_t mount_config = { - .buffer = trans->write_buffer, - .length = dma_aligned_size, - .flags = { - .mark_eof = true, - .mark_final = true, - } - }; - - portENTER_CRITICAL_SAFE(&bus_handle->spinlock); - i3c_master_ll_enable_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR); - i3c_master_ll_clear_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR); - portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); - gdma_link_mount_buffers(bus_handle->tx_dma_link, 0, &mount_config, 1, NULL); - esp_cache_msync(trans->write_buffer, write_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - if (err != ESP_OK) { - ESP_DRAM_LOGE(TAG, "memory sync failed"); - return err; - } - gdma_reset(bus_handle->dma_tx_chan); - gdma_start(bus_handle->dma_tx_chan, gdma_link_get_head_addr(bus_handle->tx_dma_link)); - } - if (trans->command_table[i].cmd_l.regular.rnw == I3C_MASTER_READ_TRANSFER) { - size_t read_size = trans->command_table[i].cmd_h.regular.dl; - size_t dma_aligned_size = I3C_ALIGN_UP(read_size, I3C_MASTER_DMA_INTERFACE_ALIGNMENT); - gdma_buffer_mount_config_t mount_config = { - .buffer = trans->read_buffer, - .length = dma_aligned_size, - .flags = { - .mark_eof = true, - .mark_final = true, - } - }; - - portENTER_CRITICAL_SAFE(&bus_handle->spinlock); - i3c_master_ll_enable_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_RECEIVE_EVENT_INTR); - i3c_master_ll_clear_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_RECEIVE_EVENT_INTR); - portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); - gdma_link_mount_buffers(bus_handle->rx_dma_link, 0, &mount_config, 1, NULL); - gdma_reset(bus_handle->dma_rx_chan); - gdma_start(bus_handle->dma_rx_chan, gdma_link_get_head_addr(bus_handle->rx_dma_link)); - } + if (trans->scl_freq_hz > 400 * 1000) { + i3c_master_ll_set_i2c_fast_mode_plus_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_freq_hz); + } else { + i3c_master_ll_set_i2c_fast_mode_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_freq_hz); } - // transaction start + i3c_master_ll_set_device_address_table(bus_handle->hal.dev, trans->addr_table, trans->addr_table_num); + i3c_master_ll_set_command(bus_handle->hal.dev, trans->command_table, trans->command_table_num); + portENTER_CRITICAL_SAFE(&bus_handle->spinlock); - i3c_master_ll_start_transaction(bus_handle->hal.dev); + i3c_master_ll_enable_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR | I3C_LL_MASTER_RECEIVE_EVENT_INTR); + i3c_master_ll_clear_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR | I3C_LL_MASTER_RECEIVE_EVENT_INTR); portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); + if (trans->write_buffer && trans->command_table[0].cmd_h.regular.dl >= I3C_LL_MASTER_DATA_BUFFER_SIZE) { + actual_write_size = I3C_LL_MASTER_DATA_BUFFER_SIZE; + } else { + actual_write_size = trans->write_buffer ? trans->command_table[0].cmd_h.regular.dl : 0; + } + + i3c_master_ll_write_tx_port(bus_handle->hal.dev, trans->write_buffer, actual_write_size); + + bus_handle->write_fifo_buffer_pointer = trans->write_buffer; + bus_handle->write_buffer_left_size = trans->write_buffer ? trans->command_table[0].cmd_h.regular.dl : 0; + + bus_handle->write_fifo_buffer_pointer += actual_write_size; + bus_handle->write_buffer_left_size -= actual_write_size; + + i3c_master_ll_start_transaction(bus_handle->hal.dev); + return ESP_OK; } @@ -133,7 +104,7 @@ static esp_err_t s_do_dma_transaction(i3c_master_bus_handle_t bus_handle, i3c_i2 If data to be sent is larger than fifo, we can add data and continue transfer with buf threshold interrupt. */ -static bool handle_tx_data_buf_thld_int(i3c_master_bus_handle_t i3c_master, uint32_t int_mask) +static bool handle_tx_data_buf_threshold_int(i3c_master_bus_handle_t i3c_master, uint32_t int_mask) { portENTER_CRITICAL_SAFE(&i3c_master->spinlock); size_t empty_size = i3c_master_ll_get_tx_fifo_empty_count(i3c_master->hal.dev) * 4; @@ -163,7 +134,7 @@ static bool handle_tx_data_buf_thld_int(i3c_master_bus_handle_t i3c_master, uint */ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master) { - i3c_master_event_t event = I3C_MASTER_EVENT_DONE; + i3c_master_event_t event = I3C_MASTER_EVENT_TRANS_DONE; BaseType_t do_yield = pdFALSE; bool need_yield = false; @@ -177,8 +148,8 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master) portEXIT_CRITICAL_SAFE(&i3c_master->spinlock); } - i3c_master_response_data_t response_data; - response_data.val = i3c_master_ll_get_response_buffer_value(i3c_master->hal.dev); + i3c_master_ll_response_descriptor_t response_data; + response_data = i3c_master_ll_get_response_data(i3c_master->hal.dev); if (response_data.err_sts == I3C_MASTER_LL_ADDRESS_NACK_OR_DYNAMIC_ADDRESS_NACK) { event = I3C_MASTER_EVENT_NACK; } @@ -192,7 +163,7 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master) i3c_i2c_transaction_desc_t *trans_desc = NULL; i3c_fsm_t expected_fsm = I3C_FSM_RUN; - if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_ENABLE_WAIT)) { + if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_WAIT)) { trans_desc = i3c_master->cur_trans; xQueueSendFromISR(i3c_master->trans_queues[I3C_TRANS_QUEUE_COMPLETE], &trans_desc, &do_yield); if (do_yield) { @@ -201,21 +172,14 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master) atomic_store(&i3c_master->fsm, I3C_FSM_ENABLE); } - i3c_master_i2c_device_handle_t i2c_dev = NULL; - i3c_i2c_master_device_list_t *device_item = NULL; - SLIST_FOREACH(device_item, &i3c_master->device_list, next) { - if (device_item->i2c_dev->address == i3c_master->cur_trans->addr_table[0].dat.i2c.static_addr) { - i2c_dev = device_item->i2c_dev; - break; - } - } + i3c_master_i2c_device_handle_t i2c_dev = i3c_master->cur_trans->dev_handle; if (i3c_master->cur_trans->read_buffer != NULL) { size_t dma_rcv_size = gdma_link_count_buffer_size_till_eof(i3c_master->rx_dma_link, 0); size_t c2m_aligned_size = I3C_ALIGN_UP(dma_rcv_size, i3c_master->cache_line_size); esp_cache_msync(i3c_master->cur_trans->read_buffer, c2m_aligned_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - i3c_i2c_master_event_data_t evt_data = { + i3c_master_i2c_device_event_data_t evt_data = { .data = i3c_master->cur_trans->read_buffer, - .recv_size = dma_rcv_size, + .data_size = dma_rcv_size, .event = event, }; @@ -227,10 +191,10 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master) } expected_fsm = I3C_FSM_ENABLE; - if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_RUN_WAIT)) { + if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_WAIT)) { if (xQueueReceiveFromISR(i3c_master->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &trans_desc, &do_yield) == pdTRUE) { atomic_store(&i3c_master->fsm, I3C_FSM_RUN); - s_do_dma_transaction(i3c_master, trans_desc); + i3c_master->transaction_handler(i3c_master, trans_desc); if (do_yield) { need_yield = true; } @@ -242,7 +206,7 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master) return need_yield; } -static void handle_rx_data_buf_threshhold_int(i3c_master_bus_handle_t i3c_master) +static void handle_rx_data_buf_threshold_int(i3c_master_bus_handle_t i3c_master) { if (i3c_master->use_dma_transaction == false) { portENTER_CRITICAL_SAFE(&i3c_master->spinlock); @@ -267,7 +231,7 @@ static void i3c_master_isr_handler_default(void *arg) portEXIT_CRITICAL_SAFE(&i3c_master->spinlock); if (int_mask & I3C_MST_TX_DATA_BUF_THLD_INT_ST) { - if (handle_tx_data_buf_thld_int(i3c_master, int_mask)) { + if (handle_tx_data_buf_threshold_int(i3c_master, int_mask)) { need_yield = true; } } @@ -279,7 +243,7 @@ static void i3c_master_isr_handler_default(void *arg) } if (int_mask & I3C_MST_RX_DATA_BUF_THLD_INT_ST) { - handle_rx_data_buf_threshhold_int(i3c_master); + handle_rx_data_buf_threshold_int(i3c_master); } if (need_yield) { @@ -304,10 +268,10 @@ static esp_err_t i3c_master_async_transaction_preparation(i3c_master_bus_t *i3c_ ESP_RETURN_ON_FALSE(xQueueSend(i3c_master_handle->trans_queues[I3C_TRANS_QUEUE_READY], &p_trans_desc, 0) == pdTRUE, ESP_ERR_INVALID_STATE, TAG, "ready queue full"); } - i3c_master_handle->i3c_async_addr_table = (i3c_master_address_table_t(*)[SOC_I3C_MASTER_ADDRESS_TABLE_NUM])heap_caps_calloc(i3c_master_handle->queue_depth, sizeof(*i3c_master_handle->i3c_async_addr_table), I3C_MASTER_MEM_ALLOC_CAPS); + i3c_master_handle->i3c_async_addr_table = (i3c_master_ll_device_address_descriptor_t(*)[SOC_I3C_MASTER_ADDRESS_TABLE_NUM])heap_caps_calloc(i3c_master_handle->queue_depth, sizeof(*i3c_master_handle->i3c_async_addr_table), I3C_MASTER_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(i3c_master_handle->i3c_async_addr_table, ESP_ERR_NO_MEM, TAG, "no mem for address table"); - i3c_master_handle->i3c_async_command_table = (i3c_master_command_table_t(*)[SOC_I3C_MASTER_COMMAND_TABLE_NUM])heap_caps_calloc(i3c_master_handle->queue_depth, sizeof(*i3c_master_handle->i3c_async_command_table), I3C_MASTER_MEM_ALLOC_CAPS); + i3c_master_handle->i3c_async_command_table = (i3c_master_ll_command_descriptor_t(*)[SOC_I3C_MASTER_COMMAND_TABLE_NUM])heap_caps_calloc(i3c_master_handle->queue_depth, sizeof(*i3c_master_handle->i3c_async_command_table), I3C_MASTER_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(i3c_master_handle->i3c_async_command_table, ESP_ERR_NO_MEM, TAG, "no mem for command table"); i3c_master_handle->ops_prepare_idx = 0; @@ -317,21 +281,25 @@ static esp_err_t i3c_master_async_transaction_preparation(i3c_master_bus_t *i3c_ return ESP_OK; } -static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const i3c_master_bus_config_t *bus_config) +static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const i3c_master_dma_config_t *dma_config) { // Initialize i3c-dma connection i3c_master_ll_enable_tx_by_dma(i3c_master_handle->hal.dev, true); i3c_master_ll_enable_rx_by_dma(i3c_master_handle->hal.dev, true); // Initialize DMA TX channel - gdma_channel_alloc_config_t dma_cfg = {}; + gdma_channel_alloc_config_t dma_cfg = { +#if CONFIG_I3C_MASTER_ISR_CACHE_SAFE + .flags.isr_cache_safe = true, +#endif + }; dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_cfg, &i3c_master_handle->dma_tx_chan), TAG, "DMA tx channel alloc failed"); gdma_connect(i3c_master_handle->dma_tx_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I3C, 0)); gdma_transfer_config_t transfer_cfg = { .access_ext_mem = false, - .max_data_burst_size = bus_config->dma_burst_size ? bus_config->dma_burst_size : 16, + .max_data_burst_size = dma_config->dma_burst_size ? dma_config->dma_burst_size : 16, }; ESP_RETURN_ON_ERROR(gdma_config_transfer(i3c_master_handle->dma_tx_chan, &transfer_cfg), TAG, "Config DMA tx channel transfer failed"); @@ -339,7 +307,7 @@ static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const size_t int_mem_align = 0; gdma_get_alignment_constraints(i3c_master_handle->dma_tx_chan, &int_mem_align, NULL); size_t buffer_alignment = I3C_ALIGN_UP(int_mem_align, I3C_MASTER_DMA_INTERFACE_ALIGNMENT); - size_t num_dma_nodes = esp_dma_calculate_node_count(bus_config->max_transfer_size, buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE); + size_t num_dma_nodes = esp_dma_calculate_node_count(dma_config->max_transfer_size, buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE); gdma_link_list_config_t dma_link_config = { .buffer_alignment = buffer_alignment, // no special buffer alignment for i3c master buffer .item_alignment = 4, // 4 bytes alignment for AHB-DMA @@ -360,38 +328,22 @@ static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const return ESP_OK; } -static esp_err_t s_i3c_pins_config(const i3c_master_bus_config_t *bus_config) +static esp_err_t i3c_pins_config(const i3c_master_bus_config_t *bus_config) { esp_err_t ret = ESP_OK; // SDA pin configurations ESP_RETURN_ON_ERROR(gpio_set_level(bus_config->sda_io_num, 1), TAG, "i2c sda pin set level failed"); gpio_input_enable(bus_config->sda_io_num); - if (bus_config->flags.enable_internal_opendrain) { - gpio_od_enable(bus_config->sda_io_num); - } - if (bus_config->flags.enable_internal_pullup) { - gpio_pullup_en(bus_config->sda_io_num); - } else { - gpio_pullup_dis(bus_config->sda_io_num); - } gpio_func_sel(bus_config->sda_io_num, PIN_FUNC_GPIO); - esp_rom_gpio_connect_out_signal(bus_config->sda_io_num, i3c_master_periph_signal->sda_out_sig, 0, 0); - esp_rom_gpio_connect_in_signal(bus_config->sda_io_num, i3c_master_periph_signal->sda_in_sig, 0); + gpio_matrix_output(bus_config->sda_io_num, i3c_master_periph_signal->sda_out_sig, 0, 0); + gpio_matrix_input(bus_config->sda_io_num, i3c_master_periph_signal->sda_in_sig, 0); // SCL pin configurations ESP_RETURN_ON_ERROR(gpio_set_level(bus_config->scl_io_num, 1), TAG, "i2c scl pin set level failed"); gpio_input_enable(bus_config->scl_io_num); - if (bus_config->flags.enable_internal_opendrain) { - gpio_od_enable(bus_config->scl_io_num); - } - if (bus_config->flags.enable_internal_pullup) { - gpio_pullup_en(bus_config->scl_io_num); - } else { - gpio_pullup_dis(bus_config->scl_io_num); - } gpio_func_sel(bus_config->scl_io_num, PIN_FUNC_GPIO); - esp_rom_gpio_connect_out_signal(bus_config->scl_io_num, i3c_master_periph_signal->scl_out_sig, 0, 0); - esp_rom_gpio_connect_in_signal(bus_config->scl_io_num, i3c_master_periph_signal->scl_in_sig, 0); + gpio_matrix_output(bus_config->scl_io_num, i3c_master_periph_signal->scl_out_sig, 0, 0); + gpio_matrix_input(bus_config->scl_io_num, i3c_master_periph_signal->scl_in_sig, 0); return ret; } @@ -454,11 +406,16 @@ static esp_err_t i3c_master_bus_destroy(i3c_master_bus_handle_t bus_handle) ESP_RETURN_ON_ERROR(esp_intr_free(bus_handle->intr_handle), TAG, "delete interrupt service failed"); } - // Free DMA resources if used - if (bus_handle->use_dma_transaction) { - i3c_master_deinit_dma(bus_handle); + if (bus_handle->clock_source) { + esp_clk_tree_enable_src((soc_module_clk_t)bus_handle->clock_source, false); } +#if CONFIG_PM_ENABLE + if (bus_handle->pm_lock) { + esp_pm_lock_delete(bus_handle->pm_lock); + } +#endif + // Free async transaction resources if (bus_handle->async_transaction) { i3c_master_del_async_transaction_source(bus_handle); @@ -483,10 +440,105 @@ esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle) return ESP_OK; } +// DMA transaction handler implementation - only linked when DMA is enabled +static esp_err_t do_dma_transaction_handler(i3c_master_bus_handle_t bus_handle, i3c_i2c_transaction_desc_t *trans) +{ + esp_err_t err = ESP_OK; + bus_handle->cur_trans = trans; + printf("here, do dma\n"); + + portENTER_CRITICAL_SAFE(&bus_handle->spinlock); + if (trans->scl_freq_hz > 400 * 1000) { + i3c_master_ll_set_i2c_fast_mode_plus_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_freq_hz); + } else { + i3c_master_ll_set_i2c_fast_mode_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_freq_hz); + } + i3c_master_ll_set_device_address_table(bus_handle->hal.dev, trans->addr_table, trans->addr_table_num); + i3c_master_ll_set_command(bus_handle->hal.dev, trans->command_table, trans->command_table_num); + portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); + + for (int i = 0; i < trans->command_table_num; i++) { + if (trans->command_table[i].cmd_l.regular.rnw == I3C_MASTER_LL_TRANSFER_DIR_WRITE) { + size_t write_size = trans->command_table[i].cmd_h.regular.dl; + size_t dma_aligned_size = I3C_ALIGN_UP(write_size, I3C_MASTER_DMA_INTERFACE_ALIGNMENT); + gdma_buffer_mount_config_t mount_config = { + .buffer = trans->write_buffer, + .length = dma_aligned_size, + .flags = { + .mark_eof = true, + .mark_final = true, + } + }; + + portENTER_CRITICAL_SAFE(&bus_handle->spinlock); + i3c_master_ll_enable_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR); + i3c_master_ll_clear_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR); + portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); + gdma_link_mount_buffers(bus_handle->tx_dma_link, 0, &mount_config, 1, NULL); + err = esp_cache_msync(trans->write_buffer, write_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + if (err != ESP_OK) { + ESP_DRAM_LOGE(TAG, "memory sync failed"); + return err; + } + gdma_reset(bus_handle->dma_tx_chan); + gdma_start(bus_handle->dma_tx_chan, gdma_link_get_head_addr(bus_handle->tx_dma_link)); + } + if (trans->command_table[i].cmd_l.regular.rnw == I3C_MASTER_LL_TRANSFER_DIR_READ) { + size_t read_size = trans->command_table[i].cmd_h.regular.dl; + size_t dma_aligned_size = I3C_ALIGN_UP(read_size, I3C_MASTER_DMA_INTERFACE_ALIGNMENT); + gdma_buffer_mount_config_t mount_config = { + .buffer = trans->read_buffer, + .length = dma_aligned_size, + .flags = { + .mark_eof = true, + .mark_final = true, + } + }; + + portENTER_CRITICAL_SAFE(&bus_handle->spinlock); + i3c_master_ll_enable_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_RECEIVE_EVENT_INTR); + i3c_master_ll_clear_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_RECEIVE_EVENT_INTR); + portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); + gdma_link_mount_buffers(bus_handle->rx_dma_link, 0, &mount_config, 1, NULL); + gdma_reset(bus_handle->dma_rx_chan); + gdma_start(bus_handle->dma_rx_chan, gdma_link_get_head_addr(bus_handle->rx_dma_link)); + } + } + + // transaction start + portENTER_CRITICAL_SAFE(&bus_handle->spinlock); + i3c_master_ll_start_transaction(bus_handle->hal.dev); + portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); + printf("here, start transaction\n"); + return ESP_OK; +} + +esp_err_t i3c_master_bus_decorate_dma(i3c_master_bus_handle_t bus_handle, const i3c_master_dma_config_t *dma_config) +{ + ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid bus handle"); + + // Initialize DMA using the max transfer size from DMA configuration + if (dma_config) { + ESP_RETURN_ON_ERROR(i3c_master_init_dma(bus_handle, dma_config), TAG, "DMA initialization failed"); + // Mark DMA as initialized and enable DMA transactions + bus_handle->dma_initialized = true; + bus_handle->use_dma_transaction = true; + // Switch to DMA transaction handler + bus_handle->transaction_handler = do_dma_transaction_handler; + } else { + i3c_master_deinit_dma(bus_handle); + bus_handle->dma_initialized = false; + bus_handle->use_dma_transaction = false; + // Switch back to FIFO transaction handler + bus_handle->transaction_handler = do_fifo_transaction_handler; + } + + return ESP_OK; +} + esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_master_bus_handle_t *ret_bus_handle) { - ESP_LOGW(TAG, "I3C driver is in developing, not all features are supported so far"); - ESP_RETURN_ON_FALSE(bus_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(bus_config && ret_bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); 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"); esp_err_t ret = ESP_OK; @@ -517,8 +569,8 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast /// clock enable I3C_MASTER_RCC_ATOMIC() { - i3c_master_ll_reset_register(i3c_master_handle->hal.dev); i3c_master_ll_enable_bus_clock(i3c_master_handle->hal.dev, true); + i3c_master_ll_reset_register(i3c_master_handle->hal.dev); } I3C_MASTER_CLOCK_SRC_ATOMIC() { i3c_master_ll_enable_controller_clock(i3c_master_handle->hal.dev, true); @@ -533,9 +585,12 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast i3c_master_ll_set_source_clk(i3c_master_handle->hal.dev, i3c_master_handle->clock_source); } - i3c_master_handle->use_dma_transaction = bus_config->flags.use_dma; - i3c_master_handle->async_transaction = bus_config->flags.enable_async_trans; // TODO: Only use dma can use async transaction. - ESP_RETURN_ON_FALSE(!(i3c_master_handle->async_transaction && !i3c_master_handle->use_dma_transaction), ESP_ERR_INVALID_ARG, TAG, "Async transaction requires DMA transaction"); + i3c_master_handle->dma_initialized = false; + i3c_master_handle->use_dma_transaction = false; + i3c_master_handle->async_transaction = bus_config->flags.enable_async_trans; + + // Set default transaction handler to FIFO mode + i3c_master_handle->transaction_handler = do_fifo_transaction_handler; esp_clk_tree_src_get_freq_hz(i3c_master_handle->clock_source, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &periph_src_clk_hz); @@ -556,19 +611,18 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast i3c_master_handle->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; /// pins configuration - ESP_GOTO_ON_ERROR(s_i3c_pins_config(bus_config), err, TAG, "pins initialization failed"); + ESP_GOTO_ON_ERROR(i3c_pins_config(bus_config), err, TAG, "pins initialization failed"); esp_cache_get_alignment(MALLOC_CAP_INTERNAL, &i3c_master_handle->cache_line_size); #if CONFIG_PM_ENABLE bool need_pm_lock = true; - // to make the I2C work reliable, the source clock must stay alive and unchanged + // to make the I3C work reliable, the source clock must stay alive and unchanged // driver will create different pm lock for that purpose, according to different clock source esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP; if (need_pm_lock) { - sprintf(i3c_master_handle->pm_lock_name, "I3C_MASTER"); // e.g. PORT_0 - ret = esp_pm_lock_create(pm_lock_type, 0, i3c_master_handle->pm_lock_name, &i3c_master_handle->pm_lock); + ret = esp_pm_lock_create(pm_lock_type, 0, i3c_master_periph_signal[0].module_name, &i3c_master_handle->pm_lock); ESP_GOTO_ON_ERROR(ret, err, TAG, "create pm lock failed"); } @@ -586,7 +640,7 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast // interrupt configurations if (bus_config->intr_priority) { - ESP_GOTO_ON_FALSE(1 << (bus_config->intr_priority) & I3C_MASTER_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", bus_config->intr_priority); + ESP_GOTO_ON_FALSE((1 << (bus_config->intr_priority)) & I3C_MASTER_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", bus_config->intr_priority); } int isr_flags = I3C_MASTER_INTR_ALLOC_FLAG; @@ -597,12 +651,12 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast ret = esp_intr_alloc_intrstatus(i3c_master_periph_signal->irq, isr_flags, (uint32_t)i3c_master_ll_get_interrupt_status_reg(i3c_master_handle->hal.dev), I3C_LL_MASTER_EVENT_INTR, i3c_master_isr_handler_default, i3c_master_handle, &i3c_master_handle->intr_handle); ESP_GOTO_ON_ERROR(ret, err, TAG, "install i3c master interrupt failed"); - // Initialize dma if dma transaction is needed. - if (i3c_master_handle->use_dma_transaction) { - ESP_GOTO_ON_ERROR(i3c_master_init_dma(i3c_master_handle, bus_config), err, TAG, "dma initialization failed"); - } + // Disable DMA by default, will be enabled when decorator is called + i3c_master_ll_enable_tx_by_dma(i3c_master_handle->hal.dev, false); + i3c_master_ll_enable_rx_by_dma(i3c_master_handle->hal.dev, false); // Initialize async transaction memory if async transaction is needed. + // Note: DMA dependency will be checked at runtime when transactions are executed if (i3c_master_handle->async_transaction) { ESP_GOTO_ON_ERROR(i3c_master_async_transaction_preparation(i3c_master_handle), err, TAG, "async transaction resource failed"); } @@ -623,16 +677,9 @@ esp_err_t i3c_master_bus_rm_i2c_device(i3c_master_i2c_device_handle_t handle) ESP_RETURN_ON_FALSE((handle != NULL), ESP_ERR_INVALID_ARG, TAG, "this device is not initialized"); i3c_master_bus_handle_t i3c_master = handle->bus_handle; - i3c_i2c_master_device_list_t *device_item; - xSemaphoreTake(i3c_master->bus_lock_mux, portMAX_DELAY); - SLIST_FOREACH(device_item, &i3c_master->device_list, next) { - if (handle == device_item->i2c_dev) { - SLIST_REMOVE(&i3c_master->device_list, device_item, i3c_i2c_master_device_list, next); - free(device_item); - break; - } - } - xSemaphoreGive(i3c_master->bus_lock_mux); + portENTER_CRITICAL_SAFE(&handle->bus_handle->spinlock); + SLIST_REMOVE(&i3c_master->device_list, handle, i3c_master_i2c_dev_t, next); + portEXIT_CRITICAL_SAFE(&handle->bus_handle->spinlock); if (handle) { free(handle); @@ -643,44 +690,32 @@ esp_err_t i3c_master_bus_rm_i2c_device(i3c_master_i2c_device_handle_t handle) esp_err_t i3c_master_bus_add_i2c_device(i3c_master_bus_handle_t bus_handle, const i3c_device_i2c_config_t *dev_config, i3c_master_i2c_device_handle_t *ret_handle) { - esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE((bus_handle != NULL), ESP_ERR_INVALID_ARG, TAG, "this bus is not initialized, please call `i3c_new_master_bus`"); ESP_RETURN_ON_FALSE(dev_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(dev_config->scl_speed_hz > 0, ESP_ERR_INVALID_ARG, TAG, "invalid scl frequency"); + ESP_RETURN_ON_FALSE(dev_config->scl_freq_hz > 0, ESP_ERR_INVALID_ARG, TAG, "invalid scl frequency"); - i3c_master_i2c_dev_t *i3c_i2c_dev = (i3c_master_i2c_dev_t *)heap_caps_calloc(1, sizeof(i3c_master_i2c_dev_t), I3C_MASTER_MEM_ALLOC_CAPS); + i3c_master_i2c_dev_t *i3c_i2c_dev = (i3c_master_i2c_dev_t *)heap_caps_calloc(1, sizeof(i3c_master_i2c_dev_t), MALLOC_CAP_INTERNAL); ESP_RETURN_ON_FALSE(i3c_i2c_dev, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); i3c_i2c_dev->address = dev_config->device_address; i3c_i2c_dev->bus_handle = bus_handle; - i3c_i2c_dev->scl_speed_hz = dev_config->scl_speed_hz; - i3c_i2c_dev->bypass_ack_check = dev_config->flags.disable_ack_check; + i3c_i2c_dev->scl_freq_hz = dev_config->scl_freq_hz; *ret_handle = i3c_i2c_dev; - i3c_i2c_master_device_list_t *device_item = (i3c_i2c_master_device_list_t *)heap_caps_calloc(1, sizeof(i3c_i2c_master_device_list_t), I3C_MASTER_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE((device_item != NULL), ESP_ERR_NO_MEM, err, TAG, "no memory for i3c-i2c device item`"); - device_item->i2c_dev = i3c_i2c_dev; - - xSemaphoreTake(bus_handle->bus_lock_mux, portMAX_DELAY); - SLIST_INSERT_HEAD(&bus_handle->device_list, device_item, next); - xSemaphoreGive(bus_handle->bus_lock_mux); + portENTER_CRITICAL_SAFE(&bus_handle->spinlock); + SLIST_INSERT_HEAD(&bus_handle->device_list, i3c_i2c_dev, next); + portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); return ESP_OK; - -err: - if (i3c_i2c_dev) { - i3c_master_bus_rm_i2c_device(i3c_i2c_dev); - } - return ret; } -static esp_err_t i3c_master_prepare_async_transaction(i3c_master_i2c_device_handle_t dev_handle, i3c_i2c_transaction_desc_t *trans_desc) +static esp_err_t do_async_transaction(i3c_master_i2c_device_handle_t dev_handle, i3c_i2c_transaction_desc_t *trans_desc) { i3c_master_bus_t *bus_handle = dev_handle->bus_handle; - memcpy(bus_handle->i3c_async_addr_table[bus_handle->ops_prepare_idx], trans_desc->addr_table, trans_desc->addr_table_num * sizeof(i3c_master_address_table_t)); - memcpy(bus_handle->i3c_async_command_table[bus_handle->ops_prepare_idx], trans_desc->command_table, trans_desc->command_table_num * sizeof(i3c_master_command_table_t)); + memcpy(bus_handle->i3c_async_addr_table[bus_handle->ops_prepare_idx], trans_desc->addr_table, trans_desc->addr_table_num * sizeof(i3c_master_ll_device_address_descriptor_t)); + memcpy(bus_handle->i3c_async_command_table[bus_handle->ops_prepare_idx], trans_desc->command_table, trans_desc->command_table_num * sizeof(i3c_master_ll_command_descriptor_t)); i3c_i2c_transaction_desc_t *t = NULL; @@ -698,7 +733,8 @@ static esp_err_t i3c_master_prepare_async_transaction(i3c_master_i2c_device_hand t->command_table_num = trans_desc->command_table_num; t->write_buffer = trans_desc->write_buffer; t->read_buffer = trans_desc->read_buffer; - t->scl_speed_hz = trans_desc->scl_speed_hz; + t->scl_freq_hz = trans_desc->scl_freq_hz; + t->dev_handle = dev_handle; ESP_RETURN_ON_FALSE(xQueueSend(bus_handle->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &t, 0) == pdTRUE, ESP_ERR_NO_MEM, TAG, "i3c-i2c transaction queue full"); bus_handle->ops_prepare_idx = (bus_handle->ops_prepare_idx + 1) % bus_handle->queue_depth; @@ -706,10 +742,10 @@ static esp_err_t i3c_master_prepare_async_transaction(i3c_master_i2c_device_hand i3c_fsm_t expected_fsm = I3C_FSM_ENABLE; esp_err_t ret = ESP_OK; - if (atomic_compare_exchange_strong(&bus_handle->fsm, &expected_fsm, I3C_FSM_RUN_WAIT)) { + if (atomic_compare_exchange_strong(&bus_handle->fsm, &expected_fsm, I3C_FSM_WAIT)) { if (xQueueReceive(bus_handle->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &t, 0)) { atomic_store(&bus_handle->fsm, I3C_FSM_RUN); - ret = s_do_dma_transaction(bus_handle, t); + ret = bus_handle->transaction_handler(bus_handle, t); assert(ret == ESP_OK); } else { atomic_store(&bus_handle->fsm, I3C_FSM_ENABLE); @@ -719,41 +755,6 @@ static esp_err_t i3c_master_prepare_async_transaction(i3c_master_i2c_device_hand return ESP_OK; } -static esp_err_t i3c_master_prepare_fifo_transaction(i3c_master_i2c_device_handle_t dev_handle, i3c_i2c_transaction_desc_t *trans_desc) -{ - dev_handle->bus_handle->read_buffer_left_size = trans_desc->read_buffer ? trans_desc->command_table[0].cmd_h.regular.dl : 0; - dev_handle->bus_handle->read_fifo_buffer_pointer = trans_desc->read_buffer; - - size_t actual_write_size = 0; - - portENTER_CRITICAL_SAFE(&dev_handle->bus_handle->spinlock); - i3c_master_ll_set_i2c_fast_mode_timing(dev_handle->bus_handle->hal.dev, dev_handle->bus_handle->clock_source_freq, trans_desc->scl_speed_hz); - - i3c_master_ll_set_address_device_table(dev_handle->bus_handle->hal.dev, trans_desc->addr_table, trans_desc->addr_table_num); - i3c_master_ll_set_command(dev_handle->bus_handle->hal.dev, trans_desc->command_table, trans_desc->command_table_num); - - i3c_master_ll_enable_intr_mask(dev_handle->bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR | I3C_LL_MASTER_RECEIVE_EVENT_INTR); - i3c_master_ll_clear_intr_mask(dev_handle->bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR | I3C_LL_MASTER_RECEIVE_EVENT_INTR); - if (trans_desc->write_buffer && trans_desc->command_table[0].cmd_h.regular.dl >= I3C_LL_MASTER_FIFO_LENGTH) { - actual_write_size = I3C_LL_MASTER_FIFO_LENGTH; - } else { - actual_write_size = trans_desc->write_buffer ? trans_desc->command_table[0].cmd_h.regular.dl : 0; - } - - i3c_master_ll_write_tx_port(dev_handle->bus_handle->hal.dev, trans_desc->write_buffer, actual_write_size); - - dev_handle->bus_handle->write_fifo_buffer_pointer = trans_desc->write_buffer; - dev_handle->bus_handle->write_buffer_left_size = trans_desc->write_buffer ? trans_desc->command_table[0].cmd_h.regular.dl : 0; - - dev_handle->bus_handle->write_fifo_buffer_pointer += actual_write_size; - dev_handle->bus_handle->write_buffer_left_size -= actual_write_size; - - i3c_master_ll_start_transaction(dev_handle->bus_handle->hal.dev); - portEXIT_CRITICAL_SAFE(&dev_handle->bus_handle->spinlock); - - return ESP_OK; -} - /** * This function handles the preparation and execution of an I3C or I2C transaction on the I3C master bus. * Depending on the configuration, the transaction can be handled asynchronously, using DMA, or via FIFO. @@ -765,21 +766,27 @@ static esp_err_t i3c_master_prepare_transaction(i3c_master_i2c_device_handle_t d esp_err_t ret = ESP_OK; TickType_t ticks_to_wait = (xfer_timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(xfer_timeout_ms); + // Check if async transaction requires DMA + if (dev_handle->bus_handle->async_transaction && !dev_handle->bus_handle->dma_initialized) { + ESP_LOGE(TAG, "Async transaction requires DMA, please call i3c_master_bus_decorate_dma() first"); + return ESP_ERR_INVALID_STATE; + } + if (xSemaphoreTake(dev_handle->bus_handle->bus_lock_mux, ticks_to_wait) != pdTRUE) { ESP_LOGE(TAG, "I3C software timeout"); return ESP_ERR_TIMEOUT; } +#if CONFIG_PM_ENABLE if (dev_handle->bus_handle->pm_lock) { ESP_GOTO_ON_ERROR(esp_pm_lock_acquire(dev_handle->bus_handle->pm_lock), err, TAG, "acquire pm_lock failed"); } +#endif if (dev_handle->bus_handle->async_transaction) { - ESP_GOTO_ON_ERROR(i3c_master_prepare_async_transaction(dev_handle, trans_desc), err, TAG, "Prepare async transaction failed"); - } else if (dev_handle->bus_handle->use_dma_transaction) { - ESP_GOTO_ON_ERROR(s_do_dma_transaction(dev_handle->bus_handle, trans_desc), err, TAG, "dma transaction failed"); + ESP_GOTO_ON_ERROR(do_async_transaction(dev_handle, trans_desc), err, TAG, "Prepare async transaction failed"); } else { - ESP_GOTO_ON_ERROR(i3c_master_prepare_fifo_transaction(dev_handle, trans_desc), err, TAG, "Prepare fifo transaction failed"); + ESP_GOTO_ON_ERROR(dev_handle->bus_handle->transaction_handler(dev_handle->bus_handle, trans_desc), err, TAG, "transaction handler failed"); } if (dev_handle->bus_handle->async_transaction == false) { @@ -788,9 +795,11 @@ static esp_err_t i3c_master_prepare_transaction(i3c_master_i2c_device_handle_t d ESP_GOTO_ON_FALSE(NULL, ESP_ERR_TIMEOUT, err, TAG, "event queue wait timeout. Please check whether stretch happened on i3c bus"); } else { if (event == I3C_MASTER_EVENT_NACK) { - ESP_GOTO_ON_FALSE(NULL, ESP_ERR_INVALID_STATE, err, TAG, "i3c-i2c nack detected"); + ESP_LOGD(TAG, "%s(%d): i3c-i2c nack detected", __FUNCTION__, __LINE__); + ret = ESP_ERR_INVALID_STATE; + goto err; } - if (event == I3C_MASTER_EVENT_DONE) { + if (event == I3C_MASTER_EVENT_TRANS_DONE) { if (dev_handle->bus_handle->use_dma_transaction) { if (trans_desc->read_buffer) { size_t dma_rcv_size = gdma_link_count_buffer_size_till_eof(dev_handle->bus_handle->rx_dma_link, 0); @@ -803,9 +812,11 @@ static esp_err_t i3c_master_prepare_transaction(i3c_master_i2c_device_handle_t d } +#if CONFIG_PM_ENABLE if (dev_handle->bus_handle->pm_lock) { ESP_GOTO_ON_ERROR(esp_pm_lock_release(dev_handle->bus_handle->pm_lock), err, TAG, "release pm_lock failed"); } +#endif xSemaphoreGive(dev_handle->bus_handle->bus_lock_mux); return ret; @@ -823,12 +834,14 @@ esp_err_t i3c_master_i2c_device_transmit(i3c_master_i2c_device_handle_t dev_hand ESP_RETURN_ON_FALSE((((uintptr_t)write_buffer & (I3C_MASTER_DMA_INTERFACE_ALIGNMENT - 1)) == 0), ESP_ERR_INVALID_ARG, TAG, "when dma transaction, write buffer address must be %d bytes aligned", I3C_MASTER_DMA_INTERFACE_ALIGNMENT); } - i3c_master_address_table_t addr_table[1] = { - {.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}}, + i3c_master_ll_i2c_speed_mode_t i2c_work_mode = (dev_handle->scl_freq_hz > 400 * 1000) ? I3C_MASTER_LL_I2C_FAST_MODE_PLUS : I3C_MASTER_LL_I2C_FAST_MODE; + + i3c_master_ll_device_address_descriptor_t addr_table[1] = { + {.i2c_static = {.static_addr = dev_handle->address, .dnrc = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}}, }; - i3c_master_command_table_t command[1] = { - {.cmd_l.regular = {.cmd_attr = I3C_MASTER_REGULAR_COMMAND, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = 0x0, .rnw = I3C_MASTER_WRITE_TRANSFER, .roc = (dev_handle->bypass_ack_check ? 0x0 : 0x1), .toc = 0x1}, .cmd_h.regular = {.dl = write_size}}, + i3c_master_ll_command_descriptor_t command[1] = { + {.cmd_l.regular = {.cmd_attr = I3C_MASTER_LL_COMMAND_REGULAR, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = i2c_work_mode, .rnw = I3C_MASTER_LL_TRANSFER_DIR_WRITE, .roc = 0x1, .toc = 0x1}, .cmd_h.regular = {.dl = write_size}}, }; i3c_i2c_transaction_desc_t trans_desc = { @@ -838,7 +851,8 @@ esp_err_t i3c_master_i2c_device_transmit(i3c_master_i2c_device_handle_t dev_hand .command_table_num = 1, .write_buffer = (uint8_t *)write_buffer, .read_buffer = NULL, - .scl_speed_hz = dev_handle->scl_speed_hz, + .scl_freq_hz = dev_handle->scl_freq_hz, + .dev_handle = dev_handle, }; return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms); @@ -853,12 +867,14 @@ esp_err_t i3c_master_i2c_device_receive(i3c_master_i2c_device_handle_t dev_handl ESP_RETURN_ON_FALSE((((uintptr_t)read_buffer & (dev_handle->bus_handle->cache_line_size - 1)) == 0), ESP_ERR_INVALID_ARG, TAG, "when dma transaction, read buffer address must be %d bytes aligned", dev_handle->bus_handle->cache_line_size); } - i3c_master_address_table_t addr_table[1] = { - {.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}}, + i3c_master_ll_i2c_speed_mode_t i2c_work_mode = (dev_handle->scl_freq_hz > 400 * 1000) ? I3C_MASTER_LL_I2C_FAST_MODE_PLUS : I3C_MASTER_LL_I2C_FAST_MODE; + + i3c_master_ll_device_address_descriptor_t addr_table[1] = { + {.i2c_static = {.static_addr = dev_handle->address, .dnrc = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}}, }; - i3c_master_command_table_t command[1] = { - {.cmd_l.regular = {.cmd_attr = I3C_MASTER_REGULAR_COMMAND, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = 0x0, .rnw = I3C_MASTER_READ_TRANSFER, .roc = (dev_handle->bypass_ack_check ? 0x0 : 0x1), .toc = 0x1}, .cmd_h.regular = {.dl = read_size}}, + i3c_master_ll_command_descriptor_t command[1] = { + {.cmd_l.regular = {.cmd_attr = I3C_MASTER_LL_COMMAND_REGULAR, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = i2c_work_mode, .rnw = I3C_MASTER_LL_TRANSFER_DIR_READ, .roc = 0x1, .toc = 0x1}, .cmd_h.regular = {.dl = read_size}}, }; i3c_i2c_transaction_desc_t trans_desc = { @@ -868,7 +884,8 @@ esp_err_t i3c_master_i2c_device_receive(i3c_master_i2c_device_handle_t dev_handl .command_table_num = 1, .write_buffer = NULL, .read_buffer = read_buffer, - .scl_speed_hz = dev_handle->scl_speed_hz, + .scl_freq_hz = dev_handle->scl_freq_hz, + .dev_handle = dev_handle, }; return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms); @@ -885,13 +902,15 @@ esp_err_t i3c_master_i2c_device_transmit_receive(i3c_master_i2c_device_handle_t ESP_RETURN_ON_FALSE((((uintptr_t)write_buffer & (I3C_MASTER_DMA_INTERFACE_ALIGNMENT - 1)) == 0), ESP_ERR_INVALID_ARG, TAG, "when dma transaction, write buffer address must be %d bytes aligned", I3C_MASTER_DMA_INTERFACE_ALIGNMENT); } - i3c_master_address_table_t addr_table[1] = { - {.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}}, + i3c_master_ll_i2c_speed_mode_t i2c_work_mode = (dev_handle->scl_freq_hz > 400 * 1000) ? I3C_MASTER_LL_I2C_FAST_MODE_PLUS : I3C_MASTER_LL_I2C_FAST_MODE; + + i3c_master_ll_device_address_descriptor_t addr_table[1] = { + {.i2c_static = {.static_addr = dev_handle->address, .dnrc = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}}, }; - i3c_master_command_table_t command[2] = { - {.cmd_l.regular = {.cmd_attr = I3C_MASTER_REGULAR_COMMAND, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = 0x0, .rnw = I3C_MASTER_WRITE_TRANSFER, .roc = (dev_handle->bypass_ack_check ? 0x0 : 0x1), .toc = 0x0}, .cmd_h.regular = {.dl = write_size}}, - {.cmd_l.regular = {.cmd_attr = I3C_MASTER_REGULAR_COMMAND, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = 0x0, .rnw = I3C_MASTER_READ_TRANSFER, .roc = (dev_handle->bypass_ack_check ? 0x0 : 0x1), .toc = 0x1}, .cmd_h.regular = {.dl = read_size}}, + i3c_master_ll_command_descriptor_t command[2] = { + {.cmd_l.regular = {.cmd_attr = I3C_MASTER_LL_COMMAND_REGULAR, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = i2c_work_mode, .rnw = I3C_MASTER_LL_TRANSFER_DIR_WRITE, .roc = 0x1, .toc = 0x0}, .cmd_h.regular = {.dl = write_size}}, + {.cmd_l.regular = {.cmd_attr = I3C_MASTER_LL_COMMAND_REGULAR, .tid = 0x1, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = i2c_work_mode, .rnw = I3C_MASTER_LL_TRANSFER_DIR_READ, .roc = 0x1, .toc = 0x1}, .cmd_h.regular = {.dl = read_size}}, }; i3c_i2c_transaction_desc_t trans_desc = { @@ -901,7 +920,8 @@ esp_err_t i3c_master_i2c_device_transmit_receive(i3c_master_i2c_device_handle_t .command_table_num = 2, .write_buffer = (uint8_t *)write_buffer, .read_buffer = read_buffer, - .scl_speed_hz = dev_handle->scl_speed_hz, + .scl_freq_hz = dev_handle->scl_freq_hz, + .dev_handle = dev_handle, }; return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms); @@ -948,28 +968,10 @@ esp_err_t i3c_master_bus_wait_all_done(i3c_master_bus_handle_t bus_handle, int t return ESP_OK; } -esp_err_t i3c_master_toggle_transfer_async_mode(i3c_master_bus_handle_t bus_handle, bool async_mode) +#if CONFIG_I3C_MASTER_ENABLE_DEBUG_LOG +__attribute__((constructor)) +static void i3c_override_default_log_level(void) { - ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(bus_handle->async_memory_allocated, ESP_ERR_INVALID_ARG, TAG, "The async memory is not initialized, which must be initialized first"); - - // Check current mode - bool cur_mode_async = bus_handle->async_transaction; - if (cur_mode_async == async_mode) { - ESP_LOGW(TAG, "The current mode is as same as the mode you want, nothing changed"); - return ESP_OK; - } - - if (async_mode == false) { - // If this API is used to switch mode from async to sync, must check if there is any remaining check. - if (bus_handle->num_trans_inflight != 0) { - ESP_LOGE(TAG, "There is still some remaining transaction in queue, switch mode failed"); - return ESP_ERR_INVALID_STATE; - } - - } - - bus_handle->async_transaction = async_mode; - - return ESP_OK; + esp_log_level_set(TAG, ESP_LOG_VERBOSE); } +#endif diff --git a/components/esp_driver_i3c/i3c_master_private.h b/components/esp_driver_i3c/i3c_master_private.h index 48bd30c734..8db2b7a981 100644 --- a/components/esp_driver_i3c/i3c_master_private.h +++ b/components/esp_driver_i3c/i3c_master_private.h @@ -8,14 +8,28 @@ #include #include +// SOC specific headers should be included early +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if CONFIG_I3C_MASTER_ENABLE_DEBUG_LOG +// The local log level must be defined before including esp_log.h +// Set the maximum log level for i3c master driver +#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE +#endif +#include "esp_log.h" +// FreeRTOS headers must be included in order: FreeRTOS.h first, then others +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" #include "hal/i3c_master_types.h" #include "hal/i3c_master_hal.h" #include "esp_dma_utils.h" #include "esp_private/gdma.h" #include "esp_private/gdma_link.h" #include "esp_private/periph_ctrl.h" +#include "esp_intr_types.h" +#include "driver/i3c_master_types.h" #include "esp_pm.h" -#include "sdkconfig.h" #ifdef __cplusplus extern "C" { @@ -23,8 +37,6 @@ extern "C" { #define I3C_MASTER_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED -#define I3C_MASTER_PM_LOCK_NAME_LEN_MAX 16 - // interface between i3c and dma need 4 bytes aligned. #define I3C_MASTER_DMA_INTERFACE_ALIGNMENT (4) @@ -69,25 +81,18 @@ typedef struct i3c_master_i2c_dev_t i3c_master_i2c_dev_t; */ typedef int i3c_master_bus_num_t; -/** - * @brief Structure representing a list of I2C devices managed by the I3C master. - */ -typedef struct i3c_i2c_master_device_list { - i3c_master_i2c_dev_t *i2c_dev; /**< Pointer to the I2C device structure. */ - SLIST_ENTRY(i3c_i2c_master_device_list) next; /**< Pointer to the next device in the single-linked list. */ -} i3c_i2c_master_device_list_t; - /** * @brief Structure representing a transaction descriptor for I3C/I2C operations. */ typedef struct { - i3c_master_address_table_t *addr_table; /**< Pointer to the address table, mapping device addresses. */ + i3c_master_ll_device_address_descriptor_t *addr_table; /**< Pointer to the address table, mapping device addresses. */ size_t addr_table_num; /**< Number of entries in the address table. */ - i3c_master_command_table_t *command_table; /**< Pointer to the command table for the transaction. */ + i3c_master_ll_command_descriptor_t *command_table; /**< Pointer to the command table for the transaction. */ size_t command_table_num; /**< Number of commands in the command table. */ uint8_t *write_buffer; /**< Pointer to the data buffer for writing. */ uint8_t *read_buffer; /**< Pointer to the data buffer for reading. */ - uint32_t scl_speed_hz; /**< Speed of the SCL clock in Hz for the transaction. */ + uint32_t scl_freq_hz; /**< Speed of the SCL clock in Hz for the transaction. */ + i3c_master_i2c_device_handle_t dev_handle; /**< Direct storage of device handle. */ } i3c_i2c_transaction_desc_t; /** @@ -104,12 +109,16 @@ enum { * @brief Enumeration representing the states of the I3C finite state machine (FSM). */ typedef enum { - I3C_FSM_ENABLE_WAIT, /**< FSM is waiting to enable the I3C system. */ + I3C_FSM_WAIT, /**< FSM is waiting for the I3C system to be ready. */ I3C_FSM_ENABLE, /**< FSM is enabling the I3C system. */ - I3C_FSM_RUN_WAIT, /**< FSM is waiting to transition to the running state. */ I3C_FSM_RUN, /**< FSM is in the running state, actively handling I3C operations. */ } i3c_fsm_t; +/** + * @brief Function pointer type for transaction handler + */ +typedef esp_err_t (*i3c_transaction_handler_t)(i3c_master_bus_t *bus_handle, i3c_i2c_transaction_desc_t *trans_desc); + /** * @brief Structure representing the I3C master bus. * @@ -122,16 +131,17 @@ struct i3c_master_bus_t { i3c_master_hal_context_t hal; /**< HAL layer context for each port (bus). */ i3c_master_clock_source_t clock_source; /**< Source of the I3C clock. */ uint32_t clock_source_freq; /**< Frequency of the clock source in Hz. */ + bool dma_initialized; /**< Flag indicating whether DMA has been initialized via decorator. */ bool use_dma_transaction; /**< Flag indicating whether DMA is used for transactions. */ bool async_transaction; /**< Flag indicating whether asynchronous transactions are enabled. */ QueueHandle_t event_queue; /**< Queue for handling I3C events. */ - SLIST_HEAD(i3c_i2c_master_device_list_head, i3c_i2c_master_device_list) device_list; /**< List of devices on the bus. */ + SLIST_HEAD(i3c_i2c_device_list_head, i3c_master_i2c_dev_t) device_list; /**< List of I2C devices on the bus. */ i3c_i2c_transaction_desc_t *cur_trans; /**< Pointer to the current transaction descriptor. */ i3c_i2c_transaction_desc_t *trans_desc_pool; /**< Pool of pre-allocated transaction descriptors. */ uint32_t ops_prepare_idx; /**< Index for preparing operations. */ bool async_memory_allocated; /**< The async transaction is allocated or not */ - i3c_master_address_table_t (*i3c_async_addr_table)[SOC_I3C_MASTER_ADDRESS_TABLE_NUM]; /**< Address table for asynchronous transactions. */ - i3c_master_command_table_t (*i3c_async_command_table)[SOC_I3C_MASTER_COMMAND_TABLE_NUM]; /**< Command table for asynchronous transactions. */ + i3c_master_ll_device_address_descriptor_t (*i3c_async_addr_table)[SOC_I3C_MASTER_ADDRESS_TABLE_NUM]; /**< Address table for asynchronous transactions. */ + i3c_master_ll_command_descriptor_t (*i3c_async_command_table)[SOC_I3C_MASTER_COMMAND_TABLE_NUM]; /**< Command table for asynchronous transactions. */ QueueHandle_t trans_queues[I3C_TRANS_QUEUE_MAX]; /**< Array of transaction queues for different states. */ intr_handle_t intr_handle; /**< Interrupt handle for I3C interrupts. */ _Atomic i3c_fsm_t fsm; /**< Current state of the I3C finite state machine. */ @@ -139,11 +149,10 @@ struct i3c_master_bus_t { size_t queue_depth; /**< Depth of the transaction queue. */ SemaphoreHandle_t bus_lock_mux; /**< Semaphore for bus locking. */ portMUX_TYPE spinlock; /**< Spinlock for protecting critical sections. */ +#ifdef CONFIG_PM_ENABLE esp_pm_lock_handle_t pm_lock; /**< Power management lock handle. */ - size_t cache_line_size; /**< Cache line size for doing C2M operation */ -#if CONFIG_PM_ENABLE - char pm_lock_name[I3C_MASTER_PM_LOCK_NAME_LEN_MAX]; /**< Name of the power management lock (if enabled). */ #endif + size_t cache_line_size; /**< Cache line size for doing C2M operation */ uint8_t *write_fifo_buffer_pointer; /**< Pointer to the write FIFO buffer. */ size_t write_buffer_left_size; /**< Remaining size of the write buffer. */ uint8_t *read_fifo_buffer_pointer; /**< Pointer to the read FIFO buffer. */ @@ -152,6 +161,7 @@ struct i3c_master_bus_t { gdma_channel_handle_t dma_rx_chan; /**< DMA channel handle for RX. */ gdma_link_list_handle_t tx_dma_link; /**< Linked list for TX DMA. */ gdma_link_list_handle_t rx_dma_link; /**< Linked list for RX DMA. */ + i3c_transaction_handler_t transaction_handler; /**< Function pointer for transaction handling (FIFO or DMA) */ }; /** @@ -163,10 +173,10 @@ struct i3c_master_bus_t { struct i3c_master_i2c_dev_t { i3c_master_bus_t *bus_handle; /**< Handle to the I3C master bus managing this device. */ uint8_t address; /**< 7-bit I2C address of the device. */ - uint32_t scl_speed_hz; /**< I2C clock speed for this device, in Hz. */ - bool bypass_ack_check; /**< Flag indicating whether to bypass ACK checking during communication. */ + uint32_t scl_freq_hz; /**< I2C clock speed for this device, in Hz. */ i3c_master_i2c_callback_t on_trans_done; /**< Callback function invoked upon transaction completion. */ void *user_ctx; /**< User-defined context passed to the callback function. */ + SLIST_ENTRY(i3c_master_i2c_dev_t) next; /**< Pointer to the next device in the single-linked list. */ }; #ifdef __cplusplus diff --git a/components/esp_driver_i3c/include/driver/i3c_master.h b/components/esp_driver_i3c/include/driver/i3c_master.h index e6049b0b50..580603f433 100644 --- a/components/esp_driver_i3c/include/driver/i3c_master.h +++ b/components/esp_driver_i3c/include/driver/i3c_master.h @@ -8,7 +8,6 @@ #include #include "esp_err.h" -#include "hal/gpio_types.h" #include "driver/i3c_master_types.h" #ifdef __cplusplus @@ -22,21 +21,26 @@ extern "C" { * including GPIO pins, clock source, queue depth, interrupt priority, and other options. */ typedef struct { - gpio_num_t sda_io_num; /*!< GPIO number of I3C SDA signal, pulled-up internally */ - gpio_num_t scl_io_num; /*!< GPIO number of I3C SCL signal, pulled-up internally */ + gpio_num_t sda_io_num; /*!< GPIO number of I3C SDA signal*/ + gpio_num_t scl_io_num; /*!< GPIO number of I3C SCL signal*/ i3c_master_clock_source_t clock_source; /*!< Clock source of I3C master bus */ 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)*/ - size_t max_transfer_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */ - size_t dma_burst_size; /*!< DMA burst size, in bytes */ int intr_priority; /*!< I3C interrupt priority, if set to 0, driver will select the default priority (1,2,3). */ 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 enable_internal_opendrain : 1; /*!< Pull-Push mode on I3C SCL/SDA pins by default. Set true if open-drain mode is needed */ - uint32_t use_dma : 1; /*!< Use dma transaction. Set to 1 to enable the use of DMA for handling data transfers, which can improve performance for large transactions. */ - uint32_t enable_async_trans : 1; /*!< Enable asynchronous transactions, allowing the master to perform other tasks while a transaction is in progress. Only works when `use_dma` is set as true. */ + uint32_t enable_async_trans : 1; /*!< Enable asynchronous transactions, allowing the master to perform other tasks while a transaction is in progress. Only works when DMA is enabled via i3c_master_bus_decorate_dma(). */ } flags; /*!< I3C master config flags */ } i3c_master_bus_config_t; +/** + * @brief Configuration structure for I3C master bus DMA decorator + * + * This structure defines the DMA configuration parameters for the I3C master bus. + */ +typedef struct { + size_t max_transfer_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes */ + size_t dma_burst_size; /*!< DMA burst size, in bytes. If 0, driver will use default value (16 bytes) */ +} i3c_master_dma_config_t; + /** * @brief Create a new I3C master bus. * @@ -68,6 +72,23 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast */ esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle); +/** + * @brief Enable DMA for I3C master bus (decorator pattern) + * + * This function enables DMA functionality for an existing I3C master bus. It follows the decorator + * pattern to avoid linking DMA code when not needed, thus reducing binary size. + * + * @param[in] bus_handle Handle to the I3C master bus to be decorated with DMA capability. + * @param[in] dma_config Pointer to the DMA configuration structure. If NULL, DMA will be disabled and fifo will be used. + * + * @return + * - ESP_OK: DMA decorator applied successfully. + * - ESP_ERR_INVALID_ARG: Invalid bus handle or configuration. + * - ESP_ERR_INVALID_STATE: Bus is already decorated with DMA or bus is in use. + * - ESP_ERR_NO_MEM: Memory allocation failed for DMA resources. + */ +esp_err_t i3c_master_bus_decorate_dma(i3c_master_bus_handle_t bus_handle, const i3c_master_dma_config_t *dma_config); + /** * @brief Wait for all pending I3C transactions done * @@ -80,26 +101,6 @@ esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle); */ esp_err_t i3c_master_bus_wait_all_done(i3c_master_bus_handle_t bus_handle, int timeout_ms); -/** - * @brief Toggle between synchronous and asynchronous transfer modes for the I3C master bus. - * - * This function allows switching the I3C master bus between synchronous and asynchronous - * transfer modes. It ensures that the necessary preconditions are met before making the switch: - * - If switching to asynchronous mode, it requires that the async memory has been initialized. - * - If switching to synchronous mode, it verifies that no pending transactions are in the queue. - * - * @param[in] bus_handle Handle to the I3C master bus. - * @param[in] async_mode Boolean value indicating the target mode: - * - `true` for asynchronous mode. - * - `false` for synchronous mode. - * - * @return - * - ESP_OK: The mode was successfully toggled. - * - ESP_ERR_INVALID_ARG: Invalid input arguments. - * - ESP_ERR_INVALID_STATE: Cannot switch to synchronous mode due to pending transactions. - */ -esp_err_t i3c_master_toggle_transfer_async_mode(i3c_master_bus_handle_t bus_handle, bool async_mode); - #ifdef __cplusplus } #endif diff --git a/components/esp_driver_i3c/include/driver/i3c_master_i2c.h b/components/esp_driver_i3c/include/driver/i3c_master_i2c.h index d6f3254b21..c522ad093e 100644 --- a/components/esp_driver_i3c/include/driver/i3c_master_i2c.h +++ b/components/esp_driver_i3c/include/driver/i3c_master_i2c.h @@ -8,7 +8,6 @@ #include #include "esp_err.h" -#include "hal/gpio_types.h" #include "driver/i3c_master_types.h" #ifdef __cplusplus @@ -26,10 +25,7 @@ extern "C" { */ typedef struct { uint8_t device_address; /*!< Device address. Must be 7-bit in I3C-I2C mode */ - uint32_t scl_speed_hz; /*!< I2C Device SCL line frequency. */ - struct { - uint32_t disable_ack_check : 1; /*!< Disable ACK check. By default this is 0, this means ack check is enabled, the transaction will be stopped and API returns error when nack is detected. */ - } flags; /*!< I2C Devices config flags */ + uint32_t scl_freq_hz; /*!< I2C Device SCL line frequency. */ /*!< I2C Devices config flags */ } i3c_device_i2c_config_t; /** @@ -82,7 +78,7 @@ esp_err_t i3c_master_bus_rm_i2c_device(i3c_master_i2c_device_handle_t dev_handle * @param[in] dev_handle Handle to the I2C device. * @param[in] write_buffer Pointer to the buffer containing data to transmit. * @param[in] write_size Size of the data in bytes. - * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. + * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. Note: -1 means wait forever. * * @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode. * In this mode, the function returns immediately after being called, even if the transmission @@ -104,7 +100,7 @@ esp_err_t i3c_master_i2c_device_transmit(i3c_master_i2c_device_handle_t dev_hand * @param[in] dev_handle Handle to the I2C device. * @param[out] read_buffer Pointer to the buffer where received data will be stored. * @param[in] read_size Number of bytes to read. - * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. + * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. Note: -1 means wait forever. * * @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode. * In this mode, the function returns immediately after being called, even if the transmission @@ -128,7 +124,7 @@ esp_err_t i3c_master_i2c_device_receive(i3c_master_i2c_device_handle_t dev_handl * @param[in] write_size Size of the data in bytes to transmit. * @param[out] read_buffer Pointer to the buffer where received data will be stored. * @param[in] read_size Number of bytes to read. - * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. + * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. Note: -1 means wait forever. * * @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode. * In this mode, the function returns immediately after being called, even if the transmission diff --git a/components/esp_driver_i3c/include/driver/i3c_master_types.h b/components/esp_driver_i3c/include/driver/i3c_master_types.h index f648ab9aa7..866d63e6e5 100644 --- a/components/esp_driver_i3c/include/driver/i3c_master_types.h +++ b/components/esp_driver_i3c/include/driver/i3c_master_types.h @@ -11,8 +11,7 @@ #include #include #include "hal/i3c_master_types.h" -#include "soc/soc_caps.h" -#include "sdkconfig.h" +#include "hal/gpio_types.h" #ifdef __cplusplus extern "C" { @@ -32,8 +31,8 @@ typedef struct i3c_master_i2c_dev_t *i3c_master_i2c_device_handle_t; * @brief Enumeration for I3C event. */ typedef enum { - I3C_MASTER_EVENT_DONE, /*!< I3C bus transaction done */ - I3C_MASTER_EVENT_NACK, /*!< I3C bus nack */ + I3C_MASTER_EVENT_TRANS_DONE, /*!< I3C bus transaction done */ + I3C_MASTER_EVENT_NACK, /*!< I3C bus nack */ } i3c_master_event_t; /** @@ -42,8 +41,8 @@ typedef enum { typedef struct { i3c_master_event_t event; /*!< The event type that occurred (e.g., transfer complete, error). */ uint8_t *data; /*!< Pointer to the data buffer for the event (e.g., received data). */ - size_t recv_size; /*!< The size of the data received, in bytes. */ -} i3c_i2c_master_event_data_t; + size_t data_size; /*!< The size of the data received, in bytes. */ +} i3c_master_i2c_device_event_data_t; /** * @brief Type definition for a callback function used in I3C/I2C master operations. @@ -56,7 +55,7 @@ typedef struct { * * @return Whether a high priority task has been waken up by this function. */ -typedef bool (*i3c_master_i2c_callback_t)(i3c_master_i2c_device_handle_t i2c_dev, const i3c_i2c_master_event_data_t *evt_data, void *arg); +typedef bool (*i3c_master_i2c_callback_t)(i3c_master_i2c_device_handle_t i2c_dev, const i3c_master_i2c_device_event_data_t *evt_data, void *arg); #ifdef __cplusplus } diff --git a/components/esp_driver_i3c/linker.lf b/components/esp_driver_i3c/linker.lf index bb2d177ffc..236b269d46 100644 --- a/components/esp_driver_i3c/linker.lf +++ b/components/esp_driver_i3c/linker.lf @@ -1,18 +1,18 @@ [mapping:i3c_driver] archive: libesp_driver_i3c.a entries: - if I3C_MASTER_ISR_CACHE_SAFE = y: + if I3C_MASTER_ISR_HANDLER_IN_IRAM = y: i3c_master: i3c_master_isr_handler_default (noflash) - i3c_master: s_do_dma_transaction (noflash) - i3c_master: handle_tx_data_buf_thld_int (noflash) + i3c_master: do_dma_transaction_handler (noflash) + i3c_master: do_fifo_transaction_handler (noflash) + i3c_master: handle_tx_data_buf_threshold_int (noflash) i3c_master: handle_transfer_complete_int (noflash) - i3c_master: handle_rx_data_buf_thld_int (noflash) + i3c_master: handle_rx_data_buf_threshold_int (noflash) [mapping:i3c_driver_gdma] archive: libesp_hw_support.a entries: - if I3C_MASTER_ISR_CACHE_SAFE = y: + if I3C_MASTER_ISR_HANDLER_IN_IRAM = y: gdma_link: gdma_link_mount_buffers (noflash) gdma_link: gdma_link_get_head_addr (noflash) - gdma: gdma_start (noflash) gdma_link: gdma_link_count_buffer_size_till_eof (noflash) diff --git a/components/esp_driver_i3c/test_apps/i3c_test_apps/CMakeLists.txt b/components/esp_driver_i3c/test_apps/i3c_test_apps/CMakeLists.txt index 59686c048a..84dcba2e53 100644 --- a/components/esp_driver_i3c/test_apps/i3c_test_apps/CMakeLists.txt +++ b/components/esp_driver_i3c/test_apps/i3c_test_apps/CMakeLists.txt @@ -11,17 +11,18 @@ set(EXTRA_COMPONENT_DIRS include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(i3c_master_test) +idf_build_get_property(elf EXECUTABLE) if(CONFIG_COMPILER_DUMP_RTL_FILES) add_custom_target(check_test_app_sections ALL - COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py - --rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_i3c/,${CMAKE_BINARY_DIR}/esp-idf/hal/ - --elf-file ${CMAKE_BINARY_DIR}/i3c_master_test.elf - find-refs - --from-sections=.iram0.text - --to-sections=.flash.text,.flash.rodata - --exit-code - DEPENDS ${elf} - ) + COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py + --rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_i3c/,${CMAKE_BINARY_DIR}/esp-idf/hal/ + --elf-file ${CMAKE_BINARY_DIR}/i3c_master_test.elf + find-refs + --from-sections=.iram0.text + --to-sections=.flash.text,.flash.rodata + --exit-code + DEPENDS ${elf} + ) endif() message(STATUS "Checking i3c registers are not read-write by half-word") diff --git a/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_app_main.c b/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_app_main.c index e9eaad7ba2..b9f22e5153 100644 --- a/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_app_main.c +++ b/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_app_main.c @@ -10,7 +10,7 @@ #include "esp_heap_caps.h" #include "sdkconfig.h" -#define LEAKS (700) +#define LEAKS (100) void setUp(void) { @@ -24,5 +24,18 @@ void tearDown(void) void app_main(void) { + + // ,--.,----. ,-----. ,--------.,------. ,---. ,--------. + // | |'.-. |' .--./ '--. .--'| .---'' .-''--. .--' + // | | .' < | | | | | `--, `. `-. | | + // | |/'-' |' '--'\ | | | `---..-' | | | + // `--'`----' `-----' `--' `------'`-----' `--' + + printf(",--.,----. ,-----. ,--------.,------. ,---. ,--------. \n"); + printf("| |'.-. |' .--./ '--. .--'| .---'' .-''--. .--' \n"); + printf("| | .' < | | | | | `--, `. `-. | | \n"); + printf("| |/'-' |' '--'\\ | | | `---..-' | | | \n"); + printf("`--'`----' `-----' `--' `------'`-----' `--' \n"); + unity_run_menu(); } diff --git a/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_i3c_master_common.c b/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_i3c_master_common.c index 944d7f9a07..fe2c02e03c 100644 --- a/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_i3c_master_common.c +++ b/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_i3c_master_common.c @@ -23,11 +23,6 @@ TEST_CASE("I3C bus install-uninstall test", "[i3c]") .clock_source = I3C_MASTER_CLK_SRC_DEFAULT, .scl_io_num = 5, .sda_io_num = 6, - .flags.enable_internal_opendrain = true, - .flags.enable_internal_pullup = true, - .flags.use_dma = true, - .flags.enable_async_trans = true, - .max_transfer_size = 1024, .trans_queue_depth = 30, }; i3c_master_bus_handle_t bus_handle; @@ -50,11 +45,6 @@ TEST_CASE("I3C driver memory leaking check", "[i3c]") .clock_source = I3C_MASTER_CLK_SRC_DEFAULT, .scl_io_num = 5, .sda_io_num = 6, - .flags.enable_internal_opendrain = true, - .flags.enable_internal_pullup = true, - .flags.use_dma = true, - .flags.enable_async_trans = true, - .max_transfer_size = 1024, .trans_queue_depth = 30, }; i3c_master_bus_handle_t bus_handle; @@ -75,11 +65,7 @@ TEST_CASE("I3C device add & remove check", "[i3c]") .clock_source = I3C_MASTER_CLK_SRC_DEFAULT, .scl_io_num = 5, .sda_io_num = 6, - .flags.enable_internal_opendrain = true, - .flags.enable_internal_pullup = true, - .flags.use_dma = true, .flags.enable_async_trans = true, - .max_transfer_size = 1024, .trans_queue_depth = 30, }; i3c_master_bus_handle_t bus_handle; @@ -87,30 +73,30 @@ TEST_CASE("I3C device add & remove check", "[i3c]") TEST_ESP_OK(i3c_new_master_bus(&i2c_mst_config_1, &bus_handle)); i3c_device_i2c_config_t dev_cfg_1 = { - .scl_speed_hz = 100 * 1000, + .scl_freq_hz = 100 * 1000, .device_address = 0x10, }; i3c_master_i2c_device_handle_t dev_1; - i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_1, &dev_1); + TEST_ESP_OK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_1, &dev_1)); i3c_device_i2c_config_t dev_cfg_2 = { - .scl_speed_hz = 100 * 1000, + .scl_freq_hz = 100 * 1000, .device_address = 0x20, }; i3c_master_i2c_device_handle_t dev_2; - i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_2, &dev_2); + TEST_ESP_OK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_2, &dev_2)); i3c_device_i2c_config_t dev_cfg_3 = { - .scl_speed_hz = 100 * 1000, + .scl_freq_hz = 100 * 1000, .device_address = 0x30, }; i3c_master_i2c_device_handle_t dev_3; - i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_3, &dev_3); + TEST_ESP_OK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_3, &dev_3)); - i3c_master_bus_rm_i2c_device(dev_1); - i3c_master_bus_rm_i2c_device(dev_2); + TEST_ESP_OK(i3c_master_bus_rm_i2c_device(dev_1)); + TEST_ESP_OK(i3c_master_bus_rm_i2c_device(dev_2)); TEST_ESP_ERR(ESP_ERR_INVALID_STATE, i3c_del_master_bus(bus_handle)); - i3c_master_bus_rm_i2c_device(dev_3); + TEST_ESP_OK(i3c_master_bus_rm_i2c_device(dev_3)); TEST_ESP_OK(i3c_del_master_bus(bus_handle)); } diff --git a/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.ci.cache_safe b/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.ci.cache_safe index b0cdad1f8f..79655bfb87 100644 --- a/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.ci.cache_safe +++ b/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.ci.cache_safe @@ -1,7 +1,7 @@ -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_I3C_MASTER_ISR_CACHE_SAFE=y +CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_HAL_ASSERTION_SILENT=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y diff --git a/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.ci.release b/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.ci.release index 91d93f163e..199b0cf97c 100644 --- a/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.ci.release +++ b/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.ci.release @@ -1,5 +1,6 @@ CONFIG_PM_ENABLE=y CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_PM_DFS_INIT_AUTO=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.defaults b/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.defaults index 8e326e32e1..cc6385c164 100644 --- a/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.defaults +++ b/components/esp_driver_i3c/test_apps/i3c_test_apps/sdkconfig.defaults @@ -1,2 +1,3 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_ESP_TASK_WDT_INIT=n +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/components/hal/esp32p4/include/hal/i3c_master_ll.h b/components/hal/esp32p4/include/hal/i3c_master_ll.h index 0bd1d678d2..4e2383be3c 100644 --- a/components/hal/esp32p4/include/hal/i3c_master_ll.h +++ b/components/hal/esp32p4/include/hal/i3c_master_ll.h @@ -31,7 +31,6 @@ extern "C" { #define I3C_LL_MASTER_TRANSMIT_EVENT_INTR (I3C_MST_TX_DATA_BUF_THLD_INT_ENA_M | I3C_MST_TRANSFER_COMPLETE_INT_ENA_M | I3C_MST_COMMAND_DONE_INT_ENA_M) #define I3C_LL_MASTER_RECEIVE_EVENT_INTR (I3C_MST_RX_DATA_BUF_THLD_INT_ENA_M | I3C_MST_TRANSFER_COMPLETE_INT_ENA_M | I3C_MST_COMMAND_DONE_INT_ENA_M) #define I3C_MASTER_LL_DEFAULT_SETUP_TIME (600) -<<<<<<< HEAD /** * @brief I3C master command types @@ -221,6 +220,17 @@ typedef enum { I3C_MASTER_LL_MODE_I2C = 1, ///< I3C works under I2C mode } i3c_master_ll_mode_t; +/** + * @brief I2C under I3C master speed mode + * + * This enumeration defines the speed modes for an I3C master. + * It can either operate in the I3C protocol mode or be backward-compatible with I2C devices. + */ +typedef enum { + I3C_MASTER_LL_I2C_FAST_MODE = 0, ///< I3C works under I2C fast mode + I3C_MASTER_LL_I2C_FAST_MODE_PLUS = 1, ///< I3C works under I2C fast mode plus +} i3c_master_ll_i2c_speed_mode_t; + /** * @brief I3C Device address descriptor * @@ -279,8 +289,6 @@ typedef union { */ uint32_t val; ///< Raw 32-bit value of the address table entry. } i3c_master_ll_device_address_descriptor_t; -======= ->>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral) typedef enum { I3C_MASTER_LL_FIFO_WM_LENGTH_2 = 0x0, @@ -290,29 +298,6 @@ typedef enum { I3C_MASTER_LL_FIFO_WM_LENGTH_31 = 0x4, } i3c_master_ll_fifo_wm_t; -/** - * @brief I3C Master Error State Enumeration - */ -typedef enum { - I3C_MASTER_LL_NO_ERROR = 0, ///< Indicates that the I3C master encountered no errors. - I3C_MASTER_LL_READ_LENGTH_MISMATCH = 3, ///< The length of data read from the slave device does not match the expected or requested length. - I3C_MASTER_LL_BROADCAST_ADDRESS_NACK_ERROR = 4, ///< Broadcast Address NACK Error. - I3C_MASTER_LL_ADDRESS_NACK_OR_DYNAMIC_ADDRESS_NACK = 5, ///< Address NACK or Dynamic Address NACK. - I3C_MASTER_LL_BUFFER_RX_OVERFLOW_TX_UNDERFLOW = 6, ///< Buffer RX Overflow or TX Underflow. - I3C_MASTER_LL_I2C_SLAVE_WRITE_DATA_NACK_ERROR = 9, ///< I2C Slave Write Data NACK Error. -} i3c_master_ll_error_state_enum_t; - -/** - * @brief I3C master operating mode - * - * This enumeration defines the operating modes for an I3C master. - * It can either operate in the I3C protocol mode or be backward-compatible with I2C devices. - */ -typedef enum { - I3C_MASTER_LL_MODE_I3C = 0, ///< I3C works under I3C mode - I3C_MASTER_LL_MODE_I2C = 1, ///< I3C works under I2C mode -} i3c_master_ll_mode_t; - /** * @brief Set the clock source for the I3C master * @@ -322,8 +307,19 @@ typedef enum { static inline void i3c_master_ll_set_source_clk(i3c_mst_dev_t *hw, i3c_master_clock_source_t src_clk) { (void)hw; // Suppress unused parameter warning - // src_clk : (1) for PLL_F160M, (0) for XTAL - HP_SYS_CLKRST.peri_clk_ctrl119.reg_i3c_mst_clk_src_sel = (src_clk == I3C_MASTER_CLK_SRC_PLL_F160M) ? 1 : 0; + switch (src_clk) { + case I3C_MASTER_CLK_SRC_XTAL: + HP_SYS_CLKRST.peri_clk_ctrl119.reg_i3c_mst_clk_src_sel = 0; + break; + case I3C_MASTER_CLK_SRC_PLL_F160M: + HP_SYS_CLKRST.peri_clk_ctrl119.reg_i3c_mst_clk_src_sel = 1; + break; + case I3C_MASTER_CLK_SRC_PLL_F120M: + HP_SYS_CLKRST.peri_clk_ctrl119.reg_i3c_mst_clk_src_sel = 2; + break; + default: + HAL_ASSERT(false); + } } /// use a macro to wrap the function, force the caller to use it in a critical section @@ -338,11 +334,7 @@ static inline void i3c_master_ll_set_source_clk(i3c_mst_dev_t *hw, i3c_master_cl * @param device_number Number of devices */ __attribute__((always_inline)) -<<<<<<< HEAD static inline void i3c_master_ll_set_device_address_table(i3c_mst_dev_t *hw, i3c_master_ll_device_address_descriptor_t *addr_table, size_t device_number) -======= -static inline void i3c_master_ll_set_address_device_table(i3c_mst_dev_t *hw, i3c_master_address_table_t *addr_table, size_t device_number) ->>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral) { for (int i = 0; i < device_number; i++) { I3C_MST_MEM.dev_addr_table[i].val = addr_table[i].val; @@ -405,11 +397,7 @@ static inline void i3c_master_ll_reset_register(i3c_mst_dev_t *hw) * @param command_num Number of commands */ __attribute__((always_inline)) -<<<<<<< HEAD static inline void i3c_master_ll_set_command(i3c_mst_dev_t *hw, i3c_master_ll_command_descriptor_t *command_buf, size_t command_num) -======= -static inline void i3c_master_ll_set_command(i3c_mst_dev_t *hw, i3c_master_command_table_t *command_buf, size_t command_num) ->>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral) { for (int i = 0; i < command_num; i++) { I3C_MST_MEM.command_buf_port.reg_command = command_buf[i].cmd_l.val; @@ -472,13 +460,14 @@ static inline void i3c_master_ll_set_i2c_fast_mode_timing(i3c_mst_dev_t *hw, uin } /** - * @brief Set the fast+ mode timing for I2C master + * @brief Set the fast mode+ timing for I2C master + * * @param hw I3C master hardware instance * @param clock_source_freq Clock source frequency * @param scl_freq SCL frequency */ __attribute__((always_inline)) -static inline void i3c_master_ll_set_i2c_fast_plus_mode_timing(i3c_mst_dev_t *hw, uint32_t clock_source_freq, uint32_t scl_freq) +static inline void i3c_master_ll_set_i2c_fast_mode_plus_timing(i3c_mst_dev_t *hw, uint32_t clock_source_freq, uint32_t scl_freq) { uint32_t period_cnt = clock_source_freq / scl_freq / 2; HAL_FORCE_MODIFY_U32_REG_FIELD(hw->scl_i2c_fmp_time, reg_i2c_fmp_high_period, (period_cnt - 1)); @@ -742,11 +731,7 @@ static inline void i3c_master_ll_enable_rx_by_dma(i3c_mst_dev_t *dev, bool enabl * @return Response data */ __attribute__((always_inline)) -<<<<<<< HEAD static inline i3c_master_ll_response_descriptor_t i3c_master_ll_get_response_data(i3c_mst_dev_t *dev) -======= -static inline uint32_t i3c_master_ll_get_response_buffer_value(i3c_mst_dev_t *dev) ->>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral) { return (i3c_master_ll_response_descriptor_t)(I3C_MST_MEM.response_buf_port.val); } diff --git a/components/soc/esp32p4/i3c_master_periph.c b/components/soc/esp32p4/i3c_master_periph.c index 8ce351b59f..e06e9f38ac 100644 --- a/components/soc/esp32p4/i3c_master_periph.c +++ b/components/soc/esp32p4/i3c_master_periph.c @@ -13,6 +13,7 @@ */ const i3c_master_signal_conn_t i3c_master_periph_signal[SOC_I3C_MASTER_PERIPH_NUM] = { { + .module_name = "I3C_MASTER", .sda_out_sig = I3C_MST_SDA_PAD_OUT_IDX, .sda_in_sig = I3C_MST_SDA_PAD_IN_IDX, .scl_out_sig = I3C_MST_SCL_PAD_OUT_IDX, diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index af6be8588c..0d1ad891d1 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -2139,6 +2139,14 @@ config SOC_I3C_MASTER_PERIPH_NUM bool default y +config SOC_I3C_MASTER_ADDRESS_TABLE_NUM + int + default 12 + +config SOC_I3C_MASTER_COMMAND_TABLE_NUM + int + default 12 + config SOC_LP_CORE_SUPPORT_ETM bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 4f9a7dc984..d789b1f1be 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -801,6 +801,8 @@ /*--------------------------- I3C ---------------------------------*/ #define SOC_I3C_MASTER_PERIPH_NUM (1) +#define SOC_I3C_MASTER_ADDRESS_TABLE_NUM (12) +#define SOC_I3C_MASTER_COMMAND_TABLE_NUM (12) /*------------------------------------- ULP CAPS -------------------------------------*/ #define SOC_LP_CORE_SUPPORT_ETM (1) /*!< LP Core supports ETM */ diff --git a/components/soc/include/soc/i3c_master_periph.h b/components/soc/include/soc/i3c_master_periph.h index 7c3ab04d01..bd317f1676 100644 --- a/components/soc/include/soc/i3c_master_periph.h +++ b/components/soc/include/soc/i3c_master_periph.h @@ -16,6 +16,7 @@ extern "C" { #if SOC_I3C_MASTER_SUPPORTED typedef struct { + const char *module_name; // peripheral name const uint8_t sda_out_sig; const uint8_t sda_in_sig; const uint8_t scl_out_sig; diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 8ff60c3444..24872b7488 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -178,6 +178,12 @@ examples/peripherals/i2s/i2s_recorder: - esp_driver_spi - esp_driver_i2s +examples/peripherals/i3c/i3c_i2c_basic: + disable: + - if: SOC_I3C_MASTER_SUPPORTED != 1 + depends_components: + - esp_driver_i3c + examples/peripherals/isp/multi_pipelines: disable: - if: SOC_MIPI_CSI_SUPPORTED != 1 diff --git a/examples/peripherals/i3c/i3c_i2c_basic/CMakeLists.txt b/examples/peripherals/i3c/i3c_i2c_basic/CMakeLists.txt new file mode 100644 index 0000000000..406b1589e7 --- /dev/null +++ b/examples/peripherals/i3c/i3c_i2c_basic/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(i3c_i2c_basic) diff --git a/examples/peripherals/i3c/i3c_i2c_basic/README.md b/examples/peripherals/i3c/i3c_i2c_basic/README.md new file mode 100644 index 0000000000..632de6ab13 --- /dev/null +++ b/examples/peripherals/i3c/i3c_i2c_basic/README.md @@ -0,0 +1,53 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | + +# Basic I3C_I2C Master Example + +This code demonstrates how to make the I3C controller work in the legacy I2C mode and read/write an I2C sensor on the bus. + +## Overview + +This example demonstrates basic usage of I3C driver by reading and writing from a I2C connected sensor. Please note that according to I3C protocol, I2C sensor connects to I3C bus should only supports 7-bits mode, and no clock stretch is allowed. For more restrictions, please refer to I3C protocol. + +## How to use example + +### Hardware Required + +To run this example, you should have an Espressif development board based on a chip listed in supported targets as well as a ICM42688. The ICM-42688 is a high-performance, low-power, 6-axis inertial measurement unit (IMU). It integrates a 3-axis gyroscope and a 3-axis accelerometer in a compact package, making it suitable for a wide range of motion-tracking applications. + +#### Pin Assignment + +**Note:** The following pin assignments are used by default, you can change these in the `menuconfig` . + +| | SDA | SCL | +| ---------------- | -------------- | -------------- | +| ESP I3C Master | I3C_MASTER_SDA | I3C_MASTER_SCL | +| ICM42688 Sensor | SDA | SCL | + +For the actual default value of `I3C_MASTER_SDA` and `I3C_MASTER_SCL` see `Example Configuration` in `menuconfig`. + +### Build and Flash + +Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +```bash +I (23676) example: temp is 28.804348, accel (x,y,z) is (-1.033691, -1.349121, 1.077637)g, gyro (x,y,z) is (-11.718750, 8.789062, 1.647949)dps +I (23776) example: temp is 28.442028, accel (x,y,z) is (-1.248047, -1.398438, 1.038574)g, gyro (x,y,z) is (-29.418945, 20.324707, -0.610352)dps +I (23876) example: temp is 28.442028, accel (x,y,z) is (-1.100586, -1.314453, 1.043457)g, gyro (x,y,z) is (-1.953125, 12.512207, -2.746582)dps +I (23976) example: temp is 28.623188, accel (x,y,z) is (-1.093750, -1.402832, 0.952148)g, gyro (x,y,z) is (-9.521484, 40.344238, -12.939453)dps +I (24076) example: temp is 28.623188, accel (x,y,z) is (-1.229004, -1.395508, 0.868652)g, gyro (x,y,z) is (-3.173828, 44.921875, -15.380859)dps +I (24176) example: temp is 28.623188, accel (x,y,z) is (-1.069824, -1.367676, 0.959473)g, gyro (x,y,z) is (-10.253906, 12.268066, 0.366211)dps +I (24276) example: temp is 28.562801, accel (x,y,z) is (-1.075195, -1.382324, 0.959961)g, gyro (x,y,z) is (-7.080078, 6.591797, -0.305176)dps +I (24376) example: temp is 28.442028, accel (x,y,z) is (-1.081543, -1.389648, 0.953125)g, gyro (x,y,z) is (-6.958008, 4.943848, 0.610352)dps +I (24476) example: temp is 28.502415, accel (x,y,z) is (-1.090820, -1.385742, 0.945801)g, gyro (x,y,z) is (-6.591797, 4.150391, 0.000000)dps +``` + +## Troubleshooting + +(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.) diff --git a/examples/peripherals/i3c/i3c_i2c_basic/main/CMakeLists.txt b/examples/peripherals/i3c/i3c_i2c_basic/main/CMakeLists.txt new file mode 100644 index 0000000000..87604a7477 --- /dev/null +++ b/examples/peripherals/i3c/i3c_i2c_basic/main/CMakeLists.txt @@ -0,0 +1,5 @@ +set(srcs "i3c_i2c_basic_main.c") + +idf_component_register(SRCS ${srcs} + PRIV_REQUIRES esp_driver_i3c + INCLUDE_DIRS ".") diff --git a/examples/peripherals/i3c/i3c_i2c_basic/main/Kconfig.projbuild b/examples/peripherals/i3c/i3c_i2c_basic/main/Kconfig.projbuild new file mode 100644 index 0000000000..7330170628 --- /dev/null +++ b/examples/peripherals/i3c/i3c_i2c_basic/main/Kconfig.projbuild @@ -0,0 +1,18 @@ +menu "Example Configuration" + + menu "I3C Master" + config I3C_MASTER_SCL + int "SCL GPIO Num" + default 4 + help + GPIO number for I3C Master clock line. + + config I3C_MASTER_SDA + int "SDA GPIO Num" + default 5 + help + GPIO number for I3C Master data line. + + endmenu + +endmenu diff --git a/examples/peripherals/i3c/i3c_i2c_basic/main/i3c_i2c_basic_main.c b/examples/peripherals/i3c/i3c_i2c_basic/main/i3c_i2c_basic_main.c new file mode 100644 index 0000000000..f020c6197c --- /dev/null +++ b/examples/peripherals/i3c/i3c_i2c_basic/main/i3c_i2c_basic_main.c @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/i3c_master.h" +#include "driver/i3c_master_i2c.h" + +#define ICM_42688_ADDRESS 0x69 +#define ICM_42688_WHO_AM_I_REG 0x75 +#define ICM_42688_WHO_AM_I_VAL 0x47 + +#define ICM_42688_REG_PWR_MGMT0 0x4E +#define ICM_42688_REG_ACCEL_CONFIG0 0x50 +#define ICM_42688_REG_GYRO_CONFIG0 0x4F +#define ICM_42688_REG_TEMP_DATA1 0x1D +#define ICM_42688_REG_ACCEL_DATA_X1 0x1F +#define ICM_42688_REG_GYRO_DATA_X1 0x25 + +#define ICM_42688_ACCEL_RANGE (16.0f) +#define ICM_42688_GYRO_RANGE (2000.0f) +#define ADC_MAX (32768.0f) + +#define I3C_I2C_TIMEOUT_MS (5000) + +typedef struct { + float accel_x; + float accel_y; + float accel_z; + float gyro_x; + float gyro_y; + float gyro_z; + float temp; +} icm42688_data_t; + +static const char *TAG = "example"; + +void icm42688_init(i3c_master_i2c_device_handle_t icm42688_handle, i3c_master_bus_handle_t bus_handle) +{ + uint8_t hw_id; + uint8_t write_hw_id = ICM_42688_WHO_AM_I_REG; + ESP_ERROR_CHECK(i3c_master_i2c_device_transmit_receive(icm42688_handle, &write_hw_id, 1, &hw_id, 1, -1)); + + if (hw_id != ICM_42688_WHO_AM_I_VAL) { + ESP_LOGE(TAG, "Invalid Hardware ID: 0x%02X", hw_id); + return; + } + + uint8_t init_cmd[2] = {ICM_42688_REG_PWR_MGMT0, 0x0f}; // 0x0f stands for enable GYRO_MODE and ACCEL_MODE + ESP_ERROR_CHECK(i3c_master_i2c_device_transmit(icm42688_handle, init_cmd, 2, I3C_I2C_TIMEOUT_MS)); + + uint8_t accel_cfg[2] = {ICM_42688_REG_ACCEL_CONFIG0, 0x23}; // 0x23 stands for range +-8g with 8khz + ESP_ERROR_CHECK(i3c_master_i2c_device_transmit(icm42688_handle, accel_cfg, 2, I3C_I2C_TIMEOUT_MS)); + + uint8_t gyro_cfg[2] = {ICM_42688_REG_GYRO_CONFIG0, 0x23}; // 0x23 stands for range +-1000dps with 8khz + ESP_ERROR_CHECK(i3c_master_i2c_device_transmit(icm42688_handle, gyro_cfg, 2, I3C_I2C_TIMEOUT_MS)); +} + +void icm42688_read_data(i3c_master_i2c_device_handle_t icm42688_handle, icm42688_data_t *data) +{ + uint8_t raw_data[14]; + uint8_t temp_data = ICM_42688_REG_TEMP_DATA1; + ESP_ERROR_CHECK(i3c_master_i2c_device_transmit_receive(icm42688_handle, &temp_data, 1, raw_data, sizeof(raw_data), I3C_I2C_TIMEOUT_MS)); + + int16_t temp_raw = (raw_data[0] << 8) | raw_data[1]; + // Provided by datasheet + data->temp = (temp_raw / 132.48f) + 25.0f; + + int16_t accel_x = (raw_data[2] << 8) | raw_data[3]; + int16_t accel_y = (raw_data[4] << 8) | raw_data[5]; + int16_t accel_z = (raw_data[6] << 8) | raw_data[7]; + data->accel_x = accel_x * (ICM_42688_ACCEL_RANGE / ADC_MAX); + data->accel_y = accel_y * (ICM_42688_ACCEL_RANGE / ADC_MAX); + data->accel_z = accel_z * (ICM_42688_ACCEL_RANGE / ADC_MAX); + + int16_t gyro_x = (raw_data[8] << 8) | raw_data[9]; + int16_t gyro_y = (raw_data[10] << 8) | raw_data[11]; + int16_t gyro_z = (raw_data[12] << 8) | raw_data[13]; + data->gyro_x = gyro_x * (ICM_42688_GYRO_RANGE / ADC_MAX); + data->gyro_y = gyro_y * (ICM_42688_GYRO_RANGE / ADC_MAX); + data->gyro_z = gyro_z * (ICM_42688_GYRO_RANGE / ADC_MAX); +} + +void app_main(void) +{ + i3c_master_bus_handle_t bus_handle = NULL; + i3c_master_i2c_device_handle_t icm42688_handle = NULL; + + i3c_master_bus_config_t i3c_bus_config = { + .clock_source = I3C_MASTER_CLK_SRC_DEFAULT, + .scl_io_num = CONFIG_I3C_MASTER_SCL, + .sda_io_num = CONFIG_I3C_MASTER_SDA, + }; + + ESP_ERROR_CHECK(i3c_new_master_bus(&i3c_bus_config, &bus_handle)); + + i3c_device_i2c_config_t dev_cfg = { + .device_address = ICM_42688_ADDRESS, + .scl_freq_hz = 100000, + }; + + ESP_ERROR_CHECK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg, &icm42688_handle)); + + icm42688_init(icm42688_handle, bus_handle); + + icm42688_data_t *data = heap_caps_malloc(sizeof(icm42688_data_t), MALLOC_CAP_DEFAULT); + assert(data); + + while (1) { + icm42688_read_data(icm42688_handle, data); + ESP_LOGI(TAG, "temp is %f, accel (x,y,z) is (%f, %f, %f)g, gyro (x,y,z) is (%f, %f, %f)dps", data->temp, data->accel_x, data->accel_y, data->accel_z, data->gyro_x, data->gyro_y, data->gyro_z); + vTaskDelay(pdMS_TO_TICKS(100)); + } +} diff --git a/examples/peripherals/i3c/i3c_i2c_basic/sdkconfig.defaults b/examples/peripherals/i3c/i3c_i2c_basic/sdkconfig.defaults new file mode 100644 index 0000000000..782ff67bc1 --- /dev/null +++ b/examples/peripherals/i3c/i3c_i2c_basic/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/tools/idf_py_actions/hints.yml b/tools/idf_py_actions/hints.yml index c795d3b37d..6a4e0cb8c6 100644 --- a/tools/idf_py_actions/hints.yml +++ b/tools/idf_py_actions/hints.yml @@ -522,3 +522,7 @@ - re_variables: ['driver/sigmadelta.h'] hint_variables: ['legacy Sigma-Delta', 'driver/sdm.h', 'esp_driver_sdm'] + +- + re: undefined reference to `i3c_new_master_bus' + hint: "The I3C master driver is not fully supported in IDF. To use this driver please enable `IDF_EXPERIMENTAL_FEATURES`"