[PATCH v3 17/17] i3c: renesas: Add runtime PM support
Claudiu Beznea
claudiu.beznea at kernel.org
Thu Jun 11 11:51:41 PDT 2026
On 6/8/26 23:46, sashiko-bot at kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
>
> New issues:
> - [High] Async Runtime PM put during driver unbind leaves the clock enabled.
> - [High] Newly added renesas_i3c_abort_xfer clears the abort bit instead of setting it, leaving hardware stuck.
>
> Pre-existing issues:
> - [High] Timeout handling in transfer loops fails to break the loop, ignores the timeout, and incorrectly returns success.
> - [High] I3C RX buffer is not advanced during FIFO reads, leaving buffer tails uninitialized.
> --
>
> commit 810cc3873a9dc52a932c03376330719f06d17de0
> Author: Claudiu Beznea <claudiu.beznea.uj at bp.renesas.com>
>
> i3c: renesas: Add runtime PM support
>
> This commit adds runtime PM support to the Renesas I3C driver, replacing
> explicit clock management with runtime PM APIs. It updates controller
> operations with PM guards and introduces a transfer abort mechanism to handle
> timeouts asynchronously.
>
>> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
>> index b6c282c53a541..d6121ca869908 100644
>> --- a/drivers/i3c/master/renesas-i3c.c
>> +++ b/drivers/i3c/master/renesas-i3c.c
> [ ... ]
>> @@ -443,7 +442,24 @@ static void renesas_i3c_enqueue_xfer(struct renesas_i3c *i3c, struct renesas_i3c
>> }
>> }
>>
>> -static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
>> +static void renesas_i3c_abort_xfer(struct renesas_i3c *i3c)
>> +{
>> + guard(spinlock_irqsave)(&i3c->xferqueue.lock);
>> +
>> + /* Disable all the interrupts */
>> + renesas_writel(i3c->regs, BIE, 0);
>> + renesas_writel(i3c->regs, NTIE, 0);
>> +
>> + /* Clear normal transfer status flags. */
>> + renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0 | NTST_RSPQFF |
>> + NTST_TEF | NTST_TABTF);
>> + /* Clear bus status flags. */
>> + renesas_clear_bit(i3c->regs, BST, BST_NACKDF | BST_TENDF | BST_SPCNDDF);
>> + /* Clear error flags. */
>> + renesas_clear_bit(i3c->regs, BCTL, BCTL_ABT);
>
> [Severity: High]
> Does this clear the abort bit instead of setting it?
>
> Hardware abort bits like BCTL_ABT typically must be written as 1 to trigger
> an abort. If we only clear the bit, will the hardware controller be left
> running the timed-out transfer in the background and get stuck?
The role of the renesas_i3c_abort_xfer() is just to avoid triggering any new
interrupt in case a transfer times out. The handler disables the interrupts and
clear any status bits along with the BCTL.ABT bit.
Maybe a different name for the function would have been better.
>
>> +}
>> +
> [ ... ]
>> @@ -975,9 +1027,14 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_
>> renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
>> }
>>
>> - renesas_i3c_wait_xfer(i3c, xfer);
>> + time_left = renesas_i3c_wait_xfer(i3c, xfer);
>> + if (!time_left)
>> + abort_xfer = true;
>
> [Severity: High]
> This isn't a bug introduced by this patch, but does the lack of a break here
> cause the loop to continue iterating?
>
> If the transfer times out, it sets abort_xfer = true but continues to the
> next command, overwriting the shared cmd descriptor.
>
> Also, since renesas_i3c_i3c_xfers() unconditionally returns 0 at the end,
> will this hide the timeout from the subsystem and potentially cause it to
> read uninitialized RX buffers?
>
>> }
>>
>> + if (abort_xfer)
>> + renesas_i3c_abort_xfer(i3c);
>> +
>> return 0;
>> }
> [ ... ]
>> @@ -1045,8 +1112,12 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev)
>> struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
>> struct i3c_master_controller *m = i3c_dev_get_master(dev);
>> struct renesas_i3c *i3c = to_renesas_i3c(m);
>> + int ret;
>>
>> - renesas_writel(i3c->regs, DATBAS(data->index), 0);
>> + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
>> + ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
>> + if (!ret)
>> + renesas_writel(i3c->regs, DATBAS(data->index), 0);
>>
>> i3c_dev_set_master_data(dev, NULL);
>> i3c->addrs[data->index].addr = 0;
>
> [Severity: High]
> Does this asynchronously put the runtime PM usage counter without a
> synchronous suspend before driver teardown completes?
>
> During driver unbind, renesas_i3c_remove() unregisters the I3C master, which
> detaches devices. The PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND macro releases
> the device asynchronously via pm_runtime_put_autosuspend().
>
> Right after remove returns, devres calls pm_runtime_disable(), which cancels
> the pending autosuspend work.
Before calling pm_runtime_disable() the devres helper
(pm_runtime_disable_action()) call:
pm_runtime_dont_use_autosuspend() ->
__pm_runtime_use_autosuspend(dev, false) ->
update_autosuspend(dev, old_delay, old_use) ->
rpm_idle(dev, RPM_AUTO)
which should end up calling the idle and suspend callbacks.
> Could this leave the hardware permanently in
> the RPM_ACTIVE state and leak resources by never disabling the clocks?
>
> [ ... ]
>> @@ -1102,7 +1180,9 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
>>
>> renesas_set_bit(i3c->regs, NTSTE, NTSTE_TDBEE0);
>>
>> - wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
>> + time_left = wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
>> + if (!time_left)
>> + abort_xfer = true;
>>
>> if (cmd->err)
>> break;
>
> [Severity: High]
> This is a pre-existing issue, but similar to I3C transfers above, does this
> fail to break the loop when wait_for_completion_timeout() times out?
>
> Since cmd->err remains 0 on timeout, the loop continues and eventually returns
> 0, hiding the timeout from the subsystem.
>
>
> Additionally, I noticed a pre-existing issue outside the immediate scope of
> this patch:
>
> [Severity: High]
> This is a pre-existing issue, but in renesas_i3c_rx_isr() and
> renesas_i3c_resp_isr(), do the I3C FIFO reads copy data into cmd->rx_buf
> without advancing the buffer pointer?
>
> renesas_i3c_rx_isr() does:
>
> i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, read_bytes);
> cmd->rx_count = read_bytes;
>
> Since cmd->rx_buf is not incremented and rx_count is overwritten rather than
> accumulated, will multiple interrupts overwrite the beginning of the buffer?
> Could this leave the tail of the buffer uninitialized, leading to information
> leaks or data corruption when the subsystem reads it?
>
--
Thank you,
Claudiu
More information about the linux-i3c
mailing list