Merge branches 'release' and 'gpe-ack' into release
[pandora-kernel.git] / drivers / acpi / osl.c
index c2bed56..07c18db 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/interrupt.h>
 #include <linux/kmod.h>
 #include <linux/delay.h>
+#include <linux/dmi.h>
 #include <linux/workqueue.h>
 #include <linux/nmi.h>
 #include <linux/acpi.h>
@@ -71,6 +72,60 @@ static unsigned int acpi_irq_irq;
 static acpi_osd_handler acpi_irq_handler;
 static void *acpi_irq_context;
 static struct workqueue_struct *kacpid_wq;
+static struct workqueue_struct *kacpi_notify_wq;
+
+#define        OSI_STRING_LENGTH_MAX 64        /* arbitrary */
+static char osi_additional_string[OSI_STRING_LENGTH_MAX];
+
+/*
+ * "Ode to _OSI(Linux)"
+ *
+ * osi_linux -- Control response to BIOS _OSI(Linux) query.
+ *
+ * As Linux evolves, the features that it supports change.
+ * So an OSI string such as "Linux" is not specific enough
+ * to be useful across multiple versions of Linux.  It
+ * doesn't identify any particular feature, interface,
+ * or even any particular version of Linux...
+ *
+ * Unfortunately, Linux-2.6.22 and earlier responded "yes"
+ * to a BIOS _OSI(Linux) query.  When
+ * a reference mobile BIOS started using it, its use
+ * started to spread to many vendor platforms.
+ * As it is not supportable, we need to halt that spread.
+ *
+ * Today, most BIOS references to _OSI(Linux) are noise --
+ * they have no functional effect and are just dead code
+ * carried over from the reference BIOS.
+ *
+ * The next most common case is that _OSI(Linux) harms Linux,
+ * usually by causing the BIOS to follow paths that are
+ * not tested during Windows validation.
+ *
+ * Finally, there is a short list of platforms
+ * where OSI(Linux) benefits Linux.
+ *
+ * In Linux-2.6.23, OSI(Linux) is first disabled by default.
+ * DMI is used to disable the dmesg warning about OSI(Linux)
+ * on platforms where it is known to have no effect.
+ * But a dmesg warning remains for systems where
+ * we do not know if OSI(Linux) is good or bad for the system.
+ * DMI is also used to enable OSI(Linux) for the machines
+ * that are known to need it.
+ *
+ * BIOS writers should NOT query _OSI(Linux) on future systems.
+ * It will be ignored by default, and to get Linux to
+ * not ignore it will require a kernel source update to
+ * add a DMI entry, or a boot-time "acpi_osi=Linux" invocation.
+ */
+#define OSI_LINUX_ENABLE 0
+
+static struct osi_linux {
+       unsigned int    enable:1;
+       unsigned int    dmi:1;
+       unsigned int    cmdline:1;
+       unsigned int    known:1;
+} osi_linux = { OSI_LINUX_ENABLE, 0, 0, 0};
 
 static void __init acpi_request_region (struct acpi_generic_address *addr,
        unsigned int length, char *desc)
@@ -120,7 +175,7 @@ static int __init acpi_reserve_resources(void)
 }
 device_initcall(acpi_reserve_resources);
 
-acpi_status acpi_os_initialize(void)
+acpi_status __init acpi_os_initialize(void)
 {
        return AE_OK;
 }
@@ -137,8 +192,9 @@ acpi_status acpi_os_initialize1(void)
                return AE_NULL_ENTRY;
        }
        kacpid_wq = create_singlethread_workqueue("kacpid");
+       kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
        BUG_ON(!kacpid_wq);
-
+       BUG_ON(!kacpi_notify_wq);
        return AE_OK;
 }
 
@@ -150,6 +206,7 @@ acpi_status acpi_os_terminate(void)
        }
 
        destroy_workqueue(kacpid_wq);
+       destroy_workqueue(kacpi_notify_wq);
 
        return AE_OK;
 }
@@ -193,8 +250,12 @@ acpi_physical_address __init acpi_os_get_root_pointer(void)
                               "System description tables not found\n");
                        return 0;
                }
