Merge branch 'acpi-pm' into fixes
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Sat, 23 Feb 2013 22:15:43 +0000 (23:15 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Sat, 23 Feb 2013 22:15:43 +0000 (23:15 +0100)
* acpi-pm:
  ACPI / PM: Take unusual configurations of power resources into account

1  2 
drivers/acpi/internal.h
drivers/acpi/scan.c

diff --combined drivers/acpi/internal.h
index 79092328cf064b237c090690c29b64ec1a733063,6306d2ecb428aa6b969d376a98b560f5f506f2a6..4d77716ededdd50d16071b54b780d76ce113a580
  
  int init_acpi_device_notify(void);
  int acpi_scan_init(void);
 +void acpi_pci_root_init(void);
 +void acpi_pci_link_init(void);
 +void acpi_platform_init(void);
  int acpi_sysfs_init(void);
 +void acpi_csrt_init(void);
 +#ifdef CONFIG_ACPI_CONTAINER
 +void acpi_container_init(void);
 +#else
 +static inline void acpi_container_init(void) {}
 +#endif
  
  #ifdef CONFIG_DEBUG_FS
  extern struct dentry *acpi_debugfs_dir;
@@@ -65,7 -56,7 +65,7 @@@ int acpi_extract_power_resources(union 
                                 struct list_head *list);
  int acpi_add_power_resource(acpi_handle handle);
  void acpi_power_add_remove_device(struct acpi_device *adev, bool add);
- int acpi_power_min_system_level(struct list_head *list);
+ int acpi_power_wakeup_list_init(struct list_head *list, int *system_level);
  int acpi_device_sleep_wake(struct acpi_device *dev,
                             int enable, int sleep_state, int dev_state);
  int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
@@@ -94,6 -85,7 +94,6 @@@ struct acpi_ec 
  
  extern struct acpi_ec *first_ec;
  
 -int acpi_pci_root_init(void);
  int acpi_ec_init(void);
  int acpi_ec_ecdt_probe(void);
  int acpi_boot_ec_enable(void);
@@@ -125,4 -117,6 +125,4 @@@ static inline void suspend_nvs_restore(
    -------------------------------------------------------------------------- */
  struct platform_device;
  
 -struct platform_device *acpi_create_platform_device(struct acpi_device *adev);
 -
  #endif /* _ACPI_INTERNAL_H_ */
diff --combined drivers/acpi/scan.c
index daee7497efd30137f2dddef2497f9968555d4e26,560b05566f3b8ede33e0b14fa84ee0e3d8575131..1c6104c942c5ba5a8f6b556892e71a587a819e70
@@@ -29,10 -29,29 +29,10 @@@ extern struct acpi_device *acpi_root
  
  static const char *dummy_hid = "device";
  
 -/*
 - * The following ACPI IDs are known to be suitable for representing as
 - * platform devices.
 - */
 -static const struct acpi_device_id acpi_platform_device_ids[] = {
 -
 -      { "PNP0D40" },
 -
 -      /* Haswell LPSS devices */
 -      { "INT33C0", 0 },
 -      { "INT33C1", 0 },
 -      { "INT33C2", 0 },
 -      { "INT33C3", 0 },
 -      { "INT33C4", 0 },
 -      { "INT33C5", 0 },
 -      { "INT33C6", 0 },
 -      { "INT33C7", 0 },
 -
 -      { }
 -};
 -
  static LIST_HEAD(acpi_device_list);
  static LIST_HEAD(acpi_bus_id_list);
 +static DEFINE_MUTEX(acpi_scan_lock);
 +static LIST_HEAD(acpi_scan_handlers_list);
  DEFINE_MUTEX(acpi_device_lock);
  LIST_HEAD(acpi_wakeup_device_list);
  
@@@ -42,27 -61,6 +42,27 @@@ struct acpi_device_bus_id
        struct list_head node;
  };
  
 +void acpi_scan_lock_acquire(void)
 +{
 +      mutex_lock(&acpi_scan_lock);
 +}
 +EXPORT_SYMBOL_GPL(acpi_scan_lock_acquire);
 +
 +void acpi_scan_lock_release(void)
 +{
 +      mutex_unlock(&acpi_scan_lock);
 +}
 +EXPORT_SYMBOL_GPL(acpi_scan_lock_release);
 +
 +int acpi_scan_add_handler(struct acpi_scan_handler *handler)
 +{
 +      if (!handler || !handler->attach)
 +              return -EINVAL;
 +
 +      list_add_tail(&handler->list_node, &acpi_scan_handlers_list);
 +      return 0;
 +}
 +
  /*
   * Creates hid/cid(s) string needed for modalias and uevent
   * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
@@@ -117,7 -115,7 +117,7 @@@ static DEVICE_ATTR(modalias, 0444, acpi
   */
  void acpi_bus_hot_remove_device(void *context)
  {
 -      struct acpi_eject_event *ej_event = (struct acpi_eject_event *) context;
 +      struct acpi_eject_event *ej_event = context;
        struct acpi_device *device = ej_event->device;
        acpi_handle handle = device->handle;
        acpi_handle temp;
        acpi_status status = AE_OK;
        u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
  
 -      ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 -              "Hot-removing device %s...\n", dev_name(&device->dev)));
 +      mutex_lock(&acpi_scan_lock);
  
 -      if (acpi_bus_trim(device)) {
 -              printk(KERN_ERR PREFIX
 -                              "Removing device failed\n");
 -              goto err_out;
 +      /* If there is no handle, the device node has been unregistered. */
 +      if (!device->handle) {
 +              dev_dbg(&device->dev, "ACPI handle missing\n");
 +              put_device(&device->dev);
 +              goto out;
        }
  
 -      /* device has been freed */
 -      device = NULL;
 +      ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 +              "Hot-removing device %s...\n", dev_name(&device->dev)));
  
 -      /* power off device */
 -      status = acpi_evaluate_object(handle, "_PS3", NULL, NULL);
 -      if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
 -              printk(KERN_WARNING PREFIX
 -                              "Power-off device failed\n");
 +      acpi_bus_trim(device);
 +      /* Device node has been unregistered. */
 +      put_device(&device->dev);
 +      device = NULL;
  
        if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) {
                arg_list.count = 1;
        status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
        if (ACPI_FAILURE(status)) {
                if (status != AE_NOT_FOUND)
 -                      printk(KERN_WARNING PREFIX
 -                                      "Eject device failed\n");
 -              goto err_out;
 -      }
 +                      acpi_handle_warn(handle, "Eject failed\n");
  
 -      kfree(context);
 -      return;
 +              /* Tell the firmware the hot-remove operation has failed. */
 +              acpi_evaluate_hotplug_ost(handle, ej_event->event,
 +                                        ost_code, NULL);
 +      }
  
 -err_out:
 -      /* Inform firmware the hot-remove operation has completed w/ error */
 -      (void) acpi_evaluate_hotplug_ost(handle,
 -                              ej_event->event, ost_code, NULL);
 + out:
 +      mutex_unlock(&acpi_scan_lock);
        kfree(context);
        return;
  }
