ACPI: Clarify resource conflict message
[safe/jmp/linux-2.6] / drivers / acpi / osl.c
index 47dfde9..7c1c59e 100644 (file)
@@ -4,6 +4,8 @@
  *  Copyright (C) 2000       Andrew Henroid
  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (c) 2008 Intel Corporation
+ *   Author: Matthew Wilcox <willy@linux.intel.com>
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/pci.h>
-#include <linux/smp_lock.h>
 #include <linux/interrupt.h>
 #include <linux/kmod.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/nmi.h>
-#include <linux/kthread.h>
-#include <acpi/acpi.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+#include <linux/semaphore.h>
+
 #include <asm/io.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/processor.h>
 #include <asm/uaccess.h>
 
-#include <linux/efi.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/processor.h>
 
 #define _COMPONENT             ACPI_OS_SERVICES
-ACPI_MODULE_NAME("osl")
+ACPI_MODULE_NAME("osl");
 #define PREFIX         "ACPI: "
 struct acpi_os_dpc {
        acpi_osd_exec_callback function;
        void *context;
+       struct work_struct work;
+       int wait;
 };
 
 #ifdef CONFIG_ACPI_CUSTOM_DSDT
@@ -67,33 +75,155 @@ EXPORT_SYMBOL(acpi_in_debugger);
 extern char line_buf[80];
 #endif                         /*ENABLE_DEBUGGER */
 
-int acpi_specific_hotkey_enabled = TRUE;
-EXPORT_SYMBOL(acpi_specific_hotkey_enabled);
-
 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;
+static struct workqueue_struct *kacpi_hotplug_wq;
+
+struct acpi_res_list {
+       resource_size_t start;
+       resource_size_t end;
+       acpi_adr_space_type resource_type; /* IO port, System memory, ...*/
+       char name[5];   /* only can have a length of 4 chars, make use of this
+                          one instead of res->name, no need to kalloc then */
+       struct list_head resource_list;
+       int count;
+};
+
+static LIST_HEAD(resource_list_head);
+static DEFINE_SPINLOCK(acpi_res_lock);
 