-       } else
-               return acpi_find_rsdp();
+       } else {
+               acpi_physical_address pa = 0;
+
+               acpi_find_root_pointer(&pa);
+               return pa;
+       }
 }
 
 void __iomem *acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
@@ -373,17 +434,14 @@ acpi_status acpi_os_read_port(acpi_io_address port, u32 * value, u32 width)
        if (!value)
                value = &dummy;
 
-       switch (width) {
-       case 8:
+       *value = 0;
+       if (width <= 8) {
                *(u8 *) value = inb(port);
-               break;
-       case 16:
+       } else if (width <= 16) {
                *(u16 *) value = inw(port);
-               break;
-       case 32:
+       } else if (width <= 32) {
                *(u32 *) value = inl(port);
-               break;
-       default:
+       } else {
                BUG();
        }
 
@@ -394,17 +452,13 @@ EXPORT_SYMBOL(acpi_os_read_port);
 
 acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width)
 {
-       switch (width) {
-       case 8:
+       if (width <= 8) {
                outb(value, port);
-               break;
-       case 16:
+       } else if (width <= 16) {
                outw(value, port);
-               break;
-       case 32:
+       } else if (width <= 32) {
                outl(value, port);
-               break;
-       default:
+       } else {
                BUG();
        }
 
@@ -603,14 +657,12 @@ void acpi_os_derive_pci_id(acpi_handle rhandle,   /* upper bound  */
 static void acpi_os_execute_deferred(struct work_struct *work)
 {
        struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
-
        if (!dpc) {
                printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
                return;
        }
 
        dpc->function(dpc->context);
-
        kfree(dpc);
 
        return;
@@ -636,15 +688,13 @@ acpi_status acpi_os_execute(acpi_execute_type type,
 {
        acpi_status status = AE_OK;
        struct acpi_os_dpc *dpc;
-
-       ACPI_FUNCTION_TRACE("os_queue_for_execution");
-
+       struct workqueue_struct *queue;
        ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
                          "Scheduling function [%p(%p)] for deferred execution.\n",
                          function, context));
 
        if (!function)
-               return_ACPI_STATUS(AE_BAD_PARAMETER);
+               return AE_BAD_PARAMETER;
 
        /*
         * Allocate/initialize DPC structure.  Note that this memory will be
@@ -663,13 +713,13 @@ acpi_status acpi_os_execute(acpi_execute_type type,
        dpc->context = context;
 
        INIT_WORK(&dpc->work, acpi_os_execute_deferred);
-       if (!queue_work(kacpid_wq, &dpc->work)) {
+       queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq;
+       if (!queue_work(queue, &dpc->work)) {
                ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "Call to queue_work() failed.\n"));
-               kfree(dpc);
+                         "Call to queue_work() failed.\n"));
                status = AE_ERROR;
+               kfree(dpc);
        }
-
        return_ACPI_STATUS(status);
 }
 
@@ -935,20 +985,62 @@ static int __init acpi_os_name_setup(char *str)
 
 __setup("acpi_os_name=", acpi_os_name_setup);
 
+static void __init set_osi_linux(unsigned int enable)
+{
+       if (osi_linux.enable != enable) {
+               osi_linux.enable = enable;
+               printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n",
+                       enable ? "Add": "Delet");
+       }
+       return;
+}
+
+static void __init acpi_cmdline_osi_linux(unsigned int enable)
+{
+       osi_linux.cmdline = 1;  /* cmdline set the default */
+       set_osi_linux(enable);
+
+       return;
+}
+
+void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d)
+{
+       osi_linux.dmi = 1;      /* DMI knows that this box asks OSI(Linux) */
+
+       printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident);
+
+       if (enable == -1)
+               return;
+
+       osi_linux.known = 1;    /* DMI knows which OSI(Linux) default needed */
+
+       set_osi_linux(enable);
+
+       return;
+}
+
 /*
- * _OSI control
+ * Modify the list of "OS Interfaces" reported to BIOS via _OSI
+ *
  * empty string disables _OSI
- * TBD additional string adds to _OSI
+ * string starting with '!' disables that string
+ * otherwise string is added to list, augmenting built-in strings
  */
 static int __init acpi_osi_setup(char *str)
 {
        if (str == NULL || *str == '\0') {
                printk(KERN_INFO PREFIX "_OSI method disabled\n");
                acpi_gbl_create_osi_method = FALSE;
-       } else {
-               /* TBD */
-               printk(KERN_ERR PREFIX "_OSI additional string ignored -- %s\n",
-                      str);
+       } else if (!strcmp("!Linux", str)) {
+               acpi_cmdline_osi_linux(0);      /* !enable */
+       } else if (*str == '!') {
+               if (acpi_osi_invalidate(++str) == AE_OK)
+                       printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str);
+       } else if (!strcmp("Linux", str)) {
+               acpi_cmdline_osi_linux(1);      /* enable */
+       } else if (*osi_additional_string == '\0') {
+               strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX);
+               printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str);
        }
 
        return 1;
