ACPI: call init_acpi_device_notify() explicitly rather than as initcall
[safe/jmp/linux-2.6] / drivers / acpi / bus.c
index cb807c4..db9eca8 100644 (file)
 #include <linux/list.h>
 #include <linux/sched.h>
 #include <linux/pm.h>
-#include <linux/pm_legacy.h>
 #include <linux/device.h>
 #include <linux/proc_fs.h>
+#include <linux/acpi.h>
 #ifdef CONFIG_X86
 #include <asm/mpspec.h>
 #endif
+#include <linux/pci.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
+#include "internal.h"
+
 #define _COMPONENT             ACPI_BUS_COMPONENT
-ACPI_MODULE_NAME("acpi_bus")
-#ifdef CONFIG_X86
-extern void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger);
-#endif
+ACPI_MODULE_NAME("bus");
 
 struct acpi_device *acpi_root;
 struct proc_dir_entry *acpi_root_dir;
@@ -50,6 +50,23 @@ EXPORT_SYMBOL(acpi_root_dir);
 
 #define STRUCT_TO_INT(s)       (*((int*)&s))
 
+static int set_power_nocheck(const struct dmi_system_id *id)
+{
+       printk(KERN_NOTICE PREFIX "%s detected - "
+               "disable power check in power transistion\n", id->ident);
+       acpi_power_nocheck = 1;
+       return 0;
+}
+static struct dmi_system_id __cpuinitdata power_nocheck_dmi_table[] = {
+       {
+       set_power_nocheck, "HP Pavilion 05", {
+       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
+       DMI_MATCH(DMI_SYS_VENDOR, "HP Pavilion 05"),
+       DMI_MATCH(DMI_PRODUCT_VERSION, "2001211RE101GLEND") }, NULL},
+       {},
+};
+
+
 /* --------------------------------------------------------------------------
                                 Device Management
    -------------------------------------------------------------------------- */
@@ -79,7 +96,7 @@ EXPORT_SYMBOL(acpi_bus_get_device);
 int acpi_bus_get_status(struct acpi_device *device)
 {
        acpi_status status = AE_OK;
-       unsigned long sta = 0;
+       unsigned long long sta = 0;
 
 
        if (!device)
@@ -97,19 +114,21 @@ int acpi_bus_get_status(struct acpi_device *device)
        }
 
        /*
-        * Otherwise we assume the status of our parent (unless we don't
-        * have one, in which case status is implied).
+        * According to ACPI spec some device can be present and functional
+        * even if the parent is not present but functional.
+        * In such conditions the child device should not inherit the status
+        * from the parent.
         */
-       else if (device->parent)
-               device->status = device->parent->status;
        else
-               STRUCT_TO_INT(device->status) = 0x0F;
+               STRUCT_TO_INT(device->status) =
+                   ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
+                   ACPI_STA_DEVICE_UI      | ACPI_STA_DEVICE_FUNCTIONING;
 
        if (device->status.functional && !device->status.present) {
-               printk(KERN_WARNING PREFIX "Device [%s] status [%08x]: "
-                      "functional but not present; setting present\n",
-                      device->pnp.bus_id, (u32) STRUCT_TO_INT(device->status));
-               device->status.present = 1;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: "
+                      "functional but not present;\n",
+                       device->pnp.bus_id,
+                       (u32) STRUCT_TO_INT(device->status)));
        }
 
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n",
@@ -121,6 +140,31 @@ int acpi_bus_get_status(struct acpi_device *device)
 
 EXPORT_SYMBOL(acpi_bus_get_status);
 
+void acpi_bus_private_data_handler(acpi_handle handle,
+                                  u32 function, void *context)
+{
+       return;
+}
+EXPORT_SYMBOL(acpi_bus_private_data_handler);
+
+int acpi_bus_get_private_data(acpi_handle handle, void **data)
+{
+       acpi_status status = AE_OK;
+
+       if (!*data)
+               return -EINVAL;
+
+       status = acpi_get_data(handle, acpi_bus_private_data_handler, data);
+       if (ACPI_FAILURE(status) || !*data) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
+                               handle));
+               return -ENODEV;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(acpi_bus_get_private_data);
+
 /* --------------------------------------------------------------------------
                                  Power Management
    -------------------------------------------------------------------------- */