@@@ -215,10 -217,12 +215,10 @@@ acpi_eject_store(struct device *d, stru
        if ((!count) || (buf[0] != '1')) {
                return -EINVAL;
        }
 -#ifndef FORCE_EJECT
 -      if (acpi_device->driver == NULL) {
 +      if (!acpi_device->driver && !acpi_device->handler) {
                ret = -ENODEV;
                goto err;
        }
 -#endif
        status = acpi_get_type(acpi_device->handle, &type);
        if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) {
                ret = -ENODEV;
                goto err;
        }
  
 +      get_device(&acpi_device->dev);
        ej_event->device = acpi_device;
        if (acpi_device->flags.eject_pending) {
                /* event originated from ACPI eject notification */
                        ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
        }
  
 -      acpi_os_hotplug_execute(acpi_bus_hot_remove_device, (void *)ej_event);
 +      status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event);
 +      if (ACPI_FAILURE(status)) {
 +              put_device(&acpi_device->dev);
 +              kfree(ej_event);
 +      }
  err:
        return ret;
  }
@@@ -496,9 -495,9 +496,9 @@@ const struct acpi_device_id *acpi_match
                                               const struct device *dev)
  {
        struct acpi_device *adev;
 +      acpi_handle handle = ACPI_HANDLE(dev);
  
 -      if (!ids || !ACPI_HANDLE(dev)
 -          || ACPI_FAILURE(acpi_bus_get_device(ACPI_HANDLE(dev), &adev)))
 +      if (!ids || !handle || acpi_bus_get_device(handle, &adev))
                return NULL;
  
        return __acpi_match_device(adev, ids);
@@@ -642,9 -641,8 +642,9 @@@ static int acpi_device_probe(struct dev
                        ret = acpi_device_install_notify_handler(acpi_dev);
                        if (ret) {
                                if (acpi_drv->ops.remove)
 -                                      acpi_drv->ops.remove(acpi_dev,
 -                                                   acpi_dev->removal_type);
 +                                      acpi_drv->ops.remove(acpi_dev);
 +                              acpi_dev->driver = NULL;
 +                              acpi_dev->driver_data = NULL;
                                return ret;
                        }
                }
@@@ -666,7 -664,7 +666,7 @@@ static int acpi_device_remove(struct de
                if (acpi_drv->ops.notify)
                        acpi_device_remove_notify_handler(acpi_dev);
                if (acpi_drv->ops.remove)
 -                      acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type);
 +                      acpi_drv->ops.remove(acpi_dev);
        }
        acpi_dev->driver = NULL;
        acpi_dev->driver_data = NULL;
