aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2013-10-17 18:13:59 +0300
committerGraeme Gregory <graeme.gregory@linaro.org>2014-06-03 09:24:28 +0100
commitfa5075f70217fabaf1fb0c53ae31e8d267bfea5a (patch)
tree7330752e3cc02b3ebf2c4e44feeb025cd0af5c9f
parent2280f1534dd681f111752bc1288859a0248055a2 (diff)
downloadleg-kernel-fa5075f70217fabaf1fb0c53ae31e8d267bfea5a.tar.gz
ACPI: Add support for device specific properties
Device Tree is used in many embedded systems to describe the system configuration to the OS. It supports attaching properties or name-value pairs to the devices it describe. With these properties one can pass additional information to the drivers that would not be available otherwise. ACPI is another configuration mechanism (among other things) typically seen on but not limited to x86 machines. ACPI allows passing arbitrary data from methods but there is nothing like Device Tree properties in the ACPI specification. In order to facilitate ACPI usage in systems where Device Tree is typically used, it would be beneficial to standardize a way to retrieve Device Tree style properties from ACPI devices, which is what we do in this patch. If a given device described in ACPI namespace wants to export properties it must implement _PRP method that returns the properties in a package of packages. For example: Method (_PRP, 0, NotSerialized) { Return (Package () { Package () { "name1", <VALUE1> }, Package () { "name2", <VALUE2> }, ... }) } Name must be string but there are no limitations for the value itself. We add several helper functions that can be used to extract these properties and convert them to different Linux data types. The ultimate goal is that we only have one device property API that retrieves the requested properties from Device Tree or from ACPI transparent to the caller. Signed-off-by: Darren Hart <dvhart@linux.intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/internal.h6
-rw-r--r--drivers/acpi/property.c512
-rw-r--r--drivers/acpi/scan.c2
-rw-r--r--include/acpi/acpi_bus.h1
-rw-r--r--include/linux/acpi.h29
6 files changed, 551 insertions, 0 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 551b6e7853f7..3433c9e9ca75 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -44,6 +44,7 @@ acpi-y += acpi_platform.o
acpi-y += power.o
acpi-y += event.o
acpi-y += sysfs.o
+acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 71193c711124..30d9e35848e5 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -196,4 +196,10 @@ int acpi_create_platform_device(struct acpi_device *adev,
bool acpi_osi_is_win8(void);
#endif
+/*--------------------------------------------------------------------------
+ Device properties
+ -------------------------------------------------------------------------- */
+void acpi_init_properties(struct acpi_device *adev);
+void acpi_free_properties(struct acpi_device *adev);
+
#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
new file mode 100644
index 000000000000..489644c6c136
--- /dev/null
+++ b/drivers/acpi/property.c
@@ -0,0 +1,512 @@
+/*
+ * Device Tree style properties from ACPI devices.
+ *
+ * Copyright (C) 2013, Intel Corporation
+ * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Darren Hart <dvhart@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/export.h>
+
+#include "internal.h"
+
+struct acpi_dev_property_lookup {
+ const char *name;
+ acpi_object_type type;
+ const union acpi_object *obj;
+};
+
+void acpi_init_properties(struct acpi_device *adev)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+
+ if (ACPI_SUCCESS(acpi_get_properties(adev->handle, &buf)))
+ adev->properties = buf.pointer;
+}
+
+void acpi_free_properties(struct acpi_device *adev)
+{
+ ACPI_FREE(adev->properties);
+ adev->properties = NULL;
+}
+
+/**
+ * acpi_dev_get_properties - get properties from a device
+ * @adev: device to get properties from
+ * @callback: callback that is called for each found property
+ * @data: data passed to @callback
+ *
+ * Function goes over device properties and for each property @callback is
+ * called. If @callback returns non-zero the iteration is terminated and
+ * that return value is returned from this function.
+ */
+int acpi_dev_get_properties(struct acpi_device *adev,
+ int (*callback)(const union acpi_object *, void *),
+ void *data)
+{
+ const union acpi_object *property;
+ int i, ret;
+
+ if (!adev)
+ return -EINVAL;
+ if (!adev->properties)
+ return -ENODATA;
+
+ for (i = 0; i < adev->properties->package.count; i++) {
+ property = &adev->properties->package.elements[i];
+ ret = callback(property, data);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_properties);
+
+/*
+ * Returns 0 if the property didn̈́'t match, 1 if it did and -EINVAL if the
+ * value found is not of expected type.
+ */
+static int acpi_dev_find_property(const union acpi_object *pkg, void *data)
+{
+ const union acpi_object *obj, *name = &pkg->package.elements[0];
+ struct acpi_dev_property_lookup *lookup = data;
+
+ if (strcmp(lookup->name, name->string.pointer))
+ return 0;
+
+ obj = pkg->package.count > 1 ? &pkg->package.elements[1] : NULL;
+
+ if (lookup->type == ACPI_TYPE_ANY ||
+ (obj && lookup->type == obj->type)) {
+ lookup->obj = obj;
+ return 1;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * acpi_dev_get_property - return an ACPI property with given name
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @type: expected type or %ACPI_TYPE_ANY if caller doesn't care
+ * @obj: property value is placed here if not %NULL
+ *
+ * Function looks up a property with @name and returns the resulting ACPI
+ * object in @obj if found. The returned object should not be released by
+ * the caller, it is released automatically by the ACPI core when @adev is
+ * removed.
+ */
+int acpi_dev_get_property(struct acpi_device *adev, const char *name,
+ acpi_object_type type, const union acpi_object **obj)
+{
+ struct acpi_dev_property_lookup lookup = {
+ .name = name,
+ .type = type,
+ };
+ int ret;
+
+ ret = acpi_dev_get_properties(adev, acpi_dev_find_property, &lookup);
+ if (ret == 1) {
+ if (obj)
+ *obj = lookup.obj;
+ return 0;
+ }
+ return ret ? ret : -ENODATA;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property);
+
+/**
+ * acpi_dev_get_property_u64 - find and read 64-bit integer property
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @value: value of the property is placed here.
+ *
+ * Search for a property with the @name and if find, place the value to
+ * @value. Returns %0 on success, %-ENODATA if the property is not found,
+ * and %-EINVAL if the property is not in correct format.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", 0x0000ffffffff0000 }
+ */
+int acpi_dev_get_property_u64(struct acpi_device *adev, const char *name,
+ u64 *value)
+{
+ const union acpi_object *obj;
+ int ret;
+
+ ret = acpi_dev_get_property(adev, name, ACPI_TYPE_INTEGER, &obj);
+ if (!ret)
+ *value = (u64)obj->integer.value;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_u64);
+
+/**
+ * acpi_dev_get_property_u32 - find and read 32-bit integer property
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @value: value of the property is placed here.
+ *
+ * Search for a property with the @name and if find, place the value to
+ * @value. Returns %0 on success, %-ENODATA if the property is not found,
+ * and %-EINVAL if the property is not in correct format.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", 0x0ffffff0 }
+ */
+int acpi_dev_get_property_u32(struct acpi_device *adev, const char *name,
+ u32 *value)
+{
+ u64 tmp;
+ int ret;
+
+ ret = acpi_dev_get_property_u64(adev, name, &tmp);
+ if (!ret)
+ *value = tmp;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_u32);
+
+/**
+ * acpi_dev_get_property_u16 - find and read 16-bit integer property
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @value: value of the property is placed here.
+ *
+ * Search for a property with the @name and if find, place the value to
+ * @value. Returns %0 on success, %-ENODATA if the property is not found,
+ * and %-EINVAL if the property is not in correct format.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", 0x0ff0 }
+ */
+int acpi_dev_get_property_u16(struct acpi_device *adev, const char *name,
+ u16 *value)
+{
+ u64 tmp;
+ int ret;
+
+ ret = acpi_dev_get_property_u64(adev, name, &tmp);
+ if (!ret)
+ *value = tmp;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_u16);
+
+/**
+ * acpi_dev_get_property_u8 - find and read 8-bit integer property
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @value: value of the property is placed here.
+ *
+ * Search for a property with the @name and if find, place the value to
+ * @value. Returns %0 on success, %-ENODATA if the property is not found,
+ * and %-EINVAL if the property is not in correct format.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", 0x3c }
+ */
+int acpi_dev_get_property_u8(struct acpi_device *adev, const char *name,
+ u8 *value)
+{
+ u64 tmp;
+ int ret;
+
+ ret = acpi_dev_get_property_u64(adev, name, &tmp);
+ if (!ret)
+ *value = tmp;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_u8);
+
+static int acpi_dev_get_property_array(struct acpi_device *adev,
+ const char *name, acpi_object_type type,
+ const union acpi_object **ret_obj)
+{
+ const union acpi_object *obj;
+ int ret, i;
+
+ ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &obj);
+ if (ret)
+ return ret;
+
+ /* Check that all elements are of correct type */
+ for (i = 0; i < obj->package.count; i++)
+ if (obj->package.elements[i].type != type)
+ return -EINVAL;
+
+ *ret_obj = obj;
+ return 0;
+}
+
+/**
+ * acpi_dev_get_property_array_u64 - find and read array of u64 from a property
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @values: array where the data is placed
+ * @nvalues: number of elements in @values array
+ *
+ * Copies integer properties array with @name into @values and returns
+ * number of items in the actual array or %-ENODATA if the property doesn't
+ * exists, %-EINVAL if the array format is invalid. @values and @nvalues
+ * can be set to %NULL and %0 respectively. In that case the function
+ * returns number of items in the array but doesn't touch @values.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", Package () { 1, 2, ... } }
+ */
+int acpi_dev_get_property_array_u64(struct acpi_device *adev, const char *name,
+ u64 *values, size_t nvalues)
+{
+ const union acpi_object *obj;
+ int ret;
+
+ ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_INTEGER, &obj);
+ if (ret)
+ return ret;
+
+ if (values) {
+ int i;
+
+ for (i = 0; i < obj->package.count && i < nvalues; i++)
+ values[i] = obj->package.elements[i].integer.value;
+ }
+
+ return obj->package.count;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_u64);
+
+/**
+ * acpi_dev_get_property_array_u32 - find and read array of u32 from a property
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @values: array where the data is placed
+ * @nvalues: number of elements in @values array
+ *
+ * Copies integer properties array with @name into @values and returns
+ * number of items in the actual array or %-ENODATA if the property doesn't
+ * exists, %-EINVAL if the array format is invalid. @values and @nvalues
+ * can be set to %NULL and %0 respectively. In that case the function
+ * returns number of items in the array but doesn't touch @values.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", Package () { 1, 2, ... } }
+ */
+int acpi_dev_get_property_array_u32(struct acpi_device *adev, const char *name,
+ u32 *values, size_t nvalues)
+{
+ const union acpi_object *obj;
+ int ret;
+
+ ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_INTEGER, &obj);
+ if (ret)
+ return ret;
+
+ if (values) {
+ int i;
+
+ for (i = 0; i < obj->package.count && i < nvalues; i++)
+ values[i] = obj->package.elements[i].integer.value;
+ }
+
+ return obj->package.count;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_u32);
+
+/**
+ * acpi_dev_get_property_array_u16 - find and read array of u16 from a property
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @values: array where the data is placed
+ * @nvalues: number of elements in @values array
+ *
+ * Copies integer properties array with @name into @values and returns
+ * number of items in the actual array or %-ENODATA if the property doesn't
+ * exists, %-EINVAL if the array format is invalid. @values and @nvalues
+ * can be set to %NULL and %0 respectively. In that case the function
+ * returns number of items in the array but doesn't touch @values.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", Package () { 1, 2, ... } }
+ */
+int acpi_dev_get_property_array_u16(struct acpi_device *adev, const char *name,
+ u16 *values, size_t nvalues)
+{
+ const union acpi_object *obj;
+ int ret;
+
+ ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_INTEGER, &obj);
+ if (ret)
+ return ret;
+
+ if (values) {
+ int i;
+
+ for (i = 0; i < obj->package.count && i < nvalues; i++)
+ values[i] = obj->package.elements[i].integer.value;
+ }
+
+ return obj->package.count;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_u16);
+
+/**
+ * acpi_dev_get_property_array_u8 - find and read array of u8 from a property
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @values: array where the data is placed. Caller allocated can be %NULL.
+ * @nvalues: number of items in @values array
+ *
+ * Copies integer properties array with @name into @values and returns
+ * number of items in the actual array or %-ENODATA if the property doesn't
+ * exists, %-EINVAL if the array format is invalid. @values and @nvalues
+ * can be set to %NULL and %0 respectively. In that case the function
+ * returns number of items in the array but doesn't touch @values.
+ *
+ * Function treats ACPI types package and buffer the same. It first looks
+ * for a package and then falls back to a buffer.
+ *
+ * A sample ASL might look like this if package is used:
+ * Package () { "property", Package () { 1, 2, ... } }
+ *
+ * And like this if buffer is used:
+ * Package () { "property", Buffer () { 1, 2, ... } }
+ */
+int acpi_dev_get_property_array_u8(struct acpi_device *adev, const char *name,
+ u8 *values, size_t nvalues)
+{
+ const union acpi_object *obj;
+ int ret, i;
+
+ ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_INTEGER, &obj);
+ if (!ret) {
+ if (values) {
+ const union acpi_object *elements;
+
+ elements = obj->package.elements;
+ for (i = 0; i < obj->package.count && i < nvalues; i++)
+ values[i] = elements[i].integer.value;
+ }
+ return obj->package.count;
+ }
+
+ ret = acpi_dev_get_property(adev, name, ACPI_TYPE_BUFFER, &obj);
+ if (ret)
+ return ret;
+
+ if (values) {
+ for (i = 0; i < obj->buffer.length && i < nvalues; i++)
+ values[i] = obj->buffer.pointer[i];
+ }
+
+ return obj->buffer.length;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_u8);
+
+/**
+ * acpi_dev_get_property_string - returns string property value
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @value: pointer to the returned string
+ *
+ * Finds property with @name, and places pointer to the string value to
+ * @value. The memory pointed by @value should not be released by the
+ * called but it will be released when the corresponding ACPI device object
+ * is removed.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", "my string property value" }
+ */
+int acpi_dev_get_property_string(struct acpi_device *adev, const char *name,
+ const char **value)
+{
+ const union acpi_object *obj;
+ int ret;
+
+ ret = acpi_dev_get_property(adev, name, ACPI_TYPE_STRING, &obj);
+ if (!ret)
+ *value = obj->string.pointer;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_string);
+
+/**
+ * acpi_dev_get_property_array_string - find and read an array of strings
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @values: array where strings are placed
+ * @nvalues: number of items in @values array
+ *
+ * Finds property with @name, verifies that it contains an array of strings
+ * and if so, fills in @values with pointers to those strings. Note that
+ * the caller shouldn't try to release those pointers. They are owned by
+ * the ACPI device @adev.
+ *
+ * String pointers will remain valid as long as the corresponding ACPI
+ * device object exists.
+ *
+ * A sample ASL might look like this:
+ * Package () {
+ * "property",
+ * Package () { "my first string", "my second string" }
+ * }
+ */
+int acpi_dev_get_property_array_string(struct acpi_device *adev,
+ const char *name, const char **values,
+ size_t nvalues)
+{
+ const union acpi_object *obj;
+ int ret;
+
+ ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_STRING, &obj);
+ if (ret)
+ return ret;
+
+ if (values) {
+ const union acpi_object *elements = obj->package.elements;
+ int i;
+
+ for (i = 0; i < obj->package.count && i < nvalues; i++)
+ values[i] = elements[i].string.pointer;
+ }
+
+ return obj->package.count;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_string);
+
+/**
+ * acpi_dev_get_property_reference - returns handle to the referenced object
+ * @adev: ACPI device to get property
+ * @name: name of the property
+ * @obj_handle: pointer to acpi_handle where the found ACPI handle is placed
+ *
+ * Function finds property with @name, verififies that it is an object
+ * reference and if so, returns the ACPI handle of the referenced object in
+ * @obj_handle. Returns %0 in case of success, %-ENODATA if the property
+ * doesn't exists or doesn't have a value, and %-EINVAL if the property
+ * value is not a reference.
+ *
+ * A sample ASL might look like this:
+ * Package () { "property", \_SB.PCI0.LPC }
+ */
+int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name,
+ acpi_handle *obj_handle)
+{
+ const union acpi_object *obj;
+ int ret;
+
+ ret = acpi_dev_get_property(adev, name, ACPI_TYPE_LOCAL_REFERENCE,
+ &obj);
+ if (!ret)
+ *obj_handle = obj->reference.handle;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 7efe546a8c42..bd1dbedcdad9 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -880,6 +880,7 @@ static void acpi_device_release(struct device *dev)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
+ acpi_free_properties(acpi_dev);
acpi_free_pnp_ids(&acpi_dev->pnp);
acpi_free_power_resources_lists(acpi_dev);
kfree(acpi_dev);
@@ -1870,6 +1871,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
acpi_set_device_status(device, sta);
acpi_device_get_busid(device);
acpi_set_pnp_ids(handle, &device->pnp, type);
+ acpi_init_properties(device);
acpi_bus_get_flags(device);
device->flags.match_driver = false;
device->flags.initialized = true;
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 84a2e29a2314..1f3d8061cbf2 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -347,6 +347,7 @@ struct acpi_device {
struct acpi_scan_handler *handler;
struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
+ const union acpi_object *properties;
void *driver_data;
struct device dev;
unsigned int physical_node_count;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index f9143c8f3f18..651abc4cde29 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -303,6 +303,35 @@ int acpi_check_region(resource_size_t start, resource_size_t n,
int acpi_resources_are_enforced(void);
+int acpi_dev_get_properties(struct acpi_device *adev,
+ int (*callback)(const union acpi_object *, void *),
+ void *data);
+int acpi_dev_get_property(struct acpi_device *adev, const char *name,
+ acpi_object_type type, const union acpi_object **obj);
+int acpi_dev_get_property_u64(struct acpi_device *adev, const char *name,
+ u64 *value);
+int acpi_dev_get_property_u32(struct acpi_device *adev, const char *name,
+ u32 *value);
+int acpi_dev_get_property_u16(struct acpi_device *adev, const char *name,
+ u16 *value);
+int acpi_dev_get_property_u8(struct acpi_device *adev, const char *name,
+ u8 *value);
+int acpi_dev_get_property_array_u64(struct acpi_device *adev, const char *name,
+ u64 *values, size_t nvalues);
+int acpi_dev_get_property_array_u32(struct acpi_device *adev, const char *name,
+ u32 *values, size_t nvalues);
+int acpi_dev_get_property_array_u16(struct acpi_device *adev, const char *name,
+ u16 *values, size_t nvalues);
+int acpi_dev_get_property_array_u8(struct acpi_device *adev, const char *name,
+ u8 *values, size_t nvalues);
+int acpi_dev_get_property_string(struct acpi_device *adev, const char *name,
+ const char **value);
+int acpi_dev_get_property_array_string(struct acpi_device *adev,
+ const char *name, const char **values,
+ size_t nvalues);
+int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name,
+ acpi_handle *obj_handle);
+
#ifdef CONFIG_HIBERNATION
void __init acpi_no_s4_hw_signature(void);
#endif