All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
To: Frank Li <Frank.li@nxp.com>
Cc: tomm.merciai@gmail.com, linux-renesas-soc@vger.kernel.org,
	biju.das.jz@bp.renesas.com,
	Wolfram Sang <wsa+renesas@sang-engineering.com>,
	Alexandre Belloni <alexandre.belloni@bootlin.com>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	linux-i3c@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 4/4] i3c: renesas: Add suspend/resume support
Date: Tue, 30 Dec 2025 17:31:06 +0100	[thread overview]
Message-ID: <aVP-Sn9ecWWI4rTq@tom-desktop> (raw)
In-Reply-To: <aVP1IxpDNUvLzHHz@lizhi-Precision-Tower-5810>

Hi Frank,
Thanks for your review!

On Tue, Dec 30, 2025 at 10:52:03AM -0500, Frank Li wrote:
> On Tue, Dec 30, 2025 at 11:39:39AM +0100, Tommaso Merciai wrote:
> > The Renesas I3C controller does not retain its register state across system
> > suspend, requiring the driver to explicitly save and restore hardware
> > configuration.
> >
> > Add suspend and resume NOIRQ callbacks to handle system sleep transitions.
> >
> > During suspend, save the Device Address Table (DAT) entries, assert reset
> > lines, and disable all related clocks to allow the controller to enter a
> > low-power state.
> >
> > On resume, re-enable clocks and reset lines in the proper order. Restore
> > the REFCKCTL register, master dynamic address, and all DAT entries, then
> > reinitialize the controller.
> >
> > Store the REFCLK divider value, and the master dynamic address to restore
> > timing and addressing configuration after resume.
> >
> > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> > ---
> > v1->v2:
> >  - Updated commit as v1 has been split into smaller patches.
> >  - Use clock bulk API into renesas_i3c_suspend_noirq() and
> >    renesas_i3c_resume_noirq().
> >  - Updated commit body accordingly.
> >
> >  drivers/i3c/master/renesas-i3c.c | 85 ++++++++++++++++++++++++++++++++
> >  1 file changed, 85 insertions(+)
> >
> > diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> > index 4f076c372b36..f089bbf19fa5 100644
> > --- a/drivers/i3c/master/renesas-i3c.c
> > +++ b/drivers/i3c/master/renesas-i3c.c
> > @@ -254,16 +254,19 @@ struct renesas_i3c {
> >  	enum i3c_internal_state internal_state;
> >  	u16 maxdevs;
> >  	u32 free_pos;
> > +	u32 dyn_addr;
> >  	u32 i2c_STDBR;
> >  	u32 i3c_STDBR;
> >  	unsigned long rate;
> >  	u8 addrs[RENESAS_I3C_MAX_DEVS];
> >  	struct renesas_i3c_xferqueue xferqueue;
> >  	void __iomem *regs;
> > +	u32 *DATBASn;
> >  	struct clk_bulk_data clks[3];
> >  	u8 num_clks;
> >  	struct reset_control *presetn;
> >  	struct reset_control *tresetn;
> > +	u8 refclk_div;
> >  };
> >
> >  struct renesas_i3c_i2c_dev_data {
> > @@ -615,6 +618,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
> >  					   EXTBR_EBRHP(pp_high_ticks));
> >
> >  	renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
> > +	i3c->refclk_div = cks;
> >
> >  	/* I3C hw init*/
> >  	renesas_i3c_hw_init(i3c);
> > @@ -623,6 +627,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
> >  	if (ret < 0)
> >  		return ret;
> >
> > +	i3c->dyn_addr = ret;
> >  	renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
> >
> >  	memset(&info, 0, sizeof(info));
> > @@ -1390,6 +1395,13 @@ 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);
> 
> nit: can you move it to previous line to reduce line number

Ack.

> 
> > +	if (!i3c->DATBASn)
> > +		return -ENOMEM;
> > +
> >  	return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false);
> >  }
> >
> > @@ -1400,6 +1412,78 @@ static void renesas_i3c_remove(struct platform_device *pdev)
> >  	i3c_master_unregister(&i3c->base);
> >  }
> >
> > +static int renesas_i3c_suspend_noirq(struct device *dev)
> > +{
> > +	struct renesas_i3c *i3c = dev_get_drvdata(dev);
> > +	int i, 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)
> > +		return ret;
> > +
> > +	ret = reset_control_assert(i3c->tresetn);
> > +	if (ret) {
> > +		reset_control_deassert(i3c->presetn);
> > +		return ret;
> > +	}
> > +
> > +	clk_bulk_disable_unprepare(i3c->num_clks, i3c->clks);
> > +
> > +	return 0;
> > +}
> > +
> > +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);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = reset_control_deassert(i3c->tresetn);
> > +	if (ret)
> > +		goto err_presetn;
> > +
> > +	ret = clk_bulk_prepare_enable(i3c->num_clks, i3c->clks);
> > +	if (ret)
> > +		goto err_tresetn;
> > +
> > +	/* Re-store I3C registers value. */
> > +	renesas_writel(i3c->regs, REFCKCTL,
> > +		       REFCKCTL_IREFCKS(i3c->refclk_div));
> > +	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);
> > +
> > +	i2c_mark_adapter_resumed(&i3c->base.i2c);
> > +
> > +	return 0;
> > +
> > +err_tresetn:
> > +	clk_bulk_disable_unprepare(i3c->num_clks, i3c->clks)
> 
> look like it should be
> 	reset_control_assert(i3c->tresetn);