@@@ -798,12 -796,10 +798,12 @@@ static void acpi_device_unregister(stru
  
        device_del(&device->dev);
        /*
 -       * Drop the reference counts of all power resources the device depends
 -       * on and turn off the ones that have no more references.
 +       * Transition the device to D3cold to drop the reference counts of all
 +       * power resources the device depends on and turn off the ones that have
 +       * no more references.
         */
 -      acpi_power_transition(device, ACPI_STATE_D3_COLD);
 +      acpi_device_set_power(device, ACPI_STATE_D3_COLD);
 +      device->handle = NULL;
        put_device(&device->dev);
  }
  
@@@ -891,23 -887,29 +891,23 @@@ EXPORT_SYMBOL(acpi_bus_unregister_drive
     -------------------------------------------------------------------------- */
  static struct acpi_device *acpi_bus_get_parent(acpi_handle handle)
  {
 +      struct acpi_device *device = NULL;
        acpi_status status;
 -      int ret;
 -      struct acpi_device *device;
  
        /*
         * Fixed hardware devices do not appear in the namespace and do not
         * have handles, but we fabricate acpi_devices for them, so we have
         * to deal with them specially.
         */
 -      if (handle == NULL)
 +      if (!handle)
                return acpi_root;
  
        do {
                status = acpi_get_parent(handle, &handle);
 -              if (status == AE_NULL_ENTRY)
 -                      return NULL;
                if (ACPI_FAILURE(status))
 -                      return acpi_root;
 -
 -              ret = acpi_bus_get_device(handle, &device);
 -              if (ret == 0)
 -                      return device;
 -      } while (1);
 +                      return status == AE_NULL_ENTRY ? NULL : acpi_root;
 +      } while (acpi_bus_get_device(handle, &device));
 +      return device;
  }
  
  acpi_status
