Skip to content

Commit

Permalink
ACPI: Allow drivers to match using Device Tree compatible property
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mika Westerberg authored and Rafael J. Wysocki committed Nov 4, 2014
1 parent b31384f commit 733e625
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 17 deletions.
39 changes: 39 additions & 0 deletions drivers/acpi/property.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}
Expand Down
106 changes: 95 additions & 11 deletions drivers/acpi/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions include/acpi/acpi_bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
8 changes: 2 additions & 6 deletions include/linux/acpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 733e625

Please sign in to comment.