[PATCH 7.1 360/438] i2c: imx: mark I2C adapter when hardware is powered down
Greg Kroah-Hartman <[email protected]>
| Newsgroups | dev.linux.lists.patches,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
7.1-stable review patch. If anyone has any objections, please let me know. ------------------ From: Carlos Song <[email protected]> commit 00d86dd5c2034e0e139e4806137b3b43e07ddd83 upstream. On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers. During system suspend/resume, there exists a time window between: - suspend_noirq and the system entering suspend - the system starting to resume and resume_noirq In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored. If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang. Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available. Fixes: 358025ac091e ("i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()") Signed-off-by: Carlos Song <[email protected]> Cc: <[email protected]> # v6.14+ Reviewed-by: Frank Li <[email protected]> Acked-by: Oleksij Rempel <[email protected]> Signed-off-by: Andi Shyti <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]> --- drivers/i2c/busses/i2c-imx.c | 45 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1952,6 +1952,47 @@ static int i2c_imx_runtime_resume(struct return 0; } +static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&i2c_imx->adapter); + + /* + * Cancel the slave timer before powering down to prevent + * i2c_imx_slave_timeout() from accessing hardware registers + * while the clock is disabled. + */ + hrtimer_cancel(&i2c_imx->slave_timer); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&i2c_imx->adapter); + if (i2c_imx->slave) { + hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY); + hrtimer_restart(&i2c_imx->slave_timer); + } + return ret; + } + + return 0; +} + +static int __maybe_unused i2c_imx_resume_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + i2c_mark_adapter_resumed(&i2c_imx->adapter); + + return 0; +} + static int i2c_imx_suspend(struct device *dev) { /* @@ -1985,8 +2026,8 @@ static int i2c_imx_resume(struct device } static const struct dev_pm_ops i2c_imx_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq, + i2c_imx_resume_noirq) SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume) RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL) };