Merge branches 'acpi-dptf' and 'acpi-messages'
[linux-2.6-microblaze.git] / drivers / acpi / scan.c
index 0c916c1..57507b6 100644 (file)
@@ -49,12 +49,6 @@ static DEFINE_MUTEX(acpi_hp_context_lock);
  */
 static u64 spcr_uart_addr;
 
-struct acpi_dep_data {
-       struct list_head node;
-       acpi_handle supplier;
-       acpi_handle consumer;
-};
-
 void acpi_scan_lock_acquire(void)
 {
        mutex_lock(&acpi_scan_lock);
@@ -614,11 +608,6 @@ struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle)
        return handle_to_device(handle, get_acpi_device);
 }
 
-void acpi_bus_put_acpi_device(struct acpi_device *adev)
-{
-       acpi_dev_put(adev);
-}
-
 static struct acpi_device_bus_id *acpi_device_bus_id_match(const char *dev_id)
 {
        struct acpi_device_bus_id *acpi_device_bus_id;
@@ -646,24 +635,29 @@ static int acpi_device_set_name(struct acpi_device *device,
        return 0;
 }
 
-int acpi_device_add(struct acpi_device *device,
-                   void (*release)(struct device *))
+static int acpi_tie_acpi_dev(struct acpi_device *adev)
 {
-       struct acpi_device_bus_id *acpi_device_bus_id;
-       int result;
+       acpi_handle handle = adev->handle;
+       acpi_status status;
 
-       if (device->handle) {
-               acpi_status status;
+       if (!handle)
+               return 0;
 
-               status = acpi_attach_data(device->handle, acpi_scan_drop_device,
-                                         device);
-               if (ACPI_FAILURE(status)) {
-                       acpi_handle_err(device->handle,
-                                       "Unable to attach device data\n");
-                       return -ENODEV;
-               }
+       status = acpi_attach_data(handle, acpi_scan_drop_device, adev);
+       if (ACPI_FAILURE(status)) {
+               acpi_handle_err(handle, "Unable to attach device data\n");
+               return -ENODEV;
        }
 
+       return 0;
+}
+
+static int __acpi_device_add(struct acpi_device *device,
+                            void (*release)(struct device *))
+{
+       struct acpi_device_bus_id *acpi_device_bus_id;
+       int result;
+
        /*
         * Linkage
         * -------
@@ -752,6 +746,17 @@ err_unlock:
        return result;
 }
 
+int acpi_device_add(struct acpi_device *adev, void (*release)(struct device *))
+{
+       int ret;
+
+       ret = acpi_tie_acpi_dev(adev);
+       if (ret)
+               return ret;
+
+       return __acpi_device_add(adev, release);
+}
+
 /* --------------------------------------------------------------------------
                                  Device Enumeration
    -------------------------------------------------------------------------- */
@@ -1672,8 +1677,16 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
        device_initialize(&device->dev);
        dev_set_uevent_suppress(&device->dev, true);
        acpi_init_coherency(device);
-       /* Assume there are unmet deps to start with. */
-       device->dep_unmet = 1;
+}
+
+static void acpi_scan_dep_init(struct acpi_device *adev)
+{
+       struct acpi_dep_data *dep;
+
+       list_for_each_entry(dep, &acpi_dep_list, node) {
+               if (dep->consumer == adev->handle)
+                       adev->dep_unmet++;
+       }
 }
 
 void acpi_device_add_finalize(struct acpi_device *device)
@@ -1689,9 +1702,10 @@ static void acpi_scan_init_status(struct acpi_device *adev)
 }
 
 static int acpi_add_single_object(struct acpi_device **child,
-                                 acpi_handle handle, int type)
+                                 acpi_handle handle, int type, bool dep_init)
 {
        struct acpi_device *device;
+       bool release_dep_lock = false;
        int result;
 
        device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
@@ -1704,13 +1718,32 @@ static int acpi_add_single_object(struct acpi_device **child,
         * acpi_bus_get_status() and use its quirk handling.  Note that
         * this must be done before the get power-/wakeup_dev-flags calls.
         */
-       if (type == ACPI_BUS_TYPE_DEVICE || type == ACPI_BUS_TYPE_PROCESSOR)
+       if (type == ACPI_BUS_TYPE_DEVICE || type == ACPI_BUS_TYPE_PROCESSOR) {
+               if (dep_init) {
+                       mutex_lock(&acpi_dep_list_lock);
+                       /*
+                        * Hold the lock until the acpi_tie_acpi_dev() call
+                        * below to prevent concurrent acpi_scan_clear_dep()
+                        * from deleting a dependency list entry without
+                        * updating dep_unmet for the device.
+                        */
+                       release_dep_lock = true;
+                       acpi_scan_dep_init(device);
+               }
                acpi_scan_init_status(device);
+       }
 
        acpi_bus_get_power_flags(device);
        acpi_bus_get_wakeup_device_flags(device);
 
-       result = acpi_device_add(device, acpi_device_release);
+       result = acpi_tie_acpi_dev(device);
+
+       if (release_dep_lock)
+               mutex_unlock(&acpi_dep_list_lock);
+
+       if (!result)
+               result = __acpi_device_add(device, acpi_device_release);
+
        if (result) {
                acpi_device_release(&device->dev);
                return result;
@@ -1887,22 +1920,6 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
        return count;
 }
 
-static void acpi_scan_dep_init(struct acpi_device *adev)
-{
-       struct acpi_dep_data *dep;
-
-       adev->dep_unmet = 0;
-
-       mutex_lock(&acpi_dep_list_lock);
-
-       list_for_each_entry(dep, &acpi_dep_list, node) {
-               if (dep->consumer == adev->handle)
-                       adev->dep_unmet++;
-       }
-
-       mutex_unlock(&acpi_dep_list_lock);
-}
-
 static bool acpi_bus_scan_second_pass;
 
 static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
@@ -1950,19 +1967,15 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
                return AE_OK;
        }
 
-       acpi_add_single_object(&device, handle, type);
-       if (!device)
-               return AE_CTRL_DEPTH;
-
-       acpi_scan_init_hotplug(device);
        /*
         * If check_dep is true at this point, the device has no dependencies,
         * or the creation of the device object would have been postponed above.
         */
-       if (check_dep)
-               device->dep_unmet = 0;
-       else
-               acpi_scan_dep_init(device);
+       acpi_add_single_object(&device, handle, type, !check_dep);
+       if (!device)
+               return AE_CTRL_DEPTH;
+
+       acpi_scan_init_hotplug(device);
 
 out:
        if (!*adev_p)
@@ -2112,29 +2125,141 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
                device->handler->hotplug.notify_online(device);
 }
 
