| 1 | |
| 2 | |
| 3 | * ACPI device specific properties support.
|
| 4 | *
|
| 5 | * Copyright (C) 2014 - 2023, Intel Corporation
|
| 6 | * All rights reserved.
|
| 7 | *
|
| 8 | * Authors: Mika Westerberg <[email protected]>
|
| 9 | * Darren Hart <[email protected]>
|
| 10 | * Rafael J. Wysocki <[email protected]>
|
| 11 | * Sakari Ailus <[email protected]>
|
| 12 |
|
| 13 |
|
| 14 | #define pr_fmt(fmt) "ACPI: " fmt
|
| 15 |
|
| 16 | #include <linux/acpi.h>
|
| 17 | #include <linux/device.h>
|
| 18 | #include <linux/export.h>
|
| 19 |
|
| 20 | #include "internal.h"
|
| 21 |
|
| 22 | static int acpi_data_get_property_array(const struct acpi_device_data *data,
|
| 23 | const char *name,
|
| 24 | acpi_object_type type,
|
| 25 | const union acpi_object **obj);
|
| 26 |
|
| 27 | |
| 28 | * The GUIDs here are made equivalent to each other in order to avoid extra
|
| 29 | * complexity in the properties handling code, with the caveat that the
|
| 30 | * kernel will accept certain combinations of GUID and properties that are
|
| 31 | * not defined without a warning. For instance if any of the properties
|
| 32 | * from different GUID appear in a property list of another, it will be
|
| 33 | * accepted by the kernel. Firmware validation tools should catch these.
|
| 34 | *
|
| 35 | * References:
|
| 36 | *
|
| 37 | * [1] UEFI DSD Guide.
|
| 38 | * https://github.com/UEFI/DSD-Guide/blob/main/src/dsd-guide.adoc
|
| 39 |
|
| 40 | static const guid_t prp_guids[] = {
|
| 41 |
|
| 42 | GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
|
| 43 | 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01),
|
| 44 |
|
| 45 | GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3,
|
| 46 | 0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4),
|
| 47 |
|
| 48 | GUID_INIT(0xefcc06cc, 0x73ac, 0x4bc3,
|
| 49 | 0xbf, 0xf0, 0x76, 0x14, 0x38, 0x07, 0xc3, 0x89),
|
| 50 |
|
| 51 | GUID_INIT(0xc44d002f, 0x69f9, 0x4e7d,
|
| 52 | 0xa9, 0x04, 0xa7, 0xba, 0xab, 0xdf, 0x43, 0xf7),
|
| 53 |
|
| 54 | GUID_INIT(0x6c501103, 0xc189, 0x4296,
|
| 55 | 0xba, 0x72, 0x9b, 0xf5, 0xa2, 0x6e, 0xbe, 0x5d),
|
| 56 |
|
| 57 | GUID_INIT(0x5025030f, 0x842f, 0x4ab4,
|
| 58 | 0xa5, 0x61, 0x99, 0xa5, 0x18, 0x97, 0x62, 0xd0),
|
| 59 | };
|
| 60 |
|
| 61 |
|
| 62 | static const guid_t ads_guid =
|
| 63 | GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6,
|
| 64 | 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b);
|
| 65 |
|
| 66 |
|
| 67 | static const guid_t buffer_prop_guid =
|
| 68 | GUID_INIT(0xedb12dd0, 0x363d, 0x4085,
|
| 69 | 0xa3, 0xd2, 0x49, 0x52, 0x2c, 0xa1, 0x60, 0xc4);
|
| 70 |
|
| 71 | static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
|
| 72 | union acpi_object *desc,
|
| 73 | struct acpi_device_data *data,
|
| 74 | struct fwnode_handle *parent);
|
| 75 | static bool acpi_extract_properties(acpi_handle handle,
|
| 76 | union acpi_object *desc,
|
| 77 | struct acpi_device_data *data);
|
| 78 |
|
| 79 | static bool acpi_nondev_subnode_extract(union acpi_object *desc,
|
| 80 | acpi_handle handle,
|
| 81 | const union acpi_object *link,
|
| 82 | struct list_head *list,
|
| 83 | struct fwnode_handle *parent)
|
| 84 | {
|
| 85 | struct acpi_data_node *dn;
|
| 86 | acpi_handle scope = NULL;
|
| 87 | bool result;
|
| 88 |
|
| 89 | if (acpi_graph_ignore_port(handle))
|
| 90 | return false;
|
| 91 |
|
| 92 | dn = kzalloc_obj(*dn);
|
| 93 | if (!dn)
|
| 94 | return false;
|
| 95 |
|
| 96 | dn->name = link->package.elements[0].string.pointer;
|
| 97 | fwnode_init(&dn->fwnode, &acpi_data_fwnode_ops);
|
| 98 | dn->parent = parent;
|
| 99 | INIT_LIST_HEAD(&dn->data.properties);
|
| 100 | INIT_LIST_HEAD(&dn->data.subnodes);
|
| 101 |
|
| 102 | |
| 103 | * The scope for the completion of relative pathname segments and
|
| 104 | * subnode object lookup is the one of the namespace node (device)
|
| 105 | * containing the object that has returned the package. That is, it's
|
| 106 | * the scope of that object's parent device.
|
| 107 |
|
| 108 | if (handle)
|
| 109 | acpi_get_parent(handle, &scope);
|
| 110 |
|
| 111 | |
| 112 | * Extract properties from the _DSD-equivalent package pointed to by
|
| 113 | * desc and use scope (if not NULL) for the completion of relative
|
| 114 | * pathname segments.
|
| 115 | *
|
| 116 | * The extracted properties will be held in the new data node dn.
|
| 117 |
|
| 118 | result = acpi_extract_properties(scope, desc, &dn->data);
|
| 119 | |
| 120 | * Look for subnodes in the _DSD-equivalent package pointed to by desc
|
| 121 | * and create child nodes of dn if there are any.
|
| 122 |
|
| 123 | if (acpi_enumerate_nondev_subnodes(scope, desc, &dn->data, &dn->fwnode))
|
| 124 | result = true;
|
| 125 |
|
| 126 | if (!result) {
|
| 127 | kfree(dn);
|
| 128 | acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
|
| 129 | return false;
|
| 130 | }
|
| 131 |
|
| 132 | |
| 133 | * This will be NULL if the desc package is embedded in an outer
|
| 134 | * _DSD-equivalent package and its scope cannot be determined.
|
| 135 |
|
| 136 | dn->handle = handle;
|
| 137 | dn->data.pointer = desc;
|
| 138 | list_add_tail(&dn->sibling, list);
|
| 139 |
|
| 140 | return true;
|
| 141 | }
|
| 142 |
|
| 143 | static bool acpi_nondev_subnode_ok(acpi_handle scope,
|
| 144 | const union acpi_object *link,
|
| 145 | struct list_head *list,
|
| 146 | struct fwnode_handle *parent)
|
| 147 | {
|
| 148 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
|
| 149 | acpi_handle handle;
|
| 150 | acpi_status status;
|
| 151 |
|
| 152 | |
| 153 | * If the scope is unknown, the _DSD-equivalent package being parsed
|
| 154 | * was embedded in an outer _DSD-equivalent package as a result of
|
| 155 | * direct evaluation of an object pointed to by a reference. In that
|
| 156 | * case, using a pathname as the target object pointer is invalid.
|
| 157 |
|
| 158 | if (!scope)
|
| 159 | return false;
|
| 160 |
|
| 161 | status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
|
| 162 | &handle);
|
| 163 | if (ACPI_FAILURE(status))
|
| 164 | return false;
|
| 165 |
|
| 166 | status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
|
| 167 | ACPI_TYPE_PACKAGE);
|
| 168 | if (ACPI_FAILURE(status))
|
| 169 | return false;
|
| 170 |
|
| 171 | if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
|
| 172 | parent))
|
| 173 | return true;
|
| 174 |
|
| 175 | ACPI_FREE(buf.pointer);
|
| 176 | return false;
|
| 177 | }
|
| 178 |
|
| 179 | static bool acpi_add_nondev_subnodes(acpi_handle scope,
|
| 180 | union acpi_object *links,
|
| 181 | struct list_head *list,
|
| 182 | struct fwnode_handle *parent)
|
| 183 | {
|
| 184 | bool ret = false;
|
| 185 | int i;
|
| 186 |
|
| 187 | |
| 188 | * Every element in the links package is expected to represent a link
|
| 189 | * to a non-device node in a tree containing device-specific data.
|
| 190 |
|
| 191 | for (i = 0; i < links->package.count; i++) {
|
| 192 | union acpi_object *link, *desc;
|
| 193 | bool result;
|
| 194 |
|
| 195 | link = &links->package.elements[i];
|
| 196 |
|
| 197 | if (link->package.count != 2)
|
| 198 | continue;
|
| 199 |
|
| 200 |
|
| 201 | if (link->package.elements[0].type != ACPI_TYPE_STRING)
|
| 202 | continue;
|
| 203 |
|
| 204 |
|
| 205 | switch (link->package.elements[1].type) {
|
| 206 | case ACPI_TYPE_STRING:
|
| 207 | |
| 208 | * The string is expected to be a full pathname or a
|
| 209 | * pathname segment relative to the given scope. That
|
| 210 | * pathname is expected to point to an object returning
|
| 211 | * a package that contains _DSD-equivalent information.
|
| 212 |
|
| 213 | result = acpi_nondev_subnode_ok(scope, link, list,
|
| 214 | parent);
|
| 215 | break;
|
| 216 | case ACPI_TYPE_PACKAGE:
|
| 217 | |
| 218 | * This happens when a reference is used in AML to
|
| 219 | * point to the target. Since the target is expected
|
| 220 | * to be a named object, a reference to it will cause it
|
| 221 | * to be avaluated in place and its return package will
|
| 222 | * be embedded in the links package at the location of
|
| 223 | * the reference.
|
| 224 | *
|
| 225 | * The target package is expected to contain _DSD-
|
| 226 | * equivalent information, but the scope in which it
|
| 227 | * is located in the original AML is unknown. Thus
|
| 228 | * it cannot contain pathname segments represented as
|
| 229 | * strings because there is no way to build full
|
| 230 | * pathnames out of them.
|
| 231 |
|
| 232 | acpi_handle_debug(scope, "subnode %s: Unknown scope\n",
|
| 233 | link->package.elements[0].string.pointer);
|
| 234 | desc = &link->package.elements[1];
|
| 235 | result = acpi_nondev_subnode_extract(desc, NULL, link,
|
| 236 | list, parent);
|
| 237 | break;
|
| 238 | case ACPI_TYPE_LOCAL_REFERENCE:
|
| 239 | |
| 240 | * It is not expected to see any local references in
|
| 241 | * the links package because referencing a named object
|
| 242 | * should cause it to be evaluated in place.
|
| 243 |
|
| 244 | acpi_handle_info(scope, "subnode %s: Unexpected reference\n",
|
| 245 | link->package.elements[0].string.pointer);
|
| 246 | fallthrough;
|
| 247 | default:
|
| 248 | result = false;
|
| 249 | break;
|
| 250 | }
|
| 251 | ret = ret || result;
|
| 252 | }
|
| 253 |
|
| 254 | return ret;
|
| 255 | }
|
| 256 |
|
| 257 | static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
|
| 258 | union acpi_object *desc,
|
| 259 | struct acpi_device_data *data,
|
| 260 | struct fwnode_handle *parent)
|
| 261 | {
|
| 262 | int i;
|
| 263 |
|
| 264 |
|
| 265 | for (i = 0; i < desc->package.count; i += 2) {
|
| 266 | const union acpi_object *guid;
|
| 267 | union acpi_object *links;
|
| 268 |
|
| 269 | guid = &desc->package.elements[i];
|
| 270 | links = &desc->package.elements[i + 1];
|
| 271 |
|
| 272 | |
| 273 | * The first element must be a GUID and the second one must be
|
| 274 | * a package.
|
| 275 |
|
| 276 | if (guid->type != ACPI_TYPE_BUFFER ||
|
| 277 | guid->buffer.length != 16 ||
|
| 278 | links->type != ACPI_TYPE_PACKAGE)
|
| 279 | break;
|
| 280 |
|
| 281 | if (!guid_equal((guid_t *)guid->buffer.pointer, &ads_guid))
|
| 282 | continue;
|
| 283 |
|
| 284 | return acpi_add_nondev_subnodes(scope, links, &data->subnodes,
|
| 285 | parent);
|
| 286 | }
|
| 287 |
|
| 288 | return false;
|
| 289 | }
|
| 290 |
|
| 291 | static bool acpi_property_value_ok(const union acpi_object *value)
|
| 292 | {
|
| 293 | int j;
|
| 294 |
|
| 295 | |
| 296 | * The value must be an integer, a string, a reference, or a package
|
| 297 | * whose every element must be an integer, a string, or a reference.
|
| 298 |
|
| 299 | switch (value->type) {
|
| 300 | case ACPI_TYPE_INTEGER:
|
| 301 | case ACPI_TYPE_STRING:
|
| 302 | case ACPI_TYPE_LOCAL_REFERENCE:
|
| 303 | return true;
|
| 304 |
|
| 305 | case ACPI_TYPE_PACKAGE:
|
| 306 | for (j = 0; j < value->package.count; j++)
|
| 307 | switch (value->package.elements[j].type) {
|
| 308 | case ACPI_TYPE_INTEGER:
|
| 309 | case ACPI_TYPE_STRING:
|
| 310 | case ACPI_TYPE_LOCAL_REFERENCE:
|
| 311 | continue;
|
| 312 |
|
| 313 | default:
|
| 314 | return false;
|
| 315 | }
|
| 316 |
|
| 317 | return true;
|
| 318 | }
|
| 319 | return false;
|
| 320 | }
|
| 321 |
|
| 322 | static bool acpi_properties_format_valid(const union acpi_object *properties)
|
| 323 | {
|
| 324 | int i;
|
| 325 |
|
| 326 | for (i = 0; i < properties->package.count; i++) {
|
| 327 | const union acpi_object *property;
|
| 328 |
|
| 329 | property = &properties->package.elements[i];
|
| 330 | |
| 331 | * Only two elements allowed, the first one must be a string and
|
| 332 | * the second one has to satisfy certain conditions.
|
| 333 |
|
| 334 | if (property->package.count != 2
|
| 335 | || property->package.elements[0].type != ACPI_TYPE_STRING
|
| 336 | || !acpi_property_value_ok(&property->package.elements[1]))
|
| 337 | return false;
|
| 338 | }
|
| 339 | return true;
|
| 340 | }
|
| 341 |
|
| 342 | static void acpi_init_of_compatible(struct acpi_device *adev)
|
| 343 | {
|
| 344 | const union acpi_object *of_compatible;
|
| 345 | int ret;
|
| 346 |
|
| 347 | ret = acpi_data_get_property_array(&adev->data, "compatible",
|
| 348 | ACPI_TYPE_STRING, &of_compatible);
|
| 349 | if (ret) {
|
| 350 | ret = acpi_dev_get_property(adev, "compatible",
|
| 351 | ACPI_TYPE_STRING, &of_compatible);
|
| 352 | if (ret) {
|
| 353 | struct acpi_device *parent;
|
| 354 |
|
| 355 | parent = acpi_dev_parent(adev);
|
| 356 | if (parent && parent->flags.of_compatible_ok)
|
| 357 | goto out;
|
| 358 |
|
| 359 | return;
|
| 360 | }
|
| 361 | }
|
| 362 | adev->data.of_compatible = of_compatible;
|
| 363 |
|
| 364 | out:
|
| 365 | adev->flags.of_compatible_ok = 1;
|
| 366 | }
|
| 367 |
|
| 368 | static bool acpi_is_property_guid(const guid_t *guid)
|
| 369 | {
|
| 370 | int i;
|
| 371 |
|
| 372 | for (i = 0; i < ARRAY_SIZE(prp_guids); i++) {
|
| 373 | if (guid_equal(guid, &prp_guids[i]))
|
| 374 | return true;
|
| 375 | }
|
| 376 |
|
| 377 | return false;
|
| 378 | }
|
| 379 |
|
| 380 | struct acpi_device_properties *
|
| 381 | acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid,
|
| 382 | union acpi_object *properties)
|
| 383 | {
|
| 384 | struct acpi_device_properties *props;
|
| 385 |
|
| 386 | props = kzalloc_obj(*props);
|
| 387 | if (props) {
|
| 388 | INIT_LIST_HEAD(&props->list);
|
| 389 | props->guid = guid;
|
| 390 | props->properties = properties;
|
| 391 | list_add_tail(&props->list, &data->properties);
|
| 392 | }
|
| 393 |
|
| 394 | return props;
|
| 395 | }
|
| 396 |
|
| 397 | static void acpi_nondev_subnode_tag(acpi_handle handle, void *context)
|
| 398 | {
|
| 399 | }
|
| 400 |
|
| 401 | static void acpi_untie_nondev_subnodes(struct acpi_device_data *data)
|
| 402 | {
|
| 403 | struct acpi_data_node *dn;
|
| 404 |
|
| 405 | list_for_each_entry(dn, &data->subnodes, sibling) {
|
| 406 | if (!dn->handle)
|
| 407 | continue;
|
| 408 |
|
| 409 | acpi_detach_data(dn->handle, acpi_nondev_subnode_tag);
|
| 410 |
|
| 411 | acpi_untie_nondev_subnodes(&dn->data);
|
| 412 | }
|
| 413 | }
|
| 414 |
|
| 415 | static bool acpi_tie_nondev_subnodes(struct acpi_device_data *data)
|
| 416 | {
|
| 417 | struct acpi_data_node *dn;
|
| 418 |
|
| 419 | list_for_each_entry(dn, &data->subnodes, sibling) {
|
| 420 | acpi_status status;
|
| 421 | bool ret;
|
| 422 |
|
| 423 | if (!dn->handle)
|
| 424 | continue;
|
| 425 |
|
| 426 | status = acpi_attach_data(dn->handle, acpi_nondev_subnode_tag, dn);
|
| 427 | if (ACPI_FAILURE(status) && status != AE_ALREADY_EXISTS) {
|
| 428 | acpi_handle_err(dn->handle, "Can't tag data node\n");
|
| 429 | return false;
|
| 430 | }
|
| 431 |
|
| 432 | ret = acpi_tie_nondev_subnodes(&dn->data);
|
| 433 | if (!ret)
|
| 434 | return ret;
|
| 435 | }
|
| 436 |
|
| 437 | return true;
|
| 438 | }
|
| 439 |
|
| 440 | static void acpi_data_add_buffer_props(acpi_handle handle,
|
| 441 | struct acpi_device_data *data,
|
| 442 | union acpi_object *properties)
|
| 443 | {
|
| 444 | struct acpi_device_properties *props;
|
| 445 | union acpi_object *package;
|
| 446 | size_t alloc_size;
|
| 447 | unsigned int i;
|
| 448 | u32 *count;
|
| 449 |
|
| 450 | if (check_mul_overflow((size_t)properties->package.count,
|
| 451 | sizeof(*package) + sizeof(void *),
|
| 452 | &alloc_size) ||
|
| 453 | check_add_overflow(sizeof(*props) + sizeof(*package), alloc_size,
|
| 454 | &alloc_size)) {
|
| 455 | acpi_handle_warn(handle,
|
| 456 | "can't allocate memory for %u buffer props",
|
| 457 | properties->package.count);
|
| 458 | return;
|
| 459 | }
|
| 460 |
|
| 461 | props = kvzalloc(alloc_size, GFP_KERNEL);
|
| 462 | if (!props)
|
| 463 | return;
|
| 464 |
|
| 465 | props->guid = &buffer_prop_guid;
|
| 466 | props->bufs = (void *)(props + 1);
|
| 467 | props->properties = (void *)(props->bufs + properties->package.count);
|
| 468 |
|
| 469 |
|
| 470 | package = props->properties;
|
| 471 | package->type = ACPI_TYPE_PACKAGE;
|
| 472 | package->package.elements = package + 1;
|
| 473 | count = &package->package.count;
|
| 474 | *count = 0;
|
| 475 |
|
| 476 |
|
| 477 | package++;
|
| 478 |
|
| 479 | for (i = 0; i < properties->package.count; i++) {
|
| 480 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
|
| 481 | union acpi_object *property = &properties->package.elements[i];
|
| 482 | union acpi_object *prop, *obj, *buf_obj;
|
| 483 | acpi_status status;
|
| 484 |
|
| 485 | if (property->type != ACPI_TYPE_PACKAGE ||
|
| 486 | property->package.count != 2) {
|
| 487 | acpi_handle_warn(handle,
|
| 488 | "buffer property %u has %u entries\n",
|
| 489 | i, property->package.count);
|
| 490 | continue;
|
| 491 | }
|
| 492 |
|
| 493 | prop = &property->package.elements[0];
|
| 494 | obj = &property->package.elements[1];
|
| 495 |
|
| 496 | if (prop->type != ACPI_TYPE_STRING ||
|
| 497 | obj->type != ACPI_TYPE_STRING) {
|
| 498 | acpi_handle_warn(handle,
|
| 499 | "wrong object types %u and %u\n",
|
| 500 | prop->type, obj->type);
|
| 501 | continue;
|
| 502 | }
|
| 503 |
|
| 504 | status = acpi_evaluate_object_typed(handle, obj->string.pointer,
|
| 505 | NULL, &buf,
|
| 506 | ACPI_TYPE_BUFFER);
|
| 507 | if (ACPI_FAILURE(status)) {
|
| 508 | acpi_handle_warn(handle,
|
| 509 | "can't evaluate \"%*pE\" as buffer\n",
|
| 510 | obj->string.length,
|
| 511 | obj->string.pointer);
|
| 512 | continue;
|
| 513 | }
|
| 514 |
|
| 515 | package->type = ACPI_TYPE_PACKAGE;
|
| 516 | package->package.elements = prop;
|
| 517 | package->package.count = 2;
|
| 518 |
|
| 519 | buf_obj = buf.pointer;
|
| 520 |
|
| 521 |
|
| 522 | obj->type = ACPI_TYPE_BUFFER;
|
| 523 | obj->buffer.length = buf_obj->buffer.length;
|
| 524 | obj->buffer.pointer = buf_obj->buffer.pointer;
|
| 525 |
|
| 526 | props->bufs[i] = buf.pointer;
|
| 527 | package++;
|
| 528 | (*count)++;
|
| 529 | }
|
| 530 |
|
| 531 | if (*count)
|
| 532 | list_add(&props->list, &data->properties);
|
| 533 | else
|
| 534 | kvfree(props);
|
| 535 | }
|
| 536 |
|
| 537 | static bool acpi_extract_properties(acpi_handle scope, union acpi_object *desc,
|
| 538 | struct acpi_device_data *data)
|
| 539 | {
|
| 540 | int i;
|
| 541 |
|
| 542 | if (desc->package.count % 2)
|
| 543 | return false;
|
| 544 |
|
| 545 |
|
| 546 | for (i = 0; i < desc->package.count; i += 2) {
|
| 547 | const union acpi_object *guid;
|
| 548 | union acpi_object *properties;
|
| 549 |
|
| 550 | guid = &desc->package.elements[i];
|
| 551 | properties = &desc->package.elements[i + 1];
|
| 552 |
|
| 553 | |
| 554 | * The first element must be a GUID and the second one must be
|
| 555 | * a package.
|
| 556 |
|
| 557 | if (guid->type != ACPI_TYPE_BUFFER ||
|
| 558 | guid->buffer.length != 16 ||
|
| 559 | properties->type != ACPI_TYPE_PACKAGE)
|
| 560 | break;
|
| 561 |
|
| 562 | if (guid_equal((guid_t *)guid->buffer.pointer,
|
| 563 | &buffer_prop_guid)) {
|
| 564 | acpi_data_add_buffer_props(scope, data, properties);
|
| 565 | continue;
|
| 566 | }
|
| 567 |
|
| 568 | if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer))
|
| 569 | continue;
|
| 570 |
|
| 571 | |
| 572 | * We found the matching GUID. Now validate the format of the
|
| 573 | * package immediately following it.
|
| 574 |
|
| 575 | if (!acpi_properties_format_valid(properties))
|
| 576 | continue;
|
| 577 |
|
| 578 | acpi_data_add_props(data, (const guid_t *)guid->buffer.pointer,
|
| 579 | properties);
|
| 580 | }
|
| 581 |
|
| 582 | return !list_empty(&data->properties);
|
| 583 | }
|
| 584 |
|
| 585 | void acpi_init_properties(struct acpi_device *adev)
|
| 586 | {
|
| 587 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
|
| 588 | struct acpi_hardware_id *hwid;
|
| 589 | acpi_status status;
|
| 590 | bool acpi_of = false;
|
| 591 |
|
| 592 | INIT_LIST_HEAD(&adev->data.properties);
|
| 593 | INIT_LIST_HEAD(&adev->data.subnodes);
|
| 594 |
|
| 595 | if (!adev->handle)
|
| 596 | return;
|
| 597 |
|
| 598 | |
| 599 | * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in
|
| 600 | * Device Tree compatible properties for this device.
|
| 601 |
|
| 602 | list_for_each_entry(hwid, &adev->pnp.ids, list) {
|
| 603 | if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) {
|
| 604 | acpi_of = true;
|
| 605 | break;
|
| 606 | }
|
| 607 | }
|
| 608 |
|
| 609 | status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
|
| 610 | ACPI_TYPE_PACKAGE);
|
| 611 | if (ACPI_FAILURE(status))
|
| 612 | goto out;
|
| 613 |
|
| 614 | if (acpi_extract_properties(adev->handle, buf.pointer, &adev->data)) {
|
| 615 | adev->data.pointer = buf.pointer;
|
| 616 | if (acpi_of)
|
| 617 | acpi_init_of_compatible(adev);
|
| 618 | }
|
| 619 | if (acpi_enumerate_nondev_subnodes(adev->handle, buf.pointer,
|
| 620 | &adev->data, acpi_fwnode_handle(adev)))
|
| 621 | adev->data.pointer = buf.pointer;
|
| 622 |
|
| 623 | if (!adev->data.pointer) {
|
| 624 | acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n");
|
| 625 | ACPI_FREE(buf.pointer);
|
| 626 | } else {
|
| 627 | if (!acpi_tie_nondev_subnodes(&adev->data))
|
| 628 | acpi_untie_nondev_subnodes(&adev->data);
|
| 629 | }
|
| 630 |
|
| 631 | out:
|
| 632 | if (acpi_of && !adev->flags.of_compatible_ok)
|
| 633 | acpi_handle_info(adev->handle,
|
| 634 | ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n");
|
| 635 |
|
| 636 | if (!adev->data.pointer)
|
| 637 | acpi_extract_apple_properties(adev);
|
| 638 | }
|
| 639 |
|
| 640 | static void acpi_free_device_properties(struct list_head *list)
|
| 641 | {
|
| 642 | struct acpi_device_properties *props, *tmp;
|
| 643 |
|
| 644 | list_for_each_entry_safe(props, tmp, list, list) {
|
| 645 | u32 i;
|
| 646 |
|
| 647 | list_del(&props->list);
|
| 648 |
|
| 649 | if (props->bufs)
|
| 650 | for (i = 0; i < props->properties->package.count; i++)
|
| 651 | ACPI_FREE(props->bufs[i]);
|
| 652 | kvfree(props);
|
| 653 | }
|
| 654 | }
|
| 655 |
|
| 656 | static void acpi_destroy_nondev_subnodes(struct list_head *list)
|
| 657 | {
|
| 658 | struct acpi_data_node *dn, *next;
|
| 659 |
|
| 660 | if (list_empty(list))
|
| 661 | return;
|
| 662 |
|
| 663 | list_for_each_entry_safe_reverse(dn, next, list, sibling) {
|
| 664 | acpi_destroy_nondev_subnodes(&dn->data.subnodes);
|
| 665 | wait_for_completion(&dn->kobj_done);
|
| 666 | list_del(&dn->sibling);
|
| 667 | ACPI_FREE((void *)dn->data.pointer);
|
| 668 | acpi_free_device_properties(&dn->data.properties);
|
| 669 | kfree(dn);
|
| 670 | }
|
| 671 | }
|
| 672 |
|
| 673 | void acpi_free_properties(struct acpi_device *adev)
|
| 674 | {
|
| 675 | acpi_untie_nondev_subnodes(&adev->data);
|
| 676 | acpi_destroy_nondev_subnodes(&adev->data.subnodes);
|
| 677 | ACPI_FREE((void *)adev->data.pointer);
|
| 678 | adev->data.of_compatible = NULL;
|
| 679 | adev->data.pointer = NULL;
|
| 680 | acpi_free_device_properties(&adev->data.properties);
|
| 681 | }
|
| 682 |
|
| 683 | |
| 684 | * acpi_data_get_property - return an ACPI property with given name
|
| 685 | * @data: ACPI device deta object to get the property from
|
| 686 | * @name: Name of the property
|
| 687 | * @type: Expected property type
|
| 688 | * @obj: Location to store the property value (if not %NULL)
|
| 689 | *
|
| 690 | * Look up a property with @name and store a pointer to the resulting ACPI
|
| 691 | * object at the location pointed to by @obj if found.
|
| 692 | *
|
| 693 | * Callers must not attempt to free the returned objects. These objects will be
|
| 694 | * freed by the ACPI core automatically during the removal of @data.
|
| 695 | *
|
| 696 | * Return: %0 if property with @name has been found (success),
|
| 697 | * %-EINVAL if the arguments are invalid,
|
| 698 | * %-EINVAL if the property doesn't exist,
|
| 699 | * %-EPROTO if the property value type doesn't match @type.
|
| 700 |
|
| 701 | static int acpi_data_get_property(const struct acpi_device_data *data,
|
| 702 | const char *name, acpi_object_type type,
|
| 703 | const union acpi_object **obj)
|
| 704 | {
|
| 705 | const struct acpi_device_properties *props;
|
| 706 |
|
| 707 | if (!data || !name)
|
| 708 | return -EINVAL;
|
| 709 |
|
| 710 | if (!data->pointer || list_empty(&data->properties))
|
| 711 | return -EINVAL;
|
| 712 |
|
| 713 | list_for_each_entry(props, &data->properties, list) {
|
| 714 | const union acpi_object *properties;
|
| 715 | unsigned int i;
|
| 716 |
|
| 717 | properties = props->properties;
|
| 718 | for (i = 0; i < properties->package.count; i++) {
|
| 719 | const union acpi_object *propname, *propvalue;
|
| 720 | const union acpi_object *property;
|
| 721 |
|
| 722 | property = &properties->package.elements[i];
|
| 723 |
|
| 724 | propname = &property->package.elements[0];
|
| 725 | propvalue = &property->package.elements[1];
|
| 726 |
|
| 727 | if (!strcmp(name, propname->string.pointer)) {
|
| 728 | if (type != ACPI_TYPE_ANY &&
|
| 729 | propvalue->type != type)
|
| 730 | return -EPROTO;
|
| 731 | if (obj)
|
| 732 | *obj = propvalue;
|
| 733 |
|
| 734 | return 0;
|
| 735 | }
|
| 736 | }
|
| 737 | }
|
| 738 | return -EINVAL;
|
| 739 | }
|
| 740 |
|
| 741 | |
| 742 | * acpi_dev_get_property - return an ACPI property with given name.
|
| 743 | * @adev: ACPI device to get the property from.
|
| 744 | * @name: Name of the property.
|
| 745 | * @type: Expected property type.
|
| 746 | * @obj: Location to store the property value (if not %NULL).
|
| 747 |
|
| 748 | int acpi_dev_get_property(const struct acpi_device *adev, const char *name,
|
| 749 | acpi_object_type type, const union acpi_object **obj)
|
| 750 | {
|
| 751 | return adev ? acpi_data_get_property(&adev->data, name, type, obj) : -EINVAL;
|
| 752 | }
|
| 753 | EXPORT_SYMBOL_GPL(acpi_dev_get_property);
|
| 754 |
|
| 755 | static const struct acpi_device_data *
|
| 756 | acpi_device_data_of_node(const struct fwnode_handle *fwnode)
|
| 757 | {
|
| 758 | if (is_acpi_device_node(fwnode)) {
|
| 759 | const struct acpi_device *adev = to_acpi_device_node(fwnode);
|
| 760 | return &adev->data;
|
| 761 | }
|
| 762 | if (is_acpi_data_node(fwnode)) {
|
| 763 | const struct acpi_data_node *dn = to_acpi_data_node(fwnode);
|
| 764 | return &dn->data;
|
| 765 | }
|
| 766 | return NULL;
|
| 767 | }
|
| 768 |
|
| 769 | |
| 770 | * acpi_node_prop_get - return an ACPI property with given name.
|
| 771 | * @fwnode: Firmware node to get the property from.
|
| 772 | * @propname: Name of the property.
|
| 773 | * @valptr: Location to store a pointer to the property value (if not %NULL).
|
| 774 |
|
| 775 | int acpi_node_prop_get(const struct fwnode_handle *fwnode,
|
| 776 | const char *propname, void **valptr)
|
| 777 | {
|
| 778 | return acpi_data_get_property(acpi_device_data_of_node(fwnode),
|
| 779 | propname, ACPI_TYPE_ANY,
|
| 780 | (const union acpi_object **)valptr);
|
| 781 | }
|
| 782 |
|
| 783 | |
| 784 | * acpi_data_get_property_array - return an ACPI array property with given name
|
| 785 | * @data: ACPI data object to get the property from
|
| 786 | * @name: Name of the property
|
| 787 | * @type: Expected type of array elements
|
| 788 | * @obj: Location to store a pointer to the property value (if not NULL)
|
| 789 | *
|
| 790 | * Look up an array property with @name and store a pointer to the resulting
|
| 791 | * ACPI object at the location pointed to by @obj if found.
|
| 792 | *
|
| 793 | * Callers must not attempt to free the returned objects. Those objects will be
|
| 794 | * freed by the ACPI core automatically during the removal of @data.
|
| 795 | *
|
| 796 | * Return: %0 if array property (package) with @name has been found (success),
|
| 797 | * %-EINVAL if the arguments are invalid,
|
| 798 | * %-EINVAL if the property doesn't exist,
|
| 799 | * %-EPROTO if the property is not a package or the type of its elements
|
| 800 | * doesn't match @type.
|
| 801 |
|
| 802 | static int acpi_data_get_property_array(const struct acpi_device_data *data,
|
| 803 | const char *name,
|
| 804 | acpi_object_type type,
|
| 805 | const union acpi_object **obj)
|
| 806 | {
|
| 807 | const union acpi_object *prop;
|
| 808 | int ret, i;
|
| 809 |
|
| 810 | ret = acpi_data_get_property(data, name, ACPI_TYPE_PACKAGE, &prop);
|
| 811 | if (ret)
|
| 812 | return ret;
|
| 813 |
|
| 814 | if (type != ACPI_TYPE_ANY) {
|
| 815 |
|
| 816 | for (i = 0; i < prop->package.count; i++)
|
| 817 | if (prop->package.elements[i].type != type)
|
| 818 | return -EPROTO;
|
| 819 | }
|
| 820 | if (obj)
|
| 821 | *obj = prop;
|
| 822 |
|
| 823 | return 0;
|
| 824 | }
|
| 825 |
|
| 826 | static struct fwnode_handle *
|
| 827 | acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
|
| 828 | const char *childname)
|
| 829 | {
|
| 830 | struct fwnode_handle *child;
|
| 831 |
|
| 832 | fwnode_for_each_child_node(fwnode, child) {
|
| 833 | if (is_acpi_data_node(child)) {
|
| 834 | if (acpi_data_node_match(child, childname))
|
| 835 | return child;
|
| 836 | continue;
|
| 837 | }
|
| 838 |
|
| 839 | if (!strncmp(acpi_device_bid(to_acpi_device_node(child)),
|
| 840 | childname, ACPI_NAMESEG_SIZE))
|
| 841 | return child;
|
| 842 | }
|
| 843 |
|
| 844 | return NULL;
|
| 845 | }
|
| 846 |
|
| 847 | static unsigned int acpi_fwnode_get_args_count(struct fwnode_handle *fwnode,
|
| 848 | const char *nargs_prop)
|
| 849 | {
|
| 850 | const struct acpi_device_data *data;
|
| 851 | const union acpi_object *obj;
|
| 852 | int ret;
|
| 853 |
|
| 854 | data = acpi_device_data_of_node(fwnode);
|
| 855 | if (!data)
|
| 856 | return 0;
|
| 857 |
|
| 858 | ret = acpi_data_get_property(data, nargs_prop, ACPI_TYPE_INTEGER, &obj);
|
| 859 | if (ret)
|
| 860 | return 0;
|
| 861 |
|
| 862 | return obj->integer.value;
|
| 863 | }
|
| 864 |
|
| 865 | static int acpi_get_ref_args(struct fwnode_reference_args *args,
|
| 866 | struct fwnode_handle *ref_fwnode,
|
| 867 | const char *nargs_prop,
|
| 868 | const union acpi_object **element,
|
| 869 | const union acpi_object *end, size_t num_args)
|
| 870 | {
|
| 871 | u32 nargs = 0, i;
|
| 872 |
|
| 873 | if (nargs_prop)
|
| 874 | num_args = acpi_fwnode_get_args_count(ref_fwnode, nargs_prop);
|
| 875 |
|
| 876 | |
| 877 | * Assume the following integer elements are all args. Stop counting on
|
| 878 | * the first reference (possibly represented as a string) or end of the
|
| 879 | * package arguments. In case of neither reference, nor integer, return
|
| 880 | * an error, we can't parse it.
|
| 881 |
|
| 882 | for (i = 0; (*element) + i < end && i < num_args; i++) {
|
| 883 | acpi_object_type type = (*element)[i].type;
|
| 884 |
|
| 885 | if (type == ACPI_TYPE_LOCAL_REFERENCE || type == ACPI_TYPE_STRING)
|
| 886 | break;
|
| 887 |
|
| 888 | if (type == ACPI_TYPE_INTEGER)
|
| 889 | nargs++;
|
| 890 | else
|
| 891 | return -EINVAL;
|
| 892 | }
|
| 893 |
|
| 894 | if (nargs > NR_FWNODE_REFERENCE_ARGS)
|
| 895 | return -EINVAL;
|
| 896 |
|
| 897 | if (args) {
|
| 898 | args->fwnode = ref_fwnode;
|
| 899 | args->nargs = nargs;
|
| 900 | for (i = 0; i < nargs; i++)
|
| 901 | args->args[i] = (*element)[i].integer.value;
|
| 902 | }
|
| 903 |
|
| 904 | (*element) += nargs;
|
| 905 |
|
| 906 | return 0;
|
| 907 | }
|
| 908 |
|
| 909 | static struct fwnode_handle *acpi_parse_string_ref(const struct fwnode_handle *fwnode,
|
| 910 | const char *refstring)
|
| 911 | {
|
| 912 | acpi_handle scope, handle;
|
| 913 | struct acpi_data_node *dn;
|
| 914 | struct acpi_device *device;
|
| 915 | acpi_status status;
|
| 916 |
|
| 917 | if (is_acpi_device_node(fwnode)) {
|
| 918 | scope = to_acpi_device_node(fwnode)->handle;
|
| 919 | } else if (is_acpi_data_node(fwnode)) {
|
| 920 | scope = to_acpi_data_node(fwnode)->handle;
|
| 921 | } else {
|
| 922 | pr_debug("Bad node type for node %pfw\n", fwnode);
|
| 923 | return NULL;
|
| 924 | }
|
| 925 |
|
| 926 | status = acpi_get_handle(scope, refstring, &handle);
|
| 927 | if (ACPI_FAILURE(status)) {
|
| 928 | acpi_handle_debug(scope, "Unable to get an ACPI handle for %s\n",
|
| 929 | refstring);
|
| 930 | return NULL;
|
| 931 | }
|
| 932 |
|
| 933 | device = acpi_fetch_acpi_dev(handle);
|
| 934 | if (device)
|
| 935 | return acpi_fwnode_handle(device);
|
| 936 |
|
| 937 | status = acpi_get_data_full(handle, acpi_nondev_subnode_tag,
|
| 938 | (void **)&dn, NULL);
|
| 939 | if (ACPI_FAILURE(status) || !dn) {
|
| 940 | acpi_handle_debug(handle, "Subnode not found\n");
|
| 941 | return NULL;
|
| 942 | }
|
| 943 |
|
| 944 | return &dn->fwnode;
|
| 945 | }
|
| 946 |
|
| 947 | static int acpi_fwnode_get_reference_args(const struct fwnode_handle *fwnode,
|
| 948 | const char *propname, const char *nargs_prop,
|
| 949 | unsigned int args_count, unsigned int index,
|
| 950 | struct fwnode_reference_args *args)
|
| 951 | {
|
| 952 | const union acpi_object *element, *end;
|
| 953 | const union acpi_object *obj;
|
| 954 | const struct acpi_device_data *data;
|
| 955 | struct fwnode_handle *ref_fwnode;
|
| 956 | struct acpi_device *device;
|
| 957 | int ret, idx = 0;
|
| 958 |
|
| 959 | data = acpi_device_data_of_node(fwnode);
|
| 960 | if (!data)
|
| 961 | return -ENOENT;
|
| 962 |
|
| 963 | ret = acpi_data_get_property(data, propname, ACPI_TYPE_ANY, &obj);
|
| 964 | if (ret)
|
| 965 | return ret == -EINVAL ? -ENOENT : -EINVAL;
|
| 966 |
|
| 967 | switch (obj->type) {
|
| 968 | case ACPI_TYPE_LOCAL_REFERENCE:
|
| 969 |
|
| 970 | if (index)
|
| 971 | return -ENOENT;
|
| 972 |
|
| 973 | device = acpi_fetch_acpi_dev(obj->reference.handle);
|
| 974 | if (!device)
|
| 975 | return -EINVAL;
|
| 976 |
|
| 977 | if (!args)
|
| 978 | return 0;
|
| 979 |
|
| 980 | args->fwnode = acpi_fwnode_handle(device);
|
| 981 | args->nargs = 0;
|
| 982 |
|
| 983 | return 0;
|
| 984 | case ACPI_TYPE_STRING:
|
| 985 | if (index)
|
| 986 | return -ENOENT;
|
| 987 |
|
| 988 | ref_fwnode = acpi_parse_string_ref(fwnode, obj->string.pointer);
|
| 989 | if (!ref_fwnode)
|
| 990 | return -EINVAL;
|
| 991 |
|
| 992 | args->fwnode = ref_fwnode;
|
| 993 | args->nargs = 0;
|
| 994 |
|
| 995 | return 0;
|
| 996 | case ACPI_TYPE_PACKAGE:
|
| 997 | |
| 998 | * If it is not a single reference, then it is a package of
|
| 999 | * references, followed by number of ints as follows:
|
| 1000 | *
|
| 1001 | * Package () { REF, INT, REF, INT, INT }
|
| 1002 | *
|
| 1003 | * Here, REF may be either a local reference or a string. The
|
| 1004 | * index argument is then used to determine which reference the
|
| 1005 | * caller wants (along with the arguments).
|
| 1006 |
|
| 1007 | break;
|
| 1008 | default:
|
| 1009 | return -EINVAL;
|
| 1010 | }
|
| 1011 |
|
| 1012 | if (index >= obj->package.count)
|
| 1013 | return -ENOENT;
|
| 1014 |
|
| 1015 | element = obj->package.elements;
|
| 1016 | end = element + obj->package.count;
|
| 1017 |
|
| 1018 | while (element < end) {
|
| 1019 | switch (element->type) {
|
| 1020 | case ACPI_TYPE_LOCAL_REFERENCE:
|
| 1021 | device = acpi_fetch_acpi_dev(element->reference.handle);
|
| 1022 | if (!device)
|
| 1023 | return -EINVAL;
|
| 1024 |
|
| 1025 | element++;
|
| 1026 | ret = acpi_get_ref_args(idx == index ? args : NULL,
|
| 1027 | acpi_fwnode_handle(device),
|
| 1028 | nargs_prop, &element, end,
|
| 1029 | args_count);
|
| 1030 | if (ret < 0)
|
| 1031 | return ret;
|
| 1032 |
|
| 1033 | if (idx == index)
|
| 1034 | return 0;
|
| 1035 |
|
| 1036 | break;
|
| 1037 | case ACPI_TYPE_STRING:
|
| 1038 | ref_fwnode = acpi_parse_string_ref(fwnode,
|
| 1039 | element->string.pointer);
|
| 1040 | if (!ref_fwnode)
|
| 1041 | return -EINVAL;
|
| 1042 |
|
| 1043 | element++;
|
| 1044 | ret = acpi_get_ref_args(idx == index ? args : NULL,
|
| 1045 | ref_fwnode, nargs_prop, &element, end,
|
| 1046 | args_count);
|
| 1047 | if (ret < 0)
|
| 1048 | return ret;
|
| 1049 |
|
| 1050 | if (idx == index)
|
| 1051 | return 0;
|
| 1052 |
|
| 1053 | break;
|
| 1054 | case ACPI_TYPE_INTEGER:
|
| 1055 | if (idx == index)
|
| 1056 | return -ENOENT;
|
| 1057 | element++;
|
| 1058 | break;
|
| 1059 | default:
|
| 1060 | return -EINVAL;
|
| 1061 | }
|
| 1062 |
|
| 1063 | idx++;
|
| 1064 | }
|
| 1065 |
|
| 1066 | return -ENOENT;
|
| 1067 | }
|
| 1068 |
|
| 1069 | |
| 1070 | * __acpi_node_get_property_reference - returns handle to the referenced object
|
| 1071 | * @fwnode: Firmware node to get the property from
|
| 1072 | * @propname: Name of the property
|
| 1073 | * @index: Index of the reference to return
|
| 1074 | * @num_args: Maximum number of arguments after each reference
|
| 1075 | * @args: Location to store the returned reference with optional arguments
|
| 1076 | * (may be NULL)
|
| 1077 | *
|
| 1078 | * Find property with @name, verifify that it is a package containing at least
|
| 1079 | * one object reference and if so, store the ACPI device object pointer to the
|
| 1080 | * target object in @args->adev. If the reference includes arguments, store
|
| 1081 | * them in the @args->args[] array.
|
| 1082 | *
|
| 1083 | * If there's more than one reference in the property value package, @index is
|
| 1084 | * used to select the one to return.
|
| 1085 | *
|
| 1086 | * It is possible to leave holes in the property value set like in the
|
| 1087 | * example below:
|
| 1088 | *
|
| 1089 | * Package () {
|
| 1090 | * "cs-gpios",
|
| 1091 | * Package () {
|
| 1092 | * ^GPIO, 19, 0, 0,
|
| 1093 | * ^GPIO, 20, 0, 0,
|
| 1094 | * 0,
|
| 1095 | * ^GPIO, 21, 0, 0,
|
| 1096 | * }
|
| 1097 | * }
|
| 1098 | *
|
| 1099 | * Calling this function with index %2 or index %3 return %-ENOENT. If the
|
| 1100 | * property does not contain any more values %-ENOENT is returned. The NULL
|
| 1101 | * entry must be single integer and preferably contain value %0.
|
| 1102 | *
|
| 1103 | * Return: %0 on success, negative error code on failure.
|
| 1104 |
|
| 1105 | int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
|
| 1106 | const char *propname, size_t index,
|
| 1107 | size_t num_args,
|
| 1108 | struct fwnode_reference_args *args)
|
| 1109 | {
|
| 1110 | return acpi_fwnode_get_reference_args(fwnode, propname, NULL, num_args, index, args);
|
| 1111 | }
|
| 1112 | EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
|
| 1113 |
|
| 1114 | static int acpi_data_prop_read_single(const struct acpi_device_data *data,
|
| 1115 | const char *propname,
|
| 1116 | enum dev_prop_type proptype, void *val)
|
| 1117 | {
|
| 1118 | const union acpi_object *obj;
|
| 1119 | int ret = 0;
|
| 1120 |
|
| 1121 | if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64)
|
| 1122 | ret = acpi_data_get_property(data, propname, ACPI_TYPE_INTEGER, &obj);
|
| 1123 | else if (proptype == DEV_PROP_STRING)
|
| 1124 | ret = acpi_data_get_property(data, propname, ACPI_TYPE_STRING, &obj);
|
| 1125 | if (ret)
|
| 1126 | return ret;
|
| 1127 |
|
| 1128 | switch (proptype) {
|
| 1129 | case DEV_PROP_U8:
|
| 1130 | if (obj->integer.value > U8_MAX)
|
| 1131 | return -EOVERFLOW;
|
| 1132 | if (val)
|
| 1133 | *(u8 *)val = obj->integer.value;
|
| 1134 | break;
|
| 1135 | case DEV_PROP_U16:
|
| 1136 | if (obj->integer.value > U16_MAX)
|
| 1137 | return -EOVERFLOW;
|
| 1138 | if (val)
|
| 1139 | *(u16 *)val = obj->integer.value;
|
| 1140 | break;
|
| 1141 | case DEV_PROP_U32:
|
| 1142 | if (obj->integer.value > U32_MAX)
|
| 1143 | return -EOVERFLOW;
|
| 1144 | if (val)
|
| 1145 | *(u32 *)val = obj->integer.value;
|
| 1146 | break;
|
| 1147 | case DEV_PROP_U64:
|
| 1148 | if (val)
|
| 1149 | *(u64 *)val = obj->integer.value;
|
| 1150 | break;
|
| 1151 | case DEV_PROP_STRING:
|
| 1152 | if (val)
|
| 1153 | *(char **)val = obj->string.pointer;
|
| 1154 | return 1;
|
| 1155 | default:
|
| 1156 | return -EINVAL;
|
| 1157 | }
|
| 1158 |
|
| 1159 |
|
| 1160 | return val ? 0 : 1;
|
| 1161 | }
|
| 1162 |
|
| 1163 | #define acpi_copy_property_array_uint(items, val, nval) \
|
| 1164 | ({ \
|
| 1165 | typeof(items) __items = items; \
|
| 1166 | typeof(val) __val = val; \
|
| 1167 | typeof(nval) __nval = nval; \
|
| 1168 | size_t i; \
|
| 1169 | int ret = 0; \
|
| 1170 | \
|
| 1171 | for (i = 0; i < __nval; i++) { \
|
| 1172 | if (__items->type == ACPI_TYPE_BUFFER) { \
|
| 1173 | __val[i] = __items->buffer.pointer[i]; \
|
| 1174 | continue; \
|
| 1175 | } \
|
| 1176 | if (__items[i].type != ACPI_TYPE_INTEGER) { \
|
| 1177 | ret = -EPROTO; \
|
| 1178 | break; \
|
| 1179 | } \
|
| 1180 | if (__items[i].integer.value > _Generic(__val, \
|
| 1181 | u8 *: U8_MAX, \
|
| 1182 | u16 *: U16_MAX, \
|
| 1183 | u32 *: U32_MAX, \
|
| 1184 | u64 *: U64_MAX)) { \
|
| 1185 | ret = -EOVERFLOW; \
|
| 1186 | break; \
|
| 1187 | } \
|
| 1188 | \
|
| 1189 | __val[i] = __items[i].integer.value; \
|
| 1190 | } \
|
| 1191 | ret; \
|
| 1192 | })
|
| 1193 |
|
| 1194 | static int acpi_copy_property_array_string(const union acpi_object *items,
|
| 1195 | char **val, size_t nval)
|
| 1196 | {
|
| 1197 | int i;
|
| 1198 |
|
| 1199 | for (i = 0; i < nval; i++) {
|
| 1200 | if (items[i].type != ACPI_TYPE_STRING)
|
| 1201 | return -EPROTO;
|
| 1202 |
|
| 1203 | val[i] = items[i].string.pointer;
|
| 1204 | }
|
| 1205 | return nval;
|
| 1206 | }
|
| 1207 |
|
| 1208 | static int acpi_data_prop_read(const struct acpi_device_data *data,
|
| 1209 | const char *propname,
|
| 1210 | enum dev_prop_type proptype,
|
| 1211 | void *val, size_t nval)
|
| 1212 | {
|
| 1213 | const union acpi_object *obj;
|
| 1214 | const union acpi_object *items;
|
| 1215 | int ret;
|
| 1216 |
|
| 1217 | if (nval == 1 || !val) {
|
| 1218 | ret = acpi_data_prop_read_single(data, propname, proptype, val);
|
| 1219 | |
| 1220 | * The overflow error means that the property is there and it is
|
| 1221 | * single-value, but its type does not match, so return.
|
| 1222 |
|
| 1223 | if (ret >= 0 || ret == -EOVERFLOW)
|
| 1224 | return ret;
|
| 1225 |
|
| 1226 | |
| 1227 | * Reading this property as a single-value one failed, but its
|
| 1228 | * value may still be represented as one-element array, so
|
| 1229 | * continue.
|
| 1230 |
|
| 1231 | }
|
| 1232 |
|
| 1233 | ret = acpi_data_get_property_array(data, propname, ACPI_TYPE_ANY, &obj);
|
| 1234 | if (ret && proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64)
|
| 1235 | ret = acpi_data_get_property(data, propname, ACPI_TYPE_BUFFER,
|
| 1236 | &obj);
|
| 1237 | if (ret)
|
| 1238 | return ret;
|
| 1239 |
|
| 1240 | if (!val) {
|
| 1241 | if (obj->type == ACPI_TYPE_BUFFER)
|
| 1242 | return obj->buffer.length;
|
| 1243 |
|
| 1244 | return obj->package.count;
|
| 1245 | }
|
| 1246 |
|
| 1247 | switch (proptype) {
|
| 1248 | case DEV_PROP_STRING:
|
| 1249 | break;
|
| 1250 | default:
|
| 1251 | if (obj->type == ACPI_TYPE_BUFFER) {
|
| 1252 | if (nval > obj->buffer.length)
|
| 1253 | return -EOVERFLOW;
|
| 1254 | } else {
|
| 1255 | if (nval > obj->package.count)
|
| 1256 | return -EOVERFLOW;
|
| 1257 | }
|
| 1258 | break;
|
| 1259 | }
|
| 1260 |
|
| 1261 | if (obj->type == ACPI_TYPE_BUFFER) {
|
| 1262 | if (proptype != DEV_PROP_U8)
|
| 1263 | return -EPROTO;
|
| 1264 | items = obj;
|
| 1265 | } else {
|
| 1266 | items = obj->package.elements;
|
| 1267 | }
|
| 1268 |
|
| 1269 | switch (proptype) {
|
| 1270 | case DEV_PROP_U8:
|
| 1271 | ret = acpi_copy_property_array_uint(items, (u8 *)val, nval);
|
| 1272 | break;
|
| 1273 | case DEV_PROP_U16:
|
| 1274 | ret = acpi_copy_property_array_uint(items, (u16 *)val, nval);
|
| 1275 | break;
|
| 1276 | case DEV_PROP_U32:
|
| 1277 | ret = acpi_copy_property_array_uint(items, (u32 *)val, nval);
|
| 1278 | break;
|
| 1279 | case DEV_PROP_U64:
|
| 1280 | ret = acpi_copy_property_array_uint(items, (u64 *)val, nval);
|
| 1281 | break;
|
| 1282 | case DEV_PROP_STRING:
|
| 1283 | nval = min(nval, obj->package.count);
|
| 1284 | if (nval == 0)
|
| 1285 | return -ENODATA;
|
| 1286 |
|
| 1287 | ret = acpi_copy_property_array_string(items, (char **)val, nval);
|
| 1288 | break;
|
| 1289 | default:
|
| 1290 | ret = -EINVAL;
|
| 1291 | break;
|
| 1292 | }
|
| 1293 | return ret;
|
| 1294 | }
|
| 1295 |
|
| 1296 | |
| 1297 | * acpi_node_prop_read - retrieve the value of an ACPI property with given name.
|
| 1298 | * @fwnode: Firmware node to get the property from.
|
| 1299 | * @propname: Name of the property.
|
| 1300 | * @proptype: Expected property type.
|
| 1301 | * @val: Location to store the property value (if not %NULL).
|
| 1302 | * @nval: Size of the array pointed to by @val.
|
| 1303 | *
|
| 1304 | * If @val is %NULL, return the number of array elements comprising the value
|
| 1305 | * of the property. Otherwise, read at most @nval values to the array at the
|
| 1306 | * location pointed to by @val.
|
| 1307 |
|
| 1308 | static int acpi_node_prop_read(const struct fwnode_handle *fwnode,
|
| 1309 | const char *propname, enum dev_prop_type proptype,
|
| 1310 | void *val, size_t nval)
|
| 1311 | {
|
| 1312 | return acpi_data_prop_read(acpi_device_data_of_node(fwnode),
|
| 1313 | propname, proptype, val, nval);
|
| 1314 | }
|
| 1315 |
|
| 1316 | static int stop_on_next(struct acpi_device *adev, void *data)
|
| 1317 | {
|
| 1318 | struct acpi_device **ret_p = data;
|
| 1319 |
|
| 1320 | if (!*ret_p) {
|
| 1321 | *ret_p = adev;
|
| 1322 | return 1;
|
| 1323 | }
|
| 1324 |
|
| 1325 |
|
| 1326 | if (*ret_p == adev)
|
| 1327 | *ret_p = NULL;
|
| 1328 |
|
| 1329 | return 0;
|
| 1330 | }
|
| 1331 |
|
| 1332 | |
| 1333 | * acpi_get_next_subnode - Return the next child node handle for a fwnode
|
| 1334 | * @fwnode: Firmware node to find the next child node for.
|
| 1335 | * @child: Handle to one of the device's child nodes or a null handle.
|
| 1336 |
|
| 1337 | static struct fwnode_handle *
|
| 1338 | acpi_get_next_subnode(const struct fwnode_handle *fwnode,
|
| 1339 | struct fwnode_handle *child)
|
| 1340 | {
|
| 1341 | struct acpi_device *adev = to_acpi_device_node(fwnode);
|
| 1342 |
|
| 1343 | if ((!child || is_acpi_device_node(child)) && adev) {
|
| 1344 | struct acpi_device *child_adev = to_acpi_device_node(child);
|
| 1345 |
|
| 1346 | acpi_dev_for_each_child(adev, stop_on_next, &child_adev);
|
| 1347 | if (child_adev)
|
| 1348 | return acpi_fwnode_handle(child_adev);
|
| 1349 |
|
| 1350 | child = NULL;
|
| 1351 | }
|
| 1352 |
|
| 1353 | if (!child || is_acpi_data_node(child)) {
|
| 1354 | const struct acpi_data_node *data = to_acpi_data_node(fwnode);
|
| 1355 | const struct list_head *head;
|
| 1356 | struct list_head *next;
|
| 1357 | struct acpi_data_node *dn;
|
| 1358 |
|
| 1359 | |
| 1360 | * We can have a combination of device and data nodes, e.g. with
|
| 1361 | * hierarchical _DSD properties. Make sure the adev pointer is
|
| 1362 | * restored before going through data nodes, otherwise we will
|
| 1363 | * be looking for data_nodes below the last device found instead
|
| 1364 | * of the common fwnode shared by device_nodes and data_nodes.
|
| 1365 |
|
| 1366 | adev = to_acpi_device_node(fwnode);
|
| 1367 | if (adev)
|
| 1368 | head = &adev->data.subnodes;
|
| 1369 | else if (data)
|
| 1370 | head = &data->data.subnodes;
|
| 1371 | else
|
| 1372 | return NULL;
|
| 1373 |
|
| 1374 | if (list_empty(head))
|
| 1375 | return NULL;
|
| 1376 |
|
| 1377 | if (child) {
|
| 1378 | dn = to_acpi_data_node(child);
|
| 1379 | next = dn->sibling.next;
|
| 1380 | if (next == head)
|
| 1381 | return NULL;
|
| 1382 |
|
| 1383 | dn = list_entry(next, struct acpi_data_node, sibling);
|
| 1384 | } else {
|
| 1385 | dn = list_first_entry(head, struct acpi_data_node, sibling);
|
| 1386 | }
|
| 1387 | return &dn->fwnode;
|
| 1388 | }
|
| 1389 | return NULL;
|
| 1390 | }
|
| 1391 |
|
| 1392 | |
| 1393 | * acpi_get_next_present_subnode - Return the next present child node handle
|
| 1394 | * @fwnode: Firmware node to find the next child node for.
|
| 1395 | * @child: Handle to one of the device's child nodes or a null handle.
|
| 1396 | *
|
| 1397 | * Like acpi_get_next_subnode(), but the device nodes returned by
|
| 1398 | * acpi_get_next_present_subnode() are guaranteed to be present.
|
| 1399 | *
|
| 1400 | * Returns: The fwnode handle of the next present sub-node.
|
| 1401 |
|
| 1402 | static struct fwnode_handle *
|
| 1403 | acpi_get_next_present_subnode(const struct fwnode_handle *fwnode,
|
| 1404 | struct fwnode_handle *child)
|
| 1405 | {
|
| 1406 | do {
|
| 1407 | child = acpi_get_next_subnode(fwnode, child);
|
| 1408 | } while (is_acpi_device_node(child) &&
|
| 1409 | !acpi_device_is_present(to_acpi_device_node(child)));
|
| 1410 |
|
| 1411 | return child;
|
| 1412 | }
|
| 1413 |
|
| 1414 | |
| 1415 | * acpi_node_get_parent - Return parent fwnode of this fwnode
|
| 1416 | * @fwnode: Firmware node whose parent to get
|
| 1417 | *
|
| 1418 | * Returns parent node of an ACPI device or data firmware node or %NULL if
|
| 1419 | * not available.
|
| 1420 |
|
| 1421 | static struct fwnode_handle *
|
| 1422 | acpi_node_get_parent(const struct fwnode_handle *fwnode)
|
| 1423 | {
|
| 1424 | if (is_acpi_data_node(fwnode)) {
|
| 1425 |
|
| 1426 | return to_acpi_data_node(fwnode)->parent;
|
| 1427 | }
|
| 1428 | if (is_acpi_device_node(fwnode)) {
|
| 1429 | struct acpi_device *parent;
|
| 1430 |
|
| 1431 | parent = acpi_dev_parent(to_acpi_device_node(fwnode));
|
| 1432 | if (parent)
|
| 1433 | return acpi_fwnode_handle(parent);
|
| 1434 | }
|
| 1435 |
|
| 1436 | return NULL;
|
| 1437 | }
|
| 1438 |
|
| 1439 | |
| 1440 | * Return true if the node is an ACPI graph node. Called on either ports
|
| 1441 | * or endpoints.
|
| 1442 |
|
| 1443 | static bool is_acpi_graph_node(struct fwnode_handle *fwnode,
|
| 1444 | const char *str)
|
| 1445 | {
|
| 1446 | unsigned int len = strlen(str);
|
| 1447 | const char *name;
|
| 1448 |
|
| 1449 | if (!len || !is_acpi_data_node(fwnode))
|
| 1450 | return false;
|
| 1451 |
|
| 1452 | name = to_acpi_data_node(fwnode)->name;
|
| 1453 |
|
| 1454 | return (fwnode_property_present(fwnode, "reg") &&
|
| 1455 | !strncmp(name, str, len) && name[len] == '@') ||
|
| 1456 | fwnode_property_present(fwnode, str);
|
| 1457 | }
|
| 1458 |
|
| 1459 | |
| 1460 | * acpi_graph_get_next_endpoint - Get next endpoint ACPI firmware node
|
| 1461 | * @fwnode: Pointer to the parent firmware node
|
| 1462 | * @prev: Previous endpoint node or %NULL to get the first
|
| 1463 | *
|
| 1464 | * Looks up next endpoint ACPI firmware node below a given @fwnode. Returns
|
| 1465 | * %NULL if there is no next endpoint or in case of error. In case of success
|
| 1466 | * the next endpoint is returned.
|
| 1467 |
|
| 1468 | static struct fwnode_handle *acpi_graph_get_next_endpoint(
|
| 1469 | const struct fwnode_handle *fwnode, struct fwnode_handle *prev)
|
| 1470 | {
|
| 1471 | struct fwnode_handle *port = NULL;
|
| 1472 | struct fwnode_handle *endpoint;
|
| 1473 |
|
| 1474 | if (!prev) {
|
| 1475 | do {
|
| 1476 | port = acpi_get_next_subnode(fwnode, port);
|
| 1477 | |
| 1478 | * The names of the port nodes begin with "port@"
|
| 1479 | * followed by the number of the port node and they also
|
| 1480 | * have a "reg" property that also has the number of the
|
| 1481 | * port node. For compatibility reasons a node is also
|
| 1482 | * recognised as a port node from the "port" property.
|
| 1483 |
|
| 1484 | if (is_acpi_graph_node(port, "port"))
|
| 1485 | break;
|
| 1486 | } while (port);
|
| 1487 | } else {
|
| 1488 | port = fwnode_get_parent(prev);
|
| 1489 | }
|
| 1490 |
|
| 1491 | if (!port)
|
| 1492 | return NULL;
|
| 1493 |
|
| 1494 | do {
|
| 1495 | endpoint = acpi_get_next_subnode(port, prev);
|
| 1496 | if (endpoint)
|
| 1497 | break;
|
| 1498 |
|
| 1499 | prev = NULL;
|
| 1500 |
|
| 1501 | do {
|
| 1502 | port = acpi_get_next_subnode(fwnode, port);
|
| 1503 | } while (port && !is_acpi_graph_node(port, "port"));
|
| 1504 | } while (port);
|
| 1505 |
|
| 1506 | |
| 1507 | * The names of the endpoint nodes begin with "endpoint@" followed by
|
| 1508 | * the number of the endpoint node and they also have a "reg" property
|
| 1509 | * that also has the number of the endpoint node. For compatibility
|
| 1510 | * reasons a node is also recognised as an endpoint node from the
|
| 1511 | * "endpoint" property.
|
| 1512 |
|
| 1513 | if (!is_acpi_graph_node(endpoint, "endpoint"))
|
| 1514 | return NULL;
|
| 1515 |
|
| 1516 | return endpoint;
|
| 1517 | }
|
| 1518 |
|
| 1519 | |
| 1520 | * acpi_graph_get_child_prop_value - Return a child with a given property value
|
| 1521 | * @fwnode: device fwnode
|
| 1522 | * @prop_name: The name of the property to look for
|
| 1523 | * @val: the desired property value
|
| 1524 | *
|
| 1525 | * Return the port node corresponding to a given port number. Returns
|
| 1526 | * the child node on success, NULL otherwise.
|
| 1527 |
|
| 1528 | static struct fwnode_handle *acpi_graph_get_child_prop_value(
|
| 1529 | const struct fwnode_handle *fwnode, const char *prop_name,
|
| 1530 | unsigned int val)
|
| 1531 | {
|
| 1532 | struct fwnode_handle *child;
|
| 1533 |
|
| 1534 | fwnode_for_each_child_node(fwnode, child) {
|
| 1535 | u32 nr;
|
| 1536 |
|
| 1537 | if (fwnode_property_read_u32(child, prop_name, &nr))
|
| 1538 | continue;
|
| 1539 |
|
| 1540 | if (val == nr)
|
| 1541 | return child;
|
| 1542 | }
|
| 1543 |
|
| 1544 | return NULL;
|
| 1545 | }
|
| 1546 |
|
| 1547 |
|
| 1548 | |
| 1549 | * acpi_graph_get_remote_endpoint - Parses and returns remote end of an endpoint
|
| 1550 | * @__fwnode: Endpoint firmware node pointing to a remote device
|
| 1551 | *
|
| 1552 | * Returns the remote endpoint corresponding to @__fwnode. NULL on error.
|
| 1553 |
|
| 1554 | static struct fwnode_handle *
|
| 1555 | acpi_graph_get_remote_endpoint(const struct fwnode_handle *__fwnode)
|
| 1556 | {
|
| 1557 | struct fwnode_handle *fwnode;
|
| 1558 | unsigned int port_nr, endpoint_nr;
|
| 1559 | struct fwnode_reference_args args;
|
| 1560 | int ret;
|
| 1561 |
|
| 1562 | memset(&args, 0, sizeof(args));
|
| 1563 | ret = acpi_node_get_property_reference(__fwnode, "remote-endpoint", 0,
|
| 1564 | &args);
|
| 1565 | if (ret)
|
| 1566 | return NULL;
|
| 1567 |
|
| 1568 |
|
| 1569 | if (!is_acpi_device_node(args.fwnode))
|
| 1570 | return args.nargs ? NULL : args.fwnode;
|
| 1571 |
|
| 1572 | |
| 1573 | * Always require two arguments with the reference: port and
|
| 1574 | * endpoint indices.
|
| 1575 |
|
| 1576 | if (args.nargs != 2)
|
| 1577 | return NULL;
|
| 1578 |
|
| 1579 | fwnode = args.fwnode;
|
| 1580 | port_nr = args.args[0];
|
| 1581 | endpoint_nr = args.args[1];
|
| 1582 |
|
| 1583 | fwnode = acpi_graph_get_child_prop_value(fwnode, "port", port_nr);
|
| 1584 |
|
| 1585 | return acpi_graph_get_child_prop_value(fwnode, "endpoint", endpoint_nr);
|
| 1586 | }
|
| 1587 |
|
| 1588 | static bool acpi_fwnode_device_is_available(const struct fwnode_handle *fwnode)
|
| 1589 | {
|
| 1590 | if (!is_acpi_device_node(fwnode))
|
| 1591 | return true;
|
| 1592 |
|
| 1593 | return acpi_device_is_present(to_acpi_device_node(fwnode));
|
| 1594 | }
|
| 1595 |
|
| 1596 | static const void *
|
| 1597 | acpi_fwnode_device_get_match_data(const struct fwnode_handle *fwnode,
|
| 1598 | const struct device *dev)
|
| 1599 | {
|
| 1600 | return acpi_device_get_match_data(dev);
|
| 1601 | }
|
| 1602 |
|
| 1603 | static bool acpi_fwnode_device_dma_supported(const struct fwnode_handle *fwnode)
|
| 1604 | {
|
| 1605 | return acpi_dma_supported(to_acpi_device_node(fwnode));
|
| 1606 | }
|
| 1607 |
|
| 1608 | static enum dev_dma_attr
|
| 1609 | acpi_fwnode_device_get_dma_attr(const struct fwnode_handle *fwnode)
|
| 1610 | {
|
| 1611 | return acpi_get_dma_attr(to_acpi_device_node(fwnode));
|
| 1612 | }
|
| 1613 |
|
| 1614 | static bool acpi_fwnode_property_present(const struct fwnode_handle *fwnode,
|
| 1615 | const char *propname)
|
| 1616 | {
|
| 1617 | return !acpi_node_prop_get(fwnode, propname, NULL);
|
| 1618 | }
|
| 1619 |
|
| 1620 | static int
|
| 1621 | acpi_fwnode_property_read_int_array(const struct fwnode_handle *fwnode,
|
| 1622 | const char *propname,
|
| 1623 | unsigned int elem_size, void *val,
|
| 1624 | size_t nval)
|
| 1625 | {
|
| 1626 | enum dev_prop_type type;
|
| 1627 |
|
| 1628 | switch (elem_size) {
|
| 1629 | case sizeof(u8):
|
| 1630 | type = DEV_PROP_U8;
|
| 1631 | break;
|
| 1632 | case sizeof(u16):
|
| 1633 | type = DEV_PROP_U16;
|
| 1634 | break;
|
| 1635 | case sizeof(u32):
|
| 1636 | type = DEV_PROP_U32;
|
| 1637 | break;
|
| 1638 | case sizeof(u64):
|
| 1639 | type = DEV_PROP_U64;
|
| 1640 | break;
|
| 1641 | default:
|
| 1642 | return -ENXIO;
|
| 1643 | }
|
| 1644 |
|
| 1645 | return acpi_node_prop_read(fwnode, propname, type, val, nval);
|
| 1646 | }
|
| 1647 |
|
| 1648 | static int
|
| 1649 | acpi_fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
|
| 1650 | const char *propname, const char **val,
|
| 1651 | size_t nval)
|
| 1652 | {
|
| 1653 | return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
|
| 1654 | val, nval);
|
| 1655 | }
|
| 1656 |
|
| 1657 | static const char *acpi_fwnode_get_name(const struct fwnode_handle *fwnode)
|
| 1658 | {
|
| 1659 | const struct acpi_device *adev;
|
| 1660 | struct fwnode_handle *parent;
|
| 1661 |
|
| 1662 |
|
| 1663 | parent = fwnode_get_parent(fwnode);
|
| 1664 | if (!parent)
|
| 1665 | return "\\";
|
| 1666 |
|
| 1667 | fwnode_handle_put(parent);
|
| 1668 |
|
| 1669 | if (is_acpi_data_node(fwnode)) {
|
| 1670 | const struct acpi_data_node *dn = to_acpi_data_node(fwnode);
|
| 1671 |
|
| 1672 | return dn->name;
|
| 1673 | }
|
| 1674 |
|
| 1675 | adev = to_acpi_device_node(fwnode);
|
| 1676 | if (WARN_ON(!adev))
|
| 1677 | return NULL;
|
| 1678 |
|
| 1679 | return acpi_device_bid(adev);
|
| 1680 | }
|
| 1681 |
|
| 1682 | static const char *
|
| 1683 | acpi_fwnode_get_name_prefix(const struct fwnode_handle *fwnode)
|
| 1684 | {
|
| 1685 | struct fwnode_handle *parent;
|
| 1686 |
|
| 1687 |
|
| 1688 | parent = fwnode_get_parent(fwnode);
|
| 1689 | if (!parent)
|
| 1690 | return "";
|
| 1691 |
|
| 1692 |
|
| 1693 | parent = fwnode_get_next_parent(parent);
|
| 1694 | if (!parent)
|
| 1695 | return "";
|
| 1696 |
|
| 1697 | fwnode_handle_put(parent);
|
| 1698 |
|
| 1699 |
|
| 1700 | return ".";
|
| 1701 | }
|
| 1702 |
|
| 1703 | static struct fwnode_handle *
|
| 1704 | acpi_fwnode_get_parent(struct fwnode_handle *fwnode)
|
| 1705 | {
|
| 1706 | return acpi_node_get_parent(fwnode);
|
| 1707 | }
|
| 1708 |
|
| 1709 | static int acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
|
| 1710 | struct fwnode_endpoint *endpoint)
|
| 1711 | {
|
| 1712 | struct fwnode_handle *port_fwnode = fwnode_get_parent(fwnode);
|
| 1713 |
|
| 1714 | endpoint->local_fwnode = fwnode;
|
| 1715 |
|
| 1716 | if (fwnode_property_read_u32(port_fwnode, "reg", &endpoint->port))
|
| 1717 | fwnode_property_read_u32(port_fwnode, "port", &endpoint->port);
|
| 1718 | if (fwnode_property_read_u32(fwnode, "reg", &endpoint->id))
|
| 1719 | fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id);
|
| 1720 |
|
| 1721 | fwnode_handle_put(port_fwnode);
|
| 1722 | return 0;
|
| 1723 | }
|
| 1724 |
|
| 1725 | static int acpi_fwnode_irq_get(const struct fwnode_handle *fwnode,
|
| 1726 | unsigned int index)
|
| 1727 | {
|
| 1728 | struct resource res;
|
| 1729 | int ret;
|
| 1730 |
|
| 1731 | ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), index, &res);
|
| 1732 | if (ret)
|
| 1733 | return ret;
|
| 1734 |
|
| 1735 | return res.start;
|
| 1736 | }
|
| 1737 |
|
| 1738 | #define DECLARE_ACPI_FWNODE_OPS(ops) \
|
| 1739 | const struct fwnode_operations ops = { \
|
| 1740 | .device_is_available = acpi_fwnode_device_is_available, \
|
| 1741 | .device_get_match_data = acpi_fwnode_device_get_match_data, \
|
| 1742 | .device_dma_supported = \
|
| 1743 | acpi_fwnode_device_dma_supported, \
|
| 1744 | .device_get_dma_attr = acpi_fwnode_device_get_dma_attr, \
|
| 1745 | .property_present = acpi_fwnode_property_present, \
|
| 1746 | .property_read_bool = acpi_fwnode_property_present, \
|
| 1747 | .property_read_int_array = \
|
| 1748 | acpi_fwnode_property_read_int_array, \
|
| 1749 | .property_read_string_array = \
|
| 1750 | acpi_fwnode_property_read_string_array, \
|
| 1751 | .get_parent = acpi_node_get_parent, \
|
| 1752 | .get_next_child_node = acpi_get_next_present_subnode, \
|
| 1753 | .get_named_child_node = acpi_fwnode_get_named_child_node, \
|
| 1754 | .get_name = acpi_fwnode_get_name, \
|
| 1755 | .get_name_prefix = acpi_fwnode_get_name_prefix, \
|
| 1756 | .get_reference_args = acpi_fwnode_get_reference_args, \
|
| 1757 | .graph_get_next_endpoint = \
|
| 1758 | acpi_graph_get_next_endpoint, \
|
| 1759 | .graph_get_remote_endpoint = \
|
| 1760 | acpi_graph_get_remote_endpoint, \
|
| 1761 | .graph_get_port_parent = acpi_fwnode_get_parent, \
|
| 1762 | .graph_parse_endpoint = acpi_fwnode_graph_parse_endpoint, \
|
| 1763 | .irq_get = acpi_fwnode_irq_get, \
|
| 1764 | }; \
|
| 1765 | EXPORT_SYMBOL_GPL(ops)
|
| 1766 |
|
| 1767 | DECLARE_ACPI_FWNODE_OPS(acpi_device_fwnode_ops);
|
| 1768 | DECLARE_ACPI_FWNODE_OPS(acpi_data_fwnode_ops);
|
| 1769 | const struct fwnode_operations acpi_static_fwnode_ops;
|
| 1770 |
|
| 1771 | bool is_acpi_device_node(const struct fwnode_handle *fwnode)
|
| 1772 | {
|
| 1773 | return !IS_ERR_OR_NULL(fwnode) &&
|
| 1774 | fwnode->ops == &acpi_device_fwnode_ops;
|
| 1775 | }
|
| 1776 | EXPORT_SYMBOL(is_acpi_device_node);
|
| 1777 |
|
| 1778 | bool is_acpi_data_node(const struct fwnode_handle *fwnode)
|
| 1779 | {
|
| 1780 | return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &acpi_data_fwnode_ops;
|
| 1781 | }
|
| 1782 | EXPORT_SYMBOL(is_acpi_data_node);
|