OSDN Git Service

ACPI: Allow drivers to match using Device Tree compatible property
authorMika Westerberg <mika.westerberg@linux.intel.com>
Tue, 21 Oct 2014 11:33:56 +0000 (13:33 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 4 Nov 2014 20:58:21 +0000 (21:58 +0100)
We have lots of existing Device Tree enabled drivers and allocating
separate _HID for each is not feasible. Instead we allocate special _HID
"PRP0001" that means that the match should be done using Device Tree
compatible property using driver's .of_match_table instead if the driver
is missing .acpi_match_table.

If there is a need to distinguish from where the device is enumerated
(DT/ACPI) driver can check dev->of_node or ACPI_COMPATION(dev).

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Grant Likely <grant.likely@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/property.c
drivers/acpi/scan.c
include/acpi/acpi_bus.h
include/linux/acpi.h

index 2541b1f..27add91 100644 (file)
@@ -76,6 +76,42 @@ static bool acpi_properties_format_valid(const union acpi_object *properties)
        return true;
 }
 
+static void acpi_init_of_compatible(struct acpi_device *adev)
+{
+       const union acpi_object *of_compatible;
+       struct acpi_hardware_id *hwid;
+       bool acpi_of = false;
+       int ret;
+
+       /*
+        * Check if the special PRP0001 ACPI ID is present and in that
+        * case we fill in Device Tree compatible properties for this
+        * device.
+        */
+       list_for_each_entry(hwid, &adev->pnp.ids, list) {
+               if (!strcmp(hwid->id, "PRP0001")) {
+                       acpi_of = true;
+                       break;
+               }
+       }
+
+       if (!acpi_of)
+               return;
+
+       ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING,
+                                         &of_compatible);
+       if (ret) {
+               ret = acpi_dev_get_property(adev, "compatible",
+                                           ACPI_TYPE_STRING, &of_compatible);
+               if (ret) {
+                       acpi_handle_warn(adev->handle,
+                                        "PRP0001 requires compatible property\n");
+                       return;
+               }
+       }
+       adev->data.of_compatible = of_compatible;
+}
+
 void acpi_init_properties(struct acpi_device *adev)
 {
        struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
@@ -119,6 +155,8 @@ void acpi_init_properties(struct acpi_device *adev)
 
                adev->data.pointer = buf.pointer;
                adev->data.properties = properties;
+
+               acpi_init_of_compatible(adev);
                return;
        }
 
@@ -130,6 +168,7 @@ void acpi_init_properties(struct acpi_device *adev)
 void acpi_free_properties(struct acpi_device *adev)
 {
        ACPI_FREE((void *)adev->data.pointer);
+       adev->data.of_compatible = NULL;
        adev->data.pointer = NULL;
        adev->data.properties = NULL;
 }
index 40d80ac..3a8f664 100644 (file)
@@ -124,17 +124,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 +941,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;
index 4757811..f59cbf8 100644 (file)
@@ -341,6 +341,7 @@ struct acpi_device_physical_node {
 struct acpi_device_data {
        const union acpi_object *pointer;
        const union acpi_object *properties;
+       const union acpi_object *of_compatible;
 };
 
 /* Device */
index 76d64d6..38296d6 100644 (file)
@@ -424,12 +424,8 @@ extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *),
 const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
                                               const struct device *dev);
 
-static inline bool acpi_driver_match_device(struct device *dev,
-                                           const struct device_driver *drv)
-{
-       return !!acpi_match_device(drv->acpi_match_table, dev);
-}
-
+extern bool acpi_driver_match_device(struct device *dev,
+                                    const struct device_driver *drv);
 int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
 int acpi_device_modalias(struct device *, char *, int);