Explorar o código

feat(i2c): Add I2C driver support for esp32p4

Cao Sen Miao %!s(int64=2) %!d(string=hai) anos
pai
achega
de85f47bc9

+ 1118 - 0
components/hal/esp32p4/include/hal/i2c_ll.h

@@ -0,0 +1,1118 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// The LL layer for I2C register operations
+
+#pragma once
+
+#include <stdbool.h>
+#include "hal/misc.h"
+#include "hal/assert.h"
+#include "soc/i2c_periph.h"
+#include "soc/soc_caps.h"
+#include "soc/i2c_struct.h"
+#include "hal/i2c_types.h"
+#include "soc/clk_tree_defs.h"
+#include "soc/hp_sys_clkrst_struct.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief I2C hardware cmd register fields.
+ */
+typedef union {
+    struct {
+        uint32_t byte_num:    8,  /*!< Specifies the length of data (in bytes) to be read or written */
+                 ack_en:      1,  /*!< Used to enable the I2C controller during a write operation to check whether ACK */
+                 ack_exp:     1,  /*!< Used to configure the level of the ACK bit expected by the I2C controller during a write operation */
+                 ack_val:     1,  /*!< Used to configure the level of the ACK bit sent by the I2C controller during a read operation */
+                 op_code:     3,  /*!< Indicates the command */
+                 reserved14: 17, /*!< Reserved bits */
+                 done:        1;  /*!< Indicates that a command has been executed */
+    };
+    uint32_t val;
+} i2c_ll_hw_cmd_t;
+
+// I2C operation mode command
+#define I2C_LL_CMD_RESTART    6    /*!<I2C restart command */
+#define I2C_LL_CMD_WRITE      1    /*!<I2C write command */
+#define I2C_LL_CMD_READ       3    /*!<I2C read command */
+#define I2C_LL_CMD_STOP       2    /*!<I2C stop command */
+#define I2C_LL_CMD_END        4    /*!<I2C end command */
+
+typedef enum {
+    I2C_LL_INTR_TXFIFO_WM = (1 << 1),
+    I2C_LL_INTR_RXFIFO_WM = (1 << 0),
+    I2C_LL_INTR_NACK = (1 << 10),
+    I2C_LL_INTR_TIMEOUT = (1 << 8),
+    I2C_LL_INTR_MST_COMPLETE = (1 << 7),
+    I2C_LL_INTR_ARBITRATION = (1 << 5),
+    I2C_LL_INTR_END_DETECT = (1 << 3),
+    I2C_LL_INTR_ST_TO = (1 << 13),
+    I2C_LL_INTR_START = (1 << 15),
+    I2C_LL_INTR_STRETCH = (1 << 16),
+    I2C_LL_INTR_UNMATCH = (1 << 18),
+} i2c_ll_intr_t;
+
+typedef enum {
+    I2C_LL_STRETCH_REASON_MASTER_START = 0,
+    I2C_LL_STRETCH_REASON_TX_EMPTY = 1,
+    I2C_LL_STRETCH_REASON_RX_FULL = 2,
+} i2c_ll_stretch_cause_t;
+
+// Get the I2C hardware instance
+#define I2C_LL_GET_HW(i2c_num)      (((i2c_num) == I2C_NUM_0) ? (&I2C0) : (((i2c_num) == I2C_NUM_1) ? (&I2C1) : (&LP_I2C)))
+#define I2C_LL_MASTER_EVENT_INTR    (I2C_NACK_INT_ENA_M|I2C_TIME_OUT_INT_ENA_M|I2C_TRANS_COMPLETE_INT_ENA_M|I2C_ARBITRATION_LOST_INT_ENA_M|I2C_END_DETECT_INT_ENA_M)
+#define I2C_LL_SLAVE_EVENT_INTR     (I2C_TRANS_COMPLETE_INT_ENA_M|I2C_TXFIFO_WM_INT_ENA_M|I2C_RXFIFO_WM_INT_ENA_M | I2C_SLAVE_STRETCH_INT_ENA_M | I2C_SLAVE_ADDR_UNMATCH_INT_ENA_M)
+#define I2C_LL_SLAVE_RX_EVENT_INTR  (I2C_TRANS_COMPLETE_INT_ENA_M | I2C_RXFIFO_WM_INT_ENA_M | I2C_SLAVE_STRETCH_INT_ENA_M | I2C_SLAVE_ADDR_UNMATCH_INT_ENA_M)
+#define I2C_LL_SLAVE_TX_EVENT_INTR  (I2C_TXFIFO_WM_INT_ENA_M | I2C_SLAVE_ADDR_UNMATCH_INT_ENA_M)
+#define I2C_LL_RESET_SLV_SCL_PULSE_NUM_DEFAULT   (9)
+
+/**
+ * @brief  Calculate I2C bus frequency
+ *         Note that the clock accuracy is affected by the external pull-up resistor,
+ *         here we try to to calculate a configuration parameter which is close to the required clock.
+ *         But in I2C communication, the clock accuracy is not very concerned.
+ *
+ * @param  source_clk I2C source clock
+ * @param  bus_freq I2C bus frequency
+ * @param  clk_cal Pointer to accept the clock configuration
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_cal_bus_clk(uint32_t source_clk, uint32_t bus_freq, i2c_hal_clk_config_t *clk_cal)
+{
+    uint32_t clkm_div = source_clk / (bus_freq * 1024) +1;
+    uint32_t sclk_freq = source_clk / clkm_div;
+    uint32_t half_cycle = sclk_freq / bus_freq / 2;
+    //SCL
+    clk_cal->clkm_div = clkm_div;
+    clk_cal->scl_low = half_cycle;
+    // default, scl_wait_high < scl_high
+    // Make 80KHz as a boundary here, because when working at lower frequency, too much scl_wait_high will faster the frequency
+    // according to some hardware behaviors.
+    clk_cal->scl_wait_high = (bus_freq >= 80*1000) ? (half_cycle / 2 - 2) : (half_cycle / 4);
+    clk_cal->scl_high = half_cycle - clk_cal->scl_wait_high;
+    clk_cal->sda_hold = half_cycle / 4;
+    clk_cal->sda_sample = half_cycle / 2;
+    clk_cal->setup = half_cycle;
+    clk_cal->hold = half_cycle;
+    //default we set the timeout value to about 10 bus cycles
+    // log(20*half_cycle)/log(2) = log(half_cycle)/log(2) +  log(20)/log(2)
+    clk_cal->tout = (int)(sizeof(half_cycle) * 8 - __builtin_clz(5 * half_cycle)) + 2;
+
+    /* Verify the assumptions made by the hardware */
+    HAL_ASSERT(clk_cal->scl_wait_high < clk_cal->sda_sample &&
+               clk_cal->sda_sample < clk_cal->scl_high);
+}
+
+/**
+ * @brief  Update I2C configuration
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_update(i2c_dev_t *hw)
+{
+    hw->ctr.conf_upgate = 1;
+}
+
+/**
+ * @brief  Configure the I2C bus timing related register.
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  bus_cfg Pointer to the data structure holding the register configuration.
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_set_bus_timing(i2c_dev_t *hw, i2c_hal_clk_config_t *bus_cfg)
+{
+    if (hw == &I2C0) {
+    HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl10, reg_i2c0_clk_div_num, bus_cfg->clkm_div - 1);
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl10, reg_i2c0_clk_div_numerator, 0);
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl10, reg_i2c0_clk_div_denominator, 0);
+    } else if (hw == &I2C1) {
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl11, reg_i2c1_clk_div_num, bus_cfg->clkm_div - 1);
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl11, reg_i2c1_clk_div_numerator, 0);
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl11, reg_i2c1_clk_div_denominator, 0);
+    }
+    /* According to the Technical Reference Manual, the following timings must be subtracted by 1.
+     * However, according to the practical measurement and some hardware behaviour, if wait_high_period and scl_high minus one.
+     * The SCL frequency would be a little higher than expected. Therefore, the solution
+     * here is not to minus scl_high as well as scl_wait high, and the frequency will be absolutely accurate to all frequency
+     * to some extent. */
+    hw->scl_low_period.scl_low_period = bus_cfg->scl_low - 1;
+    hw->scl_high_period.scl_high_period = bus_cfg->scl_high;
+    hw->scl_high_period.scl_wait_high_period = bus_cfg->scl_wait_high;
+    //sda sample
+    hw->sda_hold.sda_hold_time = bus_cfg->sda_hold - 1;
+    hw->sda_sample.sda_sample_time = bus_cfg->sda_sample - 1;
+    //setup
+    hw->scl_rstart_setup.scl_rstart_setup_time = bus_cfg->setup - 1;
+    hw->scl_stop_setup.scl_stop_setup_time = bus_cfg->setup - 1;
+    //hold
+    hw->scl_start_hold.scl_start_hold_time = bus_cfg->hold - 1;
+    hw->scl_stop_hold.scl_stop_hold_time = bus_cfg->hold - 1;
+    hw->to.time_out_value = bus_cfg->tout;
+    hw->to.time_out_en = 1;
+}
+
+/**
+ * @brief Set fractional divider
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param div_a The denominator of the frequency divider factor of the i2c function clock
+ * @param div_b The numerator of the frequency divider factor of the i2c function clock.
+ */
+static inline void i2c_ll_master_set_fractional_divider(i2c_dev_t *hw, uint8_t div_a, uint8_t div_b)
+{
+    /* Set div_a and div_b to 0, as it's not necessary to use them */
+    if (hw == &I2C0) {
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl10, reg_i2c0_clk_div_numerator, div_a);
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl10, reg_i2c0_clk_div_denominator, div_b);
+    } else if (hw == &I2C1) {
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl11, reg_i2c1_clk_div_numerator, div_a);
+        HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl11, reg_i2c1_clk_div_denominator, div_b);
+    }
+}
+
+/**
+ * @brief  Reset I2C txFIFO
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_txfifo_rst(i2c_dev_t *hw)
+{
+    hw->fifo_conf.tx_fifo_rst = 1;
+    hw->fifo_conf.tx_fifo_rst = 0;
+}
+
+/**
+ * @brief  Reset I2C rxFIFO
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_rxfifo_rst(i2c_dev_t *hw)
+{
+    hw->fifo_conf.rx_fifo_rst = 1;
+    hw->fifo_conf.rx_fifo_rst = 0;
+}
+
+/**
+ * @brief  Clear I2C interrupt status
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  mask Interrupt mask needs to be cleared
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_clear_intr_mask(i2c_dev_t *hw, uint32_t mask)
+{
+    hw->int_clr.val = mask;
+}
+
+/**
+ * @brief  Enable I2C interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  mask Interrupt mask needs to be enabled
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_enable_intr_mask(i2c_dev_t *hw, uint32_t mask)
+{
+    hw->int_ena.val |= mask;
+}
+
+/**
+ * @brief  Disable I2C interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  mask Interrupt mask needs to be disabled
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_disable_intr_mask(i2c_dev_t *hw, uint32_t mask)
+{
+    hw->int_ena.val &= (~mask);
+}
+
+/**
+ * @brief  Get I2C interrupt status
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return I2C interrupt status
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_get_intr_mask(i2c_dev_t *hw, uint32_t *intr_status)
+{
+    *intr_status = hw->int_status.val;
+}
+
+/**
+ * @brief  Configure I2C memory access mode, FIFO mode or non-FIFO mode
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  fifo_mode_en Set true to enable FIFO access mode, else, set it false
+ *
+ * @return None
+ */
+static inline void i2c_ll_slave_set_fifo_mode(i2c_dev_t *hw, bool fifo_mode_en)
+{
+    hw->fifo_conf.nonfifo_en = fifo_mode_en ? 0 : 1;
+}
+
+/**
+ * @brief  Configure I2C timeout
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  tout_num The I2C timeout value needs to be set (2^tout in core clock cycle)
+ *
+ * @return None
+ */
+static inline void i2c_ll_set_tout(i2c_dev_t *hw, int tout)
+{
+    hw->to.time_out_value = tout;
+}
+
+/**
+ * @brief  Configure I2C slave broadcasting mode.
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  broadcast_en Set true to enable broadcast, else, set it false
+ *
+ * @return None
+ */
+static inline void i2c_ll_slave_broadcast_enable(i2c_dev_t *hw, bool broadcast_en)
+{
+    hw->ctr.addr_broadcasting_en = broadcast_en;
+}
+
+/**
+ * @brief  Configure I2C slave address
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  slave_addr I2C slave address needs to be set
+ * @param  addr_10bit_en Set true to enable 10-bit slave address mode, set false to enable 7-bit address mode
+ *
+ * @return None
+ */
+static inline void i2c_ll_set_slave_addr(i2c_dev_t *hw, uint16_t slave_addr, bool addr_10bit_en)
+{
+    hw->slave_addr.addr_10bit_en = addr_10bit_en;
+    if (addr_10bit_en) {
+        uint8_t addr_14_7 = (slave_addr & 0xff) << 7;
+        uint8_t addr_6_0 = ((slave_addr & 0x300) >> 8) | 0x78;
+        hw->slave_addr.slave_addr = addr_14_7 | addr_6_0;
+        hw->ctr.addr_10bit_rw_check_en = addr_10bit_en;
+    } else {
+        hw->slave_addr.slave_addr = slave_addr;
+    }
+}
+
+/**
+ * @brief Write I2C hardware command register
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  cmd I2C hardware command
+ * @param  cmd_idx The index of the command register, should be less than 16
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_master_write_cmd_reg(i2c_dev_t *hw, i2c_ll_hw_cmd_t cmd, int cmd_idx)
+{
+    hw->command[cmd_idx].val = cmd.val;
+}
+
+/**
+ * @brief Configure I2C start timing
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  start_setup The start condition setup period (in core clock cycle)
+ * @param  start_hold The start condition hold period (in core clock cycle)
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_set_start_timing(i2c_dev_t *hw, int start_setup, int start_hold)
+{
+    hw->scl_rstart_setup.scl_rstart_setup_time = start_setup;
+    hw->scl_start_hold.scl_start_hold_time = start_hold - 1;
+}
+
+/**
+ * @brief Configure I2C stop timing
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  stop_setup The stop condition setup period (in core clock cycle)
+ * @param  stop_hold The stop condition hold period (in core clock cycle)
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_set_stop_timing(i2c_dev_t *hw, int stop_setup, int stop_hold)
+{
+    hw->scl_stop_setup.scl_stop_setup_time = stop_setup;
+    hw->scl_stop_hold.scl_stop_hold_time = stop_hold;
+}
+
+/**
+ * @brief Configure I2C stop timing
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  sda_sample The SDA sample time (in core clock cycle)
+ * @param  sda_hold The SDA hold time (in core clock cycle)
+ *
+ * @return None
+ */
+static inline void i2c_ll_set_sda_timing(i2c_dev_t *hw, int sda_sample, int sda_hold)
+{
+    hw->sda_hold.sda_hold_time = sda_hold;
+    hw->sda_sample.sda_sample_time = sda_sample;
+}
+
+/**
+ * @brief  Set I2C txFIFO empty threshold
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  empty_thr The txFIFO empty threshold
+ *
+ * @return None
+ */
+static inline void i2c_ll_set_txfifo_empty_thr(i2c_dev_t *hw, uint8_t empty_thr)
+{
+    hw->fifo_conf.txfifo_wm_thrhd = empty_thr;
+}
+
+/**
+ * @brief  Set I2C rxFIFO full threshold
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  full_thr The rxFIFO full threshold
+ *
+ * @return None
+ */
+static inline void i2c_ll_set_rxfifo_full_thr(i2c_dev_t *hw, uint8_t full_thr)
+{
+    hw->fifo_conf.rxfifo_wm_thrhd = full_thr;
+}
+
+/**
+ * @brief  Set the I2C data mode, LSB or MSB
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  tx_mode Tx data bit mode
+ * @param  rx_mode Rx data bit mode
+ *
+ * @return None
+ */
+static inline void i2c_ll_set_data_mode(i2c_dev_t *hw, i2c_trans_mode_t tx_mode, i2c_trans_mode_t rx_mode)
+{
+    hw->ctr.tx_lsb_first = tx_mode;
+    hw->ctr.rx_lsb_first = rx_mode;
+}
+
+/**
+ * @brief Get I2C sda timing configuration
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  sda_sample Pointer to accept the SDA sample timing configuration
+ * @param  sda_hold Pointer to accept the SDA hold timing configuration
+ *
+ * @return None
+ */
+static inline void i2c_ll_get_sda_timing(i2c_dev_t *hw, int *sda_sample, int *sda_hold)
+{
+    *sda_hold = hw->sda_hold.sda_hold_time;
+    *sda_sample = hw->sda_sample.sda_sample_time;
+}
+
+/**
+ * @brief Get the I2C hardware version
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return The I2C hardware version
+ */
+static inline uint32_t i2c_ll_get_hw_version(i2c_dev_t *hw)
+{
+    return hw->date.date;
+}
+
+/**
+ * @brief  Check if the I2C bus is busy
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return True if I2C state machine is busy, else false will be returned
+ */
+__attribute__((always_inline))
+static inline bool i2c_ll_is_bus_busy(i2c_dev_t *hw)
+{
+    return hw->sr.bus_busy;
+}
+
+/**
+ * @brief  Check if I2C is master mode
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return True if I2C is master mode, else false will be returned
+ */
+static inline bool i2c_ll_is_master_mode(i2c_dev_t *hw)
+{
+    return hw->ctr.ms_mode;
+}
+
+/**
+ * @brief Get the rxFIFO readable length
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return RxFIFO readable length
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_get_rxfifo_cnt(i2c_dev_t *hw, uint32_t *length)
+{
+    *length = hw->sr.rxfifo_cnt;
+}
+
+/**
+ * @brief  Get I2C txFIFO writable length
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return TxFIFO writable length
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_get_txfifo_len(i2c_dev_t *hw, uint32_t *length)
+{
+    *length = SOC_I2C_FIFO_LEN - hw->sr.txfifo_cnt;
+}
+
+/**
+ * @brief Get I2C timeout configuration
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return The I2C timeout value
+ */
+static inline void i2c_ll_get_tout(i2c_dev_t *hw, int *timeout)
+{
+    *timeout = hw->to.time_out_value;
+}
+
+/**
+ * @brief  Start I2C transfer
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_master_trans_start(i2c_dev_t *hw)
+{
+    hw->ctr.trans_start = 1;
+}
+
+/**
+ * @brief Get I2C start timing configuration
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  setup_time Pointer to accept the start condition setup period
+ * @param  hold_time Pointer to accept the start condition hold period
+ *
+ * @return None
+ */
+static inline void i2c_ll_get_start_timing(i2c_dev_t *hw, int *setup_time, int *hold_time)
+{
+    *setup_time = hw->scl_rstart_setup.scl_rstart_setup_time;
+    *hold_time = hw->scl_start_hold.scl_start_hold_time + 1;
+}
+
+/**
+ * @brief  Get I2C stop timing configuration
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  setup_time Pointer to accept the stop condition setup period
+ * @param  hold_time Pointer to accept the stop condition hold period
+ *
+ * @return None
+ */
+static inline void i2c_ll_get_stop_timing(i2c_dev_t *hw, int *setup_time, int *hold_time)
+{
+    *setup_time = hw->scl_stop_setup.scl_stop_setup_time;
+    *hold_time = hw->scl_stop_hold.scl_stop_hold_time;
+}
+
+/**
+ * @brief  Write the I2C hardware txFIFO
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  ptr Pointer to data buffer
+ * @param  len Amount of data needs to be writen
+ *
+ * @return None.
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_write_txfifo(i2c_dev_t *hw, const uint8_t *ptr, uint8_t len)
+{
+    for (int i = 0; i< len; i++) {
+        HAL_FORCE_MODIFY_U32_REG_FIELD(hw->data, fifo_rdata, ptr[i]);
+    }
+}
+
+/**
+ * @brief  Read the I2C hardware rxFIFO
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  ptr Pointer to data buffer
+ * @param  len Amount of data needs read
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_read_rxfifo(i2c_dev_t *hw, uint8_t *ptr, uint8_t len)
+{
+    for(int i = 0; i < len; i++) {
+        ptr[i] = HAL_FORCE_READ_U32_REG_FIELD(hw->data, fifo_rdata);
+    }
+}
+
+/**
+ * @brief  Write the I2C hardware txFIFO
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  ram_offset Offset value of I2C RAM.
+ * @param  ptr Pointer to data buffer
+ * @param  len Amount of data needs to be writen
+ *
+ * @return None.
+ */
+static inline void i2c_ll_write_by_nonfifo(i2c_dev_t *hw, uint8_t ram_offset, const uint8_t *ptr, uint8_t len)
+{
+    uint32_t *fifo_addr = (uint32_t *)&hw->txfifo_start_addr;
+    for (int i = 0; i < len; i++) {
+        fifo_addr[i + ram_offset] = ptr[i];
+    }
+}
+
+/**
+ * @brief  Read the I2C hardware ram
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  ram_offset Offset value of I2C RAM.
+ * @param  ptr Pointer to data buffer
+ * @param  len Amount of data needs read
+ *
+ * @return None
+ */
+static inline void i2c_ll_read_by_nonfifo(i2c_dev_t *hw, uint8_t ram_offset, uint8_t *ptr, uint8_t len)
+{
+    uint32_t *fifo_addr = (uint32_t *)&hw->rxfifo_start_addr;
+
+    for (int i = 0; i < len; i++) {
+        ptr[i] = fifo_addr[i + ram_offset];
+    }
+}
+
+/**
+ * @brief Get access to I2C RAM address directly
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  addr_wr_en Enable I2C ram address read and write
+ *
+ * @return None
+*/
+static inline void i2c_ll_enable_mem_access_nonfifo(i2c_dev_t *hw, bool addr_wr_en)
+{
+    hw->fifo_conf.fifo_addr_cfg_en = addr_wr_en;
+}
+
+/**
+ * @brief  Configure I2C hardware filter
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  filter_num If the glitch period on the line is less than this value, it can be filtered out
+ *                    If `filter_num == 0`, the filter will be disabled
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_set_filter(i2c_dev_t *hw, uint8_t filter_num)
+{
+    if (filter_num > 0) {
+        hw->filter_cfg.scl_filter_thres = filter_num;
+        hw->filter_cfg.sda_filter_thres = filter_num;
+        hw->filter_cfg.scl_filter_en = 1;
+        hw->filter_cfg.sda_filter_en = 1;
+    } else {
+        hw->filter_cfg.scl_filter_en = 0;
+        hw->filter_cfg.sda_filter_en = 0;
+    }
+}
+
+/**
+ * @brief  Get I2C hardware filter configuration
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return The hardware filter configuration
+ */
+static inline void i2c_ll_master_get_filter(i2c_dev_t *hw, uint8_t *filter_conf)
+{
+    *filter_conf = hw->filter_cfg.scl_filter_thres;
+}
+
+/**
+ * @brief Reste I2C master FSM. When the master FSM is stuck, call this function to reset the FSM
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_fsm_rst(i2c_dev_t *hw)
+{
+    // fsm_rst is a self cleared bit.
+    hw->ctr.fsm_rst = 1;
+}
+
+/**
+ * @brief Clear I2C bus, when the slave is stuck in a deadlock and keeps pulling the bus low,
+ *        master can controls the SCL bus to generate 9 CLKs.
+ *
+ * Note: The master cannot detect if deadlock happens, but when the scl_st_to interrupt is generated, a deadlock may occur.
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  slave_pulses When I2C master is IDLE, the number of pulses will be sent out.
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_clr_bus(i2c_dev_t *hw, uint32_t slave_pulses)
+{
+    hw->scl_sp_conf.scl_rst_slv_num = slave_pulses;
+    hw->scl_sp_conf.scl_rst_slv_en = 1;
+    hw->ctr.conf_upgate = 1;
+    // hardward will clear scl_rst_slv_en after sending SCL pulses,
+    // and we should set conf_upgate bit to synchronize register value.
+    while (hw->scl_sp_conf.scl_rst_slv_en);
+    hw->ctr.conf_upgate = 1;
+}
+
+/**
+ * @brief Set the ACK level that the I2C master must send when the Rx FIFO count has reached the threshold value.
+ *        ack_level: 1 (NACK)
+ *        ack_level: 0 (ACK)
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_rx_full_ack_level(i2c_dev_t *hw, int ack_level)
+{
+    hw->ctr.rx_full_ack_level = ack_level;
+}
+
+/**
+ * @brief Set I2C source clock
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  src_clk Source clock of the I2C
+ *
+ * @return None
+ */
+static inline void i2c_ll_set_source_clk(i2c_dev_t *hw, i2c_clock_source_t src_clk)
+{
+    // src_clk : (1) for RTC_CLK, (0) for XTAL
+    if (hw == &I2C0) {
+        HP_SYS_CLKRST.peri_clk_ctrl10.reg_i2c0_clk_src_sel = (src_clk == I2C_CLK_SRC_RC_FAST) ? 1: 0;
+    } else if (hw == &I2C1) {
+        HP_SYS_CLKRST.peri_clk_ctrl10.reg_i2c1_clk_src_sel = (src_clk == I2C_CLK_SRC_RC_FAST) ? 1: 0;
+    } else if (hw == &LP_I2C) {
+        // Do nothing
+        return;
+    } else {
+        HAL_ASSERT(false);
+    }
+
+}
+
+/**
+ * @brief Enable I2C peripheral controller clock
+ *
+ * @param dev Peripheral instance address
+ * @param en True to enable, False to disable
+ */
+static inline void i2c_ll_enable_controller_clock(i2c_dev_t *hw, bool en)
+{
+    if (hw == &I2C0) {
+        HP_SYS_CLKRST.soc_clk_ctrl2.reg_i2c0_apb_clk_en = en;
+        HP_SYS_CLKRST.peri_clk_ctrl10.reg_i2c0_clk_en = en;
+    } else if (hw == &I2C1) {
+        HP_SYS_CLKRST.soc_clk_ctrl2.reg_i2c1_apb_clk_en = en;
+        HP_SYS_CLKRST.peri_clk_ctrl10.reg_i2c1_clk_en = en;
+    } else if (hw == &LP_I2C) {
+        // Do nothing
+        return;
+    } else {
+        HAL_ASSERT(false);
+    }
+}
+
+/**
+ * @brief  Init I2C master
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_init(i2c_dev_t *hw)
+{
+    typeof(hw->ctr) ctrl_reg;
+    ctrl_reg.val = 0;
+    ctrl_reg.ms_mode = 1;
+    ctrl_reg.sda_force_out = 1;
+    ctrl_reg.scl_force_out = 1;
+    hw->ctr.val = ctrl_reg.val;
+}
+
+/**
+ * @brief  Init I2C slave
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+static inline void i2c_ll_slave_init(i2c_dev_t *hw)
+{
+    typeof(hw->ctr) ctrl_reg;
+    ctrl_reg.val = 0;
+    ctrl_reg.sda_force_out = 1;
+    ctrl_reg.scl_force_out = 1;
+    hw->ctr.val = ctrl_reg.val;
+    hw->fifo_conf.fifo_addr_cfg_en = 0;
+}
+
+/**
+ * @brief Set whether slave should auto start, or only start with start signal from master
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param slv_ex_auto_en 1 if slave auto start data transaction, otherwise, 0.
+ */
+static inline void i2c_ll_slave_tx_auto_start_en(i2c_dev_t *hw, bool slv_ex_auto_en)
+{
+    hw->ctr.slv_tx_auto_start_en = slv_ex_auto_en;
+}
+
+/**
+ * @brief Get I2C interrupt status register address
+ */
+static inline volatile void *i2c_ll_get_interrupt_status_reg(i2c_dev_t *dev)
+{
+    return &dev->int_status;
+}
+
+
+/**
+ * @brief Enable I2C slave clock stretch.
+ *
+ * @param dev Beginning address of the peripheral registers.
+ * @param enable true: Enable, false: Disable.
+ */
+static inline void i2c_ll_slave_enable_scl_stretch(i2c_dev_t *dev, bool enable)
+{
+    dev->scl_stretch_conf.slave_scl_stretch_en = enable;
+}
+
+/**
+ * @brief Clear I2C clock stretch status
+ *
+ * @param dev Beginning address of the peripheral registers
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_slave_clear_stretch(i2c_dev_t *dev)
+{
+    dev->scl_stretch_conf.slave_scl_stretch_clr = 1;
+}
+
+//////////////////////////////////////////Deprecated Functions//////////////////////////////////////////////////////////
+/////////////////////////////The following functions are only used by the legacy driver/////////////////////////////////
+/////////////////////////////They might be removed in the next major release (ESP-IDF 6.0)//////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// I2C master TX interrupt bitmap
+#define I2C_LL_MASTER_TX_INT          (I2C_NACK_INT_ENA_M|I2C_TIME_OUT_INT_ENA_M|I2C_TRANS_COMPLETE_INT_ENA_M|I2C_ARBITRATION_LOST_INT_ENA_M|I2C_END_DETECT_INT_ENA_M)
+// I2C master RX interrupt bitmap
+#define I2C_LL_MASTER_RX_INT          (I2C_TIME_OUT_INT_ENA_M|I2C_TRANS_COMPLETE_INT_ENA_M|I2C_ARBITRATION_LOST_INT_ENA_M|I2C_END_DETECT_INT_ENA_M)
+// I2C slave TX interrupt bitmap
+#define I2C_LL_SLAVE_TX_INT           (I2C_TXFIFO_WM_INT_ENA_M)
+// I2C slave RX interrupt bitmap
+#define I2C_LL_SLAVE_RX_INT           (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M)
+// I2C max timeout value
+#define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE
+
+#define I2C_LL_INTR_MASK          (0x3fff) /*!< I2C all interrupt bitmap */
+
+/**
+ * @brief I2C interrupt event
+ */
+typedef enum {
+    I2C_INTR_EVENT_ERR,
+    I2C_INTR_EVENT_ARBIT_LOST,   /*!< I2C arbition lost event */
+    I2C_INTR_EVENT_NACK,         /*!< I2C NACK event */
+    I2C_INTR_EVENT_TOUT,         /*!< I2C time out event */
+    I2C_INTR_EVENT_END_DET,      /*!< I2C end detected event */
+    I2C_INTR_EVENT_TRANS_DONE,   /*!< I2C trans done event */
+    I2C_INTR_EVENT_RXFIFO_FULL,  /*!< I2C rxfifo full event */
+    I2C_INTR_EVENT_TXFIFO_EMPTY, /*!< I2C txfifo empty event */
+} i2c_intr_event_t;
+
+/**
+ * @brief  Configure I2C SCL timing
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  high_period The I2C SCL hight period (in core clock cycle, hight_period > 2)
+ * @param  low_period The I2C SCL low period (in core clock cycle, low_period > 1)
+ * @param  wait_high_period The I2C SCL wait rising edge period.
+ *
+ * @return None.
+ */
+static inline void i2c_ll_set_scl_clk_timing(i2c_dev_t *hw, int high_period, int low_period, int wait_high_period)
+{
+    hw->scl_low_period.scl_low_period = low_period;
+    hw->scl_high_period.scl_high_period = high_period;
+    hw->scl_high_period.scl_wait_high_period = wait_high_period;
+}
+
+/**
+ * @brief  Get I2C SCL timing configuration
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  high_period Pointer to accept the SCL high period
+ * @param  low_period Pointer to accept the SCL low period
+ *
+ * @return None
+ */
+static inline void i2c_ll_get_scl_clk_timing(i2c_dev_t *hw, int *high_period, int *low_period, int *wait_high_period)
+{
+    *high_period = hw->scl_high_period.scl_high_period;
+    *wait_high_period = hw->scl_high_period.scl_wait_high_period;
+    *low_period = hw->scl_low_period.scl_low_period;
+}
+
+/**
+ * @brief  Get I2C master interrupt event
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  event Pointer to accept the interrupt event
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_master_get_event(i2c_dev_t *hw, i2c_intr_event_t *event)
+{
+    i2c_int_status_reg_t int_sts = hw->int_status;
+    if (int_sts.arbitration_lost_int_st) {
+        *event = I2C_INTR_EVENT_ARBIT_LOST;
+    } else if (int_sts.nack_int_st) {
+        *event = I2C_INTR_EVENT_NACK;
+    } else if (int_sts.time_out_int_st) {
+        *event = I2C_INTR_EVENT_TOUT;
+    } else if (int_sts.end_detect_int_st) {
+        *event = I2C_INTR_EVENT_END_DET;
+    } else if (int_sts.trans_complete_int_st) {
+        *event = I2C_INTR_EVENT_TRANS_DONE;
+    } else {
+        *event = I2C_INTR_EVENT_ERR;
+    }
+}
+
+/**
+ * @brief  Get I2C slave interrupt event
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  event Pointer to accept the interrupt event
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_slave_get_event(i2c_dev_t *hw, i2c_intr_event_t *event)
+{
+    typeof(hw->int_status) int_sts = hw->int_status;
+    if (int_sts.txfifo_wm_int_st) {
+        *event = I2C_INTR_EVENT_TXFIFO_EMPTY;
+    } else if (int_sts.trans_complete_int_st) {
+        *event = I2C_INTR_EVENT_TRANS_DONE;
+    } else if (int_sts.rxfifo_wm_int_st) {
+        *event = I2C_INTR_EVENT_RXFIFO_FULL;
+    } else {
+        *event = I2C_INTR_EVENT_ERR;
+    }
+}
+
+/**
+ * @brief  Enable I2C master TX interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_master_enable_tx_it(i2c_dev_t *hw)
+{
+    hw->int_clr.val = UINT32_MAX;
+    hw->int_ena.val =  I2C_LL_MASTER_TX_INT;
+}
+
+/**
+ * @brief  Enable I2C master RX interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_master_enable_rx_it(i2c_dev_t *hw)
+{
+    hw->int_clr.val = UINT32_MAX;
+    hw->int_ena.val = I2C_LL_MASTER_RX_INT;
+}
+
+/**
+ * @brief  Disable I2C master TX interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+static inline void i2c_ll_master_disable_tx_it(i2c_dev_t *hw)
+{
+    hw->int_ena.val &= (~I2C_LL_MASTER_TX_INT);
+}
+
+/**
+ * @brief  Disable I2C master RX interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_master_disable_rx_it(i2c_dev_t *hw)
+{
+    hw->int_ena.val &= (~I2C_LL_MASTER_RX_INT);
+}
+
+/**
+ * @brief
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+static inline void i2c_ll_slave_enable_tx_it(i2c_dev_t *hw)
+{
+    hw->int_ena.val |= I2C_LL_SLAVE_TX_INT;
+}
+
+/**
+ * @brief Enable I2C slave RX interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+static inline void i2c_ll_slave_enable_rx_it(i2c_dev_t *hw)
+{
+    hw->int_ena.val |= I2C_LL_SLAVE_RX_INT;
+}
+
+/**
+ * @brief Disable I2C slave TX interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+__attribute__((always_inline))
+static inline void i2c_ll_slave_disable_tx_it(i2c_dev_t *hw)
+{
+    hw->int_ena.val &= (~I2C_LL_SLAVE_TX_INT);
+}
+
+/**
+ * @brief  Disable I2C slave RX interrupt
+ *
+ * @param  hw Beginning address of the peripheral registers
+ *
+ * @return None
+ */
+static inline void i2c_ll_slave_disable_rx_it(i2c_dev_t *hw)
+{
+    hw->int_ena.val &= (~I2C_LL_SLAVE_RX_INT);
+}
+
+/**
+ * @brief  Configure I2C SCL timing
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  hight_period The I2C SCL hight period (in core clock cycle, hight_period > 2)
+ * @param  low_period The I2C SCL low period (in core clock cycle, low_period > 1)
+ *
+ * @return None.
+ */
+static inline void i2c_ll_set_scl_timing(i2c_dev_t *hw, int hight_period, int low_period)
+{
+    hw->scl_low_period.scl_low_period = low_period - 1;
+    hw->scl_high_period.scl_high_period = hight_period - 10;
+    hw->scl_high_period.scl_wait_high_period = hight_period - hw->scl_high_period.scl_high_period;
+}
+
+/**
+ * @brief  Get the I2C data mode
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  tx_mode Pointer to accept the received bytes mode
+ * @param  rx_mode Pointer to accept the sended bytes mode
+ *
+ * @return None
+ */
+static inline void i2c_ll_get_data_mode(i2c_dev_t *hw, i2c_trans_mode_t *tx_mode, i2c_trans_mode_t *rx_mode)
+{
+    *tx_mode = hw->ctr.tx_lsb_first;
+    *rx_mode = hw->ctr.rx_lsb_first;
+}
+
+/**
+ * @brief  Get I2C SCL timing configuration
+ *
+ * @param  hw Beginning address of the peripheral registers
+ * @param  high_period Pointer to accept the SCL high period
+ * @param  low_period Pointer to accept the SCL low period
+ *
+ * @return None
+ */
+static inline void i2c_ll_get_scl_timing(i2c_dev_t *hw, int *high_period, int *low_period)
+{
+    *high_period = hw->scl_high_period.scl_high_period + hw->scl_high_period.scl_wait_high_period;
+    *low_period = hw->scl_low_period.scl_low_period + 1;
+}
+
+#ifdef __cplusplus
+}
+#endif

