]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/acpi/property.c
ACPI / property: Add routine for extraction of _DSD properties
[mirror_ubuntu-jammy-kernel.git] / drivers / acpi / property.c
CommitLineData
ffdcd955
MW
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
733e6251
MW
79static void acpi_init_of_compatible(struct acpi_device *adev)
80{
81 const union acpi_object *of_compatible;
733e6251
MW
82 int ret;
83
733e6251
MW
84 ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING,
85 &of_compatible);
86 if (ret) {
87 ret = acpi_dev_get_property(adev, "compatible",
88 ACPI_TYPE_STRING, &of_compatible);
89 if (ret) {
5c53b262
RW
90 if (adev->parent
91 && adev->parent->flags.of_compatible_ok)
92 goto out;
93
733e6251
MW
94 return;
95 }
96 }
97 adev->data.of_compatible = of_compatible;
5c53b262
RW
98
99 out:
100 adev->flags.of_compatible_ok = 1;
733e6251
MW
101}
102
bd8191cc
RW
103static bool acpi_extract_properties(const union acpi_object *desc,
104 struct acpi_device_data *data)
ffdcd955 105{
ffdcd955
MW
106 int i;
107
ffdcd955 108 if (desc->package.count % 2)
bd8191cc 109 return false;
ffdcd955
MW
110
111 /* Look for the device properties UUID. */
112 for (i = 0; i < desc->package.count; i += 2) {
113 const union acpi_object *uuid, *properties;
114
115 uuid = &desc->package.elements[i];
116 properties = &desc->package.elements[i + 1];
117
118 /*
119 * The first element must be a UUID and the second one must be
120 * a package.
121 */
122 if (uuid->type != ACPI_TYPE_BUFFER || uuid->buffer.length != 16
123 || properties->type != ACPI_TYPE_PACKAGE)
124 break;
125
126 if (memcmp(uuid->buffer.pointer, prp_uuid, sizeof(prp_uuid)))
127 continue;
128
129 /*
130 * We found the matching UUID. Now validate the format of the
131 * package immediately following it.
132 */
133 if (!acpi_properties_format_valid(properties))
134 break;
135
bd8191cc
RW
136 data->properties = properties;
137 return true;
138 }
733e6251 139
bd8191cc
RW
140 return false;
141}
5c53b262 142
bd8191cc
RW
143void acpi_init_properties(struct acpi_device *adev)
144{
145 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
146 struct acpi_hardware_id *hwid;
147 acpi_status status;
148 bool acpi_of = false;
149
150 /*
151 * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in
152 * Device Tree compatible properties for this device.
153 */
154 list_for_each_entry(hwid, &adev->pnp.ids, list) {
155 if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) {
156 acpi_of = true;
157 break;
158 }
ffdcd955
MW
159 }
160
bd8191cc
RW
161 status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
162 ACPI_TYPE_PACKAGE);
163 if (ACPI_FAILURE(status))
164 goto out;
165
166 if (acpi_extract_properties(buf.pointer, &adev->data)) {
167 adev->data.pointer = buf.pointer;
168 if (acpi_of)
169 acpi_init_of_compatible(adev);
170 } else {
171 acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n");
172 ACPI_FREE(buf.pointer);
173 }
5c53b262
RW
174
175 out:
176 if (acpi_of && !adev->flags.of_compatible_ok)
177 acpi_handle_info(adev->handle,
ee892094 178 ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n");
ffdcd955
MW
179}
180
181void acpi_free_properties(struct acpi_device *adev)
182{
183 ACPI_FREE((void *)adev->data.pointer);
733e6251 184 adev->data.of_compatible = NULL;
ffdcd955
MW
185 adev->data.pointer = NULL;
186 adev->data.properties = NULL;
187}
188
189/**
190 * acpi_dev_get_property - return an ACPI property with given name
191 * @adev: ACPI device to get property
192 * @name: Name of the property
193 * @type: Expected property type
194 * @obj: Location to store the property value (if not %NULL)
195 *
196 * Look up a property with @name and store a pointer to the resulting ACPI
197 * object at the location pointed to by @obj if found.
198 *
199 * Callers must not attempt to free the returned objects. These objects will be
200 * freed by the ACPI core automatically during the removal of @adev.
201 *
202 * Return: %0 if property with @name has been found (success),
203 * %-EINVAL if the arguments are invalid,
204 * %-ENODATA if the property doesn't exist,
205 * %-EPROTO if the property value type doesn't match @type.
206 */
207int acpi_dev_get_property(struct acpi_device *adev, const char *name,
208 acpi_object_type type, const union acpi_object **obj)
209{
210 const union acpi_object *properties;
211 int i;
212
213 if (!adev || !name)
214 return -EINVAL;
215
216 if (!adev->data.pointer || !adev->data.properties)
217 return -ENODATA;
218
219 properties = adev->data.properties;
220 for (i = 0; i < properties->package.count; i++) {
221 const union acpi_object *propname, *propvalue;
222 const union acpi_object *property;
223
224 property = &properties->package.elements[i];
225
226 propname = &property->package.elements[0];
227 propvalue = &property->package.elements[1];
228
229 if (!strcmp(name, propname->string.pointer)) {
230 if (type != ACPI_TYPE_ANY && propvalue->type != type)
231 return -EPROTO;
232 else if (obj)
233 *obj = propvalue;
234
235 return 0;
236 }
237 }
238 return -ENODATA;
239}
240EXPORT_SYMBOL_GPL(acpi_dev_get_property);
241
242/**
243 * acpi_dev_get_property_array - return an ACPI array property with given name
244 * @adev: ACPI device to get property
245 * @name: Name of the property
246 * @type: Expected type of array elements
247 * @obj: Location to store a pointer to the property value (if not NULL)
248 *
249 * Look up an array property with @name and store a pointer to the resulting
250 * ACPI object at the location pointed to by @obj if found.
251 *
252 * Callers must not attempt to free the returned objects. Those objects will be
253 * freed by the ACPI core automatically during the removal of @adev.
254 *
255 * Return: %0 if array property (package) with @name has been found (success),
256 * %-EINVAL if the arguments are invalid,
257 * %-ENODATA if the property doesn't exist,
258 * %-EPROTO if the property is not a package or the type of its elements
259 * doesn't match @type.
260 */
261int acpi_dev_get_property_array(struct acpi_device *adev, const char *name,
262 acpi_object_type type,
263 const union acpi_object **obj)
264{
265 const union acpi_object *prop;
266 int ret, i;
267
268 ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &prop);
269 if (ret)
270 return ret;
271
272 if (type != ACPI_TYPE_ANY) {
273 /* Check that all elements are of correct type. */
274 for (i = 0; i < prop->package.count; i++)
275 if (prop->package.elements[i].type != type)
276 return -EPROTO;
277 }
278 if (obj)
279 *obj = prop;
280
281 return 0;
282}
283EXPORT_SYMBOL_GPL(acpi_dev_get_property_array);
284
285/**
286 * acpi_dev_get_property_reference - returns handle to the referenced object
287 * @adev: ACPI device to get property
288 * @name: Name of the property
ffdcd955
MW
289 * @index: Index of the reference to return
290 * @args: Location to store the returned reference with optional arguments
291 *
292 * Find property with @name, verifify that it is a package containing at least
293 * one object reference and if so, store the ACPI device object pointer to the
60ba032e
RW
294 * target object in @args->adev. If the reference includes arguments, store
295 * them in the @args->args[] array.
ffdcd955 296 *
60ba032e
RW
297 * If there's more than one reference in the property value package, @index is
298 * used to select the one to return.
ffdcd955
MW
299 *
300 * Return: %0 on success, negative error code on failure.
301 */
60ba032e
RW
302int acpi_dev_get_property_reference(struct acpi_device *adev,
303 const char *name, size_t index,
ffdcd955
MW
304 struct acpi_reference_args *args)
305{
306 const union acpi_object *element, *end;
307 const union acpi_object *obj;
308 struct acpi_device *device;
309 int ret, idx = 0;
310
311 ret = acpi_dev_get_property(adev, name, ACPI_TYPE_ANY, &obj);
312 if (ret)
313 return ret;
314
315 /*
316 * The simplest case is when the value is a single reference. Just
317 * return that reference then.
318 */
319 if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) {
60ba032e 320 if (index)
ffdcd955
MW
321 return -EINVAL;
322
323 ret = acpi_bus_get_device(obj->reference.handle, &device);
324 if (ret)
325 return ret;
326
327 args->adev = device;
328 args->nargs = 0;
329 return 0;
330 }
331
332 /*
333 * If it is not a single reference, then it is a package of
334 * references followed by number of ints as follows:
335 *
336 * Package () { REF, INT, REF, INT, INT }
337 *
338 * The index argument is then used to determine which reference
339 * the caller wants (along with the arguments).
340 */
341 if (obj->type != ACPI_TYPE_PACKAGE || index >= obj->package.count)
342 return -EPROTO;
343
344 element = obj->package.elements;
345 end = element + obj->package.count;
346
347 while (element < end) {
348 u32 nargs, i;
349
350 if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
351 return -EPROTO;
352
353 ret = acpi_bus_get_device(element->reference.handle, &device);
354 if (ret)
355 return -ENODEV;
356
357 element++;
358 nargs = 0;
359
60ba032e
RW
360 /* assume following integer elements are all args */
361 for (i = 0; element + i < end; i++) {
362 int type = element[i].type;
ffdcd955 363
60ba032e
RW
364 if (type == ACPI_TYPE_INTEGER)
365 nargs++;
366 else if (type == ACPI_TYPE_LOCAL_REFERENCE)
367 break;
368 else
369 return -EPROTO;
ffdcd955
MW
370 }
371
372 if (idx++ == index) {
373 args->adev = device;
374 args->nargs = nargs;
375 for (i = 0; i < nargs; i++)
376 args->args[i] = element[i].integer.value;
377
378 return 0;
379 }
380
381 element += nargs;
382 }
383
384 return -EPROTO;
385}
386EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference);
b31384fa
RW
387
388int acpi_dev_prop_get(struct acpi_device *adev, const char *propname,
389 void **valptr)
390{
391 return acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY,
392 (const union acpi_object **)valptr);
393}
394
395int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname,
396 enum dev_prop_type proptype, void *val)
397{
398 const union acpi_object *obj;
399 int ret;
400
401 if (!val)
402 return -EINVAL;
403
404 if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) {
405 ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_INTEGER, &obj);
406 if (ret)
407 return ret;
408
409 switch (proptype) {
410 case DEV_PROP_U8:
411 if (obj->integer.value > U8_MAX)
412 return -EOVERFLOW;
413 *(u8 *)val = obj->integer.value;
414 break;
415 case DEV_PROP_U16:
416 if (obj->integer.value > U16_MAX)
417 return -EOVERFLOW;
418 *(u16 *)val = obj->integer.value;
419 break;
420 case DEV_PROP_U32:
421 if (obj->integer.value > U32_MAX)
422 return -EOVERFLOW;
423 *(u32 *)val = obj->integer.value;
424 break;
425 default:
426 *(u64 *)val = obj->integer.value;
427 break;
428 }
429 } else if (proptype == DEV_PROP_STRING) {
430 ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_STRING, &obj);
431 if (ret)
432 return ret;
433
434 *(char **)val = obj->string.pointer;
435 } else {
436 ret = -EINVAL;
437 }
438 return ret;
439}
440
441static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val,
442 size_t nval)
443{
444 int i;
445
446 for (i = 0; i < nval; i++) {
447 if (items[i].type != ACPI_TYPE_INTEGER)
448 return -EPROTO;
449 if (items[i].integer.value > U8_MAX)
450 return -EOVERFLOW;
451
452 val[i] = items[i].integer.value;
453 }
454 return 0;
455}
456
457static int acpi_copy_property_array_u16(const union acpi_object *items,
458 u16 *val, size_t nval)
459{
460 int i;
461
462 for (i = 0; i < nval; i++) {
463 if (items[i].type != ACPI_TYPE_INTEGER)
464 return -EPROTO;
465 if (items[i].integer.value > U16_MAX)
466 return -EOVERFLOW;
467
468 val[i] = items[i].integer.value;
469 }
470 return 0;
471}
472
473static int acpi_copy_property_array_u32(const union acpi_object *items,
474 u32 *val, size_t nval)
475{
476 int i;
477
478 for (i = 0; i < nval; i++) {
479 if (items[i].type != ACPI_TYPE_INTEGER)
480 return -EPROTO;
481 if (items[i].integer.value > U32_MAX)
482 return -EOVERFLOW;
483
484 val[i] = items[i].integer.value;
485 }
486 return 0;
487}
488
489static int acpi_copy_property_array_u64(const union acpi_object *items,
490 u64 *val, size_t nval)
491{
492 int i;
493
494 for (i = 0; i < nval; i++) {
495 if (items[i].type != ACPI_TYPE_INTEGER)
496 return -EPROTO;
497
498 val[i] = items[i].integer.value;
499 }
500 return 0;
501}
502
503static int acpi_copy_property_array_string(const union acpi_object *items,
504 char **val, size_t nval)
505{
506 int i;
507
508 for (i = 0; i < nval; i++) {
509 if (items[i].type != ACPI_TYPE_STRING)
510 return -EPROTO;
511
512 val[i] = items[i].string.pointer;
513 }
514 return 0;
515}
516
517int acpi_dev_prop_read(struct acpi_device *adev, const char *propname,
518 enum dev_prop_type proptype, void *val, size_t nval)
519{
520 const union acpi_object *obj;
521 const union acpi_object *items;
522 int ret;
523
524 if (val && nval == 1) {
525 ret = acpi_dev_prop_read_single(adev, propname, proptype, val);
526 if (!ret)
527 return ret;
528 }
529
530 ret = acpi_dev_get_property_array(adev, propname, ACPI_TYPE_ANY, &obj);
531 if (ret)
532 return ret;
533
534 if (!val)
535 return obj->package.count;
b31384fa
RW
536
537 if (nval > obj->package.count)
538 return -EOVERFLOW;
7dc59dc9
AS
539 else if (nval <= 0)
540 return -EINVAL;
b31384fa
RW
541
542 items = obj->package.elements;
7dc59dc9 543
b31384fa
RW
544 switch (proptype) {
545 case DEV_PROP_U8:
546 ret = acpi_copy_property_array_u8(items, (u8 *)val, nval);
547 break;
548 case DEV_PROP_U16:
549 ret = acpi_copy_property_array_u16(items, (u16 *)val, nval);
550 break;
551 case DEV_PROP_U32:
552 ret = acpi_copy_property_array_u32(items, (u32 *)val, nval);
553 break;
554 case DEV_PROP_U64:
555 ret = acpi_copy_property_array_u64(items, (u64 *)val, nval);
556 break;
557 case DEV_PROP_STRING:
558 ret = acpi_copy_property_array_string(items, (char **)val, nval);
559 break;
560 default:
561 ret = -EINVAL;
562 break;
563 }
564 return ret;
565}