diff mbox series

i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()

Message ID 20241125142108.1613016-1-carlos.song@nxp.com (mailing list archive)
State New
Headers show
Series i2c: imx: make controller available until system suspend_noirq() and from resume_noirq() | expand

Commit Message

Carlos Song Nov. 25, 2024, 2:21 p.m. UTC
From: Carlos Song <carlos.song@nxp.com>

Put runtime pm to resume state between suspend() and suspend_noirq(),
resume_noirq() and resume(), because some I2C devices need controller
on to do communication during this period.

The controller can't be wakeup once runtime pm is disabled and in
runtime autosuspended state.

The problem can be easily reproduced on the I.MX8MQ platform:
PMIC needs to be used to enable regular when the system resumes.
When PMIC uses I2C controller, I2C runtime pm has not been enabled,
so in i2c xfer(), pm_runtime_resume_and_get() will return error,
which causes data transfer failed. Therefore, regulars can not
be enabled and hang system resumes.
Here is resume error log:
[   53.888902] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 529, parent: platform
[   53.897203] i2c_imx_xfer, pm_runtime_resume_and_get is -13
[   53.902713] imx-pgc imx-pgc-domain.5: failed to enable regulator: -EACCES
[   53.909518] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 12331 usecs
[   53.917545] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 529, parent: soc@0
[   53.925659] i2c_imx_xfer, pm_runtime_resume_and_get is -13
[   53.931157] imx-pgc imx-pgc-domain.6: failed to enable regulator: -EACCES

I.MX8MQ system resume normally after applying the fix. Here is resume log:
[   71.068807] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 530, parent: platform
[   71.077103] i2c_imx_xfer, pm_runtime_resume_and_get is 0
[   71.083578] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 6490 usecs
[   71.091526] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 530, parent: soc@0
[   71.099638] i2c_imx_xfer, pm_runtime_resume_and_get is 0
[   71.106091] mxc_hantro 38300000.vpu: PM: genpd_resume_noirq returned 0 after 6458 usecs

Signed-off-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
---
 drivers/i2c/busses/i2c-imx.c | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

Comments

