From ffc12e054cb317ccd44facf046b259ce2da204de Mon Sep 17 00:00:00 2001 From: David Wu Date: Wed, 2 May 2018 15:26:52 +0800 Subject: [PATCH] i2c: rk3x: Add "suspended" flag to forbid access I2C bus during suspend/resume noirq Add "suspended" flag in suspend_noirq()/resume_noirq() callback to prevent new i2c job started, and use i2c_lock_adapter() to wait for current i2c transfer finished. If any i2c client try to access I2C after suspend_noirq() or before resume_noirq() callback, return the error, and they should fix it, not to start i2c access at this moment. Change-Id: Idd1142058d10547d085895a498201c2ade6b9e96 Signed-off-by: David Wu --- drivers/i2c/busses/i2c-rk3x.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 4c6e3d2433dd..fb7635fb9dae 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -223,6 +223,7 @@ struct rk3x_i2c { enum rk3x_i2c_state state; unsigned int processed; int error; + unsigned int suspended:1; struct notifier_block i2c_restart_nb; }; @@ -1058,6 +1059,9 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap, int ret = 0; int i; + if (i2c->suspended) + return -EACCES; + spin_lock_irqsave(&i2c->lock, flags); clk_enable(i2c->clk); @@ -1152,12 +1156,35 @@ static int rk3x_i2c_restart_notify(struct notifier_block *this, return NOTIFY_DONE; } -static __maybe_unused int rk3x_i2c_resume(struct device *dev) +static __maybe_unused int rk3x_i2c_suspend_noirq(struct device *dev) +{ + struct rk3x_i2c *i2c = dev_get_drvdata(dev); + + /* + * Below code is needed only to ensure that there are no + * activities on I2C bus. if at this moment any driver + * is trying to use I2C bus - this may cause i2c timeout. + * + * So forbid access to I2C device using i2c->suspended flag. + */ + i2c_lock_adapter(&i2c->adap); + i2c->suspended = 1; + i2c_unlock_adapter(&i2c->adap); + + return 0; +} + +static __maybe_unused int rk3x_i2c_resume_noirq(struct device *dev) { struct rk3x_i2c *i2c = dev_get_drvdata(dev); rk3x_i2c_adapt_div(i2c, clk_get_rate(i2c->clk)); + /* Allow access to I2C bus */ + i2c_lock_adapter(&i2c->adap); + i2c->suspended = 0; + i2c_unlock_adapter(&i2c->adap); + return 0; } @@ -1398,7 +1425,10 @@ static int rk3x_i2c_remove(struct platform_device *pdev) return 0; } -static SIMPLE_DEV_PM_OPS(rk3x_i2c_pm_ops, NULL, rk3x_i2c_resume); +const static struct dev_pm_ops rk3x_i2c_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rk3x_i2c_suspend_noirq, + rk3x_i2c_resume_noirq) +}; static struct platform_driver rk3x_i2c_driver = { .probe = rk3x_i2c_probe,