-acpi_status acpi_os_initialize(void)
+#define        OSI_STRING_LENGTH_MAX 64        /* arbitrary */
+static char osi_additional_string[OSI_STRING_LENGTH_MAX];
+
+/*
+ * The story of _OSI(Linux)
+ *
+ * From pre-history through Linux-2.6.22,
+ * Linux responded TRUE upon a BIOS OSI(Linux) query.
+ *
+ * Unfortunately, reference BIOS writers got wind of this
+ * and put OSI(Linux) in their example code, quickly exposing
+ * this string as ill-conceived and opening the door to
+ * an un-bounded number of BIOS incompatibilities.
+ *
+ * For example, OSI(Linux) was used on resume to re-POST a
+ * video card on one system, because Linux at that time
+ * could not do a speedy restore in its native driver.
+ * But then upon gaining quick native restore capability,
+ * Linux has no way to tell the BIOS to skip the time-consuming
+ * POST -- putting Linux at a permanent performance disadvantage.
+ * On another system, the BIOS writer used OSI(Linux)
+ * to infer native OS support for IPMI!  On other systems,
+ * OSI(Linux) simply got in the way of Linux claiming to
+ * be compatible with other operating systems, exposing
+ * BIOS issues such as skipped device initialization.
+ *
+ * So "Linux" turned out to be a really poor chose of
+ * OSI string, and from Linux-2.6.23 onward we respond FALSE.
+ *
+ * BIOS writers should NOT query _OSI(Linux) on future systems.
+ * Linux will complain on the console when it sees it, and return FALSE.
+ * To get Linux to return TRUE for your system  will require
+ * a kernel source update to add a DMI entry,
+ * or boot with "acpi_osi=Linux"
+ */
+
+static struct osi_linux {
+       unsigned int    enable:1;
+       unsigned int    dmi:1;
+       unsigned int    cmdline:1;
+       unsigned int    known:1;
+} osi_linux = { 0, 0, 0, 0};
+
+static void __init acpi_request_region (struct acpi_generic_address *addr,
+       unsigned int length, char *desc)
+{
+       struct resource *res;
+
+       if (!addr->address || !length)
+               return;
+
+       if (addr->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+               res = request_region(addr->address, length, desc);
+       else if (addr->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+               res = request_mem_region(addr->address, length, desc);
+}
+
+static int __init acpi_reserve_resources(void)
+{
+       acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length,
+               "ACPI PM1a_EVT_BLK");
+
+       acpi_request_region(&acpi_gbl_FADT.xpm1b_event_block, acpi_gbl_FADT.pm1_event_length,
+               "ACPI PM1b_EVT_BLK");
+
+       acpi_request_region(&acpi_gbl_FADT.xpm1a_control_block, acpi_gbl_FADT.pm1_control_length,
+               "ACPI PM1a_CNT_BLK");
+
+       acpi_request_region(&acpi_gbl_FADT.xpm1b_control_block, acpi_gbl_FADT.pm1_control_length,
+               "ACPI PM1b_CNT_BLK");
+
+       if (acpi_gbl_FADT.pm_timer_length == 4)
+               acpi_request_region(&acpi_gbl_FADT.xpm_timer_block, 4, "ACPI PM_TMR");
+
+       acpi_request_region(&acpi_gbl_FADT.xpm2_control_block, acpi_gbl_FADT.pm2_control_length,
+               "ACPI PM2_CNT_BLK");
+
+       /* Length of GPE blocks must be a non-negative multiple of 2 */
+
+       if (!(acpi_gbl_FADT.gpe0_block_length & 0x1))
+               acpi_request_region(&acpi_gbl_FADT.xgpe0_block,
+                              acpi_gbl_FADT.gpe0_block_length, "ACPI GPE0_BLK");
+
+       if (!(acpi_gbl_FADT.gpe1_block_length & 0x1))
+               acpi_request_region(&acpi_gbl_FADT.xgpe1_block,
+                              acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK");
+
+       return 0;
+}
+device_initcall(acpi_reserve_resources);
+
+acpi_status __init acpi_os_initialize(void)
 {
        return AE_OK;
 }
 
+static void bind_to_cpu0(struct work_struct *work)
+{
+       set_cpus_allowed_ptr(current, cpumask_of(0));
+       kfree(work);
+}
+
+static void bind_workqueue(struct workqueue_struct *wq)
+{
+       struct work_struct *work;
+
+       work = kzalloc(sizeof(struct work_struct), GFP_KERNEL);
+       INIT_WORK(work, bind_to_cpu0);
+       queue_work(wq, work);
+}
+
 acpi_status acpi_os_initialize1(void)
 {
        /*
-        * Initialize PCI configuration space access, as we'll need to access
-        * it while walking the namespace (bus 0 and root bridges w/ _BBNs).
+        * On some machines, a software-initiated SMI causes corruption unless
+        * the SMI runs on CPU 0.  An SMI can be initiated by any AML, but
+        * typically it's done in GPE-related methods that are run via
+        * workqueues, so we can avoid the known corruption cases by binding
+        * the workqueues to CPU 0.
         */
-       if (!raw_pci_ops) {
-               printk(KERN_ERR PREFIX
-                      "Access to PCI configuration space unavailable\n");
-               return AE_NULL_ENTRY;
-       }
        kacpid_wq = create_singlethread_workqueue("kacpid");
+       bind_workqueue(kacpid_wq);
+       kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
+       bind_workqueue(kacpi_notify_wq);
+       kacpi_hotplug_wq = create_singlethread_workqueue("kacpi_hotplug");
+       bind_workqueue(kacpi_hotplug_wq);
        BUG_ON(!kacpid_wq);
-
+       BUG_ON(!kacpi_notify_wq);
+       BUG_ON(!kacpi_hotplug_wq);
        return AE_OK;
 }
 
@@ -105,6 +235,8 @@ acpi_status acpi_os_terminate(void)
        }
 
        destroy_workqueue(kacpid_wq);
+       destroy_workqueue(kacpi_notify_wq);
+       destroy_workqueue(kacpi_hotplug_wq);
 
        return AE_OK;
 }
@@ -117,8 +249,6 @@ void acpi_os_printf(const char *fmt, ...)
        va_end(args);
 }
 
-EXPORT_SYMBOL(acpi_os_printf);
-
 void acpi_os_vprintf(const char *fmt, va_list args)
 {
        static char buffer[512];
@@ -129,63 +259,65 @@ void acpi_os_vprintf(const char *fmt, va_list args)
        if (acpi_in_debugger) {
                kdb_printf("%s", buffer);
        } else {
-               printk("%s", buffer);
+               printk(KERN_CONT "%s", buffer);
        }
 #else
-       printk("%s", buffer);
+       printk(KERN_CONT "%s", buffer);
 #endif
 }
 
-acpi_status acpi_os_get_root_pointer(u32 flags, struct acpi_pointer *addr)
+acpi_physical_address __init acpi_os_get_root_pointer(void)
 {
        if (efi_enabled) {
-               addr->pointer_type = ACPI_PHYSICAL_POINTER;
                if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
-                       addr->pointer.physical = efi.acpi20;
+                       return efi.acpi20;
                else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
-                       addr->pointer.physical = efi.acpi;
+                       return efi.acpi;
                else {
                        printk(KERN_ERR PREFIX
                               "System description tables not found\n");
-                       return AE_NOT_FOUND;
+                       return 0;
                }
        } else {
-               if (ACPI_FAILURE(acpi_find_root_pointer(flags, addr))) {
-                       printk(KERN_ERR PREFIX
-                              "System description tables not found\n");
-                       return AE_NOT_FOUND;
-               }
-       }
+               acpi_physical_address pa = 0;
 
-       return AE_OK;
+               acpi_find_root_pointer(&pa);
+               return pa;
+       }
 }
 
