Re: [PATCH v5 2/3] i2c: nt72676: Add Novatek NT72676 I2C controller driver

[email protected] Wed, 5 Aug 2026 20:13:22 +0800
Newsgroups dev.linux.lists.sashiko-reviews,org.kernel.vger.linux-devicetree
Message-ID <[email protected]>
From: Ben Huang <[email protected]>

On Tue, 04 Aug 2026 03:45:15 +0000, [email protected] wrote:
>> +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 = nt72676_i2c_readl(i2c, I2C_REG_INTR);
>> +	unsigned int clr = 0;
>> +	int do_complete = 0;
>> +
>> +	if (!(status & I2C_IRQ_FLAG) || !i2c->current_msg)
>> +		return IRQ_NONE;
>
> [Severity: High]
> Could this early return cause an interrupt storm if a spurious interrupt
> fires, or if current_msg is NULL after a timeout? Since it returns
> IRQ_NONE without writing to the clear register, the level-triggered
> hardware interrupt might remain asserted indefinitely.

The potential issue of IRQ storm under the condition !i2c->current_msg will
be fixed by clearing all IRQs.

>> +
>> +	if (status & I2C_IRQ_NACK) {
>> +		i2c->error_code = -ENXIO;
>> +		clr |= I2C_IRQ_NACK << 8;
>> +	} else if (status & I2C_IRQ_RX_FULL) {
>
> [Severity: High]
> What happens if the hardware asserts I2C_IRQ_CLK_STR_TIMEOUT? The
> I2C_IRQ_ENABLE_SETTING macro enables this interrupt (bit 12), but it is
> omitted from this if/else chain. Does this mean it won't be added to
> the clr variable, leaving it asserted and causing an interrupt storm?

Apologies for loss of handling IRQ I2C_IRQ_CLK_STR_TIMEOUT.
The flow of I2C_IRQ_CLK_STR_TIMEOUT will be added.

>> +	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)
>> +		dev_err(i2c->dev, "[%s]: write failed (err:%d); SA[0x%X]\n",
>> +			i2c->adapter.name, i2c->error_code, msg->addr);
>> +
>> +	i2c->current_msg = NULL;
>
> [Severity: High]
> Can the timeout path race with the interrupt handler here? If the transfer
> times out, current_msg is set to NULL and the function returns, leading the
> caller to free the message buffer. If the ISR executes concurrently on another
> CPU, could it access the freed buffer via current_msg before it gets fully
> nullified, causing a use-after-free?

Spin lock for current_msg and the interrupt register will be added.

>> +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];
>
> [Severity: High]
> Is it correct to unconditionally treat the first message of any 2-message
> transfer as a subaddress write? If a user-space caller sends a 2-message array
> where the first message is a read, has a length > 4, or targets a different
> device address, won't this silently drop or misinterpret the first message and
> only execute the second one?

For the 2-message transfer, the flow of checking msg[0] is a sub-address write and
with the maximum length of 4 bytes (32 bits) will be added.

>> +	ret = nvt_i2c_init(i2c);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Setup I2C adapter */
>> +	i2c->adapter.owner = THIS_MODULE;
>> +	i2c->adapter.algo = &nvt_i2c_algo;
>> +	i2c->adapter.dev.of_node = pdev->dev.of_node;
>> +	i2c->adapter.dev.parent = &pdev->dev;
>> +	i2c->adapter.timeout = 3 * HZ;
>> +	strscpy(i2c->adapter.name, dev_name(&pdev->dev), sizeof(i2c->adapter.name));
>> +	i2c_set_adapdata(&i2c->adapter, i2c);
>> +
>> +	ret = i2c_add_adapter(&i2c->adapter);
>> +	if (ret)
>> +		return dev_err_probe(&pdev->dev, ret, "Failed to add adapter\n");
>
> [Severity: Medium]
> Does this error path need to disable the hardware interrupts that were
> enabled earlier in nvt_i2c_init()? Returning directly might leave
> interrupts enabled on a shared IRQ line.

nvt_i2c_init() will be moved after successful i2c_add_adapter().
If nvt_i2c_init() fails, no interrupts are enabled and i2c_del_adapter() will be
called for releasing the adapter resources.

>> +
>> +	platform_set_drvdata(pdev, i2c);
>> +
>> +	return 0;
>> +}
>> +
>> +static void nvt_i2c_remove(struct platform_device *pdev)
>> +{
>> +	struct nvt_i2c_bus *i2c = platform_get_drvdata(pdev);
>> +
>> +	nt72676_i2c_writel(I2C_IRQ_DISABLE_SETTING, i2c, I2C_REG_INTR);
>> +	nt72676_i2c_writel(nt72676_i2c_readl(i2c, I2C_REG_CTRL) & ~I2C_ENABLE,
>> +			   i2c, I2C_REG_CTRL);
>> +	i2c_del_adapter(&i2c->adapter);
>
> [Severity: High]
> Should the adapter be deleted before disabling the I2C controller hardware?
> Disabling interrupts and clocks first could break concurrent ongoing
> transfers or client device removal operations that happen during
> i2c_del_adapter().

i2c_del_adapter() will be moved before disabling the I2C controller.


The issues above will be fixed at the next iteration.

Sincerely,
Ben Huang