You are completely right.
Will fix this in v3.

Kind Regards,
Tommaso

> 
> Frank
> 
> > +err_presetn:
> > +	reset_control_assert(i3c->presetn);
> > +	return ret;
> > +}
> > +
> > +static const struct dev_pm_ops renesas_i3c_pm_ops = {
> > +	NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq,
> > +				  renesas_i3c_resume_noirq)
> > +};
> > +
> >  static const struct renesas_i3c_config empty_i3c_config = {
> >  };
> >
> > @@ -1420,6 +1504,7 @@ static struct platform_driver renesas_i3c = {
> >  	.driver = {
> >  		.name = "renesas-i3c",
> >  		.of_match_table = renesas_i3c_of_ids,
> > +		.pm = pm_sleep_ptr(&renesas_i3c_pm_ops),
> >  	},
> >  };
> >  module_platform_driver(renesas_i3c);
> > --
> > 2.43.0
> >

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

WARNING: multiple messages have this Message-ID (diff)
From: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
To: Frank Li <Frank.li@nxp.com>
Cc: tomm.merciai@gmail.com, linux-renesas-soc@vger.kernel.org,
	biju.das.jz@bp.renesas.com,
	Wolfram Sang <wsa+renesas@sang-engineering.com>,
	Alexandre Belloni <alexandre.belloni@bootlin.com>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	linux-i3c@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 4/4] i3c: renesas: Add suspend/resume support
Date: Tue, 30 Dec 2025 17:31:06 +0100	[thread overview]
Message-ID: <aVP-Sn9ecWWI4rTq@tom-desktop> (raw)
In-Reply-To: <aVP1IxpDNUvLzHHz@lizhi-Precision-Tower-5810>

Hi Frank,
Thanks for your review!

On Tue, Dec 30, 2025 at 10:52:03AM -0500, Frank Li wrote:
> On Tue, Dec 30, 2025 at 11:39:39AM +0100, Tommaso Merciai wrote:
> > The Renesas I3C controller does not retain its register state across system
> > suspend, requiring the driver to explicitly save and restore hardware
> > configuration.
> >
> > Add suspend and resume NOIRQ callbacks to handle system sleep transitions.
> >
> > During suspend, save the Device Address Table (DAT) entries, assert reset
> > lines, and disable all related clocks to allow the controller to enter a
> > low-power state.
> >
> > On resume, re-enable clocks and reset lines in the proper order. Restore
> > the REFCKCTL register, master dynamic address, and all DAT entries, then
> > reinitialize the controller.
> >
> > Store the REFCLK divider value, and the master dynamic address to restore
> > timing and addressing configuration after resume.
> >
> > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> > ---
> > v1->v2:
> >  - Updated commit as v1 has been split into smaller patches.
> >  - Use clock bulk API into renesas_i3c_suspend_noirq() and
> >    renesas_i3c_resume_noirq().
> >  - Updated commit body accordingly.
> >
> >  drivers/i3c/master/renesas-i3c.c | 85 ++++++++++++++++++++++++++++++++
> >  1 file changed, 85 insertions(+)
> >
> > diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
> > index 4f076c372b36..f089bbf19fa5 100644
> > --- a/drivers/i3c/master/renesas-i3c.c
> > +++ b/drivers/i3c/master/renesas-i3c.c
> > @@ -254,16 +254,19 @@ struct renesas_i3c {
> >  	enum i3c_internal_state internal_state;
> >  	u16 maxdevs;
> >  	u32 free_pos;
> > +	u32 dyn_addr;
> >  	u32 i2c_STDBR;
> >  	u32 i3c_STDBR;
> >  	unsigned long rate;
> >  	u8 addrs[RENESAS_I3C_MAX_DEVS];
> >  	struct renesas_i3c_xferqueue xferqueue;
> >  	void __iomem *regs;
> > +	u32 *DATBASn;
> >  	struct clk_bulk_data clks[3];
> >  	u8 num_clks;
> >  	struct reset_control *presetn;
> >  	struct reset_control *tresetn;
> > +	u8 refclk_div;
> >  };
> >
> >  struct renesas_i3c_i2c_dev_data {
> > @@ -615,6 +618,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
> >  					   EXTBR_EBRHP(pp_high_ticks));
> >
> >  	renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
> > +	i3c->refclk_div = cks;
> >
> >  	/* I3C hw init*/
> >  	renesas_i3c_hw_init(i3c);
> > @@ -623,6 +627,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
> >  	if (ret < 0)
> >  		return ret;
> >
> > +	i3c->dyn_addr = ret;
> >  	renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
> >
> >  	memset(&info, 0, sizeof(info));
> > @@ -1390,6 +1395,13 @@ 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);
> 
> nit: can you move it to previous line to reduce line number