-acpi_status
-acpi_os_map_memory(acpi_physical_address phys, acpi_size size,
-                  void __iomem ** virt)
+void __iomem *__init_refok
+acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
 {
        if (phys > ULONG_MAX) {
                printk(KERN_ERR PREFIX "Cannot map memory that high\n");
-               return AE_BAD_PARAMETER;
+               return NULL;
        }
-       /*
-        * ioremap checks to ensure this is in reserved space
-        */
-       *virt = ioremap((unsigned long)phys, size);
-
-       if (!*virt)
-               return AE_NO_MEMORY;
-
-       return AE_OK;
+       if (acpi_gbl_permanent_mmap)
+               /*
+               * ioremap checks to ensure this is in reserved space
+               */
+               return ioremap((unsigned long)phys, size);
+       else
+               return __acpi_map_table((unsigned long)phys, size);
 }
 EXPORT_SYMBOL_GPL(acpi_os_map_memory);
 
-void acpi_os_unmap_memory(void __iomem * virt, acpi_size size)
+void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
 {
-       iounmap(virt);
+       if (acpi_gbl_permanent_mmap)
+               iounmap(virt);
+       else
+               __acpi_unmap_table(virt, size);
 }
 EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
 
+void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
+{
+       if (!acpi_gbl_permanent_mmap)
+               __acpi_unmap_table(virt, size);
+}
+
 #ifdef ACPI_FUTURE_USAGE
 acpi_status
 acpi_os_get_physical_address(void *virt, acpi_physical_address * phys)
@@ -227,20 +359,35 @@ acpi_os_table_override(struct acpi_table_header * existing_table,
        if (!existing_table || !new_table)
                return AE_BAD_PARAMETER;
 
+       *new_table = NULL;
+
 #ifdef CONFIG_ACPI_CUSTOM_DSDT
        if (strncmp(existing_table->signature, "DSDT", 4) == 0)
                *new_table = (struct acpi_table_header *)AmlCode;
-       else
-               *new_table = NULL;
-#else
-       *new_table = NULL;
 #endif
+       if (*new_table != NULL) {
+               printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], "
+                          "this is unsafe: tainting kernel\n",
+                      existing_table->signature,
+                      existing_table->oem_table_id);
+               add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
+       }
        return AE_OK;
 }
 
-static irqreturn_t acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t acpi_irq(int irq, void *dev_id)
 {
-       return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE;
+       u32 handled;
+
+       handled = (*acpi_irq_handler) (acpi_irq_context);
+
+       if (handled) {
+               acpi_irq_handled++;
+               return IRQ_HANDLED;
+       } else {
+               acpi_irq_not_handled++;
+               return IRQ_NONE;
+       }
 }
 
 acpi_status
@@ -249,12 +396,14 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
 {
        unsigned int irq;
 
+       acpi_irq_stats_init();
+
        /*
         * Ignore the GSI from the core, and use the value in our copy of the
         * FADT. It may not be the same if an interrupt source override exists
         * for the SCI.
         */
-       gsi = acpi_fadt.sci_int;
+       gsi = acpi_gbl_FADT.sci_interrupt;
        if (acpi_gsi_to_irq(gsi, &irq) < 0) {
                printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n",
                       gsi);
@@ -292,8 +441,6 @@ void acpi_os_sleep(acpi_integer ms)
        schedule_timeout_interruptible(msecs_to_jiffies(ms));
 }
 
-EXPORT_SYMBOL(acpi_os_sleep);
-
 void acpi_os_stall(u32 us)
 {
        while (us) {
@@ -307,8 +454,6 @@ void acpi_os_stall(u32 us)
        }
 }
 
-EXPORT_SYMBOL(acpi_os_stall);
-
 /*
  * Support ACPI 3.0 AML Timer operand
  * Returns 64-bit free-running, monotonically increasing timer
@@ -338,17 +483,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();
        }
 
@@ -359,17 +501,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();
        }
 
@@ -435,7 +573,7 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width)
 
 acpi_status
 acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
-                              void *value, u32 width)
+                              u32 *value, u32 width)
 {
        int result, size;
 
@@ -456,17 +594,13 @@ acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
                return AE_ERROR;
        }
 
-       BUG_ON(!raw_pci_ops);
-
-       result = raw_pci_ops->read(pci_id->segment, pci_id->bus,
-                                  PCI_DEVFN(pci_id->device, pci_id->function),
-                                  reg, size, value);
+       result = raw_pci_read(pci_id->segment, pci_id->bus,
+                               PCI_DEVFN(pci_id->device, pci_id->function),
+                               reg, size, value);
 
        return (result ? AE_ERROR : AE_OK);
 }
 
-EXPORT_SYMBOL(acpi_os_read_pci_configuration);
-
 acpi_status
 acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
                                acpi_integer value, u32 width)
@@ -487,11 +621,9 @@ acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
                return AE_ERROR;
        }
 
-       BUG_ON(!raw_pci_ops);
-
-       result = raw_pci_ops->write(pci_id->segment, pci_id->bus,
-                                   PCI_DEVFN(pci_id->device, pci_id->function),
-                                   reg, size, value);
+       result = raw_pci_write(pci_id->segment, pci_id->bus,
+                               PCI_DEVFN(pci_id->device, pci_id->function),
+                               reg, size, value);
 
        return (result ? AE_ERROR : AE_OK);
 }
@@ -505,9 +637,8 @@ static void acpi_os_derive_pci_id_2(acpi_handle rhandle,    /* upper bound  */
        acpi_handle handle;
        struct acpi_pci_id *pci_id = *id;
        acpi_status status;
-       unsigned long temp;
+       unsigned long long temp;
        acpi_object_type type;
-       u8 tu8;
 
        acpi_get_parent(chandle, &handle);
        if (handle != rhandle) {
@@ -518,10 +649,10 @@ static void acpi_os_derive_pci_id_2(acpi_handle rhandle,  /* upper bound  */
                if ((ACPI_FAILURE(status)) || (type != ACPI_TYPE_DEVICE))
                        return;
 
-               status =
-                   acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL,
+               status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL,
                                          &temp);
                if (ACPI_SUCCESS(status)) {
+                       u32 val;
                        pci_id->device = ACPI_HIWORD(ACPI_LODWORD(temp));
                        pci_id->function = ACPI_LOWORD(ACPI_LODWORD(temp));
 
@@ -530,24 +661,24 @@ static void acpi_os_derive_pci_id_2(acpi_handle rhandle,  /* upper bound  */
 
                        /* any nicer way to get bus number of bridge ? */
                        status =
-                           acpi_os_read_pci_configuration(pci_id, 0x0e, &tu8,
+                           acpi_os_read_pci_configuration(pci_id, 0x0e, &val,
                                                           8);
                        if (ACPI_SUCCESS(status)
-                           && ((tu8 & 0x7f) == 1 || (tu8 & 0x7f) == 2)) {
+                           && ((val & 0x7f) == 1 || (val & 0x7f) == 2)) {
                                status =
                                    acpi_os_read_pci_configuration(pci_id, 0x18,
-                                                                  &tu8, 8);
+                                                                  &val, 8);
                                if (!ACPI_SUCCESS(status)) {
                                        /* Certainly broken...  FIX ME */
                                        return;
                                }
                                *is_bridge = 1;
-                               pci_id->bus = tu8;
+                               pci_id->bus = val;
                                status =
                                    acpi_os_read_pci_configuration(pci_id, 0x19,
-                                                                  &tu8, 8);
+                                                                  &val, 8);
                                if (ACPI_SUCCESS(status)) {
-                                       *bus_number = tu8;
+                                       *bus_number = val;
                                }
                        } else
                                *is_bridge = 0;
@@ -565,32 +696,15 @@ void acpi_os_derive_pci_id(acpi_handle rhandle,   /* upper bound  */
        acpi_os_derive_pci_id_2(rhandle, chandle, id, &is_bridge, &bus_number);
 }
 
-static void acpi_os_execute_deferred(void *context)
+static void acpi_os_execute_deferred(struct work_struct *work)
 {
-       struct acpi_os_dpc *dpc = NULL;
-
+       struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
 
-       dpc = (struct acpi_os_dpc *)context;
-       if (!dpc) {
-               printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
-               return;
-       }
+       if (dpc->wait)
+               acpi_os_wait_events_complete(NULL);
 
        dpc->function(dpc->context);
-
        kfree(dpc);
-
-       return;
-}
-
-static int acpi_os_execute_thread(void *context)
-{
-       struct acpi_os_dpc *dpc = (struct acpi_os_dpc *)context;
-       if (dpc) {
-               dpc->function(dpc->context);
-               kfree(dpc);
-       }
-       do_exit(0);
 }
 
 /*******************************************************************************
@@ -608,61 +722,71 @@ static int acpi_os_execute_thread(void *context)
  *
  ******************************************************************************/
 
-acpi_status acpi_os_execute(acpi_execute_type type,
-                           acpi_osd_exec_callback function, void *context)
+static acpi_status __acpi_os_execute(acpi_execute_type type,
+       acpi_osd_exec_callback function, void *context, int hp)
 {
        acpi_status status = AE_OK;
        struct acpi_os_dpc *dpc;
-       struct work_struct *task;
-       struct task_struct *p;
+       struct workqueue_struct *queue;
+       int ret;
+       ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+                         "Scheduling function [%p(%p)] for deferred execution.\n",
+                         function, context));
 
-       if (!function)
-               return AE_BAD_PARAMETER;
        /*
         * Allocate/initialize DPC structure.  Note that this memory will be
-        * freed by the callee.  The kernel handles the tq_struct list  in a
+        * freed by the callee.  The kernel handles the work_struct list  in a
         * way that allows us to also free its memory inside the callee.
         * Because we may want to schedule several tasks with different
         * parameters we can't use the approach some kernel code uses of
-        * having a static tq_struct.
-        * We can save time and code by allocating the DPC and tq_structs
-        * from the same memory.
+        * having a static work_struct.
         */
-       if (type == OSL_NOTIFY_HANDLER) {
-               dpc = kmalloc(sizeof(struct acpi_os_dpc), GFP_KERNEL);
-       } else {
-               dpc = kmalloc(sizeof(struct acpi_os_dpc) +
-                               sizeof(struct work_struct), GFP_ATOMIC);
-       }
+
+       dpc = kmalloc(sizeof(struct acpi_os_dpc), GFP_ATOMIC);
        if (!dpc)
                return AE_NO_MEMORY;
+
        dpc->function = function;
        dpc->context = context;
 
-       if (type == OSL_NOTIFY_HANDLER) {
-               p = kthread_create(acpi_os_execute_thread, dpc, "kacpid_notify");
-               if (!IS_ERR(p)) {
-                       wake_up_process(p);
-               } else {
-                       status = AE_NO_MEMORY;
-                       kfree(dpc);
-               }
-       } else {
-               task = (void *)(dpc + 1);
-               INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc);
-               if (!queue_work(kacpid_wq, task)) {
-                       status = AE_ERROR;
-                       kfree(dpc);
-               }
+       /*
+        * We can't run hotplug code in keventd_wq/kacpid_wq/kacpid_notify_wq
+        * because the hotplug code may call driver .remove() functions,
+        * which invoke flush_scheduled_work/acpi_os_wait_events_complete
+        * to flush these workqueues.
+        */
+       queue = hp ? kacpi_hotplug_wq :
+               (type == OSL_NOTIFY_HANDLER ? kacpi_notify_wq : kacpid_wq);
+       dpc->wait = hp ? 1 : 0;
+       INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+       ret = queue_work(queue, &dpc->work);
+
+       if (!ret) {
+               printk(KERN_ERR PREFIX
+                         "Call to queue_work() failed.\n");
+               status = AE_ERROR;
+               kfree(dpc);
        }
        return status;
 }
 
