Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 733e6251 authored by Mika Westerberg's avatar Mika Westerberg Committed by Rafael J. Wysocki
Browse files

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: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: default avatarGrant Likely <grant.likely@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent b31384fa
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -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;
}
+95 −11
Original line number Diff line number Diff line
@@ -124,6 +124,44 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
	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.
	 */
	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;

@@ -136,6 +174,7 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
			len += count;
			size -= count;
		}
	}

	modalias[len] = '\0';
	return len;
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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 */
+2 −6
Original line number Diff line number Diff line
@@ -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);