Takashi Iwai 2bec1a
From e479187748a8f151a85116a7091c599b121fdea5 Mon Sep 17 00:00:00 2001
Takashi Iwai 2bec1a
From: Hans de Goede <hdegoede@redhat.com>
Takashi Iwai 2bec1a
Date: Fri, 9 Apr 2021 22:29:49 -0700
Takashi Iwai 2bec1a
Subject: [PATCH] Input: silead - add workaround for x86 BIOS-es which bring the chip up in a stuck state
Takashi Iwai 2bec1a
Git-commit: e479187748a8f151a85116a7091c599b121fdea5
Takashi Iwai 2bec1a
Patch-mainline: v5.13-rc1
Takashi Iwai 2bec1a
References: git-fixes
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
Some buggy BIOS-es bring up the touchscreen-controller in a stuck
Takashi Iwai 2bec1a
state where it blocks the I2C bus. Specifically this happens on
Takashi Iwai 2bec1a
the Jumper EZpad 7 tablet model.
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
After much poking at this problem I have found that the following steps
Takashi Iwai 2bec1a
are necessary to unstuck the chip / bus:
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
1. Turn off the Silead chip.
Takashi Iwai 2bec1a
2. Try to do an I2C transfer with the chip, this will fail in response to
Takashi Iwai 2bec1a
   which the I2C-bus-driver will call: i2c_recover_bus() which will unstuck
Takashi Iwai 2bec1a
   the I2C-bus. Note the unstuck-ing of the I2C bus only works if we first
Takashi Iwai 2bec1a
   drop the chip of the bus by turning it off.
Takashi Iwai 2bec1a
3. Turn the chip back on.
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
On the x86/ACPI systems were this problem is seen, step 1. and 3. require
Takashi Iwai 2bec1a
making ACPI calls and dealing with ACPI Power Resources. This commit adds
Takashi Iwai 2bec1a
a workaround which runtime-suspends the chip to turn it off, leaving it up
Takashi Iwai 2bec1a
to the ACPI subsystem to deal with all the ACPI specific details.
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
There is no good way to detect this bug, so the workaround gets activated
Takashi Iwai 2bec1a
by a new "silead,stuck-controller-bug" boolean device-property. Since this
Takashi Iwai 2bec1a
is only used on x86/ACPI, this will be set by model specific device-props
Takashi Iwai 2bec1a
set by drivers/platform/x86/touchscreen_dmi.c. Therefor this new
Takashi Iwai 2bec1a
device-property is not documented in the DT-bindings.
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
Dmesg will contain the following messages on systems where the workaround
Takashi Iwai 2bec1a
is activated:
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
[   54.309029] silead_ts i2c-MSSL1680:00: [Firmware Bug]: Stuck I2C bus: please ignore the next 'controller timed out' error
Takashi Iwai 2bec1a
[   55.373593] i2c_designware 808622C1:04: controller timed out
Takashi Iwai 2bec1a
[   55.582186] silead_ts i2c-MSSL1680:00: Silead chip ID: 0x80360000
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Takashi Iwai 2bec1a
Link: https://lore.kernel.org/r/20210405202745.16777-1-hdegoede@redhat.com
Takashi Iwai 2bec1a
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Takashi Iwai 2bec1a
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
---
Takashi Iwai 2bec1a
 drivers/input/touchscreen/silead.c | 44 +++++++++++++++++++++++++++---
Takashi Iwai 2bec1a
 1 file changed, 40 insertions(+), 4 deletions(-)
Takashi Iwai 2bec1a
Takashi Iwai 2bec1a
diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c
Takashi Iwai 2bec1a
index 32725d7422de..1ee760bac0cf 100644
Takashi Iwai 2bec1a
--- a/drivers/input/touchscreen/silead.c
Takashi Iwai 2bec1a
+++ b/drivers/input/touchscreen/silead.c
Takashi Iwai 2bec1a
@@ -20,6 +20,7 @@
Takashi Iwai 2bec1a
 #include <linux/input/mt.h>
Takashi Iwai 2bec1a
 #include <linux/input/touchscreen.h>
Takashi Iwai 2bec1a
 #include <linux/pm.h>
Takashi Iwai 2bec1a
+#include <linux/pm_runtime.h>
Takashi Iwai 2bec1a
 #include <linux/irq.h>
Takashi Iwai 2bec1a
 #include <linux/regulator/consumer.h>
Takashi Iwai 2bec1a
 
Takashi Iwai 2bec1a
@@ -335,10 +336,8 @@ static int silead_ts_get_id(struct i2c_client *client)
Takashi Iwai 2bec1a
 
Takashi Iwai 2bec1a
 	error = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID,
Takashi Iwai 2bec1a
 					      sizeof(chip_id), (u8 *)&chip_id);