@@ -988,14 +1080,6 @@ static int __init acpi_wake_gpes_always_on_setup(char *str)
 
 __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup);
 
-/*
- * max_cstate is defined in the base kernel so modules can
- * change it w/o depending on the state of the processor module.
- */
-unsigned int max_cstate = ACPI_PROCESSOR_MAX_POWER;
-
-EXPORT_SYMBOL(max_cstate);
-
 /*
  * Acquire a spinlock.
  *
@@ -1038,7 +1122,7 @@ void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags)
 acpi_status
 acpi_os_create_cache(char *name, u16 size, u16 depth, acpi_cache_t ** cache)
 {
-       *cache = kmem_cache_create(name, size, 0, 0, NULL, NULL);
+       *cache = kmem_cache_create(name, size, 0, 0, NULL);
        if (*cache == NULL)
                return AE_ERROR;
        else
@@ -1102,6 +1186,34 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object)
        return (AE_OK);
 }
 
+/**
+ *     acpi_dmi_dump - dump DMI slots needed for blacklist entry
+ *
+ *     Returns 0 on success
+ */
+static int acpi_dmi_dump(void)
+{
+
+       if (!dmi_available)
+               return -1;
+
+       printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n",
+               dmi_get_system_info(DMI_SYS_VENDOR));
+       printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n",
+               dmi_get_system_info(DMI_PRODUCT_NAME));
+       printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n",
+               dmi_get_system_info(DMI_PRODUCT_VERSION));
+       printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n",
+               dmi_get_system_info(DMI_BOARD_NAME));
+       printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n",
+               dmi_get_system_info(DMI_BIOS_VENDOR));
+       printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n",
+               dmi_get_system_info(DMI_BIOS_DATE));
+
+       return 0;
+}
+
+
 /******************************************************************************
  *
  * FUNCTION:    acpi_os_validate_interface
@@ -1118,11 +1230,37 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object)
 acpi_status
 acpi_os_validate_interface (char *interface)
 {
+       if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX))
+               return AE_OK;
+       if (!strcmp("Linux", interface)) {
+
+               printk(KERN_NOTICE PREFIX
+                       "BIOS _OSI(Linux) query %s%s\n",
+                       osi_linux.enable ? "honored" : "ignored",
+                       osi_linux.cmdline ? " via cmdline" :
+                       osi_linux.dmi ? " via DMI" : "");
+
+               if (!osi_linux.dmi) {
+                       if (acpi_dmi_dump())
+                               printk(KERN_NOTICE PREFIX
+                                       "[please extract dmidecode output]\n");
+                       printk(KERN_NOTICE PREFIX
+                               "Please send DMI info above to "
+                               "linux-acpi@vger.kernel.org\n");
+               }
+               if (!osi_linux.known && !osi_linux.cmdline) {
+                       printk(KERN_NOTICE PREFIX
+                               "If \"acpi_osi=%sLinux\" works better, "
+                               "please notify linux-acpi@vger.kernel.org\n",
+                               osi_linux.enable ? "!" : "");
+               }
 
-    return AE_SUPPORT;
+               if (osi_linux.enable)
+                       return AE_OK;
+       }
+       return AE_SUPPORT;
 }
 
-
 /******************************************************************************
  *
  * FUNCTION:    acpi_os_validate_address
@@ -1149,5 +1287,4 @@ acpi_os_validate_address (
     return AE_OK;
 }
 
-
 #endif