OSDN Git Service

ACPI / scan: Add a scan handler for PRP0001
[uclinux-h8/linux.git] / drivers / acpi / scan.c
index bbca783..03141aa 100644 (file)
@@ -114,7 +114,12 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
        return 0;
 }
 
-/*
+/**
+ * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent
+ * @acpi_dev: ACPI device object.
+ * @modalias: Buffer to print into.
+ * @size: Size of the buffer.
+ *
  * Creates hid/cid(s) string needed for modalias and uevent
  * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
  * char *modalias: "acpi:IBM0001:ACPI0001"
@@ -122,68 +127,98 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
  *         -EINVAL: output error
  *         -ENOMEM: output is truncated
 */
-static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
-                          int size)
+static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
+                              int size)
 {
        int len;
        int count;
        struct acpi_hardware_id *id;
 
-       if (list_empty(&acpi_dev->pnp.ids))
-               return 0;
-
        /*
-        * If the device has PRP0001 we expose DT compatible modalias
-        * instead in form of of:NnameTCcompatible.
+        * Since we skip PRP0001 from the modalias below, 0 should be returned
+        * if PRP0001 is the only ACPI/PNP ID in the device's list.
         */
-       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;
+       count = 0;
+       list_for_each_entry(id, &acpi_dev->pnp.ids, list)
+               if (strcmp(id->id, "PRP0001"))
+                       count++;
 
-               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 (!count)
+               return 0;
+
+       len = snprintf(modalias, size, "acpi:");
+       if (len <= 0)
+               return len;
+
+       size -= len;
+
+       list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
+               if (!strcmp(id->id, "PRP0001"))
+                       continue;
+
+               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';
+       return len;
+}
 
+/**
+ * create_of_modalias - Creates DT compatible string for modalias and uevent
+ * @acpi_dev: ACPI device object.
+ * @modalias: Buffer to print into.
+ * @size: Size of the buffer.
+ *
+ * Expose DT compatible modalias as of:NnameTCcompatible.  This function should
+ * only be called for devices having PRP0001 in their list of ACPI/PNP IDs.
+ */
+static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
+                             int size)
+{
+       struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+       const union acpi_object *of_compatible, *obj;
+       int len, count;
+       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);
+
+       if (len <= 0)
+               return len;
+
+       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;
+       }
        modalias[len] = '\0';
        return len;
 }
@@ -194,7 +229,8 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
  *
  * Check if the given device has an ACPI companion and if that companion has
  * a valid list of PNP IDs, and if the device is the first (primary) physical
- * device associated with it.
+ * device associated with it.  Return the companion pointer if that's the case
+ * or NULL otherwise.
  *
  * If multiple physical devices are attached to a single ACPI companion, we need
  * to be careful.  The usage scenario for this kind of relationship is that all
@@ -208,88 +244,129 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
  * resources available from it but they will be matched normally using functions
  * provided by their bus types (and analogously for their modalias).
  */
-static bool acpi_companion_match(const struct device *dev)
+static struct acpi_device *acpi_companion_match(const struct device *dev)
 {
        struct acpi_device *adev;
-       bool ret;
+       struct mutex *physical_node_lock;
 
        adev = ACPI_COMPANION(dev);
        if (!adev)
-               return false;
+               return NULL;
 
        if (list_empty(&adev->pnp.ids))
-               return false;
+               return NULL;
 
-       mutex_lock(&adev->physical_node_lock);
+       physical_node_lock = &adev->physical_node_lock;
+       mutex_lock(physical_node_lock);
        if (list_empty(&adev->physical_node_list)) {
-               ret = false;
+               adev = NULL;
        } else {
                const struct acpi_device_physical_node *node;
 
                node = list_first_entry(&adev->physical_node_list,
                                        struct acpi_device_physical_node, node);
-               ret = node->dev == dev;
+               if (node->dev != dev)
+                       adev = NULL;
        }
-       mutex_unlock(&adev->physical_node_lock);
+       mutex_unlock(physical_node_lock);
 
-       return ret;
+       return adev;
 }
 
-/*
- * Creates uevent modalias field for ACPI enumerated devices.
- * Because the other buses does not support ACPI HIDs & CIDs.
- * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get:
- * "acpi:IBM0001:ACPI0001"
- */
-int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
+static int __acpi_device_uevent_modalias(struct acpi_device *adev,
+                                        struct kobj_uevent_env *env)
 {
        int len;
 
-       if (!acpi_companion_match(dev))
+       if (!adev)
                return -ENODEV;
 
+       if (list_empty(&adev->pnp.ids))
+               return 0;
+
        if (add_uevent_var(env, "MODALIAS="))
                return -ENOMEM;
-       len = create_modalias(ACPI_COMPANION(dev), &env->buf[env->buflen - 1],
-                               sizeof(env->buf) - env->buflen);
-       if (len <= 0)
+
+       len = create_pnp_modalias(adev, &env->buf[env->buflen - 1],
+                                 sizeof(env->buf) - env->buflen);
+       if (len < 0)
                return len;
+
        env->buflen += len;
+       if (!adev->data.of_compatible)
+               return 0;
+
+       if (len > 0 && add_uevent_var(env, "MODALIAS="))
+               return -ENOMEM;
+
+       len = create_of_modalias(adev, &env->buf[env->buflen - 1],
+                                sizeof(env->buf) - env->buflen);
+       if (len < 0)
+               return len;
+
+       env->buflen += len;
+
        return 0;
 }
-EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);
 
 /*
- * Creates modalias sysfs attribute for ACPI enumerated devices.
+ * Creates uevent modalias field for ACPI enumerated devices.
  * Because the other buses does not support ACPI HIDs & CIDs.
  * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get:
  * "acpi:IBM0001:ACPI0001"
  */
-int acpi_device_modalias(struct device *dev, char *buf, int size)
+int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
 {
-       int len;
+       return __acpi_device_uevent_modalias(acpi_companion_match(dev), env);
+}
+EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);
 
-       if (!acpi_companion_match(dev))
+static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size)
+{
+       int len, count;
+
+       if (!adev)
                return -ENODEV;
 
-       len = create_modalias(ACPI_COMPANION(dev), buf, size -1);
-       if (len <= 0)
+       if (list_empty(&adev->pnp.ids))
+               return 0;
+
+       len = create_pnp_modalias(adev, buf, size - 1);
+       if (len < 0) {
+               return len;
+       } else if (len > 0) {
+               buf[len++] = '\n';
+               size -= len;
+       }
+       if (!adev->data.of_compatible)
                return len;
-       buf[len++] = '\n';
+
+       count = create_of_modalias(adev, buf + len, size - 1);
+       if (count < 0) {
+               return count;
+       } else if (count > 0) {
+               len += count;
+               buf[len++] = '\n';
+       }
+
        return len;
 }
+
+/*
+ * Creates modalias sysfs attribute for ACPI enumerated devices.
+ * Because the other buses does not support ACPI HIDs & CIDs.
+ * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get:
+ * "acpi:IBM0001:ACPI0001"
+ */
+int acpi_device_modalias(struct device *dev, char *buf, int size)
+{
+       return __acpi_device_modalias(acpi_companion_match(dev), buf, size);
+}
 EXPORT_SYMBOL_GPL(acpi_device_modalias);
 
 static ssize_t
 acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) {
-       struct acpi_device *acpi_dev = to_acpi_device(dev);
-       int len;
-
-       len = create_modalias(acpi_dev, buf, 1024);
-       if (len <= 0)
-               return len;
-       buf[len++] = '\n';
-       return len;
+       return __acpi_device_modalias(to_acpi_device(dev), buf, 1024);
 }
 static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
 
@@ -298,7 +375,11 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
        struct acpi_device_physical_node *pn;
        bool offline = true;
 
-       mutex_lock(&adev->physical_node_lock);
+       /*
+        * acpi_container_offline() calls this for all of the container's
+        * children under the container's physical_node_lock lock.
+        */
+       mutex_lock_nested(&adev->physical_node_lock, SINGLE_DEPTH_NESTING);
 
        list_for_each_entry(pn, &adev->physical_node_list, node)
                if (device_supports_offline(pn->dev) && !pn->dev->offline) {
@@ -894,8 +975,51 @@ static void acpi_device_remove_files(struct acpi_device *dev)
                        ACPI Bus operations
    -------------------------------------------------------------------------- */
 
+/**
+ * acpi_of_match_device - Match device object using the "compatible" property.
+ * @adev: ACPI device object to match.
+ * @of_match_table: List of device IDs to match against.
+ *
+ * If @dev has an ACPI companion which has the special PRP0001 device ID in its
+ * list of identifiers and a _DSD object with the "compatible" property, use
+ * that property to match against the given list of identifiers.
+ */
+static bool acpi_of_match_device(struct acpi_device *adev,
+                                const struct of_device_id *of_match_table)
+{
+       const union acpi_object *of_compatible, *obj;
+       int i, nval;
+
+       if (!adev)
+               return false;
+
+       of_compatible = adev->data.of_compatible;
+       if (!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 = of_match_table; id->compatible[0]; id++)
+                       if (!strcasecmp(obj->string.pointer, id->compatible))
+                               return true;
+       }
+
+       return false;
+}
+
 static const struct acpi_device_id *__acpi_match_device(
-       struct acpi_device *device, const struct acpi_device_id *ids)
+       struct acpi_device *device,
+       const struct acpi_device_id *ids,
+       const struct of_device_id *of_ids)
 {
        const struct acpi_device_id *id;
        struct acpi_hardware_id *hwid;
@@ -904,14 +1028,27 @@ static const struct acpi_device_id *__acpi_match_device(
         * If the device is not present, it is unnecessary to load device
         * driver for it.
         */
-       if (!device->status.present)
+       if (!device || !device->status.present)
                return NULL;
 
-       for (id = ids; id->id[0]; id++)
-               list_for_each_entry(hwid, &device->pnp.ids, list)
+       list_for_each_entry(hwid, &device->pnp.ids, list) {
+               /* First, check the ACPI/PNP IDs provided by the caller. */
+               for (id = ids; id->id[0]; id++)
                        if (!strcmp((char *) id->id, hwid->id))
                                return id;
 
+               /*
+                * Next, check the special "PRP0001" ID and try to match the
+                * "compatible" property if found.
+                *
+                * The id returned by the below is not valid, but the only
+                * caller passing non-NULL of_ids here is only interested in
+                * whether or not the return value is NULL.
+                */
+               if (!strcmp("PRP0001", hwid->id)
+                   && acpi_of_match_device(device, of_ids))
+                       return id;
+       }
        return NULL;
 }
 
@@ -929,68 +1066,26 @@ static const struct acpi_device_id *__acpi_match_device(
 const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
                                               const struct device *dev)
 {
-       struct acpi_device *adev;
-       acpi_handle handle = ACPI_HANDLE(dev);
-
-       if (!ids || !handle || acpi_bus_get_device(handle, &adev))
-               return NULL;
-
-       if (!acpi_companion_match(dev))
-               return NULL;
-
-       return __acpi_match_device(adev, ids);
+       return __acpi_match_device(acpi_companion_match(dev), ids, NULL);
 }
 EXPORT_SYMBOL_GPL(acpi_match_device);
 
 int acpi_match_device_ids(struct acpi_device *device,
                          const struct acpi_device_id *ids)
 {
-       return __acpi_match_device(device, ids) ? 0 : -ENOENT;
+       return __acpi_match_device(device, ids, NULL) ? 0 : -ENOENT;
 }
 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_of_match_device(ACPI_COMPANION(dev),
+                                           drv->of_match_table);
 
-       return !!acpi_match_device(drv->acpi_match_table, dev);
+       return !!__acpi_match_device(acpi_companion_match(dev),
+                                    drv->acpi_match_table, drv->of_match_table);
 }
 EXPORT_SYMBOL_GPL(acpi_driver_match_device);
 
