|  | /* | 
|  | * Copyright (c) 2022 ITE Corporation. All Rights Reserved. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT ite_enhance_i2c | 
|  |  | 
|  | #include <zephyr/drivers/gpio.h> | 
|  | #include <zephyr/drivers/i2c.h> | 
|  | #include <zephyr/drivers/pinctrl.h> | 
|  | #include <zephyr/irq.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/pm/policy.h> | 
|  | #include <errno.h> | 
|  | #include <soc.h> | 
|  | #include <soc_dt.h> | 
|  | #include <zephyr/sys/util.h> | 
|  |  | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(i2c_ite_enhance, CONFIG_I2C_LOG_LEVEL); | 
|  |  | 
|  | #include "i2c-priv.h" | 
|  |  | 
|  | /* Start smbus session from idle state */ | 
|  | #define I2C_MSG_START BIT(5) | 
|  |  | 
|  | #define I2C_LINE_SCL_HIGH BIT(0) | 
|  | #define I2C_LINE_SDA_HIGH BIT(1) | 
|  | #define I2C_LINE_IDLE (I2C_LINE_SCL_HIGH | I2C_LINE_SDA_HIGH) | 
|  |  | 
|  | #ifdef CONFIG_I2C_IT8XXX2_CQ_MODE | 
|  | /* Reserved 5 bytes for ID and CMD_x. */ | 
|  | #define I2C_CQ_MODE_TX_MAX_PAYLOAD_SIZE  (CONFIG_I2C_CQ_MODE_MAX_PAYLOAD_SIZE - 5) | 
|  |  | 
|  | /* Repeat Start. */ | 
|  | #define I2C_CQ_CMD_L_RS BIT(7) | 
|  | /* | 
|  | * R/W (Read/ Write) decides the I2C read or write direction. | 
|  | * 1: read, 0: write | 
|  | */ | 
|  | #define I2C_CQ_CMD_L_RW BIT(6) | 
|  | /* P (STOP) is the I2C STOP condition. */ | 
|  | #define I2C_CQ_CMD_L_P  BIT(5) | 
|  | /* E (End) is this device end flag. */ | 
|  | #define I2C_CQ_CMD_L_E  BIT(4) | 
|  | /* LA (Last ACK) is Last ACK in master receiver. */ | 
|  | #define I2C_CQ_CMD_L_LA BIT(3) | 
|  | /* bit[2:0] are number of transfer out or receive data which depends on R/W. */ | 
|  | #define I2C_CQ_CMD_L_NUM_BIT_2_0 GENMASK(2, 0) | 
|  |  | 
|  | struct i2c_cq_packet { | 
|  | uint8_t id; | 
|  | uint8_t cmd_l; | 
|  | uint8_t cmd_h; | 
|  | FLEXIBLE_ARRAY_DECLARE(uint8_t, wdata); | 
|  | }; | 
|  | #endif /* CONFIG_I2C_IT8XXX2_CQ_MODE */ | 
|  |  | 
|  | struct i2c_enhance_config { | 
|  | void (*irq_config_func)(void); | 
|  | uint32_t bitrate; | 
|  | uint8_t *base; | 
|  | uint8_t i2c_irq_base; | 
|  | uint8_t port; | 
|  | uint8_t channel_switch_sel; | 
|  | /* SCL GPIO cells */ | 
|  | struct gpio_dt_spec scl_gpios; | 
|  | /* SDA GPIO cells */ | 
|  | struct gpio_dt_spec sda_gpios; | 
|  | /* I2C alternate configuration */ | 
|  | const struct pinctrl_dev_config *pcfg; | 
|  | uint8_t prescale_scl_low; | 
|  | uint8_t data_hold_time; | 
|  | uint32_t clock_gate_offset; | 
|  | int transfer_timeout_ms; | 
|  | bool target_enable; | 
|  | bool target_pio_mode; | 
|  | bool push_pull_recovery; | 
|  | }; | 
|  |  | 
|  | enum i2c_pin_fun { | 
|  | SCL = 0, | 
|  | SDA, | 
|  | }; | 
|  |  | 
|  | enum i2c_ch_status { | 
|  | I2C_CH_NORMAL = 0, | 
|  | I2C_CH_REPEAT_START, | 
|  | I2C_CH_WAIT_READ, | 
|  | I2C_CH_WAIT_NEXT_XFER, | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_I2C_IT8XXX2_CQ_MODE | 
|  | struct i2c_host_cq_buffer { | 
|  | /* Command queue tx payload. */ | 
|  | uint8_t i2c_cq_mode_tx_dlm[CONFIG_I2C_CQ_MODE_MAX_PAYLOAD_SIZE] __aligned(4); | 
|  | /* Command queue rx payload. */ | 
|  | uint8_t i2c_cq_mode_rx_dlm[CONFIG_I2C_CQ_MODE_MAX_PAYLOAD_SIZE] __aligned(4); | 
|  | }; | 
|  | #endif | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | /* | 
|  | * When accessing data exceeds the maximum buffer, the actual reload address | 
|  | * is one byte more than the maximum buffer size. Therefore, it is necessary to | 
|  | * have a buffer in place to prevent overwriting other memory. | 
|  | */ | 
|  | #define PROTECT_MEM_BUF 4 | 
|  | struct i2c_target_dma_buffer { | 
|  | /* Target mode DMA output buffer. */ | 
|  | uint8_t __aligned(4) | 
|  | out_buffer[CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE + PROTECT_MEM_BUF]; | 
|  | /* Target mode DMA input buffer. */ | 
|  | uint8_t __aligned(4) | 
|  | in_buffer[CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE + PROTECT_MEM_BUF]; | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | struct i2c_enhance_data { | 
|  | enum i2c_ch_status i2ccs; | 
|  | struct i2c_msg *active_msg; | 
|  | struct k_mutex mutex; | 
|  | struct k_sem device_sync_sem; | 
|  | /* Index into output data */ | 
|  | size_t widx; | 
|  | /* Index into input data */ | 
|  | size_t ridx; | 
|  | /* operation freq of i2c */ | 
|  | uint32_t bus_freq; | 
|  | /* Error code, if any */ | 
|  | uint32_t err; | 
|  | /* address of device */ | 
|  | uint16_t addr_16bit; | 
|  | /* wait for stop bit interrupt */ | 
|  | uint8_t stop; | 
|  | /* Number of messages. */ | 
|  | uint8_t num_msgs; | 
|  | #ifdef CONFIG_I2C_IT8XXX2_CQ_MODE | 
|  | /* Store command queue mode messages. */ | 
|  | struct i2c_msg *cq_msgs; | 
|  | #endif | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | struct i2c_target_config *target_cfg; | 
|  | uint32_t buffer_size; | 
|  | int target_nack; | 
|  | bool target_attached; | 
|  | #endif | 
|  | union { | 
|  | #ifdef CONFIG_I2C_IT8XXX2_CQ_MODE | 
|  | struct i2c_host_cq_buffer host_buffer; | 
|  | #endif | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | struct i2c_target_dma_buffer target_buffer; | 
|  | #endif | 
|  | }; | 
|  | }; | 
|  |  | 
|  | enum enhanced_i2c_transfer_direct { | 
|  | TX_DIRECT, | 
|  | RX_DIRECT, | 
|  | }; | 
|  |  | 
|  | enum enhanced_i2c_ctl { | 
|  | /* Hardware reset */ | 
|  | E_HW_RST = 0x01, | 
|  | /* Stop */ | 
|  | E_STOP = 0x02, | 
|  | /* Start & Repeat start */ | 
|  | E_START = 0x04, | 
|  | /* Acknowledge */ | 
|  | E_ACK = 0x08, | 
|  | /* State reset */ | 
|  | E_STS_RST = 0x10, | 
|  | /* Mode select */ | 
|  | E_MODE_SEL = 0x20, | 
|  | /* I2C interrupt enable */ | 
|  | E_INT_EN = 0x40, | 
|  | /* 0 : Standard mode , 1 : Receive mode */ | 
|  | E_RX_MODE = 0x80, | 
|  | /* State reset and hardware reset */ | 
|  | E_STS_AND_HW_RST = (E_STS_RST | E_HW_RST), | 
|  | /* Generate start condition and transmit slave address */ | 
|  | E_START_ID = (E_INT_EN | E_MODE_SEL | E_ACK | E_START | E_HW_RST), | 
|  | /* Generate stop condition */ | 
|  | E_FINISH = (E_INT_EN | E_MODE_SEL | E_ACK | E_STOP | E_HW_RST), | 
|  | /* Start with command queue mode */ | 
|  | E_START_CQ = (E_INT_EN | E_MODE_SEL | E_ACK | E_START), | 
|  | }; | 
|  |  | 
|  | enum enhanced_i2c_host_status { | 
|  | /* ACK receive */ | 
|  | E_HOSTA_ACK = 0x01, | 
|  | /* Interrupt pending */ | 
|  | E_HOSTA_INTP = 0x02, | 
|  | /* Read/Write */ | 
|  | E_HOSTA_RW = 0x04, | 
|  | /* Time out error */ | 
|  | E_HOSTA_TMOE = 0x08, | 
|  | /* Arbitration lost */ | 
|  | E_HOSTA_ARB = 0x10, | 
|  | /* Bus busy */ | 
|  | E_HOSTA_BB = 0x20, | 
|  | /* Address match */ | 
|  | E_HOSTA_AM = 0x40, | 
|  | /* Byte done status */ | 
|  | E_HOSTA_BDS = 0x80, | 
|  | /* time out or lost arbitration */ | 
|  | E_HOSTA_ANY_ERROR = (E_HOSTA_TMOE | E_HOSTA_ARB), | 
|  | /* Byte transfer done and ACK receive */ | 
|  | E_HOSTA_BDS_AND_ACK = (E_HOSTA_BDS | E_HOSTA_ACK), | 
|  | }; | 
|  |  | 
|  | enum i2c_reset_cause { | 
|  | I2C_RC_NO_IDLE_FOR_START = 1, | 
|  | I2C_RC_TIMEOUT, | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | enum enhanced_i2c_target_status { | 
|  | /* Time out error */ | 
|  | E_TARGET_TMOE = 0x08, | 
|  | /* Arbitration lost */ | 
|  | E_TARGET_ARB = 0x10, | 
|  | /* Time out or lost arbitration */ | 
|  | E_TARGET_ANY_ERROR = (E_TARGET_TMOE | E_TARGET_ARB), | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | static int i2c_parsing_return_value(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  |  | 
|  | if (!data->err) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Connection timed out */ | 
|  | if (data->err == ETIMEDOUT) { | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | /* The device does not respond ACK */ | 
|  | if (data->err == E_HOSTA_ACK) { | 
|  | return -ENXIO; | 
|  | } else { | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int i2c_get_line_levels(const struct device *dev) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  | int pin_sts = 0; | 
|  |  | 
|  | if (IT8XXX2_I2C_TOS(base) & IT8XXX2_I2C_SCL_IN) { | 
|  | pin_sts |= I2C_LINE_SCL_HIGH; | 
|  | } | 
|  |  | 
|  | if (IT8XXX2_I2C_TOS(base) & IT8XXX2_I2C_SDA_IN) { | 
|  | pin_sts |= I2C_LINE_SDA_HIGH; | 
|  | } | 
|  |  | 
|  | return pin_sts; | 
|  | } | 
|  |  | 
|  | static int i2c_is_busy(const struct device *dev) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | return (IT8XXX2_I2C_STR(base) & E_HOSTA_BB); | 
|  | } | 
|  |  | 
|  | static int i2c_bus_not_available(const struct device *dev) | 
|  | { | 
|  | if (i2c_is_busy(dev) || | 
|  | (i2c_get_line_levels(dev) != I2C_LINE_IDLE)) { | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void i2c_reset(const struct device *dev) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | /* State reset and hardware reset */ | 
|  | IT8XXX2_I2C_CTR(base) = E_STS_AND_HW_RST; | 
|  | } | 
|  |  | 
|  | /* Set clock frequency for i2c port D, E , or F */ | 
|  | static void i2c_enhanced_port_set_frequency(const struct device *dev, | 
|  | int freq_hz) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint32_t clk_div, psr, pll_clock, psr_h, psr_l; | 
|  | uint8_t *base = config->base; | 
|  | uint8_t prescale_scl = config->prescale_scl_low; | 
|  |  | 
|  | pll_clock = chip_get_pll_freq(); | 
|  | /* | 
|  | * Let psr(Prescale) = IT8XXX2_I2C_PSR(p_ch) | 
|  | * Then, 1 SCL cycle = 2 x (psr + 2) x SMBus clock cycle | 
|  | * SMBus clock = pll_clock / clk_div | 
|  | * SMBus clock cycle = 1 / SMBus clock | 
|  | * 1 SCL cycle = 1 / freq | 
|  | * 1 / freq = 2 x (psr + 2) x (1 / (pll_clock / clk_div)) | 
|  | * psr = ((pll_clock / clk_div) x (1 / freq) x (1 / 2)) - 2 | 
|  | */ | 
|  | if (freq_hz) { | 
|  | /* Get SMBus clock divide value */ | 
|  | clk_div = (IT8XXX2_ECPM_SCDCR2 & 0x0F) + 1U; | 
|  | /* Calculate PSR value */ | 
|  | psr = (pll_clock / (clk_div * (2U * freq_hz))) - 2U; | 
|  | /* Set psr value under 0xFD */ | 
|  | if (psr > 0xFD) { | 
|  | psr = 0xFD; | 
|  | } | 
|  |  | 
|  | /* Adjust SCL low period prescale */ | 
|  | psr_l = psr + prescale_scl; | 
|  | if (psr_l > 0xFD) { | 
|  | psr_l = 0xFD; | 
|  | LOG_WRN("(psr + prescale_scl) can not be greater than 0xfd."); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Adjust SCL high period prescale | 
|  | * The property setting prescale_scl must be less than psr and | 
|  | * the minimum value of psr_h is 2. | 
|  | */ | 
|  | if (psr > (prescale_scl + 2)) { | 
|  | psr_h = psr - prescale_scl; | 
|  | } else { | 
|  | psr_h = 2; | 
|  | LOG_WRN("prescale_scl_low should be less than (psr - 2)."); | 
|  | } | 
|  |  | 
|  | /* Set I2C Speed for SCL low period. */ | 
|  | IT8XXX2_I2C_PSR(base) = psr_l & 0xFF; | 
|  | /* Set I2C Speed for SCL high period. */ | 
|  | IT8XXX2_I2C_HSPR(base) = psr_h & 0xFF; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | static int i2c_enhance_configure(const struct device *dev, | 
|  | uint32_t dev_config_raw) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | struct i2c_enhance_data *const data = dev->data; | 
|  |  | 
|  | if (!(I2C_MODE_CONTROLLER & dev_config_raw)) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (I2C_ADDR_10_BITS & dev_config_raw) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | data->bus_freq = I2C_SPEED_GET(dev_config_raw); | 
|  |  | 
|  | i2c_enhanced_port_set_frequency(dev, config->bitrate); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_enhance_get_config(const struct device *dev, uint32_t *dev_config) | 
|  | { | 
|  | struct i2c_enhance_data *const data = dev->data; | 
|  | uint32_t speed; | 
|  |  | 
|  | if (!data->bus_freq) { | 
|  | LOG_ERR("The bus frequency is not initially configured."); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | switch (data->bus_freq) { | 
|  | case I2C_SPEED_DT: | 
|  | case I2C_SPEED_STANDARD: | 
|  | case I2C_SPEED_FAST: | 
|  | case I2C_SPEED_FAST_PLUS: | 
|  | speed = I2C_SPEED_SET(data->bus_freq); | 
|  | break; | 
|  | default: | 
|  | return -ERANGE; | 
|  | } | 
|  |  | 
|  | *dev_config = (I2C_MODE_CONTROLLER | speed); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int enhanced_i2c_error(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  | uint32_t i2c_str = IT8XXX2_I2C_STR(base); | 
|  |  | 
|  | if (i2c_str & E_HOSTA_ANY_ERROR) { | 
|  | data->err = i2c_str & E_HOSTA_ANY_ERROR; | 
|  | /* device does not respond ACK */ | 
|  | } else if ((i2c_str & E_HOSTA_BDS_AND_ACK) == E_HOSTA_BDS) { | 
|  | if (IT8XXX2_I2C_CTR(base) & E_ACK) { | 
|  | data->err = E_HOSTA_ACK; | 
|  | /* STOP */ | 
|  | IT8XXX2_I2C_CTR(base) = E_FINISH; | 
|  | } | 
|  | } | 
|  |  | 
|  | return data->err; | 
|  | } | 
|  |  | 
|  | static void enhanced_i2c_start(const struct device *dev) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | /* reset i2c port */ | 
|  | i2c_reset(dev); | 
|  | /* Set i2c frequency */ | 
|  | i2c_enhanced_port_set_frequency(dev, config->bitrate); | 
|  | /* | 
|  | * Set time out register. | 
|  | * I2C D/E/F clock/data low timeout. | 
|  | */ | 
|  | IT8XXX2_I2C_TOR(base) = I2C_CLK_LOW_TIMEOUT; | 
|  | /* bit1: Enable enhanced i2c module */ | 
|  | IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_MDL_EN; | 
|  | } | 
|  |  | 
|  | static void i2c_pio_trans_data(const struct device *dev, | 
|  | enum enhanced_i2c_transfer_direct direct, | 
|  | uint16_t trans_data, int first_byte) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  | uint32_t nack = 0; | 
|  |  | 
|  | if (first_byte) { | 
|  | /* First byte must be slave address. */ | 
|  | IT8XXX2_I2C_DTR(base) = trans_data | | 
|  | (direct == RX_DIRECT ? BIT(0) : 0); | 
|  | /* start or repeat start signal. */ | 
|  | IT8XXX2_I2C_CTR(base) = E_START_ID; | 
|  | } else { | 
|  | if (direct == TX_DIRECT) { | 
|  | /* Transmit data */ | 
|  | IT8XXX2_I2C_DTR(base) = (uint8_t)trans_data; | 
|  | } else { | 
|  | /* | 
|  | * Receive data. | 
|  | * Last byte should be NACK in the end of read cycle | 
|  | */ | 
|  | if (((data->ridx + 1) == data->active_msg->len) && | 
|  | (data->active_msg->flags & I2C_MSG_STOP)) { | 
|  | nack = 1; | 
|  | } | 
|  | } | 
|  | /* Set hardware reset to start next transmission */ | 
|  | IT8XXX2_I2C_CTR(base) = E_INT_EN | E_MODE_SEL | | 
|  | E_HW_RST | (nack ? 0 : E_ACK); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int enhanced_i2c_tran_read(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  | uint8_t in_data = 0; | 
|  |  | 
|  | if (data->active_msg->flags & I2C_MSG_START) { | 
|  | /* clear start flag */ | 
|  | data->active_msg->flags &= ~I2C_MSG_START; | 
|  | enhanced_i2c_start(dev); | 
|  | /* Direct read  */ | 
|  | data->i2ccs = I2C_CH_WAIT_READ; | 
|  | /* Send ID */ | 
|  | i2c_pio_trans_data(dev, RX_DIRECT, data->addr_16bit << 1, 1); | 
|  | } else { | 
|  | if (data->i2ccs) { | 
|  | if (data->i2ccs == I2C_CH_WAIT_READ) { | 
|  | data->i2ccs = I2C_CH_NORMAL; | 
|  | /* Receive data */ | 
|  | i2c_pio_trans_data(dev, RX_DIRECT, in_data, 0); | 
|  |  | 
|  | /* data->active_msg->flags == I2C_MSG_RESTART */ | 
|  | } else { | 
|  | /* Write to read */ | 
|  | data->i2ccs = I2C_CH_WAIT_READ; | 
|  | /* Send ID */ | 
|  | i2c_pio_trans_data(dev, RX_DIRECT, | 
|  | data->addr_16bit << 1, 1); | 
|  | } | 
|  | } else { | 
|  | if (data->ridx < data->active_msg->len) { | 
|  | /* read data */ | 
|  | *(data->active_msg->buf++) = IT8XXX2_I2C_DRR(base); | 
|  | data->ridx++; | 
|  | /* done */ | 
|  | if (data->ridx == data->active_msg->len) { | 
|  | data->active_msg->len = 0; | 
|  | if (data->active_msg->flags & I2C_MSG_STOP) { | 
|  | data->i2ccs = I2C_CH_NORMAL; | 
|  | IT8XXX2_I2C_CTR(base) = E_FINISH; | 
|  | /* wait for stop bit interrupt */ | 
|  | data->stop = 1; | 
|  | return 1; | 
|  | } | 
|  | /* End the transaction */ | 
|  | data->i2ccs = I2C_CH_WAIT_READ; | 
|  | return 0; | 
|  | } | 
|  | /* read next byte */ | 
|  | i2c_pio_trans_data(dev, RX_DIRECT, in_data, 0); | 
|  | } | 
|  | } | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int enhanced_i2c_tran_write(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  | uint8_t out_data; | 
|  |  | 
|  | if (data->active_msg->flags & I2C_MSG_START) { | 
|  | /* Clear start bit */ | 
|  | data->active_msg->flags &= ~I2C_MSG_START; | 
|  | enhanced_i2c_start(dev); | 
|  | /* Send ID */ | 
|  | i2c_pio_trans_data(dev, TX_DIRECT, data->addr_16bit << 1, 1); | 
|  | } else { | 
|  | /* Host has completed the transmission of a byte */ | 
|  | if (data->widx < data->active_msg->len) { | 
|  | out_data = *(data->active_msg->buf++); | 
|  | data->widx++; | 
|  |  | 
|  | /* Send Byte */ | 
|  | i2c_pio_trans_data(dev, TX_DIRECT, out_data, 0); | 
|  | if (data->i2ccs == I2C_CH_WAIT_NEXT_XFER) { | 
|  | data->i2ccs = I2C_CH_NORMAL; | 
|  | } | 
|  | } else { | 
|  | /* done */ | 
|  | data->active_msg->len = 0; | 
|  | if (data->active_msg->flags & I2C_MSG_STOP) { | 
|  | IT8XXX2_I2C_CTR(base) = E_FINISH; | 
|  | /* wait for stop bit interrupt */ | 
|  | data->stop = 1; | 
|  | } else { | 
|  | /* Direct write with direct read */ | 
|  | data->i2ccs = I2C_CH_WAIT_NEXT_XFER; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int i2c_transaction(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | /* no error */ | 
|  | if (!(enhanced_i2c_error(dev))) { | 
|  | if (!data->stop) { | 
|  | /* | 
|  | * The return value indicates if there is more data | 
|  | * to be read or written. If the return value = 1, | 
|  | * it means that the interrupt cannot be disable and | 
|  | * continue to transmit data. | 
|  | */ | 
|  | if (data->active_msg->flags & I2C_MSG_READ) { | 
|  | return enhanced_i2c_tran_read(dev); | 
|  | } else { | 
|  | return enhanced_i2c_tran_write(dev); | 
|  | } | 
|  | } | 
|  | } | 
|  | /* reset i2c port */ | 
|  | i2c_reset(dev); | 
|  | IT8XXX2_I2C_CTR1(base) = 0; | 
|  |  | 
|  | data->stop = 0; | 
|  | /* done doing work */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_enhance_pio_transfer(const struct device *dev, | 
|  | struct i2c_msg *msgs) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | int res; | 
|  |  | 
|  | if (data->i2ccs == I2C_CH_NORMAL) { | 
|  | struct i2c_msg *start_msg = &msgs[0]; | 
|  |  | 
|  | start_msg->flags |= I2C_MSG_START; | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < data->num_msgs; i++) { | 
|  |  | 
|  | data->widx = 0; | 
|  | data->ridx = 0; | 
|  | data->err = 0; | 
|  | data->active_msg = &msgs[i]; | 
|  |  | 
|  | /* | 
|  | * Start transaction. | 
|  | * The return value indicates if the initial configuration | 
|  | * of I2C transaction for read or write has been completed. | 
|  | */ | 
|  | if (i2c_transaction(dev)) { | 
|  | /* Enable I2C interrupt. */ | 
|  | irq_enable(config->i2c_irq_base); | 
|  | } | 
|  | /* Wait for the transfer to complete */ | 
|  | res = k_sem_take(&data->device_sync_sem, K_MSEC(config->transfer_timeout_ms)); | 
|  | /* | 
|  | * The irq will be enabled at the condition of start or | 
|  | * repeat start of I2C. If timeout occurs without being | 
|  | * wake up during suspend(ex: interrupt is not fired), | 
|  | * the irq should be disabled immediately. | 
|  | */ | 
|  | irq_disable(config->i2c_irq_base); | 
|  | /* | 
|  | * The transaction is dropped on any error(timeout, NACK, fail, | 
|  | * bus error, device error). | 
|  | */ | 
|  | if (data->err) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (res != 0) { | 
|  | data->err = ETIMEDOUT; | 
|  | /* reset i2c port */ | 
|  | i2c_reset(dev); | 
|  | LOG_ERR("I2C ch%d:0x%X reset cause %d", | 
|  | config->port, data->addr_16bit, I2C_RC_TIMEOUT); | 
|  | /* If this message is sent fail, drop the transaction. */ | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* reset i2c channel status */ | 
|  | if (data->err || (data->active_msg->flags & I2C_MSG_STOP)) { | 
|  | data->i2ccs = I2C_CH_NORMAL; | 
|  | } | 
|  |  | 
|  | return data->err; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_IT8XXX2_CQ_MODE | 
|  | static void enhanced_i2c_set_cmd_addr_regs(const struct device *dev) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | struct i2c_host_cq_buffer *host_buffer = &data->host_buffer; | 
|  | uint32_t dlm_base; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | /* Set "Address Register" to store the I2C data. */ | 
|  | dlm_base = (uint32_t)host_buffer->i2c_cq_mode_rx_dlm & 0xffffff; | 
|  | IT8XXX2_I2C_RAMH2A(base) = (dlm_base >> 16) & 0xff; | 
|  | IT8XXX2_I2C_RAMHA(base) = (dlm_base >> 8) & 0xff; | 
|  | IT8XXX2_I2C_RAMLA(base) = dlm_base & 0xff; | 
|  |  | 
|  | /* Set "Command Address Register" to get commands. */ | 
|  | dlm_base = (uint32_t)host_buffer->i2c_cq_mode_tx_dlm & 0xffffff; | 
|  | IT8XXX2_I2C_CMD_ADDH2(base) = (dlm_base >> 16) & 0xff; | 
|  | IT8XXX2_I2C_CMD_ADDH(base) = (dlm_base >> 8) & 0xff; | 
|  | IT8XXX2_I2C_CMD_ADDL(base) = dlm_base & 0xff; | 
|  | } | 
|  |  | 
|  | static void enhanced_i2c_cq_write(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | struct i2c_host_cq_buffer *host_buffer = &data->host_buffer; | 
|  | struct i2c_cq_packet *i2c_cq_pckt; | 
|  | uint8_t num_bit_2_0 = (data->cq_msgs[0].len - 1) & I2C_CQ_CMD_L_NUM_BIT_2_0; | 
|  | uint8_t num_bit_10_3 = ((data->cq_msgs[0].len - 1) >> 3) & 0xff; | 
|  |  | 
|  | i2c_cq_pckt = (struct i2c_cq_packet *)host_buffer->i2c_cq_mode_tx_dlm; | 
|  | /* Set commands in RAM. */ | 
|  | i2c_cq_pckt->id = data->addr_16bit << 1; | 
|  | i2c_cq_pckt->cmd_l = I2C_CQ_CMD_L_P | I2C_CQ_CMD_L_E | num_bit_2_0; | 
|  | i2c_cq_pckt->cmd_h = num_bit_10_3; | 
|  | for (int i = 0; i < data->cq_msgs[0].len; i++) { | 
|  | i2c_cq_pckt->wdata[i] = data->cq_msgs[0].buf[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void enhanced_i2c_cq_read(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | struct i2c_host_cq_buffer *host_buffer = &data->host_buffer; | 
|  | struct i2c_cq_packet *i2c_cq_pckt; | 
|  | uint8_t num_bit_2_0 = (data->cq_msgs[0].len - 1) & I2C_CQ_CMD_L_NUM_BIT_2_0; | 
|  | uint8_t num_bit_10_3 = ((data->cq_msgs[0].len - 1) >> 3) & 0xff; | 
|  |  | 
|  | i2c_cq_pckt = (struct i2c_cq_packet *)host_buffer->i2c_cq_mode_tx_dlm; | 
|  | /* Set commands in RAM. */ | 
|  | i2c_cq_pckt->id = data->addr_16bit << 1; | 
|  | i2c_cq_pckt->cmd_l = I2C_CQ_CMD_L_RW | I2C_CQ_CMD_L_P | | 
|  | I2C_CQ_CMD_L_E | num_bit_2_0; | 
|  | i2c_cq_pckt->cmd_h = num_bit_10_3; | 
|  | } | 
|  |  | 
|  | static void enhanced_i2c_cq_write_to_read(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | struct i2c_host_cq_buffer *host_buffer = &data->host_buffer; | 
|  | struct i2c_cq_packet *i2c_cq_pckt; | 
|  | uint8_t num_bit_2_0 = (data->cq_msgs[0].len - 1) & I2C_CQ_CMD_L_NUM_BIT_2_0; | 
|  | uint8_t num_bit_10_3 = ((data->cq_msgs[0].len - 1) >> 3) & 0xff; | 
|  | int i; | 
|  |  | 
|  | i2c_cq_pckt = (struct i2c_cq_packet *)host_buffer->i2c_cq_mode_tx_dlm; | 
|  | /* Set commands in RAM. (command byte for write) */ | 
|  | i2c_cq_pckt->id = data->addr_16bit << 1; | 
|  | i2c_cq_pckt->cmd_l = num_bit_2_0; | 
|  | i2c_cq_pckt->cmd_h = num_bit_10_3; | 
|  | for (i = 0; i < data->cq_msgs[0].len; i++) { | 
|  | i2c_cq_pckt->wdata[i] = data->cq_msgs[0].buf[i]; | 
|  | } | 
|  |  | 
|  | /* Set commands in RAM. (command byte for read) */ | 
|  | num_bit_2_0 = (data->cq_msgs[1].len - 1) & I2C_CQ_CMD_L_NUM_BIT_2_0; | 
|  | num_bit_10_3 = ((data->cq_msgs[1].len - 1) >> 3) & 0xff; | 
|  | i2c_cq_pckt->wdata[i++] = I2C_CQ_CMD_L_RS | I2C_CQ_CMD_L_RW | | 
|  | I2C_CQ_CMD_L_P | I2C_CQ_CMD_L_E | num_bit_2_0; | 
|  | i2c_cq_pckt->wdata[i] = num_bit_10_3; | 
|  | } | 
|  |  | 
|  | static int enhanced_i2c_cq_isr(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | struct i2c_host_cq_buffer *host_buffer = &data->host_buffer; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | /* Device 1 finish IRQ. */ | 
|  | if (IT8XXX2_I2C_FST(base) & IT8XXX2_I2C_FST_DEV1_IRQ) { | 
|  | uint8_t msgs_idx = data->num_msgs - 1; | 
|  |  | 
|  | /* Get data if this is a read transaction. */ | 
|  | if (data->cq_msgs[msgs_idx].flags & I2C_MSG_READ) { | 
|  | for (int i = 0; i < data->cq_msgs[msgs_idx].len; i++) { | 
|  | data->cq_msgs[msgs_idx].buf[i] = | 
|  | host_buffer->i2c_cq_mode_rx_dlm[i]; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | /* Device 1 error have occurred. eg. nack, timeout... */ | 
|  | if (IT8XXX2_I2C_NST(base) & IT8XXX2_I2C_NST_ID_NACK) { | 
|  | data->err = E_HOSTA_ACK; | 
|  | } else { | 
|  | data->err = IT8XXX2_I2C_STR(base) & | 
|  | E_HOSTA_ANY_ERROR; | 
|  | } | 
|  | } | 
|  | /* Reset bus. */ | 
|  | IT8XXX2_I2C_CTR(base) = E_STS_AND_HW_RST; | 
|  | IT8XXX2_I2C_CTR1(base) = 0; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int enhanced_i2c_cmd_queue_trans(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | /* State reset and hardware reset. */ | 
|  | IT8XXX2_I2C_CTR(base) = E_STS_AND_HW_RST; | 
|  | /* Set "PSR" registers to decide the i2c speed. */ | 
|  | i2c_enhanced_port_set_frequency(dev, config->bitrate); | 
|  | /* Set time out register. port D, E, or F clock/data low timeout. */ | 
|  | IT8XXX2_I2C_TOR(base) = I2C_CLK_LOW_TIMEOUT; | 
|  |  | 
|  | if (data->num_msgs == 2) { | 
|  | /* I2C write to read of command queue mode. */ | 
|  | enhanced_i2c_cq_write_to_read(dev); | 
|  | } else { | 
|  | /* I2C read of command queue mode. */ | 
|  | if (data->cq_msgs[0].flags & I2C_MSG_READ) { | 
|  | enhanced_i2c_cq_read(dev); | 
|  | /* I2C write of command queue mode. */ | 
|  | } else { | 
|  | enhanced_i2c_cq_write(dev); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Enable i2c module with command queue mode. */ | 
|  | IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_MDL_EN | IT8XXX2_I2C_COMQ_EN; | 
|  | /* One shot on device 1. */ | 
|  | IT8XXX2_I2C_MODE_SEL(base) = 0; | 
|  | IT8XXX2_I2C_CTR2(base) = 1; | 
|  | /* | 
|  | * The EC processor(CPU) cannot be in the k_cpu_idle() and power | 
|  | * policy during the transactions with the CQ mode(DMA mode). | 
|  | * Otherwise, the EC processor would be clock gated. | 
|  | */ | 
|  | chip_block_idle(); | 
|  | pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); | 
|  | /* Start */ | 
|  | IT8XXX2_I2C_CTR(base) = E_START_CQ; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int i2c_enhance_cq_transfer(const struct device *dev, | 
|  | struct i2c_msg *msgs) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | int res = 0; | 
|  |  | 
|  | data->err = 0; | 
|  | data->cq_msgs = msgs; | 
|  |  | 
|  | /* Start transaction */ | 
|  | if (enhanced_i2c_cmd_queue_trans(dev)) { | 
|  | /* Enable i2c interrupt */ | 
|  | irq_enable(config->i2c_irq_base); | 
|  | } | 
|  | /* Wait for the transfer to complete */ | 
|  | res = k_sem_take(&data->device_sync_sem, K_MSEC(config->transfer_timeout_ms)); | 
|  |  | 
|  | irq_disable(config->i2c_irq_base); | 
|  |  | 
|  | if (res != 0) { | 
|  | data->err = ETIMEDOUT; | 
|  | /* Reset i2c port. */ | 
|  | i2c_reset(dev); | 
|  | LOG_ERR("I2C ch%d:0x%X reset cause %d", | 
|  | config->port, data->addr_16bit, I2C_RC_TIMEOUT); | 
|  | } | 
|  |  | 
|  | /* Permit to enter power policy and idle mode. */ | 
|  | pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); | 
|  | chip_permit_idle(); | 
|  |  | 
|  | return data->err; | 
|  | } | 
|  |  | 
|  | static bool cq_mode_allowed(const struct device *dev, struct i2c_msg *msgs) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  |  | 
|  | /* | 
|  | * If the transaction of write or read is divided into two | 
|  | * transfers(not two messages), the command queue mode does | 
|  | * not support. | 
|  | */ | 
|  | if (data->i2ccs != I2C_CH_NORMAL) { | 
|  | return false; | 
|  | } | 
|  | /* | 
|  | * When there is only one message, use the command queue transfer | 
|  | * directly. | 
|  | */ | 
|  | if (data->num_msgs == 1 && (msgs[0].flags & I2C_MSG_STOP)) { | 
|  | /* Read transfer payload too long, use PIO mode */ | 
|  | if (((msgs[0].flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) && | 
|  | (msgs[0].len > CONFIG_I2C_CQ_MODE_MAX_PAYLOAD_SIZE)) { | 
|  | return false; | 
|  | } | 
|  | /* Write transfer payload too long, use PIO mode */ | 
|  | if (((msgs[0].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) && | 
|  | (msgs[0].len > I2C_CQ_MODE_TX_MAX_PAYLOAD_SIZE)) { | 
|  | return false; | 
|  | } | 
|  | /* | 
|  | * Write of I2C target address without writing data, used by | 
|  | * cmd_i2c_scan. Use PIO mode. | 
|  | */ | 
|  | if (((msgs[0].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) && | 
|  | (msgs[0].len == 0)) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | /* | 
|  | * When there are two messages, we need to judge whether or not there | 
|  | * is I2C_MSG_RESTART flag from the second message, and then decide to | 
|  | * do the command queue or PIO mode transfer. | 
|  | */ | 
|  | if (data->num_msgs == 2) { | 
|  | /* | 
|  | * The first of two messages must be write. | 
|  | * If the length of write to read transfer is greater than | 
|  | * command queue payload size, there will execute PIO mode. | 
|  | */ | 
|  | if (((msgs[0].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) && | 
|  | (msgs[0].len <= I2C_CQ_MODE_TX_MAX_PAYLOAD_SIZE)) { | 
|  | /* | 
|  | * The transfer is i2c_burst_read(). | 
|  | * | 
|  | * e.g. msg[0].flags = I2C_MSG_WRITE; | 
|  | *      msg[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | | 
|  | *                     I2C_MSG_STOP; | 
|  | */ | 
|  | if ((msgs[1].flags & I2C_MSG_RESTART) && | 
|  | ((msgs[1].flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) && | 
|  | (msgs[1].flags & I2C_MSG_STOP) && | 
|  | (msgs[1].len <= CONFIG_I2C_CQ_MODE_MAX_PAYLOAD_SIZE)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | #endif /* CONFIG_I2C_IT8XXX2_CQ_MODE */ | 
|  |  | 
|  | static int i2c_enhance_transfer(const struct device *dev, | 
|  | struct i2c_msg *msgs, | 
|  | uint8_t num_msgs, uint16_t addr) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | int ret; | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | if (data->target_attached) { | 
|  | LOG_ERR("Device is registered as target"); | 
|  | return -EBUSY; | 
|  | } | 
|  | #endif | 
|  | /* Lock mutex of i2c controller */ | 
|  | k_mutex_lock(&data->mutex, K_FOREVER); | 
|  |  | 
|  | data->num_msgs = num_msgs; | 
|  | data->addr_16bit = addr; | 
|  | /* | 
|  | * If the transaction of write to read is divided into two | 
|  | * transfers, the repeat start transfer uses this flag to | 
|  | * exclude checking bus busy. | 
|  | */ | 
|  | if (data->i2ccs == I2C_CH_NORMAL) { | 
|  | /* Make sure we're in a good state to start */ | 
|  | if (i2c_bus_not_available(dev)) { | 
|  | /* Recovery I2C bus */ | 
|  | i2c_recover_bus(dev); | 
|  | /* | 
|  | * After resetting I2C bus, if I2C bus is not available | 
|  | * (No external pull-up), drop the transaction. | 
|  | */ | 
|  | if (i2c_bus_not_available(dev)) { | 
|  | /* Unlock mutex of i2c controller */ | 
|  | k_mutex_unlock(&data->mutex); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_IT8XXX2_CQ_MODE | 
|  | if (cq_mode_allowed(dev, msgs)) { | 
|  | data->err = i2c_enhance_cq_transfer(dev, msgs); | 
|  | } else | 
|  | #endif | 
|  | { | 
|  | data->err = i2c_enhance_pio_transfer(dev, msgs); | 
|  | } | 
|  | /* Save return value. */ | 
|  | ret = i2c_parsing_return_value(dev); | 
|  | /* Unlock mutex of i2c controller */ | 
|  | k_mutex_unlock(&data->mutex); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | static void target_i2c_isr_dma(const struct device *dev, | 
|  | uint8_t interrupt_status) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | const struct i2c_target_callbacks *target_cb = data->target_cfg->callbacks; | 
|  | struct i2c_target_dma_buffer *target_buffer = &data->target_buffer; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | /* Byte counter enable */ | 
|  | if (interrupt_status & IT8XXX2_I2C_IDW_CLR) { | 
|  | IT8XXX2_I2C_BYTE_CNT_L(base) |= | 
|  | (IT8XXX2_I2C_DMA_ADDR_RELOAD | | 
|  | IT8XXX2_I2C_BYTE_CNT_ENABLE); | 
|  | } | 
|  | /* The number of received data exceeds the byte counter setting */ | 
|  | if (interrupt_status & IT8XXX2_I2C_CNT_HOLD) { | 
|  | LOG_ERR("The excess data written starts " | 
|  | "from the memory address:%p", | 
|  | target_buffer->in_buffer + | 
|  | CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE); | 
|  | } | 
|  | /* Controller to write data */ | 
|  | if (interrupt_status & IT8XXX2_I2C_SLVDATAFLG) { | 
|  | /* Number of receive data in target mode */ | 
|  | data->buffer_size = | 
|  | ((IT8XXX2_I2C_SLV_NUM_H(base) << 8) | | 
|  | IT8XXX2_I2C_SLV_NUM_L(base)) + 1; | 
|  |  | 
|  | /* Write data done callback function */ | 
|  | target_cb->buf_write_received(data->target_cfg, | 
|  | target_buffer->in_buffer, data->buffer_size); | 
|  | } | 
|  | /* Peripheral finish */ | 
|  | if (interrupt_status & IT8XXX2_I2C_P_CLR) { | 
|  | /* Transfer done callback function */ | 
|  | target_cb->stop(data->target_cfg); | 
|  | } | 
|  | /* Controller to read data */ | 
|  | if (interrupt_status & IT8XXX2_I2C_IDR_CLR) { | 
|  | uint32_t len; | 
|  | uint8_t *rdata = NULL; | 
|  |  | 
|  | /* Clear byte counter setting */ | 
|  | IT8XXX2_I2C_BYTE_CNT_L(base) &= | 
|  | ~(IT8XXX2_I2C_DMA_ADDR_RELOAD | | 
|  | IT8XXX2_I2C_BYTE_CNT_ENABLE); | 
|  | /* Read data callback function */ | 
|  | target_cb->buf_read_requested(data->target_cfg, | 
|  | &rdata, &len); | 
|  |  | 
|  | if (len > CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE) { | 
|  | LOG_ERR("The buffer size exceeds " | 
|  | "I2C_TARGET_IT8XXX2_MAX_BUF_SIZE: len=%d", | 
|  | len); | 
|  | } else { | 
|  | memcpy(target_buffer->out_buffer, rdata, len); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Write clear the peripheral status */ | 
|  | IT8XXX2_I2C_IRQ_ST(base) = interrupt_status; | 
|  | } | 
|  |  | 
|  | static int target_i2c_isr_pio(const struct device *dev, | 
|  | uint8_t interrupt_status, | 
|  | uint8_t target_status) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | const struct i2c_target_callbacks *target_cb = data->target_cfg->callbacks; | 
|  | int ret = 0; | 
|  | uint8_t *base = config->base; | 
|  | uint8_t val; | 
|  |  | 
|  | /* Target ID write flag */ | 
|  | if (interrupt_status & IT8XXX2_I2C_IDW_CLR) { | 
|  | ret = target_cb->write_requested(data->target_cfg); | 
|  | } | 
|  | /* Target ID read flag */ | 
|  | else if (interrupt_status & IT8XXX2_I2C_IDR_CLR) { | 
|  | if (!target_cb->read_requested(data->target_cfg, &val)) { | 
|  | IT8XXX2_I2C_DTR(base) = val; | 
|  | } | 
|  | } | 
|  | /* Byte transfer done */ | 
|  | else if (target_status & IT8XXX2_I2C_BYTE_DONE) { | 
|  | /* Read of write */ | 
|  | if (target_status & IT8XXX2_I2C_RW) { | 
|  | /* Host receiving, target transmitting */ | 
|  | if (!target_cb->read_processed(data->target_cfg, &val)) { | 
|  | IT8XXX2_I2C_DTR(base) = val; | 
|  | } | 
|  | } else { | 
|  | /* Host transmitting, target receiving */ | 
|  | val = IT8XXX2_I2C_DRR(base); | 
|  | ret = target_cb->write_received(data->target_cfg, val); | 
|  | } | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void target_i2c_isr(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | const struct i2c_target_callbacks *target_cb = data->target_cfg->callbacks; | 
|  | uint8_t *base = config->base; | 
|  | uint8_t target_status = IT8XXX2_I2C_STR(base); | 
|  |  | 
|  | /* Any error */ | 
|  | if (target_status & E_TARGET_ANY_ERROR) { | 
|  | /* Hardware reset */ | 
|  | IT8XXX2_I2C_CTR(base) |= IT8XXX2_I2C_HALT; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Interrupt pending */ | 
|  | if (target_status & IT8XXX2_I2C_INT_PEND) { | 
|  | uint8_t interrupt_status = IT8XXX2_I2C_IRQ_ST(base); | 
|  |  | 
|  | /* Determine whether the transaction uses PIO or DMA mode */ | 
|  | if (config->target_pio_mode) { | 
|  | if (target_i2c_isr_pio(dev, interrupt_status, target_status) < 0) { | 
|  | /* NACK */ | 
|  | IT8XXX2_I2C_CTR(base) &= ~IT8XXX2_I2C_ACK; | 
|  | IT8XXX2_I2C_CTR(base) |= IT8XXX2_I2C_HALT; | 
|  | data->target_nack = 1; | 
|  | } | 
|  | /* Peripheral finish */ | 
|  | if (interrupt_status & IT8XXX2_I2C_P_CLR) { | 
|  | /* Transfer done callback function */ | 
|  | target_cb->stop(data->target_cfg); | 
|  |  | 
|  | if (data->target_nack) { | 
|  | /* Set acknowledge */ | 
|  | IT8XXX2_I2C_CTR(base) |= | 
|  | IT8XXX2_I2C_ACK; | 
|  | data->target_nack = 0; | 
|  | } | 
|  | } | 
|  | /* Write clear the peripheral status */ | 
|  | IT8XXX2_I2C_IRQ_ST(base) = interrupt_status; | 
|  | /* Hardware reset */ | 
|  | IT8XXX2_I2C_CTR(base) |= IT8XXX2_I2C_HALT; | 
|  | } else { | 
|  | target_i2c_isr_dma(dev, interrupt_status); | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void i2c_enhance_isr(void *arg) | 
|  | { | 
|  | struct device *dev = (struct device *)arg; | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | if (data->target_attached) { | 
|  | target_i2c_isr(dev); | 
|  | } else { | 
|  | #endif | 
|  | #ifdef CONFIG_I2C_IT8XXX2_CQ_MODE | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | /* If done doing work, wake up the task waiting for the transfer */ | 
|  | if (IT8XXX2_I2C_CTR1(base) & IT8XXX2_I2C_COMQ_EN) { | 
|  | if (enhanced_i2c_cq_isr(dev)) { | 
|  | return; | 
|  | } | 
|  | } else | 
|  | #endif | 
|  | { | 
|  | if (i2c_transaction(dev)) { | 
|  | return; | 
|  | } | 
|  | } | 
|  | irq_disable(config->i2c_irq_base); | 
|  | k_sem_give(&data->device_sync_sem); | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static int i2c_enhance_init(const struct device *dev) | 
|  | { | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | uint8_t *base = config->base; | 
|  | uint8_t data_hold_time = config->data_hold_time; | 
|  | uint32_t bitrate_cfg; | 
|  | int error, status; | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | if (!config->target_enable) { | 
|  | #endif | 
|  | /* Initialize mutex and semaphore */ | 
|  | k_mutex_init(&data->mutex); | 
|  | k_sem_init(&data->device_sync_sem, 0, K_SEM_MAX_LIMIT); | 
|  |  | 
|  | /* Enable clock to specified peripheral */ | 
|  | volatile uint8_t *reg = (volatile uint8_t *) | 
|  | (IT8XXX2_ECPM_BASE + (config->clock_gate_offset >> 8)); | 
|  | uint8_t reg_mask = config->clock_gate_offset & 0xff; | 
|  | *reg &= ~reg_mask; | 
|  |  | 
|  | /* Enable I2C function */ | 
|  | /* Software reset */ | 
|  | IT8XXX2_I2C_DHTR(base) |= IT8XXX2_I2C_SOFT_RST; | 
|  | IT8XXX2_I2C_DHTR(base) &= ~IT8XXX2_I2C_SOFT_RST; | 
|  | /* reset i2c port */ | 
|  | i2c_reset(dev); | 
|  | /* bit1, Module enable */ | 
|  | IT8XXX2_I2C_CTR1(base) = 0; | 
|  |  | 
|  | #ifdef CONFIG_I2C_IT8XXX2_CQ_MODE | 
|  | /* Set command address registers. */ | 
|  | enhanced_i2c_set_cmd_addr_regs(dev); | 
|  | #endif | 
|  |  | 
|  | /* ChannelA-F switch selection of I2C pin */ | 
|  | if (config->port == SMB_CHANNEL_A) { | 
|  | IT8XXX2_SMB_SMB01CHS = (IT8XXX2_SMB_SMB01CHS &= ~GENMASK(2, 0)) | | 
|  | config->channel_switch_sel; | 
|  | } else if (config->port == SMB_CHANNEL_B) { | 
|  | IT8XXX2_SMB_SMB01CHS = (config->channel_switch_sel << 4) | | 
|  | (IT8XXX2_SMB_SMB01CHS &= ~GENMASK(6, 4)); | 
|  | } else if (config->port == SMB_CHANNEL_C) { | 
|  | IT8XXX2_SMB_SMB23CHS = (IT8XXX2_SMB_SMB23CHS &= ~GENMASK(2, 0)) | | 
|  | config->channel_switch_sel; | 
|  | } else if (config->port == I2C_CHANNEL_D) { | 
|  | IT8XXX2_SMB_SMB23CHS = (config->channel_switch_sel << 4) | | 
|  | (IT8XXX2_SMB_SMB23CHS &= ~GENMASK(6, 4)); | 
|  | } else if (config->port == I2C_CHANNEL_E) { | 
|  | IT8XXX2_SMB_SMB45CHS = (IT8XXX2_SMB_SMB45CHS &= ~GENMASK(2, 0)) | | 
|  | config->channel_switch_sel; | 
|  | } else if (config->port == I2C_CHANNEL_F) { | 
|  | IT8XXX2_SMB_SMB45CHS = (config->channel_switch_sel << 4) | | 
|  | (IT8XXX2_SMB_SMB45CHS &= ~GENMASK(6, 4)); | 
|  | } | 
|  |  | 
|  | /* Set I2C data hold time. */ | 
|  | IT8XXX2_I2C_DHTR(base) = (IT8XXX2_I2C_DHTR(base) & ~GENMASK(2, 0)) | | 
|  | (data_hold_time - 3); | 
|  |  | 
|  | /* Set clock frequency for I2C ports */ | 
|  | if (config->bitrate == I2C_BITRATE_STANDARD || | 
|  | config->bitrate == I2C_BITRATE_FAST || | 
|  | config->bitrate == I2C_BITRATE_FAST_PLUS) { | 
|  | bitrate_cfg = i2c_map_dt_bitrate(config->bitrate); | 
|  | } else { | 
|  | /* Device tree specified speed */ | 
|  | bitrate_cfg = I2C_SPEED_DT << I2C_SPEED_SHIFT; | 
|  | } | 
|  |  | 
|  | error = i2c_enhance_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg); | 
|  | data->i2ccs = I2C_CH_NORMAL; | 
|  |  | 
|  | if (error) { | 
|  | LOG_ERR("i2c: failure initializing"); | 
|  | return error; | 
|  | } | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Set the pin to I2C alternate function. */ | 
|  | status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); | 
|  | if (status < 0) { | 
|  | LOG_ERR("Failed to configure I2C pins"); | 
|  | return status; | 
|  | } | 
|  |  | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_enhance_recover_bus(const struct device *dev) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | int i, status; | 
|  |  | 
|  | /* Output type selection */ | 
|  | gpio_flags_t flags = GPIO_OUTPUT | (config->push_pull_recovery ? 0 : GPIO_OPEN_DRAIN); | 
|  | /* Set SCL of I2C as GPIO pin */ | 
|  | gpio_pin_configure_dt(&config->scl_gpios, flags); | 
|  | /* Set SDA of I2C as GPIO pin */ | 
|  | gpio_pin_configure_dt(&config->sda_gpios, flags); | 
|  |  | 
|  | /* | 
|  | * In I2C recovery bus, 1ms sleep interval for bitbanging i2c | 
|  | * is mainly to ensure that gpio has enough time to go from | 
|  | * low to high or high to low. | 
|  | */ | 
|  | /* Pull SCL and SDA pin to high */ | 
|  | gpio_pin_set_dt(&config->scl_gpios, 1); | 
|  | gpio_pin_set_dt(&config->sda_gpios, 1); | 
|  | k_msleep(1); | 
|  |  | 
|  | /* Start condition */ | 
|  | gpio_pin_set_dt(&config->sda_gpios, 0); | 
|  | k_msleep(1); | 
|  | gpio_pin_set_dt(&config->scl_gpios, 0); | 
|  | k_msleep(1); | 
|  |  | 
|  | /* 9 cycles of SCL with SDA held high */ | 
|  | for (i = 0; i < 9; i++) { | 
|  | /* SDA */ | 
|  | gpio_pin_set_dt(&config->sda_gpios, 1); | 
|  | /* SCL */ | 
|  | gpio_pin_set_dt(&config->scl_gpios, 1); | 
|  | k_msleep(1); | 
|  | /* SCL */ | 
|  | gpio_pin_set_dt(&config->scl_gpios, 0); | 
|  | k_msleep(1); | 
|  | } | 
|  | /* SDA */ | 
|  | gpio_pin_set_dt(&config->sda_gpios, 0); | 
|  | k_msleep(1); | 
|  |  | 
|  | /* Stop condition */ | 
|  | gpio_pin_set_dt(&config->scl_gpios, 1); | 
|  | k_msleep(1); | 
|  | gpio_pin_set_dt(&config->sda_gpios, 1); | 
|  | k_msleep(1); | 
|  |  | 
|  | /* Set GPIO back to I2C alternate function */ | 
|  | status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); | 
|  | if (status < 0) { | 
|  | LOG_ERR("Failed to configure I2C pins"); | 
|  | return status; | 
|  | } | 
|  |  | 
|  | /* reset i2c port */ | 
|  | i2c_reset(dev); | 
|  | LOG_ERR("I2C ch%d reset cause %d", config->port, | 
|  | I2C_RC_NO_IDLE_FOR_START); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | static int i2c_enhance_target_register(const struct device *dev, | 
|  | struct i2c_target_config *target_cfg) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  | uint8_t *base = config->base; | 
|  |  | 
|  | if (!target_cfg) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (target_cfg->flags & I2C_TARGET_FLAGS_ADDR_10_BITS) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | if (data->target_attached) { | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | data->target_cfg = target_cfg; | 
|  | data->target_attached = true; | 
|  |  | 
|  | /* Software reset */ | 
|  | IT8XXX2_I2C_DHTR(base) |= IT8XXX2_I2C_SOFT_RST; | 
|  | IT8XXX2_I2C_DHTR(base) &= ~IT8XXX2_I2C_SOFT_RST; | 
|  | /* Disable the timeout setting when clock/data are in a low state */ | 
|  | IT8XXX2_I2C_TO_ARB_ST(base) &= ~(IT8XXX2_I2C_SCL_TIMEOUT_EN | | 
|  | IT8XXX2_I2C_SDA_TIMEOUT_EN); | 
|  | /* Bit stretching */ | 
|  | IT8XXX2_I2C_TOS(base) |= IT8XXX2_I2C_CLK_STRETCH; | 
|  | /* Peripheral address(8-bit) */ | 
|  | IT8XXX2_I2C_IDR(base) = target_cfg->address << 1; | 
|  | /* I2C interrupt enable and set acknowledge */ | 
|  | IT8XXX2_I2C_CTR(base) = IT8XXX2_I2C_INT_EN | IT8XXX2_I2C_HALT | | 
|  | IT8XXX2_I2C_ACK; | 
|  | /* Interrupt status write clear */ | 
|  | IT8XXX2_I2C_IRQ_ST(base) = 0xff; | 
|  |  | 
|  | /* I2C target initial configuration of PIO mode */ | 
|  | if (config->target_pio_mode) { | 
|  | /* Block to enter power policy. */ | 
|  | pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); | 
|  |  | 
|  | /* I2C module enable */ | 
|  | IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_MDL_EN; | 
|  | /* I2C target initial configuration of DMA mode */ | 
|  | } else { | 
|  | struct i2c_target_dma_buffer *target_buffer = &data->target_buffer; | 
|  | uint32_t in_data_addr, out_data_addr; | 
|  | int buf_size = CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE; | 
|  |  | 
|  | /* Clear read and write data buffer of DMA */ | 
|  | memset(target_buffer->in_buffer, 0, buf_size); | 
|  | memset(target_buffer->out_buffer, 0, buf_size); | 
|  |  | 
|  | in_data_addr = (uint32_t)target_buffer->in_buffer & 0xffffff; | 
|  | out_data_addr = (uint32_t)target_buffer->out_buffer & 0xffffff; | 
|  | /* | 
|  | * DMA write target address register | 
|  | * for high order byte | 
|  | */ | 
|  | IT8XXX2_I2C_RAMH2A(base) = in_data_addr >> 16; | 
|  | IT8XXX2_I2C_RAMHA(base) = in_data_addr >> 8; | 
|  | IT8XXX2_I2C_RAMLA(base) = in_data_addr; | 
|  | /* | 
|  | * DMA read target address register | 
|  | * for high order byte | 
|  | */ | 
|  | IT8XXX2_I2C_CMD_ADDH2(base) = out_data_addr >> 16; | 
|  | IT8XXX2_I2C_RAMHA2(base) = out_data_addr >> 8; | 
|  | IT8XXX2_I2C_RAMLA2(base) = out_data_addr; | 
|  |  | 
|  | /* Byte counter setting */ | 
|  | /* This register indicates byte count[10:3]. */ | 
|  | IT8XXX2_I2C_BYTE_CNT_H(base) = | 
|  | CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE >> 3; | 
|  | /* This register indicates byte count[2:0]. */ | 
|  | IT8XXX2_I2C_BYTE_CNT_L(base) = | 
|  | CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE & GENMASK(2, 0); | 
|  |  | 
|  | /* | 
|  | * The EC processor(CPU) cannot be in the k_cpu_idle() and power | 
|  | * policy during the transactions with the CQ mode(DMA mode). | 
|  | * Otherwise, the EC processor would be clock gated. | 
|  | */ | 
|  | chip_block_idle(); | 
|  | pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); | 
|  |  | 
|  | /* I2C module enable and command queue mode */ | 
|  | IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_COMQ_EN | IT8XXX2_I2C_MDL_EN; | 
|  | } | 
|  |  | 
|  | ite_intc_isr_clear(config->i2c_irq_base); | 
|  | irq_enable(config->i2c_irq_base); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_enhance_target_unregister(const struct device *dev, | 
|  | struct i2c_target_config *cfg) | 
|  | { | 
|  | const struct i2c_enhance_config *config = dev->config; | 
|  | struct i2c_enhance_data *data = dev->data; | 
|  |  | 
|  | if (!data->target_attached) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | irq_disable(config->i2c_irq_base); | 
|  |  | 
|  | /* Permit to enter power policy and idle mode. */ | 
|  | pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); | 
|  | if (!config->target_pio_mode) { | 
|  | chip_permit_idle(); | 
|  | } | 
|  |  | 
|  | data->target_cfg = NULL; | 
|  | data->target_attached = false; | 
|  | data->target_nack = 0; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static DEVICE_API(i2c, i2c_enhance_driver_api) = { | 
|  | .configure = i2c_enhance_configure, | 
|  | .get_config = i2c_enhance_get_config, | 
|  | .transfer = i2c_enhance_transfer, | 
|  | .recover_bus = i2c_enhance_recover_bus, | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | .target_register = i2c_enhance_target_register, | 
|  | .target_unregister = i2c_enhance_target_unregister, | 
|  | #endif | 
|  | #ifdef CONFIG_I2C_RTIO | 
|  | .iodev_submit = i2c_iodev_submit_fallback, | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | BUILD_ASSERT(IS_ENABLED(CONFIG_I2C_TARGET_BUFFER_MODE), | 
|  | "When I2C target config is enabled, the buffer mode must be used."); | 
|  | #endif | 
|  |  | 
|  | #define I2C_ITE_ENHANCE_INIT(inst)                                              \ | 
|  | PINCTRL_DT_INST_DEFINE(inst);                                           \ | 
|  | BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) ==                    \ | 
|  | 50000) ||                                                  \ | 
|  | (DT_INST_PROP(inst, clock_frequency) ==                    \ | 
|  | I2C_BITRATE_STANDARD) ||                                   \ | 
|  | (DT_INST_PROP(inst, clock_frequency) ==                    \ | 
|  | I2C_BITRATE_FAST) ||                                       \ | 
|  | (DT_INST_PROP(inst, clock_frequency) ==                    \ | 
|  | I2C_BITRATE_FAST_PLUS), "Not support I2C bit rate value"); \ | 
|  | static void i2c_enhance_config_func_##inst(void);                       \ | 
|  | \ | 
|  | static const struct i2c_enhance_config i2c_enhance_cfg_##inst = {       \ | 
|  | .base = (uint8_t *)(DT_INST_REG_ADDR(inst)),                    \ | 
|  | .irq_config_func = i2c_enhance_config_func_##inst,              \ | 
|  | .bitrate = DT_INST_PROP(inst, clock_frequency),                 \ | 
|  | .i2c_irq_base = DT_INST_IRQN(inst),                             \ | 
|  | .port = DT_INST_PROP(inst, port_num),                           \ | 
|  | .channel_switch_sel = DT_INST_PROP(inst, channel_switch_sel),   \ | 
|  | .scl_gpios = GPIO_DT_SPEC_INST_GET(inst, scl_gpios),            \ | 
|  | .sda_gpios = GPIO_DT_SPEC_INST_GET(inst, sda_gpios),            \ | 
|  | .prescale_scl_low = DT_INST_PROP_OR(inst, prescale_scl_low, 0), \ | 
|  | .data_hold_time = DT_INST_PROP_OR(inst, data_hold_time, 0),     \ | 
|  | .clock_gate_offset = DT_INST_PROP(inst, clock_gate_offset),     \ | 
|  | .transfer_timeout_ms = DT_INST_PROP(inst, transfer_timeout_ms), \ | 
|  | .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),                   \ | 
|  | .target_enable = DT_INST_PROP(inst, target_enable),             \ | 
|  | .target_pio_mode = DT_INST_PROP(inst, target_pio_mode),         \ | 
|  | .push_pull_recovery = DT_INST_PROP(inst, push_pull_recovery),   \ | 
|  | };                                                                      \ | 
|  | \ | 
|  | static struct i2c_enhance_data i2c_enhance_data_##inst;                 \ | 
|  | \ | 
|  | I2C_DEVICE_DT_INST_DEFINE(inst, i2c_enhance_init,                       \ | 
|  | NULL,                                         \ | 
|  | &i2c_enhance_data_##inst,                     \ | 
|  | &i2c_enhance_cfg_##inst,                      \ | 
|  | POST_KERNEL,                                  \ | 
|  | CONFIG_I2C_INIT_PRIORITY,                     \ | 
|  | &i2c_enhance_driver_api);                     \ | 
|  | \ | 
|  | static void i2c_enhance_config_func_##inst(void)                        \ | 
|  | {                                                                       \ | 
|  | IRQ_CONNECT(DT_INST_IRQN(inst),                                 \ | 
|  | 0,                                                      \ | 
|  | i2c_enhance_isr,                                        \ | 
|  | DEVICE_DT_INST_GET(inst), 0);                           \ | 
|  | } | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(I2C_ITE_ENHANCE_INIT) |