aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2014-10-21 07:33:55 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-11-04 15:58:21 -0500
commitffdcd955c3078af3ce117edcfce80fde1a512bed (patch)
tree93a3be25da68fb204bece7bc32fac29629963437 /drivers
parenta5501fe4eaa4ee35f8de6efac8d1c4ad16094951 (diff)
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, but not limited to, x86 machines. ACPI allows passing arbitrary data from methods but there has not been mechanism equivalent to Device Tree until the introduction of _DSD in the recent publication of the ACPI 5.1 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 _DSD method (Device Specific Data, introduced with ACPI 5.1) that returns the properties in a package of packages. For example: Name (_DSD, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () {"name1", <VALUE1>}, Package () {"name2", <VALUE2>}, ... } }) The UUID reserved for properties is daffd814-6eba-4d8c-8a91-bc9bbf4aa301 and is documented in the ACPI 5.1 companion document called "_DSD Implementation Guide" [1], [2]. 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. [1] http://www.uefi.org/sites/default/files/resources/_DSD-implementation-guide-toplevel.htm [2] http://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org> Reviewed-by: Josh Triplett <josh@joshtriplett.org> Reviewed-by: Grant Likely <grant.likely@linaro.org> Signed-off-by: Darren Hart <dvhart@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/internal.h6
-rw-r--r--drivers/acpi/property.c364
-rw-r--r--drivers/acpi/scan.c2
4 files changed, 373 insertions, 0 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index c3b2fcb729f3..6d11522f0e48 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -47,6 +47,7 @@ acpi-y += int340x_thermal.o
47acpi-y += power.o 47acpi-y += power.o
48acpi-y += event.o 48acpi-y += event.o
49acpi-y += sysfs.o 49acpi-y += sysfs.o
50acpi-y += property.o
50acpi-$(CONFIG_X86) += acpi_cmos_rtc.o 51acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
51acpi-$(CONFIG_DEBUG_FS) += debugfs.o 52acpi-$(CONFIG_DEBUG_FS) += debugfs.o
52acpi-$(CONFIG_ACPI_NUMA) += numa.o 53acpi-$(CONFIG_ACPI_NUMA) += numa.o
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 447f6d679b29..163e82f536fa 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -173,4 +173,10 @@ static inline void suspend_nvs_restore(void) {}
173bool acpi_osi_is_win8(void); 173bool acpi_osi_is_win8(void);
174#endif 174#endif
175 175
176/*--------------------------------------------------------------------------
177 Device properties
178 -------------------------------------------------------------------------- */
179void acpi_init_properties(struct acpi_device *adev);
180void acpi_free_properties(struct acpi_device *adev);
181
176#endif /* _ACPI_INTERNAL_H_ */ 182#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
new file mode 100644
index 000000000000..c4a3e800e82c
--- /dev/null
+++ b/drivers/acpi/property.c
@@ -0,0 +1,364 @@
1/*
2 * ACPI device specific properties support.
3 *
4 * Copyright (C) 2014, Intel Corporation
5 * All rights reserved.
6 *
7 * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
8 * Darren Hart <dvhart@linux.intel.com>
9 * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/acpi.h>
17#include <linux/device.h>
18#include <linux/export.h>
19
20#include "internal.h"
21
22/* ACPI _DSD device properties UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
23static const u8 prp_uuid[16] = {
24 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
25 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01
26};
27
28static bool acpi_property_value_ok(const union acpi_object *value)
29{
30 int j;
31
32 /*
33 * The value must be an integer, a string, a reference, or a package
34 * whose every element must be an integer, a string, or a reference.
35 */
36 switch (value->type) {
37 case ACPI_TYPE_INTEGER:
38 case ACPI_TYPE_STRING:
39 case ACPI_TYPE_LOCAL_REFERENCE:
40 return true;
41
42 case ACPI_TYPE_PACKAGE:
43 for (j = 0; j < value->package.count; j++)
44 switch (value->package.elements[j].type) {
45 case ACPI_TYPE_INTEGER:
46 case ACPI_TYPE_STRING:
47 case ACPI_TYPE_LOCAL_REFERENCE:
48 continue;
49
50 default:
51 return false;
52 }
53
54 return true;
55 }
56 return false;
57}
58
59static bool acpi_properties_format_valid(const union acpi_object *properties)
60{
61 int i;
62
63 for (i = 0; i < properties->package.count; i++) {
64 const union acpi_object *property;
65
66 property = &properties->package.elements[i];
67 /*
68 * Only two elements allowed, the first one must be a string and
69 * the second one has to satisfy certain conditions.
70 */
71 if (property->package.count != 2
72 || property->package.elements[0].type != ACPI_TYPE_STRING
73 || !acpi_property_value_ok(&property->package.elements[1]))
74 return false;
75 }
76 return true;
77}
78
79void acpi_init_properties(struct acpi_device *adev)
80{
81 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
82 const union acpi_object *desc;
83 acpi_status status;
84 int i;
85
86 status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
87 ACPI_TYPE_PACKAGE);
88 if (ACPI_FAILURE(status))
89 return;
90
91 desc = buf.pointer;
92 if (desc->package.count % 2)
93 goto fail;
94
95 /* Look for the device properties UUID. */
96 for (i = 0; i < desc->package.count; i += 2) {
97 const union acpi_object *uuid, *properties;
98
99 uuid = &desc->package.elements[i];
100 properties = &desc->package.elements[i + 1];
101
102 /*
103 * The first element must be a UUID and the second one must be
104 * a package.
105 */
106 if (uuid->type != ACPI_TYPE_BUFFER || uuid->buffer.length != 16
107 || properties->type != ACPI_TYPE_PACKAGE)
108 break;
109
110 if (memcmp(uuid->buffer.pointer, prp_uuid, sizeof(prp_uuid)))
111 continue;
112
113 /*
114 * We found the matching UUID. Now validate the format of the
115 * package immediately following it.
116 */
117 if (!acpi_properties_format_valid(properties))
118 break;
119
120 adev->data.pointer = buf.pointer;
121 adev->data.properties = properties;
122 return;
123 }
124
125 fail:
126 dev_warn(&adev->dev, "Returned _DSD data is not valid, skipping\n");
127 ACPI_FREE(buf.pointer);
128}
129
130void acpi_free_properties(struct acpi_device *adev)
131{
132 ACPI_FREE((void *)adev->data.pointer);
133 adev->data.pointer = NULL;
134 adev->data.properties = NULL;
135}
136
137/**
138 * acpi_dev_get_property - return an ACPI property with given name
139 * @adev: ACPI device to get property
140 * @name: Name of the property
141 * @type: Expected property type
142 * @obj: Location to store the property value (if not %NULL)
143 *
144 * Look up a property with @name and store a pointer to the resulting ACPI
145 * object at the location pointed to by @obj if found.
146 *
147 * Callers must not attempt to free the returned objects. These objects will be
148 * freed by the ACPI core automatically during the removal of @adev.
149 *
150 * Return: %0 if property with @name has been found (success),
151 * %-EINVAL if the arguments are invalid,
152 * %-ENODATA if the property doesn't exist,
153 * %-EPROTO if the property value type doesn't match @type.
154 */
155int acpi_dev_get_property(struct acpi_device *adev, const char *name,
156 acpi_object_type type, const union acpi_object **obj)
157{
158 const union acpi_object *properties;
159 int i;
160
161 if (!adev || !name)
162 return -EINVAL;
163
164 if (!adev->data.pointer || !adev->data.properties)
165 return -ENODATA;
166
167 properties = adev->data.properties;
168 for (i = 0; i < properties->package.count; i++) {
169 const union acpi_object *propname, *propvalue;
170 const union acpi_object *property;
171
172 property = &properties->package.elements[i];
173
174 propname = &property->package.elements[0];
175 propvalue = &property->package.elements[1];
176
177 if (!strcmp(name, propname->string.pointer)) {
178 if (type != ACPI_TYPE_ANY && propvalue->type != type)
179 return -EPROTO;
180 else if (obj)
181 *obj = propvalue;
182
183 return 0;
184 }
185 }
186 return -ENODATA;
187}
188EXPORT_SYMBOL_GPL(acpi_dev_get_property);
189
190/**
191 * acpi_dev_get_property_array - return an ACPI array property with given name
192 * @adev: ACPI device to get property
193 * @name: Name of the property
194 * @type: Expected type of array elements
195 * @obj: Location to store a pointer to the property value (if not NULL)
196 *
197 * Look up an array property with @name and store a pointer to the resulting
198 * ACPI object at the location pointed to by @obj if found.
199 *
200 * Callers must not attempt to free the returned objects. Those objects will be
201 * freed by the ACPI core automatically during the removal of @adev.
202 *
203 * Return: %0 if array property (package) with @name has been found (success),
204 * %-EINVAL if the arguments are invalid,
205 * %-ENODATA if the property doesn't exist,
206 * %-EPROTO if the property is not a package or the type of its elements
207 * doesn't match @type.
208 */
209int acpi_dev_get_property_array(struct acpi_device *adev, const char *name,
210 acpi_object_type type,
211 const union acpi_object **obj)
212{
213 const union acpi_object *prop;
214 int ret, i;
215
216 ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &prop);
217 if (ret)
218 return ret;
219
220 if (type != ACPI_TYPE_ANY) {
221 /* Check that all elements are of correct type. */
222 for (i = 0; i < prop->package.count; i++)
223 if (prop->package.elements[i].type != type)
224 return -EPROTO;
225 }
226 if (obj)
227 *obj = prop;
228
229 return 0;
230}
231EXPORT_SYMBOL_GPL(acpi_dev_get_property_array);
232
233/**
234 * acpi_dev_get_property_reference - returns handle to the referenced object
235 * @adev: ACPI device to get property
236 * @name: Name of the property
237 * @size_prop: Name of the "size" property in referenced object
238 * @index: Index of the reference to return
239 * @args: Location to store the returned reference with optional arguments
240 *
241 * Find property with @name, verifify that it is a package containing at least
242 * one object reference and if so, store the ACPI device object pointer to the
243 * target object in @args->adev.
244 *
245 * If the reference includes arguments (@size_prop is not %NULL) follow the
246 * reference and check whether or not there is an integer property @size_prop
247 * under the target object and if so, whether or not its value matches the
248 * number of arguments that follow the reference. If there's more than one
249 * reference in the property value package, @index is used to select the one to
250 * return.
251 *
252 * Return: %0 on success, negative error code on failure.
253 */
254int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name,
255 const char *size_prop, size_t index,
256 struct acpi_reference_args *args)
257{
258 const union acpi_object *element, *end;
259 const union acpi_object *obj;
260 struct acpi_device *device;
261 int ret, idx = 0;
262
263 ret = acpi_dev_get_property(adev, name, ACPI_TYPE_ANY, &obj);
264 if (ret)
265 return ret;
266
267 /*
268 * The simplest case is when the value is a single reference. Just
269 * return that reference then.
270 */
271 if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) {
272 if (size_prop || index)
273 return -EINVAL;
274
275 ret = acpi_bus_get_device(obj->reference.handle, &device);
276 if (ret)
277 return ret;
278
279 args->adev = device;
280 args->nargs = 0;
281 return 0;
282 }
283
284 /*
285 * If it is not a single reference, then it is a package of
286 * references followed by number of ints as follows:
287 *
288 * Package () { REF, INT, REF, INT, INT }
289 *
290 * The index argument is then used to determine which reference
291 * the caller wants (along with the arguments).
292 */
293 if (obj->type != ACPI_TYPE_PACKAGE || index >= obj->package.count)
294 return -EPROTO;
295
296 element = obj->package.elements;
297 end = element + obj->package.count;
298
299 while (element < end) {
300 u32 nargs, i;
301
302 if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
303 return -EPROTO;
304
305 ret = acpi_bus_get_device(element->reference.handle, &device);
306 if (ret)
307 return -ENODEV;
308
309 element++;
310 nargs = 0;
311
312 if (size_prop) {
313 const union acpi_object *prop;
314
315 /*
316 * Find out how many arguments the refenced object
317 * expects by reading its size_prop property.
318 */
319 ret = acpi_dev_get_property(device, size_prop,
320 ACPI_TYPE_INTEGER, &prop);
321 if (ret)
322 return ret;
323
324 nargs = prop->integer.value;
325 if (nargs > MAX_ACPI_REFERENCE_ARGS
326 || element + nargs > end)
327 return -EPROTO;
328
329 /*
330 * Skip to the start of the arguments and verify
331 * that they all are in fact integers.
332 */
333 for (i = 0; i < nargs; i++)
334 if (element[i].type != ACPI_TYPE_INTEGER)
335 return -EPROTO;
336 } else {
337 /* assume following integer elements are all args */
338 for (i = 0; element + i < end; i++) {
339 int type = element[i].type;
340
341 if (type == ACPI_TYPE_INTEGER)
342 nargs++;
343 else if (type == ACPI_TYPE_LOCAL_REFERENCE)
344 break;
345 else
346 return -EPROTO;
347 }
348 }
349
350 if (idx++ == index) {
351 args->adev = device;
352 args->nargs = nargs;
353 for (i = 0; i < nargs; i++)
354 args->args[i] = element[i].integer.value;
355
356 return 0;
357 }
358
359 element += nargs;
360 }
361
362 return -EPROTO;
363}
364EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 0476e90b2091..40d80ac0552f 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -922,6 +922,7 @@ static void acpi_device_release(struct device *dev)
922{ 922{
923 struct acpi_device *acpi_dev = to_acpi_device(dev); 923 struct acpi_device *acpi_dev = to_acpi_device(dev);
924 924
925 acpi_free_properties(acpi_dev);
925 acpi_free_pnp_ids(&acpi_dev->pnp); 926 acpi_free_pnp_ids(&acpi_dev->pnp);
926 acpi_free_power_resources_lists(acpi_dev); 927 acpi_free_power_resources_lists(acpi_dev);
927 kfree(acpi_dev); 928 kfree(acpi_dev);
@@ -1926,6 +1927,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
1926 acpi_set_device_status(device, sta); 1927 acpi_set_device_status(device, sta);
1927 acpi_device_get_busid(device); 1928 acpi_device_get_busid(device);
1928 acpi_set_pnp_ids(handle, &device->pnp, type); 1929 acpi_set_pnp_ids(handle, &device->pnp, type);
1930 acpi_init_properties(device);
1929 acpi_bus_get_flags(device); 1931 acpi_bus_get_flags(device);
1930 device->flags.match_driver = false; 1932 device->flags.match_driver = false;
1931 device->flags.initialized = true; 1933 device->flags.initialized = true;