@@ -130,7 +174,7 @@ int acpi_bus_get_power(acpi_handle handle, int *state)
        int result = 0;
        acpi_status status = 0;
        struct acpi_device *device = NULL;
-       unsigned long psc = 0;
+       unsigned long long psc = 0;
 
 
        result = acpi_bus_get_device(handle, &device);
@@ -147,7 +191,7 @@ int acpi_bus_get_power(acpi_handle handle, int *state)
                        *state = ACPI_STATE_D0;
        } else {
                /*
-                * Get the device's power state either directly (via _PSC) or 
+                * Get the device's power state either directly (via _PSC) or
                 * indirectly (via power resources).
                 */
                if (device->power.flags.explicit_get) {
@@ -192,22 +236,31 @@ int acpi_bus_set_power(acpi_handle handle, int state)
 
        if (!device->flags.power_manageable) {
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device `[%s]' is not power manageable\n",
-                               device->kobj.name));
+                               kobject_name(&device->dev.kobj)));
                return -ENODEV;
        }
        /*
-        * Get device's current power state if it's unknown
-        * This means device power state isn't initialized or previous setting failed
+        * Get device's current power state
         */
-       if (!device->flags.force_power_state) {
-               if (device->power.state == ACPI_STATE_UNKNOWN)
-                       acpi_bus_get_power(device->handle, &device->power.state);
-               if (state == device->power.state) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n",
-                                         state));
-                       return 0;
-               }
+       if (!acpi_power_nocheck) {
+               /*
+                * Maybe the incorrect power state is returned on the bogus
+                * bios, which is different with the real power state.
+                * For example: the bios returns D0 state and the real power
+                * state is D3. OS expects to set the device to D0 state. In
+                * such case if OS uses the power state returned by the BIOS,
+                * the device can't be transisted to the correct power state.
+                * So if the acpi_power_nocheck is set, it is unnecessary to
+                * get the power state by calling acpi_bus_get_power.
+                */
+               acpi_bus_get_power(device->handle, &device->power.state);
+       }
+       if ((state == device->power.state) && !device->flags.force_power_state) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n",
+                                 state));
+               return 0;
        }
+
        if (!device->power.states[state].flags.valid) {
                printk(KERN_WARNING PREFIX "Device does not support D%d\n", state);
                return -ENODEV;
@@ -261,20 +314,45 @@ int acpi_bus_set_power(acpi_handle handle, int state)
                printk(KERN_WARNING PREFIX
                              "Transitioning device [%s] to D%d\n",
                              device->pnp.bus_id, state);
-       else
+       else {
+               device->power.state = state;
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                                  "Device [%s] transitioned to D%d\n",
                                  device->pnp.bus_id, state));
+       }
 
        return result;
 }
 
 EXPORT_SYMBOL(acpi_bus_set_power);
 
+bool acpi_bus_power_manageable(acpi_handle handle)
+{
+       struct acpi_device *device;
+       int result;
+
+       result = acpi_bus_get_device(handle, &device);
+       return result ? false : device->flags.power_manageable;
+}
+
+EXPORT_SYMBOL(acpi_bus_power_manageable);
+
+bool acpi_bus_can_wakeup(acpi_handle handle)
+{
+       struct acpi_device *device;
+       int result;
+
+       result = acpi_bus_get_device(handle, &device);
+       return result ? false : device->wakeup.flags.valid;
+}
+
+EXPORT_SYMBOL(acpi_bus_can_wakeup);
+
 /* --------------------------------------------------------------------------
                                 Event Management
    -------------------------------------------------------------------------- */
 
+#ifdef CONFIG_ACPI_PROC_EVENT
 static DEFINE_SPINLOCK(acpi_bus_event_lock);
 
 LIST_HEAD(acpi_bus_event_list);
@@ -282,15 +360,11 @@ DECLARE_WAIT_QUEUE_HEAD(acpi_bus_event_queue);
 
 extern int event_is_open;
 
-int acpi_bus_generate_event(struct acpi_device *device, u8 type, int data)
+int acpi_bus_generate_proc_event4(const char *device_class, const char *bus_id, u8 type, int data)
 {
-       struct acpi_bus_event *event = NULL;
+       struct acpi_bus_event *event;
        unsigned long flags = 0;
 
-
-       if (!device)
-               return -EINVAL;
-
        /* drop event on the floor if no one's listening */
        if (!event_is_open)
                return 0;
@@ -299,8 +373,8 @@ int acpi_bus_generate_event(struct acpi_device *device, u8 type, int data)
        if (!event)
                return -ENOMEM;
 
-       strcpy(event->device_class, device->pnp.device_class);
-       strcpy(event->bus_id, device->pnp.bus_id);
+       strcpy(event->device_class, device_class);
+       strcpy(event->bus_id, bus_id);
        event->type = type;
        event->data = data;
 
@@ -311,9 +385,20 @@ int acpi_bus_generate_event(struct acpi_device *device, u8 type, int data)
        wake_up_interruptible(&acpi_bus_event_queue);
 
        return 0;
+
 }
 
-EXPORT_SYMBOL(acpi_bus_generate_event);
+EXPORT_SYMBOL_GPL(acpi_bus_generate_proc_event4);
+
+int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data)
+{
+       if (!device)
+               return -EINVAL;
+       return acpi_bus_generate_proc_event4(device->pnp.device_class,
+                                            device->pnp.bus_id, type, data);
+}
+
+EXPORT_SYMBOL(acpi_bus_generate_proc_event);
 
 int acpi_bus_receive_event(struct acpi_bus_event *event)
 {
@@ -342,10 +427,11 @@ int acpi_bus_receive_event(struct acpi_bus_event *event)
        }
 
        spin_lock_irqsave(&acpi_bus_event_lock, flags);
-       entry =
-           list_entry(acpi_bus_event_list.next, struct acpi_bus_event, node);
-       if (entry)
+       if (!list_empty(&acpi_bus_event_list)) {
+               entry = list_entry(acpi_bus_event_list.next,
+                                  struct acpi_bus_event, node);
                list_del(&entry->node);
+       }
        spin_unlock_irqrestore(&acpi_bus_event_lock, flags);
 
        if (!entry)
@@ -358,7 +444,7 @@ int acpi_bus_receive_event(struct acpi_bus_event *event)
        return 0;
 }
 
-EXPORT_SYMBOL(acpi_bus_receive_event);
+#endif /* CONFIG_ACPI_PROC_EVENT */
 
 /* --------------------------------------------------------------------------
                              Notification Handling
@@ -441,6 +527,19 @@ static int acpi_bus_check_scope(struct acpi_device *device)
        return 0;
 }
 
+static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list);
+int register_acpi_bus_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&acpi_bus_notify_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_acpi_bus_notifier);
+
+void unregister_acpi_bus_notifier(struct notifier_block *nb)
+{
+       blocking_notifier_chain_unregister(&acpi_bus_notify_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_acpi_bus_notifier);
+
 /**
  * acpi_bus_notify
  * ---------------
@@ -451,6 +550,8 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
        int result = 0;
        struct acpi_device *device = NULL;
 
+       blocking_notifier_call_chain(&acpi_bus_notify_list,
+               type, (void *)handle);
 
        if (acpi_bus_get_device(handle, &device))
                return;
@@ -462,7 +563,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
                                  "Received BUS CHECK notification for device [%s]\n",
                                  device->pnp.bus_id));
                result = acpi_bus_check_scope(device);
-               /* 
+               /*
                 * TBD: We'll need to outsource certain events to non-ACPI
                 *      drivers via the device manager (device.c).
                 */
@@ -473,7 +574,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
                                  "Received DEVICE CHECK notification for device [%s]\n",
                                  device->pnp.bus_id));
                result = acpi_bus_check_device(device, NULL);
-               /* 
+               /*
                 * TBD: We'll need to outsource certain events to non-ACPI
                 *      drivers via the device manager (device.c).
                 */
@@ -543,7 +644,7 @@ static int __init acpi_bus_init_irq(void)
        char *message = NULL;
 
 
-       /* 
+       /*
         * Let the system know what interrupt model we are using by
         * evaluating the \_PIC object, if exists.
         */
@@ -579,7 +680,7 @@ static int __init acpi_bus_init_irq(void)
        return 0;
 }
 
-acpi_native_uint acpi_gbl_permanent_mmap;
+u8 acpi_gbl_permanent_mmap;
 
 
 void __init acpi_early_init(void)
@@ -620,22 +721,20 @@ void __init acpi_early_init(void)
 
 #ifdef CONFIG_X86
        if (!acpi_ioapic) {
-               extern acpi_interrupt_flags acpi_sci_flags;
-
                /* compatible (0) means level (3) */
-               if (acpi_sci_flags.trigger == 0)
-                       acpi_sci_flags.trigger = 3;
-
+               if (!(acpi_sci_flags & ACPI_MADT_TRIGGER_MASK)) {
+                       acpi_sci_flags &= ~ACPI_MADT_TRIGGER_MASK;
+                       acpi_sci_flags |= ACPI_MADT_TRIGGER_LEVEL;
+               }
                /* Set PIC-mode SCI trigger type */
-               acpi_pic_sci_set_trigger(acpi_fadt.sci_int,
-                                        acpi_sci_flags.trigger);
+               acpi_pic_sci_set_trigger(acpi_gbl_FADT.sci_interrupt,
+                                        (acpi_sci_flags & ACPI_MADT_TRIGGER_MASK) >> 2);
        } else {
-               extern int acpi_sci_override_gsi;
                /*
-                * now that acpi_fadt is initialized,
+                * now that acpi_gbl_FADT is initialized,
                 * update it with result from INT_SRC_OVR parsing
                 */
-               acpi_fadt.sci_int = acpi_sci_override_gsi;
+               acpi_gbl_FADT.sci_interrupt = acpi_sci_override_gsi;
        }
 #endif
 
@@ -677,18 +776,17 @@ static int __init acpi_bus_init(void)
                       "Unable to initialize ACPI OS objects\n");
                goto error1;
        }
-#ifdef CONFIG_ACPI_EC
+
        /*
         * ACPI 2.0 requires the EC driver to be loaded and work before
         * the EC device is found in the namespace (i.e. before acpi_initialize_objects()
         * is called).
         *
-        * This is accomplished by looking for the ECDT table, and getting 
+        * This is accomplished by looking for the ECDT table, and getting
         * the EC parameters out of that.
         */
        status = acpi_ec_ecdt_probe();
        /* Ignore result. Not having an ECDT is not fatal. */
-#endif
 
        status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
        if (ACPI_FAILURE(status)) {
@@ -696,8 +794,17 @@ static int __init acpi_bus_init(void)
                goto error1;
        }
 
+       /*
+        * Maybe EC region is required at bus_scan/acpi_get_devices. So it
+        * is necessary to enable it as early as possible.
+        */
+       acpi_boot_ec_enable();
+
        printk(KERN_INFO PREFIX "Interpreter enabled\n");
 
+       /* Initialize sleep structures */
+       acpi_sleep_init();
+
        /*
         * Get the system interrupt model and evaluate \_PIC.
         */
@@ -730,7 +837,7 @@ static int __init acpi_bus_init(void)
        return -ENODEV;
 }
 
-decl_subsys(acpi, NULL, NULL);
+struct kobject *acpi_kobj;
 
 static int __init acpi_init(void)
 {
@@ -742,27 +849,42 @@ static int __init acpi_init(void)
                return -ENODEV;
        }
 
-       result = firmware_register(&acpi_subsys);
-       if (result < 0)
-               printk(KERN_WARNING "%s: firmware_register error: %d\n",
-                       __FUNCTION__, result);
+       acpi_kobj = kobject_create_and_add("acpi", firmware_kobj);
+       if (!acpi_kobj) {
+               printk(KERN_WARNING "%s: kset create error\n", __func__);
+               acpi_kobj = NULL;
+       }
 
+       init_acpi_device_notify();
        result = acpi_bus_init();
 
        if (!result) {
-#ifdef CONFIG_PM_LEGACY
-               if (!PM_IS_ACTIVE())
-                       pm_active = 1;
+               pci_mmcfg_late_init();
+               if (!(pm_flags & PM_APM))
+                       pm_flags |= PM_ACPI;
                else {
                        printk(KERN_INFO PREFIX
                               "APM is already active, exiting\n");
                        disable_acpi();
                        result = -ENODEV;
                }
-#endif
        } else
                disable_acpi();
 
+       if (acpi_disabled)
+               return result;
+
+       /*
+        * If the laptop falls into the DMI check table, the power state check
+        * will be disabled in the course of device power transistion.
+        */
+       dmi_check_system(power_nocheck_dmi_table);
+
+       acpi_scan_init();
+       acpi_ec_init();
+       acpi_power_init();
+       acpi_system_init();
+       acpi_debug_init();
        return result;
 }