@@ -1031,20 +1126,7 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv)
 
 static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
-       struct acpi_device *acpi_dev = to_acpi_device(dev);
-       int len;
-
-       if (list_empty(&acpi_dev->pnp.ids))
-               return 0;
-
-       if (add_uevent_var(env, "MODALIAS="))
-               return -ENOMEM;
-       len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
-                             sizeof(env->buf) - env->buflen);
-       if (len <= 0)
-               return len;
-       env->buflen += len;
-       return 0;
+       return __acpi_device_uevent_modalias(to_acpi_device(dev), env);
 }
 
 static void acpi_device_notify(acpi_handle handle, u32 event, void *data)
@@ -1062,10 +1144,10 @@ static void acpi_device_notify_fixed(void *data)
        acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
 }
 
-static acpi_status acpi_device_fixed_event(void *data)
+static u32 acpi_device_fixed_event(void *data)
 {
        acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data);
-       return AE_OK;
+       return ACPI_INTERRUPT_HANDLED;
 }
 
 static int acpi_device_install_notify_handler(struct acpi_device *device)
@@ -2310,9 +2392,6 @@ static void acpi_default_enumeration(struct acpi_device *device)
        struct list_head resource_list;
        bool is_spi_i2c_slave = false;
 
-       if (!device->pnp.type.platform_id || device->handler)
-               return;
-
        /*
         * Do not enemerate SPI/I2C slaves as they will be enuerated by their
         * respective parents.
@@ -2325,6 +2404,29 @@ static void acpi_default_enumeration(struct acpi_device *device)
                acpi_create_platform_device(device);
 }
 
+static const struct acpi_device_id generic_device_ids[] = {
+       {"PRP0001", },
+       {"", },
+};
+
+static int acpi_generic_device_attach(struct acpi_device *adev,
+                                     const struct acpi_device_id *not_used)
+{
+       /*
+        * Since PRP0001 is the only ID handled here, the test below can be
+        * unconditional.
+        */
+       if (adev->data.of_compatible)
+               acpi_default_enumeration(adev);
+
+       return 1;
+}
+
+static struct acpi_scan_handler generic_device_handler = {
+       .ids = generic_device_ids,
+       .attach = acpi_generic_device_attach,
+};
+
 static int acpi_scan_attach_handler(struct acpi_device *device)
 {
        struct acpi_hardware_id *hwid;
@@ -2350,8 +2452,6 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
                                break;
                }
        }
-       if (!ret)
-               acpi_default_enumeration(device);
 
        return ret;
 }
@@ -2393,6 +2493,9 @@ static void acpi_bus_attach(struct acpi_device *device)
                ret = device_attach(&device->dev);
                if (ret < 0)
                        return;
+
+               if (!ret && device->pnp.type.platform_id)
+                       acpi_default_enumeration(device);
        }
        device->flags.visited = true;
 
@@ -2551,6 +2654,8 @@ int __init acpi_scan_init(void)
        acpi_pnp_init();
        acpi_int340x_thermal_init();
 
+       acpi_scan_add_handler(&generic_device_handler);
+
        mutex_lock(&acpi_scan_lock);
        /*
         * Enumerate devices in the ACPI namespace.