On Monday, September 4, 2017 1:14:44 AM CEST Rafael J. Wysocki wrote: > From: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx> > > After commit a23318feeff6 (i2c: designware: Fix system suspend) > the i2c-designware-platdrv driver always resumes the device in its > system sleep ->suspend callback which isn't particularly nice, > even though it is technically correct. > > A better approach would be to make the driver track the PM state of > the device so that it doesn't need to resume it in ->suspend and > to drop its ->prepare and ->complete callbacks which would only be > marginally useful then, so implement it. > > First, drop dw_i2c_plat_suspend() added by commit a23318feeff6 and > rename dw_i2c_plat_runtime_suspend() back to dw_i2c_plat_suspend(). > > Second, point the driver's ->late_suspend and ->early_resume > callbacks, rather than its ->suspend and ->resume callbacks, > to dw_i2c_plat_suspend() and dw_i2c_plat_resume(), respectively, > so that they are not executed in parallel with each other, for > example if runtime resume of the device takes place during system > suspend. Also, since the driver enables runtime PM unconditionally > in dw_i2c_plat_probe(), this change allows the > pm_runtime_status_suspended() check to be used in the PM callbacks > to determine whether or not the device needs to be either suspended > or resumed (moving the callbacks into the late/early stages of > system suspend/resume, respectively, guarantees the stability > of the runtime PM status at the time when they are invoked). > > Next, add a "skip_resume" flag to struct dw_i2c_dev and make > dw_i2c_plat_suspend() and dw_i2c_plat_resume() use it to avoid > resuming a previously runtime-suspended device during system resume. > > Finally, drop the driver's ->prepare and ->complete PM callbacks, > because returning "true" from ->prepare for runtime-suspended > devices is marginally useful (the PM core may still ignore that > return value and invoke the driver's ->suspend callback anyway) > and ->complete is only needed because of what ->prepare does. > > Overrides: a23318feeff6 (i2c: designware: Fix system suspend) > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx> > --- > drivers/i2c/busses/i2c-designware-core.h | 1 > drivers/i2c/busses/i2c-designware-platdrv.c | 47 +++++++++------------------- > 2 files changed, 17 insertions(+), 31 deletions(-) > > Index: linux-pm/drivers/i2c/busses/i2c-designware-core.h > =================================================================== > --- linux-pm.orig/drivers/i2c/busses/i2c-designware-core.h > +++ linux-pm/drivers/i2c/busses/i2c-designware-core.h > @@ -280,6 +280,7 @@ struct dw_i2c_dev { > int (*acquire_lock)(struct dw_i2c_dev *dev); > void (*release_lock)(struct dw_i2c_dev *dev); > bool pm_disabled; > + bool skip_resume; > void (*disable)(struct dw_i2c_dev *dev); > void (*disable_int)(struct dw_i2c_dev *dev); > int (*init)(struct dw_i2c_dev *dev); > Index: linux-pm/drivers/i2c/busses/i2c-designware-platdrv.c > =================================================================== > --- linux-pm.orig/drivers/i2c/busses/i2c-designware-platdrv.c > +++ linux-pm/drivers/i2c/busses/i2c-designware-platdrv.c > @@ -434,28 +434,17 @@ static const struct of_device_id dw_i2c_ > MODULE_DEVICE_TABLE(of, dw_i2c_of_match); > #endif > > -#ifdef CONFIG_PM_SLEEP > -static int dw_i2c_plat_prepare(struct device *dev) > -{ > - return pm_runtime_suspended(dev); > -} > - > -static void dw_i2c_plat_complete(struct device *dev) > -{ > - if (dev->power.direct_complete) > - pm_request_resume(dev); > -} > -#else > -#define dw_i2c_plat_prepare NULL > -#define dw_i2c_plat_complete NULL > -#endif > - > #ifdef CONFIG_PM > -static int dw_i2c_plat_runtime_suspend(struct device *dev) > +static int dw_i2c_plat_suspend(struct device *dev) > { > struct platform_device *pdev = to_platform_device(dev); > struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); > > + if (pm_runtime_status_suspended(dev)) { > + i_dev->skip_resume = true; > + return 0; > + } > + > i_dev->disable(i_dev); > i2c_dw_plat_prepare_clk(i_dev, false); > > @@ -467,27 +456,23 @@ static int dw_i2c_plat_resume(struct dev > struct platform_device *pdev = to_platform_device(dev); > struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); > > + if (!pm_runtime_status_suspended(dev)) This check is incorrect, I've just sent a v2 of the patch. Sorry about this. > + return 0; > + > + if (i_dev->skip_resume) { > + i_dev->skip_resume = false; > + return 0; > + } > + > i2c_dw_plat_prepare_clk(i_dev, true); > i_dev->init(i_dev); > > return 0; > } > > -#ifdef CONFIG_PM_SLEEP > -static int dw_i2c_plat_suspend(struct device *dev) > -{ > - pm_runtime_resume(dev); > - return dw_i2c_plat_runtime_suspend(dev); > -} > -#endif > - > static const struct dev_pm_ops dw_i2c_dev_pm_ops = { > - .prepare = dw_i2c_plat_prepare, > - .complete = dw_i2c_plat_complete, > - SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) > - SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, > - dw_i2c_plat_resume, > - NULL) > + SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) > + SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL) > }; > > #define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)