+ 2 - 2
components/hal/i2c_hal.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -49,8 +49,8 @@ void i2c_hal_init(i2c_hal_context_t *hal, int i2c_port)
 {
     if (hal->dev == NULL) {
         hal->dev = I2C_LL_GET_HW(i2c_port);
-        i2c_ll_enable_controller_clock(hal->dev, true);
     }
+    i2c_ll_enable_controller_clock(hal->dev, true);
 }
 
 void i2c_hal_deinit(i2c_hal_context_t *hal)

+ 16 - 0
components/soc/esp32p4/i2c_periph.c

@@ -11,4 +11,20 @@
  Bunch of constants for every I2C peripheral: GPIO signals, irqs, hw addr of registers etc
 */
 const i2c_signal_conn_t i2c_periph_signal[SOC_I2C_NUM] = {
+    {
+        .sda_out_sig = I2C0_SDA_PAD_OUT_IDX,
+        .sda_in_sig = I2C0_SDA_PAD_IN_IDX,
+        .scl_out_sig = I2C0_SCL_PAD_OUT_IDX,
+        .scl_in_sig = I2C0_SCL_PAD_IN_IDX,
+        .irq = ETS_I2C0_INTR_SOURCE,
+        .module = PERIPH_I2C0_MODULE,
+    },
+    {
+        .sda_out_sig = I2C1_SDA_PAD_OUT_IDX,
+        .sda_in_sig = I2C1_SDA_PAD_IN_IDX,
+        .scl_out_sig = I2C1_SCL_PAD_OUT_IDX,
+        .scl_in_sig = I2C1_SCL_PAD_IN_IDX,
+        .irq = ETS_I2C1_INTR_SOURCE,
+        .module = PERIPH_I2C1_MODULE,
+    },
 };

