Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boards/sifli/sf32lb52_devkit_lcd/sf32lb52_devkit_lcd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ flash: 16320
toolchain:
- zephyr
supported:
- i2c
- uart
- gpio
vendor: sifli
1 change: 1 addition & 0 deletions drivers/i2c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions drivers/i2c/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 9 additions & 0 deletions drivers/i2c/Kconfig.sf32lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2025, Qingsong Gou <[email protected]>
# 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.
311 changes: 311 additions & 0 deletions drivers/i2c/i2c_sf32lb.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
/*
* Copyright (c) 2025, Qingsong Gou <[email protected]>
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT sifli_sf32lb_i2c

#include <zephyr/arch/cpu.h>
#include <zephyr/device.h>
#include <zephyr/drivers/clock_control/sf32lb.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(i2c_sf32lb, CONFIG_I2C_LOG_LEVEL);

#include <register.h>

#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)
37 changes: 37 additions & 0 deletions dts/arm/sifli/sf32lb52x.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -229,6 +265,7 @@
status = "disabled";
};
};

};
};

Expand Down
Loading