[1/2] i2c: sprd: Prevent i2c accesses after suspend is called

Message ID 99031524fa147e72451d26f54b24f36093c0d3fa.1523255712.git.baolin.wang@linaro.org
State New
Headers show
Series
  • [1/2] i2c: sprd: Prevent i2c accesses after suspend is called
Related show

Commit Message

Baolin Wang April 9, 2018, 6:40 a.m.
Add one flag to indicate if the i2c controller has been in suspend state,
which can prevent i2c accesses after i2c controller is suspended following
system suspend.

Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
 drivers/i2c/busses/i2c-sprd.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)

Comments

Grygorii Strashko April 9, 2018, 8:56 p.m. | #1
On 04/09/2018 01:40 AM, Baolin Wang wrote:
> Add one flag to indicate if the i2c controller has been in suspend state,
> which can prevent i2c accesses after i2c controller is suspended following
> system suspend.

This usually indicates some bigger problem - there should be no i2c access to
 the I2C driver once it's suspended. But if happens - 
it means suspend dependencies between drivers are broken or there some 
scheduling primitives are not disabled properly.
(possible sources - kthreads, delayed works, timers or even threaded irqs)

> 
> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
> ---
>   drivers/i2c/busses/i2c-sprd.c |   16 ++++++++++++++++
>   1 file changed, 16 insertions(+)
> 
> diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c
> index 25fcc3c..2fdad63 100644
> --- a/drivers/i2c/busses/i2c-sprd.c
> +++ b/drivers/i2c/busses/i2c-sprd.c
> @@ -86,6 +86,7 @@ struct sprd_i2c {
>   	u32 count;
>   	int irq;
>   	int err;
> +	bool is_suspended;
>   };
>   
>   static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count)
> @@ -283,6 +284,9 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
>   	struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
>   	int im, ret;
>   
> +	if (i2c_dev->is_suspended)
> +		return -EBUSY;
> +
>   	ret = pm_runtime_get_sync(i2c_dev->dev);
>   	if (ret < 0)
>   		return ret;
> @@ -586,11 +590,23 @@ static int sprd_i2c_remove(struct platform_device *pdev)
>   
>   static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)
>   {
> +	struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
> +
> +	i2c_lock_adapter(&i2c_dev->adap);
> +	i2c_dev->is_suspended = true;
> +	i2c_unlock_adapter(&i2c_dev->adap);
> +
>   	return pm_runtime_force_suspend(pdev);
>   }
>   
>   static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)
>   {
> +	struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
> +
> +	i2c_lock_adapter(&i2c_dev->adap);
> +	i2c_dev->is_suspended = false;
> +	i2c_unlock_adapter(&i2c_dev->adap);
> +
>   	return pm_runtime_force_resume(pdev);
>   }
>   
>
Baolin Wang April 10, 2018, 8:08 a.m. | #2
Hi Grygorii,

On 10 April 2018 at 04:56, Grygorii Strashko <grygorii.strashko@ti.com> wrote:
>
>
> On 04/09/2018 01:40 AM, Baolin Wang wrote:
>> Add one flag to indicate if the i2c controller has been in suspend state,
>> which can prevent i2c accesses after i2c controller is suspended following
>> system suspend.
>
> This usually indicates some bigger problem - there should be no i2c access to
>  the I2C driver once it's suspended. But if happens -
> it means suspend dependencies between drivers are broken or there some
> scheduling primitives are not disabled properly.

Correct. But on Spreadtrum platform there are some I2C slave devices
(like some sensors), they do not care the system suspend, and we can
not control them. So we can add this flag to make sure I2C driver can
be accessed safely no matter considering other slaves's dependency
like other drivers did (i2c-tegra.c, i2c-brcmstb.c and i2c-zx2967.c).
Thanks for your comments.

Patch

diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c
index 25fcc3c..2fdad63 100644
--- a/drivers/i2c/busses/i2c-sprd.c
+++ b/drivers/i2c/busses/i2c-sprd.c
@@ -86,6 +86,7 @@  struct sprd_i2c {
 	u32 count;
 	int irq;
 	int err;
+	bool is_suspended;
 };
 
 static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count)
@@ -283,6 +284,9 @@  static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
 	struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
 	int im, ret;
 
+	if (i2c_dev->is_suspended)
+		return -EBUSY;
+
 	ret = pm_runtime_get_sync(i2c_dev->dev);
 	if (ret < 0)
 		return ret;
@@ -586,11 +590,23 @@  static int sprd_i2c_remove(struct platform_device *pdev)
 
 static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)
 {
+	struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+
+	i2c_lock_adapter(&i2c_dev->adap);
+	i2c_dev->is_suspended = true;
+	i2c_unlock_adapter(&i2c_dev->adap);
+
 	return pm_runtime_force_suspend(pdev);
 }
 
 static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)
 {
+	struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+
+	i2c_lock_adapter(&i2c_dev->adap);
+	i2c_dev->is_suspended = false;
+	i2c_unlock_adapter(&i2c_dev->adap);
+
 	return pm_runtime_force_resume(pdev);
 }