Merge tag 'iwlwifi-next-for-kalle-2014-12-30' of https://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / drivers / acpi / scan.c
index 0476e90..16914cc 100644 (file)
@@ -36,6 +36,8 @@ bool acpi_force_hot_remove;
 
 static const char *dummy_hid = "device";
 
+static LIST_HEAD(acpi_dep_list);
+static DEFINE_MUTEX(acpi_dep_list_lock);
 static LIST_HEAD(acpi_bus_id_list);
 static DEFINE_MUTEX(acpi_scan_lock);
 static LIST_HEAD(acpi_scan_handlers_list);
@@ -43,6 +45,12 @@ DEFINE_MUTEX(acpi_device_lock);
 LIST_HEAD(acpi_wakeup_device_list);
 static DEFINE_MUTEX(acpi_hp_context_lock);
 
+struct acpi_dep_data {
+       struct list_head node;
+       acpi_handle master;
+       acpi_handle slave;
+};
+
 struct acpi_device_bus_id{
        char bus_id[15];
        unsigned int instance_no;
@@ -124,17 +132,56 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
        if (list_empty(&acpi_dev->pnp.ids))
                return 0;
 
-       len = snprintf(modalias, size, "acpi:");
-       size -= len;
-
-       list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
-               count = snprintf(&modalias[len], size, "%s:", id->id);
-               if (count < 0)
-                       return -EINVAL;
-               if (count >= size)
-                       return -ENOMEM;
-               len += count;
-               size -= count;
+       /*
+        * If the device has PRP0001 we expose DT compatible modalias
+        * instead in form of of:NnameTCcompatible.
+        */
+       if (acpi_dev->data.of_compatible) {
+               struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+               const union acpi_object *of_compatible, *obj;
+               int i, nval;
+               char *c;
+
+               acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
+               /* DT strings are all in lower case */
+               for (c = buf.pointer; *c != '\0'; c++)
+                       *c = tolower(*c);
+
+               len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);
+               ACPI_FREE(buf.pointer);
+
+               of_compatible = acpi_dev->data.of_compatible;
+               if (of_compatible->type == ACPI_TYPE_PACKAGE) {
+                       nval = of_compatible->package.count;
+                       obj = of_compatible->package.elements;
+               } else { /* Must be ACPI_TYPE_STRING. */
+                       nval = 1;
+                       obj = of_compatible;
+               }
+               for (i = 0; i < nval; i++, obj++) {
+                       count = snprintf(&modalias[len], size, "C%s",
+                                        obj->string.pointer);
+                       if (count < 0)
+                               return -EINVAL;
+                       if (count >= size)
+                               return -ENOMEM;
+
+                       len += count;
+                       size -= count;
+               }
+       } else {
+               len = snprintf(modalias, size, "acpi:");
+               size -= len;
+
+               list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
+                       count = snprintf(&modalias[len], size, "%s:", id->id);
+                       if (count < 0)
+                               return -EINVAL;
+                       if (count >= size)
+                               return -ENOMEM;
+                       len += count;
+                       size -= count;
+               }
        }
 
        modalias[len] = '\0';
@@ -902,6 +949,51 @@ int acpi_match_device_ids(struct acpi_device *device,
 }
 EXPORT_SYMBOL(acpi_match_device_ids);
 
