From 254b897006d6f84a362820b7d06ba9ad29a5a557 Mon Sep 17 00:00:00 2001 From: Youngmin Nam Date: Mon, 14 Nov 2016 20:36:53 +0900 Subject: [PATCH] [COMMON] i2c: exynos5: call runtime pm callback directly in suspend / resume function If there is transfer request in system suspend stage, the runtime suspend callback will not be called after transfer completion because the runtime suspend callback of pm_runtime_put_autosuspend() is queued into workqueue as freezable. After that, without calling runtime suspend callback before system suspend, it can cause lock up by central sequencer stuck because clock request is high. To fix this issue, runtime pm callback should be called directly in suspend / resume function if runtime pm status is not "suspended". Change-Id: I5c5751debb577dfce2a838212d45c72b2c2f4a05 Signed-off-by: Youngmin Nam --- drivers/i2c/busses/i2c-exynos5.c | 66 ++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index a98c2586a6a7..86d68a330316 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -1119,6 +1119,35 @@ static int exynos5_i2c_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int exynos5_i2c_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos5_i2c *i2c = platform_get_drvdata(pdev); + + clk_disable(i2c->clk); + exynos_update_ip_idle_status(i2c->idle_ip_index, 1); + + return 0; +} + +static int exynos5_i2c_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos5_i2c *i2c = platform_get_drvdata(pdev); + int ret = 0; + + exynos_update_ip_idle_status(i2c->idle_ip_index, 0); + ret = clk_enable(i2c->clk); + if (ret) { + exynos_update_ip_idle_status(i2c->idle_ip_index, 1); + return ret; + } + + return 0; +} +#endif /* CONFIG_PM */ + #ifdef CONFIG_PM static int exynos5_i2c_suspend_noirq(struct device *dev) { @@ -1141,6 +1170,10 @@ static int exynos5_i2c_suspend_noirq(struct device *dev) clk_disable(i2c->clk); exynos_update_ip_idle_status(i2c->idle_ip_index, 1); #endif + + if (!pm_runtime_status_suspended(dev)) + exynos5_i2c_runtime_suspend(dev); + i2c->suspended = 1; i2c_unlock_adapter(&i2c->adap); @@ -1154,6 +1187,10 @@ static int exynos5_i2c_resume_noirq(struct device *dev) int ret = 0; i2c_lock_adapter(&i2c->adap); + + if (!pm_runtime_status_suspended(dev)) + exynos5_i2c_runtime_resume(dev); + exynos_update_ip_idle_status(i2c->idle_ip_index, 0); ret = clk_enable(i2c->clk); if (ret) { @@ -1182,35 +1219,6 @@ static int exynos5_i2c_resume_noirq(struct device *dev) } #endif -#ifdef CONFIG_PM -static int exynos5_i2c_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct exynos5_i2c *i2c = platform_get_drvdata(pdev); - - clk_disable(i2c->clk); - exynos_update_ip_idle_status(i2c->idle_ip_index, 1); - - return 0; -} - -static int exynos5_i2c_runtime_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct exynos5_i2c *i2c = platform_get_drvdata(pdev); - int ret = 0; - - exynos_update_ip_idle_status(i2c->idle_ip_index, 0); - ret = clk_enable(i2c->clk); - if (ret) { - exynos_update_ip_idle_status(i2c->idle_ip_index, 1); - return ret; - } - - return 0; -} -#endif /* CONFIG_PM */ - static const struct dev_pm_ops exynos5_i2c_pm = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq, exynos5_i2c_resume_noirq) -- 2.20.1