Frank Li Nov. 25, 2024, 5:19 p.m. UTC | #1
On Mon, Nov 25, 2024 at 10:21:08PM +0800, carlos.song@nxp.com wrote:
> From: Carlos Song <carlos.song@nxp.com>
>
> Put runtime pm to resume state between suspend() and suspend_noirq(),
> resume_noirq() and resume(), because some I2C devices need controller
> on to do communication during this period.
>
> The controller can't be wakeup once runtime pm is disabled and in
> runtime autosuspended state.
>
> The problem can be easily reproduced on the I.MX8MQ platform:
> PMIC needs to be used to enable regular when the system resumes.
> When PMIC uses I2C controller, I2C runtime pm has not been enabled,
> so in i2c xfer(), pm_runtime_resume_and_get() will return error,
> which causes data transfer failed. Therefore, regulars can not
> be enabled and hang system resumes.
> Here is resume error log:
> [   53.888902] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 529, parent: platform
> [   53.897203] i2c_imx_xfer, pm_runtime_resume_and_get is -13
> [   53.902713] imx-pgc imx-pgc-domain.5: failed to enable regulator: -EACCES
> [   53.909518] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 12331 usecs
> [   53.917545] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 529, parent: soc@0
> [   53.925659] i2c_imx_xfer, pm_runtime_resume_and_get is -13
> [   53.931157] imx-pgc imx-pgc-domain.6: failed to enable regulator: -EACCES
>
> I.MX8MQ system resume normally after applying the fix. Here is resume log:
> [   71.068807] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 530, parent: platform
> [   71.077103] i2c_imx_xfer, pm_runtime_resume_and_get is 0
> [   71.083578] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 6490 usecs
> [   71.091526] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 530, parent: soc@0
> [   71.099638] i2c_imx_xfer, pm_runtime_resume_and_get is 0
> [   71.106091] mxc_hantro 38300000.vpu: PM: genpd_resume_noirq returned 0 after 6458 usecs
>
> Signed-off-by: Carlos Song <carlos.song@nxp.com>
> Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/i2c/busses/i2c-imx.c | 32 +++++++++++++++++++++++++++++++-
>  1 file changed, 31 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
> index ee7070ee9e6e..c35092726465 100644
> --- a/drivers/i2c/busses/i2c-imx.c
> +++ b/drivers/i2c/busses/i2c-imx.c
> @@ -1770,7 +1770,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
>  		goto rpm_disable;
>
>  	/* Request IRQ */
> -	ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED, pdev->name, i2c_imx);
> +	ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED | IRQF_NO_SUSPEND,
> +			  pdev->name, i2c_imx);
>  	if (ret) {
>  		dev_err(&pdev->dev, "can't claim irq %d\n", irq);
>  		goto rpm_disable;
> @@ -1894,7 +1895,36 @@ static int i2c_imx_runtime_resume(struct device *dev)
>  	return ret;
>  }
>
> +static int i2c_imx_suspend(struct device *dev)
> +{
> +	/*
> +	 * Some I2C devices may need I2C controller up during resume_noirq()
> +	 * or suspend_noirq(), if the controller is autosuspended, there is
> +	 * no way to wakeup it once runtime pm is disabled (in suspend_late()).
> +	 * When system resume, I2C controller will be available until runtime pm
> +	 * is enabled(in_resume_early()). But it is too late for some devices.
> +	 * Wakeup the controller in suspend() callback while runtime pm is enabled,
> +	 * I2C controller will be available until suspend_noirq() callback
> +	 * (pm_runtime_force_suspend()) is called. During the resume, I2C controller
> +	 * can be restored by resume_noirq() callback (pm_runtime_force_resume()).
> +	 * Then resume() callback enables autosuspend. It will make I2C controller
> +	 * available until system suspend_noirq() and from resume_noirq().
> +	 */
> +	return pm_runtime_resume_and_get(dev);
> +}
> +
> +static int i2c_imx_resume(struct device *dev)
> +{
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_put_autosuspend(dev);
> +
> +	return 0;
> +}
> +
>  static const struct dev_pm_ops i2c_imx_pm_ops = {
> +	NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +				  pm_runtime_force_resume)
> +	SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
>  	RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
>  };
>
> --
> 2.34.1
>
diff mbox series

Patch

diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index ee7070ee9e6e..c35092726465 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -1770,7 +1770,8 @@  static int i2c_imx_probe(struct platform_device *pdev)
 		goto rpm_disable;
 
 	/* Request IRQ */
-	ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED, pdev->name, i2c_imx);
+	ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED | IRQF_NO_SUSPEND,
+			  pdev->name, i2c_imx);
 	if (ret) {
 		dev_err(&pdev->dev, "can't claim irq %d\n", irq);
 		goto rpm_disable;
@@ -1894,7 +1895,36 @@  static int i2c_imx_runtime_resume(struct device *dev)
 	return ret;
 }
 
+static int i2c_imx_suspend(struct device *dev)
+{
+	/*
+	 * Some I2C devices may need I2C controller up during resume_noirq()
+	 * or suspend_noirq(), if the controller is autosuspended, there is
+	 * no way to wakeup it once runtime pm is disabled (in suspend_late()).
+	 * When system resume, I2C controller will be available until runtime pm
+	 * is enabled(in_resume_early()). But it is too late for some devices.
+	 * Wakeup the controller in suspend() callback while runtime pm is enabled,
+	 * I2C controller will be available until suspend_noirq() callback
+	 * (pm_runtime_force_suspend()) is called. During the resume, I2C controller
+	 * can be restored by resume_noirq() callback (pm_runtime_force_resume()).
+	 * Then resume() callback enables autosuspend. It will make I2C controller
+	 * available until system suspend_noirq() and from resume_noirq().
+	 */
+	return pm_runtime_resume_and_get(dev);
+}
+
+static int i2c_imx_resume(struct device *dev)
+{
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	return 0;
+}
+
 static const struct dev_pm_ops i2c_imx_pm_ops = {
+	NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				  pm_runtime_force_resume)
+	SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
 	RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
 };