[PATCH v2 2/3] i2c: designware: Export symbols and add __weak for Starfive I2C driver

From: lianfeng.ouyang

Date: Wed May 27 2026 - 05:27:07 EST


From: Lianfeng Ouyang <lianfeng.ouyang@xxxxxxxxxxxxxxxx>

Export several key functions (i2c_dw_probe_master, i2c_dw_init,
i2c_dw_xfer_init, i2c_dw_read_clear_intrbits, etc.) and mark them as
__weak. This allows the i2c starfive driver to reuse the common
infrastructure while overriding the implementations where needed.

Additionally, extend the register map configuration and introduce the
MODEL_STARFIVE flag to accommodate the starfive i2c IP's different
register space.

Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@xxxxxxxxxxxxxxxx>
---
drivers/i2c/busses/i2c-designware-common.c | 72 ++++++++++++++++-----
drivers/i2c/busses/i2c-designware-core.h | 20 ++++++
drivers/i2c/busses/i2c-designware-master.c | 4 +-
drivers/i2c/busses/i2c-designware-platdrv.c | 6 ++
drivers/i2c/busses/i2c-designware-slave.c | 2 +-
5 files changed, 84 insertions(+), 20 deletions(-)

diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index 4dc57fd56170..034f6a446ab6 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -70,6 +70,7 @@ static const char *const abort_sources[] = {
"incorrect slave-transmitter mode configuration",
};

+#if !IS_ENABLED(CONFIG_I2C_STARFIVE)
static int dw_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct dw_i2c_dev *dev = context;
@@ -87,6 +88,7 @@ static int dw_reg_write(void *context, unsigned int reg, unsigned int val)

return 0;
}
+#endif

static int dw_reg_read_swab(void *context, unsigned int reg, unsigned int *val)
{
@@ -143,9 +145,15 @@ static int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
.val_bits = 32,
.reg_stride = 4,
.disable_locking = true,
+#if IS_ENABLED(CONFIG_I2C_STARFIVE)
+ .reg_read = sf_reg_read,
+ .reg_write = sf_reg_write,
+ .max_register = SF_IC_SMBUS_INTR_CLR,
+#else
.reg_read = dw_reg_read,
.reg_write = dw_reg_write,
.max_register = DW_IC_COMP_TYPE,
+#endif
};
u32 reg;
int ret;
@@ -162,6 +170,10 @@ static int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
return ret;

reg = readl(dev->base + DW_IC_COMP_TYPE);
+
+ if ((dev->flags & MODEL_MASK) == MODEL_STARFIVE)
+ reg = readl(dev->base + (SF_IC_COMP_TYPE & (~SF_REG_FLAG)));
+
i2c_dw_release_lock(dev);

if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
@@ -359,7 +371,7 @@ static inline u32 i2c_dw_acpi_round_bus_speed(struct device *device) { return 0;

#endif /* CONFIG_ACPI */

-static void i2c_dw_configure_mode(struct dw_i2c_dev *dev, int mode)
+__weak void i2c_dw_configure_mode(struct dw_i2c_dev *dev, int mode)
{
switch (mode) {
case DW_IC_MASTER:
@@ -382,7 +394,7 @@ static void i2c_dw_configure_mode(struct dw_i2c_dev *dev, int mode)
}
}

-static void i2c_dw_write_timings(struct dw_i2c_dev *dev)
+__weak void i2c_dw_write_timings(struct dw_i2c_dev *dev)
{
/* Write standard speed timing parameters */
regmap_write(dev->map, DW_IC_SS_SCL_HCNT, dev->ss_hcnt);
@@ -411,7 +423,7 @@ static void i2c_dw_write_timings(struct dw_i2c_dev *dev)
*
* The controller must be disabled before this function is called.
*/
-void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode)
+__weak void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode)
{
if (mode == DW_IC_SLAVE && !dev->slave)
mode = DW_IC_MASTER;
@@ -806,10 +818,25 @@ static int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
if (ret)
return ret;

+#if IS_ENABLED(CONFIG_I2C_STARFIVE)
+ u32 tx_fifo_cfg = 8, rx_fifo_cfg = 8;
+
+#ifdef CONFIG_OF
+ ret = of_property_read_u32(dev->dev->of_node, "starfive,i2c-tx-fifo-depth", &tx_fifo_cfg);
+ if (!ret && (tx_fifo_cfg < 2 || tx_fifo_cfg > 256))
+ tx_fifo_cfg = 8;
+
+ ret = of_property_read_u32(dev->dev->of_node, "starfive,i2c-rx-fifo-depth", &rx_fifo_cfg);
+ if (!ret && (rx_fifo_cfg < 2 || rx_fifo_cfg > 256))
+ rx_fifo_cfg = 8;
+#endif
+ param = rx_fifo_cfg << 8 | tx_fifo_cfg << 16;
+#else
ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &param);
i2c_dw_release_lock(dev);
if (ret)
return ret;
+#endif

tx_fifo_depth = FIELD_GET(DW_IC_FIFO_TX_FIELD, param) + 1;
rx_fifo_depth = FIELD_GET(DW_IC_FIFO_RX_FIELD, param) + 1;
@@ -896,20 +923,29 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
if (ret)
return ret;

- ret = i2c_dw_probe_master(dev);
- if (ret)
- return ret;
+ if (dev->mode == DW_IC_SLAVE) {
+ ret = i2c_sf_probe_slave(dev);
+ } else {
+ ret = i2c_dw_probe_master(dev);
+ if (ret)
+ return ret;

- ret = i2c_dw_init(dev);
- if (ret)
- return ret;
+ ret = i2c_dw_init(dev);
+ if (ret)
+ return ret;

- if (!adap->name[0])
- strscpy(adap->name, "Synopsys DesignWare I2C adapter");
+ if (!adap->name[0])
+ strscpy(adap->name, "Synopsys DesignWare I2C adapter");
+ }