-void acpi_walk_dep_device_list(acpi_handle handle)
+static int acpi_dev_get_first_consumer_dev_cb(struct acpi_dep_data *dep, void *data)
 {
-       struct acpi_dep_data *dep, *tmp;
        struct acpi_device *adev;
 
+       adev = acpi_bus_get_acpi_device(dep->consumer);
+       if (adev) {
+               *(struct acpi_device **)data = adev;
+               return 1;
+       }
+       /* Continue parsing if the device object is not present. */
+       return 0;
+}
+
+struct acpi_scan_clear_dep_work {
+       struct work_struct work;
+       struct acpi_device *adev;
+};
+
+static void acpi_scan_clear_dep_fn(struct work_struct *work)
+{
+       struct acpi_scan_clear_dep_work *cdw;
+
+       cdw = container_of(work, struct acpi_scan_clear_dep_work, work);
+
+       acpi_scan_lock_acquire();
+       acpi_bus_attach(cdw->adev, true);
+       acpi_scan_lock_release();
+
+       acpi_dev_put(cdw->adev);
+       kfree(cdw);
+}
+
+static bool acpi_scan_clear_dep_queue(struct acpi_device *adev)
+{
+       struct acpi_scan_clear_dep_work *cdw;
+
+       if (adev->dep_unmet)
+               return false;
+
+       cdw = kmalloc(sizeof(*cdw), GFP_KERNEL);
+       if (!cdw)
+               return false;
+
+       cdw->adev = adev;
+       INIT_WORK(&cdw->work, acpi_scan_clear_dep_fn);
+       /*
+        * Since the work function may block on the lock until the entire
+        * initial enumeration of devices is complete, put it into the unbound
+        * workqueue.
+        */
+       queue_work(system_unbound_wq, &cdw->work);
+
+       return true;
+}
+
+static int acpi_scan_clear_dep(struct acpi_dep_data *dep, void *data)
+{
+       struct acpi_device *adev = acpi_bus_get_acpi_device(dep->consumer);
+
+       if (adev) {
+               adev->dep_unmet--;
+               if (!acpi_scan_clear_dep_queue(adev))
+                       acpi_dev_put(adev);
+       }
+
+       list_del(&dep->node);
+       kfree(dep);
+
+       return 0;
+}
+
+/**
+ * acpi_walk_dep_device_list - Apply a callback to every entry in acpi_dep_list
+ * @handle:    The ACPI handle of the supplier device
+ * @callback:  Pointer to the callback function to apply
+ * @data:      Pointer to some data to pass to the callback
+ *
+ * The return value of the callback determines this function's behaviour. If 0
+ * is returned we continue to iterate over acpi_dep_list. If a positive value
+ * is returned then the loop is broken but this function returns 0. If a
+ * negative value is returned by the callback then the loop is broken and that
+ * value is returned as the final error.
+ */
+static int acpi_walk_dep_device_list(acpi_handle handle,
+                               int (*callback)(struct acpi_dep_data *, void *),
+                               void *data)
+{
+       struct acpi_dep_data *dep, *tmp;
+       int ret = 0;
+
        mutex_lock(&acpi_dep_list_lock);
        list_for_each_entry_safe(dep, tmp, &acpi_dep_list, node) {
                if (dep->supplier == handle) {
-                       acpi_bus_get_device(dep->consumer, &adev);
-
-                       if (adev) {
-                               adev->dep_unmet--;
-                               if (!adev->dep_unmet)
-                                       acpi_bus_attach(adev, true);
-                       }
-
-                       list_del(&dep->node);
-                       kfree(dep);
+                       ret = callback(dep, data);
+                       if (ret)
+                               break;
                }
        }
        mutex_unlock(&acpi_dep_list_lock);
+
+       return ret > 0 ? 0 : ret;
+}
+
+/**
+ * acpi_dev_clear_dependencies - Inform consumers that the device is now active
+ * @supplier: Pointer to the supplier &struct acpi_device
+ *
+ * Clear dependencies on the given device.
+ */
+void acpi_dev_clear_dependencies(struct acpi_device *supplier)
+{
+       acpi_walk_dep_device_list(supplier->handle, acpi_scan_clear_dep, NULL);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_clear_dependencies);
+
+/**
+ * acpi_dev_get_first_consumer_dev - Return ACPI device dependent on @supplier
+ * @supplier: Pointer to the dependee device
+ *
+ * Returns the first &struct acpi_device which declares itself dependent on
+ * @supplier via the _DEP buffer, parsed from the acpi_dep_list.
+ *
+ * The caller is responsible for putting the reference to adev when it is no
+ * longer needed.
+ */
+struct acpi_device *acpi_dev_get_first_consumer_dev(struct acpi_device *supplier)
+{
+       struct acpi_device *adev = NULL;
+
+       acpi_walk_dep_device_list(supplier->handle,
+                                 acpi_dev_get_first_consumer_dev_cb, &adev);
+
+       return adev;
 }
