From: sashiko-bot@kernel.org
To: lianfeng.ouyang <lianfeng.ouyang@starfivetech.com>
Cc: conor+dt@kernel.org, robh@kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH v1 3/3] i2c: dwc: Add StarFive JHB100 I2C master/slave support
Date: Thu, 21 May 2026 05:08:31 +0000 [thread overview]
Message-ID: <20260521050832.576051F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260521034340.27837-4-lianfeng.ouyang@starfivetech.com>
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [High] Global symbol namespace collisions and overriding of core driver functions break the generic DesignWare I2C driver.
- [High] Device tree references are leaked and a dangling pointer to the master adapter creates a Use-After-Free risk.
- [High] Unsigned arithmetic underflow in the SCL High Count (HCNT) calculation corrupts hardware timing.
- [High] Hardware timing registers are programmed locklessly, out of sequence, and lost on resume.
- [High] Unconditional read of the SMBus interrupt register on potentially unsupported hardware.
- [Medium] Incorrect register read invalidates the hardware High Speed capability check.
- [Medium] Slave mode initialization clobbers the interrupt state machine.
- [Medium] Mismatch between bus frequency fallback flag and actual timing values.
- [Low] Missing inclusion of 'i2c-dwc-core.h' causes compilation to fail due to undeclared identifiers.
- [Low] Missing EXPORT_SYMBOL for i2c_dw_slave_algo creates cross-module linking failures.
--
commit 30ef7c6d2faa5af3ac90a02dd939e51c945c393c
Author: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
i2c: dwc: Add StarFive JHB100 I2C master/slave support
Add support for the Synopsys DesignWare Core (DWC) I2C controller
found on the StarFive JHB100 platform. The controller supports both
master and slave modes and uses a different register layout compared
to the standard DesignWare I2C IP.
> diff --git a/drivers/i2c/busses/i2c-dwc-master.c b/drivers/i2c/busses/i2c-dwc-master.c
> new file mode 100644
> index 0000000000000..6e8d1828f99e6
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-dwc-master.c
> @@ -0,0 +1,441 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Synopsys DWC I2C adapter driver (master only).
> + *
> + * Based on the TI DAVINCI I2C adapter driver.
> + *
> + * Copyright (C) 2006 Texas Instruments.
> + * Copyright (C) 2007 MontaVista Software Inc.
> + * Copyright (C) 2009 Provigent Ltd.
> + */
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/export.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/units.h>
> +
> +#include "i2c-designware-core.h"
[Severity: Low]
Is there a missing include for "i2c-dwc-core.h" here? It looks like macros
like DWC_IC_CTRL are used in this file but not declared.
[ ... ]
> +/**
> + * i2c_dwc_scl_hcnt() - Calculate SCL HCNT
> + * @ic_clk: Input clock in kHz
> + * @thigh: Duration in ns of logic 1 to generate
> + * @tr: SCL rise time in ns
> + * @spk_cnt: Spike count
> + */
> +static u32 i2c_dwc_scl_hcnt(u32 ic_clk, u32 thigh, u32 tr, u32 spk_cnt)
> +{
> + u64 min_thigh_cnt, rise_cnt;
> +
> + /* Formula: cnt = f_kHz * t_ns * 10^(-6) */
> + min_thigh_cnt = DIV_ROUND_CLOSEST_ULL((u64)ic_clk * thigh, MICRO);
> + rise_cnt = DIV_ROUND_CLOSEST_ULL((u64)ic_clk * tr, MICRO);
> +
> + return max(5, min_thigh_cnt + rise_cnt - spk_cnt - 3);
[Severity: High]
Could the subtraction here cause an unsigned arithmetic underflow?
If the input clock frequency is low and the controller is in High Speed mode,
spk_cnt + 3 could exceed min_thigh_cnt + rise_cnt, which would result in a
huge positive value being written to the hardware register.
[ ... ]
> +static int i2c_dwc_set_timings_master(struct dw_i2c_dev *dev)
> +{
> + u32 scl_falling_time = 0, scl_rising_time = 0;
> + u32 scl_high_time = 0, scl_low_time = 0;
> + struct i2c_timings *t = &dev->timings;
> + unsigned int comp_param1;
> + u32 ic_clk, spk_cnt;
> + int ret;
> +
> + ret = i2c_dw_acquire_lock(dev);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(dev->map, DWC_IC_CTRL, &comp_param1);
[Severity: Medium]
Should this register read be from DW_IC_COMP_PARAM_1 instead of DWC_IC_CTRL?
The later code checks for DWC_IC_COMP_PARAM_1_SPEED_MODE_HIGH in
comp_param1, but DWC_IC_CTRL is at offset 0x24 while DW_IC_COMP_PARAM_1
is at 0xf4.
> + i2c_dw_release_lock(dev);
> + if (ret)
> + return ret;
> +
> + ic_clk = i2c_dw_clk_rate(dev);
> +
> + /* 50ns maximum spike */
> + spk_cnt = DIV_ROUND_CLOSEST_ULL((u64)ic_clk * 50, MICRO);
> +
> + regmap_write(dev->map, DWC_IC_HS_SPKLEN, spk_cnt);
> + regmap_write(dev->map, DWC_IC_SPKLEN, spk_cnt);
[Severity: High]
Are these registers being programmed locklessly and out of sequence?
The bus lock was released above, and this is happening before the controller
is disabled by __i2c_dw_disable(). Also, because this configuration is done
during probe, will the SPKLEN settings be lost upon resuming from suspend?
[ ... ]
> + /* Check is high speed possible and fall back to fast mode if not */
> + if ((dev->master_cfg & DWC_IC_CTRL_SPEED_MASK) == DWC_IC_CTRL_SPEED_HIGH) {
> + if ((comp_param1 & DWC_IC_COMP_PARAM_1_SPEED_MODE_MASK)
> + != DWC_IC_COMP_PARAM_1_SPEED_MODE_HIGH) {
> + dev_err(dev->dev, "High Speed not supported!\n");
> + t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
> + dev->master_cfg &= ~DWC_IC_CTRL_SPEED_MASK;
> + dev->master_cfg |= DWC_IC_CTRL_SPEED_FAST;
> + dev->hs_hcnt = 0;
> + dev->hs_lcnt = 0;
> +
> + /* Replace with I2C_MAX_FAST_MODE_PLUS_FREQ */
> + scl_rising_time = max(scl_rising_time, 120);
> + scl_falling_time = max(scl_falling_time, 120);
> + scl_high_time = 260; /* tHIGH_min = 260 ns */
> + scl_low_time = 500; /* tLOW_min = 500 ns */
[Severity: Medium]
Is there a mismatch here between the fallback bus frequency and the timing
values?
The bus frequency is set to I2C_MAX_FAST_MODE_FREQ (400kHz), but the
hardcoded timing values correspond to I2C_MAX_FAST_MODE_PLUS_FREQ (1MHz).
[ ... ]
> +int i2c_dw_init(struct dw_i2c_dev *dev)
[Severity: High]
Does defining a strong global function i2c_dw_init() here break the generic
DesignWare I2C driver?
If CONFIG_I2C_DWC_CORE is enabled, all standard DesignWare controllers might
execute this DWC-specific logic instead of their own, accessing incorrect
register offsets.
> +{
> + int ret;
[ ... ]
> +void i2c_dw_configure_master(struct dw_i2c_dev *dev)
[Severity: High]
Like i2c_dw_init() above, will defining i2c_dw_configure_master() here also
override the generic DesignWare implementation and break other generic hardware?
> +{
> + struct i2c_timings *t = &dev->timings;
[ ... ]
> +int i2c_dw_probe_master(struct dw_i2c_dev *dev)
[Severity: High]
Will defining i2c_dw_probe_master() here cause namespace collisions and override
the generic i2c-designware-core driver implementation?
> +{
> + struct i2c_adapter *adap = &dev->adapter;
> + unsigned int ic_ctrl;
> + int ret;
[ ... ]
> diff --git a/drivers/i2c/busses/i2c-dwc-slave.c b/drivers/i2c/busses/i2c-dwc-slave.c
> new file mode 100644
> index 0000000000000..5369eb8812382
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-dwc-slave.c
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Synopsys DWC I2C adapter driver (slave only).
> + *
> + * Based on the Synopsys DWC I2C adapter driver (master).
> + *
> + * Copyright (C) 2016 Synopsys Inc.
> + */
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +
> +#include "i2c-designware-core.h"
[Severity: Low]
Should "i2c-dwc-core.h" be included here to prevent build failures from
undeclared identifiers like DWC_IC_DAR?
> +
> +void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode)
> +{
> + if (dev->mode == DW_IC_SLAVE && mode == DW_IC_SLAVE) {
> + regmap_write(dev->map, DWC_IC_DAR, dev->slave->addr);
> + __i2c_dw_enable(dev);
> + dev->status = 0;
[Severity: Medium]
Could clearing dev->status unconditionally here clobber the interrupt state
machine?
Because the hardware is active and interrupts are already unmasked, an
interrupt might fire and update dev->status concurrently.
> + }
> +}
[ ... ]
> +u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
> +{
> + unsigned int stat;
> + unsigned int smbus_stat;
> +
> + /*
> + * The IC_INTR_STAT register just indicates "enabled" interrupts.
> + * The unmasked raw version of interrupt status bits is available
> + * in the IC_RAW_INTR_STAT register.
> + *
> + * That is,
> + * stat = readl(IC_INTR_STAT);
> + * equals to,
> + * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
> + *
> + * The raw version might be useful for debugging purposes.
> + */
> + regmap_read(dev->map, DW_IC_INTR_STAT, &stat);
> + regmap_read(dev->map, DWC_IC_SMBUS_INTR_STAT, &smbus_stat);
[Severity: High]
Is it safe to unconditionally read DWC_IC_SMBUS_INTR_STAT here?
The SMBus block is optional, and accessing unmapped or disabled IP block
registers might cause imprecise external aborts or bus faults on some
architectures. Should this check DWC_IC_CAPABILITIES_IC_SMBUS first?
[ ... ]
> +const struct i2c_algorithm i2c_dw_slave_algo = {
> + .master_xfer = i2c_dwc_xfer_ms,
> + .functionality = i2c_dw_func,
> + .reg_slave = i2c_dw_reg_slave,
> + .unreg_slave = i2c_dw_unreg_slave,
> +};
[Severity: Low]
Does i2c_dw_slave_algo need an EXPORT_SYMBOL_GPL()?
It is referenced by i2c-designware-common.c, which could cause a cross-module
linking failure if the drivers are built as separate modules.
[ ... ]
> +int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
[Severity: High]
Similar to the master driver, will defining a strong i2c_dw_probe_slave()
function here override the generic DesignWare implementation and break other
I2C controllers?
> +{
> + struct i2c_adapter *adap = &dev->adapter;
> + struct device_node *ms_node;
> + int ret;
> +
> + ret = i2c_dwc_init_slave(dev);
> + if (ret)
> + return ret;
> +
> + snprintf(adap->name, sizeof(adap->name), "Synopsys DWC I2C Slave adapter");
> +
> + /* Get the specified I2C master adapter from DTS for MCTP */
> + ms_node = of_parse_phandle(dev->dev->of_node, "starfive,mctp-i2c-ms", 0);
> + if (ms_node) {
> + struct platform_device *ms_pdev = of_find_device_by_node(ms_node);
> +
> + if (ms_pdev) {
> + struct dw_i2c_dev *ms_dev = platform_get_drvdata(ms_pdev);
> +
> + if (ms_dev)
> + dev->ms_adapter = &ms_dev->adapter;
[Severity: High]
Are we leaking device tree references and risking a Use-After-Free here?
of_parse_phandle() and of_find_device_by_node() increment reference counts,
but of_node_put() and put_device() are never called. Also, a raw pointer to
the master adapter is stored in dev->ms_adapter without taking a proper
reference. If the master device is unbound, dev->ms_adapter could become a
dangling pointer.
> + }
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(i2c_dw_probe_slave);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260521034340.27837-1-lianfeng.ouyang@starfivetech.com?part=3
next prev parent reply other threads:[~2026-05-21 5:08 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-21 3:43 [PATCH v1 0/3] i2c: dwc: Add I2C DWC master/slave support for StarFive JHB100 lianfeng.ouyang
2026-05-21 3:43 ` [PATCH v1 1/3] dt-bindings: i2c: snps,dwc-i2c: Add StarFive JHB100 bindings lianfeng.ouyang
2026-05-21 4:15 ` sashiko-bot
2026-05-21 3:43 ` [PATCH v1 2/3] i2c: designware: Export symbols and add __weak for DWC I2C driver lianfeng.ouyang
2026-05-21 4:31 ` sashiko-bot
2026-05-21 3:43 ` [PATCH v1 3/3] i2c: dwc: Add StarFive JHB100 I2C master/slave support lianfeng.ouyang
2026-05-21 5:08 ` sashiko-bot [this message]
2026-05-21 4:55 ` [PATCH v1 0/3] i2c: dwc: Add I2C DWC master/slave support for StarFive JHB100 Mika Westerberg
2026-05-21 7:01 ` 回复: " Lianfeng Ouyang
2026-05-21 8:47 ` Mika Westerberg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260521050832.576051F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=lianfeng.ouyang@starfivetech.com \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox