]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/acpi/ec.c
ACPI: EC: Don't trust ECDT tables from ASUS
[linux-2.6-omap-h63xx.git] / drivers / acpi / ec.c
index 30f3ef236ecb1b354548c5bb2570866eaa8ec6be..a2b82c90a6838eaff23a83cd30e741e0c3422e2a 100644 (file)
@@ -42,7 +42,6 @@
 #include <asm/io.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
-#include <acpi/actypes.h>
 
 #define ACPI_EC_CLASS                  "embedded_controller"
 #define ACPI_EC_DEVICE_NAME            "Embedded Controller"
@@ -121,31 +120,6 @@ static struct acpi_ec {
        spinlock_t curr_lock;
 } *boot_ec, *first_ec;
 
-/* 
- * Some Asus system have exchanged ECDT data/command IO addresses.
- */
-static int print_ecdt_error(const struct dmi_system_id *id)
-{
-       printk(KERN_NOTICE PREFIX "%s detected - "
-               "ECDT has exchanged control/data I/O address\n",
-               id->ident);
-       return 0;
-}
-
-static struct dmi_system_id __cpuinitdata ec_dmi_table[] = {
-       {
-       print_ecdt_error, "Asus L4R", {
-       DMI_MATCH(DMI_BIOS_VERSION, "1008.006"),
-       DMI_MATCH(DMI_PRODUCT_NAME, "L4R"),
-       DMI_MATCH(DMI_BOARD_NAME, "L4R") }, NULL},
-       {
-       print_ecdt_error, "Asus M6R", {
-       DMI_MATCH(DMI_BIOS_VERSION, "0207"),
-       DMI_MATCH(DMI_PRODUCT_NAME, "M6R"),
-       DMI_MATCH(DMI_BOARD_NAME, "M6R") }, NULL},
-       {},
-};
-
 /* --------------------------------------------------------------------------
                              Transaction Management
    -------------------------------------------------------------------------- */
@@ -370,7 +344,7 @@ unlock:
  * Note: samsung nv5000 doesn't work with ec burst mode.
  * http://bugzilla.kernel.org/show_bug.cgi?id=4980
  */
-int acpi_ec_burst_enable(struct acpi_ec *ec)
+static int acpi_ec_burst_enable(struct acpi_ec *ec)
 {
        u8 d;
        struct transaction t = {.command = ACPI_EC_BURST_ENABLE,
@@ -380,7 +354,7 @@ int acpi_ec_burst_enable(struct acpi_ec *ec)
        return acpi_ec_transaction(ec, &t, 0);
 }
 
-int acpi_ec_burst_disable(struct acpi_ec *ec)
+static int acpi_ec_burst_disable(struct acpi_ec *ec)
 {
        struct transaction t = {.command = ACPI_EC_BURST_DISABLE,
                                .wdata = NULL, .rdata = NULL,
@@ -756,10 +730,15 @@ static acpi_status
 acpi_ec_register_query_methods(acpi_handle handle, u32 level,
                               void *context, void **return_value)
 {
-       struct acpi_namespace_node *node = handle;
+       char node_name[5];
+       struct acpi_buffer buffer = { sizeof(node_name), node_name };
        struct acpi_ec *ec = context;
        int value = 0;
-       if (sscanf(node->name.ascii, "_Q%x", &value) == 1) {
+       acpi_status status;
+
+       status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
+
+       if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1) {
                acpi_ec_add_query_handler(ec, value, handle, NULL, NULL);
        }
        return AE_OK;
@@ -978,8 +957,8 @@ static const struct acpi_device_id ec_device_ids[] = {
 
 int __init acpi_ec_ecdt_probe(void)
 {
-       int ret;
        acpi_status status;
+       struct acpi_ec *saved_ec = NULL;
        struct acpi_table_ecdt *ecdt_ptr;
 
        boot_ec = make_acpi_ec();
@@ -994,42 +973,55 @@ int __init acpi_ec_ecdt_probe(void)
                pr_info(PREFIX "EC description table is found, configuring boot EC\n");
                boot_ec->command_addr = ecdt_ptr->control.address;
                boot_ec->data_addr = ecdt_ptr->data.address;
-               if (dmi_check_system(ec_dmi_table)) {
-                       /*
-                        * If the board falls into ec_dmi_table, it means
-                        * that ECDT table gives the incorrect command/status
-                        * & data I/O address. Just fix it.
-                        */
-                       boot_ec->data_addr = ecdt_ptr->control.address;
-                       boot_ec->command_addr = ecdt_ptr->data.address;
-               }
                boot_ec->gpe = ecdt_ptr->gpe;
                boot_ec->handle = ACPI_ROOT_OBJECT;
                acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle);
+               /* Don't trust ECDT, which comes from ASUSTek */
+               if (!dmi_name_in_vendors("ASUS"))
+                       goto install;
+               saved_ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL);
+               if (!saved_ec)
+                       return -ENOMEM;
+               memcpy(&saved_ec, boot_ec, sizeof(saved_ec));
+       /* fall through */
+       }
+       /* This workaround is needed only on some broken machines,
+        * which require early EC, but fail to provide ECDT */
+       printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n");
+       status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device,
+                                       boot_ec, NULL);
+       /* Check that acpi_get_devices actually find something */
+       if (ACPI_FAILURE(status) || !boot_ec->handle)
+               goto error;
+       if (saved_ec) {
+               /* try to find good ECDT from ASUSTek */
+               if (saved_ec->command_addr != boot_ec->command_addr ||
+                   saved_ec->data_addr != boot_ec->data_addr ||
+                   saved_ec->gpe != boot_ec->gpe ||
+                   saved_ec->handle != boot_ec->handle)
+                       pr_info(PREFIX "ASUSTek keeps feeding us with broken "
+                       "ECDT tables, which are very hard to workaround. "
+                       "Trying to use DSDT EC info instead. Please send "
+                       "output of acpidump to linux-acpi@vger.kernel.org\n");
+               kfree(saved_ec);
+               saved_ec = NULL;
        } else {
-               /* This workaround is needed only on some broken machines,
-                * which require early EC, but fail to provide ECDT */
-               acpi_handle x;
-               printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n");
-               status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device,
-                                               boot_ec, NULL);
-               /* Check that acpi_get_devices actually find something */
-               if (ACPI_FAILURE(status) || !boot_ec->handle)
-                       goto error;
                /* We really need to limit this workaround, the only ASUS,
-                * which needs it, has fake EC._INI method, so use it as flag.
-                * Keep boot_ec struct as it will be needed soon.
-                */
-               if (ACPI_FAILURE(acpi_get_handle(boot_ec->handle, "_INI", &x)))
+               * which needs it, has fake EC._INI method, so use it as flag.
+               * Keep boot_ec struct as it will be needed soon.
+               */
+               acpi_handle dummy;
+               if (!dmi_name_in_vendors("ASUS") ||
+                   ACPI_FAILURE(acpi_get_handle(boot_ec->handle, "_INI",
+                                                       &dummy)))
                        return -ENODEV;
        }
-
-       ret = ec_install_handlers(boot_ec);
-       if (!ret) {
+install:
+       if (!ec_install_handlers(boot_ec)) {
                first_ec = boot_ec;
                return 0;
        }
-      error:
+error:
        kfree(boot_ec);
        boot_ec = NULL;
        return -ENODEV;