From 8503ff166504272577e8f4c77d658395e744a2bb Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 20 May 2015 22:33:13 +0800 Subject: i2c: designware: Avoid unnecessary resuming during system suspend Commit 1fc2fe204cb9 ("i2c: designware: Add runtime PM hooks") adds runtime pm support using the same ops for system pm and runtime pm. When suspend to ram, the i2c host may have been runtime suspended, thus i2c_dw_disable() hangs. Previously, I fixed this issue by separating ops for system pm and runtime pm, then in the system suspend/resume path, runtime pm apis are used to ensure the device is at correct state. But as Mika Westerberg pointed out: it sounds a bit silly to resume the device just because you want to call i2c_dw_disable() for it before suspending again. He then suggested an elegant solution which keeps the device runtime suspended during system suspend with the help of 'dev->power.direct_complete'. This patch adopted this solution, and in fact Mika provided the main code. Signed-off-by: Jisheng Zhang Acked-by: Mika Westerberg Tested-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 32 +++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'drivers/i2c/busses/i2c-designware-platdrv.c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 0a80e4aabaed..4794911a6165 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -298,6 +298,22 @@ static const struct of_device_id dw_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, dw_i2c_of_match); #endif +#ifdef CONFIG_PM_SLEEP +static int dw_i2c_prepare(struct device *dev) +{ + return pm_runtime_suspended(dev); +} + +static void dw_i2c_complete(struct device *dev) +{ + if (dev->power.direct_complete) + pm_request_resume(dev); +} +#else +#define dw_i2c_prepare NULL +#define dw_i2c_complete NULL +#endif + #ifdef CONFIG_PM static int dw_i2c_suspend(struct device *dev) { @@ -322,10 +338,18 @@ static int dw_i2c_resume(struct device *dev) return 0; } -#endif -static UNIVERSAL_DEV_PM_OPS(dw_i2c_dev_pm_ops, dw_i2c_suspend, - dw_i2c_resume, NULL); +static const struct dev_pm_ops dw_i2c_dev_pm_ops = { + .prepare = dw_i2c_prepare, + .complete = dw_i2c_complete, + SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_suspend, dw_i2c_resume) + SET_RUNTIME_PM_OPS(dw_i2c_suspend, dw_i2c_resume, NULL) +}; + +#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops) +#else +#define DW_I2C_DEV_PMOPS NULL +#endif /* work with hotplug and coldplug */ MODULE_ALIAS("platform:i2c_designware"); @@ -337,7 +361,7 @@ static struct platform_driver dw_i2c_driver = { .name = "i2c_designware", .of_match_table = of_match_ptr(dw_i2c_of_match), .acpi_match_table = ACPI_PTR(dw_i2c_acpi_match), - .pm = &dw_i2c_dev_pm_ops, + .pm = DW_I2C_DEV_PMOPS, }, }; -- cgit v1.2.3-59-g8ed1b From edfc39012364a6ea8e4c7067c2655c92c2d02df4 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 17 Jun 2015 12:08:38 +0300 Subject: i2c: designware: Make sure the device is suspended before disabling runtime PM The driver calls pm_runtime_put() right before pm_runtime_disable() in its ->remove() hook to make sure clock is gated etc. However, it turns out that pm_runtime_put() only calls ->idle() hook without actually suspending anything. The following pm_runtime_disable() will prevent the driver from suspending thus leaving it "active". It is better to suspend the device synchronously to make sure it is actually suspended before disabling runtime PM from it. While there, undo call to pm_runtime_use_autosuspend(). Signed-off-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/i2c/busses/i2c-designware-platdrv.c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 4794911a6165..3dd2de31a2f8 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -281,7 +281,8 @@ static int dw_i2c_remove(struct platform_device *pdev) i2c_dw_disable(dev); - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); if (has_acpi_companion(&pdev->dev)) -- cgit v1.2.3-59-g8ed1b