+acpi_status acpi_os_execute(acpi_execute_type type,
+                           acpi_osd_exec_callback function, void *context)
+{
+       return __acpi_os_execute(type, function, context, 0);
+}
 EXPORT_SYMBOL(acpi_os_execute);
 
+acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function,
+       void *context)
+{
+       return __acpi_os_execute(0, function, context, 1);
+}
+
 void acpi_os_wait_events_complete(void *context)
 {
        flush_workqueue(kacpid_wq);
+       flush_workqueue(kacpi_notify_wq);
 }
 
 EXPORT_SYMBOL(acpi_os_wait_events_complete);
@@ -690,7 +814,6 @@ acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
 {
        struct semaphore *sem = NULL;
 
-
        sem = acpi_os_allocate(sizeof(struct semaphore));
        if (!sem)
                return AE_NO_MEMORY;
@@ -706,8 +829,6 @@ acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
        return AE_OK;
 }
 
-EXPORT_SYMBOL(acpi_os_create_semaphore);
-
 /*
  * TODO: A better way to delete semaphores?  Linux doesn't have a
  * 'delete_semaphore()' function -- may result in an invalid
@@ -719,36 +840,28 @@ acpi_status acpi_os_delete_semaphore(acpi_handle handle)
 {
        struct semaphore *sem = (struct semaphore *)handle;
 
-
        if (!sem)
                return AE_BAD_PARAMETER;
 
        ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Deleting semaphore[%p].\n", handle));
 
+       BUG_ON(!list_empty(&sem->wait_list));
        kfree(sem);
        sem = NULL;
 
        return AE_OK;
 }
 
-EXPORT_SYMBOL(acpi_os_delete_semaphore);
-
 /*
- * TODO: The kernel doesn't have a 'down_timeout' function -- had to
- * improvise.  The process is to sleep for one scheduler quantum
- * until the semaphore becomes available.  Downside is that this
- * may result in starvation for timeout-based waits when there's
- * lots of semaphore activity.
- *
  * TODO: Support for units > 1?
  */
 acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout)
 {
        acpi_status status = AE_OK;
        struct semaphore *sem = (struct semaphore *)handle;
+       long jiffies;
        int ret = 0;
 
-
        if (!sem || (units < 1))
                return AE_BAD_PARAMETER;
 
@@ -758,48 +871,14 @@ acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout)
        ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Waiting for semaphore[%p|%d|%d]\n",
                          handle, units, timeout));
 
