|  | /* | 
|  | * Copyright (c) 2017 Intel Corporation | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | /* Include esp-idf headers first to avoid redefining BIT() macro */ | 
|  | #include <soc/dport_reg.h> | 
|  | #include <soc/i2c_reg.h> | 
|  | #include <rom/gpio.h> | 
|  | #include <soc/gpio_sig_map.h> | 
|  |  | 
|  | #include <soc.h> | 
|  | #include <errno.h> | 
|  | #include <gpio.h> | 
|  | #include <gpio/gpio_esp32.h> | 
|  | #include <i2c.h> | 
|  | #include <misc/util.h> | 
|  | #include <string.h> | 
|  |  | 
|  | /* Number of entries in hardware command queue */ | 
|  | #define I2C_ESP32_NUM_CMDS 16 | 
|  | /* Number of bytes in hardware FIFO */ | 
|  | #define I2C_ESP32_BUFFER_SIZE 32 | 
|  |  | 
|  | #define I2C_ESP32_TIMEOUT_MS 100 | 
|  | #define I2C_ESP32_SPIN_THRESHOLD 600 | 
|  | #define I2C_ESP32_YIELD_THRESHOLD (I2C_ESP32_SPIN_THRESHOLD / 2) | 
|  | #define I2C_ESP32_TIMEOUT \ | 
|  | ((I2C_ESP32_YIELD_THRESHOLD) + (I2C_ESP32_SPIN_THRESHOLD)) | 
|  |  | 
|  | enum i2c_esp32_opcodes { | 
|  | I2C_ESP32_OP_RSTART, | 
|  | I2C_ESP32_OP_WRITE, | 
|  | I2C_ESP32_OP_READ, | 
|  | I2C_ESP32_OP_STOP, | 
|  | I2C_ESP32_OP_END | 
|  | }; | 
|  |  | 
|  | struct i2c_esp32_cmd { | 
|  | u32_t num_bytes : 8; | 
|  | u32_t ack_en : 1; | 
|  | u32_t ack_exp : 1; | 
|  | u32_t ack_val : 1; | 
|  | u32_t opcode : 3; | 
|  | u32_t reserved : 17; | 
|  | u32_t done : 1; | 
|  | }; | 
|  |  | 
|  | struct i2c_esp32_data { | 
|  | u32_t dev_config; | 
|  | u16_t address; | 
|  |  | 
|  | struct k_sem fifo_sem; | 
|  | struct k_sem transfer_sem; | 
|  | }; | 
|  |  | 
|  | typedef void (*irq_connect_cb)(void); | 
|  |  | 
|  | struct i2c_esp32_config { | 
|  | int index; | 
|  |  | 
|  | irq_connect_cb connect_irq; | 
|  |  | 
|  | const struct { | 
|  | int sda_out; | 
|  | int sda_in; | 
|  | int scl_out; | 
|  | int scl_in; | 
|  | } sig; | 
|  |  | 
|  | const struct { | 
|  | int scl; | 
|  | int sda; | 
|  | } pins; | 
|  |  | 
|  | const struct esp32_peripheral peripheral; | 
|  |  | 
|  | const struct { | 
|  | bool tx_lsb_first; | 
|  | bool rx_lsb_first; | 
|  | } mode; | 
|  |  | 
|  | const struct { | 
|  | int source; | 
|  | int line; | 
|  | } irq; | 
|  |  | 
|  | const u32_t default_config; | 
|  | }; | 
|  |  | 
|  | static int i2c_esp32_configure_pins(int pin, int matrix_out, int matrix_in) | 
|  | { | 
|  | const int pin_mode = GPIO_DIR_OUT | | 
|  | GPIO_DS_DISCONNECT_LOW | | 
|  | GPIO_PUD_PULL_UP; | 
|  | const char *device_name = gpio_esp32_get_gpio_for_pin(pin); | 
|  | struct device *gpio; | 
|  | int ret; | 
|  |  | 
|  | if (!device_name) { | 
|  | return -EINVAL; | 
|  | } | 
|  | gpio = device_get_binding(device_name); | 
|  | if (!gpio) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | ret = gpio_pin_configure(gpio, pin, pin_mode); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = gpio_pin_write(gpio, pin, 1); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | esp32_rom_gpio_matrix_out(pin, matrix_out, false, false); | 
|  | esp32_rom_gpio_matrix_in(pin, matrix_in, false); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_configure_speed(const struct i2c_esp32_config *config, | 
|  | u32_t speed) | 
|  | { | 
|  | static const u32_t speed_to_freq_tbl[] = { | 
|  | [I2C_SPEED_STANDARD] = KHZ(100), | 
|  | [I2C_SPEED_FAST] = KHZ(400), | 
|  | [I2C_SPEED_FAST_PLUS] = MHZ(1), | 
|  | [I2C_SPEED_HIGH] = 0, | 
|  | [I2C_SPEED_ULTRA] = 0 | 
|  | }; | 
|  | u32_t freq_hz = speed_to_freq_tbl[speed]; | 
|  | u32_t period; | 
|  |  | 
|  | if (!freq_hz) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | period = (APB_CLK_FREQ / freq_hz) / 2; | 
|  |  | 
|  | esp32_set_mask32(period << I2C_SCL_LOW_PERIOD_S, | 
|  | I2C_SCL_LOW_PERIOD_REG(config->index)); | 
|  | esp32_set_mask32(period << I2C_SCL_HIGH_PERIOD_S, | 
|  | I2C_SCL_HIGH_PERIOD_REG(config->index)); | 
|  |  | 
|  | period /= 2; /* Set hold and setup times to 1/2th of period */ | 
|  | esp32_set_mask32(period << I2C_SCL_START_HOLD_TIME_S, | 
|  | I2C_SCL_START_HOLD_REG(config->index)); | 
|  | esp32_set_mask32(period << I2C_SCL_RSTART_SETUP_TIME_S, | 
|  | I2C_SCL_RSTART_SETUP_REG(config->index)); | 
|  | esp32_set_mask32(period << I2C_SCL_STOP_HOLD_TIME_S, | 
|  | I2C_SCL_STOP_HOLD_REG(config->index)); | 
|  | esp32_set_mask32(period << I2C_SCL_STOP_SETUP_TIME_S, | 
|  | I2C_SCL_STOP_SETUP_REG(config->index)); | 
|  |  | 
|  | period /= 2; /* Set sample and hold times to 1/4th of period */ | 
|  | esp32_set_mask32(period << I2C_SDA_HOLD_TIME_S, | 
|  | I2C_SDA_HOLD_REG(config->index)); | 
|  | esp32_set_mask32(period << I2C_SDA_SAMPLE_TIME_S, | 
|  | I2C_SDA_SAMPLE_REG(config->index)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_configure(struct device *dev, u32_t dev_config) | 
|  | { | 
|  | const struct i2c_esp32_config *config = dev->config->config_info; | 
|  | struct i2c_esp32_data *data = dev->driver_data; | 
|  | unsigned int key = irq_lock(); | 
|  | u32_t v = 0; | 
|  | int ret; | 
|  |  | 
|  | ret = i2c_esp32_configure_pins(config->pins.scl, | 
|  | config->sig.scl_out, | 
|  | config->sig.scl_in); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = i2c_esp32_configure_pins(config->pins.sda, | 
|  | config->sig.sda_out, | 
|  | config->sig.sda_in); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | esp32_enable_peripheral(&config->peripheral); | 
|  |  | 
|  | /* MSB or LSB first is configurable for both TX and RX */ | 
|  | if (config->mode.tx_lsb_first) { | 
|  | v |= I2C_TX_LSB_FIRST; | 
|  | } | 
|  |  | 
|  | if (config->mode.rx_lsb_first) { | 
|  | v |= I2C_RX_LSB_FIRST; | 
|  | } | 
|  |  | 
|  | if (dev_config & I2C_MODE_MASTER) { | 
|  | v |= I2C_MS_MODE; | 
|  | sys_write32(0, I2C_SLAVE_ADDR_REG(config->index)); | 
|  | } else { | 
|  | u32_t addr = (data->address & I2C_SLAVE_ADDR_V); | 
|  |  | 
|  | if (dev_config & I2C_ADDR_10_BITS) { | 
|  | addr |= I2C_ADDR_10BIT_EN; | 
|  | } | 
|  | sys_write32(addr << I2C_SLAVE_ADDR_S, | 
|  | I2C_SLAVE_ADDR_REG(config->index)); | 
|  |  | 
|  | /* Before setting up FIFO and interrupts, stop transmission */ | 
|  | sys_clear_bit(I2C_CTR_REG(config->index), I2C_TRANS_START_S); | 
|  |  | 
|  | /* Byte after address isn't the offset address in slave RAM */ | 
|  | sys_clear_bit(I2C_FIFO_CONF_REG(config->index), | 
|  | I2C_FIFO_ADDR_CFG_EN_S); | 
|  | } | 
|  |  | 
|  | /* Use open-drain for clock and data pins */ | 
|  | v |= (I2C_SCL_FORCE_OUT | I2C_SDA_FORCE_OUT); | 
|  | v |= I2C_CLK_EN; | 
|  | sys_write32(v, I2C_CTR_REG(config->index)); | 
|  |  | 
|  | ret = i2c_esp32_configure_speed(config, I2C_SPEED_GET(dev_config)); | 
|  | if (ret < 0) { | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | /* Use FIFO to transmit data */ | 
|  | sys_clear_bit(I2C_FIFO_CONF_REG(config->index), I2C_NONFIFO_EN_S); | 
|  |  | 
|  | v = CONFIG_I2C_ESP32_TIMEOUT & I2C_TIME_OUT_REG; | 
|  | sys_write32(v << I2C_TIME_OUT_REG_S, I2C_TO_REG(config->index)); | 
|  |  | 
|  | /* Enable interrupt types handled by the ISR */ | 
|  | sys_write32(I2C_ACK_ERR_INT_ENA_M | | 
|  | I2C_TIME_OUT_INT_ENA_M | | 
|  | I2C_TRANS_COMPLETE_INT_ENA_M | | 
|  | I2C_ARBITRATION_LOST_INT_ENA_M, | 
|  | I2C_INT_ENA_REG(config->index)); | 
|  |  | 
|  | irq_enable(config->irq.line); | 
|  |  | 
|  | out: | 
|  | irq_unlock(key); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static inline void i2c_esp32_reset_fifo(const struct i2c_esp32_config *config) | 
|  | { | 
|  | u32_t reg = I2C_FIFO_CONF_REG(config->index); | 
|  |  | 
|  | /* Writing 1 and then 0 to these bits will reset the I2C fifo */ | 
|  | esp32_set_mask32(I2C_TX_FIFO_RST | I2C_RX_FIFO_RST, reg); | 
|  | esp32_clear_mask32(I2C_TX_FIFO_RST | I2C_RX_FIFO_RST, reg); | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_spin_yield(int *counter) | 
|  | { | 
|  | *counter = *counter + 1; | 
|  |  | 
|  | if (*counter > I2C_ESP32_TIMEOUT) { | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | if (*counter > I2C_ESP32_SPIN_THRESHOLD) { | 
|  | k_yield(); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_transmit(struct device *dev) | 
|  | { | 
|  | const struct i2c_esp32_config *config = dev->config->config_info; | 
|  | struct i2c_esp32_data *data = dev->driver_data; | 
|  | u32_t status; | 
|  |  | 
|  | /* Start transmission and wait for the ISR to give the semaphore */ | 
|  | sys_set_bit(I2C_CTR_REG(config->index), I2C_TRANS_START_S); | 
|  | if (k_sem_take(&data->fifo_sem, I2C_ESP32_TIMEOUT_MS) < 0) { | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | status = sys_read32(I2C_INT_RAW_REG(config->index)); | 
|  | if (status & (I2C_ARBITRATION_LOST_INT_RAW | I2C_ACK_ERR_INT_RAW)) { | 
|  | return -EIO; | 
|  | } | 
|  | if (status & I2C_TIME_OUT_INT_RAW) { | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_wait(struct device *dev, | 
|  | volatile struct i2c_esp32_cmd *wait_cmd) | 
|  | { | 
|  | const struct i2c_esp32_config *config = dev->config->config_info; | 
|  | int counter = 0; | 
|  | int ret; | 
|  |  | 
|  | if (wait_cmd) { | 
|  | while (!wait_cmd->done) { | 
|  | ret = i2c_esp32_spin_yield(&counter); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Wait for I2C bus to finish its business */ | 
|  | while (sys_read32(I2C_SR_REG(config->index)) & I2C_BUS_BUSY) { | 
|  | ret = i2c_esp32_spin_yield(&counter); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_transmit_wait(struct device *dev, | 
|  | volatile struct i2c_esp32_cmd *wait_cmd) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = i2c_esp32_transmit(dev); | 
|  | if (!ret) { | 
|  | return i2c_esp32_wait(dev, wait_cmd); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static volatile struct i2c_esp32_cmd * | 
|  | i2c_esp32_write_addr(struct device *dev, | 
|  | volatile struct i2c_esp32_cmd *cmd, | 
|  | struct i2c_msg *msg, | 
|  | u16_t addr) | 
|  | { | 
|  | const struct i2c_esp32_config *config = dev->config->config_info; | 
|  | struct i2c_esp32_data *data = dev->driver_data; | 
|  | u32_t addr_len = 1; | 
|  |  | 
|  | i2c_esp32_reset_fifo(config); | 
|  |  | 
|  | sys_write32(addr & I2C_FIFO_RDATA, I2C_DATA_APB_REG(config->index)); | 
|  | if (data->dev_config & I2C_ADDR_10_BITS) { | 
|  | sys_write32(I2C_DATA_APB_REG(config->index), | 
|  | (addr >> 8) & I2C_FIFO_RDATA); | 
|  | addr_len++; | 
|  | } | 
|  |  | 
|  | if ((msg->flags & I2C_MSG_RW_MASK) != I2C_MSG_WRITE) { | 
|  | *cmd++ = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_WRITE, | 
|  | .ack_en = true, | 
|  | .num_bytes = addr_len, | 
|  | }; | 
|  | } else { | 
|  | msg->len += addr_len; | 
|  | } | 
|  |  | 
|  | return cmd; | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_read_msg(struct device *dev, u16_t addr, | 
|  | struct i2c_msg msg) | 
|  | { | 
|  | const struct i2c_esp32_config *config = dev->config->config_info; | 
|  | volatile struct i2c_esp32_cmd *cmd = | 
|  | (void *)I2C_COMD0_REG(config->index); | 
|  | u32_t i; | 
|  | int ret; | 
|  |  | 
|  | /* Set the R/W bit to R */ | 
|  | addr |= BIT(0); | 
|  |  | 
|  | *cmd++ = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_RSTART | 
|  | }; | 
|  |  | 
|  | cmd = i2c_esp32_write_addr(dev, cmd, &msg, addr); | 
|  |  | 
|  | for (; msg.len; cmd = (void *)I2C_COMD0_REG(config->index)) { | 
|  | volatile struct i2c_esp32_cmd *wait_cmd = NULL; | 
|  | u32_t to_read = min(I2C_ESP32_BUFFER_SIZE, msg.len - 1); | 
|  |  | 
|  | /* Might be the last byte, in which case, `to_read` will | 
|  | * be 0 here.  See comment below. | 
|  | */ | 
|  | if (to_read) { | 
|  | *cmd++ = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_READ, | 
|  | .num_bytes = to_read, | 
|  | }; | 
|  | } | 
|  |  | 
|  | /* I2C master won't acknowledge the last byte read from the | 
|  | * slave device.  Divide the read command in two segments as | 
|  | * recommended by the ESP32 Technical Reference Manual. | 
|  | */ | 
|  | if (msg.len - to_read <= 1) { | 
|  | /* Read the last byte and explicitly ask for an | 
|  | * acknowledgment. | 
|  | */ | 
|  | *cmd++ = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_READ, | 
|  | .num_bytes = 1, | 
|  | .ack_val = true, | 
|  | }; | 
|  |  | 
|  | /* Account for the `msg.len - 1` when clamping | 
|  | * transmission length to FIFO buffer size. | 
|  | */ | 
|  | to_read++; | 
|  |  | 
|  | if (msg.flags & I2C_MSG_STOP) { | 
|  | wait_cmd = cmd; | 
|  | *cmd++ = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_STOP | 
|  | }; | 
|  | } | 
|  | } | 
|  | if (!wait_cmd) { | 
|  | *cmd++ = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_END | 
|  | }; | 
|  | } | 
|  |  | 
|  | ret = i2c_esp32_transmit_wait(dev, wait_cmd); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < to_read; i++) { | 
|  | u32_t v = sys_read32(I2C_DATA_APB_REG(config->index)); | 
|  |  | 
|  | *msg.buf++ = v & I2C_FIFO_RDATA; | 
|  | } | 
|  | msg.len -= to_read; | 
|  |  | 
|  | i2c_esp32_reset_fifo(config); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_write_msg(struct device *dev, u16_t addr, | 
|  | struct i2c_msg msg) | 
|  | { | 
|  | const struct i2c_esp32_config *config = dev->config->config_info; | 
|  | volatile struct i2c_esp32_cmd *cmd = | 
|  | (void *)I2C_COMD0_REG(config->index); | 
|  |  | 
|  | *cmd++ = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_RSTART | 
|  | }; | 
|  |  | 
|  | cmd = i2c_esp32_write_addr(dev, cmd, &msg, addr); | 
|  |  | 
|  | for (; msg.len; cmd = (void *)I2C_COMD0_REG(config->index)) { | 
|  | u32_t to_send = min(I2C_ESP32_BUFFER_SIZE, msg.len); | 
|  | u32_t i; | 
|  | int ret; | 
|  |  | 
|  | /* Copy data to TX fifo */ | 
|  | for (i = 0; i < to_send; i++) { | 
|  | sys_write32(*msg.buf++, | 
|  | I2C_DATA_APB_REG(config->index)); | 
|  | } | 
|  | *cmd++ = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_WRITE, | 
|  | .num_bytes = to_send, | 
|  | .ack_en = true, | 
|  | }; | 
|  | msg.len -= to_send; | 
|  |  | 
|  | if (!msg.len && (msg.flags & I2C_MSG_STOP)) { | 
|  | *cmd = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_STOP | 
|  | }; | 
|  | } else { | 
|  | *cmd = (struct i2c_esp32_cmd) { | 
|  | .opcode = I2C_ESP32_OP_END | 
|  | }; | 
|  | } | 
|  |  | 
|  | ret = i2c_esp32_transmit_wait(dev, cmd); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | i2c_esp32_reset_fifo(config); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_transfer(struct device *dev, struct i2c_msg *msgs, | 
|  | u8_t num_msgs, u16_t addr) | 
|  | { | 
|  | struct i2c_esp32_data *data = dev->driver_data; | 
|  | int ret = 0; | 
|  | u8_t i; | 
|  |  | 
|  | k_sem_take(&data->transfer_sem, K_FOREVER); | 
|  |  | 
|  | /* Mask out unused address bits, and make room for R/W bit */ | 
|  | addr &= BIT_MASK(data->dev_config & I2C_ADDR_10_BITS ? 10 : 7); | 
|  | addr <<= 1; | 
|  |  | 
|  | for (i = 0; i < num_msgs; i++) { | 
|  | if ((msgs[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { | 
|  | ret = i2c_esp32_write_msg(dev, addr, msgs[i]); | 
|  | } else { | 
|  | ret = i2c_esp32_read_msg(dev, addr, msgs[i]); | 
|  | } | 
|  |  | 
|  | if (ret < 0) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | k_sem_give(&data->transfer_sem); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void i2c_esp32_isr(void *arg) | 
|  | { | 
|  | const int fifo_give_mask = I2C_ACK_ERR_INT_ST | | 
|  | I2C_TIME_OUT_INT_ST | | 
|  | I2C_TRANS_COMPLETE_INT_ST | | 
|  | I2C_ARBITRATION_LOST_INT_ST; | 
|  | struct device *device = arg; | 
|  | const struct i2c_esp32_config *config = device->config->config_info; | 
|  |  | 
|  | if (sys_read32(I2C_INT_STATUS_REG(config->index)) & fifo_give_mask) { | 
|  | struct i2c_esp32_data *data = device->driver_data; | 
|  |  | 
|  | /* Only give the semaphore if a watched interrupt happens. | 
|  | * Error checking is performed at the other side of the | 
|  | * semaphore, by reading the status register. | 
|  | */ | 
|  | k_sem_give(&data->fifo_sem); | 
|  | } | 
|  |  | 
|  | /* Acknowledge all I2C interrupts */ | 
|  | sys_write32(~0, I2C_INT_CLR_REG(config->index)); | 
|  | } | 
|  |  | 
|  | static int i2c_esp32_init(struct device *dev); | 
|  |  | 
|  | static const struct i2c_driver_api i2c_esp32_driver_api = { | 
|  | .configure = i2c_esp32_configure, | 
|  | .transfer = i2c_esp32_transfer, | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_I2C_0 | 
|  | DEVICE_DECLARE(i2c_esp32_0); | 
|  |  | 
|  | static void i2c_esp32_connect_irq_0(void) | 
|  | { | 
|  | IRQ_CONNECT(CONFIG_I2C_ESP32_0_IRQ, 1, i2c_esp32_isr, | 
|  | DEVICE_GET(i2c_esp32_0), 0); | 
|  | } | 
|  |  | 
|  | static const struct i2c_esp32_config i2c_esp32_config_0 = { | 
|  | .index = 0, | 
|  | .connect_irq = i2c_esp32_connect_irq_0, | 
|  | .sig = { | 
|  | .sda_out = I2CEXT0_SDA_OUT_IDX, | 
|  | .sda_in = I2CEXT0_SDA_IN_IDX, | 
|  | .scl_out = I2CEXT0_SCL_OUT_IDX, | 
|  | .scl_in = I2CEXT0_SCL_IN_IDX, | 
|  | }, | 
|  | .pins = { | 
|  | .scl = CONFIG_I2C_ESP32_0_SCL_PIN, | 
|  | .sda = CONFIG_I2C_ESP32_0_SDA_PIN, | 
|  | }, | 
|  | .peripheral = { | 
|  | .clk = DPORT_I2C_EXT0_CLK_EN, | 
|  | .rst = DPORT_I2C_EXT0_RST, | 
|  | }, | 
|  | .mode = { | 
|  | .tx_lsb_first = | 
|  | IS_ENABLED(CONFIG_ESP32_I2C_0_TX_LSB_FIRST), | 
|  | .rx_lsb_first = | 
|  | IS_ENABLED(CONFIG_ESP32_I2C_0_RX_LSB_FIRST), | 
|  | }, | 
|  | .irq = { | 
|  | .source = ETS_I2C_EXT0_INTR_SOURCE, | 
|  | .line = CONFIG_I2C_ESP32_0_IRQ, | 
|  | }, | 
|  | .default_config = CONFIG_I2C_0_DEFAULT_CFG, | 
|  | }; | 
|  |  | 
|  | static struct i2c_esp32_data i2c_esp32_data_0; | 
|  |  | 
|  | DEVICE_AND_API_INIT(i2c_esp32_0, CONFIG_I2C_0_NAME, &i2c_esp32_init, | 
|  | &i2c_esp32_data_0, &i2c_esp32_config_0, | 
|  | POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, | 
|  | &i2c_esp32_driver_api); | 
|  | #endif /* CONFIG_I2C_0 */ | 
|  |  | 
|  | #ifdef CONFIG_I2C_1 | 
|  | DEVICE_DECLARE(i2c_esp32_1); | 
|  |  | 
|  | static void i2c_esp32_connect_irq_1(void) | 
|  | { | 
|  | IRQ_CONNECT(CONFIG_I2C_ESP32_1_IRQ, 1, i2c_esp32_isr, | 
|  | DEVICE_GET(i2c_esp32_1), 0); | 
|  | } | 
|  |  | 
|  | static const struct i2c_esp32_config i2c_esp32_config_1 = { | 
|  | .index = 1, | 
|  | .connect_irq = i2c_esp32_connect_irq_1, | 
|  | .sig = { | 
|  | .sda_out = I2CEXT1_SDA_OUT_IDX, | 
|  | .sda_in = I2CEXT1_SDA_IN_IDX, | 
|  | .scl_out = I2CEXT1_SCL_OUT_IDX, | 
|  | .scl_in = I2CEXT1_SCL_IN_IDX, | 
|  | }, | 
|  | .pins = { | 
|  | .scl = CONFIG_I2C_ESP32_1_SCL_PIN, | 
|  | .sda = CONFIG_I2C_ESP32_1_SDA_PIN, | 
|  | }, | 
|  | .peripheral = { | 
|  | .clk = DPORT_I2C_EXT1_CLK_EN, | 
|  | .rst = DPORT_I2C_EXT1_RST, | 
|  | }, | 
|  | .mode = { | 
|  | .tx_lsb_first = | 
|  | IS_ENABLED(CONFIG_ESP32_I2C_1_TX_LSB_FIRST), | 
|  | .rx_lsb_first = | 
|  | IS_ENABLED(CONFIG_ESP32_I2C_1_RX_LSB_FIRST), | 
|  | }, | 
|  | .irq = { | 
|  | .source = ETS_I2C_EXT1_INTR_SOURCE, | 
|  | .line = CONFIG_I2C_ESP32_1_IRQ, | 
|  | }, | 
|  | .default_config = CONFIG_I2C_1_DEFAULT_CFG, | 
|  | }; | 
|  |  | 
|  | static struct i2c_esp32_data i2c_esp32_data_1; | 
|  |  | 
|  | DEVICE_AND_API_INIT(i2c_esp32_1, CONFIG_I2C_1_NAME, &i2c_esp32_init, | 
|  | &i2c_esp32_data_1, &i2c_esp32_config_1, | 
|  | POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, | 
|  | &i2c_esp32_driver_api); | 
|  | #endif /* CONFIG_I2C_1 */ | 
|  |  | 
|  | static int i2c_esp32_init(struct device *dev) | 
|  | { | 
|  | const struct i2c_esp32_config *config = dev->config->config_info; | 
|  | struct i2c_esp32_data *data = dev->driver_data; | 
|  | unsigned int key = irq_lock(); | 
|  |  | 
|  | k_sem_init(&data->fifo_sem, 1, 1); | 
|  | k_sem_init(&data->transfer_sem, 1, 1); | 
|  |  | 
|  | irq_disable(config->irq.line); | 
|  |  | 
|  | /* Even if irq_enable() is called on config->irq.line, disable | 
|  | * interrupt sources in the I2C controller. | 
|  | */ | 
|  | sys_write32(0, I2C_INT_ENA_REG(config->index)); | 
|  | esp32_rom_intr_matrix_set(0, config->irq.source, config->irq.line); | 
|  |  | 
|  | config->connect_irq(); | 
|  | irq_unlock(key); | 
|  |  | 
|  | return i2c_esp32_configure(dev, config->default_config); | 
|  | } |