Hi, Ugh, I had a local compile-fix which I forgot to git commit -a --amend, so this version does not compile, please ignore. V2 coming up right away. Regards, Hans On 1/28/21 1:52 PM, Hans de Goede wrote: > 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 <hdegoede@xxxxxxxxxx> > --- > drivers/input/touchscreen/silead.c | 39 +++++++++++++++++++++++++++--- > 1 file changed, 35 insertions(+), 4 deletions(-) > > diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c > index 8fa2f3b7cfd8..9deef6317761 100644 > --- a/drivers/input/touchscreen/silead.c > +++ b/drivers/input/touchscreen/silead.c > @@ -20,6 +20,7 @@ > #include <linux/input/mt.h> > #include <linux/input/touchscreen.h> > #include <linux/pm.h> > +#include <linux/pm_runtime.h> > #include <linux/irq.h> > #include <linux/regulator/consumer.h> > > @@ -335,10 +336,8 @@ static int silead_ts_get_id(struct i2c_client *client) > > error = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID, > sizeof(chip_id), (u8 *)&chip_id); > - if (error < 0) { > - dev_err(&client->dev, "Chip ID read error %d\n", error); > + if (error < 0) > return error; > - } > > data->chip_id = le32_to_cpu(chip_id); > dev_info(&client->dev, "Silead chip ID: 0x%8X", data->chip_id); > @@ -351,12 +350,44 @@ static int silead_ts_setup(struct i2c_client *client) > int error; > u32 status; > > + /* > + * Some buggy BIOS-es bring up the chip in a stuck state where it blocks the I2C bus. > + * 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. The workaround below > + * runtime-suspends the chip to turn it off, leaving it up to the ACPI subsystem > + * to deal with this. > + */ > + if (device_property_read_bool(dev, "silead,stuck-controller-bug")) { > + pm_runtime_set_active(&client->dev); > + pm_runtime_enable(&client->dev); > + pm_runtime_allow(&client->dev); > + > + pm_runtime_suspend(&client->dev); > + > + dev_warn(&client->dev, FW_BUG "Stuck I2C bus: please ignore the next 'controller timed out' error\n"); > + silead_ts_get_id(client); > + > + /* The forbid will also resume the device */ > + pm_runtime_forbid(&client->dev); > + pm_runtime_disable(&client->dev); > + } > + > silead_ts_set_power(client, SILEAD_POWER_OFF); > silead_ts_set_power(client, SILEAD_POWER_ON); > > error = silead_ts_get_id(client); > - if (error) > + if (error) { > + dev_err(&client->dev, "Chip ID read error %d\n", error); > return error; > + } > > error = silead_ts_init(client); > if (error) >