Ack.

> 
> > +	if (!i3c->DATBASn)
> > +		return -ENOMEM;
> > +
> >  	return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false);
> >  }
> >
> > @@ -1400,6 +1412,78 @@ static void renesas_i3c_remove(struct platform_device *pdev)
> >  	i3c_master_unregister(&i3c->base);
> >  }
> >
> > +static int renesas_i3c_suspend_noirq(struct device *dev)
> > +{
> > +	struct renesas_i3c *i3c = dev_get_drvdata(dev);
> > +	int i, 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)
> > +		return ret;
> > +
> > +	ret = reset_control_assert(i3c->tresetn);
> > +	if (ret) {
> > +		reset_control_deassert(i3c->presetn);
> > +		return ret;
> > +	}
> > +
> > +	clk_bulk_disable_unprepare(i3c->num_clks, i3c->clks);
> > +
> > +	return 0;
> > +}
> > +
> > +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);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = reset_control_deassert(i3c->tresetn);
> > +	if (ret)
> > +		goto err_presetn;
> > +
> > +	ret = clk_bulk_prepare_enable(i3c->num_clks, i3c->clks);
> > +	if (ret)
> > +		goto err_tresetn;
> > +
> > +	/* Re-store I3C registers value. */
> > +	renesas_writel(i3c->regs, REFCKCTL,
> > +		       REFCKCTL_IREFCKS(i3c->refclk_div));
> > +	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);
> > +
> > +	i2c_mark_adapter_resumed(&i3c->base.i2c);
> > +
> > +	return 0;
> > +
> > +err_tresetn:
> > +	clk_bulk_disable_unprepare(i3c->num_clks, i3c->clks)
> 
> look like it should be
> 	reset_control_assert(i3c->tresetn);

You are completely right.
Will fix this in v3.

Kind Regards,
Tommaso

> 
> Frank
> 
> > +err_presetn:
> > +	reset_control_assert(i3c->presetn);
> > +	return ret;
> > +}
> > +
> > +static const struct dev_pm_ops renesas_i3c_pm_ops = {
> > +	NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq,
> > +				  renesas_i3c_resume_noirq)
> > +};
> > +
> >  static const struct renesas_i3c_config empty_i3c_config = {
> >  };
> >
> > @@ -1420,6 +1504,7 @@ static struct platform_driver renesas_i3c = {
> >  	.driver = {
> >  		.name = "renesas-i3c",
> >  		.of_match_table = renesas_i3c_of_ids,
> > +		.pm = pm_sleep_ptr(&renesas_i3c_pm_ops),
> >  	},
> >  };
> >  module_platform_driver(renesas_i3c);
> > --
> > 2.43.0
> >

  reply	other threads:[~2025-12-30 16:31 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-30 10:39 [PATCH v2 0/4] i3c: renesas: Add suspend/resume support Tommaso Merciai
2025-12-30 10:39 ` Tommaso Merciai
2025-12-30 10:39 ` [PATCH v2 1/4] i3c: renesas: Switch to clk_bulk API and store clocks in private data Tommaso Merciai
2025-12-30 10:39   ` Tommaso Merciai
2025-12-30 15:44   ` Frank Li
2025-12-30 15:44     ` Frank Li
2025-12-30 16:24     ` Tommaso Merciai
2025-12-30 16:24       ` Tommaso Merciai
2025-12-30 18:39       ` Tommaso Merciai
2025-12-30 18:39         ` Tommaso Merciai
2025-12-30 10:39 ` [PATCH v2 2/4] i3c: renesas: Store clock rate and reset controls in struct renesas_i3c Tommaso Merciai
2025-12-30 10:39   ` Tommaso Merciai
2025-12-30 15:45   ` Frank Li
2025-12-30 15:45     ` Frank Li
2025-12-30 10:39 ` [PATCH v2 3/4] i3c: renesas: Factor out hardware initialization to separate function Tommaso Merciai
2025-12-30 10:39   ` Tommaso Merciai
2025-12-30 15:47   ` Frank Li
2025-12-30 15:47     ` Frank Li
2025-12-30 16:27     ` Tommaso Merciai
2025-12-30 16:27       ` Tommaso Merciai
2025-12-30 10:39 ` [PATCH v2 4/4] i3c: renesas: Add suspend/resume support Tommaso Merciai
2025-12-30 10:39   ` Tommaso Merciai
2025-12-30 15:52   ` Frank Li
2025-12-30 15:52     ` Frank Li
2025-12-30 16:31     ` Tommaso Merciai [this message]
2025-12-30 16:31       ` Tommaso Merciai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aVP-Sn9ecWWI4rTq@tom-desktop \
    --to=tommaso.merciai.xr@bp.renesas.com \
    --cc=Frank.li@nxp.com \
    --cc=alexandre.belloni@bootlin.com \
    --cc=biju.das.jz@bp.renesas.com \
    --cc=linux-i3c@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    --cc=p.zabel@pengutronix.de \
    --cc=tomm.merciai@gmail.com \
    --cc=wsa+renesas@sang-engineering.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.