From d994c399cbcee3b68929027e2312aec088cb49a2 Mon Sep 17 00:00:00 2001 From: fimohame Date: Tue, 30 Sep 2025 23:29:41 +0530 Subject: [PATCH 1/4] simplicity_sdk: Patch for sl_i2c.c file Updating the SL_I2C driver to incorporate design enhancements from the GSDK I2C implementation Upstream-Status: Pending Signed-off-by: S Mohamed Fiaz --- .../platform/driver/i2c/src/sl_i2c.c | 2683 +++++++++-------- 1 file changed, 1411 insertions(+), 1272 deletions(-) diff --git a/simplicity_sdk/platform/driver/i2c/src/sl_i2c.c b/simplicity_sdk/platform/driver/i2c/src/sl_i2c.c index 732c8d91..b27615f6 100644 --- a/simplicity_sdk/platform/driver/i2c/src/sl_i2c.c +++ b/simplicity_sdk/platform/driver/i2c/src/sl_i2c.c @@ -29,32 +29,27 @@ ******************************************************************************/ #include - -#include "sl_assert.h" +#include "sl_sleeptimer.h" +#include "sl_clock_manager.h" #include "sl_udelay.h" -#include "sl_status.h" #include "sl_interrupt_manager.h" -#include "sl_clock_manager.h" -#include "sl_sleeptimer.h" -#include "sl_hal_gpio.h" #include "sl_hal_i2c.h" +#include "sl_hal_gpio.h" #include "sl_i2c.h" #include "sli_i2c.h" /******************************************************************************* - ******************************* DEFINES *********************************** - ******************************************************************************/ - +******************************* DEFINES *********************************** +*******************************************************************************/ /** - * @brief I2C transfer timeout in milliseconds. + * @brief Number of clock cycles for bus recovery. * * @note - * Set to 30000 milliseconds to allow enough time for I2C operations to complete, - * especially for slower devices or larger data transfers. - * This value ensures the transfer doesn't hang indefinitely - * and allows the system to handle transfer failures. + * Set to 10 clock cycles to ensure the bus can be cleared effectively, + * allowing for proper recovery. + * This helps resume communication quickly without leaving the bus in a stalled state. */ -#define I2C_TRANSFER_TIMEOUT 30000 +#define I2C_RECOVER_NUM_CLOCKS 10 /** * @brief SCL hold time in microseconds. @@ -69,100 +64,116 @@ #define I2C_SCL_HOLD_TIME_US 100 #endif -/** - * @brief Number of clock cycles for bus recovery. - * - * @note - * Set to 10 clock cycles to ensure the bus can be cleared effectively, - * allowing for proper recovery. - * This helps resume communication quickly without leaving the bus in a stalled state. - */ -#define I2C_RECOVER_NUM_CLOCKS 10 - -// Validation macro for I2C freq mode. -#define I2C_FREQ_MODE_IS_VALID(freq_mode) ((freq_mode == SL_I2C_FREQ_STANDARD_MODE) \ - || (freq_mode == SL_I2C_FREQ_FAST_MODE) \ - || (freq_mode == SL_I2C_FREQ_FASTPLUS_MODE)) +/******************************************************************************* +***************************** LOCAL VARIABLES ***************************** +*******************************************************************************/ +/// Active transfer contexts for each I2C instance. +static sl_i2c_handle_t *i2c_handle_contexts[I2C_COUNT] = { NULL }; + +// Lookup tables for I2C frequency and clock high/low ratio (CLHR). +static const uint32_t i2c_max_freq_table[] = { + [SL_I2C_FREQ_STANDARD_MODE] = SL_HAL_I2C_FREQ_STANDARD_MAX, + [SL_I2C_FREQ_FAST_MODE] = SL_HAL_I2C_FREQ_FAST_MAX, + [SL_I2C_FREQ_FASTPLUS_MODE] = SL_HAL_I2C_FREQ_FASTPLUS_MAX +}; + +static const sl_i2c_clock_hlr_t i2c_clhr_table[] = { + [SL_I2C_FREQ_STANDARD_MODE] = SL_I2C_CLK_HLR_STANDARD, + [SL_I2C_FREQ_FAST_MODE] = SL_I2C_CLK_HLR_ASYMMETRIC, + [SL_I2C_FREQ_FASTPLUS_MODE] = SL_I2C_CLK_HLR_FAST +}; -#define RSTART_WRITE_INPROGRESS 1 -#define RSTART_READ_INPROGRESS 2 +/// Temporary buffer for I2C operations. +static uint8_t temp_buffer[] = { 0xFF }; /******************************************************************************* - ************************** LOCAL FUNCTIONS ******************************** + *************************** LOCAL FUNCTIONS ******************************* ******************************************************************************/ -static sl_status_t i2c_get_peripheral_instance(I2C_TypeDef *i2c_base_addr, sl_peripheral_t* i2c_peripheral); static uint32_t get_current_time_ms(void); -static sl_status_t i2c_leader_mode_blocking_state_machine(sli_i2c_instance_t *sl_i2c_instance, uint32_t timeout); -static sl_status_t i2c_follower_mode_blocking_state_machine(sli_i2c_instance_t *sl_i2c_instance, uint32_t timeout); -static void i2c_common_irq_handler(sli_i2c_instance_t *sl_i2c_instance); +static sl_status_t i2c_setup_blocking_transfer_interrupts(sl_i2c_handle_t *i2c_handle); +static sl_status_t i2c_leader_mode_blocking_state_machine(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len, + uint32_t timeout); +static sl_status_t i2c_follower_mode_blocking_state_machine(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len, + uint32_t timeout); +static sl_status_t i2c_get_dma_trigger_signals(sl_i2c_handle_t *i2c_handle, + DMADRV_PeripheralSignal_t *dma_tx_trigger_source, + DMADRV_PeripheralSignal_t *dma_rx_trigger_source); +static void stop_active_dma_transfers(sl_i2c_handle_t *i2c_handle); +static sl_status_t i2c_setup_leader_non_blocking_dma_transfer(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len); +static sl_status_t i2c_setup_follower_non_blocking_dma_transfer(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len); +static void i2c_common_irq_handler(uint8_t i2c_instance); /******************************************************************************* - ***************************** GLOBAL VARIABLES **************************** - ******************************************************************************/ -/// Array to hold each I2C device instance. -static sli_i2c_instance_t sl_i2c_device_instance[I2C_COUNT]; - -/// Temporary buffer for I2C operations. -static uint8_t temp_buffer[] = { 0xFF }; +************************** GLOBAL FUNCTIONS ******************************* +*******************************************************************************/ /***************************************************************************//** - * This function initializes the I2C Module. + * Initializes the I2C Module. ******************************************************************************/ -sl_status_t sl_i2c_init(const sl_i2c_init_params_t *init_params, - sl_i2c_handle_t *i2c_handle) +sl_status_t sl_i2c_init(sl_i2c_handle_t *i2c_handle, + const sl_i2c_init_params_t *init_params) { CORE_DECLARE_IRQ_STATE; - sli_i2c_instance_t *sl_i2c_instance = NULL; - int8_t i2c_instance_num; - bool is_10bit_addr; - uint8_t clk_pulses; - sl_peripheral_t i2c_peripheral; + I2C_TypeDef *i2c_base_addr; sl_bus_clock_t i2c_bus_clk; + int8_t i2c_instance_num; // Parameter validation - if (init_params == NULL || i2c_handle == NULL) { + if (i2c_handle == NULL || init_params == NULL) { return SL_STATUS_NULL_POINTER; } - // Validate I2C base address - if (!SL_HAL_I2C_REF_VALID(init_params->i2c_base_addr)) { + // Validate I2C peripheral instance + i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(init_params->i2c_peripheral); + i2c_instance_num = I2C_NUM(i2c_base_addr); + if (!SL_HAL_I2C_REF_VALID(i2c_base_addr)) { return SL_STATUS_NOT_SUPPORTED; } // Validate operating mode if (!SL_I2C_OPERATING_MODE_IS_VALID(init_params->operating_mode)) { return SL_STATUS_INVALID_PARAMETER; } - - CORE_ENTER_ATOMIC(); - - // Validate follower address (7-bit or 10-bit or invalid) - if (init_params->follower_address <= 0x7F) { - is_10bit_addr = false; - } else if (init_params->follower_address <= 0x3FF) { - is_10bit_addr = true; - } else { - CORE_EXIT_ATOMIC(); - return SL_STATUS_INVALID_RANGE; + // Validate frequency mode + if (!SL_I2C_FREQ_MODE_IS_VALID(init_params->frequency_mode)) { + return SL_STATUS_INVALID_PARAMETER; } - // Retrieve I2C instance number - i2c_instance_num = I2C_NUM(init_params->i2c_base_addr); + CORE_ENTER_ATOMIC(); // Check if the I2C instance is already initialized - if (sl_i2c_device_instance[i2c_instance_num].i2c_base_addr != NULL) { + if (i2c_handle_contexts[i2c_instance_num] != NULL) { CORE_EXIT_ATOMIC(); return SL_STATUS_ALREADY_INITIALIZED; } + // Enable clocks + i2c_bus_clk = sl_device_peripheral_get_bus_clock(init_params->i2c_peripheral); + sl_clock_manager_enable_bus_clock(i2c_bus_clk); + sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_GPIO); + // GPIO Configuration - sli_i2c_configure_gpio(init_params->i2c_base_addr, - init_params->sda_gpio, - init_params->scl_gpio, - true); + sl_hal_gpio_set_pin_mode(&(init_params->scl_gpio), SL_GPIO_MODE_WIRED_AND_PULLUP, 1); + sl_hal_gpio_set_pin_mode(&(init_params->sda_gpio), SL_GPIO_MODE_WIRED_AND_PULLUP, 1); // Generate 9 clock pulses to recover the follower device from an unknown state. // Refer to the 'Bus Lock-Up' section in the I2C part of the reference manual. if (init_params->operating_mode == SL_I2C_LEADER_MODE) { - for (clk_pulses = 0; clk_pulses < I2C_RECOVER_NUM_CLOCKS; clk_pulses++) { + for (int clk_pulses = 0; clk_pulses < I2C_RECOVER_NUM_CLOCKS; clk_pulses++) { sl_hal_gpio_clear_pin(&(init_params->scl_gpio)); sl_udelay_wait(I2C_SCL_HOLD_TIME_US); sl_hal_gpio_set_pin(&(init_params->scl_gpio)); @@ -170,53 +181,48 @@ sl_status_t sl_i2c_init(const sl_i2c_init_params_t *init_params, } } - // Get the I2C peripheral instance and Enable I2C bus clock - i2c_get_peripheral_instance(init_params->i2c_base_addr, &i2c_peripheral); - i2c_bus_clk = sl_device_peripheral_get_bus_clock(i2c_peripheral); - sl_clock_manager_enable_bus_clock(i2c_bus_clk); - - // Configure I2C instance - sli_i2c_instance_configuration(init_params); - - // Set follower address and address mode (7-bit/10-bit) - if (init_params->operating_mode == SL_I2C_FOLLOWER_MODE) { - sli_i2c_set_follower_address(init_params->i2c_base_addr, - init_params->follower_address, - is_10bit_addr); - } - - // Configure DMA settings + // GPIO Routing + GPIO->I2CROUTE[i2c_instance_num].ROUTEEN = GPIO_I2C_ROUTEEN_SDAPEN | GPIO_I2C_ROUTEEN_SCLPEN; + GPIO->I2CROUTE[i2c_instance_num].SCLROUTE = (uint32_t)((init_params->scl_gpio.pin << _GPIO_I2C_SCLROUTE_PIN_SHIFT) + | (init_params->scl_gpio.port << _GPIO_I2C_SCLROUTE_PORT_SHIFT)); + GPIO->I2CROUTE[i2c_instance_num].SDAROUTE = (uint32_t)((init_params->sda_gpio.pin << _GPIO_I2C_SDAROUTE_PIN_SHIFT) + | (init_params->sda_gpio.port << _GPIO_I2C_SDAROUTE_PORT_SHIFT)); + + // Fill I2C handle structure with initialization parameters + i2c_handle->i2c_peripheral = init_params->i2c_peripheral; + i2c_handle->operating_mode = init_params->operating_mode; + i2c_handle->frequency_mode = init_params->frequency_mode; + i2c_handle->scl_gpio = init_params->scl_gpio; + i2c_handle->sda_gpio = init_params->sda_gpio; + + // DMA Configuration DMADRV_Init(); - - // Populate I2C instance - sl_i2c_instance = &sl_i2c_device_instance[i2c_instance_num]; - sl_i2c_instance->i2c_base_addr = init_params->i2c_base_addr; - sl_i2c_instance->operating_mode = init_params->operating_mode; - sl_i2c_instance->follower_address = (init_params->follower_address << 1); - sl_i2c_instance->is_10bit_addr = is_10bit_addr; - - if ((DMADRV_AllocateChannel(&(sl_i2c_instance->dma_channel.dma_tx_channel), NULL) != ECODE_EMDRV_DMADRV_OK) - || (DMADRV_AllocateChannel(&(sl_i2c_instance->dma_channel.dma_rx_channel), NULL) != ECODE_EMDRV_DMADRV_OK)) { + if (DMADRV_AllocateChannel(&(i2c_handle->dma_channel.dma_tx_channel), NULL) != ECODE_EMDRV_DMADRV_OK) { + CORE_EXIT_ATOMIC(); + return SL_STATUS_ALLOCATION_FAILED; + } + if (DMADRV_AllocateChannel(&(i2c_handle->dma_channel.dma_rx_channel), NULL) != ECODE_EMDRV_DMADRV_OK) { + DMADRV_FreeChannel(i2c_handle->dma_channel.dma_tx_channel); CORE_EXIT_ATOMIC(); return SL_STATUS_ALLOCATION_FAILED; } - // Assign instance to the I2C handle - *i2c_handle = (sl_i2c_handle_t)sl_i2c_instance; + // I2C instance Configuration + sli_i2c_init_core(i2c_handle); CORE_EXIT_ATOMIC(); - return SL_STATUS_OK; } /***************************************************************************//** - * This function Deinitializes and resets the I2C Instance. + * Deinitializes and resets the I2C instance. ******************************************************************************/ -sl_status_t sl_i2c_deinit(sl_i2c_handle_t i2c_handle) +sl_status_t sl_i2c_deinit(sl_i2c_handle_t *i2c_handle) { CORE_DECLARE_IRQ_STATE; - sl_peripheral_t i2c_peripheral; sl_bus_clock_t i2c_bus_clk; + I2C_TypeDef *i2c_base_addr; + int8_t i2c_instance_num; // Validate handle if (i2c_handle == NULL) { @@ -226,209 +232,191 @@ sl_status_t sl_i2c_deinit(sl_i2c_handle_t i2c_handle) CORE_ENTER_ATOMIC(); // Get the I2C instance and base address from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - I2C_TypeDef *i2c_base_addr = sl_i2c_instance->i2c_base_addr; + i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + i2c_instance_num = I2C_NUM(i2c_base_addr); + + // De-Allocate DMA channels + DMADRV_FreeChannel(i2c_handle->dma_channel.dma_tx_channel); + DMADRV_FreeChannel(i2c_handle->dma_channel.dma_rx_channel); - // Get peripheral information - i2c_get_peripheral_instance(i2c_base_addr, &i2c_peripheral); + // Clear follower address if in follower mode + if (i2c_handle->operating_mode == SL_I2C_FOLLOWER_MODE) { + sl_hal_i2c_set_follower_address(i2c_base_addr, 0x00, false); + } // Flush buffers and reset I2C peripheral sl_hal_i2c_flush_buffers(i2c_base_addr); sl_hal_i2c_reset(i2c_base_addr); - sl_hal_i2c_set_follower_address(i2c_base_addr, 0x00, false); - - // Disable the clock for the I2C peripheral - i2c_bus_clk = sl_device_peripheral_get_bus_clock(i2c_peripheral); - sl_clock_manager_disable_bus_clock(i2c_bus_clk); - // Deinitialize DMA channels and driver - DMADRV_FreeChannel(sl_i2c_instance->dma_channel.dma_tx_channel); - DMADRV_FreeChannel(sl_i2c_instance->dma_channel.dma_rx_channel); + // Disable i2c peripheral + sl_hal_i2c_disable(i2c_base_addr); // Reset GPIO configuration - sli_i2c_configure_gpio(i2c_base_addr, - sl_i2c_instance->sda_gpio, - sl_i2c_instance->scl_gpio, - false); + GPIO->I2CROUTE[i2c_instance_num].ROUTEEN = _GPIO_I2C_ROUTEEN_RESETVALUE; + GPIO->I2CROUTE[i2c_instance_num].SCLROUTE = _GPIO_I2C_SCLROUTE_RESETVALUE; + GPIO->I2CROUTE[i2c_instance_num].SDAROUTE = _GPIO_I2C_SDAROUTE_RESETVALUE; - // Clear instance-specific data - memset(sl_i2c_instance, 0, sizeof(sli_i2c_instance_t)); + sl_hal_gpio_set_pin_mode(&(i2c_handle->scl_gpio), SL_GPIO_MODE_DISABLED, 0); + sl_hal_gpio_set_pin_mode(&(i2c_handle->sda_gpio), SL_GPIO_MODE_DISABLED, 0); - // Set the handle to NULL - i2c_handle = NULL; + // Disable the clock for the I2C peripheral + i2c_bus_clk = sl_device_peripheral_get_bus_clock(i2c_handle->i2c_peripheral); + sl_clock_manager_disable_bus_clock(i2c_bus_clk); + + // Clear the initialization flag + i2c_handle_contexts[i2c_instance_num] = NULL; CORE_EXIT_ATOMIC(); return SL_STATUS_OK; } /***************************************************************************//** - * This function sets the frequency of I2C Bus. + * Sets the frequency of the I2C bus. ******************************************************************************/ -sl_status_t sl_i2c_set_frequency(sl_i2c_handle_t i2c_handle, - sl_i2c_freq_mode_t freq_mode) +sl_status_t sl_i2c_set_frequency(sl_i2c_handle_t *i2c_handle, + sl_i2c_freq_mode_t frequency_mode) { CORE_DECLARE_IRQ_STATE; uint32_t freq = 0, max_freq = 0; sl_i2c_clock_hlr_t clhr = 0; - sl_peripheral_t i2c_peripheral; sl_clock_branch_t i2c_clk_branch; - // Validate handle + // Validate handle and frequency mode if (i2c_handle == NULL) { return SL_STATUS_NULL_POINTER; } - // Validate frequency mode - if (!I2C_FREQ_MODE_IS_VALID(freq_mode)) { + if (!SL_I2C_FREQ_MODE_IS_VALID(frequency_mode)) { return SL_STATUS_INVALID_PARAMETER; } CORE_ENTER_ATOMIC(); - // Get the I2C instance from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Get the I2C peripheral - i2c_get_peripheral_instance(sl_i2c_instance->i2c_base_addr, &i2c_peripheral); - - // Retrieve the clock frequency for the I2C peripheral - i2c_clk_branch = sl_device_peripheral_get_clock_branch(i2c_peripheral); + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + i2c_clk_branch = sl_device_peripheral_get_clock_branch(i2c_handle->i2c_peripheral); sl_clock_manager_get_clock_branch_frequency(i2c_clk_branch, &freq); - // Configure max frequency and clock settings based on frequency mode - switch (freq_mode) { - case SL_I2C_FREQ_STANDARD_MODE: - max_freq = SL_HAL_I2C_FREQ_STANDARD_MAX; - clhr = SL_I2C_CLK_HLR_STANDARD; - break; - - case SL_I2C_FREQ_FAST_MODE: - max_freq = SL_HAL_I2C_FREQ_FAST_MAX; - clhr = SL_I2C_CLK_HLR_ASYMMETRIC; - break; - - case SL_I2C_FREQ_FASTPLUS_MODE: - max_freq = SL_HAL_I2C_FREQ_FASTPLUS_MAX; - clhr = SL_I2C_CLK_HLR_FAST; - break; - } + max_freq = i2c_max_freq_table[frequency_mode]; + clhr = i2c_clhr_table[frequency_mode]; - // Set the I2C bus clock frequency - sl_hal_i2c_set_clock_frequency(sl_i2c_instance->i2c_base_addr, freq, max_freq, clhr); + sl_hal_i2c_set_clock_frequency(i2c_base_addr, freq, max_freq, clhr); CORE_EXIT_ATOMIC(); return SL_STATUS_OK; } /***************************************************************************//** - * This function gets the current frequency of I2C module. + * Gets the current configured frequency of the I2C module. ******************************************************************************/ -sl_status_t sl_i2c_get_frequency(sl_i2c_handle_t i2c_handle, - uint32_t *frequency) +sl_status_t sl_i2c_get_frequency(sl_i2c_handle_t *i2c_handle, + sl_i2c_freq_mode_t *frequency_mode) { CORE_DECLARE_IRQ_STATE; - uint32_t clock_frequency; - sl_peripheral_t i2c_peripheral; + uint32_t clock_frequency = 0, freq = 0; sl_clock_branch_t i2c_clk_branch; - // Validate input pointers - if (i2c_handle == NULL || frequency == NULL) { + if (i2c_handle == NULL || frequency_mode == NULL) { return SL_STATUS_NULL_POINTER; } CORE_ENTER_ATOMIC(); - // Get the I2C instance from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Get the I2C peripheral instance - i2c_get_peripheral_instance(sl_i2c_instance->i2c_base_addr, &i2c_peripheral); - - // Retrieve the clock branch frequency for the I2C peripheral - i2c_clk_branch = sl_device_peripheral_get_clock_branch(i2c_peripheral); + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + i2c_clk_branch = sl_device_peripheral_get_clock_branch(i2c_handle->i2c_peripheral); sl_clock_manager_get_clock_branch_frequency(i2c_clk_branch, &clock_frequency); - // Retrieve the current I2C bus frequency - *frequency = sl_hal_i2c_get_clock_frequency(sl_i2c_instance->i2c_base_addr, clock_frequency); + freq = sl_hal_i2c_get_clock_frequency(i2c_base_addr, clock_frequency); + + if (freq <= SL_HAL_I2C_FREQ_STANDARD_MAX) { + *frequency_mode = SL_I2C_FREQ_STANDARD_MODE; + } else if (freq <= SL_HAL_I2C_FREQ_FAST_MAX) { + *frequency_mode = SL_I2C_FREQ_FAST_MODE; + } else if (freq <= SL_HAL_I2C_FREQ_FASTPLUS_MAX) { + *frequency_mode = SL_I2C_FREQ_FASTPLUS_MODE; + } else { + CORE_EXIT_ATOMIC(); + return SL_STATUS_INVALID_CONFIGURATION; + } CORE_EXIT_ATOMIC(); return SL_STATUS_OK; } /***************************************************************************//** - * This API is supported only in Leader mode and it is used to set the follower address. - * It is used in Multi Follower mode (addressing a new follower which has similar - * configurations set during Init API). + * Set the follower device address for I2C communication. ******************************************************************************/ -sl_status_t sl_i2c_set_follower_address(sl_i2c_handle_t i2c_handle, - uint16_t follower_address) +sl_status_t sl_i2c_set_follower_address(sl_i2c_handle_t *i2c_handle, + uint16_t address, + bool is_10bit_addr) { CORE_DECLARE_IRQ_STATE; - // Validate input pointer + // Null pointer validation if (i2c_handle == NULL) { return SL_STATUS_NULL_POINTER; } + // Operating mode validation + if (i2c_handle->operating_mode != SL_I2C_FOLLOWER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Follower address validation + if (address > 0x3FF) { + return SL_STATUS_INVALID_PARAMETER; + } + // Add reserved address validation for 7-bit addresses + if (!is_10bit_addr) { + // Check for reserved 7-bit addresses (0x00-0x07 and 0x78-0x7F) + if ((address <= 0x07) || (address >= 0x78)) { + return SL_STATUS_INVALID_PARAMETER; + } + } CORE_ENTER_ATOMIC(); - // Get the I2C instance from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Validate and determine the address type (7-bit or 10-bit) - if (follower_address <= 0x7F) { - sl_i2c_instance->is_10bit_addr = false; - } else if (follower_address <= 0x3FF) { - sl_i2c_instance->is_10bit_addr = true; - } else { - CORE_EXIT_ATOMIC(); - return SL_STATUS_INVALID_PARAMETER; - } + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); - // Set the follower address, left-aligned for the I2C address frame - sl_i2c_instance->follower_address = (follower_address << 1); + // Set the follower address in the I2C peripheral + i2c_handle->follower_address = address; + sl_hal_i2c_set_follower_address(i2c_base_addr, (address << 1), is_10bit_addr); + sl_hal_i2c_set_follower_mask_address(i2c_base_addr, 0xFE); // 0xFE means exact address match CORE_EXIT_ATOMIC(); + return SL_STATUS_OK; } /***************************************************************************//** - * This is an optional API for configuring the DMA for the I2C instance. + * Configures the DMA channels for the I2C instance. ******************************************************************************/ -sl_status_t sl_i2c_configure_dma(sl_i2c_handle_t i2c_handle, - sl_i2c_dma_channel_info_t dma_channel) +sl_status_t sl_i2c_configure_dma(sl_i2c_handle_t *i2c_handle, + sl_i2c_dma_channel_info_t *dma_channel) { CORE_DECLARE_IRQ_STATE; - // Validate input pointers - if (i2c_handle == NULL) { + if (i2c_handle == NULL || dma_channel == NULL) { return SL_STATUS_NULL_POINTER; } CORE_ENTER_ATOMIC(); - // Get the I2C instance from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Allocate new DMA channels for TX and RX operations and update I2C instance. - if ((sl_i2c_instance->dma_channel.dma_tx_channel != dma_channel.dma_tx_channel) - || (sl_i2c_instance->dma_channel.dma_rx_channel != dma_channel.dma_rx_channel)) { - if (sl_i2c_instance->dma_channel.dma_tx_channel != dma_channel.dma_tx_channel) { - if (DMADRV_AllocateChannel(&(dma_channel.dma_tx_channel), NULL) != ECODE_EMDRV_DMADRV_OK) { - CORE_EXIT_ATOMIC(); - return SL_STATUS_ALLOCATION_FAILED; - } - DMADRV_FreeChannel(sl_i2c_instance->dma_channel.dma_tx_channel); - sl_i2c_instance->dma_channel.dma_tx_channel = dma_channel.dma_tx_channel; + if (i2c_handle->dma_channel.dma_tx_channel != dma_channel->dma_tx_channel) { + if (DMADRV_AllocateChannel(&dma_channel->dma_tx_channel, NULL) != ECODE_EMDRV_DMADRV_OK) { + CORE_EXIT_ATOMIC(); + return SL_STATUS_ALLOCATION_FAILED; } - if (sl_i2c_instance->dma_channel.dma_rx_channel != dma_channel.dma_rx_channel) { - if (DMADRV_AllocateChannel(&(dma_channel.dma_rx_channel), NULL) != ECODE_EMDRV_DMADRV_OK) { - CORE_EXIT_ATOMIC(); - return SL_STATUS_ALLOCATION_FAILED; - } - DMADRV_FreeChannel(sl_i2c_instance->dma_channel.dma_rx_channel); - sl_i2c_instance->dma_channel.dma_rx_channel = dma_channel.dma_rx_channel; + DMADRV_FreeChannel(i2c_handle->dma_channel.dma_tx_channel); + i2c_handle->dma_channel.dma_tx_channel = dma_channel->dma_tx_channel; + } + + if (i2c_handle->dma_channel.dma_rx_channel != dma_channel->dma_rx_channel) { + if (DMADRV_AllocateChannel(&dma_channel->dma_rx_channel, NULL) != ECODE_EMDRV_DMADRV_OK) { + DMADRV_FreeChannel(i2c_handle->dma_channel.dma_tx_channel); + CORE_EXIT_ATOMIC(); + return SL_STATUS_ALLOCATION_FAILED; } + + DMADRV_FreeChannel(i2c_handle->dma_channel.dma_rx_channel); + i2c_handle->dma_channel.dma_rx_channel = dma_channel->dma_rx_channel; } CORE_EXIT_ATOMIC(); @@ -436,14 +424,13 @@ sl_status_t sl_i2c_configure_dma(sl_i2c_handle_t i2c_handle, } /***************************************************************************//** - * Leader Mode : This function is used to send the data to the follower configured during Init API. - * Follower Mode : This function is used to send the data to the addressed I2C Leader. - * Returns on transfer complete or on error. + * Leader Mode: Send data to the specified follower device (blocking). ******************************************************************************/ -sl_status_t sl_i2c_send_blocking(sl_i2c_handle_t i2c_handle, - const uint8_t *tx_buffer, - uint16_t tx_len, - uint32_t timeout) +sl_status_t sl_i2c_leader_send_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint32_t timeout) { CORE_DECLARE_IRQ_STATE; sl_status_t status = SL_STATUS_OK; @@ -452,1395 +439,1547 @@ sl_status_t sl_i2c_send_blocking(sl_i2c_handle_t i2c_handle, if (i2c_handle == NULL || tx_buffer == NULL) { return SL_STATUS_NULL_POINTER; } + // Only allow leader mode for this API + if (i2c_handle->operating_mode != SL_I2C_LEADER_MODE) { + return SL_STATUS_INVALID_MODE; + } // Validate the length of data to send if (tx_len == 0) { return SL_STATUS_INVALID_PARAMETER; } - - CORE_ENTER_ATOMIC(); - - // Get the I2C instance from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Validate operating mode - if (!SL_I2C_OPERATING_MODE_IS_VALID(sl_i2c_instance->operating_mode)) { - CORE_EXIT_ATOMIC(); + // Validate follower address range + if (address > 0x3FF) { return SL_STATUS_INVALID_PARAMETER; } + CORE_ENTER_ATOMIC(); + // Initialize transaction parameters - sl_i2c_instance->tx_buffer = tx_buffer; - sl_i2c_instance->tx_len = tx_len; - sl_i2c_instance->tx_offset = 0; - sl_i2c_instance->transfer_seq = SL_I2C_WRITE; - sl_i2c_instance->transfer_mode = SLI_I2C_BLOCKING_TRANSFER; + i2c_handle->follower_address = address; + i2c_handle->tx_offset = 0; + i2c_handle->transfer_direction = SL_I2C_WRITE; // Initialize the I2C transfer - sli_i2c_transfer_init(sl_i2c_instance->i2c_base_addr, - sl_i2c_instance->operating_mode, - SL_I2C_WRITE); + i2c_setup_blocking_transfer_interrupts(i2c_handle); - if (sl_i2c_instance->operating_mode == SL_I2C_LEADER_MODE) { - sl_i2c_instance->state = SLI_I2C_STATE_SEND_START_AND_ADDR; - status = i2c_leader_mode_blocking_state_machine(sl_i2c_instance, timeout); - } else { // Follower mode - status = i2c_follower_mode_blocking_state_machine(sl_i2c_instance, timeout); - } + status = i2c_leader_mode_blocking_state_machine(i2c_handle, tx_buffer, tx_len, NULL, 0, timeout); CORE_EXIT_ATOMIC(); return status; } /***************************************************************************//** - * Leader Mode : This function is used to receive the data from the follower configured during Init API. - * Follower Mode : This function is used to receive the data from the addressed I2C Leader. - * Returns on transfer complete or on error. + * Leader Mode: Receive data from the specified follower device (blocking). ******************************************************************************/ -sl_status_t sl_i2c_receive_blocking(sl_i2c_handle_t i2c_handle, - uint8_t *rx_buffer, - uint16_t rx_len, - uint32_t timeout) +sl_status_t sl_i2c_leader_receive_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + uint8_t *rx_buffer, + uint32_t rx_len, + uint32_t timeout) { CORE_DECLARE_IRQ_STATE; sl_status_t status = SL_STATUS_OK; - // Null pointer validation + // Validate input parameters if (i2c_handle == NULL || rx_buffer == NULL) { return SL_STATUS_NULL_POINTER; } - // Validate the length of data to receive + // Only allow leader mode for this API + if (i2c_handle->operating_mode != SL_I2C_LEADER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Validate the length of data to send if (rx_len == 0) { return SL_STATUS_INVALID_PARAMETER; } - - CORE_ENTER_ATOMIC(); - - // Get the I2C instance from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Validate operating mode - if (!SL_I2C_OPERATING_MODE_IS_VALID(sl_i2c_instance->operating_mode)) { - CORE_EXIT_ATOMIC(); + // Validate follower address range + if (address > 0x3FF) { return SL_STATUS_INVALID_PARAMETER; } - // Initialize the I2C instance for receiving data - sl_i2c_instance->rx_buffer = rx_buffer; - sl_i2c_instance->rx_len = rx_len; - sl_i2c_instance->rx_offset = 0; - sl_i2c_instance->transfer_seq = SL_I2C_READ; - sl_i2c_instance->transfer_mode = SLI_I2C_BLOCKING_TRANSFER; + CORE_ENTER_ATOMIC(); - // Prepare I2C transfer configuration - sli_i2c_transfer_init(sl_i2c_instance->i2c_base_addr, - sl_i2c_instance->operating_mode, - SL_I2C_READ); + // Initialize transaction parameters + i2c_handle->follower_address = address; + i2c_handle->rx_offset = 0; + i2c_handle->transfer_direction = SL_I2C_READ; - if (sl_i2c_instance->operating_mode == SL_I2C_LEADER_MODE) { - sl_i2c_instance->state = SLI_I2C_STATE_SEND_START_AND_ADDR; - status = i2c_leader_mode_blocking_state_machine(sl_i2c_instance, timeout); - } else { // Follower mode - status = i2c_follower_mode_blocking_state_machine(sl_i2c_instance, timeout); - } + // Initialize the I2C transfer + i2c_setup_blocking_transfer_interrupts(i2c_handle); + + status = i2c_leader_mode_blocking_state_machine(i2c_handle, NULL, 0, rx_buffer, rx_len, timeout); CORE_EXIT_ATOMIC(); return status; } /***************************************************************************//** - * This function is used to send and receive the data from the follower configured during Init API. - * This API is supported only in Leader Mode and for blocking transfer. - * Returns on transfer complete or on error. + * Leader Mode: Send then receive data from the specified follower device (blocking). ******************************************************************************/ -sl_status_t sl_i2c_transfer(sl_i2c_handle_t i2c_handle, - const uint8_t *tx_buffer, - uint16_t tx_len, - uint8_t *rx_buffer, - uint16_t rx_len) +sl_status_t sl_i2c_leader_transfer_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len, + uint32_t timeout) { CORE_DECLARE_IRQ_STATE; sl_status_t status = SL_STATUS_OK; - // Null pointer validation + // Validate input parameters if (i2c_handle == NULL || tx_buffer == NULL || rx_buffer == NULL) { return SL_STATUS_NULL_POINTER; } - // Validate the lengths of Tx and Rx buffers + // Only allow leader mode for this API + if (i2c_handle->operating_mode != SL_I2C_LEADER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Validate the input and length of data to send and receive if (tx_len == 0 || rx_len == 0) { return SL_STATUS_INVALID_PARAMETER; } - - CORE_ENTER_ATOMIC(); - - // Get the I2C instance from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Ensure the operating mode is in Leader mode - if (sl_i2c_instance->operating_mode != SL_I2C_LEADER_MODE) { - CORE_EXIT_ATOMIC(); + // Validate follower address range + if (address > 0x3FF) { return SL_STATUS_INVALID_PARAMETER; } - // Set up I2C instance for the transfer operation - sl_i2c_instance->tx_buffer = tx_buffer; - sl_i2c_instance->tx_len = tx_len; - sl_i2c_instance->tx_offset = 0; - sl_i2c_instance->rx_buffer = rx_buffer; - sl_i2c_instance->rx_len = rx_len; - sl_i2c_instance->rx_offset = 0; - sl_i2c_instance->transfer_seq = SL_I2C_WRITE_READ; - sl_i2c_instance->transfer_mode = SLI_I2C_BLOCKING_TRANSFER; + CORE_ENTER_ATOMIC(); + + // Initialize transaction parameters + i2c_handle->follower_address = address; + i2c_handle->tx_offset = 0; + i2c_handle->rx_offset = 0; + i2c_handle->transfer_direction = SL_I2C_WRITE_READ; - sli_i2c_transfer_init(sl_i2c_instance->i2c_base_addr, - sl_i2c_instance->operating_mode, - SL_I2C_WRITE_READ); + // Initialize the I2C transfer + i2c_setup_blocking_transfer_interrupts(i2c_handle); - sl_i2c_instance->state = SLI_I2C_STATE_SEND_START_AND_ADDR; - status = i2c_leader_mode_blocking_state_machine(sl_i2c_instance, I2C_TRANSFER_TIMEOUT); + status = i2c_leader_mode_blocking_state_machine(i2c_handle, tx_buffer, tx_len, rx_buffer, rx_len, timeout); CORE_EXIT_ATOMIC(); + return status; } /***************************************************************************//** - * Leader Mode : This function uses DMA and Interrupt, to send the data to the follower set up during Init API. - * Follower Mode : This function uses DMA and Interrupt, to send the data to the addressed I2C Leader. - * User will be notified with the callback function. + * Leader Mode: Send data to the specified follower device (non-blocking). ******************************************************************************/ -sl_status_t sl_i2c_send_non_blocking(sl_i2c_handle_t i2c_handle, - const uint8_t *tx_buffer, - uint16_t tx_len, - sl_i2c_irq_callback_t i2c_callback, - void *context) +sl_status_t sl_i2c_leader_send_non_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + const uint8_t *tx_buffer, + uint32_t tx_len, + void *user_data) { CORE_DECLARE_IRQ_STATE; + sl_status_t status = SL_STATUS_OK; - // Null pointer validation + // Validate input parameters if (i2c_handle == NULL || tx_buffer == NULL) { return SL_STATUS_NULL_POINTER; } - // Tx length validation + // Only allow leader mode for this API + if (i2c_handle->operating_mode != SL_I2C_LEADER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Validate the length of data to receive if (tx_len == 0) { return SL_STATUS_INVALID_PARAMETER; } CORE_ENTER_ATOMIC(); - // Get the I2C instance from the handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Validate operating mode - if (!SL_I2C_OPERATING_MODE_IS_VALID(sl_i2c_instance->operating_mode)) { - CORE_EXIT_ATOMIC(); - return SL_STATUS_INVALID_PARAMETER; - } - - // Initialize the I2C instance structure - sl_i2c_instance->tx_buffer = tx_buffer; - sl_i2c_instance->tx_len = tx_len; - sl_i2c_instance->tx_offset = 0; - sl_i2c_instance->transfer_seq = SL_I2C_WRITE; - sl_i2c_instance->transfer_mode = SLI_I2C_NON_BLOCKING_TRANSFER; - sl_i2c_instance->callback = i2c_callback; - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IDLE; - sl_i2c_instance->context = context; - memset(sl_i2c_instance->addr_buffer, 0, sizeof(sl_i2c_instance->addr_buffer)); - - // Set the state based on the operating mode (Leader or Follower) - if (sl_i2c_instance->operating_mode == SL_I2C_LEADER_MODE) { - sl_i2c_instance->state = SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK; - } else { // Follower mode - sl_i2c_instance->state = SLI_I2C_STATE_ADDRESS_MATCH; - } + // Initialize transaction parameters + i2c_handle->follower_address = address; + i2c_handle->transfer_direction = SL_I2C_WRITE; + i2c_handle->event = SL_I2C_EVENT_IDLE; + i2c_handle->user_data = user_data; - sli_i2c_dma_transfer_init(sl_i2c_instance); + status = i2c_setup_leader_non_blocking_dma_transfer(i2c_handle, tx_buffer, tx_len, NULL, 0); CORE_EXIT_ATOMIC(); - return SL_STATUS_OK; + + return status; } /***************************************************************************//** - * Leader Mode : This function uses DMA and Interrupt, to receive the data from the follower set up during Init API. - * Follower Mode : This function uses DMA and Interrupt, to receive the data from the addressed I2C Leader. - * User will be notified with the callback function. + * Leader Mode: Receive data from the specified follower device (non-blocking). ******************************************************************************/ -sl_status_t sl_i2c_receive_non_blocking(sl_i2c_handle_t i2c_handle, - uint8_t *rx_buffer, - uint16_t rx_len, - sl_i2c_irq_callback_t i2c_callback, - void *context) +sl_status_t sl_i2c_leader_receive_non_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + uint8_t *rx_buffer, + uint32_t rx_len, + void *user_data) { CORE_DECLARE_IRQ_STATE; + sl_status_t status = SL_STATUS_OK; - // Null pointer validation + // Validate input parameters if (i2c_handle == NULL || rx_buffer == NULL) { return SL_STATUS_NULL_POINTER; } - // Rx length validation + // Only allow leader mode for this API + if (i2c_handle->operating_mode != SL_I2C_LEADER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Validate the length of data to receive if (rx_len == 0) { return SL_STATUS_INVALID_PARAMETER; } CORE_ENTER_ATOMIC(); - // Get the I2C instance from handle - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - - // Validate operating mode - if (!SL_I2C_OPERATING_MODE_IS_VALID(sl_i2c_instance->operating_mode)) { - CORE_EXIT_ATOMIC(); - return SL_STATUS_INVALID_PARAMETER; - } - - // Fill the internal instance structure for the non-blocking receive - sl_i2c_instance->rx_buffer = rx_buffer; - sl_i2c_instance->rx_len = rx_len; - sl_i2c_instance->rx_offset = 0; - sl_i2c_instance->transfer_seq = SL_I2C_READ; - sl_i2c_instance->transfer_mode = SLI_I2C_NON_BLOCKING_TRANSFER; - sl_i2c_instance->callback = i2c_callback; - sl_i2c_instance->context = context; - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IDLE; - memset(sl_i2c_instance->addr_buffer, 0, sizeof(sl_i2c_instance->addr_buffer)); - - // Set the state based on the operating mode (Leader or Follower) - if (sl_i2c_instance->operating_mode == SL_I2C_LEADER_MODE) { - sl_i2c_instance->state = SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK; - } else { // Follower mode - sl_i2c_instance->state = SLI_I2C_STATE_ADDRESS_MATCH; - } + // Initialize transaction parameters + i2c_handle->follower_address = address; + i2c_handle->transfer_direction = SL_I2C_READ; + i2c_handle->event = SL_I2C_EVENT_IDLE; + i2c_handle->user_data = user_data; - sli_i2c_dma_transfer_init(sl_i2c_instance); + status = i2c_setup_leader_non_blocking_dma_transfer(i2c_handle, NULL, 0, rx_buffer, rx_len); CORE_EXIT_ATOMIC(); - return SL_STATUS_OK; -} - -/***************************************************************************//** - * @brief Initiates a non-blocking combined write-read I2C transfer (Leader mode only). - * - * @param i2c_handle I2C handle - * @param tx_buffer Pointer to transmit buffer - * @param tx_len Number of bytes to transmit - * @param rx_buffer Pointer to receive buffer - * @param rx_len Number of bytes to receive - * @param i2c_callback Callback function to notify transfer completion - * @param context User context for callback - * @return sl_status_t - */ -sl_status_t sl_i2c_transfer_non_blocking(sl_i2c_handle_t i2c_handle, - const uint8_t *tx_buffer, - uint16_t tx_len, - uint8_t *rx_buffer, - uint16_t rx_len, - sl_i2c_irq_callback_t i2c_callback, - void *context) -{ - sli_i2c_instance_t *sl_i2c_instance = (sli_i2c_instance_t *)i2c_handle; - sl_status_t status; - - sl_i2c_instance->rstart = RSTART_WRITE_INPROGRESS; - sl_i2c_instance->rx_buffer = rx_buffer; - sl_i2c_instance->rx_len = rx_len; - sl_i2c_instance->rx_offset = 0; - status = sl_i2c_send_non_blocking(i2c_handle, tx_buffer, tx_len, i2c_callback, context); - return status; + return status; } -/******************************************************************************* - ************************** INTERNAL FUNCTIONS ***************************** - ******************************************************************************/ - /***************************************************************************//** - * This function configures the I2C module. + * Leader Mode: Send then receive data from the specified follower device (non-blocking). ******************************************************************************/ -sl_status_t sli_i2c_instance_configuration(const sl_i2c_init_params_t *init_params) +sl_status_t sl_i2c_leader_transfer_non_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len, + void *user_data) { - uint32_t ref_freq, max_freq = 0; - sl_peripheral_t i2c_peripheral; - sl_i2c_clock_hlr_t clhr = 0; - sl_clock_branch_t i2c_clk_branch; + CORE_DECLARE_IRQ_STATE; + sl_status_t status = SL_STATUS_OK; // Null pointer validation - if (init_params == NULL) { + if (i2c_handle == NULL || tx_buffer == NULL || rx_buffer == NULL) { return SL_STATUS_NULL_POINTER; } - - // Get I2C peripheral based on the provided base address - i2c_get_peripheral_instance(init_params->i2c_base_addr, &i2c_peripheral); - - // Retrieve the I2C peripheral clock frequency - i2c_clk_branch = sl_device_peripheral_get_clock_branch(i2c_peripheral); - sl_clock_manager_get_clock_branch_frequency(i2c_clk_branch, &ref_freq); - - // Clear and disable all I2C interrupts to ensure clean state - sl_hal_i2c_clear_interrupts(init_params->i2c_base_addr, _I2C_IF_MASK); - sl_hal_i2c_disable_interrupts(init_params->i2c_base_addr, _I2C_IEN_MASK); - - // Initialize the I2C module with the appropriate operating mode (leader or follower) - sl_hal_i2c_init(init_params->i2c_base_addr, init_params->operating_mode); - - // FIFO configuration, if present, using the specified thresholds for TX and RX -#if defined(SL_CATALOG_I2C_FIFO_PRESENT) - if (init_params->fifo_threshold != NULL) { - sl_hal_i2c_set_tx_fifo_threshold(init_params->i2c_base_addr, init_params->fifo_threshold->tx_threshold_val); - sl_hal_i2c_set_rx_fifo_threshold(init_params->i2c_base_addr, init_params->fifo_threshold->rx_threshold_val); + // Operating mode validation + if (i2c_handle->operating_mode != SL_I2C_LEADER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Tx and Rx length validation + if (tx_len == 0 || rx_len == 0) { + return SL_STATUS_INVALID_PARAMETER; } -#endif - // Configure the I2C frequency based on the operating mode and the frequency mode - if (init_params->operating_mode == SL_I2C_LEADER_MODE) { - switch (init_params->freq_mode) { - case SL_I2C_FREQ_STANDARD_MODE: - max_freq = SL_HAL_I2C_FREQ_STANDARD_MAX; - clhr = SL_I2C_CLK_HLR_STANDARD; - break; - case SL_I2C_FREQ_FAST_MODE: - max_freq = SL_HAL_I2C_FREQ_FAST_MAX; - clhr = SL_I2C_CLK_HLR_ASYMMETRIC; - break; - case SL_I2C_FREQ_FASTPLUS_MODE: - max_freq = SL_HAL_I2C_FREQ_FASTPLUS_MAX; - clhr = SL_I2C_CLK_HLR_FAST; - break; - } + CORE_ENTER_ATOMIC(); - // Set the I2C bus frequency based on the reference clock and the max frequency - sl_hal_i2c_set_clock_frequency(init_params->i2c_base_addr, ref_freq, max_freq, clhr); - } + // Initialize i2c instance structure + i2c_handle->follower_address = address; + i2c_handle->transfer_direction = SL_I2C_WRITE_READ; + i2c_handle->event = SL_I2C_EVENT_IDLE; + i2c_handle->user_data = user_data; - // Enable the I2C peripheral - sl_hal_i2c_enable(init_params->i2c_base_addr); + status = i2c_setup_leader_non_blocking_dma_transfer(i2c_handle, tx_buffer, tx_len, rx_buffer, rx_len); - return SL_STATUS_OK; -} + CORE_EXIT_ATOMIC(); -/***************************************************************************//** - * This function is used for the setting the follower address for follower. - ******************************************************************************/ -void sli_i2c_set_follower_address(I2C_TypeDef *i2c_base_addr, - uint16_t follower_address, - bool is_10bit_addr) -{ - sl_hal_i2c_set_follower_address(i2c_base_addr, (follower_address << 1), is_10bit_addr); - sl_hal_i2c_set_follower_mask_address(i2c_base_addr, 0xFE); // 0xFE means exact address match + return status; } /***************************************************************************//** - * This function configures the GPIO module. + * Follower Mode: Send data to the I2C leader (blocking). ******************************************************************************/ -void sli_i2c_configure_gpio(I2C_TypeDef *i2c_base_addr, - sl_gpio_t sda_gpio, - sl_gpio_t scl_gpio, - bool enable) +sl_status_t sl_i2c_follower_send_blocking(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint32_t timeout) { - // Retrieve I2C instance number based on the base address - int8_t i2c_instance_num = I2C_NUM(i2c_base_addr); - - if (enable) { - sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_GPIO); - - // Configure the SDA and SCL pins as wired AND with pull-up enabled - // Output value must be set to 1 to avoid driving the lines low - sl_hal_gpio_set_pin_mode(&scl_gpio, SL_GPIO_MODE_WIRED_AND_PULLUP, 1); - sl_hal_gpio_set_pin_mode(&sda_gpio, SL_GPIO_MODE_WIRED_AND_PULLUP, 1); - - // Enable I2C routing for SDA and SCL pins - GPIO->I2CROUTE[i2c_instance_num].ROUTEEN = GPIO_I2C_ROUTEEN_SDAPEN | GPIO_I2C_ROUTEEN_SCLPEN; - GPIO->I2CROUTE[i2c_instance_num].SCLROUTE = (uint32_t)((scl_gpio.pin << _GPIO_I2C_SCLROUTE_PIN_SHIFT) - | (scl_gpio.port << _GPIO_I2C_SCLROUTE_PORT_SHIFT)); - GPIO->I2CROUTE[i2c_instance_num].SDAROUTE = (uint32_t)((sda_gpio.pin << _GPIO_I2C_SDAROUTE_PIN_SHIFT) - | (sda_gpio.port << _GPIO_I2C_SDAROUTE_PORT_SHIFT)); - } else { - GPIO->I2CROUTE[i2c_instance_num].ROUTEEN = _GPIO_I2C_ROUTEEN_RESETVALUE; - GPIO->I2CROUTE[i2c_instance_num].SCLROUTE = _GPIO_I2C_SCLROUTE_RESETVALUE; - GPIO->I2CROUTE[i2c_instance_num].SDAROUTE = _GPIO_I2C_SDAROUTE_RESETVALUE; - - sl_hal_gpio_set_pin_mode(&scl_gpio, SL_GPIO_MODE_DISABLED, 0); - sl_hal_gpio_set_pin_mode(&sda_gpio, SL_GPIO_MODE_DISABLED, 0); + CORE_DECLARE_IRQ_STATE; + sl_status_t status = SL_STATUS_OK; - sl_clock_manager_disable_bus_clock(SL_BUS_CLOCK_GPIO); + // Validate input parameters + if (i2c_handle == NULL || tx_buffer == NULL) { + return SL_STATUS_NULL_POINTER; + } + // Only allow follower mode for this API + if (i2c_handle->operating_mode != SL_I2C_FOLLOWER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Validate the input and length of data to send and receive + if (tx_len == 0) { + return SL_STATUS_INVALID_PARAMETER; } -} -/***************************************************************************//** - * This function is used for the configuring I2C instance. - ******************************************************************************/ -void sli_i2c_transfer_init(I2C_TypeDef *i2c_base_addr, - sl_i2c_operating_mode_t operating_mode, - sl_i2c_transfer_seq_t transfer_seq) -{ - sl_hal_i2c_flush_buffers(i2c_base_addr); - sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); - sl_hal_i2c_enable_interrupts(i2c_base_addr, I2C_IEN_ACK | I2C_IEN_NACK | SL_HAL_I2C_IEN_ERRORS); + CORE_ENTER_ATOMIC(); - switch (operating_mode) { - case SL_I2C_LEADER_MODE: - sl_hal_i2c_enable_interrupts(i2c_base_addr, I2C_IEN_MSTOP); - if (transfer_seq != SL_I2C_WRITE) { - sl_hal_i2c_enable_interrupts(i2c_base_addr, I2C_IEN_RXDATAV); - } - break; + // Initialize transaction parameters + i2c_handle->tx_offset = 0; + i2c_handle->transfer_direction = SL_I2C_WRITE; - case SL_I2C_FOLLOWER_MODE: - sl_hal_i2c_enable_interrupts(i2c_base_addr, I2C_IEN_SSTOP | I2C_IEN_RXDATAV | I2C_IEN_ADDR); - break; - } + // Initialize the I2C transfer + i2c_setup_blocking_transfer_interrupts(i2c_handle); + + status = i2c_follower_mode_blocking_state_machine(i2c_handle, tx_buffer, tx_len, NULL, 0, timeout); + + CORE_EXIT_ATOMIC(); + + return status; } /***************************************************************************//** - * This function is used for the initialization of I2C DMA transfer. + * Follower Mode: Receive data from the I2C leader (blocking). ******************************************************************************/ -sl_status_t sli_i2c_dma_transfer_init(sli_i2c_instance_t *i2c_instance) +sl_status_t sl_i2c_follower_receive_blocking(sl_i2c_handle_t *i2c_handle, + uint8_t *rx_buffer, + uint32_t rx_len, + uint32_t timeout) { - // Null pointer validation - if (i2c_instance == NULL) { + CORE_DECLARE_IRQ_STATE; + sl_status_t status = SL_STATUS_OK; + + // Validate input parameters + if (i2c_handle == NULL || rx_buffer == NULL) { return SL_STATUS_NULL_POINTER; } + // Only allow follower mode for this API + if (i2c_handle->operating_mode != SL_I2C_FOLLOWER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Validate the input and length of data to send and receive + if (rx_len == 0) { + return SL_STATUS_INVALID_PARAMETER; + } + + CORE_ENTER_ATOMIC(); + + // Initialize transaction parameters + i2c_handle->rx_offset = 0; + i2c_handle->transfer_direction = SL_I2C_READ; + + // Initialize the I2C transfer + i2c_setup_blocking_transfer_interrupts(i2c_handle); - I2C_TypeDef *i2c_base_addr = i2c_instance->i2c_base_addr; - bool is_10bit_addr = i2c_instance->is_10bit_addr; - uint16_t follower_address = (i2c_instance->follower_address >> 1); - uint8_t *addr_buffer = i2c_instance->addr_buffer, addr_buffer_count = 0; - sl_i2c_operating_mode_t operating_mode = i2c_instance->operating_mode; - sl_i2c_dma_channel_info_t *dma_channel = &(i2c_instance->dma_channel); - DMADRV_PeripheralSignal_t dma_tx_trigger_source, dma_rx_trigger_source; - - const uint8_t *data_buffer = NULL; - uint16_t data_len = 0; - if (i2c_instance->transfer_seq == SL_I2C_WRITE) { - data_buffer = i2c_instance->tx_buffer; - data_len = i2c_instance->tx_len; - } else { // SL_I2C_READ - data_buffer = i2c_instance->rx_buffer; - data_len = i2c_instance->rx_len; + status = i2c_follower_mode_blocking_state_machine(i2c_handle, NULL, 0, rx_buffer, rx_len, timeout); + + CORE_EXIT_ATOMIC(); + + return status; +} + +/***************************************************************************//** + * Follower Mode: Send data to the I2C leader (non-blocking). + ******************************************************************************/ +sl_status_t sl_i2c_follower_send_non_blocking(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + void *user_data) +{ + CORE_DECLARE_IRQ_STATE; + sl_status_t status = SL_STATUS_OK; + + // Null pointer validation + if (i2c_handle == NULL || tx_buffer == NULL) { + return SL_STATUS_NULL_POINTER; + } + // Operating mode validation + if (i2c_handle->operating_mode != SL_I2C_FOLLOWER_MODE) { + return SL_STATUS_INVALID_PARAMETER; + } + // Tx length validation + if (tx_len == 0) { + return SL_STATUS_INVALID_PARAMETER; } - sl_hal_i2c_flush_buffers(i2c_base_addr); + CORE_ENTER_ATOMIC(); - switch (I2C_NUM(i2c_base_addr)) { -#if defined(LDMAXBAR_CH_REQSEL_SIGSEL_I2C0TXBL) && defined(LDMAXBAR_CH_REQSEL_SIGSEL_I2C0RXDATAV) \ - || defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C0TXBL) && defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C0RXDATAV) - case 0: - dma_tx_trigger_source = dmadrvPeripheralSignal_I2C0_TXBL; - dma_rx_trigger_source = dmadrvPeripheralSignal_I2C0_RXDATAV; - sl_interrupt_manager_clear_irq_pending(I2C0_IRQn); - sl_interrupt_manager_enable_irq(I2C0_IRQn); - break; -#endif + // Initialize the I2C instance structure + i2c_handle->transfer_direction = SL_I2C_WRITE; + i2c_handle->event = SL_I2C_EVENT_IDLE; + i2c_handle->user_data = user_data; -#if defined(LDMAXBAR_CH_REQSEL_SIGSEL_I2C1TXBL) && defined(LDMAXBAR_CH_REQSEL_SIGSEL_I2C1RXDATAV) \ - || defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C1TXBL) && defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C1RXDATAV) - case 1: - dma_tx_trigger_source = dmadrvPeripheralSignal_I2C1_TXBL; - dma_rx_trigger_source = dmadrvPeripheralSignal_I2C1_RXDATAV; - sl_interrupt_manager_clear_irq_pending(I2C1_IRQn); - sl_interrupt_manager_enable_irq(I2C1_IRQn); - break; -#endif + status = i2c_setup_follower_non_blocking_dma_transfer(i2c_handle, tx_buffer, tx_len, NULL, 0); -#if defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C2TXBL) && defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C2RXDATAV) - case 2: - dma_tx_trigger_source = dmadrvPeripheralSignal_I2C2_TXBL; - dma_rx_trigger_source = dmadrvPeripheralSignal_I2C2_RXDATAV; - sl_interrupt_manager_clear_irq_pending(I2C2_IRQn); - sl_interrupt_manager_enable_irq(I2C2_IRQn); - break; -#endif + CORE_EXIT_ATOMIC(); + return status; +} - default: - return SL_STATUS_INVALID_CONFIGURATION; +/***************************************************************************//** + * Follower Mode: Receive data from the I2C leader (non-blocking). + ******************************************************************************/ +sl_status_t sl_i2c_follower_receive_non_blocking(sl_i2c_handle_t *i2c_handle, + uint8_t *rx_buffer, + uint32_t rx_len, + void *user_data) +{ + CORE_DECLARE_IRQ_STATE; + sl_status_t status = SL_STATUS_OK; + + // Null pointer validation + if (i2c_handle == NULL || rx_buffer == NULL) { + return SL_STATUS_NULL_POINTER; + } + // Operating mode validation + if (i2c_handle->operating_mode != SL_I2C_FOLLOWER_MODE) { + return SL_STATUS_INVALID_MODE; + } + // Rx length validation + if (rx_len == 0) { + return SL_STATUS_INVALID_PARAMETER; } -#if defined(EMDRV_DMADRV_LDMA) - LDMA_TransferCfg_t tx_cfg = LDMA_TRANSFER_CFG_PERIPHERAL(dma_tx_trigger_source); - LDMA_TransferCfg_t rx_cfg = LDMA_TRANSFER_CFG_PERIPHERAL(dma_rx_trigger_source); + CORE_ENTER_ATOMIC(); - sl_interrupt_manager_disable_irq(LDMA_IRQn); - LDMA_IntDisable(1 << dma_channel->dma_tx_channel); - LDMA_IntDisable(1 << dma_channel->dma_rx_channel); -#elif defined(EMDRV_DMADRV_LDMA_S3) - sl_hal_ldma_transfer_config_t tx_cfg = SL_HAL_LDMA_TRANSFER_CFG_PERIPHERAL(dma_tx_trigger_source); - sl_hal_ldma_transfer_config_t rx_cfg = SL_HAL_LDMA_TRANSFER_CFG_PERIPHERAL(dma_rx_trigger_source); + // Initialize the I2C instance structure + i2c_handle->transfer_direction = SL_I2C_READ; + i2c_handle->user_data = user_data; - sl_hal_ldma_disable_interrupts(LDMA0, _LDMA_IEN_MASK); -#endif + status = i2c_setup_follower_non_blocking_dma_transfer(i2c_handle, NULL, 0, rx_buffer, rx_len); - if (operating_mode == SL_I2C_LEADER_MODE) { - sl_hal_i2c_enable_interrupts(i2c_base_addr, (I2C_IEN_ACK | I2C_IEN_NACK | I2C_IEN_MSTOP | SL_HAL_I2C_IEN_ERRORS)); + CORE_EXIT_ATOMIC(); + return status; +} - if (is_10bit_addr) { - addr_buffer[0] = ((((follower_address << 1) >> 8) & 0x06) | (SL_I2C_FIRST_BYTE_10BIT_ADDR_MASK)); - addr_buffer[1] = ((follower_address << 1) & 0xFF); - if (i2c_instance->transfer_seq == SL_I2C_READ) { - addr_buffer[2] = addr_buffer[0] | 1; - } - addr_buffer_count = 2; - } else { // 7-bit address - addr_buffer[0] = ((follower_address << 1) & SL_I2C_7BIT_FOLLOWER_ADDRESS_MASK); - if (i2c_instance->transfer_seq == SL_I2C_READ) { - addr_buffer[0] |= 1; - } - addr_buffer_count = 1; - } +/***************************************************************************//** + * Register a transfer complete callback for non-blocking APIs. + ******************************************************************************/ +sl_status_t sl_i2c_set_transfer_complete_callback(sl_i2c_handle_t *i2c_handle, + sl_i2c_transfer_complete_callback_t callback) +{ + CORE_DECLARE_IRQ_STATE; -#if defined(EMDRV_DMADRV_LDMA) - if (i2c_instance->transfer_seq == SL_I2C_WRITE) { - if (i2c_instance->rstart == 0) { - (i2c_base_addr)->CTRL_SET = I2C_CTRL_AUTOSE; - } - i2c_instance->tx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_M2P_BYTE((void*)(addr_buffer), &((i2c_base_addr)->TXDATA), addr_buffer_count, 1); - i2c_instance->tx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_M2P_BYTE((void*)data_buffer, &((i2c_base_addr)->TXDATA), data_len); - } else if (i2c_instance->transfer_seq == SL_I2C_READ) { - (i2c_base_addr)->CTRL_SET = I2C_CTRL_AUTOACK; - i2c_instance->tx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_M2P_BYTE((void*)(addr_buffer), &((i2c_base_addr)->TXDATA), addr_buffer_count); - // For 10 bit and 7 bit, receive operations are similar - if (data_len == 1) { - i2c_instance->rx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); - i2c_instance->rx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)data_buffer, 1, 1); - i2c_instance->rx_desc[2] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_WRITE((I2C_CMD_NACK | I2C_CMD_STOP), (uint32_t)(&(i2c_base_addr)->CMD)); - } else { - i2c_instance->rx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)data_buffer, data_len - 1, 1); - i2c_instance->rx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); - i2c_instance->rx_desc[2] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE((uint32_t)(&(i2c_base_addr)->RXDATA), (uint32_t)(data_buffer + data_len - 1), 1, 1); - i2c_instance->rx_desc[3] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_WRITE((I2C_CMD_NACK | I2C_CMD_STOP), (uint32_t)(&(i2c_base_addr)->CMD)); - } + // Validate input parameters + if (i2c_handle == NULL) { + return SL_STATUS_NULL_POINTER; + } + + CORE_ENTER_ATOMIC(); + + i2c_handle->transfer_complete_callback = callback; + + CORE_EXIT_ATOMIC(); + return SL_STATUS_OK; +} + +/***************************************************************************//** + * Register an event callback for non-blocking APIs. + ******************************************************************************/ +sl_status_t sl_i2c_set_event_callback(sl_i2c_handle_t *i2c_handle, + sl_i2c_event_callback_t callback) +{ + CORE_DECLARE_IRQ_STATE; + + // Validate input parameters + if (i2c_handle == NULL) { + return SL_STATUS_NULL_POINTER; + } + + CORE_ENTER_ATOMIC(); + + i2c_handle->event_callback = callback; + + CORE_EXIT_ATOMIC(); + return SL_STATUS_OK; +} + +/******************************************************************************* + ************************** INTERNAL FUNCTIONS ***************************** + ******************************************************************************/ + +/***************************************************************************//** + * Configure the I2C instance with initialization parameters. + ******************************************************************************/ +sl_status_t sli_i2c_init_core(sl_i2c_handle_t *i2c_handle) +{ + if (i2c_handle == NULL) { + return SL_STATUS_NULL_POINTER; + } + + uint32_t ref_freq, max_freq = 0; + sl_i2c_clock_hlr_t clhr = 0; + sl_clock_branch_t i2c_clk_branch; + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + if (!SL_HAL_I2C_REF_VALID(i2c_base_addr)) { + return SL_STATUS_NOT_SUPPORTED; + } + + // Retrieve the I2C peripheral clock frequency + i2c_clk_branch = sl_device_peripheral_get_clock_branch(i2c_handle->i2c_peripheral); + if (sl_clock_manager_get_clock_branch_frequency(i2c_clk_branch, &ref_freq) != SL_STATUS_OK) { + return SL_STATUS_FAIL; + } + + // Reset the I2C instance + sl_hal_i2c_reset(i2c_base_addr); + + // Clear and disable all I2C interrupts to ensure clean state + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); + sl_hal_i2c_disable_interrupts(i2c_base_addr, _I2C_IEN_MASK); + + // Initialize the I2C module with the appropriate operating mode (leader or follower) + sl_hal_i2c_init(i2c_base_addr, i2c_handle->operating_mode); + + // Configure the I2C frequency based on the operating mode and the frequency mode + if (i2c_handle->operating_mode == SL_I2C_LEADER_MODE) { + max_freq = i2c_max_freq_table[i2c_handle->frequency_mode]; + clhr = i2c_clhr_table[i2c_handle->frequency_mode]; + sl_hal_i2c_set_clock_frequency(i2c_base_addr, ref_freq, max_freq, clhr); + } + + // Enable the I2C peripheral + sl_hal_i2c_enable(i2c_base_addr); + + // Register this I2C handle in the global context table. + i2c_handle_contexts[I2C_NUM(i2c_base_addr)] = i2c_handle; + + return SL_STATUS_OK; +} + +/***************************************************************************//** + * Dispatch interrupt handler for the I2C Leader + ******************************************************************************/ +void sli_i2c_leader_dispatch_interrupt(sl_i2c_handle_t *i2c_handle) +{ + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + uint32_t pending_irq = sl_hal_i2c_get_enabled_pending_interrupts(i2c_base_addr); + + if (pending_irq & I2C_IF_ACK) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_ACK); + switch (i2c_handle->state) { + case SL_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK: + if (i2c_handle->follower_address < 0x7F) { + i2c_handle->event = SL_I2C_EVENT_IN_PROGRESS; + if (i2c_handle->transfer_direction != SL_I2C_READ) { + i2c_handle->state = SL_I2C_STATE_DATA_WAIT_FOR_ACK_OR_NACK; + } + } else { + i2c_handle->state = SL_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK; + } + break; + + case SL_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK: + if (i2c_handle->transfer_direction != SL_I2C_READ) { + i2c_handle->event = SL_I2C_EVENT_IN_PROGRESS; + i2c_handle->state = SL_I2C_STATE_DATA_WAIT_FOR_ACK_OR_NACK; + } else { + sl_hal_i2c_start_cmd(i2c_base_addr); + sl_hal_i2c_tx(i2c_base_addr, i2c_handle->addr_buffer[0] | 1); + i2c_handle->state = SL_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK; + } + break; + + case SL_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK: + i2c_handle->event = SL_I2C_EVENT_IN_PROGRESS; + break; } -#elif defined(EMDRV_DMADRV_LDMA_S3) - if (i2c_instance->transfer_seq == SL_I2C_WRITE) { - (i2c_base_addr)->CTRL_SET = I2C_CTRL_AUTOSE; - i2c_instance->tx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)(addr_buffer), &(i2c_base_addr)->TXDATA, addr_buffer_count, 1); - i2c_instance->tx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)data_buffer, &((i2c_base_addr)->TXDATA), data_len); - } else if (i2c_instance->transfer_seq == SL_I2C_READ) { - (i2c_base_addr)->CTRL_SET = I2C_CTRL_AUTOACK; - i2c_instance->tx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)(addr_buffer), &((i2c_base_addr)->TXDATA), addr_buffer_count); - // For 10 bit and 7 bit, receive operations are similar - if (data_len == 1) { - i2c_instance->rx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); - i2c_instance->rx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)data_buffer, 1, 1); - i2c_instance->rx_desc[2] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_WRITE((I2C_CMD_NACK | I2C_CMD_STOP), (uint32_t)(&(i2c_base_addr)->CMD)); - } else { - i2c_instance->rx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)data_buffer, data_len - 1, 1); - i2c_instance->rx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); - i2c_instance->rx_desc[2] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, (uint32_t)(&(i2c_base_addr)->RXDATA), (uint32_t)(data_buffer + data_len - 1), 1, 1); - i2c_instance->rx_desc[3] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_WRITE((I2C_CMD_NACK | I2C_CMD_STOP), (uint32_t)(&(i2c_base_addr)->CMD)); - } + } else if (pending_irq & I2C_IF_TXC) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_TXC); + sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_TXC); + sl_hal_i2c_start_cmd(i2c_base_addr); + sl_hal_i2c_tx(i2c_base_addr, i2c_handle->addr_buffer[0] | 1); + } else if (pending_irq & I2C_IF_NACK) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); + switch (i2c_handle->state) { + case SL_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK: + case SL_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK: + case SL_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK: + i2c_handle->event = SL_I2C_EVENT_INVALID_ADDR; + break; + case SL_I2C_STATE_DATA_WAIT_FOR_ACK_OR_NACK: + i2c_handle->event = SL_I2C_EVENT_DATA_NACK; + break; } -#endif - DMADRV_LdmaStartTransfer(dma_channel->dma_tx_channel, - &tx_cfg, - (void*)&(i2c_instance->tx_desc), - NULL, NULL); - if (i2c_instance->transfer_seq == SL_I2C_READ) { - DMADRV_LdmaStartTransfer(dma_channel->dma_rx_channel, - &rx_cfg, - (void*)&(i2c_instance->rx_desc), - NULL, NULL); + i2c_handle->state = SL_I2C_STATE_WAIT_FOR_STOP; + sl_hal_i2c_stop_cmd(i2c_base_addr); + } else if (pending_irq & I2C_IF_MSTOP) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IEN_MASK); + sl_hal_i2c_disable_interrupts(i2c_base_addr, _I2C_IEN_MASK); + stop_active_dma_transfers(i2c_handle); + i2c_base_addr->CTRL = _I2C_CTRL_RESETVALUE; + if (i2c_handle->event == SL_I2C_EVENT_IN_PROGRESS) { + i2c_handle->event = SL_I2C_EVENT_COMPLETED; + } + } else if (pending_irq & SL_HAL_I2C_IF_ERRORS) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IEN_MASK); + sl_hal_i2c_disable_interrupts(i2c_base_addr, _I2C_IEN_MASK); + stop_active_dma_transfers(i2c_handle); + i2c_base_addr->CMD = I2C_CMD_ABORT; + if (pending_irq & I2C_IF_ARBLOST) { + i2c_handle->event = SL_I2C_EVENT_ARBITRATION_LOST; + } else if (pending_irq & I2C_IF_BUSERR) { + i2c_handle->event = SL_I2C_EVENT_BUS_ERROR; } + } +} - sl_hal_i2c_start_cmd(i2c_base_addr); - } else if (operating_mode == SL_I2C_FOLLOWER_MODE) { - sl_hal_i2c_enable_interrupts(i2c_base_addr, (I2C_IEN_ADDR | SL_HAL_I2C_IEN_ERRORS)); - (i2c_base_addr)->CTRL_SET = I2C_CTRL_AUTOACK; -#if defined(EMDRV_DMADRV_LDMA) - if (i2c_instance->transfer_seq == SL_I2C_READ) { - addr_buffer_count = is_10bit_addr ? 2 : 1; - i2c_instance->rx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)(addr_buffer), addr_buffer_count, 1); - if (data_len == 1) { - i2c_instance->rx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); - i2c_instance->rx_desc[2] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)data_buffer, 1, 1); - i2c_instance->rx_desc[3] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CMD_NACK, (uint32_t)(&(i2c_base_addr)->CMD)); - } else { - i2c_instance->rx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)data_buffer, data_len - 1, 1); - i2c_instance->rx_desc[2] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); - i2c_instance->rx_desc[3] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE((uint32_t)(&(i2c_base_addr)->RXDATA), (uint32_t)(data_buffer + data_len - 1), 1, 1); - i2c_instance->rx_desc[4] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CMD_NACK, (uint32_t)(&(i2c_base_addr)->CMD)); - } - } else if (i2c_instance->transfer_seq == SL_I2C_WRITE) { - addr_buffer_count = is_10bit_addr ? 3 : 1; - i2c_instance->rx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)(addr_buffer), 3, 1); - i2c_instance->rx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR)); - i2c_instance->tx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_M2P_BYTE((void*)data_buffer, &((i2c_base_addr)->TXDATA), data_len, 1); - i2c_instance->tx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_M2P_BYTE((void*)temp_buffer, &((i2c_base_addr)->TXDATA), 1, 0); +/***************************************************************************//** + * Dispatch interrupt handler for the I2C Follower + ******************************************************************************/ +void sli_i2c_follower_dispatch_interrupt(sl_i2c_handle_t *i2c_handle) +{ + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + uint32_t pending_irq = sl_hal_i2c_get_enabled_pending_interrupts(i2c_base_addr); + + if (pending_irq & I2C_IF_ADDR) { + sl_hal_i2c_send_ack(i2c_base_addr); + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_ADDR | I2C_IF_RXDATAV | I2C_IF_SSTOP); + sl_hal_i2c_enable_interrupts(i2c_base_addr, (I2C_IEN_NACK | I2C_IEN_SSTOP)); + + switch (i2c_handle->state) { + case SL_I2C_STATE_ADDRESS_MATCH: + if (i2c_handle->follower_address < 0x7F) { + i2c_handle->event = SL_I2C_EVENT_IN_PROGRESS; + if (i2c_handle->transfer_direction == SL_I2C_READ) { + i2c_base_addr->CTRL_SET = I2C_CTRL_AUTOACK; + } + } else { + sl_hal_i2c_enable_interrupts(i2c_base_addr, I2C_IEN_RXDATAV); + i2c_handle->state = SL_I2C_STATE_10BIT_ADDRESS_MATCH; + } + break; + + case SL_I2C_STATE_REP_ADDR_MATCH: + sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_ADDR); + i2c_handle->event = SL_I2C_EVENT_IN_PROGRESS; + break; } -#elif defined(EMDRV_DMADRV_LDMA_S3) - if (i2c_instance->transfer_seq == SL_I2C_READ) { - addr_buffer_count = is_10bit_addr ? 2 : 1; - i2c_instance->rx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)(addr_buffer), addr_buffer_count, 1); - if (data_len == 1) { - i2c_instance->rx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); - i2c_instance->rx_desc[2] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)data_buffer, 1, 1); - i2c_instance->rx_desc[3] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CMD_NACK, (uint32_t)(&(i2c_base_addr)->CMD)); + } else if (pending_irq & I2C_IF_RXDATAV) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_RXDATAV); + sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_RXDATAV); + + if (i2c_handle->state == SL_I2C_STATE_10BIT_ADDRESS_MATCH) { + if (((i2c_handle->follower_address << 1) & 0xFF) == i2c_handle->addr_buffer[1]) { + sl_hal_i2c_send_ack(i2c_base_addr); + if (i2c_handle->transfer_direction == SL_I2C_READ) { + i2c_base_addr->CTRL_SET = I2C_CTRL_AUTOACK; + i2c_handle->event = SL_I2C_EVENT_IN_PROGRESS; + } else { + i2c_handle->state = SL_I2C_STATE_REP_ADDR_MATCH; + } } else { - i2c_instance->rx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)data_buffer, data_len - 1, 1); - i2c_instance->rx_desc[2] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); - i2c_instance->rx_desc[3] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, (uint32_t)(&(i2c_base_addr)->RXDATA), (uint32_t)(data_buffer + data_len - 1), 1, 1); - i2c_instance->rx_desc[4] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CMD_NACK, (uint32_t)(&(i2c_base_addr)->CMD)); + sl_hal_i2c_send_nack(i2c_base_addr); + i2c_handle->event = SL_I2C_EVENT_INVALID_ADDR; } - } else if (i2c_instance->transfer_seq == SL_I2C_WRITE) { - addr_buffer_count = is_10bit_addr ? 3 : 1; - i2c_instance->rx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)(addr_buffer), addr_buffer_count, 1); - i2c_instance->rx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR)); - i2c_instance->tx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)data_buffer, &((i2c_base_addr)->TXDATA), data_len, 1); - i2c_instance->tx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)temp_buffer, &((i2c_base_addr)->TXDATA), 1, 0); + } else { + sl_hal_i2c_flush_buffers(i2c_base_addr); + sl_hal_i2c_send_nack(i2c_base_addr); } -#endif - - DMADRV_LdmaStartTransfer(dma_channel->dma_rx_channel, - &rx_cfg, - (void*)&(i2c_instance->rx_desc), - NULL, NULL); - if (i2c_instance->transfer_seq == SL_I2C_WRITE) { - DMADRV_LdmaStartTransfer(dma_channel->dma_tx_channel, - &tx_cfg, - (void*)&(i2c_instance->tx_desc), - NULL, NULL); + } else if (pending_irq & I2C_IF_NACK) { + sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_NACK); + } else if (pending_irq & I2C_IF_SSTOP) { + sl_hal_i2c_disable_interrupts(i2c_base_addr, _I2C_IEN_MASK); + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IEN_MASK); + stop_active_dma_transfers(i2c_handle); + i2c_base_addr->CTRL = _I2C_CTRL_RESETVALUE; + if (i2c_handle->event == SL_I2C_EVENT_IN_PROGRESS) { + i2c_handle->event = SL_I2C_EVENT_COMPLETED; + } + } else if (pending_irq & SL_HAL_I2C_IF_ERRORS) { + sl_hal_i2c_disable_interrupts(i2c_base_addr, _I2C_IEN_MASK); + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IEN_MASK); + stop_active_dma_transfers(i2c_handle); + i2c_base_addr->CMD = I2C_CMD_ABORT; + if (pending_irq & I2C_IF_ARBLOST) { + i2c_handle->event = SL_I2C_EVENT_ARBITRATION_LOST; + } else if (pending_irq & I2C_IF_BUSERR) { + i2c_handle->event = SL_I2C_EVENT_BUS_ERROR; } } - return SL_STATUS_OK; } /******************************************************************************* - ************************** LOCAL FUNCTIONS ******************************** + *************************** LOCAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * Get the time in milli seconds. + * + * @return + * Current time in milliseconds. ******************************************************************************/ +static uint32_t get_current_time_ms(void) +{ + return sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count()); +} /***************************************************************************//** - * This function is used for getting the respective i2c peripheral instance + * Enables I2C transfer-related interrupts for the specified I2C handle. * - * @param[in] i2c_base_addr i2c base address instance - * @param[out] i2c_peripheral i2c peripheral instance + * @details + * Flushes I2C buffers, clears all pending interrupts, and enables the necessary + * interrupt sources based on the operating mode (Leader or Follower) and transfer + * direction. Configures the interrupt mask to include ACK, NACK, error interrupts, + * and other relevant interrupts such as STOP, RXDATAV, ADDR, etc. + * + * @param[in] i2c_handle Pointer to the I2C handle structure. * * @return return status. ******************************************************************************/ -static sl_status_t i2c_get_peripheral_instance(I2C_TypeDef *i2c_base_addr, - sl_peripheral_t* i2c_peripheral) +static sl_status_t i2c_setup_blocking_transfer_interrupts(sl_i2c_handle_t *i2c_handle) { - // Null pointer validation. - if (i2c_peripheral == NULL || i2c_base_addr == NULL) { - return SL_STATUS_NULL_POINTER; - } + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + IRQn_Type i2c_irq = (IRQn_Type)0; + // Disable I2C IQR Handler switch (I2C_NUM(i2c_base_addr)) { - case 0: *i2c_peripheral = SL_PERIPHERAL_I2C0; - break; - case 1: *i2c_peripheral = SL_PERIPHERAL_I2C1; - break; - case 2: *i2c_peripheral = SL_PERIPHERAL_I2C2; - break; - case 3: *i2c_peripheral = SL_PERIPHERAL_I2C3; - break; +#if defined(I2C0) + case 0: i2c_irq = I2C0_IRQn; break; +#endif +#if defined(I2C1) + case 1: i2c_irq = I2C1_IRQn; break; +#endif +#if defined(I2C2) + case 2: i2c_irq = I2C2_IRQn; break; +#endif +#if defined(I2C3) + case 3: i2c_irq = I2C3_IRQn; break; +#endif + default: + return SL_STATUS_INVALID_CONFIGURATION; } - return SL_STATUS_OK; -} -/***************************************************************************//** - * This function is used to get the current time in ms. - ******************************************************************************/ -static uint32_t get_current_time_ms(void) -{ - return sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count()); + sl_interrupt_manager_disable_irq(i2c_irq); + + // Flush and clear + sl_hal_i2c_flush_buffers(i2c_base_addr); + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); + + // Build interrupt mask + uint32_t ien_mask = I2C_IEN_ACK | I2C_IEN_NACK | SL_HAL_I2C_IEN_ERRORS; + + if (i2c_handle->operating_mode == SL_I2C_LEADER_MODE) { + ien_mask |= I2C_IEN_MSTOP; + if (i2c_handle->transfer_direction != SL_I2C_WRITE) { + ien_mask |= I2C_IEN_RXDATAV; + } + } else { // Follower mode + ien_mask |= I2C_IEN_SSTOP | I2C_IEN_RXDATAV | I2C_IEN_ADDR; + } + + // Enable all interrupts at once + sl_hal_i2c_enable_interrupts(i2c_base_addr, ien_mask); + + return SL_STATUS_OK; } /***************************************************************************//** * State machine for leader mode in blocking I2C transfer. - * This function manages the state transitions for an I2C leader - * operating in blocking transfer mode. * - * @param[in] sl_i2c_instance Pointer to the I2C instance. - * @param[in] timeout Timeout duration for the blocking transfer. + * @details + * Implements a blocking state machine for I2C leader (master) mode + * transfers. It manages the sequence of sending the follower (slave) address, + * handling 7-bit and 10-bit addressing, transmitting and/or receiving data, + * and processing all relevant I2C protocol events (ACK, NACK, STOP, errors). + * The function blocks until the transfer is complete, an error occurs, or the + * specified timeout is reached. * - * @note Error Handling: - * - Arbitration Fault: This occurs if a follower device - * does not respond as expected or if another leader is - * present on the bus. - * - Bus Error: A misplaced START or STOP condition may indicate - * an unexpected bus state. In leader mode, this should not happen - * under normal conditions if controlled by this software. + * @param[in] i2c_handle Pointer to the I2C handle structure. + * @param[in] tx_buffer Pointer to the transmit buffer (can be NULL if not transmitting). + * @param[in] tx_len Number of bytes to transmit. + * @param[out] rx_buffer Pointer to the receive buffer (can be NULL if not receiving). + * @param[in] rx_len Number of bytes to receive. + * @param[in] timeout Timeout duration for the blocking transfer, in milliseconds (0 = no timeout). * - * @return returns status. + * @return + * SL_STATUS_OK on success, or appropriate error code on failure: + * - SL_STATUS_TIMEOUT if the operation timed out. + * - SL_STATUS_NOT_FOUND if address NACKed. + * - SL_STATUS_ABORT if data NACKed. + * - SL_STATUS_TRANSMIT if arbitration lost. + * - SL_STATUS_IO if bus error. + * - SL_STATUS_FAIL for software fault. + * + * @note + * Error Handling: + * - Arbitration Lost: Occurs if another leader takes control of the bus. + * - Bus Error: Occurs if an unexpected bus condition is detected. + * - Timeout: Occurs if the transfer does not complete within the specified time. ******************************************************************************/ -static sl_status_t i2c_leader_mode_blocking_state_machine(sli_i2c_instance_t *sl_i2c_instance, +static sl_status_t i2c_leader_mode_blocking_state_machine(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len, uint32_t timeout) { - sl_status_t status = SL_STATUS_OK; - bool exit = false; - uint32_t pending_irq, current_time; - uint8_t follower_address; - I2C_TypeDef *i2c_base_addr = sl_i2c_instance->i2c_base_addr; + uint32_t pending_irq; + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + + i2c_handle->state = SL_I2C_STATE_SEND_START_AND_ADDR; + + bool is_10bit_addr = (i2c_handle->follower_address > 0x7F); + uint16_t actual_follower_addr = (i2c_handle->follower_address << 1); + uint8_t first_addr_byte, second_addr_byte; + if (is_10bit_addr) { + first_addr_byte = (((actual_follower_addr >> 8) & 0x06) | SL_I2C_FIRST_BYTE_10BIT_ADDR_MASK); + second_addr_byte = (actual_follower_addr & 0xFF); + } else { + first_addr_byte = (actual_follower_addr & SL_I2C_7BIT_FOLLOWER_ADDRESS_MASK); + if (i2c_handle->transfer_direction == SL_I2C_READ) { + first_addr_byte |= 1; + } + } + uint32_t start_time = get_current_time_ms(); - while ((!exit)) { - // check for timeout. - current_time = get_current_time_ms(); - if ((current_time - start_time) > timeout) { - (i2c_base_addr)->CMD = I2C_CMD_ABORT; - sl_i2c_instance->state = SLI_I2C_STATE_TIMEOUT; - sl_i2c_instance->transfer_event = SL_I2C_EVENT_TIMEOUT; - status = SL_STATUS_TIMEOUT; - exit = true; - break; + while (true) { + if (timeout > 0 && (get_current_time_ms() - start_time >= timeout)) { + i2c_base_addr->CMD = I2C_CMD_ABORT; + i2c_handle->event = SL_I2C_EVENT_TIMEOUT; + return SL_STATUS_TIMEOUT; } pending_irq = sl_hal_i2c_get_enabled_pending_interrupts(i2c_base_addr); - // Handle errors + // Handle errors first - fastest path for error conditions if (pending_irq & SL_HAL_I2C_IF_ERRORS) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); + i2c_base_addr->CMD = I2C_CMD_ABORT; + i2c_handle->state = SL_I2C_STATE_ERROR; + if (pending_irq & I2C_IF_ARBLOST) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_ARBITRATION_LOST; - } else if (pending_irq & I2C_IF_BUSERR) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_BUS_ERROR; + i2c_handle->event = SL_I2C_EVENT_ARBITRATION_LOST; + return SL_STATUS_TRANSMIT; + } else { + i2c_handle->event = SL_I2C_EVENT_BUS_ERROR; + return SL_STATUS_IO; } - sl_i2c_instance->state = SLI_I2C_STATE_ERROR; - sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); - (i2c_base_addr)->CMD = I2C_CMD_ABORT; - status = SL_STATUS_ABORT; - break; } - switch (sl_i2c_instance->state) { - /********************************************************/ - /* Handles the following states: */ - /* - SLI_I2C_STATE_SEND_START_AND_ADDR: */ - /* Sends the first start condition with the address */ - /* (includes the first byte for 10-bit addresses). */ - /* - SLI_I2C_STATE_SEND_REPEATED_START_AND_ADDR: */ - /* Sends a repeated start condition with the address. */ - /********************************************************/ - case SLI_I2C_STATE_SEND_START_AND_ADDR: - case SLI_I2C_STATE_SEND_REPEATED_START_AND_ADDR: - if (sl_i2c_instance->is_10bit_addr) { - follower_address = (((sl_i2c_instance->follower_address >> 8) & 0x06) | (SL_I2C_FIRST_BYTE_10BIT_ADDR_MASK)); - } else { // 7-bit address - follower_address = (sl_i2c_instance->follower_address & SL_I2C_7BIT_FOLLOWER_ADDRESS_MASK); - if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - follower_address |= 1; - } - } + switch (i2c_handle->state) { + case SL_I2C_STATE_SEND_START_AND_ADDR: + sl_hal_i2c_start_cmd(i2c_base_addr); + sl_hal_i2c_tx(i2c_base_addr, first_addr_byte); + i2c_handle->state = SL_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK; + i2c_handle->event = SL_I2C_EVENT_IN_PROGRESS; + break; - if (sl_i2c_instance->state == SLI_I2C_STATE_SEND_START_AND_ADDR) { - sl_hal_i2c_tx(i2c_base_addr, follower_address); - sl_hal_i2c_start_cmd(i2c_base_addr); - sl_i2c_instance->state = SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK; - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IN_PROGRESS; - } else if (sl_i2c_instance->state == SLI_I2C_STATE_SEND_REPEATED_START_AND_ADDR) { - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE_READ) { - follower_address |= 1; - } - sl_hal_i2c_start_cmd(i2c_base_addr); - sl_hal_i2c_tx(i2c_base_addr, follower_address); - sl_i2c_instance->state = SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK; - } + case SL_I2C_STATE_SEND_REPEATED_START_AND_ADDR: + sl_hal_i2c_start_cmd(i2c_base_addr); + sl_hal_i2c_tx(i2c_base_addr, (first_addr_byte | 1)); + i2c_handle->state = SL_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK; break; - /****************************************************************************/ - /* Handles ACK/NACK responses for different address transmission scenarios. */ - /* */ - /* This case manages: */ - /* 1. SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK */ - /* - Waits for an ACK/NACK after sending the first address byte. */ - /* - If ACK is received: */ - /* - For 10-bit mode, sends the second address byte and waits for */ - /* another ACK/NACK. */ - /* - For 7-bit mode, determines if it's a read or write operation */ - /* and transitions accordingly. */ - /* */ - /* 2. SLI_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK */ - /* - Waits for ACK/NACK after sending the second byte in 10-bit mode. */ - /* - If ACK is received: */ - /* - If a read operation, issues a repeated start. */ - /* - Otherwise, transitions to data transmission. */ - /* */ - /* 3. SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK */ - /* - Waits for ACK/NACK after a repeated START + address. */ - /* - If ACK is received: */ - /* - In 10-bit mode, transitions to receiving data. */ - /* - In 7-bit write-read sequences, transitions to receiving data. */ - /* - If NACK is received at any stage, the transfer is aborted, and a */ - /* STOP condition is issued. */ - /****************************************************************************/ - case SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK: - case SLI_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK: - case SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK: - if (pending_irq & I2C_IF_NACK) { - /* Handle NACK response */ - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); - sl_hal_i2c_stop_cmd(i2c_base_addr); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_NACK_RECEIVED; - sl_i2c_instance->state = SLI_I2C_STATE_SEND_STOP; - status = SL_STATUS_ABORT; - } else if (pending_irq & I2C_IF_ACK) { - /* Handle ACK response */ + case SL_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK: + if (pending_irq & I2C_IF_ACK) { sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_ACK); - - if (sl_i2c_instance->state == SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK) { - if (sl_i2c_instance->is_10bit_addr) { - sl_hal_i2c_tx(i2c_base_addr, (sl_i2c_instance->follower_address & 0xFF)); - sl_i2c_instance->state = SLI_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK; - } else { // 7-Bit address - if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - sl_i2c_instance->state = SLI_I2C_STATE_RECEIVE_DATA; - } else { // WRITE OR WRITE-READ - sl_i2c_instance->state = SLI_I2C_STATE_SEND_DATA; - } - } - } else if (sl_i2c_instance->state == SLI_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK) { - if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - sl_hal_i2c_start_cmd(i2c_base_addr); - uint8_t follower_address = (((sl_i2c_instance->follower_address >> 8) & 0x06) | SL_I2C_FIRST_BYTE_10BIT_ADDR_MASK) | 1; - sl_hal_i2c_tx(i2c_base_addr, follower_address); - sl_i2c_instance->state = SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK; - } else { // WRITE or WRITE_READ - sl_i2c_instance->state = SLI_I2C_STATE_SEND_DATA; - } - } else if (sl_i2c_instance->state == SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK) { - if ((sl_i2c_instance->is_10bit_addr) && (sl_i2c_instance->transfer_seq != SL_I2C_WRITE)) { // READ or WRITE_READ - sl_i2c_instance->state = SLI_I2C_STATE_RECEIVE_DATA; - } else if ((!sl_i2c_instance->is_10bit_addr) && (sl_i2c_instance->transfer_seq == SL_I2C_WRITE_READ)) { - sl_i2c_instance->state = SLI_I2C_STATE_RECEIVE_DATA; + if (is_10bit_addr) { + sl_hal_i2c_tx(i2c_base_addr, second_addr_byte); + i2c_handle->state = SL_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK; + } else { + if (i2c_handle->transfer_direction == SL_I2C_READ) { + i2c_handle->state = SL_I2C_STATE_RECEIVE_DATA; + } else { // WRITE or WRITE-READ + i2c_handle->state = SL_I2C_STATE_SEND_DATA; } } + } else if (pending_irq & I2C_IF_NACK) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); + sl_hal_i2c_stop_cmd(i2c_base_addr); + i2c_handle->event = SL_I2C_EVENT_ADDR_NACK; + i2c_handle->state = SL_I2C_STATE_SEND_STOP; } break; - /************************************/ - /* Send a data byte to the follower */ - /************************************/ - case SLI_I2C_STATE_SEND_DATA: - if (sl_i2c_instance->tx_offset < sl_i2c_instance->tx_len) { - sl_hal_i2c_tx(i2c_base_addr, sl_i2c_instance->tx_buffer[sl_i2c_instance->tx_offset]); - sl_i2c_instance->tx_offset++; - sl_i2c_instance->state = SLI_I2C_STATE_WAIT_FOR_ACK_OR_NACK; - } else if (sl_i2c_instance->tx_offset == sl_i2c_instance->tx_len) { - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE_READ) { - sl_i2c_instance->state = SLI_I2C_STATE_SEND_REPEATED_START_AND_ADDR; + case SL_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK: + if (pending_irq & I2C_IF_ACK) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_ACK); + if (i2c_handle->transfer_direction == SL_I2C_READ) { + sl_hal_i2c_start_cmd(i2c_base_addr); + sl_hal_i2c_tx(i2c_base_addr, (first_addr_byte | 1)); + i2c_handle->state = SL_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK; } else { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; - sl_i2c_instance->state = SLI_I2C_STATE_SEND_STOP; - sl_hal_i2c_stop_cmd(i2c_base_addr); + i2c_handle->state = SL_I2C_STATE_SEND_DATA; } + } else if (pending_irq & I2C_IF_NACK) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); + sl_hal_i2c_stop_cmd(i2c_base_addr); + i2c_handle->event = SL_I2C_EVENT_ADDR_NACK; + i2c_handle->state = SL_I2C_STATE_SEND_STOP; } - break; - /*****************************************************************/ - /* Wait for ACK/NACK from the follower after sending data to it. */ - /*****************************************************************/ - case SLI_I2C_STATE_WAIT_FOR_ACK_OR_NACK: - if (pending_irq & I2C_IF_NACK) { + case SL_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK: + if (pending_irq & I2C_IF_ACK) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_ACK); + i2c_handle->state = SL_I2C_STATE_RECEIVE_DATA; + } else if (pending_irq & I2C_IF_NACK) { sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); - if ((sl_i2c_instance->transfer_seq == SL_I2C_WRITE_READ) && (sl_i2c_instance->tx_offset == sl_i2c_instance->tx_len)) { - sl_i2c_instance->state = SLI_I2C_STATE_SEND_REPEATED_START_AND_ADDR; + sl_hal_i2c_stop_cmd(i2c_base_addr); + i2c_handle->event = SL_I2C_EVENT_ADDR_NACK; + i2c_handle->state = SL_I2C_STATE_SEND_STOP; + } + break; + + case SL_I2C_STATE_SEND_DATA: + if (i2c_handle->tx_offset < tx_len) { + sl_hal_i2c_tx(i2c_base_addr, tx_buffer[i2c_handle->tx_offset++]); + i2c_handle->state = SL_I2C_STATE_DATA_WAIT_FOR_ACK_OR_NACK; + } else { + if (i2c_handle->transfer_direction == SL_I2C_WRITE_READ) { + i2c_handle->state = SL_I2C_STATE_SEND_REPEATED_START_AND_ADDR; } else { sl_hal_i2c_stop_cmd(i2c_base_addr); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_NACK_RECEIVED; - sl_i2c_instance->state = SLI_I2C_STATE_SEND_STOP; + i2c_handle->state = SL_I2C_STATE_SEND_STOP; } - } else if (pending_irq & I2C_IF_ACK) { + } + break; + + case SL_I2C_STATE_DATA_WAIT_FOR_ACK_OR_NACK: + if (pending_irq & I2C_IF_ACK) { sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_ACK); - sl_i2c_instance->state = SLI_I2C_STATE_SEND_DATA; + i2c_handle->state = SL_I2C_STATE_SEND_DATA; + } else if (pending_irq & I2C_IF_NACK) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); + + sl_hal_i2c_stop_cmd(i2c_base_addr); + i2c_handle->state = SL_I2C_STATE_SEND_STOP; + i2c_handle->event = SL_I2C_EVENT_DATA_NACK; } - break; - /*****************************************/ - /* Receive a data byte from the follower */ - /*****************************************/ - case SLI_I2C_STATE_RECEIVE_DATA: + case SL_I2C_STATE_RECEIVE_DATA: if (pending_irq & I2C_IF_RXDATAV) { sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_RXDATAV); - if (sl_i2c_instance->rx_offset < sl_i2c_instance->rx_len) { - sl_i2c_instance->rx_buffer[sl_i2c_instance->rx_offset] = sl_hal_i2c_rx(i2c_base_addr); - sl_i2c_instance->rx_offset++; + if (i2c_handle->rx_offset < rx_len) { + rx_buffer[i2c_handle->rx_offset++] = sl_hal_i2c_rx(i2c_base_addr); + } - if (sl_i2c_instance->rx_offset == sl_i2c_instance->rx_len) { - sl_hal_i2c_send_nack(i2c_base_addr); - sl_hal_i2c_stop_cmd(i2c_base_addr); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; - sl_i2c_instance->state = SLI_I2C_STATE_SEND_STOP; - } else { - sl_hal_i2c_send_ack(i2c_base_addr); - } + if (i2c_handle->rx_offset == rx_len) { + sl_hal_i2c_send_nack(i2c_base_addr); + sl_hal_i2c_stop_cmd(i2c_base_addr); + i2c_handle->state = SL_I2C_STATE_SEND_STOP; + } else { + sl_hal_i2c_send_ack(i2c_base_addr); } } break; - /***********************************/ - /* Wait for STOP to have been sent */ - /***********************************/ - case SLI_I2C_STATE_SEND_STOP: + case SL_I2C_STATE_SEND_STOP: if (pending_irq & I2C_IF_MSTOP) { sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_MSTOP); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; - sl_i2c_instance->state = SLI_I2C_STATE_DONE; - exit = true; + if (i2c_handle->event == SL_I2C_EVENT_ADDR_NACK) { + return SL_STATUS_NOT_FOUND; + } else if (i2c_handle->event == SL_I2C_EVENT_DATA_NACK) { + return SL_STATUS_ABORT; + } else { + i2c_handle->event = SL_I2C_EVENT_COMPLETED; + return SL_STATUS_OK; + } } - break; default: - sl_i2c_instance->transfer_event = SL_I2C_EVENT_SW_FAULT; - sl_i2c_instance->state = SLI_I2C_STATE_ERROR; - status = SL_STATUS_ABORT; - exit = true; - break; + i2c_handle->event = SL_I2C_EVENT_SW_FAULT; + i2c_handle->state = SL_I2C_STATE_ERROR; + return SL_STATUS_FAIL; } } - - return status; } /***************************************************************************//** - * State machine for leader mode in blocking I2C transfer. - * This function manages the state transitions for an I2C leader operating - * in blocking transfer mode. + * State machine for follower mode in blocking I2C transfer. + * + * @details + * Implements a blocking state machine for I2C follower (slave) mode transfers. + * Handles 7-bit and 10-bit addressing, manages the sequence of address matching, + * data transmission and reception, and processes all relevant I2C protocol events + * (ACK, NACK, STOP, errors). The function blocks until the transfer is complete, + * an error occurs, or the specified timeout is reached. * - * @param[in] sl_i2c_instance Pointer to the I2C instance. - * @param[in] timeout Timeout duration for the blocking transfer. + * @param[in] i2c_handle Pointer to the I2C handle structure. + * @param[in] tx_buffer Pointer to the transmit buffer (can be NULL if not transmitting). + * @param[in] tx_len Number of bytes to transmit. + * @param[out] rx_buffer Pointer to the receive buffer (can be NULL if not receiving). + * @param[in] rx_len Number of bytes to receive. + * @param[in] timeout Timeout duration for the blocking transfer, in milliseconds (0 = no timeout). * - * @note Error Handling: - * - Arbitration Fault: Occurs when a follower device does not - * respond as expected or another leader is present on the bus. - * - Bus Error: A misplaced START or STOP condition may indicate - * an unexpected bus state. In leader mode, this should not happen - * under normal conditions if controlled by this software. + * @return + * SL_STATUS_OK on success, or appropriate error code on failure: + * - SL_STATUS_TIMEOUT if the operation timed out. + * - SL_STATUS_NOT_FOUND if address NACKed. + * - SL_STATUS_FULL if receive buffer is full. + * - SL_STATUS_TRANSMIT if arbitration lost. + * - SL_STATUS_IO if bus error. + * - SL_STATUS_FAIL for software fault. * - * @return returns status. + * @note + * Error Handling: + * - Arbitration Lost: Occurs if another leader takes control of the bus. + * - Bus Error: Occurs if an unexpected bus condition is detected. + * - Timeout: Occurs if the transfer does not complete within the specified time. ******************************************************************************/ -static sl_status_t i2c_follower_mode_blocking_state_machine(sli_i2c_instance_t *sl_i2c_instance, +static sl_status_t i2c_follower_mode_blocking_state_machine(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len, uint32_t timeout) { - sl_status_t status = SL_STATUS_OK; - bool exit = false, address_match = false; - uint32_t pending_irq, current_time; - uint16_t rx_data; - I2C_TypeDef *i2c_base_addr = sl_i2c_instance->i2c_base_addr; - uint32_t start_time = get_current_time_ms(); + uint32_t pending_irq, second_addr_byte = 0; + uint8_t rx_data; + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + bool is_10bit_addr = (i2c_handle->follower_address > 0x7F); - while (!address_match) { - // check for timeout. - current_time = get_current_time_ms(); - if ((current_time - start_time) > timeout) { - (i2c_base_addr)->CMD = I2C_CMD_ABORT; - sl_i2c_instance->state = SLI_I2C_STATE_TIMEOUT; - sl_i2c_instance->transfer_event = SL_I2C_EVENT_TIMEOUT; - status = SL_STATUS_TIMEOUT; - exit = true; - break; + i2c_handle->state = SL_I2C_STATE_IDLE; + + if (is_10bit_addr) { + second_addr_byte = ((i2c_handle->follower_address << 1) & 0xFF); + } + + uint32_t start_time = get_current_time_ms(); + while (true) { + if (timeout > 0 && (get_current_time_ms() - start_time >= timeout)) { + i2c_base_addr->CMD = I2C_CMD_ABORT; + i2c_handle->event = SL_I2C_EVENT_TIMEOUT; + return SL_STATUS_TIMEOUT; } pending_irq = sl_hal_i2c_get_enabled_pending_interrupts(i2c_base_addr); - // Handle errors + // Error handling if (pending_irq & SL_HAL_I2C_IF_ERRORS) { - if (pending_irq & I2C_IF_ARBLOST) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_ARBITRATION_LOST; - } else if (pending_irq & I2C_IF_BUSERR) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_BUS_ERROR; - } - sl_i2c_instance->state = SLI_I2C_STATE_ERROR; - sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); - (i2c_base_addr)->CMD = I2C_CMD_ABORT; - status = SL_STATUS_ABORT; - exit = true; - break; - } - - // Match the address (first byte if 10 bit) - if (pending_irq & I2C_IF_ADDR) { sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); - rx_data = sl_hal_i2c_rx(i2c_base_addr); - sl_hal_i2c_send_ack(i2c_base_addr); + i2c_base_addr->CMD = I2C_CMD_ABORT; + i2c_handle->state = SL_I2C_STATE_ERROR; - if (sl_i2c_instance->is_10bit_addr) { - sl_i2c_instance->state = SLI_I2C_STATE_10BIT_ADDRESS_MATCH; + if (pending_irq & I2C_IF_ARBLOST) { + i2c_handle->event = SL_I2C_EVENT_ARBITRATION_LOST; + return SL_STATUS_TRANSMIT; } else { - if (rx_data & 0x01) { - sl_i2c_instance->state = SLI_I2C_STATE_SEND_DATA; - } else { - sl_i2c_instance->state = SLI_I2C_STATE_RECEIVE_DATA; - } + i2c_handle->event = SL_I2C_EVENT_BUS_ERROR; + return SL_STATUS_IO; } - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IN_PROGRESS; - address_match = true; - } - } - - while ((!exit)) { - // check for timeout. - current_time = get_current_time_ms(); - if ((current_time - start_time) > timeout) { - (i2c_base_addr)->CMD = I2C_CMD_ABORT; - sl_i2c_instance->state = SLI_I2C_STATE_TIMEOUT; - sl_i2c_instance->transfer_event = SL_I2C_EVENT_TIMEOUT; - status = SL_STATUS_TIMEOUT; - exit = true; - break; } - pending_irq = sl_hal_i2c_get_enabled_pending_interrupts(i2c_base_addr); - - switch (sl_i2c_instance->state) { - /***************************************************/ - /* Match the second byte of the 10 address . */ - /***************************************************/ - case SLI_I2C_STATE_10BIT_ADDRESS_MATCH: - if (pending_irq & I2C_IF_RXDATAV) { - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_RXDATAV); + switch (i2c_handle->state) { + case SL_I2C_STATE_IDLE: + if (pending_irq & I2C_IF_ADDR) { rx_data = sl_hal_i2c_rx(i2c_base_addr); + sl_hal_i2c_clear_interrupts(i2c_base_addr, (I2C_IF_ADDR | I2C_IF_RXDATAV | I2C_IF_SSTOP)); + sl_hal_i2c_send_ack(i2c_base_addr); - if ((sl_i2c_instance->follower_address & 0xFF) == rx_data) { - sl_hal_i2c_send_ack(i2c_base_addr); - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE) { - sl_i2c_instance->state = SLI_I2C_STATE_REP_ADDR_MATCH; + if (is_10bit_addr) { + i2c_handle->state = SL_I2C_STATE_10BIT_ADDRESS_MATCH; + } else { + if (rx_data & 0x01) { + i2c_handle->state = SL_I2C_STATE_SEND_DATA; } else { - sl_i2c_instance->state = SLI_I2C_STATE_RECEIVE_DATA; + i2c_handle->state = SL_I2C_STATE_RECEIVE_DATA; } - } else { - sl_hal_i2c_send_nack(i2c_base_addr); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; - sl_i2c_instance->state = SLI_I2C_STATE_WAIT_FOR_STOP; } + i2c_handle->event = SL_I2C_EVENT_IN_PROGRESS; } break; - /***********************************************************************/ - /* Wait for match the repeated start + address (first byte if 10 bit). */ - /***********************************************************************/ - case SLI_I2C_STATE_REP_ADDR_MATCH: - if (pending_irq & I2C_IF_ADDR) { - sl_hal_i2c_send_ack(i2c_base_addr); - sl_hal_i2c_clear_interrupts(i2c_base_addr, (I2C_IF_ADDR | I2C_IF_RXDATAV)); + case SL_I2C_STATE_10BIT_ADDRESS_MATCH: + if (pending_irq & I2C_IF_RXDATAV) { rx_data = sl_hal_i2c_rx(i2c_base_addr); + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_RXDATAV); - if (sl_i2c_instance->is_10bit_addr) { - if (rx_data & 0x01) { - sl_i2c_instance->state = SLI_I2C_STATE_SEND_DATA; - } else { - sl_hal_i2c_send_nack(i2c_base_addr); - sl_i2c_instance->state = SLI_I2C_STATE_WAIT_FOR_STOP; - status = SL_STATUS_ABORT; + if (second_addr_byte == rx_data) { + sl_hal_i2c_send_ack(i2c_base_addr); + if (i2c_handle->transfer_direction == SL_I2C_READ) { + i2c_handle->state = SL_I2C_STATE_RECEIVE_DATA; + } else { // SL_I2C_WRITE + i2c_handle->state = SL_I2C_STATE_REP_ADDR_MATCH; } } } break; - /***************************************/ - /* Receive a data byte from the leader */ - /***************************************/ - case SLI_I2C_STATE_RECEIVE_DATA: - if (pending_irq & I2C_IF_RXDATAV) { + case SL_I2C_STATE_REP_ADDR_MATCH: + if (pending_irq & I2C_IF_ADDR) { rx_data = sl_hal_i2c_rx(i2c_base_addr); - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_RXDATAV); - - if (sl_i2c_instance->rx_offset < sl_i2c_instance->rx_len) { - sl_i2c_instance->rx_buffer[sl_i2c_instance->rx_offset] = rx_data; - sl_i2c_instance->rx_offset++; - - if (sl_i2c_instance->rx_offset == sl_i2c_instance->rx_len) { - sl_hal_i2c_send_nack(i2c_base_addr); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; - sl_i2c_instance->state = SLI_I2C_STATE_WAIT_FOR_STOP; - } else { - sl_hal_i2c_send_ack(i2c_base_addr); - } + sl_hal_i2c_clear_interrupts(i2c_base_addr, (I2C_IF_ADDR | I2C_IF_RXDATAV)); + sl_hal_i2c_send_ack(i2c_base_addr); + if (rx_data & 0x01) { + i2c_handle->state = SL_I2C_STATE_SEND_DATA; + } else { + sl_hal_i2c_send_nack(i2c_base_addr); + i2c_handle->event = SL_I2C_EVENT_ADDR_NACK; + i2c_handle->state = SL_I2C_STATE_WAIT_FOR_STOP; } - } else if (pending_irq & I2C_IF_SSTOP) { - sl_i2c_instance->state = SLI_I2C_STATE_WAIT_FOR_STOP; } break; - /**********************************/ - /* Send a data byte to the leader */ - /**********************************/ - case SLI_I2C_STATE_SEND_DATA: - if (sl_i2c_instance->tx_offset < sl_i2c_instance->tx_len) { - sl_hal_i2c_tx(i2c_base_addr, sl_i2c_instance->tx_buffer[sl_i2c_instance->tx_offset]); - sl_i2c_instance->tx_offset++; - sl_i2c_instance->state = SLI_I2C_STATE_WAIT_FOR_ACK_OR_NACK; - } else if (sl_i2c_instance->tx_offset == sl_i2c_instance->tx_len) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; - sl_i2c_instance->state = SLI_I2C_STATE_WAIT_FOR_STOP; - sl_i2c_instance->tx_offset++; + case SL_I2C_STATE_SEND_DATA: + if (i2c_handle->tx_offset < tx_len) { + sl_hal_i2c_tx(i2c_base_addr, tx_buffer[i2c_handle->tx_offset++]); + } else { + sl_hal_i2c_tx(i2c_base_addr, 0xFF); } + i2c_handle->state = SL_I2C_STATE_DATA_WAIT_FOR_ACK_OR_NACK; break; - /***************************************************************/ - /* Wait for ACK/NACK from the leader after sending data to it. */ - /***************************************************************/ - case SLI_I2C_STATE_WAIT_FOR_ACK_OR_NACK: - if (pending_irq & I2C_IF_NACK) { - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_NACK_RECEIVED; - sl_i2c_instance->state = SLI_I2C_STATE_WAIT_FOR_STOP; - } else if (pending_irq & I2C_IF_ACK) { + case SL_I2C_STATE_DATA_WAIT_FOR_ACK_OR_NACK: + if (pending_irq & I2C_IF_ACK) { sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_ACK); - sl_i2c_instance->state = SLI_I2C_STATE_SEND_DATA; + i2c_handle->state = SL_I2C_STATE_SEND_DATA; + } else if (pending_irq & I2C_IF_NACK) { + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); + i2c_handle->state = SL_I2C_STATE_WAIT_FOR_STOP; + } + break; + + case SL_I2C_STATE_RECEIVE_DATA: + if (pending_irq & I2C_IF_RXDATAV) { + rx_data = sl_hal_i2c_rx(i2c_base_addr); + sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_RXDATAV); + + if (i2c_handle->rx_offset < rx_len) { + rx_buffer[i2c_handle->rx_offset++] = rx_data; + sl_hal_i2c_send_ack(i2c_base_addr); + } else { + sl_hal_i2c_send_nack(i2c_base_addr); + i2c_handle->event = SL_I2C_EVENT_BUFFER_FULL; + i2c_handle->state = SL_I2C_STATE_WAIT_FOR_STOP; + } + } else if (pending_irq & I2C_IF_SSTOP) { + i2c_handle->state = SL_I2C_STATE_WAIT_FOR_STOP; } break; - /********************************************/ - /* Wait for STOP to receive from the leader */ - /********************************************/ - case SLI_I2C_STATE_WAIT_FOR_STOP: + case SL_I2C_STATE_WAIT_FOR_STOP: if (pending_irq & I2C_IF_SSTOP) { sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_SSTOP); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; - sl_i2c_instance->state = SLI_I2C_STATE_DONE; - exit = true; - } else if (sl_i2c_instance->tx_offset >= sl_i2c_instance->tx_len) { - sl_hal_i2c_tx(i2c_base_addr, 0xFF); + if (i2c_handle->event == SL_I2C_EVENT_ADDR_NACK) { + return SL_STATUS_NOT_FOUND; + } else if (i2c_handle->event == SL_I2C_EVENT_BUFFER_FULL) { + return SL_STATUS_FULL; + } else { + i2c_handle->event = SL_I2C_EVENT_COMPLETED; + return SL_STATUS_OK; + } } break; default: - sl_i2c_instance->transfer_event = SL_I2C_EVENT_SW_FAULT; - sl_i2c_instance->state = SLI_I2C_STATE_ERROR; - exit = true; - break; + i2c_handle->event = SL_I2C_EVENT_SW_FAULT; + i2c_handle->state = SL_I2C_STATE_ERROR; + return SL_STATUS_FAIL; } } - - return status; } /***************************************************************************//** - * Function called by the IRQHandlers for handling the interrupts for non blocking mode leader + * Get DMA trigger sources for I2C TX and RX operations. + * + * @details + * Determines the appropriate DMA peripheral signals for the given I2C instance. + * Maps the I2C base address to the corresponding DMA trigger sources and enables + * the appropriate I2C interrupt. + * + * @param[in] i2c_base_addr Pointer to the I2C peripheral base address. + * @param[out] dma_tx_trigger_source Pointer to store the TX DMA trigger source. + * @param[out] dma_rx_trigger_source Pointer to store the RX DMA trigger source. * - * @details This function is called when I2C interrupts are handled by the IRQHandlers. - * Function gets pending interrupt flags and performs the necessary actions - * according to the pending interrupt flags. + * @note This function also: + * - Clears any pending interrupts for the I2C instance + * - Enables the I2C interrupt in the interrupt manager + * - Function is used internally by DMA initialization routines * - * @param sl_i2c_instance Pointer to the I2C instance structure. + * @return Return Status code. ******************************************************************************/ -void sli_i2c_leader_dispatch_interrupt(sli_i2c_instance_t *sl_i2c_instance) +static sl_status_t i2c_get_dma_trigger_signals(sl_i2c_handle_t *i2c_handle, + DMADRV_PeripheralSignal_t *dma_tx_trigger_source, + DMADRV_PeripheralSignal_t *dma_rx_trigger_source) { - I2C_TypeDef *i2c_base_addr = sl_i2c_instance->i2c_base_addr; - uint32_t pending_irq = sl_hal_i2c_get_enabled_pending_interrupts(i2c_base_addr); + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + IRQn_Type i2c_irq; - if (pending_irq & I2C_IF_MSTOP) { - sl_hal_i2c_disable_interrupts(i2c_base_addr, _I2C_IEN_MASK); - sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); + switch (I2C_NUM(i2c_base_addr)) { +#if defined(LDMAXBAR_CH_REQSEL_SIGSEL_I2C0TXBL) && defined(LDMAXBAR_CH_REQSEL_SIGSEL_I2C0RXDATAV) \ + || defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C0TXBL) && defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C0RXDATAV) + case 0: + *dma_tx_trigger_source = dmadrvPeripheralSignal_I2C0_TXBL; + *dma_rx_trigger_source = dmadrvPeripheralSignal_I2C0_RXDATAV; + i2c_irq = I2C0_IRQn; + break; +#endif - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE) { - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_tx_channel); - } else if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_rx_channel); - sl_i2c_instance->rstart = 0; - } +#if defined(LDMAXBAR_CH_REQSEL_SIGSEL_I2C1TXBL) && defined(LDMAXBAR_CH_REQSEL_SIGSEL_I2C1RXDATAV) \ + || defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C1TXBL) && defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C1RXDATAV) + case 1: + *dma_tx_trigger_source = dmadrvPeripheralSignal_I2C1_TXBL; + *dma_rx_trigger_source = dmadrvPeripheralSignal_I2C1_RXDATAV; + i2c_irq = I2C1_IRQn; + break; +#endif - (i2c_base_addr)->CTRL = _I2C_CTRL_RESETVALUE; +#if defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C2TXBL) && defined(LDMAXBAR0_CH_REQSEL_SIGSEL_I2C2RXDATAV) + case 2: + *dma_tx_trigger_source = dmadrvPeripheralSignal_I2C2_TXBL; + *dma_rx_trigger_source = dmadrvPeripheralSignal_I2C2_RXDATAV; + i2c_irq = I2C2_IRQn; + break; +#endif - if (sl_i2c_instance->transfer_event == SL_I2C_EVENT_IN_PROGRESS) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; - } - } else if (pending_irq & I2C_IF_TXC) { - sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_TXC); - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_TXC); - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_tx_channel); - (i2c_base_addr)->CTRL = _I2C_CTRL_RESETVALUE; - if (sl_i2c_instance->rstart == RSTART_WRITE_INPROGRESS) { - sl_i2c_instance->transfer_seq = SL_I2C_READ; - sl_i2c_instance->transfer_mode = SLI_I2C_NON_BLOCKING_TRANSFER; - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IDLE; - sl_i2c_instance->addr_buffer[0] = 0; - sl_i2c_instance->state = SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK; - sl_i2c_instance->rstart = RSTART_READ_INPROGRESS; - memset(sl_i2c_instance->tx_desc, 0, sizeof(sl_i2c_instance->tx_desc)); - sli_i2c_dma_transfer_init(sl_i2c_instance); - } - } else if (pending_irq & I2C_IF_NACK) { - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); - switch (sl_i2c_instance->state) { - case SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK: - case SLI_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK: - case SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK: - sl_i2c_instance->transfer_event = SL_I2C_EVENT_INVALID_ADDR; - break; - } - sl_hal_i2c_stop_cmd(i2c_base_addr); - } else if (pending_irq & I2C_IF_ACK) { - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_ACK); + default: + return SL_STATUS_INVALID_CONFIGURATION; + } - switch (sl_i2c_instance->state) { - case SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK: - if (sl_i2c_instance->is_10bit_addr) { - sl_i2c_instance->state = SLI_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK; - } else { - sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_ACK); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IN_PROGRESS; - } - if (sl_i2c_instance->rstart == RSTART_WRITE_INPROGRESS) { - sl_hal_i2c_enable_interrupts(i2c_base_addr, I2C_IEN_TXC); - } - break; + sl_interrupt_manager_clear_irq_pending(i2c_irq); + sl_interrupt_manager_enable_irq(i2c_irq); + return SL_STATUS_OK; +} - case SLI_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK: - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE) { - sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_ACK); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IN_PROGRESS; - } else if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - sl_hal_i2c_start_cmd(i2c_base_addr); - sl_hal_i2c_tx(i2c_base_addr, sl_i2c_instance->addr_buffer[2]); - sl_i2c_instance->state = SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK; - } - break; +/***************************************************************************//** + * Safely stops all active DMA transfers associated with the given I2C handle. + * + * @details + * This static helper function checks if there are any ongoing DMA transfers + * (both TX and RX) for the specified I2C handle. If active transfers are found, + * it stops them to ensure a clean state for subsequent operations or shutdown. + * + * @param[in] i2c_handle Pointer to the I2C handle structure containing DMA channel information. + ******************************************************************************/ +static void stop_active_dma_transfers(sl_i2c_handle_t *i2c_handle) +{ + bool tx_active = false, rx_active = false; - case SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK: - if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_ACK); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IN_PROGRESS; - } - break; + // Check and stop TX DMA if active + if (DMADRV_TransferActive(i2c_handle->dma_channel.dma_tx_channel, &tx_active) == ECODE_EMDRV_DMADRV_OK) { + if (tx_active) { + DMADRV_StopTransfer(i2c_handle->dma_channel.dma_tx_channel); } - } else { - if (pending_irq & I2C_IF_ARBLOST) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_ARBITRATION_LOST; - } else if (pending_irq & I2C_IF_BUSERR) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_BUS_ERROR; + } + + // Check and stop RX DMA if active + if (DMADRV_TransferActive(i2c_handle->dma_channel.dma_rx_channel, &rx_active) == ECODE_EMDRV_DMADRV_OK) { + if (rx_active) { + DMADRV_StopTransfer(i2c_handle->dma_channel.dma_rx_channel); } - sl_i2c_instance->state = SLI_I2C_STATE_ERROR; - sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE) { - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_tx_channel); - } else if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_rx_channel); - } - // Abort on error - (i2c_base_addr)->CMD = I2C_CMD_ABORT; } } /***************************************************************************//** - * Function called by the IRQHandlers for handling the interrupts for non blocking mode follower + * Setup DMA transfer for I2C leader mode non-blocking operations. + * + * @details + * Configures DMA channels and descriptors specifically for I2C + * leader mode non-blocking transfers. It handles leader-initiated write and + * read operations to communicate with follower devices on the I2C bus. + * + * Leader mode operations supported: + * - Leader write: Send data to follower device + * - Leader read: Request and receive data from follower device + * - Both 7-bit and 10-bit follower addressing + * + * @param[in] i2c_handle Pointer to the I2C handle structure containing: + * - I2C peripheral instance (must be in leader mode) + * - Transfer direction (SL_I2C_WRITE or SL_I2C_READ) + * - Follower address (7-bit or 10-bit) + * - Buffer information (TX/RX buffers and lengths) + * - DMA channel allocation * - * @details This function is called when I2C interrupts are handled by the IRQHandlers. - * Function gets pending interrupt flags and performs the necessary actions - * according to the pending interrupt flags. + * @return Status code indicating the result of the operation. * - * @param sl_i2c_instance Pointer to the I2C instance structure. + * @note + * - This function is ONLY for I2C leader mode operations + * - Leader initiates communication by sending follower address + * - DMA channel interrupts are disabled; completion handled via I2C interrupts + * + * @warning + * - Must be called with i2c_handle->operating_mode == SL_I2C_LEADER_MODE + * - DMA channels must be pre-allocated before calling this function ******************************************************************************/ -void sli_i2c_follower_dispatch_interrupt(sli_i2c_instance_t *sl_i2c_instance) +static sl_status_t i2c_setup_leader_non_blocking_dma_transfer(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len) { - I2C_TypeDef *i2c_base_addr = sl_i2c_instance->i2c_base_addr; - uint32_t pending_irq = sl_hal_i2c_get_enabled_pending_interrupts(i2c_base_addr); + sl_status_t status; + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + DMADRV_PeripheralSignal_t dma_tx_trigger_source, dma_rx_trigger_source; + sl_i2c_dma_channel_info_t *dma_channel = &i2c_handle->dma_channel; - if (pending_irq & I2C_IF_SSTOP) { - sl_hal_i2c_disable_interrupts(i2c_base_addr, _I2C_IEN_MASK); - sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); + status = i2c_get_dma_trigger_signals(i2c_handle, &dma_tx_trigger_source, &dma_rx_trigger_source); + if (status != SL_STATUS_OK) { + return status; + } - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE) { - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_tx_channel); - } else if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_rx_channel); + bool is_10bit_addr = (i2c_handle->follower_address > 0x7F); + uint16_t follower_address = (i2c_handle->follower_address << 1); + uint8_t addr_buffer_count = 0; + + if (is_10bit_addr) { + // 10-bit address + i2c_handle->addr_buffer[0] = ((follower_address >> 8) & 0x06) | SL_I2C_FIRST_BYTE_10BIT_ADDR_MASK; + i2c_handle->addr_buffer[1] = (uint8_t)(follower_address & 0xFF); + addr_buffer_count = 2; + } else { + // 7-bit address + uint8_t addr = follower_address & SL_I2C_7BIT_FOLLOWER_ADDRESS_MASK; + if (i2c_handle->transfer_direction == SL_I2C_READ) { + addr |= 1; } + i2c_handle->addr_buffer[0] = addr; + addr_buffer_count = 1; + } + + sl_hal_i2c_flush_buffers(i2c_base_addr); + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); + sl_hal_i2c_enable_interrupts(i2c_base_addr, (I2C_IEN_ACK | I2C_IEN_NACK | I2C_IEN_MSTOP | SL_HAL_I2C_IEN_ERRORS)); + i2c_handle->state = SL_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK; + i2c_handle->event = SL_I2C_EVENT_IDLE; - (i2c_base_addr)->CTRL = _I2C_CTRL_RESETVALUE; +#if defined(EMDRV_DMADRV_LDMA) + LDMA_TransferCfg_t tx_cfg = LDMA_TRANSFER_CFG_PERIPHERAL(dma_tx_trigger_source); + LDMA_TransferCfg_t rx_cfg = LDMA_TRANSFER_CFG_PERIPHERAL(dma_rx_trigger_source); + + // Series 2: Disable specific channel interrupts only + LDMA_IntDisable(1 << dma_channel->dma_tx_channel); + LDMA_IntDisable(1 << dma_channel->dma_rx_channel); - if (sl_i2c_instance->transfer_event == SL_I2C_EVENT_IN_PROGRESS) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_COMPLETED; + if (i2c_handle->transfer_direction == SL_I2C_WRITE || i2c_handle->transfer_direction == SL_I2C_WRITE_READ) { + if (i2c_handle->transfer_direction == SL_I2C_WRITE) { + i2c_base_addr->CTRL_SET = I2C_CTRL_AUTOSE; + } else { + sl_hal_i2c_enable_interrupts(i2c_base_addr, I2C_IEN_TXC); } - } else if (pending_irq & I2C_IF_ADDR) { - (i2c_base_addr)->CTRL_SET = I2C_CTRL_AUTOACK; - sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); - sl_hal_i2c_enable_interrupts(i2c_base_addr, (I2C_IEN_NACK | I2C_IEN_RXDATAV | I2C_IEN_SSTOP)); - - switch (sl_i2c_instance->state) { - case SLI_I2C_STATE_ADDRESS_MATCH: - if (sl_i2c_instance->is_10bit_addr) { - sl_i2c_instance->state = SLI_I2C_STATE_10BIT_ADDRESS_MATCH; - } else { - sl_hal_i2c_disable_interrupts(i2c_base_addr, (I2C_IEN_ADDR | I2C_IEN_RXDATAV)); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IN_PROGRESS; - } - break; + i2c_handle->dma_desc.tx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_M2P_BYTE((void*)(i2c_handle->addr_buffer), &((i2c_base_addr)->TXDATA), addr_buffer_count, 1); + i2c_handle->dma_desc.tx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_M2P_BYTE((void*)(tx_buffer), &((i2c_base_addr)->TXDATA), tx_len); + } + if (i2c_handle->transfer_direction == SL_I2C_READ || i2c_handle->transfer_direction == SL_I2C_WRITE_READ) { + if (i2c_handle->transfer_direction == SL_I2C_READ) { + i2c_handle->dma_desc.tx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_M2P_BYTE((void*)(i2c_handle->addr_buffer), &((i2c_base_addr)->TXDATA), addr_buffer_count); + } + if (rx_len > 1) { + i2c_base_addr->CTRL_SET = I2C_CTRL_AUTOACK; + i2c_handle->dma_desc.rx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&i2c_base_addr->RXDATA, (void*)(rx_buffer), rx_len - 1, 1); + i2c_handle->dma_desc.rx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)&i2c_base_addr->CTRL_CLR, 1); + i2c_handle->dma_desc.rx_desc[2] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&i2c_base_addr->RXDATA, (void*)(rx_buffer + rx_len - 1), 1, 1); + i2c_handle->dma_desc.rx_desc[3] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CMD_NACK | I2C_CMD_STOP, (uint32_t)&i2c_base_addr->CMD); + } else { + i2c_handle->dma_desc.rx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&i2c_base_addr->RXDATA, (void*)(rx_buffer), 1, 1); + i2c_handle->dma_desc.rx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_WRITE((I2C_CMD_NACK | I2C_CMD_STOP), (uint32_t)(&i2c_base_addr->CMD)); + } + } +#elif defined(EMDRV_DMADRV_LDMA_S3) + sl_hal_ldma_transfer_config_t tx_cfg = SL_HAL_LDMA_TRANSFER_CFG_PERIPHERAL(dma_tx_trigger_source); + sl_hal_ldma_transfer_config_t rx_cfg = SL_HAL_LDMA_TRANSFER_CFG_PERIPHERAL(dma_rx_trigger_source); - case SLI_I2C_STATE_REP_ADDR_MATCH: - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE) { - sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_ADDR); - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IN_PROGRESS; - } - break; + // Series 3: Disable specific channel interrupts only + sl_hal_ldma_disable_interrupts(LDMA0, (1 << dma_channel->dma_tx_channel)); + sl_hal_ldma_disable_interrupts(LDMA0, (1 << dma_channel->dma_rx_channel)); + + if (i2c_handle->transfer_direction == SL_I2C_WRITE || i2c_handle->transfer_direction == SL_I2C_WRITE_READ) { + if (i2c_handle->transfer_direction == SL_I2C_WRITE) { + i2c_base_addr->CTRL_SET = I2C_CTRL_AUTOSE; + } else { + sl_hal_i2c_enable_interrupts(i2c_base_addr, I2C_IEN_TXC); } - } else if (pending_irq & I2C_IF_RXDATAV) { - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_RXDATAV); - sl_hal_i2c_disable_interrupts(i2c_base_addr, I2C_IEN_RXDATAV); - if (sl_i2c_instance->state == SLI_I2C_STATE_10BIT_ADDRESS_MATCH) { - if ((sl_i2c_instance->follower_address & 0XFF) == sl_i2c_instance->addr_buffer[1]) { - if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_IN_PROGRESS; - } else if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE) { - sl_i2c_instance->state = SLI_I2C_STATE_REP_ADDR_MATCH; - } - } else { - sl_hal_i2c_send_nack(i2c_base_addr); - } + i2c_handle->dma_desc.tx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)(i2c_handle->addr_buffer), &((i2c_base_addr)->TXDATA), addr_buffer_count, 1); + i2c_handle->dma_desc.tx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)(tx_buffer), &((i2c_base_addr)->TXDATA), tx_len); + } + if (i2c_handle->transfer_direction == SL_I2C_READ || i2c_handle->transfer_direction == SL_I2C_WRITE_READ) { + if (i2c_handle->transfer_direction == SL_I2C_READ) { + i2c_handle->dma_desc.tx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)(i2c_handle->addr_buffer), &((i2c_base_addr)->TXDATA), addr_buffer_count); } - } else if (pending_irq & I2C_IF_NACK) { - sl_hal_i2c_clear_interrupts(i2c_base_addr, I2C_IF_NACK); - } else { - if (pending_irq & I2C_IF_ARBLOST) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_ARBITRATION_LOST; - } else if (pending_irq & I2C_IF_BUSERR) { - sl_i2c_instance->transfer_event = SL_I2C_EVENT_BUS_ERROR; + + if (rx_len > 1) { + i2c_base_addr->CTRL_SET = I2C_CTRL_AUTOACK; + i2c_handle->dma_desc.rx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &i2c_base_addr->RXDATA, (void*)(rx_buffer), rx_len - 1, 1); + i2c_handle->dma_desc.rx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)&i2c_base_addr->CTRL_CLR, 1); + i2c_handle->dma_desc.rx_desc[2] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &i2c_base_addr->RXDATA, (void*)(rx_buffer + rx_len - 1), 1, 1); + i2c_handle->dma_desc.rx_desc[3] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CMD_NACK | I2C_CMD_STOP, (uint32_t)&i2c_base_addr->CMD); + } else { + i2c_handle->dma_desc.rx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &i2c_base_addr->RXDATA, (void*)(rx_buffer), 1, 1); + i2c_handle->dma_desc.rx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_WRITE((I2C_CMD_NACK | I2C_CMD_STOP), (uint32_t)(&i2c_base_addr->CMD)); } - sl_i2c_instance->state = SLI_I2C_STATE_ERROR; - sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); - if (sl_i2c_instance->transfer_seq == SL_I2C_WRITE) { - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_tx_channel); - } else if (sl_i2c_instance->transfer_seq == SL_I2C_READ) { - DMADRV_StopTransfer(sl_i2c_instance->dma_channel.dma_rx_channel); - } - // Abort on error - (i2c_base_addr)->CMD = I2C_CMD_ABORT; } +#endif + + DMADRV_LdmaStartTransfer(dma_channel->dma_tx_channel, + &tx_cfg, + (void*)&(i2c_handle->dma_desc.tx_desc), + NULL, NULL); + if (i2c_handle->transfer_direction == SL_I2C_READ || i2c_handle->transfer_direction == SL_I2C_WRITE_READ) { + DMADRV_LdmaStartTransfer(dma_channel->dma_rx_channel, + &rx_cfg, + (void*)&(i2c_handle->dma_desc.rx_desc), + NULL, NULL); + } + + sl_hal_i2c_start_cmd(i2c_base_addr); + return SL_STATUS_OK; +} + +/***************************************************************************//** + * Sets up a non-blocking DMA transfer for the I2C follower device. + * + * @details + * Configures the I2C peripheral to perform a DMA-based data transfer + * in follower (slave) mode without blocking the CPU. Prepares the necessary DMA + * channels and I2C settings to enable efficient data movement between the I2C bus + * and memory. + * + * @param[in] i2c_handle Pointer to the I2C handle structure containing configuration + * and state information for the I2C peripheral. + * + * @return + * - SL_STATUS_OK if the setup was successful. + * - Appropriate error code otherwise. + * + * @note + * - The caller must ensure that the DMA and I2C peripherals are properly initialized + * before calling this function. + * - This function does not start the transfer; it only sets up the necessary resources. + **********************************************************************************/ +static sl_status_t i2c_setup_follower_non_blocking_dma_transfer(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len) +{ + sl_status_t status; + I2C_TypeDef *i2c_base_addr = sl_device_peripheral_i2c_get_base_addr(i2c_handle->i2c_peripheral); + uint8_t *addr_buffer = i2c_handle->addr_buffer, addr_buffer_count = 0; + bool is_10bit_addr = (i2c_handle->follower_address > 0x7F); + + DMADRV_PeripheralSignal_t dma_tx_trigger_source, dma_rx_trigger_source; + sl_i2c_dma_channel_info_t *dma_channel = &i2c_handle->dma_channel; + status = i2c_get_dma_trigger_signals(i2c_handle, &dma_tx_trigger_source, &dma_rx_trigger_source); + if (status != SL_STATUS_OK) { + return status; + } + + sl_hal_i2c_flush_buffers(i2c_base_addr); + sl_hal_i2c_clear_interrupts(i2c_base_addr, _I2C_IF_MASK); + sl_hal_i2c_enable_interrupts(i2c_base_addr, (I2C_IEN_ADDR | SL_HAL_I2C_IEN_ERRORS)); + i2c_handle->state = SL_I2C_STATE_ADDRESS_MATCH; + i2c_handle->event = SL_I2C_EVENT_IDLE; + +#if defined(EMDRV_DMADRV_LDMA) + LDMA_TransferCfg_t tx_cfg = LDMA_TRANSFER_CFG_PERIPHERAL(dma_tx_trigger_source); + LDMA_TransferCfg_t rx_cfg = LDMA_TRANSFER_CFG_PERIPHERAL(dma_rx_trigger_source); + + // Series 2: Disable specific channel interrupts only + LDMA_IntDisable(1 << dma_channel->dma_tx_channel); + LDMA_IntDisable(1 << dma_channel->dma_rx_channel); + + if (i2c_handle->transfer_direction == SL_I2C_READ) { + addr_buffer_count = is_10bit_addr ? 2 : 1; + + i2c_handle->dma_desc.rx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)(addr_buffer), addr_buffer_count, 1); + i2c_handle->dma_desc.rx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)rx_buffer, rx_len, 1); + i2c_handle->dma_desc.rx_desc[2] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); + i2c_handle->dma_desc.rx_desc[3] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CMD_NACK, (uint32_t)(&(i2c_base_addr)->CMD)); + } else { // WRITE + addr_buffer_count = is_10bit_addr ? 3 : 1; + + i2c_handle->dma_desc.rx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_P2M_BYTE(&((i2c_base_addr)->RXDATA), (void*)(addr_buffer), addr_buffer_count); + i2c_handle->dma_desc.tx_desc[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_M2P_BYTE((void*)tx_buffer, &((i2c_base_addr)->TXDATA), tx_len, 1); + i2c_handle->dma_desc.tx_desc[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_M2P_BYTE((void*)temp_buffer, &((i2c_base_addr)->TXDATA), 1, 0); + } +#elif defined(EMDRV_DMADRV_LDMA_S3) + sl_hal_ldma_transfer_config_t tx_cfg = SL_HAL_LDMA_TRANSFER_CFG_PERIPHERAL(dma_tx_trigger_source); + sl_hal_ldma_transfer_config_t rx_cfg = SL_HAL_LDMA_TRANSFER_CFG_PERIPHERAL(dma_rx_trigger_source); + + // Series 3: Disable specific channel interrupts only + sl_hal_ldma_disable_interrupts(LDMA0, (1 << dma_channel->dma_tx_channel)); + sl_hal_ldma_disable_interrupts(LDMA0, (1 << dma_channel->dma_rx_channel)); + + if (i2c_handle->transfer_direction == SL_I2C_READ) { + addr_buffer_count = is_10bit_addr ? 2 : 1; + + i2c_handle->dma_desc.rx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)(addr_buffer), addr_buffer_count, 1); + i2c_handle->dma_desc.rx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)rx_buffer, rx_len, 1); + i2c_handle->dma_desc.rx_desc[2] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_WRITE(I2C_CTRL_AUTOACK, (uint32_t)(&(i2c_base_addr)->CTRL_CLR), 1); + i2c_handle->dma_desc.rx_desc[3] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_WRITE(I2C_CMD_NACK, (uint32_t)(&(i2c_base_addr)->CMD)); + } else { // WRITE + addr_buffer_count = is_10bit_addr ? 3 : 1; + + i2c_handle->dma_desc.rx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_SINGLE_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, &((i2c_base_addr)->RXDATA), (void*)(addr_buffer), addr_buffer_count); + i2c_handle->dma_desc.tx_desc[0] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)tx_buffer, &((i2c_base_addr)->TXDATA), tx_len, 1); + i2c_handle->dma_desc.tx_desc[1] = (sl_hal_ldma_descriptor_t)SL_HAL_LDMA_DESCRIPTOR_LINKREL_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, (void*)temp_buffer, &((i2c_base_addr)->TXDATA), 1, 0); + } +#endif + + DMADRV_LdmaStartTransfer(dma_channel->dma_rx_channel, + &rx_cfg, + (void*)&(i2c_handle->dma_desc.rx_desc), + NULL, NULL); + if (i2c_handle->transfer_direction == SL_I2C_WRITE) { + DMADRV_LdmaStartTransfer(dma_channel->dma_tx_channel, + &tx_cfg, + (void*)&(i2c_handle->dma_desc.tx_desc), + NULL, NULL); + } + return SL_STATUS_OK; } /***************************************************************************//** * Function called by IRQ handlers to process I2C interrupts. * - * @details This function handles interrupts for I2C. It dispatches - * the interrupt handling based on the operating mode (leader or follower). + * @details Handles interrupts for I2C. It dispatches the interrupt handling + * based on the operating mode (leader or follower). * - * @param[in] sl_i2c_instance Pointer to the I2C instance structure. + * @param[in] i2c_peripheral I2C Inst. ******************************************************************************/ -static void i2c_common_irq_handler(sli_i2c_instance_t *sl_i2c_instance) +static void i2c_common_irq_handler(uint8_t i2c_instance) { - if (sl_i2c_instance->transfer_mode == SLI_I2C_NON_BLOCKING_TRANSFER) { - if (sl_i2c_instance->operating_mode == SL_I2C_LEADER_MODE) { - sli_i2c_leader_dispatch_interrupt(sl_i2c_instance); - } else { // Follower mode - sli_i2c_follower_dispatch_interrupt(sl_i2c_instance); + sl_i2c_handle_t *i2c_handle = i2c_handle_contexts[i2c_instance]; + + if (i2c_handle->operating_mode == SL_I2C_LEADER_MODE) { + sli_i2c_leader_dispatch_interrupt(i2c_handle); + } else { + sli_i2c_follower_dispatch_interrupt(i2c_handle); + } + + if (i2c_handle->event == SL_I2C_EVENT_COMPLETED) { + if (i2c_handle->transfer_complete_callback) { + i2c_handle->transfer_complete_callback(i2c_handle, i2c_handle->user_data); } - if (sl_i2c_instance->transfer_event != SL_I2C_EVENT_IN_PROGRESS) { - if (sl_i2c_instance->callback) { - sl_i2c_instance->callback(sl_i2c_instance->transfer_event, sl_i2c_instance->context); - } + } else if (i2c_handle->event != SL_I2C_EVENT_IN_PROGRESS && i2c_handle->state == SL_I2C_STATE_WAIT_FOR_STOP) { + i2c_handle->state = SL_I2C_STATE_DONE; + if (i2c_handle->event_callback) { + i2c_handle->event_callback(i2c_handle, i2c_handle->event, i2c_handle->user_data); } } } #if defined(I2C0) +/***************************************************************************//** + * @brief + * Interrupt handler for I2C0 peripheral. + * + * @details + * Invoked when an interrupt is triggered by the I2C0 peripheral. + * It retrieves the I2C0 device instance and passes it to the common I2C IRQ handler. + ******************************************************************************/ void I2C0_IRQHandler(void) { - sli_i2c_instance_t *sl_i2c_instance = &sl_i2c_device_instance[0]; - i2c_common_irq_handler(sl_i2c_instance); + i2c_common_irq_handler(0); } #endif // I2C0 #if defined(I2C1) +/***************************************************************************//** + * @brief + * Interrupt handler for I2C1 peripheral. + * + * @details + * Invoked when an interrupt is triggered by the I2C1 peripheral. + * It retrieves the I2C1 device instance and passes it to the common I2C IRQ handler. + ******************************************************************************/ void I2C1_IRQHandler(void) { - sli_i2c_instance_t *sl_i2c_instance = &sl_i2c_device_instance[1]; - i2c_common_irq_handler(sl_i2c_instance); + i2c_common_irq_handler(1); } #endif // I2C1 #if defined(I2C2) +/***************************************************************************//** + * @brief + * Interrupt handler for I2C2 peripheral. + * + * @details + * Invoked when an interrupt is triggered by the I2C2 peripheral. + * It retrieves the I2C2 device instance and passes it to the common I2C IRQ handler. + ******************************************************************************/ void I2C2_IRQHandler(void) { - sli_i2c_instance_t *sl_i2c_instance = &sl_i2c_device_instance[2]; - i2c_common_irq_handler(sl_i2c_instance); + i2c_common_irq_handler(2); } #endif // I2C2 From c323db43dfbe911233653e72c0661e1fff1ce7ba Mon Sep 17 00:00:00 2001 From: fimohame Date: Tue, 30 Sep 2025 23:39:02 +0530 Subject: [PATCH 2/4] simplicity_sdk: Patch for sl_i2c.h file Updating the SL_I2C.h driver file to incorporate design enhancements from the GSDK I2C implementation Upstream-Status: Pending Signed-off-by: S Mohamed Fiaz --- .../platform/driver/i2c/inc/sl_i2c.h | 686 +++++++++++++----- 1 file changed, 503 insertions(+), 183 deletions(-) diff --git a/simplicity_sdk/platform/driver/i2c/inc/sl_i2c.h b/simplicity_sdk/platform/driver/i2c/inc/sl_i2c.h index dc9f976c..203525d5 100644 --- a/simplicity_sdk/platform/driver/i2c/inc/sl_i2c.h +++ b/simplicity_sdk/platform/driver/i2c/inc/sl_i2c.h @@ -31,16 +31,16 @@ #ifndef SL_I2C_H #define SL_I2C_H -#ifdef __cplusplus -extern "C" { -#endif - +#include +#include "sl_status.h" +#include "sl_enum.h" #include "sl_device_peripheral.h" #include "sl_device_i2c.h" #include "sl_device_gpio.h" +#include "dmadrv.h" -#if defined(SL_COMPONENT_CATALOG_PRESENT) -#include "sl_component_catalog.h" +#ifdef __cplusplus +extern "C" { #endif /* *INDENT-OFF* */ @@ -57,54 +57,123 @@ extern "C" { // ***************************************************************************** /* *INDENT-ON* */ +/******************************************************************************* + ******************************** DEFINES ************************************ + ******************************************************************************/ +#define SL_I2C_DMA_MAX_TX_DESCRIPTOR_COUNT 2 +#define SL_I2C_DMA_MAX_RX_DESCRIPTOR_COUNT 4 + /******************************************************************************* ******************************** ENUMS ************************************ ******************************************************************************/ /// Freq mode enum SL_ENUM(sl_i2c_freq_mode_t) { - SL_I2C_FREQ_STANDARD_MODE, /// Standard mode max frequency assuming using 4:4 ratio for Nlow:Nhigh. - SL_I2C_FREQ_FAST_MODE, /// Fast mode max frequency assuming using 6:3 ratio for Nlow:Nhigh. - SL_I2C_FREQ_FASTPLUS_MODE, /// Fast mode+ max frequency assuming using 11:6 ratio for Nlow:Nhigh. + SL_I2C_FREQ_STANDARD_MODE, /// Standard mode (up to 100 kHz) assuming using 4:4 ratio for Nlow:Nhigh + SL_I2C_FREQ_FAST_MODE, /// Fast mode (up to 400 kHz) assuming using 6:3 ratio for Nlow:Nhigh + SL_I2C_FREQ_FASTPLUS_MODE, /// Fast mode+ (up to 1 MHz) assuming using 11:6 ratio for Nlow:Nhigh }; -/// Transaction mode enum -SL_ENUM(sl_i2c_transfer_seq_t) { - SL_I2C_WRITE = (1 << 0), /// Sending the data - SL_I2C_READ = (1 << 1), /// Reading the data - SL_I2C_WRITE_READ = (2 << 1), /// Sending and reading the data +/// Transfer direction enum +SL_ENUM(sl_i2c_transfer_direction_t) { + SL_I2C_WRITE = 0, /// Write: Send data to the I2C bus + SL_I2C_READ = 1, /// Read: Receive data from the I2C bus + SL_I2C_WRITE_READ = 2, /// Write followed by Read: Send then receive data in a single transaction }; -/// Transfer event enum +/// I2C Transfer event enum SL_ENUM(sl_i2c_event_t) { SL_I2C_EVENT_IN_PROGRESS = 0, /// Transfer in progress SL_I2C_EVENT_COMPLETED = 1, /// Transfer completed successfully - SL_I2C_EVENT_NACK_RECEIVED = 2, /// NACK received - SL_I2C_EVENT_ARBITRATION_LOST = 3, /// Arbitration lost - SL_I2C_EVENT_BUS_ERROR = 4, /// Bus error occurred - SL_I2C_EVENT_STOP = 5, /// Transfer stopped - SL_I2C_EVENT_SW_FAULT = 6, /// Transfer fault due to software - SL_I2C_EVENT_IDLE = 7, /// Instance is idle - SL_I2C_EVENT_INVALID_ADDR = 8, /// Address invalid error - SL_I2C_EVENT_TIMEOUT = 9, /// Timeout + SL_I2C_EVENT_ADDR_NACK = 2, /// Address byte not acknowledged + SL_I2C_EVENT_DATA_NACK = 3, /// Data byte not acknowledged + SL_I2C_EVENT_ARBITRATION_LOST = 4, /// Arbitration lost + SL_I2C_EVENT_BUS_ERROR = 5, /// Bus error occurred + SL_I2C_EVENT_CLOCK_TIMEOUT = 6, /// Clock timeout + SL_I2C_EVENT_STOP = 7, /// Transfer stopped + SL_I2C_EVENT_SW_FAULT = 8, /// Transfer fault due to software + SL_I2C_EVENT_IDLE = 9, /// Instance is idle + SL_I2C_EVENT_INVALID_ADDR = 10, /// Address invalid error + SL_I2C_EVENT_TIMEOUT = 11, /// Timeout + SL_I2C_EVENT_BUFFER_FULL = 12 /// Buffer full, cannot receive more data +}; + +/// I2C state enum +SL_ENUM(sl_i2c_transaction_state_t) { + SL_I2C_STATE_ERROR = 0, /// Indicates an error occurred. + SL_I2C_STATE_IDLE = 1, /// I2C is idle. + SL_I2C_STATE_SEND_START_AND_ADDR = 2, /// Send start and address byte. + SL_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK = 3, /// Wait for ACK/NACK on address byte. + SL_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK = 4, /// Wait for ACK/NACK on 2nd byte (10-bit). + SL_I2C_STATE_ADDRESS_MATCH = 5, /// Wait for address matching. + SL_I2C_STATE_10BIT_ADDRESS_MATCH = 6, /// Wait for 10-bit address match. + SL_I2C_STATE_SEND_REPEATED_START_AND_ADDR = 7, /// Send repeated start and address byte. + SL_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK = 8, /// Wait for ACK/NACK for repeated start. + SL_I2C_STATE_REP_ADDR_MATCH = 9, /// Wait for repeated start and address match. + SL_I2C_STATE_SEND_DATA = 10, /// Send data byte. + SL_I2C_STATE_DATA_WAIT_FOR_ACK_OR_NACK = 11, /// Wait for ACK or NACK after sending data. + SL_I2C_STATE_RECEIVE_DATA = 12, /// Data to be received. + SL_I2C_STATE_SEND_STOP = 13, /// Send stop signal. + SL_I2C_STATE_WAIT_FOR_STOP = 14, /// Wait for stop condition. + SL_I2C_STATE_DONE = 15 /// Transfer completed. }; +/******************************************************************************* + ******************************* TYPEDEFS ********************************** + ******************************************************************************/ + +typedef struct sl_i2c_handle_t sl_i2c_handle_t; ///< Forward declaration of I2C handle type + +/***************************************************************************//** + * @brief Transfer Complete Callback + * + * @note Invoked exactly once when an asynchronous I²C operation completes + * successfully (no errors). This applies to: + * - Transmit (TX) + * - Receive (RX) + * - Combined Transmit-Receive (TX→RX / repeated-start) + * + * @param[in] i2c_handle Pointer to the I2C driver handle. + * @param[in] user_data User-defined data provided during the asynchronous + * transaction + * + * @return Status code. + ******************************************************************************/ +typedef sl_status_t (*sl_i2c_transfer_complete_callback_t)(sl_i2c_handle_t *i2c_handle, void *user_data); + +/***************************************************************************//** + * @brief I2C Event Callback + * + * @note Invoked on I2C events other than transfer completion. + * + * @param[in] i2c_handle Pointer to the I2C driver handle. + * @param[in] event Event type (see @ref sl_i2c_event_t). + * @param[in] user_data User-defined data provided during the asynchronous transaction. + * + * @return Status code. + ******************************************************************************/ +typedef sl_status_t (*sl_i2c_event_callback_t)(sl_i2c_handle_t *i2c_handle, sl_i2c_event_t event, void *user_data); + /******************************************************************************* ******************************* STRUCTS *********************************** ******************************************************************************/ -/// FIFO Threshold struct -#if defined(SL_CATALOG_I2C_FIFO_PRESENT) -typedef struct { - sl_i2c_fifo_threshold_t tx_threshold_val; /// Transmit FIFO Threshold value - sl_i2c_fifo_threshold_t rx_threshold_val; /// Receive FIFO Threshold value -} sl_i2c_fifo_threshold_level_t; -#endif /// I2C Tx and Rx DMA Channel and trigger source. typedef struct { - unsigned int dma_tx_channel; /// DMA Channel assigned for Tx operations - unsigned int dma_rx_channel; /// DMA Channel assigned for Rx operations + unsigned int dma_tx_channel; /// DMA Channel assigned for Tx operations + unsigned int dma_rx_channel; /// DMA Channel assigned for Rx operations } sl_i2c_dma_channel_info_t; +/// DMA descriptors for I2C transfers +typedef struct { +#if defined(EMDRV_DMADRV_LDMA) + LDMA_Descriptor_t tx_desc[SL_I2C_DMA_MAX_TX_DESCRIPTOR_COUNT]; + LDMA_Descriptor_t rx_desc[SL_I2C_DMA_MAX_RX_DESCRIPTOR_COUNT]; +#elif defined(EMDRV_DMADRV_LDMA_S3) + sl_hal_ldma_descriptor_t tx_desc[SL_I2C_DMA_MAX_TX_DESCRIPTOR_COUNT]; + sl_hal_ldma_descriptor_t rx_desc[SL_I2C_DMA_MAX_RX_DESCRIPTOR_COUNT]; +#endif +} sl_i2c_dma_descriptors_t; + /** * @struct sl_i2c_init_params_t * @brief Initialization parameters for an I2C Driver instance. @@ -116,216 +185,467 @@ typedef struct { * Follower Mode : Used to set the I2C device's own (self) address. */ typedef struct { - I2C_TypeDef *i2c_base_addr; ///< I2C Module Instance + sl_peripheral_t i2c_peripheral; ///< I2C Peripheral Instance sl_i2c_operating_mode_t operating_mode; ///< Operating mode: Leader or Follower - sl_i2c_freq_mode_t freq_mode; ///< I2C Speed mode (Standard, Fast, etc.) - uint16_t follower_address; ///< Follower address (see usage note above) + sl_i2c_freq_mode_t frequency_mode; ///< I2C Speed mode (Standard, Fast, etc.) sl_gpio_t scl_gpio; ///< SCL GPIO Port and Pin (Serial Clock Line) sl_gpio_t sda_gpio; ///< SDA GPIO Port and Pin (Serial Data Line) -#if defined(SL_CATALOG_I2C_FIFO_PRESENT) - sl_i2c_fifo_threshold_level_t *fifo_threshold; ///< Transmit and Receive FIFO Threshold levels -#endif } sl_i2c_init_params_t; +/** + * @struct sl_i2c_handle_t + * @brief Represents an I2C instance handle. + * Contains configuration, buffers, state, and callbacks. + * + * @warning + * This structure is defined in the public header for driver implementation + * purposes only. Applications must NOT access, modify, or rely upon any + * members of this structure directly. + * Use only the provided API functions to interact with I2C handles. + * + * @note Usage of `follower_address` depends on the operating mode: + * Leader Mode : Used to specify the address of the follower device. + * Follower Mode : Used to set the I2C device's own (self) address. + */ +typedef struct sl_i2c_handle_t { + // Peripheral and configuration + sl_peripheral_t i2c_peripheral; ///< I2C Peripheral Instance + sl_i2c_operating_mode_t operating_mode; ///< Operating mode: Leader or Follower + sl_i2c_freq_mode_t frequency_mode; ///< I2C Speed mode (Standard, Fast, etc.) + uint16_t follower_address; ///< Follower address (see usage note above) + sl_gpio_t scl_gpio; ///< SCL GPIO Port and Pin (Serial Clock Line) + sl_gpio_t sda_gpio; ///< SDA GPIO Port and Pin (Serial Data Line) + + // DMA configuration + sl_i2c_dma_channel_info_t dma_channel; ///< DMA channel info for Tx/Rx + sl_i2c_dma_descriptors_t dma_desc; ///< DMA descriptors for I2C transfers + + // Buffer information + uint32_t tx_offset; ///< Number of bytes transferred (TX) + uint32_t rx_offset; ///< Number of bytes received (RX) + + // Transaction state and event + sl_i2c_transaction_state_t state; ///< Current transaction state + sl_i2c_event_t event; ///< Current event + sl_i2c_transfer_direction_t transfer_direction; ///< Transfer direction (Read/Write) + uint8_t addr_buffer[3]; ///< Address buffer. + + // Callbacks and user data + sl_i2c_transfer_complete_callback_t transfer_complete_callback; ///< Transfer complete callback + sl_i2c_event_callback_t event_callback; ///< Event callback + void* user_data; ///< User defined data +} sl_i2c_handle_t; + /******************************************************************************* - ******************************* TYPEDEFS ********************************** + ***************************** PROTOTYPES ********************************** ******************************************************************************/ -typedef void *sl_i2c_handle_t; /// i2c instance handle /***************************************************************************//** - * I2C interrupt callback function pointer. + * Initializes the I2C Module. + * + * @details Configures the I2C peripheral with the specified + * initialization parameters and prepares it for operation. * - * @param[in] transfer_event Transfer Event. - * @param[in] context User-defined context or data. + * @param[out] i2c_handle A pointer to the I2C instance handle. + * @param[in] init_params A pointer to initialization parameters. * - * @return return status. + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_ALREADY_INITIALIZED if already initialized. + * - SL_STATUS_INVALID_PARAMETER for invalid config. + * - SL_STATUS_NOT_SUPPORTED if peripheral is not valid. + * - SL_STATUS_ALLOCATION_FAILED if DMA allocation fails. ******************************************************************************/ -typedef sl_status_t (*sl_i2c_irq_callback_t)(sl_i2c_event_t transfer_event, void *context); +sl_status_t sl_i2c_init(sl_i2c_handle_t *i2c_handle, + const sl_i2c_init_params_t *init_params); /***************************************************************************//** - * This function initializes the I2C Module. + * Deinitializes and resets the I2C instance. * - * @param[in] p_init_params A pointer to Init Params. - * @param[out] i2c_handle I2C instance handle. + * @details + * Disables interrupts, DMA, clocks, and resets hardware and GPIOs. * - * @return return status. + * @param[in] i2c_handle Pointer to the I2C instance handle. + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if handle is NULL. ******************************************************************************/ -sl_status_t sl_i2c_init(const sl_i2c_init_params_t *init_params, - sl_i2c_handle_t *i2c_handle); +sl_status_t sl_i2c_deinit(sl_i2c_handle_t *i2c_handle); /***************************************************************************//** - * This function Deinitializes and resets the I2C Instance. + * Sets the frequency of the I2C bus. * - * @param[in] i2c_handle I2C instance handle. + * @param[in] i2c_handle A pointer to the I2C instance handle. + * @param[in] frequency_mode Operating speed mode (e.g., Standard, Fast, Fast-Plus). * - * @return return status. + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if handle is NULL. + * - SL_STATUS_INVALID_PARAMETER if mode is invalid. ******************************************************************************/ -sl_status_t sl_i2c_deinit(sl_i2c_handle_t i2c_handle); +sl_status_t sl_i2c_set_frequency(sl_i2c_handle_t *i2c_handle, + sl_i2c_freq_mode_t frequency_mode); /***************************************************************************//** - * This function sets the frequency of I2C Bus. + * Get the current configured frequency of the I2C module. * - * @param[in] i2c_handle I2C instance handle. - * @param[in] freq_mode Operating Speed. - - * @return return status. - ******************************************************************************/ -sl_status_t sl_i2c_set_frequency(sl_i2c_handle_t i2c_handle, - sl_i2c_freq_mode_t freq_mode); - -/***************************************************************************//** - * This function gets the current frequency of I2C module. + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[out] frequency_mode Pointer to store the current mode. * - * @param[in] i2c_handle I2C instance handle. - * @param[out] frequency Currently configured frequency. - - * @return return status. + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_CONFIGURATION if frequency is out of range. ******************************************************************************/ -sl_status_t sl_i2c_get_frequency(sl_i2c_handle_t i2c_handle, - uint32_t *frequency); +sl_status_t sl_i2c_get_frequency(sl_i2c_handle_t *i2c_handle, + sl_i2c_freq_mode_t *frequency_mode); /***************************************************************************//** - * This API is supported only in Leader mode and it is used to set the follower address. - * It is used in Multi Follower mode (addressing a new follower which has similar configurations set during Init API). + * Set the follower device address for I2C communication. * - * @param[in] i2c_handle I2C instance handle. - * @param[in] follower_address Follower address to which the I2C Leader wants to communicate. + * @details Sets the address of the follower device that the I2C leader + * will communicate with during transactions. + * Validates address ranges and checks for reserved addresses. * - * @return return status. + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] address Follower device address (7-bit or 10-bit). + * @param[in] is_10bit_addr true for 10-bit address, false for 7-bit address. + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if handle is NULL. + * - SL_STATUS_INVALID_MODE if not in follower mode. + * - SL_STATUS_INVALID_PARAMETER if address is invalid. ******************************************************************************/ -sl_status_t sl_i2c_set_follower_address(sl_i2c_handle_t i2c_handle, - uint16_t follower_address); +sl_status_t sl_i2c_set_follower_address(sl_i2c_handle_t *i2c_handle, + uint16_t address, + bool is_10bit_addr); /***************************************************************************//** - * This API is for configuring the DMA for the I2C instance. + * Configures the DMA channels for the I2C instance. * - * @note This is an optional API that can be used only in the case where the - * user wants to use their own DMA channels and overwrite the existing allocated DMA channels. + * @note This is an optional API that allows the user to assign custom DMA channels, + * overriding the default channels allocated by the driver. * - * @param[in] i2c_handle I2C instance handle. - * @param[in] dma_channel DMA Channel Info. + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] dma_channel Pointer to the DMA channel info structure. * - * @return return status. + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_PARAMETER if channel info is invalid. ******************************************************************************/ -sl_status_t sl_i2c_configure_dma(sl_i2c_handle_t i2c_handle, - sl_i2c_dma_channel_info_t dma_channel); +sl_status_t sl_i2c_configure_dma(sl_i2c_handle_t *i2c_handle, + sl_i2c_dma_channel_info_t *dma_channel); /***************************************************************************//** - * Leader Mode : This function is used to send the data to the follower configured during Init API. - * Follower Mode : This function is used to send the data to the addressed I2C Leader. - * Returns on transfer complete or on error. - * - * @param[in] i2c_handle I2C Instance handle. - * @param[in] tx_buffer A pointer to the transmit data buffer. - * @param[in] tx_len Transmit data length. - * @param[in] timeout Timeout of the blocking call to exit the loop (in milliseconds). - * - * @return return status. + * Leader Mode: Send data to the specified follower device (blocking). + * + * @details Transmits data to the follower device. Blocks until the transfer + * completes successfully or an error occurs. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] address Address of the follower device. + * @param[in] tx_buffer Pointer to the transmit data buffer. + * @param[in] tx_len Length of the data to transmit. + * @param[in] timeout Timeout duration in milliseconds (0 = no timeout). + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in leader mode. + * - SL_STATUS_INVALID_PARAMETER if length/address invalid. + * - SL_STATUS_TIMEOUT if operation timed out. + * - SL_STATUS_NOT_FOUND if address NACK received. + * - SL_STATUS_ABORT if data NACK received. + * - SL_STATUS_TRANSMIT or SL_STATUS_IO for bus errors. ******************************************************************************/ -sl_status_t sl_i2c_send_blocking(sl_i2c_handle_t i2c_handle, - const uint8_t *tx_buffer, - uint16_t tx_len, - uint32_t timeout); +sl_status_t sl_i2c_leader_send_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint32_t timeout); /***************************************************************************//** - * Leader Mode : This function is used to receive the data from the follower configured during Init API. - * Follower Mode : This function is used to receive the data from the addressed I2C Leader. - * Returns on transfer complete or on error. - * - * @param[in] i2c_handle I2C Instance handle. - * @param[in] rx_buffer A pointer to the Receive data buffer. - * @param[in] rx_len Receive data length. - * @param[in] timeout Timeout of the blocking call to exit the loop (in milliseconds). - * - * @return return status. + * Leader Mode: Receive data from the specified follower device (blocking). + * + * @details Receives data from the follower device. Blocks until the transfer + * completes successfully or an error occurs. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] address Address of the follower device. + * @param[out] rx_buffer Pointer to the receive data buffer. + * @param[in] rx_len Length of the data to receive. + * @param[in] timeout Timeout duration in milliseconds (0 = no timeout). + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in leader mode. + * - SL_STATUS_INVALID_PARAMETER if length/address invalid. + * - SL_STATUS_TIMEOUT if operation timed out. + * - SL_STATUS_NOT_FOUND if address NACK received. + * - SL_STATUS_ABORT if data NACK received. + * - SL_STATUS_TRANSMIT or SL_STATUS_IO for bus errors. ******************************************************************************/ -sl_status_t sl_i2c_receive_blocking(sl_i2c_handle_t i2c_handle, - uint8_t *rx_buffer, - uint16_t rx_len, - uint32_t timeout); +sl_status_t sl_i2c_leader_receive_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + uint8_t *rx_buffer, + uint32_t rx_len, + uint32_t timeout); /***************************************************************************//** - * This function is used to send and receive the data from the follower configured during Init API. - * This API is supported only in Leader Mode and for blocking transfer. - * Returns on transfer complete or on error. - * - * @param[in] i2c_handle I2C Instance handle. - * @param[in] tx_buffer A pointer to transmit data buffer - * @param[in] tx_len Transmit data length - * @param[in] rx_buffer A pointer to receive data buffer - * @param[in] rx_len Receive data length - * - * @return return status. + * Leader Mode: Send then receive data from the specified follower device (blocking). + * + * @details Performs a combined write-read operation using a repeated start + * condition. Blocks until both operations complete or an error occurs. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] address Address of the follower device. + * @param[in] tx_buffer Pointer to the transmit data buffer. + * @param[in] tx_len Length of the data to transmit. + * @param[out] rx_buffer Pointer to the receive data buffer. + * @param[in] rx_len Length of the data to receive. + * @param[in] timeout Timeout duration in milliseconds (0 = no timeout). + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in leader mode. + * - SL_STATUS_INVALID_PARAMETER if lengths/address invalid. + * - SL_STATUS_TIMEOUT if operation timed out. + * - SL_STATUS_NOT_FOUND if address NACK received. + * - SL_STATUS_ABORT if data NACK received. + * - SL_STATUS_TRANSMIT or SL_STATUS_IO for bus errors. ******************************************************************************/ -sl_status_t sl_i2c_transfer(sl_i2c_handle_t i2c_handle, - const uint8_t *tx_buffer, - uint16_t tx_len, - uint8_t *rx_buffer, - uint16_t rx_len); +sl_status_t sl_i2c_leader_transfer_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len, + uint32_t timeout); /***************************************************************************//** - * Leader Mode : This function uses DMA and Interrupt, to send the data to the follower set up during Init API. - * Follower Mode : This function uses DMA and Interrupt, to send the data to the addressed I2C Leader. - * The user is notified through the provided callback function upon completion. - * - * @param[in] i2c_handle I2C Instance handle. - * @param[in] tx_buffer A pointer to transmit data buffer - * @param[in] tx_len Transmit data length - * @param[in] i2c_callback A callback function on completion. - * @param[in] context A pointer to user-defined data for callback. - * - * @return return status. + * Leader Mode: Send data to the specified follower device (non-blocking). + * + * @details Initiates a data transmission using DMA and interrupts. Returns + * immediately; completion is signaled via registered callbacks. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] address Address of the follower device. + * @param[in] tx_buffer Pointer to the transmit data buffer. + * @param[in] tx_len Length of the data to transmit. + * @param[in] user_data User-defined data passed to callbacks. + * + * @return + * - SL_STATUS_OK if transfer initiated successfully. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in leader mode. + * - SL_STATUS_INVALID_PARAMETER if length/address invalid. + * - Other error codes for DMA/IRQ setup failures. + ******************************************************************************/ +sl_status_t sl_i2c_leader_send_non_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + const uint8_t *tx_buffer, + uint32_t tx_len, + void *user_data); + +/***************************************************************************//** + * Leader Mode: Receive data from the specified follower device (non-blocking). + * + * @details Initiates a data reception using DMA and interrupts. Returns + * immediately; completion is signaled via registered callbacks. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] address Address of the follower device. + * @param[out] rx_buffer Pointer to the receive data buffer. + * @param[in] rx_len Length of the data to receive. + * @param[in] user_data User-defined data passed to callbacks. + * + * @return + * - SL_STATUS_OK if transfer initiated successfully. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in leader mode. + * - SL_STATUS_INVALID_PARAMETER if length/address invalid. + * - Other error codes for DMA/IRQ setup failures. + ******************************************************************************/ +sl_status_t sl_i2c_leader_receive_non_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + uint8_t *rx_buffer, + uint32_t rx_len, + void *user_data); + +/***************************************************************************//** + * Leader Mode: Send then receive data from the specified follower device (non-blocking). + * + * @details Initiates a combined write-read operation using a repeated start + * condition. Uses DMA and interrupts. Returns immediately; completion + * is signaled via registered callbacks. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] address Address of the follower device. + * @param[in] tx_buffer Pointer to the transmit data buffer. + * @param[in] tx_len Length of the data to transmit. + * @param[out] rx_buffer Pointer to the receive data buffer. + * @param[in] rx_len Length of the data to receive. + * @param[in] user_data User-defined data passed to callbacks. + * + * @return + * - SL_STATUS_OK if transfer initiated successfully. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in leader mode. + * - SL_STATUS_INVALID_PARAMETER if lengths/address invalid. + * - Other error codes for DMA/IRQ setup failures. + ******************************************************************************/ +sl_status_t sl_i2c_leader_transfer_non_blocking(sl_i2c_handle_t *i2c_handle, + uint16_t address, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint8_t *rx_buffer, + uint32_t rx_len, + void *user_data); + +/***************************************************************************//** + * Follower Mode: Send data to the I2C leader (blocking). + * + * @details Transmits data when addressed by the leader. Blocks until the + * transfer completes or an error occurs. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] tx_buffer Pointer to the transmit data buffer. + * @param[in] tx_len Number of bytes to transmit. + * @param[in] timeout Timeout duration in milliseconds (0 = no timeout). + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in follower mode. + * - SL_STATUS_INVALID_PARAMETER if length invalid. + * - SL_STATUS_TIMEOUT if operation timed out. + * - SL_STATUS_FULL if buffer full. + * - SL_STATUS_TRANSMIT or SL_STATUS_IO for bus errors. + ******************************************************************************/ +sl_status_t sl_i2c_follower_send_blocking(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + uint32_t timeout); + +/***************************************************************************//** + * Follower Mode: Receive data from the I2C leader (blocking). + * + * @details Receives data when addressed by the leader. Blocks until the + * transfer completes or an error occurs. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[out] rx_buffer Pointer to the receive data buffer. + * @param[in] rx_len Number of bytes to receive. + * @param[in] timeout Timeout duration in milliseconds (0 = no timeout). + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in follower mode. + * - SL_STATUS_INVALID_PARAMETER if length invalid. + * - SL_STATUS_TIMEOUT if operation timed out. + * - SL_STATUS_FULL if buffer full. + * - SL_STATUS_TRANSMIT or SL_STATUS_IO for bus errors. + ******************************************************************************/ +sl_status_t sl_i2c_follower_receive_blocking(sl_i2c_handle_t *i2c_handle, + uint8_t *rx_buffer, + uint32_t rx_len, + uint32_t timeout); + +/***************************************************************************//** + * Follower Mode: Send data to the I2C leader (non-blocking). + * + * @details Initiates data transmission when addressed by the leader using + * DMA and interrupts. Returns immediately; completion is signaled + * via registered callbacks. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[in] tx_buffer Pointer to the transmit data buffer. + * @param[in] tx_len Number of bytes to transmit. + * @param[in] user_data User-defined data passed to callbacks. + * + * @return + * - SL_STATUS_OK if transfer initiated successfully. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in follower mode. + * - SL_STATUS_INVALID_PARAMETER if length invalid. + * - Other error codes for DMA/IRQ setup failures. ******************************************************************************/ -sl_status_t sl_i2c_send_non_blocking(sl_i2c_handle_t i2c_handle, - const uint8_t *tx_buffer, - uint16_t tx_len, - sl_i2c_irq_callback_t i2c_callback, - void *context); +sl_status_t sl_i2c_follower_send_non_blocking(sl_i2c_handle_t *i2c_handle, + const uint8_t *tx_buffer, + uint32_t tx_len, + void *user_data); /***************************************************************************//** - * Leader Mode : This function uses DMA and Interrupt, to receive the data from the follower set up during Init API. - * Follower Mode : This function uses DMA and Interrupt, to receive the data from the addressed I2C Leader. - * The user is notified through the provided callback function upon completion. + * Follower Mode: Receive data from the I2C leader (non-blocking). + * + * @details Initiates data reception when addressed by the leader using + * DMA and interrupts. Returns immediately; completion is signaled + * via registered callbacks. + * + * @param[in] i2c_handle Pointer to the I2C instance handle. + * @param[out] rx_buffer Pointer to the receive data buffer. + * @param[in] rx_len Number of bytes to receive. + * @param[in] user_data User-defined data passed to callbacks. + * + * @return + * - SL_STATUS_OK if transfer initiated successfully. + * - SL_STATUS_NULL_POINTER if arguments are NULL. + * - SL_STATUS_INVALID_MODE if not in follower mode. + * - SL_STATUS_INVALID_PARAMETER if length invalid. + * - Other error codes for DMA/IRQ setup failures. + ******************************************************************************/ +sl_status_t sl_i2c_follower_receive_non_blocking(sl_i2c_handle_t *i2c_handle, + uint8_t *rx_buffer, + uint32_t rx_len, + void *user_data); + +/***************************************************************************//** + * Register a transfer complete callback for non-blocking APIs. * - * @param[in] i2c_handle I2C Instance handle. - * @param[in] rx_buffer A pointer to receive data buffer - * @param[in] rx_len Receive data length - * @param[in] i2c_callback A callback function on completion. - * @param[in] context A pointer to user-defined data for callback. + * @details + * The callback is called once on successful completion of a non-blocking transfer. * - * @return return status. + * @note + * Passing NULL as the callback parameter will unset/disable the callback. + * + * @param[in] i2c_handle Pointer to the I2C driver handle. + * @param[in] callback Transfer complete callback function. + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if handle is NULL. ******************************************************************************/ -sl_status_t sl_i2c_receive_non_blocking(sl_i2c_handle_t i2c_handle, - uint8_t *rx_buffer, - uint16_t rx_len, - sl_i2c_irq_callback_t i2c_callback, - void *context); +sl_status_t sl_i2c_set_transfer_complete_callback(sl_i2c_handle_t *i2c_handle, + sl_i2c_transfer_complete_callback_t callback); + /***************************************************************************//** - * Leader Mode : This function uses DMA and Interrupt, to perform a combined - * write (tx_buffer) followed by a read (rx_buffer) from the follower configured - * during Init API. A repeated START is generated between write and read without - * issuing a STOP in between. - * The user is notified through the provided callback function upon completion. - * - * @param[in] i2c_handle I2C Instance handle. - * @param[in] tx_buffer A pointer to transmit data buffer. - * @param[in] tx_len Transmit data length. - * @param[out] rx_buffer A pointer to receive data buffer. - * @param[in] rx_len Receive data length. - * @param[in] i2c_callback A callback function on completion. - * @param[in] context A pointer to user-defined data for callback. - * - * @return return status. + * Register an event callback for non-blocking APIs. + * + * @details + * The callback is called on I2C events other than transfer completion. + * + * @note + * Passing NULL as the callback parameter will unset/disable the callback. + * + * @param[in] i2c_handle Pointer to the I2C driver handle. + * @param[in] callback Event callback function. + * + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if handle is NULL. ******************************************************************************/ -sl_status_t sl_i2c_transfer_non_blocking(sl_i2c_handle_t i2c_handle, - const uint8_t *tx_buffer, - uint16_t tx_len, - uint8_t *rx_buffer, - uint16_t rx_len, - sl_i2c_irq_callback_t i2c_callback, - void *context); +sl_status_t sl_i2c_set_event_callback(sl_i2c_handle_t *i2c_handle, + sl_i2c_event_callback_t callback); /** @} (end addtogroup i2c driver) */ + #ifdef __cplusplus } #endif From ec82a8ade0216c0208d98a8a45d3df7db9dc2274 Mon Sep 17 00:00:00 2001 From: fimohame Date: Tue, 30 Sep 2025 23:41:47 +0530 Subject: [PATCH 3/4] simplicity_sdk: Patch for sli_i2c.h file Updating the SLI_I2C.h driver file to incorporate design enhancements from the GSDK I2C implementation Upstream-Status: Pending Signed-off-by: S Mohamed Fiaz --- .../platform/driver/i2c/src/sli_i2c.h | 160 +++--------------- 1 file changed, 26 insertions(+), 134 deletions(-) diff --git a/simplicity_sdk/platform/driver/i2c/src/sli_i2c.h b/simplicity_sdk/platform/driver/i2c/src/sli_i2c.h index 010a6777..745caee9 100644 --- a/simplicity_sdk/platform/driver/i2c/src/sli_i2c.h +++ b/simplicity_sdk/platform/driver/i2c/src/sli_i2c.h @@ -3,7 +3,7 @@ * @brief I2C Driver Private API definition. ******************************************************************************* * # License - * Copyright 2024 Silicon Laboratories Inc. www.silabs.com + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com ******************************************************************************* * * SPDX-License-Identifier: Zlib @@ -31,163 +31,55 @@ #ifndef SLI_I2C_H #define SLI_I2C_H -#include "dmadrv.h" #include "sl_i2c.h" -#include "sl_status.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************************* - ******************************* DEFINES *********************************** + ***************************** PROTOTYPES ********************************** ******************************************************************************/ -// Max number of descriptors for dma tx and rx operations. -#define SL_I2C_DMA_MAX_TX_DESCRIPTOR_COUNT 5 -#define SL_I2C_DMA_MAX_RX_DESCRIPTOR_COUNT 5 - -/******************************************************************************* - ******************************** ENUMS ************************************ - ******************************************************************************/ -/// I2C state enum -SL_ENUM(sli_i2c_transaction_state_t) { - SLI_I2C_STATE_ERROR = 0, /// Indicates an error occurred. - SLI_I2C_STATE_SEND_START_AND_ADDR = 1, /// Send start and address byte. - SLI_I2C_STATE_ADDR_WAIT_FOR_ACK_OR_NACK = 2, /// Wait for ACK/NACK on address byte. - SLI_I2C_STATE_SEND_REPEATED_START_AND_ADDR = 3, /// Send repeated start and address byte. - SLI_I2C_STATE_REPEATED_ADDR_WAIT_FOR_ACK_OR_NACK = 4, /// Wait for ACK/NACK for repeated start. - SLI_I2C_STATE_ADDRESS_MATCH = 5, /// Wait for address matching. - SLI_I2C_STATE_ADDR_2ND_BYTE_10BIT_WAIT_FOR_ACK_OR_NACK = 6, /// Wait for ACK/NACK on 2nd byte (10-bit). - SLI_I2C_STATE_10BIT_ADDRESS_MATCH = 7, /// Wait for 10-bit address match. - SLI_I2C_STATE_REP_ADDR_MATCH = 18, /// Wait for repeated start and address match. - SLI_I2C_STATE_SEND_DATA = 8, /// Send data byte. - SLI_I2C_STATE_WAIT_FOR_ACK_OR_NACK = 9, /// Wait for ACK or NACK after sending data. - SLI_I2C_STATE_RECEIVE_DATA = 11, /// Data to be received. - SLI_I2C_STATE_SEND_STOP = 13, /// Send stop signal. - SLI_I2C_STATE_WAIT_FOR_STOP = 14, /// Wait for stop condition. - SLI_I2C_STATE_DONE = 16, /// Transfer completed. - SLI_I2C_STATE_TIMEOUT = 19 /// Timeout. -}; - -/// I2C transfer mode enum -SL_ENUM(sli_i2c_transfer_mode_t) { - SLI_I2C_BLOCKING_TRANSFER = 0, /// I2C blocking transfer - SLI_I2C_NON_BLOCKING_TRANSFER = 1, /// I2C non blocking transfer -}; - -/******************************************************************************* - ******************************* STRUCTS *********************************** - ******************************************************************************/ -/** - * @struct sli_i2c_instance_t - * @brief This struct represents an I2C instance, holding configuration details - * for the peripheral, buffers and transaction state. - * - * @note Usage of `follower_address` depends on the operating mode: - * Leader Mode : Used to specify the address of the follower device. - * Follower Mode : Used to set the I2C device's own (self) address. - */ -typedef struct { - I2C_TypeDef *i2c_base_addr; /// I2C base address. - sl_i2c_operating_mode_t operating_mode; /// Leader/Follower operating mode. - uint16_t follower_address; /// Follower address. - bool is_10bit_addr; /// 7-bit or 10-bit address selection. - sl_gpio_t scl_gpio; /// SCL GPIO Port and Pin. - sl_gpio_t sda_gpio; /// SDA GPIO Port and Pin. - const uint8_t *tx_buffer; /// Transmit data buffer. - uint8_t *rx_buffer; /// Receive data buffer. - uint16_t tx_len; /// Transmit buffer length. - uint16_t rx_len; /// Receive buffer length. - uint16_t tx_offset; /// Transmit offset in buffer. - uint16_t rx_offset; /// Receive offset in buffer. - sl_i2c_transfer_seq_t transfer_seq; /// I2C transaction sequence. - sli_i2c_transaction_state_t state; /// I2C transaction state. - sl_i2c_event_t transfer_event; /// I2C transfer event type. - sli_i2c_transfer_mode_t transfer_mode; /// I2C transfer mode (blocking/non-blocking). - sl_i2c_dma_channel_info_t dma_channel; /// I2C DMA Channel Info. -/// DMA descriptor arrays for TX and RX operations. -#if defined(EMDRV_DMADRV_LDMA) - LDMA_Descriptor_t tx_desc[SL_I2C_DMA_MAX_TX_DESCRIPTOR_COUNT]; - LDMA_Descriptor_t rx_desc[SL_I2C_DMA_MAX_RX_DESCRIPTOR_COUNT]; - #elif defined(EMDRV_DMADRV_LDMA_S3) - sl_hal_ldma_descriptor_t tx_desc[SL_I2C_DMA_MAX_TX_DESCRIPTOR_COUNT]; - sl_hal_ldma_descriptor_t rx_desc[SL_I2C_DMA_MAX_RX_DESCRIPTOR_COUNT]; - #endif - uint8_t addr_buffer[3]; /// Address buffer. - sl_i2c_irq_callback_t callback; /// I2C Callback. - void *context; /// User-defined context. - uint8_t addr_buffer_write[1]; /// Write address byte - uint8_t addr_buffer_read[1]; /// Read address byte - uint8_t rstart; /// Repeated Start. -} sli_i2c_instance_t; - -/***************************************************************************//** - * This function is used for the configuring I2C instance. - * - * @param[in] init_params A pointer to Init Params. - * - * @return return status. - ******************************************************************************/ -sl_status_t sli_i2c_instance_configuration(const sl_i2c_init_params_t *init_params); /***************************************************************************//** - * This function is used for the setting the follower address for follower. + * Configure the I2C instance with initialization parameters. * - * @param[in] i2c_base_addr Pointer to I2C Base Address. - * @param[in] follower_address Follower address. - * @param[in] is_10bit_addr Indicates whether the follower address is 7-bit or 10-bit. - * True if the address is 10-bit, false if it is 7-bit. - ******************************************************************************/ -void sli_i2c_set_follower_address(I2C_TypeDef *i2c_base_addr, - uint16_t follower_address, - bool is_10bit_addr); - -/***************************************************************************//** - * This function is used for the configuring GPIOs for I2C instance. + * @details + * This function resets the I2C peripheral, configures interrupts, sets the + * operating mode (leader/follower), and sets the clock frequency for leader mode. + * It is called internally during driver initialization. * - * @param[in] i2c_base_addr Pointer to I2C Base Address. - * @param[in] sda_gpio SDA GPIO Port and Pin. - * @param[in] scl_gpio SCL GPIO Port and Pin. - * @param[in] enable Enable/Disable the gpio. - ******************************************************************************/ -void sli_i2c_configure_gpio(I2C_TypeDef *i2c_base_addr, - sl_gpio_t sda_gpio, - sl_gpio_t scl_gpio, - bool enable); - -/***************************************************************************//** - * This function is used for the initialization of I2C instance. + * @param[in] i2c_handle Pointer to the I2C instance handle. * - * @param[in] i2c_base_addr Pointer to I2C Base Address. - * @param[in] operating_mode Operating mode of the i2c instance (Leader/Follower). - * @param[in] transfer_seq Transfer Sequence. + * @return + * - SL_STATUS_OK on success. + * - SL_STATUS_NULL_POINTER if init_params is NULL. + * - SL_STATUS_NOT_SUPPORTED if the peripheral is invalid. + * - SL_STATUS_FAIL if clock frequency retrieval fails. ******************************************************************************/ -void sli_i2c_transfer_init(I2C_TypeDef *i2c_base_addr, - sl_i2c_operating_mode_t operating_mode, - sl_i2c_transfer_seq_t transfer_seq); +sl_status_t sli_i2c_init_core(sl_i2c_handle_t *i2c_handle); /***************************************************************************//** - * This function is used for the initialization of I2C DMA transfer. + * Dispatch interrupt handler for the I2C Leader * - * @param[in] i2c_instance I2C Instance + * @details Handles all I2C interrupts for leader mode, including ACK/NACK, STOP, + * arbitration lost, and bus errors. Updates the driver state and event fields. + * Called from the IRQ handler or internal state machine. * - * @return return status. + * @param[in] i2c_handle Pointer to the I2C instance handle. ******************************************************************************/ -sl_status_t sli_i2c_dma_transfer_init(sli_i2c_instance_t *i2c_instance); +void sli_i2c_leader_dispatch_interrupt(sl_i2c_handle_t *i2c_handle); /***************************************************************************//** - * Handles the I2C leader mode non-blocking interrupt dispatch. + * Dispatch interrupt handler for the I2C Follower * - * @param[in] sl_i2c_instance Pointer to the I2C instance structure. - ******************************************************************************/ -void sli_i2c_leader_dispatch_interrupt(sli_i2c_instance_t *sl_i2c_instance); - -/***************************************************************************//** - * Handles the I2C follower mode non-blocking interrupt dispatch. + * @details Handles all I2C interrupts for follower mode, including address match, + * RX data, STOP, and error conditions. Updates the driver state and event fields. + * Called from the IRQ handler or internal state machine. * - * @param[in] sl_i2c_instance Pointer to the I2C instance structure. + * @param[in] i2c_handle Pointer to the I2C instance handle. ******************************************************************************/ -void sli_i2c_follower_dispatch_interrupt(sli_i2c_instance_t *sl_i2c_instance); +void sli_i2c_follower_dispatch_interrupt(sl_i2c_handle_t *i2c_handle); #ifdef __cplusplus } From c4002e832c5a179668ad001a53ec843b00beb1c5 Mon Sep 17 00:00:00 2001 From: fimohame Date: Tue, 30 Sep 2025 23:42:39 +0530 Subject: [PATCH 4/4] simplicity_sdk: Patch for sl_device_i2c.h file Updating the sl_device_i2c.h file to incorporate design enhancements from the GSDK I2C implementation Upstream-Status: Pending Signed-off-by: S Mohamed Fiaz --- .../platform/service/device_manager/inc/sl_device_i2c.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/simplicity_sdk/platform/service/device_manager/inc/sl_device_i2c.h b/simplicity_sdk/platform/service/device_manager/inc/sl_device_i2c.h index 0eb96adc..7cf008c5 100644 --- a/simplicity_sdk/platform/service/device_manager/inc/sl_device_i2c.h +++ b/simplicity_sdk/platform/service/device_manager/inc/sl_device_i2c.h @@ -87,10 +87,10 @@ SL_ENUM(sl_i2c_clock_hlr_t) { /// Validation of operating mode #define SL_I2C_OPERATING_MODE_IS_VALID(operating_mode) ((operating_mode == SL_I2C_LEADER_MODE) || (operating_mode == SL_I2C_FOLLOWER_MODE)) -/// Validation of FIFO Threshold -#if defined(SL_CATALOG_I2C_FIFO_PRESENT) -#define SL_I2C_FIFO_THRESHOLD_IS_VALID(threshold) ((threshold == SL_I2C_FIFO_THRESHOLD_1) || (threshold == SL_I2C_FIFO_THRESHOLD_2)) -#endif +// Validation macro for I2C freq mode. +#define SL_I2C_FREQ_MODE_IS_VALID(freq_mode) ((freq_mode == SL_I2C_FREQ_STANDARD_MODE) \ + || (freq_mode == SL_I2C_FREQ_FAST_MODE) \ + || (freq_mode == SL_I2C_FREQ_FASTPLUS_MODE)) /** @} (end addtogroup device_i2c) */