+/* Performs match against special "PRP0001" shoehorn ACPI ID */
+static bool acpi_of_driver_match_device(struct device *dev,
+                                       const struct device_driver *drv)
+{
+       const union acpi_object *of_compatible, *obj;
+       struct acpi_device *adev;
+       int i, nval;
+
+       adev = ACPI_COMPANION(dev);
+       if (!adev)
+               return false;
+
+       of_compatible = adev->data.of_compatible;
+       if (!drv->of_match_table || !of_compatible)
+               return false;
+
+       if (of_compatible->type == ACPI_TYPE_PACKAGE) {
+               nval = of_compatible->package.count;
+               obj = of_compatible->package.elements;
+       } else { /* Must be ACPI_TYPE_STRING. */
+               nval = 1;
+               obj = of_compatible;
+       }
+       /* Now we can look for the driver DT compatible strings */
+       for (i = 0; i < nval; i++, obj++) {
+               const struct of_device_id *id;
+
+               for (id = drv->of_match_table; id->compatible[0]; id++)
+                       if (!strcasecmp(obj->string.pointer, id->compatible))
+                               return true;
+       }
+
+       return false;
+}
+
+bool acpi_driver_match_device(struct device *dev,
+                             const struct device_driver *drv)
+{
+       if (!drv->acpi_match_table)
+               return acpi_of_driver_match_device(dev, drv);
+
+       return !!acpi_match_device(drv->acpi_match_table, dev);
+}
+EXPORT_SYMBOL_GPL(acpi_driver_match_device);
+
 static void acpi_free_power_resources_lists(struct acpi_device *device)
 {
        int i;
@@ -922,6 +1014,7 @@ static void acpi_device_release(struct device *dev)
 {
        struct acpi_device *acpi_dev = to_acpi_device(dev);
 
+       acpi_free_properties(acpi_dev);
        acpi_free_pnp_ids(&acpi_dev->pnp);
        acpi_free_power_resources_lists(acpi_dev);
        kfree(acpi_dev);
@@ -1304,6 +1397,26 @@ int acpi_device_add(struct acpi_device *device,
        return result;
 }
 
+struct acpi_device *acpi_get_next_child(struct device *dev,
+                                       struct acpi_device *child)
+{
+       struct acpi_device *adev = ACPI_COMPANION(dev);
+       struct list_head *head, *next;
+
+       if (!adev)
+               return NULL;
+
+       head = &adev->children;
+       if (list_empty(head))
+               return NULL;
+
+       if (!child)
+               return list_first_entry(head, struct acpi_device, node);
+
+       next = child->node.next;
+       return next == head ? NULL : list_entry(next, struct acpi_device, node);
+}
+
 /* --------------------------------------------------------------------------
                                  Driver Management
    -------------------------------------------------------------------------- */
@@ -1923,9 +2036,11 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
        device->device_type = type;
        device->handle = handle;
        device->parent = acpi_bus_get_parent(handle);
+       device->fwnode.type = FWNODE_ACPI;
        acpi_set_device_status(device, sta);
        acpi_device_get_busid(device);
        acpi_set_pnp_ids(handle, &device->pnp, type);
+       acpi_init_properties(device);
        acpi_bus_get_flags(device);
        device->flags.match_driver = false;
        device->flags.initialized = true;
@@ -2086,6 +2201,59 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev)
        }
 }
 
+static void acpi_device_dep_initialize(struct acpi_device *adev)
+{
+       struct acpi_dep_data *dep;
+       struct acpi_handle_list dep_devices;
+       acpi_status status;
+       int i;
+
+       if (!acpi_has_method(adev->handle, "_DEP"))
+               return;
+
+       status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
+                                       &dep_devices);
+       if (ACPI_FAILURE(status)) {
+               dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
+               return;
+       }
+
+       for (i = 0; i < dep_devices.count; i++) {
+               struct acpi_device_info *info;
+               int skip;
+
+               status = acpi_get_object_info(dep_devices.handles[i], &info);
+               if (ACPI_FAILURE(status)) {
+                       dev_dbg(&adev->dev, "Error reading _DEP device info\n");
+                       continue;
+               }
+
+               /*
+                * Skip the dependency of Windows System Power
+                * Management Controller
+                */
+               skip = info->valid & ACPI_VALID_HID &&
+                       !strcmp(info->hardware_id.string, "INT3396");
+
+               kfree(info);
+
+               if (skip)
+                       continue;
+
+               dep = kzalloc(sizeof(struct acpi_dep_data), GFP_KERNEL);
+               if (!dep)
+                       return;
+
+               dep->master = dep_devices.handles[i];
+               dep->slave  = adev->handle;
+               adev->dep_unmet++;
+
+               mutex_lock(&acpi_dep_list_lock);
+               list_add_tail(&dep->node , &acpi_dep_list);
+               mutex_unlock(&acpi_dep_list_lock);
+       }
+}
+
 static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
                                      void *not_used, void **return_value)
 {
@@ -2112,6 +2280,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
                return AE_CTRL_DEPTH;
 
        acpi_scan_init_hotplug(device);
+       acpi_device_dep_initialize(device);
 
  out:
        if (!*return_value)
@@ -2232,6 +2401,29 @@ static void acpi_bus_attach(struct acpi_device *device)
                device->handler->hotplug.notify_online(device);
 }
 
+void acpi_walk_dep_device_list(acpi_handle handle)
+{
+       struct acpi_dep_data *dep, *tmp;
+       struct acpi_device *adev;
+
+       mutex_lock(&acpi_dep_list_lock);
+       list_for_each_entry_safe(dep, tmp, &acpi_dep_list, node) {
+               if (dep->master == handle) {
+                       acpi_bus_get_device(dep->slave, &adev);
+                       if (!adev)
+                               continue;
+
+                       adev->dep_unmet--;
+                       if (!adev->dep_unmet)
+                               acpi_bus_attach(adev);
+                       list_del(&dep->node);
+                       kfree(dep);
+               }
+       }
+       mutex_unlock(&acpi_dep_list_lock);
+}
+EXPORT_SYMBOL_GPL(acpi_walk_dep_device_list);
+
 /**
  * acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
  * @handle: Root of the namespace scope to scan.