@@@ -1002,7 -1004,14 +1002,14 @@@ static int acpi_bus_extract_wakeup_devi
        if (!list_empty(&wakeup->resources)) {
                int sleep_state;
  
-               sleep_state = acpi_power_min_system_level(&wakeup->resources);
+               err = acpi_power_wakeup_list_init(&wakeup->resources,
+                                                 &sleep_state);
+               if (err) {
+                       acpi_handle_warn(handle, "Retrieving current states "
+                                        "of wakeup power resources failed\n");
+                       acpi_power_resources_list_free(&wakeup->resources);
+                       goto out;
+               }
                if (sleep_state < wakeup->sleep_state) {
                        acpi_handle_warn(handle, "Overriding _PRW sleep state "
                                         "(S%d) by S%d from power resources\n",
@@@ -1443,21 -1452,19 +1450,21 @@@ void acpi_init_device_object(struct acp
        acpi_device_get_busid(device);
        acpi_device_set_id(device);
        acpi_bus_get_flags(device);
 +      device->flags.match_driver = false;
        device_initialize(&device->dev);
        dev_set_uevent_suppress(&device->dev, true);
  }
  
  void acpi_device_add_finalize(struct acpi_device *device)
  {
 +      device->flags.match_driver = true;
        dev_set_uevent_suppress(&device->dev, false);
        kobject_uevent(&device->dev.kobj, KOBJ_ADD);
  }
  
  static int acpi_add_single_object(struct acpi_device **child,
                                  acpi_handle handle, int type,
 -                                unsigned long long sta, bool match_driver)
 +                                unsigned long long sta)
  {
        int result;
        struct acpi_device *device;
        acpi_bus_get_power_flags(device);
        acpi_bus_get_wakeup_device_flags(device);
  
 -      device->flags.match_driver = match_driver;
        result = acpi_device_add(device, acpi_device_release);
        if (result) {
                acpi_device_release(&device->dev);
@@@ -1565,10 -1573,12 +1572,10 @@@ static acpi_status acpi_bus_check_add(a
                return AE_CTRL_DEPTH;
        }
  
 -      acpi_add_single_object(&device, handle, type, sta, false);
 +      acpi_add_single_object(&device, handle, type, sta);
        if (!device)
                return AE_CTRL_DEPTH;
  
 -      device->flags.match_driver = true;
 -
   out:
        if (!*return_value)
                *return_value = device;
        return AE_OK;
  }
  
 +static int acpi_scan_do_attach_handler(struct acpi_device *device, char *id)
 +{
 +      struct acpi_scan_handler *handler;
 +
 +      list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) {
 +              const struct acpi_device_id *devid;
 +
 +              for (devid = handler->ids; devid->id[0]; devid++) {
 +                      int ret;
 +
 +                      if (strcmp((char *)devid->id, id))
 +                              continue;
 +
 +                      ret = handler->attach(device, devid);
 +                      if (ret > 0) {
 +                              device->handler = handler;
 +                              return ret;
 +                      } else if (ret < 0) {
 +                              return ret;
 +                      }
 +              }
 +      }
 +      return 0;
 +}
 +
 +static int acpi_scan_attach_handler(struct acpi_device *device)
 +{
 +      struct acpi_hardware_id *hwid;
 +      int ret = 0;
 +
 +      list_for_each_entry(hwid, &device->pnp.ids, list) {
 +              ret = acpi_scan_do_attach_handler(device, hwid->id);
 +              if (ret)
 +                      break;
 +
 +      }
 +      return ret;
 +}
 +
  static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
                                          void *not_used, void **ret_not_used)
  {
 -      acpi_status status = AE_OK;
        struct acpi_device *device;
        unsigned long long sta_not_used;
 -      int type_not_used;
 +      int ret;
  
        /*
         * Ignore errors ignored by acpi_bus_check_add() to avoid terminating
         * namespace walks prematurely.
         */
 -      if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used))
 +      if (acpi_bus_type_and_status(handle, &ret, &sta_not_used))
                return AE_OK;
  
        if (acpi_bus_get_device(handle, &device))
                return AE_CTRL_DEPTH;
  
 -      if (!acpi_match_device_ids(device, acpi_platform_device_ids)) {
 -              /* This is a known good platform device. */
 -              acpi_create_platform_device(device);
 -      } else if (device_attach(&device->dev) < 0) {
 -              status = AE_CTRL_DEPTH;
 -      }
 -      return status;
 -}
 -
 -static int acpi_bus_scan(acpi_handle handle)
 -{
 -      void *device = NULL;
 -
 -      if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device)))
 -              acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
 -                                  acpi_bus_check_add, NULL, NULL, &device);
 -
 -      if (!device)
 -              return -ENODEV;
 -
 -      if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL)))
 -              acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
 -                                  acpi_bus_device_attach, NULL, NULL, NULL);
 +      ret = acpi_scan_attach_handler(device);
 +      if (ret)
 +              return ret > 0 ? AE_OK : AE_CTRL_DEPTH;
  
 -      return 0;
 +      ret = device_attach(&device->dev);
 +      return ret >= 0 ? AE_OK : AE_CTRL_DEPTH;
  }
  
  /**
 - * acpi_bus_add - Add ACPI device node objects in a given namespace scope.
 + * acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
   * @handle: Root of the namespace scope to scan.
   *
   * Scan a given ACPI tree (probably recently hot-plugged) and create and add
   * there has been a real error.  There just have been no suitable ACPI objects
   * in the table trunk from which the kernel could create a device and add an
   * appropriate driver.
 + *
 + * Must be called under acpi_scan_lock.
   */
 -int acpi_bus_add(acpi_handle handle)
 +int acpi_bus_scan(acpi_handle handle)
  {
 -      int err;
 +      void *device = NULL;
 +      int error = 0;
  
 -      err = acpi_bus_scan(handle);
 -      if (err)
 -              return err;
 +      if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device)))
 +              acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
 +                                  acpi_bus_check_add, NULL, NULL, &device);
  
 -      acpi_update_all_gpes();
 -      return 0;
 +      if (!device)
 +              error = -ENODEV;
 +      else if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL)))
 +              acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
 +                                  acpi_bus_device_attach, NULL, NULL, NULL);
 +
 +      return error;
  }
 -EXPORT_SYMBOL(acpi_bus_add);
 +EXPORT_SYMBOL(acpi_bus_scan);
  
  static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used,
                                          void *not_used, void **ret_not_used)
        struct acpi_device *device = NULL;
  
        if (!acpi_bus_get_device(handle, &device)) {
 +              struct acpi_scan_handler *dev_handler = device->handler;
 +
                device->removal_type = ACPI_BUS_REMOVAL_EJECT;
 -              device_release_driver(&device->dev);
 +              if (dev_handler) {
 +                      if (dev_handler->detach)
 +                              dev_handler->detach(device);
 +
 +                      device->handler = NULL;
 +              } else {
 +                      device_release_driver(&device->dev);
 +              }
        }
        return AE_OK;
  }
