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

From: lianfeng.ouyang

Date: Thu May 21 2026 - 00:01:05 EST


From: Lianfeng Ouyang <lianfeng.ouyang@xxxxxxxxxxxxxxxx>

The upcoming StarFive DWC I2C driver is based on the DesignWare I2C
core but requires its own probe and configuration routines due to
register layout differences.

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 DWC driver to reuse the common infrastructure
while overriding the implementations where needed, promoting code
sharing without sacrificing flexibility for the DWC variant.

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

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

diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index 4dc57fd56170..cfeec5d338bb 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -167,6 +167,11 @@ static int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
map_cfg.max_register = AMD_UCSI_INTR_REG;

+#if IS_ENABLED(CONFIG_I2C_DWC_CORE)
+ if ((dev->flags & MODEL_MASK) == MODEL_STARFIVE)
+ map_cfg.max_register = DWC_IC_SMBUS_INTR_CLR;
+#endif
+
if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
map_cfg.reg_read = dw_reg_read_swab;
map_cfg.reg_write = dw_reg_write_swab;
@@ -411,7 +416,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;
@@ -430,7 +435,7 @@ void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode)
*
* Return: 0 on success, or negative errno otherwise.
*/
-int i2c_dw_init(struct dw_i2c_dev *dev)
+__weak int i2c_dw_init(struct dw_i2c_dev *dev)
{
int ret;

@@ -806,10 +811,25 @@ static int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
if (ret)
return ret;

+#if IS_ENABLED(CONFIG_I2C_DWC_CORE)
+ u32 tx_fifo_cfg = 8, rx_fifo_cfg = 8;
+
+#ifdef CONFIG_OF
+ ret = of_property_read_u32(dev->dev->of_node, "dwc-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, "dwc-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;
@@ -835,7 +855,9 @@ u32 i2c_dw_func(struct i2c_adapter *adap)

void i2c_dw_disable(struct dw_i2c_dev *dev)
{
+#if !IS_ENABLED(CONFIG_I2C_DWC_CORE)
unsigned int dummy;
+#endif
int ret;

ret = i2c_dw_acquire_lock(dev);
@@ -847,7 +869,12 @@ void i2c_dw_disable(struct dw_i2c_dev *dev)

/* Disable all interrupts */
__i2c_dw_write_intr_mask(dev, 0);
+
+#if IS_ENABLED(CONFIG_I2C_DWC_CORE)
+ regmap_write(dev->map, DWC_IC_INTR_CLR, DWC_CLR_INTR);
+#else
regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
+#endif

i2c_dw_release_lock(dev);
}
@@ -896,6 +923,12 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
if (ret)
return ret;

+#if IS_ENABLED(CONFIG_I2C_DWC_CORE)
+ if (dev->mode == DW_IC_SLAVE)
+ i2c_dw_probe_slave(dev);
+ else
+ i2c_dw_probe_master(dev);
+#else
ret = i2c_dw_probe_master(dev);
if (ret)
return ret;
@@ -906,10 +939,16 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)

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

adap->retries = 3;
adap->algo = &i2c_dw_algo;
+#if IS_ENABLED(CONFIG_I2C_DWC_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 +977,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_DWC_CORE) || 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..263bff23dd3b 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_DWC_CORE)
+ 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,16 @@ 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);

+void i2c_dw_xfer_init(struct dw_i2c_dev *dev);
+int i2c_dw_init_recovery_info(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_DWC_CORE)
+#include "i2c-dwc-core.h"
+#else
static inline void __i2c_dw_enable(struct dw_i2c_dev *dev)
{
dev->status |= STATUS_ACTIVE;
@@ -372,6 +386,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)
@@ -409,11 +424,21 @@ static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { }
static inline irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) { return IRQ_NONE; }
#endif

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

int i2c_dw_probe(struct dw_i2c_dev *dev);
int i2c_dw_init(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..ef15f590ac5c 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -185,7 +185,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
return 0;
}

-static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
+__weak void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
u32 ic_con = 0, ic_tar = 0;
@@ -397,8 +397,12 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
* IC_RESTART_EN are set, we must manually
* set restart bit between messages.
*/
+#if IS_ENABLED(CONFIG_I2C_DWC_CORE)
+ if (dev->msg_write_idx > 0)
+#else
if ((dev->master_cfg & DW_IC_CON_RESTART_EN) &&
(dev->msg_write_idx > 0))
+#endif
need_restart = true;
}

@@ -570,7 +574,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;

@@ -921,7 +925,7 @@ int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
return i2c_dw_xfer_common(dev, msgs, num);
}

-void i2c_dw_configure_master(struct dw_i2c_dev *dev)
+__weak void i2c_dw_configure_master(struct dw_i2c_dev *dev)
{
struct i2c_timings *t = &dev->timings;

@@ -967,7 +971,7 @@ static void i2c_dw_unprepare_recovery(struct i2c_adapter *adap)
i2c_dw_init(dev);
}

-static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
+int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
{
struct i2c_bus_recovery_info *rinfo = &dev->rinfo;
struct i2c_adapter *adap = &dev->adapter;
@@ -1006,7 +1010,7 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
return 0;
}

-int i2c_dw_probe_master(struct dw_i2c_dev *dev)
+__weak int i2c_dw_probe_master(struct dw_i2c_dev *dev)
{
unsigned int ic_con;
int ret;
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 426ffec06e22..a637c5ab0ea4 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-dwc-i2c-master") ||
+ device_is_compatible(device, "starfive,jhb100-dwc-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-dwc-i2c-master" },
+ { .compatible = "starfive,jhb100-dwc-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..7b03dc88286a 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;

@@ -176,7 +176,7 @@ irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev)
return IRQ_HANDLED;
}

-void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
+__weak void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
{
if (dev->flags & ACCESS_POLLING)
return;
--
2.43.0