]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/i2c/i2c-core-acpi.c
i2c: acpi: Force bus speed to 400KHz if a Silead touchscreen is present
[linux.git] / drivers / i2c / i2c-core-acpi.c
index 9cb2aa1e20ef31a2707a5e82678cf15264b2a572..62a1c92ab803d5a124d5a9403c97b2878a4d64c2 100644 (file)
@@ -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);