@@@ -1705,13 -1679,7 +1712,13 @@@ static acpi_status acpi_bus_remove(acpi
        return AE_OK;
  }
  
 -int acpi_bus_trim(struct acpi_device *start)
 +/**
 + * acpi_bus_trim - Remove ACPI device node and all of its descendants
 + * @start: Root of the ACPI device nodes subtree to remove.
 + *
 + * Must be called under acpi_scan_lock.
 + */
 +void acpi_bus_trim(struct acpi_device *start)
  {
        /*
         * Execute acpi_bus_device_detach() as a post-order callback to detach
        acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL,
                            acpi_bus_remove, NULL, NULL);
        acpi_bus_remove(start->handle, 0, NULL, NULL);
 -      return 0;
  }
  EXPORT_SYMBOL_GPL(acpi_bus_trim);
  
  static int acpi_bus_scan_fixed(void)
  {
        int result = 0;
 -      struct acpi_device *device = NULL;
  
        /*
         * Enumerate all fixed-feature devices.
         */
 -      if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) {
 +      if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) {
 +              struct acpi_device *device = NULL;
 +
                result = acpi_add_single_object(&device, NULL,
                                                ACPI_BUS_TYPE_POWER_BUTTON,
 -                                              ACPI_STA_DEFAULT, true);
 +                                              ACPI_STA_DEFAULT);
 +              if (result)
 +                      return result;
 +
 +              result = device_attach(&device->dev);
 +              if (result < 0)
 +                      return result;
 +
                device_init_wakeup(&device->dev, true);
        }
  
 -      if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) {
 +      if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) {
 +              struct acpi_device *device = NULL;
 +
                result = acpi_add_single_object(&device, NULL,
                                                ACPI_BUS_TYPE_SLEEP_BUTTON,
 -                                              ACPI_STA_DEFAULT, true);
 +                                              ACPI_STA_DEFAULT);
 +              if (result)
 +                      return result;
 +
 +              result = device_attach(&device->dev);
        }
  
 -      return result;
 +      return result < 0 ? result : 0;
  }
  
  int __init acpi_scan_init(void)
        }
  
        acpi_pci_root_init();
 +      acpi_pci_link_init();
 +      acpi_platform_init();
 +      acpi_csrt_init();
 +      acpi_container_init();
  
 +      mutex_lock(&acpi_scan_lock);
        /*
         * Enumerate devices in the ACPI namespace.
         */
        result = acpi_bus_scan(ACPI_ROOT_OBJECT);
        if (result)
 -              return result;
 +              goto out;
  
        result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root);
 -      if (!result)
 -              result = acpi_bus_scan_fixed();
 -
        if (result)
 +              goto out;
 +
 +      result = acpi_bus_scan_fixed();
 +      if (result) {
                acpi_device_unregister(acpi_root);
 -      else
 -              acpi_update_all_gpes();
 +              goto out;
 +      }
 +
 +      acpi_update_all_gpes();
  
 + out:
 +      mutex_unlock(&acpi_scan_lock);
        return result;
  }