+ 9 - 1
components/soc/esp32p4/include/soc/Kconfig.soc_caps.in

@@ -51,6 +51,10 @@ config SOC_RTC_MEM_SUPPORTED
     bool
     default y
 
+config SOC_I2C_SUPPORTED
+    bool
+    default y
+
 config SOC_SYSTIMER_SUPPORTED
     bool
     default y
@@ -313,12 +317,16 @@ config SOC_DEDIC_PERIPH_ALWAYS_ENABLE
 
 config SOC_I2C_NUM
     int
-    default 1
+    default 2
 
 config SOC_I2C_FIFO_LEN
     int
     default 32
 
+config SOC_I2C_CMD_REG_NUM
+    int
+    default 8
+
 config SOC_I2C_SUPPORT_SLAVE
     bool
     default y

+ 12 - 0
components/soc/esp32p4/include/soc/clk_tree_defs.h

@@ -278,6 +278,18 @@ typedef enum {
 
 /////////////////////////////////////////////////I2C////////////////////////////////////////////////////////////////////
 
+/**
+ * @brief Array initializer for all supported clock sources of I2C
+ */
+#define SOC_I2C_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_RC_FAST}
+/**
+ * @brief Type of I2C clock source.
+ */
+typedef enum {
+    I2C_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL,                        /*!< Select XTAL as the source clock */
+    I2C_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST,                  /*!< Select RC_FAST as the source clock */
+    I2C_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL,                     /*!< Select XTAL as the default source clock */
+} soc_periph_i2c_clk_src_t;
 /////////////////////////////////////////////////SPI////////////////////////////////////////////////////////////////////
 
 //TODO: IDF-7502

+ 12 - 164
components/soc/esp32p4/include/soc/i2c_struct.h

@@ -1002,12 +1002,12 @@ typedef union {
 
 /** Group: Command registers */
 /** Type of comd0 register
- *  I2C command register 0
+ *  I2C command register 0~7
  */
 typedef union {
     struct {
-        /** command0 : R/W; bitpos: [13:0]; default: 0;
-         *  Configures command 0. It consists of three parts:
+        /** command : R/W; bitpos: [13:0]; default: 0;
+         *  Configures command. It consists of three parts:
          *  op_code is the command,
          *  0: RSTART,
          *  1: WRITE,
@@ -1019,166 +1019,18 @@ typedef union {
          *  ack_check_en, ack_exp and ack are used to control the ACK bit. See I2C cmd
          *  structure for more information.
          */
-        uint32_t command0:14;
+        uint32_t command:14;
         uint32_t reserved_14:17;
-        /** command0_done : R/W/SS; bitpos: [31]; default: 0;
-         *  Represents whether command 0 is done in I2C Master mode.
+        /** command_done : R/W/SS; bitpos: [31]; default: 0;
+         *  Represents whether command is done in I2C Master mode.
          *  0: Not done
          *
          *  1: Done
          */
-        uint32_t command0_done:1;
+        uint32_t command_done:1;
     };
     uint32_t val;
-} i2c_comd0_reg_t;
-
-/** Type of comd1 register
- *  I2C command register 1
- */
-typedef union {
-    struct {
-        /** command1 : R/W; bitpos: [13:0]; default: 0;
-         *  Configures command 1. See details in I2C_CMD0_REG[13:0].
-         */
-        uint32_t command1:14;
-        uint32_t reserved_14:17;
-        /** command1_done : R/W/SS; bitpos: [31]; default: 0;
-         *  Represents whether command 1 is done in I2C Master mode.
-         *  0: Not done
-         *
-         *  1: Done
-         */
-        uint32_t command1_done:1;
-    };
-    uint32_t val;
-} i2c_comd1_reg_t;
-
-/** Type of comd2 register
- *  I2C command register 2
- */
-typedef union {
-    struct {
-        /** command2 : R/W; bitpos: [13:0]; default: 0;
-         *  Configures command 2. See details in I2C_CMD0_REG[13:0].
-         */
-        uint32_t command2:14;
-        uint32_t reserved_14:17;
-        /** command2_done : R/W/SS; bitpos: [31]; default: 0;
-         *  Represents whether command 2 is done in I2C Master mode.
-         *  0: Not done
-         *
-         *  1: Done
-         */
-        uint32_t command2_done:1;
-    };
-    uint32_t val;
-} i2c_comd2_reg_t;
-
-/** Type of comd3 register
- *  I2C command register 3
- */
-typedef union {
-    struct {
-        /** command3 : R/W; bitpos: [13:0]; default: 0;
-         *  Configures command 3. See details in I2C_CMD0_REG[13:0].
-         */
-        uint32_t command3:14;
-        uint32_t reserved_14:17;
-        /** command3_done : R/W/SS; bitpos: [31]; default: 0;
-         *  Represents whether command 3 is done in I2C Master mode.
-         *  0: Not done
-         *
-         *  1: Done
-         */
-        uint32_t command3_done:1;
-    };
-    uint32_t val;
-} i2c_comd3_reg_t;
-
-/** Type of comd4 register
- *  I2C command register 4
- */
-typedef union {
-    struct {
-        /** command4 : R/W; bitpos: [13:0]; default: 0;
-         *  Configures command 4. See details in I2C_CMD0_REG[13:0].
-         */
-        uint32_t command4:14;
-        uint32_t reserved_14:17;
-        /** command4_done : R/W/SS; bitpos: [31]; default: 0;
-         *  Represents whether command 4 is done in I2C Master mode.
-         *  0: Not done
-         *
-         *  1: Done
-         */
-        uint32_t command4_done:1;
-    };
-    uint32_t val;
-} i2c_comd4_reg_t;
-
-/** Type of comd5 register
- *  I2C command register 5
- */
-typedef union {
-    struct {
-        /** command5 : R/W; bitpos: [13:0]; default: 0;
-         *  Configures command 5. See details in I2C_CMD0_REG[13:0].
-         */
-        uint32_t command5:14;
-        uint32_t reserved_14:17;
-        /** command5_done : R/W/SS; bitpos: [31]; default: 0;
-         *  Represents whether command 5 is done in I2C Master mode.
-         *  0: Not done
-         *
-         *  1: Done
-         */
-        uint32_t command5_done:1;
-    };
-    uint32_t val;
-} i2c_comd5_reg_t;
-
-/** Type of comd6 register
- *  I2C command register 6
- */
-typedef union {
-    struct {
-        /** command6 : R/W; bitpos: [13:0]; default: 0;
-         *  Configures command 6. See details in I2C_CMD0_REG[13:0].
-         */
-        uint32_t command6:14;
-        uint32_t reserved_14:17;
-        /** command6_done : R/W/SS; bitpos: [31]; default: 0;
-         *  Represents whether command 6 is done in I2C Master mode.
-         *  0: Not done
-         *
-         *  1: Done
-         */
-        uint32_t command6_done:1;
-    };
-    uint32_t val;
-} i2c_comd6_reg_t;
-
-/** Type of comd7 register
- *  I2C command register 7
- */
-typedef union {
-    struct {
-        /** command7 : R/W; bitpos: [13:0]; default: 0;
-         *  Configures command 7. See details in I2C_CMD0_REG[13:0].
-         */
-        uint32_t command7:14;
-        uint32_t reserved_14:17;
-        /** command7_done : R/W/SS; bitpos: [31]; default: 0;
-         *  Represents whether command 7 is done in I2C Master mode.
-         *  0: Not done
-         *
-         *  1: Done
-         */
-        uint32_t command7_done:1;
-    };
-    uint32_t val;
-} i2c_comd7_reg_t;
-
+} i2c_comd_reg_t;
 
 /** Group: Version register */
 /** Type of date register
@@ -1246,14 +1098,7 @@ typedef struct {
     volatile i2c_scl_stop_setup_reg_t scl_stop_setup;
     volatile i2c_filter_cfg_reg_t filter_cfg;
     uint32_t reserved_054;
-    volatile i2c_comd0_reg_t comd0;
-    volatile i2c_comd1_reg_t comd1;
-    volatile i2c_comd2_reg_t comd2;
-    volatile i2c_comd3_reg_t comd3;
-    volatile i2c_comd4_reg_t comd4;
-    volatile i2c_comd5_reg_t comd5;
-    volatile i2c_comd6_reg_t comd6;
-    volatile i2c_comd7_reg_t comd7;
+    volatile i2c_comd_reg_t command[8];
     volatile i2c_scl_st_time_out_reg_t scl_st_time_out;
     volatile i2c_scl_main_st_time_out_reg_t scl_main_st_time_out;
     volatile i2c_scl_sp_conf_reg_t scl_sp_conf;
@@ -1266,6 +1111,9 @@ typedef struct {
     volatile i2c_rxfifo_start_addr_reg_t rxfifo_start_addr;
 } i2c_dev_t;
 
+extern i2c_dev_t I2C0;
+extern i2c_dev_t I2C1;
+extern i2c_dev_t LP_I2C;
 
 #ifndef __cplusplus
 _Static_assert(sizeof(i2c_dev_t) == 0x184, "Invalid size of i2c_dev_t structure");

+ 4 - 3
components/soc/esp32p4/include/soc/soc_caps.h

@@ -52,7 +52,7 @@
 // #define SOC_SDM_SUPPORTED               1  //TODO: IDF-7551
 // #define SOC_GPSPI_SUPPORTED             1  //TODO: IDF-7502, TODO: IDF-7503
 // #define SOC_LEDC_SUPPORTED              1  //TODO: IDF-6510
-// #define SOC_I2C_SUPPORTED               1  //TODO: IDF-6507, TODO: IDF-7491
+#define SOC_I2C_SUPPORTED               1  //TODO: IDF-6507, TODO: IDF-7491
 #define SOC_SYSTIMER_SUPPORTED          1
 // #define SOC_AES_SUPPORTED               1  //TODO: IDF-6519
 #define SOC_MPI_SUPPORTED               1
@@ -211,10 +211,11 @@
 #define SOC_DEDIC_PERIPH_ALWAYS_ENABLE  (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */
 
 /*-------------------------- I2C CAPS ----------------------------------------*/
-// ESP32-P4 has 1 I2C
-#define SOC_I2C_NUM                 (1U)
+// ESP32-P4 has 2 I2Cs
+#define SOC_I2C_NUM                 (2U)
 
 #define SOC_I2C_FIFO_LEN            (32) /*!< I2C hardware FIFO depth */
+#define SOC_I2C_CMD_REG_NUM         (8)  /*!< Number of I2C command registers */
 #define SOC_I2C_SUPPORT_SLAVE       (1)
 
 // FSM_RST only resets the FSM, not using it. So SOC_I2C_SUPPORT_HW_FSM_RST not defined.

+ 1 - 1
examples/peripherals/i2c/i2c_self_test/main/i2c_example_main.c

@@ -280,7 +280,7 @@ static void i2c_test_task(void *arg)
         ret = i2c_master_write_slave(I2C_MASTER_NUM, data_wr, RW_TEST_LENGTH);
         if (ret == ESP_OK) {
             size = i2c_slave_read_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_PERIOD_MS);
-        }
+    }
         if (ret == ESP_ERR_TIMEOUT) {
             ESP_LOGE(TAG, "I2C Timeout");
         } else if (ret == ESP_OK) {

+ 1 - 0
tools/ci/check_public_headers_exceptions.txt

@@ -194,6 +194,7 @@ components/hal/esp32p4/include/hal/cache_ll.h
 components/hal/esp32p4/include/hal/clk_tree_ll.h
 components/hal/esp32p4/include/hal/ecc_ll.h
 components/hal/esp32p4/include/hal/gpspi_flash_ll.h
+components/hal/esp32p4/include/hal/i2c_ll.h
 components/hal/esp32p4/include/hal/mcpwm_ll.h
 components/hal/esp32p4/include/hal/mpi_ll.h
 components/hal/esp32p4/include/hal/mpu_ll.h