zram: add set_max_streams knob
[firefly-linux-kernel-4.4.55.git] / drivers / acpi / glue.c
index ef6f155469b58ed370aa9d1232d5c2e6bb14c29a..238412077c8394ee42ce66af4609697c670d6405 100644 (file)
@@ -36,12 +36,11 @@ int register_acpi_bus_type(struct acpi_bus_type *type)
 {
        if (acpi_disabled)
                return -ENODEV;
-       if (type && type->bus && type->find_device) {
+       if (type && type->match && type->find_device) {
                down_write(&bus_type_sem);
                list_add_tail(&type->list, &bus_type_list);
                up_write(&bus_type_sem);
-               printk(KERN_INFO PREFIX "bus type %s registered\n",
-                      type->bus->name);
+               printk(KERN_INFO PREFIX "bus type %s registered\n", type->name);
                return 0;
        }
        return -ENODEV;
@@ -56,24 +55,21 @@ int unregister_acpi_bus_type(struct acpi_bus_type *type)
                down_write(&bus_type_sem);
                list_del_init(&type->list);
                up_write(&bus_type_sem);
-               printk(KERN_INFO PREFIX "ACPI bus type %s unregistered\n",
-                      type->bus->name);
+               printk(KERN_INFO PREFIX "bus type %s unregistered\n",
+                      type->name);
                return 0;
        }
        return -ENODEV;
 }
 EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
 
-static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
+static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
 {
        struct acpi_bus_type *tmp, *ret = NULL;
 
-       if (!type)
-               return NULL;
-
        down_read(&bus_type_sem);
        list_for_each_entry(tmp, &bus_type_list, list) {
-               if (tmp->bus == type) {
+               if (tmp->match(dev)) {
                        ret = tmp;
                        break;
                }
@@ -82,48 +78,99 @@ static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
        return ret;
 }
 
-static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
+static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used,
+                                 void *not_used, void **ret_p)
 {
-       struct acpi_bus_type *tmp;
-       int ret = -ENODEV;
+       struct acpi_device *adev = NULL;
 
-       down_read(&bus_type_sem);
-       list_for_each_entry(tmp, &bus_type_list, list) {
-               if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) {
-                       ret = 0;
-                       break;
-               }
+       acpi_bus_get_device(handle, &adev);
+       if (adev) {
+               *ret_p = handle;
+               return AE_CTRL_TERMINATE;
        }
-       up_read(&bus_type_sem);
-       return ret;
+       return AE_OK;
+}
+
+static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge)
+{
+       unsigned long long sta;
+       acpi_status status;
+
+       status = acpi_bus_get_status_handle(handle, &sta);
+       if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
+               return false;
+
+       if (is_bridge) {
+               void *test = NULL;
+
+               /* Check if this object has at least one child device. */
+               acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+                                   acpi_dev_present, NULL, NULL, &test);
+               return !!test;
+       }
+       return true;
 }
 
-static acpi_status do_acpi_find_child(acpi_handle handle, u32 lvl_not_used,
-                                     void *addr_p, void **ret_p)
+struct find_child_context {
+       u64 addr;
+       bool is_bridge;
+       acpi_handle ret;
+       bool ret_checked;
+};
+
+static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
+                                void *data, void **not_used)
 {
+       struct find_child_context *context = data;
        unsigned long long addr;
        acpi_status status;
 
        status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
-       if (ACPI_SUCCESS(status) && addr == *((u64 *)addr_p)) {
-               *ret_p = handle;
+       if (ACPI_FAILURE(status) || addr != context->addr)
+               return AE_OK;
+
+       if (!context->ret) {
+               /* This is the first matching object.  Save its handle. */
+               context->ret = handle;
+               return AE_OK;
+       }
+       /*
+        * There is more than one matching object with the same _ADR value.
+        * That really is unexpected, so we are kind of beyond the scope of the
+        * spec here.  We have to choose which one to return, though.
+        *
+        * First, check if the previously found object is good enough and return
+        * its handle if so.  Second, check the same for the object that we've
+        * just found.
+        */
+       if (!context->ret_checked) {
+               if (acpi_extra_checks_passed(context->ret, context->is_bridge))
+                       return AE_CTRL_TERMINATE;
+               else
+                       context->ret_checked = true;
+       }
+       if (acpi_extra_checks_passed(handle, context->is_bridge)) {
+               context->ret = handle;
                return AE_CTRL_TERMINATE;
        }
        return AE_OK;
 }
 
-acpi_handle acpi_get_child(acpi_handle parent, u64 address)
+acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge)
 {
-       void *ret = NULL;
-
-       if (!parent)
-               return NULL;
-
-       acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, NULL,
-                           do_acpi_find_child, &address, &ret);
-       return (acpi_handle)ret;
+       if (parent) {
+               struct find_child_context context = {
+                       .addr = addr,
+                       .is_bridge = is_bridge,
+               };
+
+               acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child,
+                                   NULL, &context, NULL);
+               return context.ret;
+       }
+       return NULL;
 }
-EXPORT_SYMBOL(acpi_get_child);
+EXPORT_SYMBOL_GPL(acpi_find_child);
 
 static int acpi_bind_one(struct device *dev, acpi_handle handle)
 {
@@ -261,29 +308,12 @@ err:
 
 static int acpi_platform_notify(struct device *dev)
 {
-       struct acpi_bus_type *type;
+       struct acpi_bus_type *type = acpi_get_bus_type(dev);
        acpi_handle handle;
        int ret;
 
        ret = acpi_bind_one(dev, NULL);
-       if (ret && (!dev->bus || !dev->parent)) {
-               /* bridge devices genernally haven't bus or parent */
-               ret = acpi_find_bridge_device(dev, &handle);
-               if (!ret) {
-                       ret = acpi_bind_one(dev, handle);
-                       if (ret)
-                               goto out;
-               }
-       }
-
-       type = acpi_get_bus_type(dev->bus);
-       if (ret) {
-               if (!type || !type->find_device) {
-                       DBG("No ACPI bus support for %s\n", dev_name(dev));
-                       ret = -EINVAL;
-                       goto out;
-               }
-
+       if (ret && type) {
                ret = type->find_device(dev, &handle);
                if (ret) {
                        DBG("Unable to get handle for %s\n", dev_name(dev));
@@ -316,7 +346,7 @@ static int acpi_platform_notify_remove(struct device *dev)
 {
        struct acpi_bus_type *type;
 
-       type = acpi_get_bus_type(dev->bus);
+       type = acpi_get_bus_type(dev);
        if (type && type->cleanup)
                type->cleanup(dev);