adap->retries = 3;
adap->algo = &i2c_dw_algo;
+#if IS_ENABLED(CONFIG_I2C_STARFIVE_SLAVE)
+ if (dev->mode == DW_IC_SLAVE)
+ adap->algo = &i2c_dw_slave_algo;
+#else
adap->quirks = &i2c_dw_quirks;
+#endif
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);

@@ -938,16 +974,18 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
if (!dev->emptyfifo_hold_master)
irq_flags |= IRQF_NO_THREAD;

- ret = i2c_dw_acquire_lock(dev);
- if (ret)
- return ret;
+ if (!IS_ENABLED(CONFIG_I2C_STARFIVE) || dev->mode == DW_IC_MASTER) {
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;

- __i2c_dw_write_intr_mask(dev, 0);
- i2c_dw_release_lock(dev);
+ __i2c_dw_write_intr_mask(dev, 0);
+ i2c_dw_release_lock(dev);
+ }

if (!(dev->flags & ACCESS_POLLING)) {
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr,
- irq_flags, dev_name(dev->dev), dev);
+ irq_flags, dev_name(dev->dev), dev);
if (ret)
return ret;
}
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 9d8d104cc391..87223f7e28aa 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -321,6 +321,11 @@ struct dw_i2c_dev {
u32 bus_capacitance_pF;
bool clk_freq_optimized;
bool emptyfifo_hold_master;
+#if IS_ENABLED(CONFIG_I2C_STARFIVE)
+ u16 scl_hcnt;
+ u16 scl_lcnt;
+ struct i2c_adapter *ms_adapter; /* Bind another I2C master controller */
+#endif
};

#define ACCESS_INTR_MASK BIT(0)
@@ -328,6 +333,7 @@ struct dw_i2c_dev {
#define ARBITRATION_SEMAPHORE BIT(2)
#define ACCESS_POLLING BIT(3)

+#define MODEL_STARFIVE BIT(9)
#define MODEL_AMD_NAVI_GPU BIT(10)
#define MODEL_WANGXUN_SP BIT(11)
#define MODEL_MASK GENMASK(11, 8)
@@ -359,8 +365,14 @@ int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev);
u32 i2c_dw_func(struct i2c_adapter *adap);
irqreturn_t i2c_dw_isr_master(struct dw_i2c_dev *dev);

+u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev);
+u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev);
+
extern const struct dev_pm_ops i2c_dw_dev_pm_ops;

+#if IS_ENABLED(CONFIG_I2C_STARFIVE)
+#include "i2c-starfive-core.h"
+#else
static inline void __i2c_dw_enable(struct dw_i2c_dev *dev)
{
dev->status |= STATUS_ACTIVE;
@@ -372,6 +384,7 @@ static inline void __i2c_dw_disable_nowait(struct dw_i2c_dev *dev)
regmap_write(dev->map, DW_IC_ENABLE, 0);
dev->status &= ~STATUS_ACTIVE;
}
+#endif

static inline void __i2c_dw_write_intr_mask(struct dw_i2c_dev *dev,
unsigned int intr_mask)
@@ -411,8 +424,15 @@ static inline irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) { return IRQ_

static inline void i2c_dw_configure(struct dw_i2c_dev *dev)
{
+#if IS_ENABLED(CONFIG_I2C_STARFIVE)
+ if (device_is_compatible(dev->dev, "starfive,jhb100-i2c-slave"))
+ i2c_sf_configure_slave(dev);
+ else
+ i2c_sf_configure_master(dev);
+#else
i2c_dw_configure_slave(dev);
i2c_dw_configure_master(dev);
+#endif
}

int i2c_dw_probe(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index de929b91d5ea..f27c0fc761da 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -31,7 +31,7 @@
#define AMD_TIMEOUT_MAX_US 250
#define AMD_MASTERCFG_MASK GENMASK(15, 0)

-static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
+__weak int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
{
unsigned int comp_param1;
u32 sda_falling_time, scl_falling_time;
@@ -570,7 +570,7 @@ i2c_dw_read(struct dw_i2c_dev *dev)
}
}

-static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
+__weak u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
{
unsigned int stat, dummy;

diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 426ffec06e22..8c0f5c39e4ff 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -151,6 +151,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
if (device_property_present(device, "wx,i2c-snps-model"))
flags = MODEL_WANGXUN_SP | ACCESS_POLLING;

+ if (device_is_compatible(device, "starfive,jhb100-i2c-master") ||
+ device_is_compatible(device, "starfive,jhb100-i2c-slave"))
+ flags |= MODEL_STARFIVE;
+
dev->dev = device;
dev->irq = irq;
dev->flags = flags;
@@ -255,6 +259,8 @@ static const struct of_device_id dw_i2c_of_match[] = {
{ .compatible = "mobileye,eyeq6lplus-i2c" },
{ .compatible = "mscc,ocelot-i2c" },
{ .compatible = "snps,designware-i2c" },
+ { .compatible = "starfive,jhb100-i2c-master" },
+ { .compatible = "starfive,jhb100-i2c-slave" },
{}
};
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c
index ad0d5fbfa6d5..1b94e4fbaad1 100644
--- a/drivers/i2c/busses/i2c-designware-slave.c
+++ b/drivers/i2c/busses/i2c-designware-slave.c
@@ -61,7 +61,7 @@ int i2c_dw_unreg_slave(struct i2c_client *slave)
return 0;
}

-static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
+__weak u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
{
unsigned int stat, dummy;

--
2.43.0