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 |
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 --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) };