drivers: esp32: I2C driver implementation

Supports both master and slave mode, standard and fast modes,
configurable timeouts, and a few other tunable settings.

Signed-off-by: Leandro Pereira <leandro.pereira@intel.com>
diff --git a/boards/xtensa/esp32/esp32_defconfig b/boards/xtensa/esp32/esp32_defconfig
index ec3f1fd..578cd3b 100644
--- a/boards/xtensa/esp32/esp32_defconfig
+++ b/boards/xtensa/esp32/esp32_defconfig
@@ -28,3 +28,18 @@
 CONFIG_SW_ISR_TABLE=y
 CONFIG_GEN_ISR_TABLES=y
 CONFIG_GEN_IRQ_VECTOR_TABLE=n
+
+CONFIG_I2C=y
+CONFIG_I2C_ESP32=y
+CONFIG_I2C_0=y
+CONFIG_I2C_1=y
+CONFIG_I2C_2=n
+CONFIG_I2C_3=n
+
+# FIXME: ESP32 port should support DTS.
+CONFIG_HAS_DTS_I2C=n
+
+# IRQ priorities are fixed on Xtensa, but Kconfig will ask for these
+# if not set.
+CONFIG_I2C_0_IRQ_PRI=1
+CONFIG_I2C_1_IRQ_PRI=1
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 409c1ee..9d5e6a3 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -369,4 +369,6 @@
 	  The hold time on the data signal after a negative edge of i2c clock
 	  while i2c acts as receiver. The unit is i2c module base clock.
 
+source "drivers/i2c/Kconfig.esp32"
+
 endif # I2C
diff --git a/drivers/i2c/Kconfig.esp32 b/drivers/i2c/Kconfig.esp32
new file mode 100644
index 0000000..b41d802
--- /dev/null
+++ b/drivers/i2c/Kconfig.esp32
@@ -0,0 +1,79 @@
+# Kconfig.esp32 - ESP32 I2C configuration options
+#
+#
+# Copyright (c) 2017 Intel Corporation
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+menuconfig I2C_ESP32
+	bool "ESP32 I2C"
+	depends on I2C && SOC_ESP32
+	select GPIO_ESP32
+	default n
+	help
+	  Enables the ESP32 I2C driver
+
+if I2C_ESP32
+
+config I2C_ESP32_TIMEOUT
+	int "I2C timeout to receive a data bit in APB clock cycles"
+	default 200000
+
+if I2C_0
+
+config I2C_0_DEFAULT_CFG
+	# Standard speed, master
+	default 0x12
+
+config I2C_ESP32_0_TX_LSB_FIRST
+	bool "Port 0 Transmit LSB first"
+	default n
+
+config I2C_ESP32_0_RX_LSB_FIRST
+	bool "Port 0 Receive LSB first"
+	default n
+
+config I2C_ESP32_0_IRQ
+	int "Port 0 IRQ line"
+	default 8
+
+config I2C_ESP32_0_SCL_PIN
+	int "Port 0 SCL pin"
+	default 2
+
+config I2C_ESP32_0_SDA_PIN
+	int "Port 0 SDA pin"
+	default 4
+
+endif # I2C_0
+
+if I2C_1
+
+config I2C_1_DEFAULT_CFG
+	# Standard speed, master
+	default 0x12
+
+config I2C_ESP32_1_TX_LSB_FIRST
+	bool "Port 1 Transmit LSB first"
+	default n
+
+config I2C_ESP32_1_RX_LSB_FIRST
+	bool "Port 1 Receive LSB first"
+	default n
+
+config I2C_ESP32_1_IRQ
+	int "Port 1 IRQ line"
+	default 9
+
+config I2C_ESP32_1_SCL_PIN
+	int "Port 1 SCL pin"
+	default 5
+
+config I2C_ESP32_1_SDA_PIN
+	int "Port 1 SDA pin"
+	default 18
+
+endif # I2C_1
+
+endif # I2C_ESP32
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 986f8af..dcbe3ca 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -10,3 +10,4 @@
 obj-$(CONFIG_I2C_STM32_V1) += i2c_ll_stm32_v1.o i2c_ll_stm32.o
 obj-$(CONFIG_I2C_STM32_V2) += i2c_ll_stm32_v2.o i2c_ll_stm32.o
 obj-$(CONFIG_TWIHS_SAM) += twihs_sam.o
+obj-$(CONFIG_I2C_ESP32) += i2c_esp32.o
diff --git a/drivers/i2c/i2c_esp32.c b/drivers/i2c/i2c_esp32.c
new file mode 100644
index 0000000..cbcd82f
--- /dev/null
+++ b/drivers/i2c/i2c_esp32.c
@@ -0,0 +1,716 @@
+/*
+ * 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_0_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);
+}