| /* |
| * 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 <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 10 |
| #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 { |
| int clk; |
| int rst; |
| } enable_bits; |
| |
| const struct { |
| bool tx_lsb_first; |
| bool rx_lsb_first; |
| } mode; |
| |
| const struct { |
| int source; |
| int line; |
| } irq; |
| |
| const u32_t default_config; |
| }; |
| |
| static inline void set_mask32(u32_t v, u32_t mem_addr) |
| { |
| sys_write32(sys_read32(mem_addr) | v, mem_addr); |
| } |
| |
| static inline void clear_mask32(u32_t v, u32_t mem_addr) |
| { |
| sys_write32(sys_read32(mem_addr) & ~v, mem_addr); |
| } |
| |
| static void i2c_esp32_enable_peripheral(const struct i2c_esp32_config *config) |
| { |
| set_mask32(config->enable_bits.clk, DPORT_PERIP_CLK_EN_REG); |
| clear_mask32(config->enable_bits.rst, DPORT_PERIP_RST_EN_REG); |
| } |
| |
| static const char *i2c_esp32_get_gpio_for_pin(int pin) |
| { |
| if (pin < 32) { |
| #if defined(CONFIG_GPIO_ESP32_0) |
| return CONFIG_GPIO_ESP32_0_NAME; |
| #else |
| return NULL; |
| #endif /* CONFIG_GPIO_ESP32_0 */ |
| } |
| |
| #if defined(CONFIG_GPIO_ESP32_1) |
| return CONFIG_GPIO_ESP32_1_NAME; |
| #else |
| return NULL; |
| #endif /* CONFIG_GPIO_ESP32_1 */ |
| } |
| |
| 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 = i2c_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; |
| |
| set_mask32(period << I2C_SCL_LOW_PERIOD_S, |
| I2C_SCL_LOW_PERIOD_REG(config->index)); |
| 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 */ |
| set_mask32(period << I2C_SCL_START_HOLD_TIME_S, |
| I2C_SCL_START_HOLD_REG(config->index)); |
| set_mask32(period << I2C_SCL_RSTART_SETUP_TIME_S, |
| I2C_SCL_RSTART_SETUP_REG(config->index)); |
| set_mask32(period << I2C_SCL_STOP_HOLD_TIME_S, |
| I2C_SCL_STOP_HOLD_REG(config->index)); |
| 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 */ |
| set_mask32(period << I2C_SDA_HOLD_TIME_S, |
| I2C_SDA_HOLD_REG(config->index)); |
| 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; |
| 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; |
| } |
| |
| i2c_esp32_enable_peripheral(config); |
| |
| /* 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 */ |
| set_mask32(I2C_TX_FIFO_RST | I2C_RX_FIFO_RST, reg); |
| 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_INT_STATUS_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++; |
| } |
| |
| *cmd++ = (struct i2c_esp32_cmd) { |
| .opcode = I2C_ESP32_OP_WRITE, |
| .ack_en = true, |
| .num_bytes = addr_len, |
| }; |
| |
| 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); |
| |
| if (msg.flags & I2C_MSG_RESTART) { |
| *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 = max(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); |
| |
| if (msg.flags & I2C_MSG_RESTART) { |
| *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 = cmd; |
| 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 |
| }; |
| } |
| *cmd++ = (struct i2c_esp32_cmd) { |
| .opcode = I2C_ESP32_OP_END |
| }; |
| |
| ret = i2c_esp32_transmit_wait(dev, wait_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, |
| }, |
| .enable_bits = { |
| .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, |
| }, |
| .enable_bits = { |
| .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; |
| 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); |
| } |