[PATCH v5 06/17] i3c: renesas: Perform Dynamic Address Assignment on resume
Claudiu Beznea <[email protected]> Mon, 13 Jul 2026 16:05:34 +0300
| Newsgroups | org.infradead.lists.linux-i3c,org.kernel.vger.linux-kernel,org.kernel.vger.linux-renesas-soc,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
From: Claudiu Beznea <[email protected]> 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(). Along with this, struct renesas_i3c::DATBASn and its usage were removed, as they are no longer needed. Fixes: e7218986319b ("i3c: renesas: Add suspend/resume support") Cc: [email protected] Signed-off-by: Claudiu Beznea <[email protected]> --- Changes in v5: - restore it to the level of v1; the other scenarios updated by sashiko were already present w/ and w/o this patch and could be addressed incrementally - updated the patch description 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 | 38 +++++++++++++------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c index 6590da962592..acc30ed615ab 100644 --- a/drivers/i3c/master/renesas-i3c.c +++ b/drivers/i3c/master/renesas-i3c.c @@ -265,7 +265,6 @@ struct renesas_i3c { u8 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; @@ -495,8 +494,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) @@ -1419,12 +1418,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); } @@ -1435,17 +1428,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; @@ -1466,10 +1455,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) @@ -1495,15 +1484,19 @@ 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); + ret = i3c_master_do_daa_ext(&i3c->base, true); + if (ret) + dev_err(dev, "DAA failed on resume, ret=%d", ret); + 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: @@ -1516,8 +1509,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 [email protected] http://lists.infradead.org/mailman/listinfo/linux-i3c