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 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.westerb...@linux.intel.com>
---
 drivers/acpi/property.c | 34 ++++++++++++++++++
 drivers/acpi/scan.c     | 91 +++++++++++++++++++++++++++++++++++++++++++------
 include/acpi/acpi_bus.h |  1 +
 include/linux/acpi.h    |  8 ++---
 4 files changed, 118 insertions(+), 16 deletions(-)

diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index e50964012da8..c6c3ab6f68c3 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -76,6 +76,37 @@ 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;
+
+       /*
+        * 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;
+
+       if (acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING,
+                                       &of_compatible)) {
+               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 +150,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 +163,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;
 }
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index f4aabf9c0aad..0c8e751cbe6b 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -124,17 +124,43 @@ 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;
+       /*
+        * If the device has PRP0001 we expose DT compatible modalias
+        * instead.
+        */
+       if (acpi_dev->data.of_compatible) {
+               const union acpi_object *of_compatible, *obj;
+               int i;
+
+               len = snprintf(modalias, size, "of:Nprp0001Tacpi");
+
+               of_compatible = acpi_dev->data.of_compatible;
+               for (i = 0; i < of_compatible->package.count; i++) {
+                       obj = &of_compatible->package.elements[i];
 
-       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;
+                       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';
@@ -864,6 +890,51 @@ int acpi_match_device_ids(struct acpi_device *device,
 }
 EXPORT_SYMBOL(acpi_match_device_ids);
 
+/* Performs match for special "PRP0001" shoehorn ACPI ID */
+static bool acpi_of_driver_match_device(struct device *dev,
+                                       const struct device_driver *drv)
+{
+       struct acpi_device *adev = ACPI_COMPANION(dev);
+       const union acpi_object *of_compatible;
+       int i;
+
+       /*
+        * If the ACPI device does not have corresponding compatible
+        * property or the driver in question does not have DT matching
+        * table we consider the match succesful (matches the ACPI ID).
+        */
+       of_compatible = adev->data.of_compatible;
+       if (!drv->of_match_table || !of_compatible)
+               return true;
+
+       /* Now we can look for the driver DT compatible strings */
+       for (i = 0; i < of_compatible->package.count; i++) {
+               const struct of_device_id *id;
+               const union acpi_object *obj;
+
+               obj = &of_compatible->package.elements[i];
+
+               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)
+{
+       const struct acpi_device_id *id;
+
+       id = acpi_match_device(drv->acpi_match_table, dev);
+       if (!id)
+               return false;
+
+       return acpi_of_driver_match_device(dev, drv);
+}
+EXPORT_SYMBOL_GPL(acpi_driver_match_device);
+
 static void acpi_free_power_resources_lists(struct acpi_device *device)
 {
        int i;
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 620b4f89c87b..65866e23e3e9 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -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 */
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 288abbdf2fb4..37a5280a64a5 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -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);
 
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to