mirror of https://gitee.com/openkylin/linux.git
Merge branch 'i2c/for-current' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c fixes from Wolfram Sang: "An I2C core fix to prevent a use-after-free in a rare error path, and an I2C ACPI addition to work around broken HW/firmware related to touchscreens" * 'i2c/for-current' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: i2c: core: fix use after free in of_i2c_notify i2c: acpi: Force bus speed to 400KHz if a Silead touchscreen is present
This commit is contained in:
commit
6b27354cca
|
@ -39,6 +39,7 @@ struct i2c_acpi_lookup {
|
|||
int index;
|
||||
u32 speed;
|
||||
u32 min_speed;
|
||||
u32 force_speed;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -285,6 +286,19 @@ i2c_acpi_match_device(const struct acpi_device_id *matches,
|
|||
return acpi_match_device(matches, &client->dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = {
|
||||
/*
|
||||
* These Silead touchscreen controllers only work at 400KHz, for
|
||||
* some reason they do not work at 100KHz. On some devices the ACPI
|
||||
* tables list another device at their bus as only being capable
|
||||
* of 100KHz, testing has shown that these other devices work fine
|
||||
* at 400KHz (as can be expected of any recent i2c hw) so we force
|
||||
* the speed of the bus to 400 KHz if a Silead device is present.
|
||||
*/
|
||||
{ "MSSL1680", 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
|
||||
void *data, void **return_value)
|
||||
{
|
||||
|
@ -303,6 +317,9 @@ static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
|
|||
if (lookup->speed <= lookup->min_speed)
|
||||
lookup->min_speed = lookup->speed;
|
||||
|
||||
if (acpi_match_device_ids(adev, i2c_acpi_force_400khz_device_ids) == 0)
|
||||
lookup->force_speed = 400000;
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
|
@ -340,7 +357,16 @@ u32 i2c_acpi_find_bus_speed(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0;
|
||||
if (lookup.force_speed) {
|
||||
if (lookup.force_speed != lookup.min_speed)
|
||||
dev_warn(dev, FW_BUG "DSDT uses known not-working I2C bus speed %d, forcing it to %d\n",
|
||||
lookup.min_speed, lookup.force_speed);
|
||||
return lookup.force_speed;
|
||||
} else if (lookup.min_speed != UINT_MAX) {
|
||||
return lookup.min_speed;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed);
|
||||
|
||||
|
|
|
@ -245,14 +245,14 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
|
|||
}
|
||||
|
||||
client = of_i2c_register_device(adap, rd->dn);
|
||||
put_device(&adap->dev);
|
||||
|
||||
if (IS_ERR(client)) {
|
||||
dev_err(&adap->dev, "failed to create client for '%pOF'\n",
|
||||
rd->dn);
|
||||
put_device(&adap->dev);
|
||||
of_node_clear_flag(rd->dn, OF_POPULATED);
|
||||
return notifier_from_errno(PTR_ERR(client));
|
||||
}
|
||||
put_device(&adap->dev);
|
||||
break;
|
||||
case OF_RECONFIG_CHANGE_REMOVE:
|
||||
/* already depopulated? */
|
||||
|
|
Loading…
Reference in New Issue