diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index b1f9917106f96..216348440a0bd 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -1,280 +1,24 @@ -# I2C configuration options - -# Copyright (c) 2015 Intel Corporation +# Copyright (c) 2025, Qingsong Gou # SPDX-License-Identifier: Apache-2.0 -# -# I2C options -# -menuconfig I2C - bool "Inter-Integrated Circuit (I2C) bus drivers" - help - Enable I2C Driver Configuration - -if I2C - -config I2C_SHELL - bool "I2C Shell" - depends on SHELL - help - Enable I2C Shell. - - The I2C shell supports scanning, bus recovery, I2C read and write - operations. - -config I2C_STATS - bool "I2C device Stats" - depends on STATS - help - Enable I2C Stats. - -config I2C_DUMP_MESSAGES - bool "Log I2C transactions" - depends on LOG - depends on I2C_LOG_LEVEL_DBG - help - Dump every I2C transaction to the system log as debug level log messages. - -config I2C_DUMP_MESSAGES_ALLOWLIST - bool "Use allowlist for logging of I2C transactions" - depends on I2C_DUMP_MESSAGES - depends on DT_HAS_ZEPHYR_I2C_DUMP_ALLOWLIST_ENABLED - help - Use allowlist to specify which devices transactions should be logged. - The allowlist is defined in the devicetree using the compatible string of - "zephyr,i2c-dump-allowlist" and phandles to the devices that need to be traced. - Example of devicetree node: - i2c-dump-allowlist { - compatible = "zephyr,i2c-dump-allowlist"; - devices = < &display0 >, < &sensor3 >; - }; - -config I2C_CALLBACK - bool "I2C asynchronous callback API" - help - API and implementations of i2c_transfer_cb. - -config I2C_ALLOW_NO_STOP_TRANSACTIONS - bool "Allows I2C transfers with no STOP on the last transaction [DEPRECATED]" - depends on !I2C_NRFX_TWI - depends on !I2C_NRFX_TWIM - depends on !I2C_STM32 - depends on !I2C_GD32 - depends on !I2C_ESP32 - depends on (!I2C_DW || I2C_RTS5912) - select DEPRECATED - help - Allow I2C transactions with no STOP on the last message. This is - unsupported and can leave the bus in an unexpected state. The option - will be removed in Zephyr 4.1. - -config I2C_RTIO - bool "I2C RTIO API" - select EXPERIMENTAL - select RTIO - select RTIO_WORKQ - help - API and implementations of I2C for RTIO - -if I2C_RTIO -config I2C_RTIO_SQ_SIZE - int "Submission queue size for blocking calls" - default 4 - help - Blocking i2c calls when I2C_RTIO is enabled are copied into a per driver - submission queue. The queue depth determines the number of possible i2c_msg - structs that may be in the array given to i2c_transfer. A sensible default - is going to be 4 given the device address, register address, and a value - to be read or written. - -config I2C_RTIO_CQ_SIZE - int "Completion queue size for blocking calls" - default 4 - help - Blocking i2c calls when I2C_RTIO is enabled are copied into a per driver - submission queue. The queue depth determines the number of possible i2c_msg - structs that may be in the array given to i2c_transfer. A sensible default - is going to be 4 given the device address, register address, and a value - to be read or written. - -config I2C_RTIO_FALLBACK_MSGS - int "Number of available i2c_msg structs for the default handler to use" - default 4 - help - When RTIO is used with a driver that does not yet implement the submit API - natively the submissions are converted back to struct i2c_msg values that - are given to i2c_transfer. This requires some number of msgs be available to convert - the submissions into on the stack. MISRA rules dictate we must know this in - advance. - - In all likelihood 4 is going to work for everyone, but in case you do end up with - an issue where you are using RTIO, your driver does not implement submit natively, - and get an error relating to not enough i2c msgs this is the Kconfig to manipulate. - -endif # I2C_RTIO - - -# Include these first so that any properties (e.g. defaults) below can be -# overridden (by defining symbols in multiple locations) -source "drivers/i2c/target/Kconfig" -# zephyr-keep-sorted-start -source "drivers/i2c/Kconfig.ambiq" -source "drivers/i2c/Kconfig.andes_atciic100" -source "drivers/i2c/Kconfig.b91" -source "drivers/i2c/Kconfig.bcm_iproc" -source "drivers/i2c/Kconfig.cc13xx_cc26xx" -source "drivers/i2c/Kconfig.cc23x0" -source "drivers/i2c/Kconfig.cdns" -source "drivers/i2c/Kconfig.dw" -source "drivers/i2c/Kconfig.ene" -source "drivers/i2c/Kconfig.esp32" -source "drivers/i2c/Kconfig.gd32" -source "drivers/i2c/Kconfig.gpio" -source "drivers/i2c/Kconfig.i2c_emul" -source "drivers/i2c/Kconfig.ifx_cat1" -source "drivers/i2c/Kconfig.ifx_xmc4" -source "drivers/i2c/Kconfig.it51xxx" -source "drivers/i2c/Kconfig.it8xxx2" -source "drivers/i2c/Kconfig.litex" -source "drivers/i2c/Kconfig.lpc11u6x" -source "drivers/i2c/Kconfig.max32" -source "drivers/i2c/Kconfig.mchp_mss" -source "drivers/i2c/Kconfig.mcux" -source "drivers/i2c/Kconfig.npcx" -source "drivers/i2c/Kconfig.nrfx" -source "drivers/i2c/Kconfig.numaker" -source "drivers/i2c/Kconfig.omap" -source "drivers/i2c/Kconfig.rcar" -source "drivers/i2c/Kconfig.renesas_ra" -source "drivers/i2c/Kconfig.renesas_rx" -source "drivers/i2c/Kconfig.renesas_rz" -source "drivers/i2c/Kconfig.rts5912" -source "drivers/i2c/Kconfig.sam0" -source "drivers/i2c/Kconfig.sam_twihs" -source "drivers/i2c/Kconfig.sbcon" -source "drivers/i2c/Kconfig.sc18im704" -source "drivers/i2c/Kconfig.sedi" -source "drivers/i2c/Kconfig.sifive" -source "drivers/i2c/Kconfig.silabs" -source "drivers/i2c/Kconfig.smartbond" -source "drivers/i2c/Kconfig.stm32" -source "drivers/i2c/Kconfig.sy1xx" -source "drivers/i2c/Kconfig.tca954x" -source "drivers/i2c/Kconfig.test" -source "drivers/i2c/Kconfig.wch" -source "drivers/i2c/Kconfig.xec" -source "drivers/i2c/Kconfig.xilinx_axi" -# zephyr-keep-sorted-stop - -config I2C_INIT_PRIORITY - int "Init priority" - default KERNEL_INIT_PRIORITY_DEVICE - help - I2C device driver initialization priority. - - -module = I2C -module-str = i2c -source "subsys/logging/Kconfig.template.log_config" - -config I2C_GECKO - bool "Gecko I2C driver" - default y - depends on DT_HAS_SILABS_GECKO_I2C_ENABLED - select SOC_GECKO_I2C - select PINCTRL if SOC_FAMILY_SILABS_S2 - help - Enable the SiLabs Gecko I2C bus driver. - -config I2C_SAM_TWIM - bool "Atmel SAM (TWIM) I2C driver" - default y - depends on DT_HAS_ATMEL_SAM_I2C_TWIM_ENABLED - select PINCTRL - help - Enable Atmel SAM MCU Family (TWIM) I2C bus driver. - -config I2C_SAM_TWI - bool "Atmel SAM (TWI) I2C driver" +config I2C_SF32LB + bool "SF32LB I2C instance" default y - depends on DT_HAS_ATMEL_SAM_I2C_TWI_ENABLED - select PINCTRL + depends on DT_HAS_SIFLI_SF32LB_I2C_ENABLED help - Enable Atmel SAM MCU Family (TWI) I2C bus driver. + Enable I2C instance on SF32LB chips. -config I2C_MCUX - bool "MCUX I2C driver" - default y - depends on DT_HAS_NXP_KINETIS_I2C_ENABLED - select PINCTRL - help - Enable the mcux I2C driver. - -config I2C_MCUX_LPI2C - bool "MCUX LPI2C driver" - default y - depends on DT_HAS_NXP_LPI2C_ENABLED - depends on CLOCK_CONTROL - select PINCTRL - help - Enable the mcux LPI2C driver. - -config I2C_MCUX_LPI2C_BUS_RECOVERY - bool "Bus recovery support" - depends on I2C_MCUX_LPI2C && PINCTRL - select I2C_BITBANG - help - Enable LPI2C driver bus recovery support via GPIO bitbanging. +if I2C_SF32LB -config I2C_IMX - bool "i.MX I2C driver" - default y - depends on DT_HAS_FSL_IMX21_I2C_ENABLED - select PINCTRL +config I2C_SF32LB_INTERRUPT + bool "SF32LB I2C Interrupt Support" help - Enable the i.MX I2C driver. + Enable interrupt support on SF32LB chips. -config I2C_CC32XX - bool "CC32XX I2C driver" - default y - depends on DT_HAS_TI_CC32XX_I2C_ENABLED - select PINCTRL - help - Enable the CC32XX I2C driver. - -config I2C_BITBANG - bool - help - Enable library used for software driven (bit banging) I2C support - -config I2C_NIOS2 - bool "Nios-II I2C driver" - default y - depends on DT_HAS_ALTR_NIOS2_I2C_ENABLED - help - Enable the Nios-II I2C driver. - -config I2C_RV32M1_LPI2C - bool "RV32M1 LPI2C driver" - default y - depends on DT_HAS_OPENISA_RV32M1_LPI2C_ENABLED - depends on CLOCK_CONTROL - select PINCTRL - help - Enable the RV32M1 LPI2C driver. - -config GPIO_I2C_SWITCH - bool "GPIO controlled I2C bus switch" - default y - depends on DT_HAS_GPIO_I2C_SWITCH_ENABLED - help - Enable GPIO controlled I2C bus switch driver. - -config I2C_NXP_II2C - bool "NXP i.MX8M serial I2C driver" - default y - depends on DT_HAS_NXP_II2C_ENABLED +config I2C_SF32LB_DMA + bool "SF32LB I2C DMA Support" + select DMA help - Enable the NXP II2C driver. + Enable DMA support on SF32LB chips. -endif # I2C +endif # I2C_SF32LB \ No newline at end of file diff --git a/drivers/i2c/i2c_sf32lb.c b/drivers/i2c/i2c_sf32lb.c new file mode 100644 index 0000000000000..fa9a99e80ccb5 --- /dev/null +++ b/drivers/i2c/i2c_sf32lb.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2025, Qingsong Gou + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT sifli_sf32lb_i2c + +#include +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(i2c_sf32lb, CONFIG_I2C_LOG_LEVEL); + +#ifdef CONFIG_I2C_SF32LB_DMA +#include +#endif +#include + +#include "i2c-priv.h" + +#define I2C_CR offsetof(I2C_TypeDef, CR) +#define I2C_TCR offsetof(I2C_TypeDef, TCR) +#define I2C_IER offsetof(I2C_TypeDef, IER) +#define I2C_SR offsetof(I2C_TypeDef, SR) +#define I2C_DBR offsetof(I2C_TypeDef, DBR) +#define I2C_SAR offsetof(I2C_TypeDef, SAR) +#define I2C_LCR offsetof(I2C_TypeDef, LCR) +#define I2C_WCR offsetof(I2C_TypeDef, WCR) +#define I2C_RCCR offsetof(I2C_TypeDef, RCCR) +#define I2C_BMR offsetof(I2C_TypeDef, BMR) +#define I2C_DNR offsetof(I2C_TypeDef, DNR) +#define I2C_RSVD1 offsetof(I2C_TypeDef, RSVD1) +#define I2C_FIFO offsetof(I2C_TypeDef, FIFO) + +#define I2C_MODE_STD (0x00U) +#define I2C_MODE_FS (0x01U) +#define I2C_MODE_HS_STD (0x02U) +#define I2C_MODE_HS_FS (0x03U) + +#define I2C_TIMEOUT_MS (50U) + +struct i2c_sf32lb_config { + uintptr_t base; + const struct pinctrl_dev_config *pincfg; + struct sf32lb_clock_dt_spec clock; + uint32_t bitrate; +#ifdef CONFIG_I2C_SF32LB_INTERRUPT + void (*irq_cfg_func)(const struct device *dev); +#endif +#ifdef CONFIG_I2C_SF32LB_DMA + struct sf32lb_dma_dt_spec dma_rx; + struct sf32lb_dma_dt_spec dma_tx; +#endif +}; + +struct i2c_sf32lb_data { + struct k_mutex lock; + struct k_sem i2c_compl; +}; + +#if defined(CONFIG_I2C_SF32LB_INTERRUPT) || defined(CONFIG_I2C_SF32LB_DMA) +static void i2c_sf32lb_isr(const struct device *dev) +{ + const struct i2c_sf32lb_config *config = dev->config; + struct i2c_sf32lb_data *data = dev->data; +#ifdef CONFIG_I2C_SF32LB_INTERRUPT +#endif + +#ifdef CONFIG_I2C_SF32LB_DMA + if (sys_test_bit(config->base + I2C_SR, I2C_SR_DMADONE_Pos)) { + sys_set_bit((config->base + I2C_SR), I2C_SR_DMADONE_Pos); + } + k_sem_give(&data->i2c_compl); +#endif +} +#endif + +static int i2c_sf32lb_send_addr(const struct device *dev, uint16_t addr, struct i2c_msg *msg) +{ + int ret = 0; + const struct i2c_sf32lb_config *cfg = dev->config; + uint32_t tcr = 0; + uint32_t sr = sys_read32(cfg->base + I2C_SR); + + addr = addr << 1; + if (i2c_is_read_op(msg)) { + addr |= 1; + } + + tcr |= I2C_TCR_START; + tcr |= I2C_TCR_TB; + + if ((msg->len == 0) && i2c_is_stop_op(msg)) { + tcr |= I2C_TCR_STOP; + } + + sys_write32(0, cfg->base + I2C_IER); + sys_clear_bits(cfg->base + I2C_CR, I2C_CR_DMAEN | I2C_CR_LASTSTOP | I2C_CR_LASTNACK); + sys_clear_bits(cfg->base + I2C_SR, I2C_SR_TE | I2C_SR_BED); + + sys_write8(addr, cfg->base + I2C_DBR); + sys_write32(tcr, cfg->base + I2C_TCR); + + while (!sys_test_bit(cfg->base + I2C_SR, I2C_SR_TE_Pos)) { + } + + sys_set_bit(cfg->base + I2C_SR, I2C_SR_TE_Pos); + + if (sys_test_bit(cfg->base + I2C_SR, I2C_SR_NACK_Pos)) { + ret = -EIO; + } + + return ret; +} + +static int i2c_sf32lb_dma_tx_config(const struct device *dev, struct i2c_msg *msg) +{ + const struct i2c_sf32lb_config *config = dev->config; + int err; + struct dma_config dma_cfg = {0}; + struct dma_block_config dma_blk = {0}; + struct dma_status dma_status = {0}; + + err = sf32lb_dma_get_status_dt(&config->dma_tx, &dma_status); + if (err) { + LOG_ERR("Unable to get Rx status (%d)", err); + return err; + } + + if (dma_status.busy) { + LOG_ERR("Tx DMA Channel is busy"); + return -EBUSY; + } + + err = sf32lb_dma_reload_dt(&config->dma_tx, (uint32_t)msg->buf, + (uint32_t)(config->base + I2C_FIFO), msg->len); + if (err) { + LOG_ERR("Error configuring Rx DMA (%d)", err); + } + + return err; +} + +static int i2c_sf32lb_dma_rx_config(const struct device *dev, struct i2c_msg *msg) +{ + const struct i2c_sf32lb_config *config = dev->config; + int err; + struct dma_config dma_cfg = {0}; + struct dma_block_config dma_blk = {0}; + struct dma_status dma_status = {0}; + + err = sf32lb_dma_get_status_dt(&config->dma_rx, &dma_status); + if (err) { + LOG_ERR("Unable to get Rx status (%d)", err); + return err; + } + + if (dma_status.busy) { + LOG_ERR("Rx DMA Channel is busy"); + return -EBUSY; + } + + err = sf32lb_dma_reload_dt(&config->dma_rx, (uint32_t)(config->base + I2C_FIFO), + (uint32_t)msg->buf, msg->len); + if (err) { + LOG_ERR("Error configuring Rx DMA (%d)", err); + } + + return err; +} + +static int i2c_sf32lb_master_send(const struct device *dev, uint16_t addr, struct i2c_msg *msg) +{ + int ret = 0; + const struct i2c_sf32lb_config *cfg = dev->config; + uint32_t tcr = 0; + uint32_t sr = 0; + + ret = i2c_sf32lb_send_addr(dev, addr, msg); + if (ret < 0) { + return ret; + } + + for (int j = 0; j < msg->len; j++) { + bool last = (msg->len - j == 1) ? true : false; + + if (last) { + tcr |= I2C_TCR_STOP; + } + + sys_write8(msg->buf[j], cfg->base + I2C_DBR); + tcr |= I2C_TCR_TB; + sys_write32(tcr, cfg->base + I2C_TCR); + + while (!sys_test_bit(cfg->base + I2C_SR, I2C_SR_TE_Pos)) { + } + + sys_set_bit(cfg->base + I2C_SR, I2C_SR_TE_Pos); + + if (sys_test_bit(cfg->base + I2C_SR, I2C_SR_NACK_Pos)) { + ret = -EIO; + break; + } + + tcr = 0; + } + + while (sys_test_bit(cfg->base + I2C_SR, I2C_SR_UB_Pos)) { + } + + return ret; +} + +static int i2c_sf32lb_master_send_dma(const struct device *dev, uint16_t addr, struct i2c_msg *msg) +{ + int ret = 0; + const struct i2c_sf32lb_config *config = dev->config; + struct i2c_sf32lb_data *data = dev->data; + + if (msg->len > 512U) { + return -ENOTSUP; + } + + /* Send address first */ + ret = i2c_sf32lb_send_addr(dev, addr, msg); + if (ret < 0) { + return ret; + } + + if (i2c_is_stop_op(msg)) { + sys_set_bit(config->base + I2C_CR, I2C_CR_LASTSTOP_Pos); + } + sys_write8(config->base + I2C_DNR, msg->len); + sys_set_bit(config->base + I2C_CR, I2C_CR_DMAEN_Pos); + + ret = i2c_sf32lb_dma_tx_config(dev, msg); + if (ret < 0) { + return ret; + } + + ret = sf32lb_dma_start_dt(&config->dma_tx); + if (ret < 0) { + return ret; + } + + k_sem_take(&data->i2c_compl, K_MSEC(I2C_TIMEOUT_MS)); + + return ret; +} + +static int i2c_sf32lb_master_recv(const struct device *dev, uint16_t addr, struct i2c_msg *msg) +{ + int ret = 0; + const struct i2c_sf32lb_config *cfg = dev->config; + uint32_t tcr = 0; + + ret = i2c_sf32lb_send_addr(dev, addr, msg); + if (ret < 0) { + return ret; + } + + for (int j = 0; j < msg->len; j++) { + bool last = (j == (msg->len - 1)) ? true : false; + + if (last && i2c_is_stop_op(msg)) { + tcr |= (I2C_TCR_STOP | I2C_TCR_NACK); + } + + tcr |= I2C_TCR_TB; + sys_write32(tcr, cfg->base + I2C_TCR); + + while (!sys_test_bit(cfg->base + I2C_SR, I2C_SR_RF_Pos)) { + } + + sys_set_bit(cfg->base + I2C_SR, I2C_SR_RF_Pos); + + msg->buf[j] = sys_read8(cfg->base + I2C_DBR); + tcr = 0; + } + + while (sys_test_bit(cfg->base + I2C_SR, I2C_SR_UB_Pos)) { + } + + return ret; +} + +static int i2c_sf32lb_master_recv_dma(const struct device *dev, uint16_t addr, struct i2c_msg *msg) +{ + int ret; + const struct i2c_sf32lb_config *config = dev->config; + struct i2c_sf32lb_data *data = dev->data; + + if (msg->len > 512U) { + return -ENOTSUP; + } + + /* Send address first */ + ret = i2c_sf32lb_send_addr(dev, addr, msg); + if (ret < 0) { + return ret; + } + + if (i2c_is_stop_op(msg)) { + sys_set_bit(config->base + I2C_CR, I2C_CR_LASTNACK_Pos); + } + sys_write8(config->base + I2C_DNR, msg->len); + sys_set_bit(config->base + I2C_CR, I2C_CR_DMAEN_Pos); + + ret = i2c_sf32lb_dma_rx_config(dev, msg); + if (ret < 0) { + return ret; + } + + ret = sf32lb_dma_start_dt(&config->dma_rx); + if (ret < 0) { + return ret; + } + + k_sem_take(&data->i2c_compl, K_MSEC(I2C_TIMEOUT_MS)); + + if (sys_read8(config->base + I2C_DNR) != msg->len) { + ret = -EIO; + } + + return ret; +} + +static int i2c_sf32lb_configure(const struct device *dev, uint32_t dev_config) +{ + const struct i2c_sf32lb_config *cfg = dev->config; + struct i2c_sf32lb_data *data = dev->data; + uint32_t cr = sys_read32(cfg->base + I2C_CR); + uint32_t dnf = (cr >> I2C_CR_DNF_Pos) & I2C_CR_DNF_Msk; + uint32_t lv; + uint32_t slv; + uint32_t flv; + + if (!(I2C_MODE_CONTROLLER & dev_config)) { + return -ENOTSUP; + } + + switch (I2C_SPEED_GET(dev_config)) { + case I2C_SPEED_STANDARD: + cr |= I2C_MODE_STD; + lv = (48000000U / cfg->bitrate - dnf - 7 + 1) / 2; + slv = (lv << I2C_LCR_SLV_Pos) & I2C_LCR_SLV_Msk; + sys_write32(slv, cfg->base + I2C_LCR); + break; + + case I2C_SPEED_FAST: + cr |= I2C_MODE_FS; + lv = (48000000U / cfg->bitrate - dnf - 7 + 1) / 2; + flv = (lv << I2C_LCR_FLV_Pos) & I2C_LCR_FLV_Msk; + sys_write32(flv, cfg->base + I2C_LCR); + break; + + case I2C_SPEED_FAST_PLUS: + cr |= I2C_MODE_HS_STD; + break; + + case I2C_SPEED_HIGH: + cr |= I2C_MODE_HS_FS; + break; + + case I2C_SPEED_ULTRA: + return -ENOTSUP; + + default: + LOG_ERR("Unsupported I2C speed requested:%d", I2C_SPEED_GET(dev_config)); + return -ENOTSUP; + } + + k_mutex_lock(&data->lock, K_FOREVER); + sys_write32(cr, cfg->base + I2C_CR); + k_mutex_unlock(&data->lock); + + return 0; +} + +static int i2c_sf32lb_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, + uint16_t addr) +{ + const struct i2c_sf32lb_config *cfg = dev->config; + struct i2c_sf32lb_data *data = dev->data; + int ret = 0; + + if (!num_msgs) { + return 0; + } + + if (sys_test_bit(cfg->base + I2C_SR, I2C_SR_UB_Pos)) { + return -EBUSY; + }; + + k_mutex_lock(&data->lock, K_FOREVER); + + for (int i = 0; i < num_msgs; i++) { + if (I2C_MSG_ADDR_10_BITS & msgs->flags) { + ret = -ENOTSUP; + break; + } + + if (msgs[i].flags & I2C_MSG_READ) { +#ifdef CONFIG_I2C_SF32LB_DMA + ret = i2c_sf32lb_master_recv_dma(dev, addr, &msgs[i]); +#else + ret = i2c_sf32lb_master_recv(dev, addr, &msgs[i]); +#endif + if (ret < 0) { + break; + } + } else { +#ifdef CONFIG_I2C_SF32LB_DMA + ret = i2c_sf32lb_master_send_dma(dev, addr, &msgs[i]); +#else + ret = i2c_sf32lb_master_send(dev, addr, &msgs[i]); +#endif + + if (ret < 0) { + break; + } + } + } + + k_mutex_unlock(&data->lock); + + return ret; +} + +static DEVICE_API(i2c, i2c_sf32lb_driver_api) = { + .configure = i2c_sf32lb_configure, + .transfer = i2c_sf32lb_transfer, +}; + +static int i2c_sf32lb_init(const struct device *dev) +{ + const struct i2c_sf32lb_config *config = dev->config; + struct i2c_sf32lb_data *data = dev->data; + int ret; +#ifdef CONFIG_I2C_SF32LB_DMA + struct dma_config tx_dma_cfg = {0}; + struct dma_config rx_dma_cfg = {0}; + struct dma_block_config dma_blk = {0}; +#endif + + ret = k_mutex_init(&data->lock); + if (ret < 0) { + return ret; + } + + ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + if (ret) { + return ret; + } + + if (config->clock.dev != NULL) { + if (!sf3232lb_clock_is_ready_dt(&config->clock)) { + return -ENODEV; + } + + ret = sf32lb_clock_control_on_dt(&config->clock); + if (ret < 0) { + return ret; + } + } + + ret = i2c_sf32lb_configure(dev, I2C_MODE_CONTROLLER | (config->bitrate)); +#ifdef CONFIG_I2C_SF32LB_DMA + if (!sf32lb_dma_is_ready_dt(&config->dma_tx)) { + return -ENODEV; + } + + if (!sf32lb_dma_is_ready_dt(&config->dma_rx)) { + return -ENODEV; + } + + sf32lb_dma_config_init_dt(&config->dma_tx, &tx_dma_cfg); + sf32lb_dma_config_init_dt(&config->dma_rx, &rx_dma_cfg); + + tx_dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL; + tx_dma_cfg.block_count = 1U; + tx_dma_cfg.source_data_size = 1U; + tx_dma_cfg.dest_data_size = 1U; + + tx_dma_cfg.head_block = &dma_blk; + dma_blk.source_addr_adj = DMA_ADDR_ADJ_INCREMENT; + dma_blk.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + + sf32lb_dma_config_dt(&config->dma_tx, &tx_dma_cfg); + + rx_dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY; + rx_dma_cfg.block_count = 1U; + rx_dma_cfg.source_data_size = 1U; + rx_dma_cfg.dest_data_size = 1U; + + rx_dma_cfg.head_block = &dma_blk; + dma_blk.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + dma_blk.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + + sf32lb_dma_config_dt(&config->dma_rx, &rx_dma_cfg); + + sys_set_bit(config->base + I2C_IER, I2C_IER_DMADONEIE_Pos); +#endif + + sys_set_bits(config->base + I2C_CR, I2C_CR_IUE | I2C_CR_SCLE); + +#ifdef CONFIG_I2C_SF32LB_INTERRUPT + if (config->irq_cfg_func) { + config->irq_cfg_func(); + } +#endif + return ret; +} + +#define I2C_SF32LB_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + IF_ENABLED(CONFIG_I2C_SF32LB_DMA, \ + (static void i2c_sf32lb_irq_config_func_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), i2c_sf32lb_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + })); \ + static struct i2c_sf32lb_data i2c_sf32lb_data_##n; \ + static const struct i2c_sf32lb_config i2c_sf32lb_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .clock = SF32LB_CLOCK_DT_INST_SPEC_GET_OR(n, {}), \ + .bitrate = DT_INST_PROP(n, clock_frequency), \ + IF_ENABLED(CONFIG_I2C_SF32LB_DMA, ( \ + .dma_tx = SF32LB_DMA_DT_INST_SPEC_GET_BY_NAME(n, tx), \ + .dma_rx = SF32LB_DMA_DT_INST_SPEC_GET_BY_NAME(n, rx), \ + )) \ + IF_ENABLED(CONFIG_I2C_SF32LB_INTERRUPT, ( \ + .irq_cfg_func = i2c_sf32lb_irq_config_func_##n, \ + )) }; \ + DEVICE_DT_INST_DEFINE(n, i2c_sf32lb_init, NULL, &i2c_sf32lb_data_##n, \ + &i2c_sf32lb_config_##n, POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \ + &i2c_sf32lb_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(I2C_SF32LB_INIT)