Takashi Iwai 2bec1a
-	if (error < 0) {
Takashi Iwai 2bec1a
-		dev_err(&client->dev, "Chip ID read error %d\n", error);
Takashi Iwai 2bec1a
+	if (error < 0)
Takashi Iwai 2bec1a
 		return error;
Takashi Iwai 2bec1a
-	}
Takashi Iwai 2bec1a
 
Takashi Iwai 2bec1a
 	data->chip_id = le32_to_cpu(chip_id);
Takashi Iwai 2bec1a
 	dev_info(&client->dev, "Silead chip ID: 0x%8X", data->chip_id);
Takashi Iwai 2bec1a
@@ -351,12 +350,49 @@ static int silead_ts_setup(struct i2c_client *client)
Takashi Iwai 2bec1a
 	int error;
Takashi Iwai 2bec1a
 	u32 status;
Takashi Iwai 2bec1a
 
Takashi Iwai 2bec1a
+	/*
Takashi Iwai 2bec1a
+	 * Some buggy BIOS-es bring up the chip in a stuck state where it
Takashi Iwai 2bec1a
+	 * blocks the I2C bus. The following steps are necessary to
Takashi Iwai 2bec1a
+	 * unstuck the chip / bus:
Takashi Iwai 2bec1a
+	 * 1. Turn off the Silead chip.
Takashi Iwai 2bec1a
+	 * 2. Try to do an I2C transfer with the chip, this will fail in
Takashi Iwai 2bec1a
+	 *    response to which the I2C-bus-driver will call:
Takashi Iwai 2bec1a
+	 *    i2c_recover_bus() which will unstuck the I2C-bus. Note the
Takashi Iwai 2bec1a
+	 *    unstuck-ing of the I2C bus only works if we first drop the
Takashi Iwai 2bec1a
+	 *    chip off the bus by turning it off.
Takashi Iwai 2bec1a
+	 * 3. Turn the chip back on.
Takashi Iwai 2bec1a
+	 *
Takashi Iwai 2bec1a
+	 * On the x86/ACPI systems were this problem is seen, step 1. and
Takashi Iwai 2bec1a
+	 * 3. require making ACPI calls and dealing with ACPI Power
Takashi Iwai 2bec1a
+	 * Resources. The workaround below runtime-suspends the chip to
Takashi Iwai 2bec1a
+	 * turn it off, leaving it up to the ACPI subsystem to deal with
Takashi Iwai 2bec1a
+	 * this.
Takashi Iwai 2bec1a
+	 */
Takashi Iwai 2bec1a
+
Takashi Iwai 2bec1a
+	if (device_property_read_bool(&client->dev,
Takashi Iwai 2bec1a
+				      "silead,stuck-controller-bug")) {
Takashi Iwai 2bec1a
+		pm_runtime_set_active(&client->dev);
Takashi Iwai 2bec1a
+		pm_runtime_enable(&client->dev);
Takashi Iwai 2bec1a
+		pm_runtime_allow(&client->dev);
Takashi Iwai 2bec1a
+
Takashi Iwai 2bec1a
+		pm_runtime_suspend(&client->dev);
Takashi Iwai 2bec1a
+
Takashi Iwai 2bec1a
+		dev_warn(&client->dev, FW_BUG "Stuck I2C bus: please ignore the next 'controller timed out' error\n");
Takashi Iwai 2bec1a
+		silead_ts_get_id(client);
Takashi Iwai 2bec1a
+
Takashi Iwai 2bec1a
+		/* The forbid will also resume the device */
Takashi Iwai 2bec1a
+		pm_runtime_forbid(&client->dev);
Takashi Iwai 2bec1a
+		pm_runtime_disable(&client->dev);
Takashi Iwai 2bec1a
+	}
Takashi Iwai 2bec1a
+
Takashi Iwai 2bec1a
 	silead_ts_set_power(client, SILEAD_POWER_OFF);
Takashi Iwai 2bec1a
 	silead_ts_set_power(client, SILEAD_POWER_ON);
Takashi Iwai 2bec1a
 
Takashi Iwai 2bec1a
 	error = silead_ts_get_id(client);
Takashi Iwai 2bec1a
-	if (error)
Takashi Iwai 2bec1a
+	if (error) {
Takashi Iwai 2bec1a
+		dev_err(&client->dev, "Chip ID read error %d\n", error);
Takashi Iwai 2bec1a
 		return error;
Takashi Iwai 2bec1a
+	}
Takashi Iwai 2bec1a
 
Takashi Iwai 2bec1a
 	error = silead_ts_init(client);
Takashi Iwai 2bec1a
 	if (error)
Takashi Iwai 2bec1a
-- 
Takashi Iwai 2bec1a
2.26.2
Takashi Iwai 2bec1a