diff options
Diffstat (limited to 'drivers/i2c/i2c-core-acpi.c')
-rw-r--r-- | drivers/i2c/i2c-core-acpi.c | 49 |
1 files changed, 33 insertions, 16 deletions
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 4dbbc9a35f65..8f3dbc97a057 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -39,6 +39,7 @@ struct i2c_acpi_lookup { int index; u32 speed; u32 min_speed; + u32 force_speed; }; /** @@ -224,7 +225,7 @@ static void i2c_acpi_register_device(struct i2c_adapter *adapter, adev->power.flags.ignore_parent = true; acpi_device_set_enumerated(adev); - if (!i2c_new_device(adapter, info)) { + if (IS_ERR(i2c_new_client_device(adapter, info))) { adev->power.flags.ignore_parent = false; dev_err(&adapter->dev, "failed to add I2C device %s from ACPI\n", @@ -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); @@ -354,17 +380,13 @@ static int i2c_acpi_find_match_adapter(struct device *dev, const void *data) return ACPI_HANDLE(dev) == (acpi_handle)data; } -static int i2c_acpi_find_match_device(struct device *dev, const void *data) -{ - return ACPI_COMPANION(dev) == data; -} - struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) { struct device *dev; dev = bus_find_device(&i2c_bus_type, NULL, handle, i2c_acpi_find_match_adapter); + return dev ? i2c_verify_adapter(dev) : NULL; } EXPORT_SYMBOL_GPL(i2c_acpi_find_adapter_by_handle); @@ -373,8 +395,7 @@ static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) { struct device *dev; - dev = bus_find_device(&i2c_bus_type, NULL, adev, - i2c_acpi_find_match_device); + dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); return dev ? i2c_verify_client(dev) : NULL; } @@ -430,7 +451,8 @@ struct notifier_block i2c_acpi_notifier = { * resources, in that case this function can be used to create an i2c-client * for other I2cSerialBus resources in the Current Resource Settings table. * - * Also see i2c_new_device, which this function calls to create the i2c-client. + * Also see i2c_new_client_device, which this function calls to create the + * i2c-client. * * Returns a pointer to the new i2c-client, or error pointer in case of failure. * Specifically, -EPROBE_DEFER is returned if the adapter is not found. @@ -440,7 +462,6 @@ struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, { struct i2c_acpi_lookup lookup; struct i2c_adapter *adapter; - struct i2c_client *client; struct acpi_device *adev; LIST_HEAD(resource_list); int ret; @@ -468,11 +489,7 @@ struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, if (!adapter) return ERR_PTR(-EPROBE_DEFER); - client = i2c_new_device(adapter, info); - if (!client) - return ERR_PTR(-ENODEV); - - return client; + return i2c_new_client_device(adapter, info); } EXPORT_SYMBOL_GPL(i2c_acpi_new_device); |