diff --git a/boards/sifli/sf32lb52_devkit_lcd/sf32lb52_devkit_lcd.yaml b/boards/sifli/sf32lb52_devkit_lcd/sf32lb52_devkit_lcd.yaml index 7b45fc2fe18fc..dd328b47b3f09 100644 --- a/boards/sifli/sf32lb52_devkit_lcd/sf32lb52_devkit_lcd.yaml +++ b/boards/sifli/sf32lb52_devkit_lcd/sf32lb52_devkit_lcd.yaml @@ -10,6 +10,7 @@ flash: 16320 toolchain: - zephyr supported: + - i2c - uart - gpio vendor: sifli diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index 6fdb72b57e7b3..df7fea98029b4 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -68,6 +68,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_SAM_TWIM i2c_sam4l_twim.c) zephyr_library_sources_ifdef(CONFIG_I2C_SBCON i2c_sbcon.c) zephyr_library_sources_ifdef(CONFIG_I2C_SC18IM704 i2c_sc18im704.c) zephyr_library_sources_ifdef(CONFIG_I2C_SEDI i2c_sedi.c) +zephyr_library_sources_ifdef(CONFIG_I2C_SF32LB i2c_sf32lb.c) zephyr_library_sources_ifdef(CONFIG_I2C_SIFIVE i2c_sifive.c) zephyr_library_sources_ifdef(CONFIG_I2C_SILABS i2c_silabs.c) zephyr_library_sources_ifdef(CONFIG_I2C_SMARTBOND i2c_smartbond.c) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index b1f9917106f96..0bdd8ea8afa97 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -154,6 +154,7 @@ 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.sf32lb" source "drivers/i2c/Kconfig.sifive" source "drivers/i2c/Kconfig.silabs" source "drivers/i2c/Kconfig.smartbond" diff --git a/drivers/i2c/Kconfig.sf32lb b/drivers/i2c/Kconfig.sf32lb new file mode 100644 index 0000000000000..c00cab6c0f843 --- /dev/null +++ b/drivers/i2c/Kconfig.sf32lb @@ -0,0 +1,9 @@ +# Copyright (c) 2025, Qingsong Gou +# SPDX-License-Identifier: Apache-2.0 + +config I2C_SF32LB + bool "SF32LB I2C instance" + default y + depends on DT_HAS_SIFLI_SF32LB_I2C_ENABLED + help + Enable I2C instance on SF32LB chips. diff --git a/drivers/i2c/i2c_sf32lb.c b/drivers/i2c/i2c_sf32lb.c new file mode 100644 index 0000000000000..5421b3d4aa310 --- /dev/null +++ b/drivers/i2c/i2c_sf32lb.c @@ -0,0 +1,311 @@ +/* + * 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); + +#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) + +struct i2c_sf32lb_config { + uintptr_t base; + const struct pinctrl_dev_config *pincfg; + struct sf32lb_clock_dt_spec clock; + uint32_t bitrate; +}; + +struct i2c_sf32lb_data { + struct k_mutex lock; +}; + +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_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_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_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++) { + LOG_DBG("flags:0x%x", msgs[i].flags); + if (I2C_MSG_ADDR_10_BITS & msgs->flags) { + ret = -ENOTSUP; + break; + } + + if (msgs[i].flags & I2C_MSG_READ) { + ret = i2c_sf32lb_master_recv(dev, addr, &msgs[i]); + if (ret < 0) { + break; + } + } else { + ret = i2c_sf32lb_master_send(dev, addr, &msgs[i]); + 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; + + 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 | i2c_map_dt_bitrate(config->bitrate)); + + sys_set_bits(config->base + I2C_CR, I2C_CR_IUE | I2C_CR_SCLE); + + return ret; +} + +#define I2C_SF32LB_INIT(n) \ + PINCTRL_DT_INST_DEFINE(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), \ + }; \ + 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) diff --git a/dts/arm/sifli/sf32lb52x.dtsi b/dts/arm/sifli/sf32lb52x.dtsi index fc115ca6afb6d..9e3f7194b90b0 100644 --- a/dts/arm/sifli/sf32lb52x.dtsi +++ b/dts/arm/sifli/sf32lb52x.dtsi @@ -164,6 +164,42 @@ status = "disabled"; }; + i2c1: i2c@5009c000 { + compatible = "sifli,sf32lb-i2c"; + reg = <0x5009c000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&rcc_clk SF32LB52X_CLOCK_I2C1>; + status = "disabled"; + }; + + i2c2: i2c@5009d000 { + compatible = "sifli,sf32lb-i2c"; + reg = <0x5009d000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&rcc_clk SF32LB52X_CLOCK_I2C2>; + status = "disabled"; + }; + + i2c3: i2c@5009e000 { + compatible = "sifli,sf32lb-i2c"; + reg = <0x5009e000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&rcc_clk SF32LB52X_CLOCK_I2C3>; + status = "disabled"; + }; + + i2c4: i2c@5009f000 { + compatible = "sifli,sf32lb-i2c"; + reg = <0x5009f000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&rcc_clk SF32LB52X_CLOCK_I2C4>; + status = "disabled"; + }; + gpioa: gpio@500a0000 { compatible = "sifli,sf32lb-gpio-parent"; reg = <0x500a0000 0x1000>; @@ -229,6 +265,7 @@ status = "disabled"; }; }; + }; }; diff --git a/dts/bindings/i2c/sifli,sf32lb-i2c.yaml b/dts/bindings/i2c/sifli,sf32lb-i2c.yaml new file mode 100644 index 0000000000000..1da6dedbed9e2 --- /dev/null +++ b/dts/bindings/i2c/sifli,sf32lb-i2c.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2025, Qingsong Gou +# SPDX-License-Identifier: Apache-2.0 + +description: Sifli SF32LB I2C controller + +compatible: "sifli,sf32lb-i2c" + +include: [i2c-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true