aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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
4 files changed, 521 insertions, 0 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 8e9bbe6b03c0..d030fca22d05 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -46,6 +46,7 @@ acpi-y += acpi_pnp.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 9a1822803479..4b9a3341bd64 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -188,4 +188,10 @@ struct platform_device *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 f775fa0d850f..fc97e6123864 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);
@@ -1876,6 +1877,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;