drivers: i2c: litex: add driver for litei2c

add driver for litei2c.

Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt
index 564ca89..ef4e171 100644
--- a/drivers/i2c/CMakeLists.txt
+++ b/drivers/i2c/CMakeLists.txt
@@ -40,6 +40,7 @@
 zephyr_library_sources_ifdef(CONFIG_I2C_ITE_ENHANCE	i2c_ite_enhance.c)
 zephyr_library_sources_ifdef(CONFIG_I2C_ITE_IT8XXX2	i2c_ite_it8xxx2.c)
 zephyr_library_sources_ifdef(CONFIG_I2C_LITEX		i2c_litex.c)
+zephyr_library_sources_ifdef(CONFIG_I2C_LITEX_LITEI2C	i2c_litex_litei2c.c)
 zephyr_library_sources_ifdef(CONFIG_I2C_LPC11U6X	i2c_lpc11u6x.c)
 zephyr_library_sources_ifdef(CONFIG_I2C_MCHP_MSS	i2c_mchp_mss.c)
 zephyr_library_sources_ifdef(CONFIG_I2C_MCUX		i2c_mcux.c)
diff --git a/drivers/i2c/Kconfig.litex b/drivers/i2c/Kconfig.litex
index d768c4e..9637f44 100644
--- a/drivers/i2c/Kconfig.litex
+++ b/drivers/i2c/Kconfig.litex
@@ -8,3 +8,10 @@
 	select I2C_BITBANG
 	help
 	  Enable support for Litex I2C driver
