* [PATCH v4 01/16] i3c: renesas: Check that the transfer is valid before accessing it
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:20 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 02/16] i3c: renesas: Restore STDBR and EXTBR registers on resume Claudiu Beznea
` (15 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
The Renesas I3C driver uses an asynchronous model to transfer data. It
prepares a struct renesas_i3c_xfer, enqueues it, and waits for completion.
The interrupt handler dequeues the transfer, updates/uses it, and signals
the waiting thread.
If the completion times out, the waiting thread dequeues the transfer and
free it. If an interrupt fires after that, the handler may access freed
memory, leading to crashes.
Check that the transfer is still valid before accessing it in the
interrupt handler. Along with it, clear any status flag to avoid
triggering the same interrupts again.
Fixes: d028219a9f14 ("i3c: master: Add basic driver for the Renesas I3C controller")
Cc: stable@vger.kernel.org
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- disable also the interrupts
- dropped the Rb tag
Changes in v3:
- none
Changes in v2:
- clean the IRQ status bits before returning IRQ_HANDLED and adjusted the
patch description to reflect this change
- collected Frank's tag. Frank, please let me know if you consider
I should drop your tag. Thanks!
drivers/i3c/master/renesas-i3c.c | 50 ++++++++++++++++++++++++++++----
1 file changed, 45 insertions(+), 5 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index f39c449922ca..bc1a0ae1d12d 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -1014,6 +1014,12 @@ static irqreturn_t renesas_i3c_tx_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ /* Disable interrupts. */
+ renesas_clear_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
if (xfer->is_i2c_xfer) {
@@ -1053,11 +1059,20 @@ static irqreturn_t renesas_i3c_resp_isr(int irq, void *data)
int ret = 0;
scoped_guard(spinlock, &i3c->xferqueue.lock) {
+ /* Clear the Respone Queue Full status flag */
+ renesas_clear_bit(i3c->regs, NTST, NTST_RSPQFF);
+
xfer = i3c->xferqueue.cur;
- cmd = xfer->cmds;
+ if (!xfer) {
+ /* Disable interrupts. */
+ renesas_clear_bit(i3c->regs, NTIE, NTIE_TDBEIE0 | NTIE_RDBFIE0);
+ /* Clear any error flags. */
+ renesas_clear_bit(i3c->regs, BCTL, BCTL_ABT);
+ renesas_clear_bit(i3c->regs, NTST, NTST_TEF | NTST_TABTF);
+ return IRQ_HANDLED;
+ }
- /* Clear the Respone Queue Full status flag*/
- renesas_clear_bit(i3c->regs, NTST, NTST_RSPQFF);
+ cmd = xfer->cmds;
data_len = NRSPQP_DATA_LEN(resp_descriptor);
@@ -1138,6 +1153,15 @@ static irqreturn_t renesas_i3c_tend_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ /* Disable interrupts. */
+ renesas_clear_bit(i3c->regs, BIE, BIE_TENDIE | BIE_TENDIE);
+ renesas_clear_bit(i3c->regs, NTSTE, NTSTE_TDBEE0);
+ /* Clear any status flag. */
+ renesas_clear_bit(i3c->regs, BST, BST_NACKDF | BST_TENDF);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
if (xfer->is_i2c_xfer) {
@@ -1184,6 +1208,14 @@ static irqreturn_t renesas_i3c_rx_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ /* Clear any status registers. */
+ renesas_clear_bit(i3c->regs, BST, BST_SPCNDDF);
+ /* Clear the Read Buffer Full status flag. */
+ renesas_clear_bit(i3c->regs, NTST, NTST_RDBFF0);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
if (xfer->is_i2c_xfer) {
@@ -1234,8 +1266,6 @@ static irqreturn_t renesas_i3c_stop_isr(int irq, void *data)
struct renesas_i3c_xfer *xfer;
scoped_guard(spinlock, &i3c->xferqueue.lock) {
- xfer = i3c->xferqueue.cur;
-
/* read back registers to confirm writes have fully propagated */
renesas_writel(i3c->regs, BST, 0);
renesas_readl(i3c->regs, BST);
@@ -1243,6 +1273,10 @@ static irqreturn_t renesas_i3c_stop_isr(int irq, void *data)
renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0);
renesas_clear_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE);
+ xfer = i3c->xferqueue.cur;
+ if (!xfer)
+ return IRQ_HANDLED;
+
xfer->ret = 0;
complete(&xfer->comp);
}
@@ -1259,6 +1293,12 @@ static irqreturn_t renesas_i3c_start_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ /* Clear any status registers. */
+ renesas_clear_bit(i3c->regs, BST, BST_STCNDDF);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
if (xfer->is_i2c_xfer) {
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 01/16] i3c: renesas: Check that the transfer is valid before accessing it
2026-06-12 16:04 ` [PATCH v4 01/16] i3c: renesas: Check that the transfer is valid before accessing it Claudiu Beznea
@ 2026-06-12 16:20 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:20 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: Frank.Li, linux-i3c
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
New issues:
- [High] The `!xfer` early return path in `renesas_i3c_rx_isr` fails to disable the `NTIE_RDBFIE0` interrupt, causing an interrupt storm.
- [High] A typo in the `!xfer` early return path of `renesas_i3c_tend_isr` duplicates the `BIE_TENDIE` flag, failing to mask the NACK interrupt.
- [High] The `!xfer` early return path in `renesas_i3c_start_isr` fails to disable the `BIE_STCNDDIE` interrupt.
Pre-existing issues:
- [Critical] The transfer functions `renesas_i3c_i3c_xfers` and `renesas_i3c_i2c_xfers` silently ignore timeouts and hardware errors, falsely returning success.
- [Critical] Hardware State Desync causing cross-transfer corruption due to failure to abort hardware on timeout.
- [Critical] FIFO overflow in I3C writes due to unconditionally writing the entire transfer length without progress tracking.
- [Critical] Out-of-bounds write and pointer offset corruption in `renesas_i3c_rx_isr` during I3C reads.
- [High] Hardware interrupt registers are modified via non-atomic read-modify-write operations from process context without locking, causing lost mask updates.
--
commit 80958e2863c878df9b87948c2774499050e47aee
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Check that the transfer is valid before accessing it
The Renesas I3C driver uses an asynchronous model to transfer data. It
prepares a struct renesas_i3c_xfer, enqueues it, and waits for completion.
The interrupt handler dequeues the transfer, updates/uses it, and signals
the waiting thread.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index f39c449922ca2..bc1a0ae1d12d8 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
[ ... ]
> @@ -1138,6 +1153,15 @@ static irqreturn_t renesas_i3c_tend_isr(int irq, void *data)
>
> scoped_guard(spinlock, &i3c->xferqueue.lock) {
> xfer = i3c->xferqueue.cur;
> + if (!xfer) {
> + /* Disable interrupts. */
> + renesas_clear_bit(i3c->regs, BIE, BIE_TENDIE | BIE_TENDIE);
[Severity: High]
Does this duplicated flag fail to mask the NACK interrupt?
It appears the code is meant to mask both the Transmit End and NACK
interrupts (as the next line clears BST_NACKDF | BST_TENDF).
The failure to include BIE_NACKDIE leaves the NACK interrupt unmasked,
which could cause an interrupt storm if a NACK triggered the ISR.
> + renesas_clear_bit(i3c->regs, NTSTE, NTSTE_TDBEE0);
> + /* Clear any status flag. */
> + renesas_clear_bit(i3c->regs, BST, BST_NACKDF | BST_TENDF);
> + return IRQ_HANDLED;
> + }
[ ... ]
> @@ -1184,6 +1208,14 @@ static irqreturn_t renesas_i3c_rx_isr(int irq, void *data)
>
> scoped_guard(spinlock, &i3c->xferqueue.lock) {
> xfer = i3c->xferqueue.cur;
> + if (!xfer) {
> + /* Clear any status registers. */
> + renesas_clear_bit(i3c->regs, BST, BST_SPCNDDF);
> + /* Clear the Read Buffer Full status flag. */
> + renesas_clear_bit(i3c->regs, NTST, NTST_RDBFF0);
> + return IRQ_HANDLED;
> + }
[Severity: High]
Can this early return path cause an interrupt storm?
When a Read Buffer Full interrupt fires, this attempts to handle an aborted
transfer by simply clearing the status flag NTST_RDBFF0.
Because the hardware fifo remains full and the interrupt enable bit
NTIE_RDBFIE0 is not disabled, the interrupt will immediately re-trigger
when the ISR returns.
[ ... ]
> @@ -1259,6 +1293,12 @@ static irqreturn_t renesas_i3c_start_isr(int irq, void *data)
>
> scoped_guard(spinlock, &i3c->xferqueue.lock) {
> xfer = i3c->xferqueue.cur;
> + if (!xfer) {
> + /* Clear any status registers. */
> + renesas_clear_bit(i3c->regs, BST, BST_STCNDDF);
> + return IRQ_HANDLED;
> + }
[Severity: High]
Does this early return path fail to disable the Start Condition interrupt?
The early return clears the BST_STCNDDF status flag but doesn't disable the
Start Condition interrupt via BIE_STCNDDIE.
This could leave the interrupt permanently enabled, leading to stray
interrupts whenever a Start condition is detected on the bus.
Additionally, while reviewing this file, I noticed a few pre-existing regressions:
[Severity: High]
This is a pre-existing issue, but are hardware interrupt registers modified
without locking, potentially causing lost mask updates?
In drivers/i3c/master/renesas-i3c.c:renesas_i3c_i2c_xfers():
renesas_set_bit(i3c->regs, BIE, BIE_NACKDIE);
renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
renesas_set_bit(i3c->regs, BIE, BIE_STCNDDIE);
These configure hardware interrupt registers using renesas_set_bit() from
process context without holding the i3c->xferqueue.lock spinlock.
renesas_set_bit() performs a non-atomic read-modify-write sequence. If a
hardware interrupt fires between the read and the write, the ISR's interrupt
disabling operations (which properly hold the lock) could be entirely
overwritten and lost when the process context writes its stale value back.
[Severity: High]
This is a pre-existing issue, but does renesas_i3c_i3c_xfers() silently ignore
timeouts and hardware errors?
In drivers/i3c/master/renesas-i3c.c:renesas_i3c_i3c_xfers():
time_left = renesas_i3c_wait_xfer(i3c, xfer);
if (!time_left)
xfer_failed = true;
if (xfer_failed)
renesas_i3c_irqs_mask_and_clear(i3c);
return 0;
If renesas_i3c_wait_xfer() times out, xfer_failed is set to true, but the
loop continues processing subsequent transfers, and the function
unconditionally returns 0 at the end.
Hardware errors propagated via xfer->ret are never checked or returned,
which could lead to silent data corruption as subsystems assume transfers
succeeded.
[Severity: High]
This is a pre-existing issue, but can a timeout cause hardware state desync
and cross-transfer corruption?
In drivers/i3c/master/renesas-i3c.c:renesas_i3c_wait_xfer():
time_left = wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000));
if (!time_left)
renesas_i3c_dequeue_xfer(i3c, xfer);
When this times out, it dequeues xfer from the software queue but does not
instruct the hardware controller to abort the ongoing physical transaction.
The caller loop in renesas_i3c_i3c_xfers() then immediately queues the next
transfer using the exact same xfer memory allocation:
struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
Because the hardware is still processing the first transfer, its delayed
interrupt will match !xfer == false (as cur now points to the same re-queued
xfer structure). The ISR could complete the second transfer prematurely using
the results of the first.
[Severity: High]
This is a pre-existing issue, but can I3C writes overflow the hardware fifo
due to unconditionally writing the entire transfer length?
In drivers/i3c/master/renesas-i3c.c:renesas_i3c_tx_isr():
} else {
i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len);
}
Both the initial enqueue and the tx interrupt handler blindly call
i3c_writel_fifo() with cmd->len bytes. The hardware fifo depth is fixed at 16
words (64 bytes).
If cmd->len is larger than 64, the fifo could immediately overflow and
corrupt the transfer and hardware state. Also, since cmd->tx_count is
neither used nor updated, each subsequent tx interrupt would repeatedly
write the entire buffer from offset 0.
[Severity: High]
This is a pre-existing issue, but can I3C reads cause an out-of-bounds write
and pointer offset corruption?
In drivers/i3c/master/renesas-i3c.c:renesas_i3c_rx_isr():
} else {
read_bytes = NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) * sizeof(u32);
i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, read_bytes);
cmd->rx_count = read_bytes;
}
When an I3C read triggers, it calculates read_bytes by multiplying the number
of 32-bit words in the hardware fifo by sizeof(u32).
If the requested read length is unaligned (e.g., 2 bytes), reading 4 bytes
could corrupt the adjacent heap memory. Moreover, the pointer cmd->rx_buf is
never advanced, and cmd->rx_count is assigned rather than incremented,
meaning multi-part transfers could repeatedly overwrite the beginning of the
buffer.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=1
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 02/16] i3c: renesas: Restore STDBR and EXTBR registers on resume
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
2026-06-12 16:04 ` [PATCH v4 01/16] i3c: renesas: Check that the transfer is valid before accessing it Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:17 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 03/16] i3c: renesas: Follow the reset deassert order used in probe Claudiu Beznea
` (14 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
The Renesas RZ/G3S supports a power saving state where power to the most
SoC componentes (including I3C) is lost.
The STDBR and EXTBR are configured in initialization phase though the
struct i3c_master_controller_ops::bus_init. Set them on resume function
as well to keep the same state of the controller after a suspend with
power loss and a similar initialization sequence as in bus_init.
Fixes: e7218986319b ("i3c: renesas: Add suspend/resume support")
Cc: stable@vger.kernel.org
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index bc1a0ae1d12d..e70db3a17bc7 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -260,6 +260,7 @@ struct renesas_i3c {
u32 dyn_addr;
u32 i2c_STDBR;
u32 i3c_STDBR;
+ u32 extbr;
unsigned long rate;
u8 addrs[RENESAS_I3C_MAX_DEVS];
struct renesas_i3c_xferqueue xferqueue;
@@ -607,10 +608,9 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
/* Extended Bit Rate setting */
- renesas_writel(i3c->regs, EXTBR, EXTBR_EBRLO(od_low_ticks) |
- EXTBR_EBRHO(od_high_ticks) |
- EXTBR_EBRLP(pp_low_ticks) |
- EXTBR_EBRHP(pp_high_ticks));
+ i3c->extbr = EXTBR_EBRLO(od_low_ticks) | EXTBR_EBRHO(od_high_ticks) |
+ EXTBR_EBRLP(pp_low_ticks) | EXTBR_EBRHP(pp_high_ticks);
+ renesas_writel(i3c->regs, EXTBR, i3c->extbr);
renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
i3c->refclk_div = cks;
@@ -1470,6 +1470,8 @@ static int renesas_i3c_resume_noirq(struct device *dev)
goto err_tresetn;
/* Re-store I3C registers value. */
+ renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
+ renesas_writel(i3c->regs, EXTBR, i3c->extbr);
renesas_writel(i3c->regs, REFCKCTL,
REFCKCTL_IREFCKS(i3c->refclk_div));
renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV |
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 02/16] i3c: renesas: Restore STDBR and EXTBR registers on resume
2026-06-12 16:04 ` [PATCH v4 02/16] i3c: renesas: Restore STDBR and EXTBR registers on resume Claudiu Beznea
@ 2026-06-12 16:17 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:17 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: linux-i3c, Frank.Li
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Silent data truncation in EXTBR register configuration for slow clock speeds.
--
commit decdc939209046d74f5ae66361c115e1c225a379
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Restore STDBR and EXTBR registers on resume
This commit restores the STDBR and EXTBR registers during the resume process
for the Renesas RZ/G3S. This ensures the controller maintains the same state
after a suspend with power loss.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
[ ... ]
> @@ -607,10 +608,9 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
> renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
>
> /* Extended Bit Rate setting */
> - renesas_writel(i3c->regs, EXTBR, EXTBR_EBRLO(od_low_ticks) |
> - EXTBR_EBRHO(od_high_ticks) |
> - EXTBR_EBRLP(pp_low_ticks) |
> - EXTBR_EBRHP(pp_high_ticks));
> + i3c->extbr = EXTBR_EBRLO(od_low_ticks) | EXTBR_EBRHO(od_high_ticks) |
> + EXTBR_EBRLP(pp_low_ticks) | EXTBR_EBRHP(pp_high_ticks);
[Severity: High]
This is a pre-existing issue, but does this code silently truncate
od_low_ticks and od_high_ticks when the clock is slow?
For slow I2C speeds, od_low_ticks and od_high_ticks can reach up to 510
because the clock divider loop only ensures (od_low_ticks / 2) <= 0xFF.
The STDBR register configuration applies a right shift when double_SBR is
set to avoid overflow.
Since the EXTBR_EBRLO and EXTBR_EBRHO macros use 8-bit masks, and this line
doesn't apply the double_SBR right shift, won't any tick value greater than
255 get truncated by the bitwise AND and result in incorrect extended bit
rate timings?
> + renesas_writel(i3c->regs, EXTBR, i3c->extbr);
>
> renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
> i3c->refclk_div = cks;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=2
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 03/16] i3c: renesas: Follow the reset deassert order used in probe
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
2026-06-12 16:04 ` [PATCH v4 01/16] i3c: renesas: Check that the transfer is valid before accessing it Claudiu Beznea
2026-06-12 16:04 ` [PATCH v4 02/16] i3c: renesas: Restore STDBR and EXTBR registers on resume Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:17 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 04/16] i3c: renesas: Reconfigure the DATBAS register on re-attach Claudiu Beznea
` (13 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Use the same reset deassert order in the resume and probe paths to avoid
potential failures due to ordering differences.
Fixes: e7218986319b ("i3c: renesas: Add suspend/resume support")
Cc: stable@vger.kernel.org
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index e70db3a17bc7..7d448936b74c 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -1457,17 +1457,17 @@ static int renesas_i3c_resume_noirq(struct device *dev)
struct renesas_i3c *i3c = dev_get_drvdata(dev);
int i, ret;
- ret = reset_control_deassert(i3c->presetn);
+ ret = reset_control_deassert(i3c->tresetn);
if (ret)
return ret;
- ret = reset_control_deassert(i3c->tresetn);
+ ret = reset_control_deassert(i3c->presetn);
if (ret)
- goto err_presetn;
+ goto err_tresetn;
ret = clk_bulk_enable(i3c->num_clks, i3c->clks);
if (ret)
- goto err_tresetn;
+ goto err_presetn;
/* Re-store I3C registers value. */
renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
@@ -1488,10 +1488,10 @@ static int renesas_i3c_resume_noirq(struct device *dev)
return 0;
-err_tresetn:
- reset_control_assert(i3c->tresetn);
err_presetn:
reset_control_assert(i3c->presetn);
+err_tresetn:
+ reset_control_assert(i3c->tresetn);
return ret;
}
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 03/16] i3c: renesas: Follow the reset deassert order used in probe
2026-06-12 16:04 ` [PATCH v4 03/16] i3c: renesas: Follow the reset deassert order used in probe Claudiu Beznea
@ 2026-06-12 16:17 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:17 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: Frank.Li, linux-i3c
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] In `renesas_i3c_i2c_xfers`, unhandled timeouts cause the loop to proceed, risking data corruption and silent failures.
- [High] In `renesas_i3c_i3c_xfers`, transfer statuses and errors are completely ignored, unconditionally returning 0.
- [High] Missing hardware abort sequence on transfer timeout risks controller lockup.
--
commit 85523970e99a91763d831b25e5f1d70eaf5d402f
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Follow the reset deassert order used in probe
Use the same reset deassert order in the resume and probe paths to avoid
potential failures due to ordering differences.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index e70db3a17bc72..7d448936b74c1 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
> @@ -1457,17 +1457,17 @@ static int renesas_i3c_resume_noirq(struct device *dev)
> struct renesas_i3c *i3c = dev_get_drvdata(dev);
> int i, ret;
>
> - ret = reset_control_deassert(i3c->presetn);
> + ret = reset_control_deassert(i3c->tresetn);
> if (ret)
> return ret;
[ ... ]
While reviewing this file, I noticed a few pre-existing issues that might need
your attention:
[Severity: High]
This is a pre-existing issue, but does this code properly handle timeouts
during I2C transfers?
drivers/i3c/master/renesas-i3c.c:renesas_i3c_i2c_xfers() {
...
renesas_set_bit(i3c->regs, NTSTE, NTSTE_TDBEE0);
wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
if (cmd->err)
break;
start_bit = CNDCTL_SRCND;
...
}
If wait_for_completion_timeout() times out, its return value is discarded and
cmd->err remains 0. The loop condition if (cmd->err) break; does not trigger,
and the loop proceeds to the next iteration.
Can this overwrite shared state and issue a new START condition while the
hardware might still be processing the previous request? This could lead to
silent failures and controller desynchronization since the function eventually
returns success despite the timeout.
[Severity: High]
This is a pre-existing issue, but does renesas_i3c_i3c_xfers() ignore hardware
errors and timeouts?
drivers/i3c/master/renesas-i3c.c:renesas_i3c_i3c_xfers() {
...
if (!i3c_xfers[i].rnw && i3c_xfers[i].len > 4) {
i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len);
if (cmd->len > NTDTBP0_DEPTH * sizeof(u32))
renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
}
renesas_i3c_wait_xfer(i3c, xfer);
}
return 0;
}
The loop iterates over i3c_nxfers and waits for each transfer using
renesas_i3c_wait_xfer(), but completely ignores xfer->ret which is populated
by the ISR with actual hardware error statuses like NACK or parity errors.
The function then unconditionally returns 0. Could masking all hardware errors
and timeouts from the upper layers lead to subsequent silent failures?
[Severity: High]
This is a pre-existing issue, but is there a missing hardware abort sequence
when a transfer times out?
drivers/i3c/master/renesas-i3c.c:renesas_i3c_wait_xfer() {
...
static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
{
unsigned long time_left;
renesas_i3c_enqueue_xfer(i3c, xfer);
time_left = wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000));
if (!time_left)
renesas_i3c_dequeue_xfer(i3c, xfer);
}
...
}
When a transfer times out, the software state is dequeued via
renesas_i3c_dequeue_xfer(). However, it does not appear to set the BCTL_ABT
bit or reset the controller, leaving the hardware in an indeterminate busy
state.
When the next transfer is enqueued, renesas_i3c_start_xfer_locked()
indiscriminately writes new commands to the hardware command queue (NCMDQP).
Could this cause a bus collision or controller lockup?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=3
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 04/16] i3c: renesas: Reconfigure the DATBAS register on re-attach
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (2 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 03/16] i3c: renesas: Follow the reset deassert order used in probe Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:24 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 05/16] i3c: renesas: Reset the controller on resume Claudiu Beznea
` (12 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
During re-attach, the device may change its position in the i3c->addrs[]
array. As a result, it may use a different Device Address Table Basic
Register (DATBAS), which needs to be reconfigured.
Reconfigure the DATBAS register on re-attach. Along with it update
software caches.
Fixes: d028219a9f14 ("i3c: master: Add basic driver for the Renesas I3C controller")
Cc: stable@vger.kernel.org
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- use "data->index > pos" condition
Changes in v3:
- collected tags
Changes in v2:
- dropped the "if (pos < 0)" check in renesas_i3c_reattach_i3c_dev() to allow
re-attaching in case of a full bus; along with it the condition to update
the DATBAS register and software caches was updated to
if (data->index != pos && pos >= 0)
- adjusted the patch title
drivers/i3c/master/renesas-i3c.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 7d448936b74c..4b81c32b1fde 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -892,10 +892,26 @@ static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev,
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ int pos;
+
+ pos = renesas_i3c_get_free_pos(i3c);
+
+ if (data->index > pos && pos >= 0) {
+ renesas_writel(i3c->regs, DATBAS(data->index), 0);
+ i3c->addrs[data->index] = 0;
+ i3c->free_pos |= BIT(data->index);
+
+ data->index = pos;
+ i3c->free_pos &= ~BIT(data->index);
+ }
i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
dev->info.static_addr;
+ renesas_writel(i3c->regs, DATBAS(data->index),
+ DATBAS_DVSTAD(dev->info.static_addr) |
+ datbas_dvdyad_with_parity(i3c->addrs[data->index]));
+
return 0;
}
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 04/16] i3c: renesas: Reconfigure the DATBAS register on re-attach
2026-06-12 16:04 ` [PATCH v4 04/16] i3c: renesas: Reconfigure the DATBAS register on re-attach Claudiu Beznea
@ 2026-06-12 16:24 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:24 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: Frank.Li, linux-i3c
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Hardware transfers are not aborted on software timeout, permanently desynchronizing the bus state and precipitating severe memory corruption.
- [High] Hardware DAA implementation erroneously assumes free device slots are contiguous, assigning already-in-use addresses to new devices under fragmented slot conditions.
- [High] Ignored completion timeout returns in `renesas_i3c_i2c_xfers` lead to silent operational failures.
- [High] Undefined behavior in dynamic `GENMASK` generation causes false phantom device attachment.
--
commit 80894950fcbc5a0242a4c816117f647e2715af0b
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Reconfigure the DATBAS register on re-attach
This commit reconfigures the DATBAS register during re-attach to handle
cases where a device changes its position in the i3c->addrs[] array. It
ensures the correct Device Address Table Basic Register is used and
updates software caches.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index 7d448936b74c1..4b81c32b1fdec 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
[ ... ]
> @@ -892,10 +892,26 @@ static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev,
> struct i3c_master_controller *m = i3c_dev_get_master(dev);
> struct renesas_i3c *i3c = to_renesas_i3c(m);
> struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
> + int pos;
> +
> + pos = renesas_i3c_get_free_pos(i3c);
> +
> + if (data->index > pos && pos >= 0) {
> + renesas_writel(i3c->regs, DATBAS(data->index), 0);
> + i3c->addrs[data->index] = 0;
> + i3c->free_pos |= BIT(data->index);
> +
> + data->index = pos;
> + i3c->free_pos &= ~BIT(data->index);
> + }
>
> i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
> dev->info.static_addr;
>
> + renesas_writel(i3c->regs, DATBAS(data->index),
> + DATBAS_DVSTAD(dev->info.static_addr) |
> + datbas_dvdyad_with_parity(i3c->addrs[data->index]));
> +
> return 0;
> }
[Severity: High]
This isn't a bug introduced by this patch, but looking at renesas_i3c_daa()
in this file, is it possible for the hardware to overwrite an active device
if slots are fragmented?
In renesas_i3c_daa():
cmd->cmd0 = NCMDQP_CMD_ATTR(NCMDQP_ADDR_ASSGN) | NCMDQP_ROC |
NCMDQP_TID(I3C_COMMAND_ADDRESS_ASSIGNMENT) |
NCMDQP_CMD(I3C_CCC_ENTDAA) | NCMDQP_DEV_INDEX(ret) |
NCMDQP_DEV_COUNT(i3c->maxdevs - ret) | NCMDQP_TOC;
The code passes i3c->maxdevs - ret to NCMDQP_DEV_COUNT, assuming all
subsequent slots are free. If slot 1 is free but slot 2 is in use, could the
hardware increment its internal DATBAS index and overwrite the active device
in slot 2 when multiple new devices respond to ENTDAA?
[Severity: Critical]
This is a pre-existing issue, but I have a question about how hardware aborts
are handled on software timeout.
When wait_for_completion_timeout() expires in renesas_i3c_wait_xfer():
time_left = wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000));
if (!time_left)
renesas_i3c_dequeue_xfer(i3c, xfer);
The xfer is dequeued, but the hardware transfer doesn't appear to be aborted.
If a new transfer is enqueued, and the hardware eventually completes the
timed-out transfer, could renesas_i3c_resp_isr() blindly apply the late
response to the new transfer buffer?
xfer = i3c->xferqueue.cur;
...
if (NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) && !cmd->err)
bytes_remaining = data_len - cmd->rx_count;
i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, bytes_remaining);
Does this create a scenario where i3c_readl_fifo() copies data into the new
cmd->rx_buf based on the old data_len, potentially causing a buffer overflow?
[Severity: High]
This isn't a bug introduced by this patch, but does the I2C transfer logic
mask completion timeouts?
In renesas_i3c_i2c_xfers():
time_left = wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
if (!time_left)
xfer_failed = true;
if (cmd->err)
break;
If the timeout expires, xfer_failed is set to true but cmd->err remains 0
because the ISR never executed. Since cmd->err is 0, the loop does not break,
which seems like it could cause the driver to continue to the next message
and eventually return 0, masking the timeout.
[Severity: High]
This is a pre-existing issue, but can the dynamic mask generation in
renesas_i3c_daa() result in phantom device attachments?
In renesas_i3c_daa(), when configuring the mask for new devices:
newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, first_i3c_pos);
If no devices respond to the ENTDAA command, cmd->rx_count is set to
i3c->maxdevs - ret. If ret is 0, this makes the upper bound of GENMASK
evaluate to -1. Would executing GENMASK(-1, 0) evaluate to ~0UL and cause
the driver to iterate over all bits, falsely registering phantom devices
via i3c_master_add_i3c_dev_locked()?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=4
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 05/16] i3c: renesas: Reset the controller on resume
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (3 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 04/16] i3c: renesas: Reconfigure the DATBAS register on re-attach Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:18 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 06/16] i3c: renesas: Perform Dynamic Address Assignment " Claudiu Beznea
` (11 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Reset the controller on resume after enabling the clocks to follow the
same sequence as in probe and avoid potential ordering related failures.
With it, renesas_i3c_reset() was updated to use read_poll_timeout_atomic(),
as the driver's resume callback is executed during the noirq phase of
resume, where interrupts are disabled.
Fixes: e7218986319b ("i3c: renesas: Add suspend/resume support")
Cc: stable@vger.kernel.org
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- collected tags
Changes in v2:
- replaced the read_poll_timeout() in renesas_i3c_reset() with
read_poll_timeout_atomic() as the renesas_i3c_reset() is called
in noirq phase of the suspend/resume; updated the patch description
to reflect that
- collected Frank's tag. Frank, please let me know if this should be
dropped. Thanks!
drivers/i3c/master/renesas-i3c.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 4b81c32b1fde..67b9a31300d0 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -480,8 +480,8 @@ static int renesas_i3c_reset(struct renesas_i3c *i3c)
renesas_writel(i3c->regs, BCTL, 0);
renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST);
- return read_poll_timeout(renesas_readl, val, !(val & RSTCTL_RI3CRST),
- 0, 1000, false, i3c->regs, RSTCTL);
+ return read_poll_timeout_atomic(renesas_readl, val, !(val & RSTCTL_RI3CRST),
+ 0, 1000, false, i3c->regs, RSTCTL);
}
static void renesas_i3c_hw_init(struct renesas_i3c *i3c)
@@ -1485,6 +1485,10 @@ static int renesas_i3c_resume_noirq(struct device *dev)
if (ret)
goto err_presetn;
+ ret = renesas_i3c_reset(i3c);
+ if (ret)
+ goto err_clks_disable;
+
/* Re-store I3C registers value. */
renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
renesas_writel(i3c->regs, EXTBR, i3c->extbr);
@@ -1504,6 +1508,8 @@ static int renesas_i3c_resume_noirq(struct device *dev)
return 0;
+err_clks_disable:
+ clk_bulk_disable(i3c->num_clks, i3c->clks);
err_presetn:
reset_control_assert(i3c->presetn);
err_tresetn:
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 05/16] i3c: renesas: Reset the controller on resume
2026-06-12 16:04 ` [PATCH v4 05/16] i3c: renesas: Reset the controller on resume Claudiu Beznea
@ 2026-06-12 16:18 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:18 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: linux-i3c, Frank.Li
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Missing hardware cancellation on transfer timeout causes use-after-free and memory corruption.
- [High] The driver ignores transfer timeouts and errors, silently reporting success.
--
commit 06142498cb9164f084dca924bf2625138121a705
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Reset the controller on resume
This commit updates renesas_i3c_reset() to use read_poll_timeout_atomic()
since the resume callback runs in the noirq phase. It also resets the
controller on resume to follow the probe sequence and avoid ordering
related failures.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index 4b81c32b1fdec..67b9a31300d09 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
[ ... ]
> @@ -1504,6 +1508,8 @@ static int renesas_i3c_resume_noirq(struct device *dev)
>
> return 0;
>
> +err_clks_disable:
> + clk_bulk_disable(i3c->num_clks, i3c->clks);
> err_presetn:
> reset_control_assert(i3c->presetn);
> err_tresetn:
[Severity: Critical]
This is a pre-existing issue, but does the driver have a structural flaw
with transfer timeouts causing memory corruption?
When a transfer times out in renesas_i3c_wait_xfer(), the transfer is
dequeued and can be freed:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_wait_xfer() {
...
if (!time_left)
renesas_i3c_dequeue_xfer(i3c, xfer);
...
}
However, the active hardware transaction is never aborted. If the target
eventually completes the transaction, the hardware will write a response
descriptor to NRSPQP.
When a subsequent transfer is initiated, the driver re-enables the interrupt,
and the ISR will fire, picking up the new transfer:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_resp_isr() {
...
xfer = i3c->xferqueue.cur;
...
data_len = NRSPQP_DATA_LEN(resp_descriptor);
...
i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, bytes_remaining);
...
}
Will the ISR blindly read the stale data from the old completed hardware
transaction into the new transfer's rx_buf?
If the old transfer's length exceeds the new transfer's buffer size, couldn't
this overflow the buffer and cause memory corruption?
[Severity: High]
This is a pre-existing issue, but does the driver ignore transfer timeouts
and errors, silently reporting success?
In renesas_i3c_i3c_xfers(), the function ignores transfer timeouts and errors,
returning 0 unconditionally:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_i3c_xfers() {
...
if (xfer_failed)
renesas_i3c_irqs_mask_and_clear(i3c);
return 0;
}
Similarly, in renesas_i3c_i2c_xfers():
drivers/i3c/master/renesas-i3c.c:renesas_i3c_i2c_xfers() {
...
if (xfer_failed)
renesas_i3c_irqs_mask_and_clear(i3c);
return cmd->err;
}
Since cmd->err is only updated to a negative code by the ISR upon an explicit
NACK, it remains 0 on timeout. Doesn't this falsely claim that the message
was successfully delivered to the core I3C subsystem?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=5
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 06/16] i3c: renesas: Perform Dynamic Address Assignment on resume
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (4 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 05/16] i3c: renesas: Reset the controller on resume Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:19 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 07/16] i3c: renesas: Clean DATBAS register on detach Claudiu Beznea
` (10 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
The Renesas RZ/G3S SoC supports a power saving mode where power to most
SoC components, including I3C, is turned off.
On systems where the I3C devices also loses power during suspend (e.g. NXP
P3T1085UK-ARD connected to the PMOD1_6A connector of the RZ SMARC Carrier
2 + Renesas RZ/G3S SMARC SOM), the devices becomes unreachable after
resume.
Running DAA in the controller resume path restores communication. However,
DAA relies on interrupts for TX/RX, which are not available in the noirq
suspend/resume phase (unless they are wakeup interrupts). For this, the
suspend/resume callbacks were moved out of the noirq phase. Currently,
there is no identified use case on either the Renesas RZ/G3S or Renesas
RZ/G3E SoCs that requires the controller suspend/resume hooks to be part of
the noirq suspend/resume phase.
Since renesas_i3c_reset() is not called anymore in atomic context
update it to use read_poll_timeout().
To cover the case where the controller had already attached all the
i3c->maxdevs devices before a suspend/resume cycle and i3c->free_pos is
zero, struct renesas_i3c::resuming flag was introduced.
The flag is set in renesas_i3c_resume() before calling
i3c_master_do_daa_ext() and checked in renesas_i3c_daa().
When resuming from suspend, renesas_i3c_daa() re-runs DAA for all slots
except those used by I2C devices. Since I2C devices may be attached both
during probe (at bus initialization time) and at runtime, the device
slots maintained by the driver may become interleaved with both I2C and
I3C devices.
However, the controller performs dynamic address assignment for I3C
devices starting from a specified DATBAS register index and for a
specified number of I3C devices. This becomes a problem when the bus is
fully occupied by I2C and I3C devices before suspend.
To address this, I2C and I3C devices are grouped, if resuming, in the
i3c->addrs[] array such that I2C devices occupy the first slots and I3C
devices occupy the remaining ones. In this scenario, I3C devices are
re-attached using i3c_master_reattach_i3c_dev_locked(), since the bus is
fully occupied and renesas_i3c_get_free_pos() would otherwise return a
negative error code.
Fixes: e7218986319b ("i3c: renesas: Add suspend/resume support")
Cc: stable@vger.kernel.org
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- used directly i3c_dev instead of i3c_dev->dev->desc
- fixed the swap in renesas_i3c_group_devs_in_slots() for i3c->addr[]
Changes in v3:
- added renesas_i3c_group_devs_in_slots(); along with it, the
struct renesas_i3c_addr was updated with i3c_dev and i3c_dev
and the attach/detach/re-attach APIs were adjusted accordingly
- dropped DATBASn member of struct renesas_i3c
- used i3c_master_reattach_i3c_dev_locked() to re-attach devices
on a fully occupied bus
- in resume, moved i2c_mark_adapter_resumed() after i3c_master_do_daa_ext()
since it can update the internal driver data structure i2c specific
Changes in v2:
- adjusted the code to still work in case the full bus was occupied before
a suspend/resume cycle; for that:
-- introduced struct renesas_i3c_addr
-- preserved i3c->DATBASn[] which is saved in suspend and used in resume,
in renesas_i3c_daa()
- updated the patch description to reflect the new updates
drivers/i3c/master/renesas-i3c.c | 209 ++++++++++++++++++++++++-------
1 file changed, 166 insertions(+), 43 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 67b9a31300d0..a63edddbc9fe 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -252,6 +252,15 @@ struct renesas_i3c_xferqueue {
spinlock_t lock;
};
+struct renesas_i3c_addr {
+ union {
+ struct i2c_dev_desc *i2c_dev;
+ struct i3c_dev_desc *i3c_dev;
+ };
+ bool is_i2c;
+ u8 addr;
+};
+
struct renesas_i3c {
struct i3c_master_controller base;
enum i3c_internal_state internal_state;
@@ -262,13 +271,13 @@ struct renesas_i3c {
u32 i3c_STDBR;
u32 extbr;
unsigned long rate;
- u8 addrs[RENESAS_I3C_MAX_DEVS];
+ struct renesas_i3c_addr addrs[RENESAS_I3C_MAX_DEVS];
struct renesas_i3c_xferqueue xferqueue;
void __iomem *regs;
- u32 *DATBASn;
struct clk_bulk_data *clks;
struct reset_control *presetn;
struct reset_control *tresetn;
+ bool resuming;
u8 num_clks;
u8 refclk_div;
};
@@ -335,7 +344,7 @@ static int renesas_i3c_get_addr_pos(struct renesas_i3c *i3c, u8 addr)
int pos;
for (pos = 0; pos < i3c->maxdevs; pos++) {
- if (addr == i3c->addrs[pos])
+ if (addr == i3c->addrs[pos].addr)
return pos;
}
@@ -480,8 +489,8 @@ static int renesas_i3c_reset(struct renesas_i3c *i3c)
renesas_writel(i3c->regs, BCTL, 0);
renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST);
- return read_poll_timeout_atomic(renesas_readl, val, !(val & RSTCTL_RI3CRST),
- 0, 1000, false, i3c->regs, RSTCTL);
+ return read_poll_timeout(renesas_readl, val, !(val & RSTCTL_RI3CRST),
+ 0, 1000, false, i3c->regs, RSTCTL);
}
static void renesas_i3c_hw_init(struct renesas_i3c *i3c)
@@ -637,12 +646,80 @@ static void renesas_i3c_bus_cleanup(struct i3c_master_controller *m)
renesas_i3c_reset(i3c);
}
+static u8 renesas_i3c_group_devs_in_slots(struct renesas_i3c *i3c)
+{
+ struct renesas_i3c_i2c_dev_data *new_i2c_data, *new_i3c_data;
+ struct renesas_i3c_addr i2c_addr, i3c_addr;
+ struct i3c_dev_desc *i3c_dev;
+ struct i2c_dev_desc *i2c_dev;
+ u8 i2c_pos = 0;
+
+ /*
+ * The controller cannot handle interleaved I2C and I3C devices in the
+ * slots. It expects to be configured with a starting DATBAS register
+ * index and the number of I3C devices for which to run dynamic address
+ * assignment.
+ *
+ * Since I2C devices may be added after bus initialization, group the
+ * devices in slots so that the I2C devices occupy the first slots
+ * and I3C devices the remaining ones.
+ *
+ * This allows the controller to run ENTDAA by specifying the starting
+ * DATBAS register index and the number of I3C devices. As a result,
+ * ENTDAA can be re-run for all I3C devices and they can be re-attached
+ * after a suspend/resume cycle in which power to both the controller
+ * and the I3C devices was lost.
+ */
+ for (u8 pos = 0; pos < i3c->maxdevs; pos++) {
+ if (i3c->free_pos & BIT(pos))
+ continue;
+
+ if (!i3c->addrs[pos].is_i2c)
+ continue;
+
+ if (pos == i2c_pos) {
+ i2c_pos++;
+ continue;
+ }
+
+ /*
+ * Swap the I3C device on i2c_pos slot with the I2C dev
+ * on pos slot.
+ */
+ i3c_addr = i3c->addrs[i2c_pos];
+ i3c_dev = i3c_addr.i3c_dev;
+
+ i2c_addr = i3c->addrs[pos];
+ i2c_dev = i2c_addr.i2c_dev;
+
+ new_i2c_data = i3c_dev_get_master_data(i3c_dev);
+ new_i3c_data = i2c_dev_get_master_data(i2c_dev);
+
+ i3c->addrs[i2c_pos] = i2c_addr;
+ i3c->addrs[i2c_pos].is_i2c = true;
+ i3c->addrs[i2c_pos].i2c_dev = i2c_dev;
+
+ i3c->addrs[pos] = i3c_addr;
+ i3c->addrs[pos].is_i2c = false;
+ i3c->addrs[pos].i3c_dev = i3c_dev;
+
+ new_i2c_data->index = i2c_pos++;
+ new_i3c_data->index = pos;
+
+ i2c_dev_set_master_data(i2c_dev, new_i2c_data);
+ i3c_dev_set_master_data(i3c_dev, new_i3c_data);
+ }
+
+ return i2c_pos;
+}
+
static int renesas_i3c_daa(struct i3c_master_controller *m)
{
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_cmd *cmd;
u32 olddevs, newdevs;
u8 last_addr = 0, pos;
+ u8 first_i3c_pos = 0;
int ret;
struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
@@ -655,16 +732,25 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
olddevs = ~(i3c->free_pos);
i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_ENTDAA;
+ if (i3c->resuming)
+ first_i3c_pos = renesas_i3c_group_devs_in_slots(i3c);
+
/* Setting DATBASn registers for target devices. */
for (pos = 0; pos < i3c->maxdevs; pos++) {
- if (olddevs & BIT(pos))
- continue;
+ if (olddevs & BIT(pos)) {
+ if (i3c->resuming) {
+ if (pos < first_i3c_pos)
+ continue;
+ } else {
+ continue;
+ }
+ }
ret = i3c_master_get_free_addr(m, last_addr + 1);
if (ret < 0)
return -ENOSPC;
- i3c->addrs[pos] = ret;
+ i3c->addrs[pos].addr = ret;
last_addr = ret;
renesas_writel(i3c->regs, DATBAS(pos), datbas_dvdyad_with_parity(ret));
@@ -674,9 +760,14 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
cmd = xfer->cmds;
cmd->rx_count = 0;
- ret = renesas_i3c_get_free_pos(i3c);
- if (ret < 0)
- return ret;
+ if (i3c->resuming) {
+ /* Run ENTDAA for all the I3C devices on the bus, if resuming. */
+ ret = first_i3c_pos;
+ } else {
+ ret = renesas_i3c_get_free_pos(i3c);
+ if (ret < 0)
+ return ret;
+ }
/*
* Setup the command descriptor to start the ENTDAA command
@@ -689,12 +780,38 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
renesas_i3c_wait_xfer(i3c, xfer);
- newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0);
- newdevs &= ~olddevs;
+ newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, first_i3c_pos);
+ /* Re-attach all the I3C devices on resume. */
+ if (!i3c->resuming)
+ newdevs &= ~olddevs;
for (pos = 0; pos < i3c->maxdevs; pos++) {
- if (newdevs & BIT(pos))
- i3c_master_add_i3c_dev_locked(m, i3c->addrs[pos]);
+ if (!(newdevs & BIT(pos)))
+ continue;
+
+ /*
+ * If the bus was fully occupied before suspend, re-attach the
+ * devices and update the addresses tracked by the subsystem.
+ * Without this, i3c_master_add_i3c_dev_locked() returns errors
+ * due to a lack of free slots (as reported by
+ * renesas_i3c_get_free_pos()), causing the attachment to fail.
+ *
+ * Otherwise, follow the normal
+ * i3c_master_add_i3c_dev_locked() path, which will re-attach the
+ * devices, keep the subsystem state and driver addresses in sync,
+ * and perform all the required internal bookkeeping.
+ */
+ if (!i3c->free_pos && i3c->resuming) {
+ struct i3c_dev_desc *dev = i3c->addrs[pos].i3c_dev;
+ u8 old_dyn_addr;
+
+ old_dyn_addr = dev->info.dyn_addr;
+ dev->info.dyn_addr = i3c->addrs[pos].addr;
+
+ i3c_master_reattach_i3c_dev_locked(dev, old_dyn_addr);
+ } else {
+ i3c_master_add_i3c_dev_locked(m, i3c->addrs[pos].addr);
+ }
}
return 0;
@@ -876,11 +993,12 @@ static int renesas_i3c_attach_i3c_dev(struct i3c_dev_desc *dev)
return -ENOMEM;
data->index = pos;
- i3c->addrs[pos] = dev->info.dyn_addr ? : dev->info.static_addr;
+ i3c->addrs[pos].addr = dev->info.dyn_addr ? : dev->info.static_addr;
+ i3c->addrs[pos].i3c_dev = dev;
i3c->free_pos &= ~BIT(pos);
renesas_writel(i3c->regs, DATBAS(pos), DATBAS_DVSTAD(dev->info.static_addr) |
- datbas_dvdyad_with_parity(i3c->addrs[pos]));
+ datbas_dvdyad_with_parity(i3c->addrs[pos].addr));
i3c_dev_set_master_data(dev, data);
return 0;
@@ -892,25 +1010,28 @@ static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev,
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ struct i3c_dev_desc *tmp_dev = i3c->addrs[data->index].i3c_dev;
int pos;
pos = renesas_i3c_get_free_pos(i3c);
if (data->index > pos && pos >= 0) {
renesas_writel(i3c->regs, DATBAS(data->index), 0);
- i3c->addrs[data->index] = 0;
+ i3c->addrs[data->index].addr = 0;
+ i3c->addrs[data->index].i3c_dev = NULL;
i3c->free_pos |= BIT(data->index);
data->index = pos;
i3c->free_pos &= ~BIT(data->index);
}
- i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
+ i3c->addrs[data->index].addr = dev->info.dyn_addr ? dev->info.dyn_addr :
dev->info.static_addr;
+ i3c->addrs[data->index].i3c_dev = tmp_dev;
renesas_writel(i3c->regs, DATBAS(data->index),
DATBAS_DVSTAD(dev->info.static_addr) |
- datbas_dvdyad_with_parity(i3c->addrs[data->index]));
+ datbas_dvdyad_with_parity(i3c->addrs[data->index].addr));
return 0;
}
@@ -922,7 +1043,8 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev)
struct renesas_i3c *i3c = to_renesas_i3c(m);
i3c_dev_set_master_data(dev, NULL);
- i3c->addrs[data->index] = 0;
+ i3c->addrs[data->index].addr = 0;
+ i3c->addrs[data->index].i3c_dev = NULL;
i3c->free_pos |= BIT(data->index);
kfree(data);
}
@@ -1002,7 +1124,9 @@ static int renesas_i3c_attach_i2c_dev(struct i2c_dev_desc *dev)
return -ENOMEM;
data->index = pos;
- i3c->addrs[pos] = dev->addr;
+ i3c->addrs[pos].addr = dev->addr;
+ i3c->addrs[pos].is_i2c = true;
+ i3c->addrs[pos].i2c_dev = dev;
i3c->free_pos &= ~BIT(pos);
i2c_dev_set_master_data(dev, data);
@@ -1016,7 +1140,9 @@ static void renesas_i3c_detach_i2c_dev(struct i2c_dev_desc *dev)
struct renesas_i3c *i3c = to_renesas_i3c(m);
i2c_dev_set_master_data(dev, NULL);
- i3c->addrs[data->index] = 0;
+ i3c->addrs[data->index].addr = 0;
+ i3c->addrs[data->index].is_i2c = false;
+ i3c->addrs[data->index].i2c_dev = NULL;
i3c->free_pos |= BIT(data->index);
kfree(data);
}
@@ -1421,12 +1547,6 @@ static int renesas_i3c_probe(struct platform_device *pdev)
i3c->maxdevs = RENESAS_I3C_MAX_DEVS;
i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0);
- /* Allocate dynamic Device Address Table backup. */
- i3c->DATBASn = devm_kzalloc(&pdev->dev, sizeof(u32) * i3c->maxdevs,
- GFP_KERNEL);
- if (!i3c->DATBASn)
- return -ENOMEM;
-
return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false);
}
@@ -1437,17 +1557,13 @@ static void renesas_i3c_remove(struct platform_device *pdev)
i3c_master_unregister(&i3c->base);
}
-static int renesas_i3c_suspend_noirq(struct device *dev)
+static int renesas_i3c_suspend(struct device *dev)
{
struct renesas_i3c *i3c = dev_get_drvdata(dev);
- int i, ret;
+ int ret;
i2c_mark_adapter_suspended(&i3c->base.i2c);
- /* Store Device Address Table values. */
- for (i = 0; i < i3c->maxdevs; i++)
- i3c->DATBASn[i] = renesas_readl(i3c->regs, DATBAS(i));
-
ret = reset_control_assert(i3c->presetn);
if (ret)
goto err_mark_resumed;
@@ -1468,10 +1584,10 @@ static int renesas_i3c_suspend_noirq(struct device *dev)
return ret;
}
-static int renesas_i3c_resume_noirq(struct device *dev)
+static int renesas_i3c_resume(struct device *dev)
{
struct renesas_i3c *i3c = dev_get_drvdata(dev);
- int i, ret;
+ int ret;
ret = reset_control_deassert(i3c->tresetn);
if (ret)
@@ -1497,15 +1613,23 @@ static int renesas_i3c_resume_noirq(struct device *dev)
renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV |
MSDVAD_MDYAD(i3c->dyn_addr));
- /* Restore Device Address Table values. */
- for (i = 0; i < i3c->maxdevs; i++)
- renesas_writel(i3c->regs, DATBAS(i), i3c->DATBASn[i]);
-
/* I3C hw init. */
renesas_i3c_hw_init(i3c);
+ i3c->resuming = true;
+
+ ret = i3c_master_do_daa_ext(&i3c->base, true);
+ if (ret)
+ dev_err(dev, "DAA failed on resume, ret=%d", ret);
+
+ i3c->resuming = false;
+
i2c_mark_adapter_resumed(&i3c->base.i2c);
+ /*
+ * I3C devices may have retained their dynamic address anyway. Do not
+ * fail the resume because of DAA error.
+ */
return 0;
err_clks_disable:
@@ -1518,8 +1642,7 @@ static int renesas_i3c_resume_noirq(struct device *dev)
}
static const struct dev_pm_ops renesas_i3c_pm_ops = {
- NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq,
- renesas_i3c_resume_noirq)
+ SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend, renesas_i3c_resume)
};
static const struct of_device_id renesas_i3c_of_ids[] = {
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 06/16] i3c: renesas: Perform Dynamic Address Assignment on resume
2026-06-12 16:04 ` [PATCH v4 06/16] i3c: renesas: Perform Dynamic Address Assignment " Claudiu Beznea
@ 2026-06-12 16:19 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:19 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: linux-i3c, Frank.Li
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] `renesas_i3c_group_devs_in_slots()` crashes due to NULL pointer dereference when swapping an I2C device into a previously empty slot.
- [High] Slot state corruption and hardware configuration overwrite during device grouping.
- [High] Build failure and state corruption due to calling undefined function `i3c_master_reattach_i3c_dev_locked()` and bypassing PID matching.
--
commit e2b35e6a6eb1e4e88dd575a2f0b9fe6f7bf5f7c5
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Perform Dynamic Address Assignment on resume
The Renesas RZ/G3S SoC supports a power saving mode where power to most
SoC components, including I3C, is turned off. Running DAA in the controller
resume path restores communication for devices that also lose power.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index 67b9a31300d09..a63edddbc9fe0 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
[ ... ]
> @@ -637,12 +646,80 @@ static void renesas_i3c_bus_cleanup(struct i3c_master_controller *m)
> renesas_i3c_reset(i3c);
> }
>
> +static u8 renesas_i3c_group_devs_in_slots(struct renesas_i3c *i3c)
> +{
> + struct renesas_i3c_i2c_dev_data *new_i2c_data, *new_i3c_data;
> + struct renesas_i3c_addr i2c_addr, i3c_addr;
> + struct i3c_dev_desc *i3c_dev;
> + struct i2c_dev_desc *i2c_dev;
> + u8 i2c_pos = 0;
[ ... ]
> + for (u8 pos = 0; pos < i3c->maxdevs; pos++) {
> + if (i3c->free_pos & BIT(pos))
> + continue;
> +
> + if (!i3c->addrs[pos].is_i2c)
> + continue;
> +
> + if (pos == i2c_pos) {
> + i2c_pos++;
> + continue;
> + }
> +
> + /*
> + * Swap the I3C device on i2c_pos slot with the I2C dev
> + * on pos slot.
> + */
> + i3c_addr = i3c->addrs[i2c_pos];
> + i3c_dev = i3c_addr.i3c_dev;
> +
> + i2c_addr = i3c->addrs[pos];
> + i2c_dev = i2c_addr.i2c_dev;
> +
> + new_i2c_data = i3c_dev_get_master_data(i3c_dev);
[Severity: High]
Does this crash when swapping an I2C device into a previously empty slot?
When i2c_pos does not advance over empty slots, i3c_dev will be NULL if
the destination slot is empty. Since i3c_dev_get_master_data() unconditionally
dereferences the pointer (dev->common.master_priv), it seems this would cause
a kernel panic when swapping into an empty slot.
> + new_i3c_data = i2c_dev_get_master_data(i2c_dev);
> +
> + i3c->addrs[i2c_pos] = i2c_addr;
> + i3c->addrs[i2c_pos].is_i2c = true;
> + i3c->addrs[i2c_pos].i2c_dev = i2c_dev;
> +
> + i3c->addrs[pos] = i3c_addr;
> + i3c->addrs[pos].is_i2c = false;
> + i3c->addrs[pos].i3c_dev = i3c_dev;
> +
> + new_i2c_data->index = i2c_pos++;
> + new_i3c_data->index = pos;
> +
> + i2c_dev_set_master_data(i2c_dev, new_i2c_data);
> + i3c_dev_set_master_data(i3c_dev, new_i3c_data);
> + }
[ ... ]
> @@ -655,16 +732,25 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
> olddevs = ~(i3c->free_pos);
> i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_ENTDAA;
>
> + if (i3c->resuming)
> + first_i3c_pos = renesas_i3c_group_devs_in_slots(i3c);
> +
> /* Setting DATBASn registers for target devices. */
> for (pos = 0; pos < i3c->maxdevs; pos++) {
> - if (olddevs & BIT(pos))
> - continue;
> + if (olddevs & BIT(pos)) {
[Severity: High]
Does this clobber the configuration of I2C devices swapped into empty slots?
Since renesas_i3c_group_devs_in_slots() does not update i3c->free_pos during
a swap, the olddevs mask calculated before the grouping is now stale.
If an I2C device was moved to a previously empty slot, olddevs & BIT(pos) will
evaluate to false. This bypasses the skip logic, treats the newly moved I2C
device as an unassigned slot, and generates a new dynamic address for it,
blindly overwriting its DATBAS hardware configuration.
[ ... ]
> @@ -689,12 +780,38 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
>
> renesas_i3c_wait_xfer(i3c, xfer);
>
> - newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0);
> - newdevs &= ~olddevs;
> + newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, first_i3c_pos);
> + /* Re-attach all the I3C devices on resume. */
> + if (!i3c->resuming)
> + newdevs &= ~olddevs;
>
> for (pos = 0; pos < i3c->maxdevs; pos++) {
> - if (newdevs & BIT(pos))
> - i3c_master_add_i3c_dev_locked(m, i3c->addrs[pos]);
> + if (!(newdevs & BIT(pos)))
> + continue;
> +
> + /*
> + * If the bus was fully occupied before suspend, re-attach the
> + * devices and update the addresses tracked by the subsystem.
> + * Without this, i3c_master_add_i3c_dev_locked() returns errors
> + * due to a lack of free slots (as reported by
> + * renesas_i3c_get_free_pos()), causing the attachment to fail.
> + *
> + * Otherwise, follow the normal
> + * i3c_master_add_i3c_dev_locked() path, which will re-attach the
> + * devices, keep the subsystem state and driver addresses in sync,
> + * and perform all the required internal bookkeeping.
> + */
> + if (!i3c->free_pos && i3c->resuming) {
> + struct i3c_dev_desc *dev = i3c->addrs[pos].i3c_dev;
> + u8 old_dyn_addr;
> +
> + old_dyn_addr = dev->info.dyn_addr;
> + dev->info.dyn_addr = i3c->addrs[pos].addr;
> +
> + i3c_master_reattach_i3c_dev_locked(dev, old_dyn_addr);
[Severity: High]
This calls i3c_master_reattach_i3c_dev_locked() which does not appear to exist
in the kernel, causing a build failure.
Also, does this logic cross-wire physical devices with the wrong software
structures?
It blindly assigns newly generated dynamic addresses to the pre-suspend
device structures based solely on their slot index (pos). However, ENTDAA
assigns addresses based on Provisioned ID (PID) arbitration order, not the
previous slot indices. If the bus topology changes or devices arbitrate in a
different order, commands might be sent to the incorrect targets, potentially
causing data corruption.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=6
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 07/16] i3c: renesas: Clean DATBAS register on detach
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (5 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 06/16] i3c: renesas: Perform Dynamic Address Assignment " Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:20 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 08/16] i3c: renesas: Use reset_control_bulk_{assert, deassert}() Claudiu Beznea
` (9 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
The controller uses DATBAS registers on TX/RX logic. Clean the DATBAS
register for the detached I3C device to avoid issues.
Fixes: d028219a9f14 ("i3c: master: Add basic driver for the Renesas I3C controller")
Cc: stable@vger.kernel.org
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index a63edddbc9fe..abe24e563d21 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -1042,6 +1042,8 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev)
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct renesas_i3c *i3c = to_renesas_i3c(m);
+ renesas_writel(i3c->regs, DATBAS(data->index), 0);
+
i3c_dev_set_master_data(dev, NULL);
i3c->addrs[data->index].addr = 0;
i3c->addrs[data->index].i3c_dev = NULL;
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 07/16] i3c: renesas: Clean DATBAS register on detach
2026-06-12 16:04 ` [PATCH v4 07/16] i3c: renesas: Clean DATBAS register on detach Claudiu Beznea
@ 2026-06-12 16:20 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:20 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: Frank.Li, linux-i3c
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Critical] `renesas_i3c_group_devs_in_slots` assumes the target swap slot is occupied by an I3C device, which crashes if the slot is free. It also fails to update the free slot bitmask.
Pre-existing issues:
- [High] The DATBAS registers and device address trackers for unused slots are left dirty after Dynamic Address Assignment (DAA), which can cause hardware TX/RX logic issues.
--
commit bcdea49e3d9ced6f5b774a208065f0889dbb7c12
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Clean DATBAS register on detach
The controller uses DATBAS registers on TX/RX logic. Clean the DATBAS
register for the detached I3C device to avoid issues.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index a63edddbc9fe0..abe24e563d217 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
> @@ -1042,6 +1042,8 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev)
> struct i3c_master_controller *m = i3c_dev_get_master(dev);
> struct renesas_i3c *i3c = to_renesas_i3c(m);
>
> + renesas_writel(i3c->regs, DATBAS(data->index), 0);
> +
> i3c_dev_set_master_data(dev, NULL);
> i3c->addrs[data->index].addr = 0;
> i3c->addrs[data->index].i3c_dev = NULL;
[Severity: Critical]
Since we are allowing I3C devices to be detached and free their slots here,
will this cause a null pointer dereference in
renesas_i3c_group_devs_in_slots() during resume?
If a slot at i2c_pos was previously detached, i3c_addr.i3c_dev can be
completely free. In this scenario, it seems i3c_dev is null and we
dereference it:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_group_devs_in_slots() {
...
/*
* Swap the I3C device on i2c_pos slot with the I2C dev
* on pos slot.
*/
i3c_addr = i3c->addrs[i2c_pos];
i3c_dev = i3c_addr.i3c_dev;
i2c_addr = i3c->addrs[pos];
i2c_dev = i2c_addr.i2c_dev;
new_i2c_data = i3c_dev_get_master_data(i3c_dev);
...
}
It also looks like the function moves the I2C device to i2c_pos but fails
to update the i3c->free_pos bitmask, causing state corruption where the
controller considers the new I2C slot as free. Does this bitmask need to
be updated when swapping devices?
[Severity: High]
This isn't a bug introduced by this patch, but since we are cleaning the
DATBAS registers on detach to avoid tx/rx logic issues, do we also need to
clean them after Dynamic Address Assignment for unassigned slots?
In renesas_i3c_daa(), all free DATBAS registers are pre-configured before
the ENTDAA command:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_daa() {
...
ret = i3c_master_get_free_addr(m, last_addr + 1);
if (ret < 0)
return -ENOSPC;
i3c->addrs[pos].addr = ret;
last_addr = ret;
renesas_writel(i3c->regs, DATBAS(pos),
datbas_dvdyad_with_parity(ret));
}
...
}
After the ENTDAA command completes, it uses the remaining count
(cmd->rx_count) to determine how many devices were actually assigned.
However, it appears the driver never clears DATBAS(pos) and
i3c->addrs[pos].addr for the remaining unassigned slots.
Could leaving invalid dynamic addresses in the DATBAS registers cause the
controller's tx/rx logic to misbehave?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=7
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 08/16] i3c: renesas: Use reset_control_bulk_{assert, deassert}()
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (6 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 07/16] i3c: renesas: Clean DATBAS register on detach Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:04 ` [PATCH v4 09/16] i3c: renesas: Return immediately if there is no transfer Claudiu Beznea
` (8 subsequent siblings)
16 siblings, 0 replies; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Use reset_control_bulk_assert() and reset_control_bulk_deassert() in the
suspend and resume paths to simplify the code.
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- none
drivers/i3c/master/renesas-i3c.c | 30 +++++++++++++-----------------
1 file changed, 13 insertions(+), 17 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index abe24e563d21..9edd2dc96107 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -1562,24 +1562,22 @@ static void renesas_i3c_remove(struct platform_device *pdev)
static int renesas_i3c_suspend(struct device *dev)
{
struct renesas_i3c *i3c = dev_get_drvdata(dev);
+ struct reset_control_bulk_data resets[] = {
+ { .rstc = i3c->presetn },
+ { .rstc = i3c->tresetn },
+ };
int ret;
i2c_mark_adapter_suspended(&i3c->base.i2c);
- ret = reset_control_assert(i3c->presetn);
+ ret = reset_control_bulk_assert(ARRAY_SIZE(resets), resets);
if (ret)
goto err_mark_resumed;
- ret = reset_control_assert(i3c->tresetn);
- if (ret)
- goto err_presetn;
-
clk_bulk_disable(i3c->num_clks, i3c->clks);
return 0;
-err_presetn:
- reset_control_deassert(i3c->presetn);
err_mark_resumed:
i2c_mark_adapter_resumed(&i3c->base.i2c);
@@ -1589,19 +1587,19 @@ static int renesas_i3c_suspend(struct device *dev)
static int renesas_i3c_resume(struct device *dev)
{
struct renesas_i3c *i3c = dev_get_drvdata(dev);
+ struct reset_control_bulk_data resets[] = {
+ { .rstc = i3c->presetn },
+ { .rstc = i3c->tresetn },
+ };
int ret;
- ret = reset_control_deassert(i3c->tresetn);
+ ret = reset_control_bulk_deassert(ARRAY_SIZE(resets), resets);
if (ret)
return ret;
- ret = reset_control_deassert(i3c->presetn);
- if (ret)
- goto err_tresetn;
-
ret = clk_bulk_enable(i3c->num_clks, i3c->clks);
if (ret)
- goto err_presetn;
+ goto err_resets_asserted;
ret = renesas_i3c_reset(i3c);
if (ret)
@@ -1636,10 +1634,8 @@ static int renesas_i3c_resume(struct device *dev)
err_clks_disable:
clk_bulk_disable(i3c->num_clks, i3c->clks);
-err_presetn:
- reset_control_assert(i3c->presetn);
-err_tresetn:
- reset_control_assert(i3c->tresetn);
+err_resets_asserted:
+ reset_control_bulk_assert(ARRAY_SIZE(resets), resets);
return ret;
}
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 09/16] i3c: renesas: Return immediately if there is no transfer
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (7 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 08/16] i3c: renesas: Use reset_control_bulk_{assert, deassert}() Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:23 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 10/16] i3c: renesas: Follow a unified pattern for transfer and command initialization Claudiu Beznea
` (7 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
There is no need to allocate a transfer structure when i2c_nxfers is zero.
Return immediately instead of unnecessarily allocating memory.
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- updated patch title
drivers/i3c/master/renesas-i3c.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 9edd2dc96107..fa9c15381ed6 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -1061,13 +1061,13 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
u8 start_bit = CNDCTL_STCND;
int i;
+ if (!i2c_nxfers)
+ return 0;
+
struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
if (!xfer)
return -ENOMEM;
- if (!i2c_nxfers)
- return 0;
-
renesas_i3c_bus_enable(m, false);
init_completion(&xfer->comp);
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 09/16] i3c: renesas: Return immediately if there is no transfer
2026-06-12 16:04 ` [PATCH v4 09/16] i3c: renesas: Return immediately if there is no transfer Claudiu Beznea
@ 2026-06-12 16:23 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:23 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: linux-i3c, Frank.Li
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Hardware Desynchronization and Memory Corruption on timeout
- [Critical] Missing Bounds Check causing Kernel Heap Buffer Overflow in Receive ISRs
- [High] Unchecked timeout leading to hardware desynchronization and incorrect success reporting in `renesas_i3c_i2c_xfers`
- [High] Missing Error Handling in `renesas_i3c_i3c_xfers` unconditionally returns success
- [High] Out-of-Bounds Read and Endianness violation on short TX transfers
--
commit e7c9bbe000eba8d228d206b4d05e155779672539
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Return immediately if there is no transfer
There is no need to allocate a transfer structure when i2c_nxfers is zero.
Return immediately instead of unnecessarily allocating memory.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index 9edd2dc961073..fa9c15381ed6a 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
> @@ -1061,13 +1061,13 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
> u8 start_bit = CNDCTL_STCND;
> int i;
>
> + if (!i2c_nxfers)
> + return 0;
> +
> struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
> if (!xfer)
> return -ENOMEM;
>
> - if (!i2c_nxfers)
> - return 0;
> -
> renesas_i3c_bus_enable(m, false);
>
> init_completion(&xfer->comp);
[Severity: High]
This is a pre-existing issue, but I noticed a potential problem with
unchecked timeouts later in this same function:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_i2c_xfers() {
...
wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
if (cmd->err)
break;
...
If wait_for_completion_timeout() times out, time_left will be 0 but cmd->err
remains 0 (its initialization value, since the ISR did not run to update it).
The check evaluates to false, causing the driver to continue the loop and issue
the next I2C message while the hardware may still be executing the previous one.
Finally, it unconditionally returns cmd->err (0), falsely reporting success to
the I2C subsystem on timeout. Should this explicitly check the timeout return
value?
[Severity: High]
This isn't a bug introduced by this patch, but renesas_i3c_i3c_xfers() appears
to unconditionally return success even on error:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_i3c_xfers() {
...
renesas_i3c_wait_xfer(i3c, xfer);
}
return 0;
}
The function iterates through I3C transfers but ignores the error status
(xfer->ret) populated by the ISR on hardware failures (e.g., NACK, CRC errors).
Even if a timeout occurs, the function unconditionally returns 0 at the end,
masking all I3C transfer failures from the subsystem. Could it return xfer->ret
or an appropriate error code instead?
[Severity: Critical]
This is a pre-existing issue, but I noticed a potential problem with hardware
desynchronization and memory corruption on timeout.
If a transfer timeout occurs, renesas_i3c_wait_xfer() removes the transfer from
the software queue, but the driver fails to abort the hardware operation. In
the ISR:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_resp_isr() {
...
xfer = i3c->xferqueue.cur;
if (!xfer) {
/* Disable interrupts. */
renesas_clear_bit(i3c->regs, NTIE, NTIE_TDBEIE0 | NTIE_RDBFIE0);
/* Clear any error flags. */
renesas_clear_bit(i3c->regs, BCTL, BCTL_ABT);
...
The driver clears the BCTL_ABT bit instead of setting it. When a subsequent
transfer starts, interrupts are re-enabled and the hardware may fire a delayed
completion interrupt for the old transfer. The ISR will attribute this interrupt
to the newly enqueued transfer (i3c->xferqueue.cur), potentially causing data
corruption and out-of-bounds writes into the new transfer's buffer. Does
BCTL_ABT need to be set instead to properly abort?
[Severity: Critical]
This isn't a bug introduced by this patch, but there appears to be a missing
bounds check causing a kernel heap buffer overflow in the receive ISRs.
In both renesas_i3c_resp_isr() and renesas_i3c_rx_isr(), the length of incoming
data is calculated directly from hardware-provided registers without being
validated against the allocated buffer size (cmd->len).
For example, in renesas_i3c_resp_isr():
drivers/i3c/master/renesas-i3c.c:renesas_i3c_resp_isr() {
...
if (NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) && !cmd->err)
bytes_remaining = data_len - cmd->rx_count;
i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, bytes_remaining);
...
And in renesas_i3c_rx_isr():
drivers/i3c/master/renesas-i3c.c:renesas_i3c_rx_isr() {
...
read_bytes = NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) * sizeof(u32);
i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, read_bytes);
...
A malfunctioning or malicious target device sending an oversized response could
cause i3c_readl_fifo() to write past the bounds of cmd->rx_buf. Can this be
safeguarded by checking against cmd->len?
[Severity: High]
This is a pre-existing issue, but I noticed a potential out-of-bounds read and
endianness violation on short TX transfers.
In renesas_i3c_start_xfer_locked(), immediate transfers of 1 to 3 bytes cast
the tx_buf pointer to a u32 * and dereference it:
drivers/i3c/master/renesas-i3c.c:renesas_i3c_start_xfer_locked() {
...
if (cmd->len <= 4) {
cmd->cmd0 |= NCMDQP_CMD_ATTR(NCMDQP_IMMED_XFER);
cmd->cmd0 |= NCMDQP_BYTE_CNT(cmd->len);
cmd->tx_count = cmd->len;
cmd1 = cmd->len == 0 ? 0 : *(u32 *)cmd->tx_buf;
} else {
...
Since the caller may only provide a 1-byte array on the stack, this reads
out-of-bounds memory, potentially triggering KASAN or causing unaligned memory
access faults. Additionally, it reads the bytes in CPU host byte order, causing
the data sent over the I3C wire to be incorrectly ordered on Big Endian systems.
Would it be better to copy the bytes safely into a temporary u32 and convert to
the correct endianness?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=9
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 10/16] i3c: renesas: Follow a unified pattern for transfer and command initialization
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (8 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 09/16] i3c: renesas: Return immediately if there is no transfer Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:21 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 11/16] i3c: renesas: Drop the explicit memset() call Claudiu Beznea
` (6 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Follow a unified pattern for transfer and command initialization across
the driver. This keeps the code cleaner and easier to follow. Also, in
some cases the I3C device was enabled before the transfer data structure
was even allocated.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index fa9c15381ed6..883be4326e43 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -726,6 +726,10 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
if (!xfer)
return -ENOMEM;
+ init_completion(&xfer->comp);
+ cmd = xfer->cmds;
+ cmd->rx_count = 0;
+
/* Enable I3C bus. */
renesas_i3c_bus_enable(m, true);
@@ -756,10 +760,6 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
renesas_writel(i3c->regs, DATBAS(pos), datbas_dvdyad_with_parity(ret));
}
- init_completion(&xfer->comp);
- cmd = xfer->cmds;
- cmd->rx_count = 0;
-
if (i3c->resuming) {
/* Run ENTDAA for all the I3C devices on the bus, if resuming. */
ret = first_i3c_pos;
@@ -878,13 +878,13 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m,
if (!xfer)
return -ENOMEM;
- renesas_i3c_bus_enable(m, true);
-
init_completion(&xfer->comp);
cmd = xfer->cmds;
cmd->rnw = ccc->rnw;
cmd->cmd0 = 0;
+ renesas_i3c_bus_enable(m, true);
+
/* Calculate the command descriptor. */
switch (ccc->id) {
case I3C_CCC_SETDASA:
@@ -934,15 +934,15 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_
struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
int i;
- /* Enable I3C bus. */
- renesas_i3c_bus_enable(m, true);
-
struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
if (!xfer)
return -ENOMEM;
init_completion(&xfer->comp);
+ /* Enable I3C bus. */
+ renesas_i3c_bus_enable(m, true);
+
for (i = 0; i < i3c_nxfers; i++) {
struct renesas_i3c_cmd *cmd = xfer->cmds;
@@ -1068,12 +1068,12 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
if (!xfer)
return -ENOMEM;
- renesas_i3c_bus_enable(m, false);
-
init_completion(&xfer->comp);
xfer->is_i2c_xfer = true;
cmd = xfer->cmds;
+ renesas_i3c_bus_enable(m, false);
+
if (!(renesas_readl(i3c->regs, BCST) & BCST_BFREF)) {
cmd->err = -EBUSY;
return cmd->err;
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 10/16] i3c: renesas: Follow a unified pattern for transfer and command initialization
2026-06-12 16:04 ` [PATCH v4 10/16] i3c: renesas: Follow a unified pattern for transfer and command initialization Claudiu Beznea
@ 2026-06-12 16:21 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:21 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: linux-i3c, Frank.Li
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
Pre-existing issues:
- [Critical] The `renesas_i3c_i3c_xfers` function silently ignores all transfer errors and timeouts, always returning success.
- [High] The driver does not abort or reset the underlying hardware controller when an I2C or I3C transfer times out. This leaves the hardware active in the background, which will corrupt subsequent transfers.
- [High] In `renesas_i3c_i2c_xfers`, the return value of `wait_for_completion_timeout` is ignored, leading to erroneous state progression and reporting success when transfers actually timed out.
- [High] The driver unconditionally terminates every I3C private transfer with a STOP condition, failing to support atomic transactions that require Repeated Start (Sr).
- [High] I3C write transfers larger than 64 bytes will overflow the hardware FIFO and repeatedly corrupt data.
- [High] I3C read transfers larger than 64 bytes will overwrite the beginning of the receive buffer.
- [High] Dynamic Address Assignment (DAA) ignores transfer failures and timeouts.
--
commit 172b123b52edbe37fd1e66252700d27e875fa822
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Follow a unified pattern for transfer and command initialization
This patch unifies the pattern for transfer and command initialization
across the renesas i3c driver. It ensures the transfer data structure is
allocated, completion initialized, and command properties set before enabling
the I3C bus.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index fa9c15381ed6a..883be4326e430 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
> @@ -726,6 +726,10 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
> if (!xfer)
> return -ENOMEM;
>
> + init_completion(&xfer->comp);
> + cmd = xfer->cmds;
> + cmd->rx_count = 0;
> +
> /* Enable I3C bus. */
> renesas_i3c_bus_enable(m, true);
[Severity: High]
This isn't a bug introduced by this patch, but looking further down in
renesas_i3c_daa():
time_left = renesas_i3c_wait_xfer(i3c, xfer);
if (!time_left)
renesas_i3c_irqs_mask_and_clear(i3c);
newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, first_i3c_pos);
The result of the ENTDAA transfer is unverified before trusting
cmd->rx_count. If the assignment command fails or times out, does the
driver blindly proceed to calculate device masks using a stale or zero
rx_count? Could this lead to incorrect device attachment logic and
state desynchronization?
[ ... ]
> @@ -934,15 +934,15 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_
> struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
> int i;
>
> - /* Enable I3C bus. */
> - renesas_i3c_bus_enable(m, true);
> -
> struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
> if (!xfer)
> return -ENOMEM;
>
> init_completion(&xfer->comp);
>
> + /* Enable I3C bus. */
> + renesas_i3c_bus_enable(m, true);
> +
> for (i = 0; i < i3c_nxfers; i++) {
> struct renesas_i3c_cmd *cmd = xfer->cmds;
[Severity: High]
This isn't a bug introduced by this patch, but looking at the loop in
renesas_i3c_i3c_xfers() where it sets up the command descriptor:
cmd->cmd0 = NCMDQP_DEV_INDEX(data->index) | NCMDQP_MODE(0) |
NCMDQP_RNW(cmd->rnw) | NCMDQP_ROC | NCMDQP_TOC;
Does setting NCMDQP_TOC unconditionally inside the loop break support for
Repeated Start (Sr) sequences? By forcing a STOP condition after each
message, doesn't this prevent the driver from executing atomic
transactions?
[Severity: Critical]
This is also a pre-existing issue, but further down in
renesas_i3c_i3c_xfers(), the function ignores the transfer result:
if (xfer_failed)
renesas_i3c_irqs_mask_and_clear(i3c);
return 0;
Is it intentional to silently ignore all transfer errors and timeouts here?
Because the function always returns 0, upper layers will assume writes
succeeded and might process uninitialized or corrupted read buffers.
[Severity: High]
Another pre-existing issue in this area: does this function properly handle
transfers larger than 64 bytes?
Looking at the write path, the code pushes cmd->len bytes regardless of the
NTDTBP0_DEPTH limit (16 words). Additionally, in renesas_i3c_tx_isr(), the
TX interrupt repeatedly writes the entire cmd->len buffer from the base
address instead of advancing the pointer:
i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len);
Will this reliably overflow the hardware FIFO and repeatedly corrupt data?
[Severity: High]
Similarly for the read path, this is a pre-existing issue, but does the
driver overwrite the beginning of the receive buffer for large reads?
In renesas_i3c_rx_isr() and renesas_i3c_resp_isr(), data is read using:
i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, read_bytes);
cmd->rx_count = read_bytes;
Because cmd->rx_buf is not offset by the bytes already read, and
cmd->rx_count is overwritten instead of accumulated, won't subsequent
interrupt reads write into the very beginning of the user's buffer?
[ ... ]
> @@ -1068,12 +1068,12 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
> if (!xfer)
> return -ENOMEM;
>
> - renesas_i3c_bus_enable(m, false);
> -
> init_completion(&xfer->comp);
> xfer->is_i2c_xfer = true;
> cmd = xfer->cmds;
>
> + renesas_i3c_bus_enable(m, false);
> +
> if (!(renesas_readl(i3c->regs, BCST) & BCST_BFREF)) {
> cmd->err = -EBUSY;
> return cmd->err;
[Severity: High]
This is a pre-existing issue, but looking at the timeout handling in
renesas_i3c_i2c_xfers():
time_left = wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
if (!time_left)
xfer_failed = true;
if (cmd->err)
break;
If the transfer times out, time_left is 0 but cmd->err remains 0. Does this
cause the loop to continue and overwrite the cmd state while the hardware
might still be stuck, eventually returning success (0)?
[Severity: High]
This is another pre-existing issue related to timeouts. When
wait_for_completion_timeout() times out here, the software removes the xfer
object from the queue and masks interrupts in
renesas_i3c_irqs_mask_and_clear():
renesas_writel(i3c->regs, BIE, 0);
renesas_writel(i3c->regs, NTIE, 0);
However, it does not send an abort command or reset the hardware.
If a new transfer is initiated, the hardware continues operation in the
background. When the slow hardware eventually completes the original
timed-out transfer, it triggers an interrupt. Will renesas_i3c_resp_isr()
then fetch the new i3c->xferqueue.cur and blindly apply the old transfer's
completion status and read data into the new transfer's buffers, causing
cross-transfer memory corruption?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=10
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 11/16] i3c: renesas: Drop the explicit memset() call
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (9 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 10/16] i3c: renesas: Follow a unified pattern for transfer and command initialization Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:04 ` [PATCH v4 12/16] i3c: renesas: Update HW registers after SW computations are done Claudiu Beznea
` (5 subsequent siblings)
16 siblings, 0 replies; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Drop the explicit memset() call on struct i3c_device_info object, as it is
already initialized at declaration through compiler initialization.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 883be4326e43..d5247e841a83 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -634,7 +634,6 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
i3c->dyn_addr = ret;
renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
- memset(&info, 0, sizeof(info));
info.dyn_addr = ret;
return i3c_master_set_info(&i3c->base, &info);
}
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 12/16] i3c: renesas: Update HW registers after SW computations are done
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (10 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 11/16] i3c: renesas: Drop the explicit memset() call Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:19 ` sashiko-bot
2026-06-12 16:04 ` [PATCH v4 13/16] i3c: renesas: Organize structures to avoid unnecessary padding Claudiu Beznea
` (4 subsequent siblings)
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
renesas_i3c_bus_init() performs a number of computations and software
cache updates, interleaving them with hardware register writes. While
this works today, it makes it harder to minimize the time the controller
must remain powered when runtime PM is introduced.
Perform all software computations and cache updates first, then update
the hardware registers. This prepares for future runtime PM support.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index d5247e841a83..96d4fed32bf9 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -560,10 +560,6 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
if (!i3c->rate)
return -EINVAL;
- ret = renesas_i3c_reset(i3c);
- if (ret)
- return ret;
-
i2c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i2c);
i3c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i3c);
@@ -614,27 +610,31 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
STDBR_SBRHO(double_SBR, od_high_ticks) |
STDBR_SBRLP(pp_low_ticks) |
STDBR_SBRHP(pp_high_ticks);
- renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
/* Extended Bit Rate setting */
i3c->extbr = EXTBR_EBRLO(od_low_ticks) | EXTBR_EBRHO(od_high_ticks) |
EXTBR_EBRLP(pp_low_ticks) | EXTBR_EBRHP(pp_high_ticks);
- renesas_writel(i3c->regs, EXTBR, i3c->extbr);
-
- renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
- i3c->refclk_div = cks;
-
- /* I3C hw init*/
- renesas_i3c_hw_init(i3c);
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
return ret;
+ info.dyn_addr = ret;
i3c->dyn_addr = ret;
- renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
+ i3c->refclk_div = cks;
+
+ ret = renesas_i3c_reset(i3c);
+ if (ret)
+ return ret;
+
+ renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
+ renesas_writel(i3c->regs, EXTBR, i3c->extbr);
+ renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
+ renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(i3c->dyn_addr) | MSDVAD_MDYADV);
+
+ /* I3C hw init*/
+ renesas_i3c_hw_init(i3c);
- info.dyn_addr = ret;
return i3c_master_set_info(&i3c->base, &info);
}
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 12/16] i3c: renesas: Update HW registers after SW computations are done
2026-06-12 16:04 ` [PATCH v4 12/16] i3c: renesas: Update HW registers after SW computations are done Claudiu Beznea
@ 2026-06-12 16:19 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:19 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: linux-i3c, Frank.Li
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Division by zero crash if `i2c-scl-hz` property is omitted in pure mode.
- [Medium] Potential division by zero crash and precision loss in open-drain timing calculations.
--
commit a5b09dc4623aa6e4464b99ec9c493bf28137c24d
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Update HW registers after SW computations are done
The patch defers hardware register writes until after software calculations
and cache updates are complete in renesas_i3c_bus_init(), aiming to
support future runtime PM enhancements.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> index d5247e841a838..96d4fed32bf95 100644
> --- a/drivers/i3c/master/renesas-i3c.c
> +++ b/drivers/i3c/master/renesas-i3c.c
> @@ -560,10 +560,6 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
> if (!i3c->rate)
> return -EINVAL;
>
> - ret = renesas_i3c_reset(i3c);
> - if (ret)
> - return ret;
> -
> i2c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i2c);
[Severity: High]
This is a pre-existing issue and was not introduced by this patch, but can
this cause a division by zero crash?
If the i2c-scl-hz device tree property is not present, of_populate_i3c_bus()
leaves master->bus.scl_rate.i2c as 0. In pure mode (I3C_BUS_MODE_PURE),
i3c_bus_set_mode() explicitly does not initialize scl_rate.i2c, which
would leave it as 0 here.
[ ... ]
> ret = i3c_master_get_free_addr(m, 0);
> if (ret < 0)
> return ret;
>
> + info.dyn_addr = ret;
> i3c->dyn_addr = ret;
> - renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
> + i3c->refclk_div = cks;
> +
> + ret = renesas_i3c_reset(i3c);
> + if (ret)
> + return ret;
[Severity: Medium]
This is a pre-existing issue and was not introduced by this patch, but does
the timing calculation in this function risk a division by zero or precision
loss?
In renesas_i3c_bus_init(), there are calculations similar to:
od_low_ticks -= t.scl_fall_ns / (NSEC_PER_SEC / i3c->rate) + 1;
If the input clock rate i3c->rate exceeds 1 GHz (NSEC_PER_SEC), the inner
division evaluates to 0, which would cause a divide-by-zero kernel panic.
Even for rates under 1 GHz, this pattern causes severe precision loss for
non-integer multiples of 1 GHz.
Would using a pattern like DIV_ROUND_UP_ULL((u64)ns * rate, NSEC_PER_SEC)
be safer here to prevent both the crash and precision loss?
> + renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
> + renesas_writel(i3c->regs, EXTBR, i3c->extbr);
> + renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
> + renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(i3c->dyn_addr) | MSDVAD_MDYADV);
> +
> + /* I3C hw init*/
> + renesas_i3c_hw_init(i3c);
>
> - info.dyn_addr = ret;
> return i3c_master_set_info(&i3c->base, &info);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=12
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 13/16] i3c: renesas: Organize structures to avoid unnecessary padding
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (11 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 12/16] i3c: renesas: Update HW registers after SW computations are done Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:04 ` [PATCH v4 14/16] i3c: renesas: Use the "dev_name:irq_name" format for the interrupt name Claudiu Beznea
` (3 subsequent siblings)
16 siblings, 0 replies; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Reorder structure members to reduce padding and improve memory layout.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 96d4fed32bf9..ab73fee1813b 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -221,19 +221,19 @@ enum renesas_i3c_event {
};
struct renesas_i3c_cmd {
+ const void *tx_buf;
+ void *rx_buf;
+ /* i2c xfer */
+ u8 *i2c_buf;
+ const struct i2c_msg *msg;
+ int i2c_bytes_left;
+ int i2c_is_last;
u32 cmd0;
u32 len;
- const void *tx_buf;
u32 tx_count;
- void *rx_buf;
u32 rx_count;
u32 err;
u8 rnw;
- /* i2c xfer */
- int i2c_bytes_left;
- int i2c_is_last;
- u8 *i2c_buf;
- const struct i2c_msg *msg;
};
struct renesas_i3c_xfer {
@@ -262,22 +262,22 @@ struct renesas_i3c_addr {
};
struct renesas_i3c {
+ void __iomem *regs;
+ struct clk_bulk_data *clks;
+ struct reset_control *presetn;
+ struct reset_control *tresetn;
+ struct renesas_i3c_xferqueue xferqueue;
struct i3c_master_controller base;
+ struct renesas_i3c_addr addrs[RENESAS_I3C_MAX_DEVS];
+ unsigned long rate;
enum i3c_internal_state internal_state;
- u16 maxdevs;
+ bool resuming;
u32 free_pos;
u32 dyn_addr;
u32 i2c_STDBR;
u32 i3c_STDBR;
u32 extbr;
- unsigned long rate;
- struct renesas_i3c_addr addrs[RENESAS_I3C_MAX_DEVS];
- struct renesas_i3c_xferqueue xferqueue;
- void __iomem *regs;
- struct clk_bulk_data *clks;
- struct reset_control *presetn;
- struct reset_control *tresetn;
- bool resuming;
+ u16 maxdevs;
u8 num_clks;
u8 refclk_div;
};
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 14/16] i3c: renesas: Use the "dev_name:irq_name" format for the interrupt name
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (12 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 13/16] i3c: renesas: Organize structures to avoid unnecessary padding Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:04 ` [PATCH v4 15/16] i3c: renesas: Drop unnecessary tab Claudiu Beznea
` (2 subsequent siblings)
16 siblings, 0 replies; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Use the "dev_name:irq_name" format for the interrupt names. This makes it
easier to identify interrupts in systems where multiple devices may request
interrupts with the same name.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index ab73fee1813b..a5384c0f71e3 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -1533,12 +1533,19 @@ static int renesas_i3c_probe(struct platform_device *pdev)
return ret;
for (i = 0; i < ARRAY_SIZE(renesas_i3c_irqs); i++) {
+ const char *irqname;
+
ret = platform_get_irq_byname(pdev, renesas_i3c_irqs[i].name);
if (ret < 0)
return ret;
+ irqname = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s:%s", dev_name(&pdev->dev),
+ renesas_i3c_irqs[i].desc);
+ if (!irqname)
+ return -ENOMEM;
+
ret = devm_request_irq(&pdev->dev, ret, renesas_i3c_irqs[i].isr,
- 0, renesas_i3c_irqs[i].desc, i3c);
+ 0, irqname, i3c);
if (ret)
return ret;
}
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 15/16] i3c: renesas: Drop unnecessary tab
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (13 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 14/16] i3c: renesas: Use the "dev_name:irq_name" format for the interrupt name Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:04 ` [PATCH v4 16/16] i3c: renesas: Add runtime PM support Claudiu Beznea
2026-06-12 20:17 ` [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Frank Li
16 siblings, 0 replies; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Remove an unnecessary tab to make the code cleaner.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- none
Changes in v3:
- none
Changes in v2:
- collected tags
drivers/i3c/master/renesas-i3c.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index a5384c0f71e3..1bed2304b5ee 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -109,7 +109,7 @@
#define NCMDQP_DATA_LENGTH(x) FIELD_PREP(GENMASK(31, 16), x)
#define NRSPQP 0x154 /* Normal Respone Queue */
-#define NRSPQP_NO_ERROR 0
+#define NRSPQP_NO_ERROR 0
#define NRSPQP_ERROR_CRC 1
#define NRSPQP_ERROR_PARITY 2
#define NRSPQP_ERROR_FRAME 3
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 16/16] i3c: renesas: Add runtime PM support
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (14 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 15/16] i3c: renesas: Drop unnecessary tab Claudiu Beznea
@ 2026-06-12 16:04 ` Claudiu Beznea
2026-06-12 16:34 ` sashiko-bot
2026-06-12 20:17 ` [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Frank Li
16 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2026-06-12 16:04 UTC (permalink / raw)
To: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel
Cc: claudiu.beznea, linux-i3c, linux-kernel, claudiu.beznea,
Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
On the SoCs where the Renesas I3C driver is enabled (RZ/G3S and RZ/G3E),
the clocks of the IP are managed through a clock PM domain. To keep the
I3C code simpler, the explicit clock handling was dropped along with the
addition of runtime PM support, in favor of the runtime PM APIs. Only the
code for getting tclk was preserved, as it is necessary to compute the
I3C clock rate.
All the APIs provided to the I3C subsystem through struct
i3c_master_controller_ops are guarded with runtime PM APIs to
enable/disable the controller at runtime.
As the Renesas I3C driver implements an asynchronous transmit model by
preparing a transfer and waiting for its completion through the ISR,
renesas_i3c_abort_xfer() was added to disable interrupts and clear any
pending IRQ status bits when there is no completion in the defined
timeout. Along with this, renesas_i3c_wait_xfer() return type was changed
to unsigned long.
Add runtime PM support for the Renesas I3C driver.
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v4:
- renamed renesas_i3c_abort_xfer() from v3 to
renesas_i3c_irqs_mask_and_clear(); along with it renamed local varibles
abort_xfer to xfer_failed
Changes in v3:
- dropped the RPM resume/suspend in renesas_i3c_suspend() to read the
DATBASn registers as the DATBASn register are not used on suspend/resume
path anymore in this version
Changes in v2:
- dropped the runtime suspend/resume functions as for now, there will be
no pinctrl sleep state described in DT
- do not synchronize the IRQs in renesas_i3c_abort_xfer() as some handlers
may re-enable interrupts; instead just disable the interrupts and clean
any status bits that the IRQ handlers are touching; with this the
struct renesas_i3c::{irqs, num_irqs} and the associated code was removed
- dropped the renesas_i3c_dont_use_autosuspend() along with the
devm_add_action_or_reset() call to set it since the same operation is
done by the devres cleanup helper of devm_pm_runtime_enable()
- adjusted the renesas_i3c_suspend() with RPM calls to save the DATBAS
registers
- use pm_runtime_resume_and_get() in renesas_i3c_resume() to avoid
mixing gotos with cleanup helpers
- adjusted the patch description to reflect these changes
drivers/i3c/master/renesas-i3c.c | 150 +++++++++++++++++++++++++------
1 file changed, 122 insertions(+), 28 deletions(-)
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 1bed2304b5ee..e0e75144440e 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include "../internals.h"
@@ -199,8 +200,6 @@
#define RENESAS_I3C_MAX_DEVS 8
#define I2C_INIT_MSG -1
-#define RENESAS_I3C_TCLK_IDX 1
-
enum i3c_internal_state {
I3C_INTERNAL_STATE_DISABLED,
I3C_INTERNAL_STATE_CONTROLLER_IDLE,
@@ -263,9 +262,10 @@ struct renesas_i3c_addr {
struct renesas_i3c {
void __iomem *regs;
- struct clk_bulk_data *clks;
+ struct clk *tclk;
struct reset_control *presetn;
struct reset_control *tresetn;
+ struct device *dev;
struct renesas_i3c_xferqueue xferqueue;
struct i3c_master_controller base;
struct renesas_i3c_addr addrs[RENESAS_I3C_MAX_DEVS];
@@ -278,7 +278,6 @@ struct renesas_i3c {
u32 i3c_STDBR;
u32 extbr;
u16 maxdevs;
- u8 num_clks;
u8 refclk_div;
};
@@ -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_irqs_mask_and_clear(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);
+}
+
+static unsigned long renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
{
unsigned long time_left;
@@ -452,6 +468,8 @@ static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xf
time_left = wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000));
if (!time_left)
renesas_i3c_dequeue_xfer(i3c, xfer);
+
+ return time_left;
}
static void renesas_i3c_set_prts(struct renesas_i3c *i3c, u32 val)
@@ -485,6 +503,12 @@ static void renesas_i3c_bus_enable(struct i3c_master_controller *m, bool i3c_mod
static int renesas_i3c_reset(struct renesas_i3c *i3c)
{
u32 val;
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
renesas_writel(i3c->regs, BCTL, 0);
renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST);
@@ -556,7 +580,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
int od_high_ticks, od_low_ticks, i2c_total_ticks;
int ret;
- i3c->rate = clk_get_rate(i3c->clks[RENESAS_I3C_TCLK_IDX].clk);
+ i3c->rate = clk_get_rate(i3c->tclk);
if (!i3c->rate)
return -EINVAL;
@@ -627,6 +651,11 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
if (ret)
return ret;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
renesas_writel(i3c->regs, EXTBR, i3c->extbr);
renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
@@ -716,6 +745,7 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
{
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_cmd *cmd;
+ unsigned long time_left;
u32 olddevs, newdevs;
u8 last_addr = 0, pos;
u8 first_i3c_pos = 0;
@@ -729,6 +759,11 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
cmd = xfer->cmds;
cmd->rx_count = 0;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
/* Enable I3C bus. */
renesas_i3c_bus_enable(m, true);
@@ -777,7 +812,9 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
NCMDQP_CMD(I3C_CCC_ENTDAA) | NCMDQP_DEV_INDEX(ret) |
NCMDQP_DEV_COUNT(i3c->maxdevs - ret) | NCMDQP_TOC;
- renesas_i3c_wait_xfer(i3c, xfer);
+ time_left = renesas_i3c_wait_xfer(i3c, xfer);
+ if (!time_left)
+ renesas_i3c_irqs_mask_and_clear(i3c);
newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, first_i3c_pos);
/* Re-attach all the I3C devices on resume. */
@@ -865,6 +902,7 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m,
{
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_cmd *cmd;
+ unsigned long time_left;
int ret, pos = 0;
if (ccc->id & I3C_CCC_DIRECT) {
@@ -882,6 +920,11 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m,
cmd->rnw = ccc->rnw;
cmd->cmd0 = 0;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
renesas_i3c_bus_enable(m, true);
/* Calculate the command descriptor. */
@@ -916,7 +959,9 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m,
}
}
- renesas_i3c_wait_xfer(i3c, xfer);
+ time_left = renesas_i3c_wait_xfer(i3c, xfer);
+ if (!time_left)
+ renesas_i3c_irqs_mask_and_clear(i3c);
ret = xfer->ret;
if (ret)
@@ -931,7 +976,9 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
- int i;
+ unsigned long time_left;
+ bool xfer_failed = false;
+ int i, ret;
struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
if (!xfer)
@@ -939,6 +986,11 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_
init_completion(&xfer->comp);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
/* Enable I3C bus. */
renesas_i3c_bus_enable(m, true);
@@ -970,9 +1022,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)
+ xfer_failed = true;
}
+ if (xfer_failed)
+ renesas_i3c_irqs_mask_and_clear(i3c);
+
return 0;
}
@@ -981,12 +1038,17 @@ static int renesas_i3c_attach_i3c_dev(struct i3c_dev_desc *dev)
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_i2c_dev_data *data;
- int pos;
+ int pos, ret;
pos = renesas_i3c_get_free_pos(i3c);
if (pos < 0)
return pos;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
data = kzalloc_obj(*data);
if (!data)
return -ENOMEM;
@@ -1010,7 +1072,12 @@ static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev,
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
struct i3c_dev_desc *tmp_dev = i3c->addrs[data->index].i3c_dev;
- int pos;
+ int pos, ret;
+
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
pos = renesas_i3c_get_free_pos(i3c);
@@ -1040,8 +1107,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;
@@ -1058,7 +1129,9 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_cmd *cmd;
u8 start_bit = CNDCTL_STCND;
- int i;
+ unsigned long time_left;
+ bool xfer_failed = false;
+ int i, ret;
if (!i2c_nxfers)
return 0;
@@ -1071,6 +1144,11 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
xfer->is_i2c_xfer = true;
cmd = xfer->cmds;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
renesas_i3c_bus_enable(m, false);
if (!(renesas_readl(i3c->regs, BCST) & BCST_BFREF)) {
@@ -1097,7 +1175,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)
+ xfer_failed = true;
if (cmd->err)
break;
@@ -1106,6 +1186,10 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
}
renesas_i3c_dequeue_xfer(i3c, xfer);
+
+ if (xfer_failed)
+ renesas_i3c_irqs_mask_and_clear(i3c);
+
return cmd->err;
}
@@ -1508,12 +1592,16 @@ static int renesas_i3c_probe(struct platform_device *pdev)
if (IS_ERR(i3c->regs))
return PTR_ERR(i3c->regs);
- ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &i3c->clks);
- if (ret <= RENESAS_I3C_TCLK_IDX)
- return dev_err_probe(&pdev->dev, ret < 0 ? ret : -EINVAL,
- "Failed to get clocks (need > %d, got %d)\n",
- RENESAS_I3C_TCLK_IDX, ret);
- i3c->num_clks = ret;
+ i3c->tclk = devm_clk_get(&pdev->dev, "tclk");
+ if (IS_ERR(i3c->tclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i3c->tclk), "Failed to get tclk");
+
+ i3c->dev = &pdev->dev;
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 300);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
i3c->tresetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn");
if (IS_ERR(i3c->tresetn))
@@ -1580,8 +1668,6 @@ static int renesas_i3c_suspend(struct device *dev)
if (ret)
goto err_mark_resumed;
- clk_bulk_disable(i3c->num_clks, i3c->clks);
-
return 0;
err_mark_resumed:
@@ -1603,13 +1689,13 @@ static int renesas_i3c_resume(struct device *dev)
if (ret)
return ret;
- ret = clk_bulk_enable(i3c->num_clks, i3c->clks);
+ ret = renesas_i3c_reset(i3c);
if (ret)
goto err_resets_asserted;
- ret = renesas_i3c_reset(i3c);
+ ret = pm_runtime_resume_and_get(dev);
if (ret)
- goto err_clks_disable;
+ goto err_resets_asserted;
/* Re-store I3C registers value. */
renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
@@ -1632,15 +1718,23 @@ static int renesas_i3c_resume(struct device *dev)
i2c_mark_adapter_resumed(&i3c->base.i2c);
+ pm_runtime_put_autosuspend(dev);
+
/*
* I3C devices may have retained their dynamic address anyway. Do not
* fail the resume because of DAA error.
*/
return 0;
-err_clks_disable:
- clk_bulk_disable(i3c->num_clks, i3c->clks);
err_resets_asserted:
+ /*
+ * If this happens, there is no way to recover from this state without
+ * reloading the driver. We want to avoid keeping the reset line
+ * deasserted unnecessarily. The runtime paths will still work correctly
+ * even if the IP registers are accessed while reset is asserted (e.g.
+ * if a runtime path is triggered after a failed resume). Checked on
+ * RZ/G3S.
+ */
reset_control_bulk_assert(ARRAY_SIZE(resets), resets);
return ret;
}
--
2.43.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 16/16] i3c: renesas: Add runtime PM support
2026-06-12 16:04 ` [PATCH v4 16/16] i3c: renesas: Add runtime PM support Claudiu Beznea
@ 2026-06-12 16:34 ` sashiko-bot
0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-06-12 16:34 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: linux-i3c, Frank.Li
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Timeout handler fails to abort hardware transfers because it clears the BCTL_ABT bit instead of setting it.
Pre-existing issues:
- [High] Transfer loops do not break on timeout and unconditionally return success, leading to silent failures, race conditions, and memory corruption.
--
commit d8976043a71981cb4634aa1e9eafca1113e44ec6
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
i3c: renesas: Add runtime PM support
This commit adds runtime PM support to the Renesas I3C driver, guarding
subsystem operations with pm_runtime APIs. It also drops explicit clock
handling, modifies wait_xfer to return unsigned long, and introduces a
function to clear pending IRQs on transfer aborts.
> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> --- 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_irqs_mask_and_clear(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 actually abort the hardware transfer? The commit message mentions
disabling interrupts and clearing pending IRQ status bits when a completion
timeout occurs.
However, clearing the BCTL_ABT bit is usually how an abort is acknowledged.
To actually initiate a hardware abort, the host typically must set the
BCTL_ABT bit, or explicitly reset the controller.
By only clearing an already-zero bit, does the controller remain in a busy
or hung state, potentially corrupting subsequent transfers?
[ ... ]
> @@ -777,7 +812,9 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
> NCMDQP_CMD(I3C_CCC_ENTDAA) | NCMDQP_DEV_INDEX(ret) |
> NCMDQP_DEV_COUNT(i3c->maxdevs - ret) | NCMDQP_TOC;
>
> - renesas_i3c_wait_xfer(i3c, xfer);
> + time_left = renesas_i3c_wait_xfer(i3c, xfer);
> + if (!time_left)
> + renesas_i3c_irqs_mask_and_clear(i3c);
>
> newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, first_i3c_pos);
[Severity: High]
This is a pre-existing issue, but I wanted to ask about the timeout handling
here in renesas_i3c_daa().
If a timeout occurs (!time_left), the code proceeds without breaking early
or returning an error. Because cmd->rx_count might be empty on a timeout,
does this end up parsing a full newdevs mask and erroneously attaching
phantom devices for all remaining address slots?
[ ... ]
> @@ -970,9 +1022,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)
> + xfer_failed = true;
> }
>
> + if (xfer_failed)
> + renesas_i3c_irqs_mask_and_clear(i3c);
> +
> return 0;
> }
[Severity: High]
This is also a pre-existing issue, but does this loop in
renesas_i3c_i3c_xfers() need to break when a timeout occurs?
By continuing the loop after xfer_failed is set to true, it seems
subsequent commands are enqueued to a controller that might still be hung
from the timed-out transfer.
Additionally, this function unconditionally returns 0 (success) at the end,
completely ignoring the timeout and any hardware errors recorded in
xfer->ret. Would it be safer to return an error code and break out of the
loop immediately?
[ ... ]
> @@ -1097,7 +1175,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)
> + xfer_failed = true;
>
> if (cmd->err)
> break;
[Severity: High]
This is a pre-existing issue, but similar to the i3c transfers above, the
i2c transfer loop in renesas_i3c_i2c_xfers() fails to break on a timeout.
Because it continues enqueuing commands to a potentially hung controller,
could this cause a direct data race on cmd->i2c_buf and ultimately result in
returning a false success status?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612160458.3102106-1-claudiu.beznea@kernel.org?part=16
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM
2026-06-12 16:04 [PATCH v4 00/16] i3c: renesas: Suspend to RAM with power loss and runtime PM Claudiu Beznea
` (15 preceding siblings ...)
2026-06-12 16:04 ` [PATCH v4 16/16] i3c: renesas: Add runtime PM support Claudiu Beznea
@ 2026-06-12 20:17 ` Frank Li
16 siblings, 0 replies; 29+ messages in thread
From: Frank Li @ 2026-06-12 20:17 UTC (permalink / raw)
To: Claudiu Beznea
Cc: wsa+renesas, tommaso.merciai.xr, alexandre.belloni, Frank.Li,
p.zabel, linux-i3c, linux-kernel, claudiu.beznea, Claudiu Beznea
On Fri, Jun 12, 2026 at 07:04:42PM +0300, Claudiu Beznea wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>
> Hi,
>
> This series adjusts the suspend to RAM code to handle cases where power
> to the connected devices is lost during suspend to RAM. The fixes
> included in this series are required for that support.
>
> Along with suspend to RAM support, runtime PM support is also added.
> Cleanup patches were included to prepare for clean runtime PM support.
>
> Please note that series need to be applied on top of [1] since
> patch "i3c: renesas: Perform Dynamic Address Assignment on resume"
> uses i3c_master_reattach_i3c_dev_locked().
Please sashiko AI review result, except pre-existing problem you can fix
later, other problem need be fixed or provide your judegement.
Frank
>
> Thank you,
> Claudiu
>
> Changes in v4:
> - dropped patch "i3c: renesas: Do not attach devices if xfer failed"
> - fixed the swap in renesas_i3c_group_devs_in_slots() for i3c->addr[]
> renesas_i3c_irqs_mask_and_clear();
> - in patch 16/16 renamed renesas_i3c_abort_xfer() from v3 to
>
> Changes in v3:
> - re-based on top of series [1] to be able to use
> i3c_master_reattach_i3c_dev_locked()
> - used i3c_master_reattach_i3c_dev_locked() on patch
> "i3c: renesas: Perform Dynamic Address Assignment on resume" along with
> grouping the I2C and I3C devices in the driver slot
> - collected tags
>
> Changes in v2:
> - dropped patch "i3c: renesas: Use the divider 128"
> - adjusted the patches title and description where requested in the
> review process
> - adjusted the DAA procedure after resume to still properly re-configure
> the controller in case the bus was full before a suspend
> - added patch "i3c: renesas: Do not attach devices if xfer failed"
> - collected tags
>
> [1] https://lore.kernel.org/linux-i3c/20260525064209.2263045-1-lakshay.piplani@nxp.com/T/#m74ddb24e1e3a8006d933bd6106cc0c4508a11d2c
>
> Claudiu Beznea (16):
> i3c: renesas: Check that the transfer is valid before accessing it
> i3c: renesas: Restore STDBR and EXTBR registers on resume
> i3c: renesas: Follow the reset deassert order used in probe
> i3c: renesas: Reconfigure the DATBAS register on re-attach
> i3c: renesas: Reset the controller on resume
> i3c: renesas: Perform Dynamic Address Assignment on resume
> i3c: renesas: Clean DATBAS register on detach
> i3c: renesas: Use reset_control_bulk_{assert, deassert}()
> i3c: renesas: Return immediately if there is no transfer
> i3c: renesas: Follow a unified pattern for transfer and command
> initialization
> i3c: renesas: Drop the explicit memset() call
> i3c: renesas: Update HW registers after SW computations are done
> i3c: renesas: Organize structures to avoid unnecessary padding
> i3c: renesas: Use the "dev_name:irq_name" format for the interrupt
> name
> i3c: renesas: Drop unnecessary tab
> i3c: renesas: Add runtime PM support
>
> drivers/i3c/master/renesas-i3c.c | 541 +++++++++++++++++++++++--------
> 1 file changed, 413 insertions(+), 128 deletions(-)
>
> --
> 2.43.0
>
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 29+ messages in thread