-       switch (timeout) {
-               /*
-                * No Wait:
-                * --------
-                * A zero timeout value indicates that we shouldn't wait - just
-                * acquire the semaphore if available otherwise return AE_TIME
-                * (a.k.a. 'would block').
-                */
-       case 0:
-               if (down_trylock(sem))
-                       status = AE_TIME;
-               break;
-
-               /*
-                * Wait Indefinitely:
-                * ------------------
-                */
-       case ACPI_WAIT_FOREVER:
-               down(sem);
-               break;
-
-               /*
-                * Wait w/ Timeout:
-                * ----------------
-                */
-       default:
-               // TODO: A better timeout algorithm?
-               {
-                       int i = 0;
-                       static const int quantum_ms = 1000 / HZ;
-
-                       ret = down_trylock(sem);
-                       for (i = timeout; (i > 0 && ret != 0); i -= quantum_ms) {
-                               schedule_timeout_interruptible(1);
-                               ret = down_trylock(sem);
-                       }
-
-                       if (ret != 0)
-                               status = AE_TIME;
-               }
-               break;
-       }
+       if (timeout == ACPI_WAIT_FOREVER)
+               jiffies = MAX_SCHEDULE_TIMEOUT;
+       else
+               jiffies = msecs_to_jiffies(timeout);
+       
+       ret = down_timeout(sem, jiffies);
+       if (ret)
+               status = AE_TIME;
 
        if (ACPI_FAILURE(status)) {
                ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
@@ -815,8 +894,6 @@ acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout)
        return status;
 }
 
-EXPORT_SYMBOL(acpi_os_wait_semaphore);
-
 /*
  * TODO: Support for units > 1?
  */
@@ -824,7 +901,6 @@ acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units)
 {
        struct semaphore *sem = (struct semaphore *)handle;
 
-
        if (!sem || (units < 1))
                return AE_BAD_PARAMETER;
 
@@ -839,8 +915,6 @@ acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units)
        return AE_OK;
 }
 
-EXPORT_SYMBOL(acpi_os_signal_semaphore);
-
 #ifdef ACPI_FUTURE_USAGE
 u32 acpi_os_get_line(char *buffer)
 {
@@ -861,26 +935,6 @@ u32 acpi_os_get_line(char *buffer)
 }
 #endif                         /*  ACPI_FUTURE_USAGE  */
 
-/* Assumes no unreadable holes inbetween */
-u8 acpi_os_readable(void *ptr, acpi_size len)
-{
-#if defined(__i386__) || defined(__x86_64__)
-       char tmp;
-       return !__get_user(tmp, (char __user *)ptr)
-           && !__get_user(tmp, (char __user *)ptr + len - 1);
-#endif
-       return 1;
-}
-
-#ifdef ACPI_FUTURE_USAGE
-u8 acpi_os_writable(void *ptr, acpi_size len)
-{
-       /* could do dummy write (racy) or a kernel page table lookup.
-          The later may be difficult at early boot when kmap doesn't work yet. */
-       return 1;
-}
-#endif
-
 acpi_status acpi_os_signal(u32 function, void *info)
 {
        switch (function) {
@@ -904,8 +958,6 @@ acpi_status acpi_os_signal(u32 function, void *info)
        return AE_OK;
 }
 
-EXPORT_SYMBOL(acpi_os_signal);
-
 static int __init acpi_os_name_setup(char *str)
 {
        char *p = acpi_os_name;
@@ -930,20 +982,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)
+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;
@@ -983,21 +1077,133 @@ static int __init acpi_wake_gpes_always_on_setup(char *str)
 
 __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup);
 
