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