diff options
author | Mika Westerberg <mika.westerberg@linux.intel.com> | 2014-10-21 13:33:56 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-11-04 21:58:21 +0100 |
commit | 733e625139fe455b4d910ac63c18c90f7cbe2d6f (patch) | |
tree | 0c67448b04c118138a66f485094fe951dfe24e84 /drivers/acpi | |
parent | b31384fa5de37a100507751dfb5c0a49d06cee67 (diff) |
ACPI: Allow drivers to match using Device Tree compatible property
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>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/property.c | 39 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 106 |
2 files changed, 134 insertions, 11 deletions
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 2541b1fd1fa5..27add91bc270 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -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; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 40d80ac0552f..3a8f66444532 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -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; |