-EXPORT_SYMBOL_GPL(acpi_walk_dep_device_list);
+EXPORT_SYMBOL_GPL(acpi_dev_get_first_consumer_dev);
 
 /**
  * acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
@@ -2224,7 +2349,7 @@ int acpi_bus_register_early_device(int type)
        struct acpi_device *device = NULL;
        int result;
 
-       result = acpi_add_single_object(&device, NULL, type);
+       result = acpi_add_single_object(&device, NULL, type, false);
        if (result)
                return result;
 
@@ -2244,7 +2369,7 @@ static int acpi_bus_scan_fixed(void)
                struct acpi_device *device = NULL;
 
                result = acpi_add_single_object(&device, NULL,
-                                               ACPI_BUS_TYPE_POWER_BUTTON);
+                                               ACPI_BUS_TYPE_POWER_BUTTON, false);
                if (result)
                        return result;
 
@@ -2260,7 +2385,7 @@ static int acpi_bus_scan_fixed(void)
                struct acpi_device *device = NULL;
 
                result = acpi_add_single_object(&device, NULL,
-                                               ACPI_BUS_TYPE_SLEEP_BUTTON);
+                                               ACPI_BUS_TYPE_SLEEP_BUTTON, false);
                if (result)
                        return result;
 
@@ -2361,7 +2486,7 @@ int __init acpi_scan_init(void)
                }
        }
 
-       acpi_turn_off_unused_power_resources(true);
+       acpi_turn_off_unused_power_resources();
 
        acpi_scan_initialized = true;
 
@@ -2409,46 +2534,28 @@ int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr)
        return count;
 }
 
-struct acpi_table_events_work {
-       struct work_struct work;
-       void *table;
-       u32 event;
-};
-
 static void acpi_table_events_fn(struct work_struct *work)
 {
-       struct acpi_table_events_work *tew;
+       acpi_scan_lock_acquire();
+       acpi_bus_scan(ACPI_ROOT_OBJECT);
+       acpi_scan_lock_release();
 
-       tew = container_of(work, struct acpi_table_events_work, work);
-
-       if (tew->event == ACPI_TABLE_EVENT_LOAD) {
-               acpi_scan_lock_acquire();
-               acpi_bus_scan(ACPI_ROOT_OBJECT);
-               acpi_scan_lock_release();
-       }
-
-       kfree(tew);
+       kfree(work);
 }
 
-void acpi_scan_table_handler(u32 event, void *table, void *context)
+void acpi_scan_table_notify(void)
 {
-       struct acpi_table_events_work *tew;
+       struct work_struct *work;
 
        if (!acpi_scan_initialized)
                return;
 
-       if (event != ACPI_TABLE_EVENT_LOAD)
-               return;
-
-       tew = kmalloc(sizeof(*tew), GFP_KERNEL);
-       if (!tew)
+       work = kmalloc(sizeof(*work), GFP_KERNEL);
+       if (!work)
                return;
 
-       INIT_WORK(&tew->work, acpi_table_events_fn);
-       tew->table = table;
-       tew->event = event;
-
-       schedule_work(&tew->work);
+       INIT_WORK(work, acpi_table_events_fn);
+       schedule_work(work);
 }
 
 int acpi_reconfig_notifier_register(struct notifier_block *nb)