Skip to content

Commit 87264fb

Browse files
jwrdegoedegregkh
authored andcommitted
Input: silead - add workaround for x86 BIOS-es which bring the chip up in a stuck state
[ Upstream commit e479187 ] Some buggy BIOS-es bring up the touchscreen-controller in a stuck state where it blocks the I2C bus. Specifically this happens on the Jumper EZpad 7 tablet model. After much poking at this problem I have found that the following steps are necessary to unstuck the chip / bus: 1. Turn off the Silead chip. 2. Try to do an I2C transfer with the chip, this will fail in response to which the I2C-bus-driver will call: i2c_recover_bus() which will unstuck the I2C-bus. Note the unstuck-ing of the I2C bus only works if we first drop the chip of the bus by turning it off. 3. Turn the chip back on. On the x86/ACPI systems were this problem is seen, step 1. and 3. require making ACPI calls and dealing with ACPI Power Resources. This commit adds a workaround which runtime-suspends the chip to turn it off, leaving it up to the ACPI subsystem to deal with all the ACPI specific details. There is no good way to detect this bug, so the workaround gets activated by a new "silead,stuck-controller-bug" boolean device-property. Since this is only used on x86/ACPI, this will be set by model specific device-props set by drivers/platform/x86/touchscreen_dmi.c. Therefor this new device-property is not documented in the DT-bindings. Dmesg will contain the following messages on systems where the workaround is activated: [ 54.309029] silead_ts i2c-MSSL1680:00: [Firmware Bug]: Stuck I2C bus: please ignore the next 'controller timed out' error [ 55.373593] i2c_designware 808622C1:04: controller timed out [ 55.582186] silead_ts i2c-MSSL1680:00: Silead chip ID: 0x80360000 Signed-off-by: Hans de Goede <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Dmitry Torokhov <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent c2742ef commit 87264fb

File tree

1 file changed

+40
-4
lines changed

1 file changed

+40
-4
lines changed

drivers/input/touchscreen/silead.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/input/mt.h>
2121
#include <linux/input/touchscreen.h>
2222
#include <linux/pm.h>
23+
#include <linux/pm_runtime.h>
2324
#include <linux/irq.h>
2425
#include <linux/regulator/consumer.h>
2526

@@ -335,10 +336,8 @@ static int silead_ts_get_id(struct i2c_client *client)
335336

336337
error = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID,
337338
sizeof(chip_id), (u8 *)&chip_id);
338-
if (error < 0) {
339-
dev_err(&client->dev, "Chip ID read error %d\n", error);
339+
if (error < 0)
340340
return error;
341-
}
342341

343342
data->chip_id = le32_to_cpu(chip_id);
344343
dev_info(&client->dev, "Silead chip ID: 0x%8X", data->chip_id);
@@ -351,12 +350,49 @@ static int silead_ts_setup(struct i2c_client *client)
351350
int error;
352351
u32 status;
353352

353+
/*
354+
* Some buggy BIOS-es bring up the chip in a stuck state where it
355+
* blocks the I2C bus. The following steps are necessary to
356+
* unstuck the chip / bus:
357+
* 1. Turn off the Silead chip.
358+
* 2. Try to do an I2C transfer with the chip, this will fail in
359+
* response to which the I2C-bus-driver will call:
360+
* i2c_recover_bus() which will unstuck the I2C-bus. Note the
361+
* unstuck-ing of the I2C bus only works if we first drop the
362+
* chip off the bus by turning it off.
363+
* 3. Turn the chip back on.
364+
*
365+
* On the x86/ACPI systems were this problem is seen, step 1. and
366+
* 3. require making ACPI calls and dealing with ACPI Power
367+
* Resources. The workaround below runtime-suspends the chip to
368+
* turn it off, leaving it up to the ACPI subsystem to deal with
369+
* this.
370+
*/
371+
372+
if (device_property_read_bool(&client->dev,
373+
"silead,stuck-controller-bug")) {
374+
pm_runtime_set_active(&client->dev);
375+
pm_runtime_enable(&client->dev);
376+
pm_runtime_allow(&client->dev);
377+
378+
pm_runtime_suspend(&client->dev);
379+
380+
dev_warn(&client->dev, FW_BUG "Stuck I2C bus: please ignore the next 'controller timed out' error\n");
381+
silead_ts_get_id(client);
382+
383+
/* The forbid will also resume the device */
384+
pm_runtime_forbid(&client->dev);
385+
pm_runtime_disable(&client->dev);
386+
}
387+
354388
silead_ts_set_power(client, SILEAD_POWER_OFF);
355389
silead_ts_set_power(client, SILEAD_POWER_ON);
356390

357391
error = silead_ts_get_id(client);
358-
if (error)
392+
if (error) {
393+
dev_err(&client->dev, "Chip ID read error %d\n", error);
359394
return error;
395+
}
360396

361397
error = silead_ts_init(client);
362398
if (error)

0 commit comments

Comments
 (0)