+
+config I2C_LITEX_LITEI2C
+	bool "LiteX LiteI2C I2C driver"
+	default y
+	depends on DT_HAS_LITEX_LITEI2C_ENABLED
+	help
+	  Enable support for Litex I2C driver
diff --git a/drivers/i2c/i2c_litex_litei2c.c b/drivers/i2c/i2c_litex_litei2c.c
new file mode 100644
index 0000000..45eff30
--- /dev/null
+++ b/drivers/i2c/i2c_litex_litei2c.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2024 Vogl Electronic GmbH
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT litex_litei2c
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/i2c.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(i2c_litex_litei2c, CONFIG_I2C_LOG_LEVEL);
+
+#include "i2c-priv.h"
+
+#include <soc.h>
+
+#define MASTER_STATUS_TX_READY_OFFSET 0x0
+#define MASTER_STATUS_RX_READY_OFFSET 0x1
+#define MASTER_STATUS_NACK_OFFSET     0x8
+
+struct i2c_litex_litei2c_config {
+	uint32_t phy_speed_mode_addr;
+	uint32_t master_active_addr;
+	uint32_t master_settings_addr;
+	uint32_t master_addr_addr;
+	uint32_t master_rxtx_addr;
+	uint32_t master_status_addr;
+	uint32_t bitrate;
+};
+
+static int i2c_litex_configure(const struct device *dev, uint32_t dev_config)
+{
+	const struct i2c_litex_litei2c_config *config = dev->config;
+
+	if (I2C_ADDR_10_BITS & dev_config) {
+		return -ENOTSUP;
+	}
+
+	if (!(I2C_MODE_CONTROLLER & dev_config)) {
+		return -ENOTSUP;
+	}
+
+	/* Setup speed to use */
+	switch (I2C_SPEED_GET(dev_config)) {
+	case I2C_SPEED_STANDARD:
+		litex_write8(0, config->phy_speed_mode_addr);
+		break;
+	case I2C_SPEED_FAST:
+		litex_write8(1, config->phy_speed_mode_addr);
+		break;
+	case I2C_SPEED_FAST_PLUS:
+		litex_write8(2, config->phy_speed_mode_addr);
+		break;
+	default:
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+static int i2c_litex_get_config(const struct device *dev, uint32_t *config)
+{
+	const struct i2c_litex_litei2c_config *dev_config = dev->config;
+
+	*config = I2C_MODE_CONTROLLER;
+
+	switch (litex_read8(dev_config->phy_speed_mode_addr)) {
+	case 0:
+		*config |= I2C_SPEED_SET(I2C_SPEED_STANDARD);
+		break;
+	case 1:
+		*config |= I2C_SPEED_SET(I2C_SPEED_FAST);
+		break;
+	case 2:
+		*config |= I2C_SPEED_SET(I2C_SPEED_FAST_PLUS);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int i2c_litex_write_settings(const struct device *dev, uint8_t len_tx, uint8_t len_rx,
+				    bool recover)
+{
+	const struct i2c_litex_litei2c_config *config = dev->config;
+
+	uint32_t settings = len_tx | (len_rx << 8) | (recover << 16);
+
+	litex_write32(settings, config->master_settings_addr);
+
+	return 0;
+}
+
+static int i2c_litex_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
+			      uint16_t addr)
+{
+	const struct i2c_litex_litei2c_config *config = dev->config;
+	uint32_t len_tx_buf = 0;
+	uint32_t len_rx_buf = 0;
+	uint8_t len_tx = 0;
+	uint8_t len_rx = 0;
+
+	uint8_t *tx_buf_ptr;
+	uint8_t *rx_buf_ptr;
+
+	uint32_t tx_buf;
+	uint32_t rx_buf;
+
+	uint32_t tx_j = 0;
+	uint32_t rx_j = 0;
+
+	int ret = 0;
+
+	litex_write8(1, config->master_active_addr);
+
+	LOG_DBG("addr: 0x%x", addr);
+	litex_write8((uint8_t)addr, config->master_addr_addr);
+
+	for (uint8_t i = 0; i < num_msgs; i++) {
+		if (msgs[i].flags & I2C_MSG_READ) {
+			len_tx_buf = 0;
+			len_rx_buf = msgs[i].len;
+			rx_buf_ptr = msgs[i].buf;
+			tx_buf_ptr = NULL;
+		} else {
+			len_tx_buf = msgs[i].len;
+			tx_buf_ptr = msgs[i].buf;
+			if (!(msgs[i].flags & I2C_MSG_STOP) && (i + 1 < num_msgs) &&
+			    (msgs[i + 1].flags & I2C_MSG_READ) &&
+			    (msgs[i + 1].flags & I2C_MSG_RESTART)) {
+				i++;
+				len_rx_buf = msgs[i].len;
+				rx_buf_ptr = msgs[i].buf;
+			} else {
+				len_rx_buf = 0;
+				rx_buf_ptr = NULL;
+			}
+		}
+
+		LOG_HEXDUMP_DBG(tx_buf_ptr, len_tx_buf, "tx_buf");
+
+		tx_j = 0;
+		rx_j = 0;
+		do {
+
+			if (len_tx_buf > (tx_j + 4)) {
+				len_tx = 5;
+				len_rx = 0;
+			} else {
+				len_tx = len_tx_buf - tx_j;
+
+				if (len_rx_buf > (rx_j + 4)) {
+					len_rx = 5;
+				} else {
+					len_rx = len_rx_buf - rx_j;
+				}
+			}
+
+			tx_buf = 0;
+
+			switch (len_tx) {
+			case 5:
+			case 4:
+				tx_buf |= tx_buf_ptr[0 + tx_j] << 24;
+				tx_buf |= tx_buf_ptr[1 + tx_j] << 16;
+				tx_buf |= tx_buf_ptr[2 + tx_j] << 8;
+				tx_buf |= tx_buf_ptr[3 + tx_j];
+				tx_j += 4;
+				break;
+			case 3:
+				tx_buf |= tx_buf_ptr[0 + tx_j] << 16;
+				tx_buf |= tx_buf_ptr[1 + tx_j] << 8;
+				tx_buf |= tx_buf_ptr[2 + tx_j];
+				tx_j += 3;
+				break;
+			case 2:
+				tx_buf |= tx_buf_ptr[0 + tx_j] << 8;
+				tx_buf |= tx_buf_ptr[1 + tx_j];
+				tx_j += 2;
+				break;
+			case 1:
+				tx_buf |= tx_buf_ptr[0 + tx_j];
+				tx_j += 1;
+				break;
+			default:
+				break;
+			}
+
+			LOG_DBG("len_tx: %d, len_rx: %d", len_tx, len_rx);
+			i2c_litex_write_settings(dev, len_tx, len_rx, false);
+
+			while (!(litex_read8(config->master_status_addr) &
+				 BIT(MASTER_STATUS_TX_READY_OFFSET))) {
+				;
+			}
+
+			LOG_DBG("tx_buf: 0x%x", tx_buf);
+			litex_write32(tx_buf, config->master_rxtx_addr);
+
+			while (!(litex_read8(config->master_status_addr) &
+				 BIT(MASTER_STATUS_RX_READY_OFFSET))) {
+				;
+			}
+
+			if (litex_read16(config->master_status_addr) &
+			    BIT(MASTER_STATUS_NACK_OFFSET)) {
+				LOG_DBG("NACK received (addr: 0x%x)", addr);
+				ret = -EIO;
+			}
+
+			rx_buf = litex_read32(config->master_rxtx_addr);
+			LOG_DBG("rx_buf: 0x%x", rx_buf);
+
+			switch (len_rx) {
+			case 5:
+			case 4:
+				rx_buf_ptr[0 + rx_j] = rx_buf >> 24;
+				rx_buf_ptr[1 + rx_j] = rx_buf >> 16;
+				rx_buf_ptr[2 + rx_j] = rx_buf >> 8;
+				rx_buf_ptr[3 + rx_j] = rx_buf;
+				rx_j += 4;
+				break;
+			case 3:
+				rx_buf_ptr[0 + rx_j] = rx_buf >> 16;
+				rx_buf_ptr[1 + rx_j] = rx_buf >> 8;
+				rx_buf_ptr[2 + rx_j] = rx_buf;
+				rx_j += 3;
+				break;
+			case 2:
+				rx_buf_ptr[0 + rx_j] = rx_buf >> 8;
+				rx_buf_ptr[1 + rx_j] = rx_buf;
+				rx_j += 2;
+				break;
+			case 1:
+				rx_buf_ptr[0 + rx_j] = rx_buf;
+				rx_j += 1;
+				break;
+			default:
+				break;
+			}
+
+			if (ret < 0) {
+				goto transfer_end;
+			}
+
+		} while ((tx_j < len_tx_buf) || (rx_j < len_rx_buf));
+
+		LOG_HEXDUMP_DBG(rx_buf_ptr, len_rx_buf, "rx_buf");
+	}
+
+transfer_end:
+
+	litex_write8(0, config->master_active_addr);
+
+	return ret;
+}
+
+static int i2c_litex_recover_bus(const struct device *dev)
+{
+	const struct i2c_litex_litei2c_config *config = dev->config;
+
+	litex_write8(1, config->master_active_addr);
+
+	i2c_litex_write_settings(dev, 0, 0, true);
+
+	while (!(litex_read8(config->master_status_addr) & BIT(MASTER_STATUS_TX_READY_OFFSET))) {
+		;
+	}
+
+	litex_write32(0, config->master_rxtx_addr);
+
+	while (!(litex_read8(config->master_status_addr) & BIT(MASTER_STATUS_RX_READY_OFFSET))) {
+		;
+	}
+
+	(void)litex_read32(config->master_rxtx_addr);
+
+	litex_write8(0, config->master_active_addr);
+
+	return 0;
+}
+
+static int i2c_litex_init(const struct device *dev)
+{
+	const struct i2c_litex_litei2c_config *config = dev->config;
+	int ret;
+
+	ret = i2c_litex_configure(dev, I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate));
+	if (ret != 0) {
+		LOG_ERR("failed to configure I2C: %d", ret);
+	}
+
+	return ret;
+}
+
+static DEVICE_API(i2c, i2c_litex_litei2c_driver_api) = {
+	.configure = i2c_litex_configure,
+	.get_config = i2c_litex_get_config,
+	.transfer = i2c_litex_transfer,
+	.recover_bus = i2c_litex_recover_bus,
+#ifdef CONFIG_I2C_RTIO
+	.iodev_submit = i2c_iodev_submit_fallback,
+#endif
+};
+
+/* Device Instantiation */
+
+#define I2C_LITEX_INIT(n)                                                                          \
+	static const struct i2c_litex_litei2c_config i2c_litex_litei2c_config_##n = {              \
+		.phy_speed_mode_addr = DT_INST_REG_ADDR_BY_NAME(n, phy_speed_mode),                \
+		.master_active_addr = DT_INST_REG_ADDR_BY_NAME(n, master_active),                  \
+		.master_settings_addr = DT_INST_REG_ADDR_BY_NAME(n, master_settings),              \
+		.master_addr_addr = DT_INST_REG_ADDR_BY_NAME(n, master_addr),                      \
+		.master_rxtx_addr = DT_INST_REG_ADDR_BY_NAME(n, master_rxtx),                      \
+		.master_status_addr = DT_INST_REG_ADDR_BY_NAME(n, master_status),                  \
+		.bitrate = DT_INST_PROP(n, clock_frequency),                                       \
+	};                                                                                         \
+                                                                                                   \
+	I2C_DEVICE_DT_INST_DEFINE(n, i2c_litex_init, NULL, NULL,                                   \
+				  &i2c_litex_litei2c_config_##n, POST_KERNEL,                      \
+				  CONFIG_I2C_INIT_PRIORITY, &i2c_litex_litei2c_driver_api);
+
+DT_INST_FOREACH_STATUS_OKAY(I2C_LITEX_INIT)
diff --git a/dts/bindings/i2c/litex,litei2c.yaml b/dts/bindings/i2c/litex,litei2c.yaml
new file mode 100644
index 0000000..d85bd41
--- /dev/null
+++ b/dts/bindings/i2c/litex,litei2c.yaml
@@ -0,0 +1,18 @@
+#
+# Copyright (c) 2024 Vogl Electronic GmbH
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+description: LiteX LiteI2C I2C controller
+
+compatible: "litex,litei2c"
+
+include: i2c-controller.yaml
+
+properties:
+  reg:
+    required: true
+
+  clock-frequency:
+    required: true