[PATCH 2/2] i2c: designware: Set is_suspended flag when suspended

From: Hans de Goede
Date: Tue Dec 11 2018 - 09:29:44 EST


Set the is_suspended flag when suspended so that the i2c-core will warn
if we get called while the controller is (system-wide) suspended. Note
this also switches the runtime-pm handling in i2c-designware-master.c
to the new I2C_AQ_RUNTIME_PM flag, as this is required for the
is_suspended flag to work properly in combination with runtime-pm.

On most Intel Bay- and Cherry-Trail systems the PMIC is connected over I2C
and the PMIC is accessed through various means by the _PS0 and _PS3 ACPI
methods (power on / off methods) of various devices.

This leads to suspend/resume ordering problems where a device may be
resumed and get its _PS0 method executed before the I2C controller is
resumed. On Cherry Trail this leads to errors like these:

i2c_designware 808622C1:06: controller timed out
ACPI Error: AE_ERROR, Returned by Handler for [UserDefinedRegion]
ACPI Error: Method parse/execution failed \_SB.P18W._ON, AE_ERROR
video LNXVIDEO:00: Failed to change power state to D0

But on Bay Trail this caused I2C reads to seem to succeed, but they end
up returning wrong data, which ends up getting written back by the typical
read-modify-write cycle done to turn on various power-resources.

Debugging the problems caused by this silent data corruption is quite
nasty.

This commit properly marks the adapter as suspended until our resume
callback has run, triggering the i2c-core's WARN_ON when i2c_transfer gets
called on a suspended adapter.

Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx>
---
drivers/i2c/busses/i2c-designware-master.c | 11 ++---------
drivers/i2c/busses/i2c-designware-platdrv.c | 4 ++++
2 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 8d1bc44d2530..ebb2b8368f6f 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -424,8 +424,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)

dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);

- pm_runtime_get_sync(dev->dev);
-
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
@@ -439,7 +437,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)

ret = i2c_dw_acquire_lock(dev);
if (ret)
- goto done_nolock;
+ return ret;

ret = i2c_dw_wait_bus_not_busy(dev);
if (ret < 0)
@@ -493,11 +491,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)

done:
i2c_dw_release_lock(dev);
-
-done_nolock:
- pm_runtime_mark_last_busy(dev->dev);
- pm_runtime_put_autosuspend(dev->dev);
-
return ret;
}

@@ -507,7 +500,7 @@ static const struct i2c_algorithm i2c_dw_algo = {
};

static const struct i2c_adapter_quirks i2c_dw_quirks = {
- .flags = I2C_AQ_NO_ZERO_LEN,
+ .flags = I2C_AQ_NO_ZERO_LEN | I2C_AQ_RUNTIME_PM,
};

static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 9eaac3be1f63..e220336f34ae 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -454,6 +454,8 @@ static int dw_i2c_plat_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);

+ i_dev->adapter.is_suspended = true;
+
if (i_dev->shared_with_punit)
return 0;

@@ -472,6 +474,8 @@ static int dw_i2c_plat_resume(struct device *dev)

i_dev->init(i_dev);

+ i_dev->adapter.is_suspended = false;
+
return 0;
}

--
2.19.2


--------------5EB9D799064076BC26E44817--