From: sashiko-bot@kernel.org
To: Nina_Kuo@novatek.com.tw
Cc: robh@kernel.org, devicetree@vger.kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v3 3/3] i2c: Add i2c-nt726xx.c I2C driver for Novatek NT726xx SoCs
Date: Wed, 15 Jul 2026 07:47:25 +0000 [thread overview]
Message-ID: <20260715074726.593981F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260715073534.147663-1-Nina_Kuo@novatek.com.tw>
Thank you for your contribution! Sashiko AI review found 11 potential issue(s) to consider:
- [High] The driver bypasses Device Tree abstractions by mapping hardcoded physical MMIO addresses, and triggers a sleep-in-atomic panic during system resume.
- [High] The master transfer function improperly intercepts 2-message sequences, silently dropping the first message and potentially leaking uninitialized kernel memory to hardware.
- [High] The interrupt handler returns IRQ_NONE without clearing asserted IRQ flags on unhandled events, leading to a hard lockup.
- [High] A race condition between the transfer timeout path and the ISR leads to a use-after-free of the I2C message buffer.
- [High] Hardware interrupts are enabled before the driver state is initialized or the adapter is registered.
- [High] Hardware and interrupts are disabled before unregistering the I2C adapter in `remove`, forcefully stalling ongoing transfers.
- [High] `I2C_REG_CTRL` accumulates target addresses and operation flags across multiple messages without clearing previous states.
- [Medium] Device tree node reference leak in the probe error path.
- [Medium] The driver uses a custom string property 'novatek,hwmods' to assign the I2C adapter number instead of standard DT aliases.
- [Low] PM callbacks and `dev_pm_ops` are unconditionally assigned, producing dead code when PM is disabled.
- [Low] Missing Kconfig OF dependency leads to unused variable warnings.
--
--- Patch [3]: [PATCH v3 3/3] i2c: Add i2c-nt726xx.c I2C driver for Novatek NT726xx SoCs ---
commit fffdbc67de89a90f8a40ad053da0475e89a835d6
Author: Ben Huang <Ben_Huang@novatek.com.tw>
Subject: i2c: Add i2c-nt726xx.c I2C driver for Novatek NT726xx SoCs
This patch introduces the I2C bus controller driver for Novatek NT726xx SoCs,
supporting standard and fast modes.
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index d354569942803..52ee2c82e9d5e 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -962,6 +962,16 @@ config I2C_NPCM
> controllers.
> Driver can also support slave mode (select I2C_SLAVE).
>
> +config I2C_NT726XX
> + tristate "Novatek NT726xx Driver"
> + default n
[Severity: Low]
Does this need a "depends on OF" directive? Without it, of_match_ptr() can
evaluate to NULL when CONFIG_OF is disabled, causing a compiler warning for
an unused nvt_i2c_of_match variable.
[ ... ]
> diff --git a/drivers/i2c/busses/i2c-nt726xx.c b/drivers/i2c/busses/i2c-nt726xx.c
> new file mode 100644
> index 0000000000000..ac3fd7ccc68cb
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-nt726xx.c
[ ... ]
> +static void nvt_i2c_use_case_feature(struct nvt_i2c_bus *i2c)
> +{
> + void __iomem *reg_tmp;
> +
> + if (i2c->stbc_i2c) {
> + reg_tmp = ioremap(STBC_PSWD, 4);
[Severity: High]
Is it safe to call ioremap() on a hardcoded physical address here? Since
this is called from nvt_i2c_resume() via the resume_early PM hook,
interrupts might be disabled. ioremap() can sleep, which could trigger a
scheduling-while-atomic panic during early resume.
Also, should this physical address be managed via standard device tree
resources instead?
[ ... ]
> +static irqreturn_t nvt_i2c_isr(int irq, void *dev_id)
> +{
> + struct nvt_i2c_bus *i2c = dev_id;
> + struct i2c_msg *msg = i2c->current_msg;
> + unsigned int status = readl(i2c->base + I2C_REG_INTR);
> + unsigned int clr = 0;
> + int do_complete = 0;
> +
> + if (!(status & I2C_IRQ_FLAG) || !i2c->current_msg)
> + return IRQ_NONE;
[Severity: High]
If an interrupt fires while current_msg is NULL, this returns IRQ_NONE
without clearing the hardware interrupt status in I2C_REG_INTR. Since the
IRQ is requested as IRQF_TRIGGER_HIGH (level-triggered), won't the
uncleared interrupt keep the IRQ line asserted and lead to an interrupt
storm?
[ ... ]
> +static int nvt_i2c_write(struct nvt_i2c_bus *i2c, struct i2c_msg *msg)
> +{
[ ... ]
> + ctrl_mask = readl(i2c->base + I2C_REG_CTRL);
> + ctrl_mask |= (((msg->addr << 1) << 8) | I2C_ENABLE |
> + I2C_CLOCK_DUTY_ENABLE | I2C_CLOCK_STRETCH_ENABLE |
> + I2C_MASTER_CLK_STRETCH_ENABLE | I2C_TRIGGER);
> + writel(ctrl_mask, i2c->base + I2C_REG_CTRL);
[Severity: High]
Since this uses a bitwise OR to set the address and flags (like
I2C_READ_OPERATION) into ctrl_mask, and the previous state is not cleared,
won't subsequent messages bitwise OR their target address with the previous
message's address? Could this also cause a write message to be executed as
a read if the previous message was a read?
> +
> + ret = wait_for_completion_timeout(&i2c->msg_complete, i2c->adapter.timeout);
> + if (ret == 0) {
> + i2c->error_code = -ETIMEDOUT;
> + nvt_i2c_reset(i2c);
> + }
> + if (i2c->error_code)
> + I2C_ERR_LOG("[%s.%d]: write failed (err:%d);"
> + " SA[0x%X]\n",
> + dev_name(i2c->dev), i2c->adapter.nr, i2c->error_code,
> + msg->addr);
> +
> + i2c->current_msg = NULL;
> +
> + return i2c->error_code;
> +}
[Severity: High]
When a transfer times out, this sets i2c->current_msg to NULL and returns,
which allows the caller to free the message buffer. What happens if the ISR
is running concurrently on another CPU and has already cached current_msg?
Could the ISR proceed to access the freed msg->buf, causing a
use-after-free?
[ ... ]
> +static int nvt_i2c_xfer(struct i2c_adapter *adap,
> + struct i2c_msg msgs[],
> + int num)
> +{
> + struct nvt_i2c_bus *i2c = i2c_get_adapdata(adap);
> + int ret = 0, i = 0;
> + struct i2c_msg *msg = NULL;
> +
> + nvt_i2c_ctrl_init(i2c);
> +
> + if (num == 2) {
> + nvt_i2c_set_subaddr(i2c, &msgs[0]);
> + msg = &msgs[1];
> +
> + if (msg->flags & I2C_M_RD)
> + ret = nvt_i2c_read(i2c, msg);
> + else
> + ret = nvt_i2c_write(i2c, msg);
[Severity: High]
Is it intentional to unconditionally pass msgs[0] to nvt_i2c_set_subaddr()
and only execute the transfer for msgs[1]?
If msgs[0] is actually a read request, its buffer could be uninitialized
memory, which nvt_i2c_set_subaddr() might then read and write to the
hardware register, potentially leaking kernel memory. Additionally, wouldn't
this break the API contract by silently dropping the first message of a
valid 2-message sequence?
[ ... ]
> +static int nvt_i2c_parse_dts(struct nvt_i2c_bus *i2c)
> +{
> + int ret;
> + struct device *dev = i2c->dev;
> + struct device_node *np = dev->of_node;
> + const char *hwmods_val = NULL;
> +
> + /* read DTS(novatek,hwmods) and set bus number */
> + ret = of_property_read_string(np, "novatek,hwmods", &hwmods_val);
> + if (ret == 0) {
> + i2c->adapter.nr = nvt_i2c_get_hwmods(hwmods_val);
[Severity: Medium]
Is there a specific reason to use a custom property like 'novatek,hwmods'
for assigning the I2C bus number? Typically, fixed indices for I2C
controllers are assigned using standard device tree aliases, like this:
aliases { i2c0 = &i2c_node; };
[ ... ]
> +static int nvt_i2c_probe(struct platform_device *pdev)
> +{
[ ... ]
> + nvt_i2c_init(i2c);
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + I2C_ERR_LOG("No IRQ resource\n");
> + return irq;
> + }
> +
> + ret = devm_request_irq(&pdev->dev, irq, nvt_i2c_isr,
[Severity: High]
The nvt_i2c_init() function enables hardware interrupts before the handler
is registered by devm_request_irq(). If the controller already has a
pending interrupt on boot, won't this trigger an immediate interrupt storm,
especially since the ISR returns IRQ_NONE when current_msg is NULL?
[ ... ]
> + ret = i2c_add_numbered_adapter(&i2c->adapter);
> + if (ret) {
> + I2C_ERR_LOG("[%s] failed to add adapter\n", dev_name(&pdev->dev));
> + return ret;
> + }
[Severity: Medium]
If i2c_add_numbered_adapter() fails, does this error path need to call
of_node_put() to release the reference to i2c->adapter.dev.of_node
acquired earlier via of_node_get()?
[ ... ]
> +static void nvt_i2c_remove(struct platform_device *pdev)
> +{
> + struct nvt_i2c_bus *i2c = platform_get_drvdata(pdev);
> +
> + writel(I2C_IRQ_DISABLE_SETTING, i2c->base + I2C_REG_INTR);
> + writel(readl(i2c->base + I2C_REG_CTRL) & ~I2C_ENABLE,
> + i2c->base + I2C_REG_CTRL);
> + of_node_put(i2c->adapter.dev.of_node);
> + i2c_del_adapter(&i2c->adapter);
> +}
[Severity: High]
By disabling the I2C controller and its interrupts before calling
i2c_del_adapter(), couldn't we interrupt active I2C operations?
If a transfer is ongoing concurrently, disabling the hardware out from under
it will cause it to stall and time out. Should i2c_del_adapter() be called
first to cleanly block new transfers and wait for active ones to finish?
> +
> +static const struct dev_pm_ops nvt_i2c_pm_ops = {
> + .resume_early = nvt_i2c_resume,
> + .suspend_late = nvt_i2c_suspend,
> +};
[Severity: Low]
Should these callbacks be wrapped with pm_sleep_ptr()? When CONFIG_PM_SLEEP
is disabled, these functions and the struct assignment below will become
dead code and might trigger compiler warnings.
> +
> +static struct platform_driver nvt_i2c_driver = {
> + .probe = nvt_i2c_probe,
> + .remove = nvt_i2c_remove,
> + .driver = {
> + .name = "nvt_nt726xx_i2c",
> + .owner = THIS_MODULE,
> + .pm = &nvt_i2c_pm_ops,
[Severity: Low]
Can we wrap the pm_ops pointer with pm_ptr() to ensure it compiles away cleanly
when CONFIG_PM is disabled?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715073521.147610-1-Nina_Kuo@novatek.com.tw?part=3
next prev parent reply other threads:[~2026-07-15 7:47 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-15 7:34 [PATCH v3 0/3] i2c: Add Novatek NT726xx SoC I2C controller Nina_Kuo
2026-07-15 7:35 ` [PATCH v3 1/3] MAINTAINERS: Add entry for Novatek NT726xx SoC I2C controller driver Nina_Kuo
2026-07-15 7:35 ` [PATCH v3 2/3] dt-bindings: i2c: Add Novatek NT726xx SoC I2C controller Nina_Kuo
2026-07-15 7:35 ` [PATCH v3 3/3] i2c: Add i2c-nt726xx.c I2C driver for Novatek NT726xx SoCs Nina_Kuo
2026-07-15 7:47 ` sashiko-bot [this message]
2026-07-15 7:44 ` [PATCH v3 2/3] dt-bindings: i2c: Add Novatek NT726xx SoC I2C controller sashiko-bot
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=20260715074726.593981F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=Nina_Kuo@novatek.com.tw \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.