-static int __init acpi_hotkey_setup(char *str)
+/* Check of resource interference between native drivers and ACPI
+ * OperationRegions (SystemIO and System Memory only).
+ * IO ports and memory declared in ACPI might be used by the ACPI subsystem
+ * in arbitrary AML code and can interfere with legacy drivers.
+ * acpi_enforce_resources= can be set to:
+ *
+ *   - strict (default) (2)
+ *     -> further driver trying to access the resources will not load
+ *   - lax              (1)
+ *     -> further driver trying to access the resources will load, but you
+ *     get a system message that something might go wrong...
+ *
+ *   - no               (0)
+ *     -> ACPI Operation Region resources will not be registered
+ *
+ */
+#define ENFORCE_RESOURCES_STRICT 2
+#define ENFORCE_RESOURCES_LAX    1
+#define ENFORCE_RESOURCES_NO     0
+
+static unsigned int acpi_enforce_resources = ENFORCE_RESOURCES_STRICT;
+
+static int __init acpi_enforce_resources_setup(char *str)
 {
-       acpi_specific_hotkey_enabled = FALSE;
+       if (str == NULL || *str == '\0')
+               return 0;
+
+       if (!strcmp("strict", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_STRICT;
+       else if (!strcmp("lax", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_LAX;
+       else if (!strcmp("no", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_NO;
+
        return 1;
 }
 
-__setup("acpi_generic_hotkey", acpi_hotkey_setup);
+__setup("acpi_enforce_resources=", acpi_enforce_resources_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;
+/* Check for resource conflicts between ACPI OperationRegions and native
+ * drivers */
+int acpi_check_resource_conflict(struct resource *res)
+{
+       struct acpi_res_list *res_list_elem;
+       int ioport;
+       int clash = 0;
+
+       if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
+               return 0;
+       if (!(res->flags & IORESOURCE_IO) && !(res->flags & IORESOURCE_MEM))
+               return 0;
+
+       ioport = res->flags & IORESOURCE_IO;
+
+       spin_lock(&acpi_res_lock);
+       list_for_each_entry(res_list_elem, &resource_list_head,
+                           resource_list) {
+               if (ioport && (res_list_elem->resource_type
+                              != ACPI_ADR_SPACE_SYSTEM_IO))
+                       continue;
+               if (!ioport && (res_list_elem->resource_type
+                               != ACPI_ADR_SPACE_SYSTEM_MEMORY))
+                       continue;
+
+               if (res->end < res_list_elem->start
+                   || res_list_elem->end < res->start)
+                       continue;
+               clash = 1;
+               break;
+       }
+       spin_unlock(&acpi_res_lock);
+
+       if (clash) {
+               if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) {
+                       printk("%sACPI: %s resource %s [0x%llx-0x%llx]"
+                              " conflicts with ACPI region %s"
+                              " [0x%llx-0x%llx]\n",
+                              acpi_enforce_resources == ENFORCE_RESOURCES_LAX
+                              ? KERN_WARNING : KERN_ERR,
+                              ioport ? "I/O" : "Memory", res->name,
+                              (long long) res->start, (long long) res->end,
+                              res_list_elem->name,
+                              (long long) res_list_elem->start,
+                              (long long) res_list_elem->end);
+                       if (acpi_enforce_resources == ENFORCE_RESOURCES_LAX)
+                               printk(KERN_NOTICE "ACPI: This conflict may"
+                                      " cause random problems and system"
+                                      " instability\n");
+                       printk(KERN_INFO "ACPI: If an ACPI driver is available"
+                              " for this device, you should use it instead of"
+                              " the native driver\n");
+               }
+               if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT)
+                       return -EBUSY;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(acpi_check_resource_conflict);
+
+int acpi_check_region(resource_size_t start, resource_size_t n,
+                     const char *name)
+{
+       struct resource res = {
+               .start = start,
+               .end   = start + n - 1,
+               .name  = name,
+               .flags = IORESOURCE_IO,
+       };
+
+       return acpi_check_resource_conflict(&res);
+}
+EXPORT_SYMBOL(acpi_check_region);
 
-EXPORT_SYMBOL(max_cstate);
+int acpi_check_mem_region(resource_size_t start, resource_size_t n,
+                     const char *name)
+{
+       struct resource res = {
+               .start = start,
+               .end   = start + n - 1,
+               .name  = name,
+               .flags = IORESOURCE_MEM,
+       };
+
+       return acpi_check_resource_conflict(&res);
+
+}
+EXPORT_SYMBOL(acpi_check_mem_region);
 
 /*
  * Acquire a spinlock.
@@ -1041,8 +1247,8 @@ 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);
-       if (cache == NULL)
+       *cache = kmem_cache_create(name, size, 0, 0, NULL);
+       if (*cache == NULL)
                return AE_ERROR;
        else
                return AE_OK;
@@ -1062,7 +1268,7 @@ acpi_os_create_cache(char *name, u16 size, u16 depth, acpi_cache_t ** cache)
 
 acpi_status acpi_os_purge_cache(acpi_cache_t * cache)
 {
-       (void)kmem_cache_shrink(cache);
+       kmem_cache_shrink(cache);
        return (AE_OK);
 }
 
@@ -1081,7 +1287,7 @@ acpi_status acpi_os_purge_cache(acpi_cache_t * cache)
 
 acpi_status acpi_os_delete_cache(acpi_cache_t * cache)
 {
-       (void)kmem_cache_destroy(cache);
+       kmem_cache_destroy(cache);
        return (AE_OK);
 }
 
@@ -1121,10 +1327,104 @@ 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.enable)
+                       return AE_OK;
+       }
+       return AE_SUPPORT;
+}
+
+static inline int acpi_res_list_add(struct acpi_res_list *res)
+{
+       struct acpi_res_list *res_list_elem;
+
+       list_for_each_entry(res_list_elem, &resource_list_head,
+                           resource_list) {
+
+               if (res->resource_type == res_list_elem->resource_type &&
+                   res->start == res_list_elem->start &&
+                   res->end == res_list_elem->end) {
+
+                       /*
+                        * The Region(addr,len) already exist in the list,
+                        * just increase the count
+                        */
+
+                       res_list_elem->count++;
+                       return 0;
+               }
+       }
+
+       res->count = 1;
+       list_add(&res->resource_list, &resource_list_head);
+       return 1;
+}
+
+static inline void acpi_res_list_del(struct acpi_res_list *res)
+{
+       struct acpi_res_list *res_list_elem;
+
+       list_for_each_entry(res_list_elem, &resource_list_head,
+                           resource_list) {
+
+               if (res->resource_type == res_list_elem->resource_type &&
+                   res->start == res_list_elem->start &&
+                   res->end == res_list_elem->end) {
+
+                       /*
+                        * If the res count is decreased to 0,
+                        * remove and free it
+                        */
 
-    return AE_SUPPORT;
+                       if (--res_list_elem->count == 0) {
+                               list_del(&res_list_elem->resource_list);
+                               kfree(res_list_elem);
+                       }
+                       return;
+               }
+       }
 }
 
+acpi_status
+acpi_os_invalidate_address(
+    u8                   space_id,
+    acpi_physical_address   address,
+    acpi_size               length)
+{
+       struct acpi_res_list res;
+
+       switch (space_id) {
+       case ACPI_ADR_SPACE_SYSTEM_IO:
+       case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+               /* Only interference checks against SystemIO and SytemMemory
+                  are needed */
+               res.start = address;
+               res.end = address + length - 1;
+               res.resource_type = space_id;
+               spin_lock(&acpi_res_lock);
+               acpi_res_list_del(&res);
+               spin_unlock(&acpi_res_lock);
+               break;
+       case ACPI_ADR_SPACE_PCI_CONFIG:
+       case ACPI_ADR_SPACE_EC:
+       case ACPI_ADR_SPACE_SMBUS:
+       case ACPI_ADR_SPACE_CMOS:
+       case ACPI_ADR_SPACE_PCI_BAR_TARGET:
+       case ACPI_ADR_SPACE_DATA_TABLE:
+       case ACPI_ADR_SPACE_FIXED_HARDWARE:
+               break;
+       }
+       return AE_OK;
+}
 
 /******************************************************************************
  *
@@ -1146,11 +1446,50 @@ acpi_status
 acpi_os_validate_address (
     u8                   space_id,
     acpi_physical_address   address,
-    acpi_size               length)
+    acpi_size               length,
+    char *name)
 {
+       struct acpi_res_list *res;
+       int added;
+       if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
+               return AE_OK;
 
-    return AE_OK;
+       switch (space_id) {
+       case ACPI_ADR_SPACE_SYSTEM_IO:
+       case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+               /* Only interference checks against SystemIO and SytemMemory
+                  are needed */
+               res = kzalloc(sizeof(struct acpi_res_list), GFP_KERNEL);
+               if (!res)
+                       return AE_OK;
+               /* ACPI names are fixed to 4 bytes, still better use strlcpy */
+               strlcpy(res->name, name, 5);
+               res->start = address;
+               res->end = address + length - 1;
+               res->resource_type = space_id;
+               spin_lock(&acpi_res_lock);
+               added = acpi_res_list_add(res);
+               spin_unlock(&acpi_res_lock);
+               pr_debug("%s %s resource: start: 0x%llx, end: 0x%llx, "
+                        "name: %s\n", added ? "Added" : "Already exist",
+                        (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+                        ? "SystemIO" : "System Memory",
+                        (unsigned long long)res->start,
+                        (unsigned long long)res->end,
+                        res->name);
+               if (!added)
+                       kfree(res);
+               break;
+       case ACPI_ADR_SPACE_PCI_CONFIG:
+       case ACPI_ADR_SPACE_EC:
+       case ACPI_ADR_SPACE_SMBUS:
+       case ACPI_ADR_SPACE_CMOS:
+       case ACPI_ADR_SPACE_PCI_BAR_TARGET:
+       case ACPI_ADR_SPACE_DATA_TABLE:
+       case ACPI_ADR_SPACE_FIXED_HARDWARE:
+               break;
+       }
+       return AE_OK;
 }
 
-
 #endif