Merge branch 'device-properties'

* device-properties:
  ACPI / property: Document usage rules for _DSD properties
  ACPI / property: Hierarchical properties support update
This commit is contained in:
Rafael J. Wysocki 2016-12-12 20:48:18 +01:00
commit 80f1b3dea9
4 changed files with 202 additions and 43 deletions

View File

@ -0,0 +1,97 @@
_DSD Device Properties Usage Rules
----------------------------------
Properties, Property Sets and Property Subsets
----------------------------------------------
The _DSD (Device Specific Data) configuration object, introduced in ACPI 5.1,
allows any type of device configuration data to be provided via the ACPI
namespace. In principle, the format of the data may be arbitrary, but it has to
be identified by a UUID which must be recognized by the driver processing the
_DSD output. However, there are generic UUIDs defined for _DSD recognized by
the ACPI subsystem in the Linux kernel which automatically processes the data
packages associated with them and makes those data available to device drivers
as "device properties".
A device property is a data item consisting of a string key and a value (of a
specific type) associated with it.
In the ACPI _DSD context it is an element of the sub-package following the
generic Device Properties UUID in the _DSD return package as specified in the
Device Properties UUID definition document [1].
It also may be regarded as the definition of a key and the associated data type
that can be returned by _DSD in the Device Properties UUID sub-package for a
given device.
A property set is a collection of properties applicable to a hardware entity
like a device. In the ACPI _DSD context it is the set of all properties that
can be returned in the Device Properties UUID sub-package for the device in
question.
Property subsets are nested collections of properties. Each of them is
associated with an additional key (name) allowing the subset to be referred
to as a whole (and to be treated as a separate entity). The canonical
representation of property subsets is via the mechanism specified in the
Hierarchical Properties Extension UUID definition document [2].
Property sets may be hierarchical. That is, a property set may contain
multiple property subsets that each may contain property subsets of its
own and so on.
General Validity Rule for Property Sets
---------------------------------------
Valid property sets must follow the guidance given by the Device Properties UUID
definition document [1].
_DSD properties are intended to be used in addition to, and not instead of, the
existing mechanisms defined by the ACPI specification. Therefore, as a rule,
they should only be used if the ACPI specification does not make direct
provisions for handling the underlying use case. It generally is invalid to
return property sets which do not follow that rule from _DSD in data packages
associated with the Device Properties UUID.
Additional Considerations
-------------------------
There are cases in which, even if the general rule given above is followed in
principle, the property set may still not be regarded as a valid one.
For example, that applies to device properties which may cause kernel code
(either a device driver or a library/subsystem) to access hardware in a way
possibly leading to a conflict with AML methods in the ACPI namespace. In
particular, that may happen if the kernel code uses device properties to
manipulate hardware normally controlled by ACPI methods related to power
management, like _PSx and _DSW (for device objects) or _ON and _OFF (for power
resource objects), or by ACPI device disabling/enabling methods, like _DIS and
_SRS.
In all cases in which kernel code may do something that will confuse AML as a
result of using device properties, the device properties in question are not
suitable for the ACPI environment and consequently they cannot belong to a valid
property set.
Property Sets and Device Tree Bindings
--------------------------------------
It often is useful to make _DSD return property sets that follow Device Tree
bindings.
In those cases, however, the above validity considerations must be taken into
account in the first place and returning invalid property sets from _DSD must be
avoided. For this reason, it may not be possible to make _DSD return a property
set following the given DT binding literally and completely. Still, for the
sake of code re-use, it may make sense to provide as much of the configuration
data as possible in the form of device properties and complement that with an
ACPI-specific mechanism suitable for the use case at hand.
In any case, property sets following DT bindings literally should not be
expected to automatically work in the ACPI environment regardless of their
contents.
References
----------
[1] http://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf
[2] http://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf

View File

@ -415,3 +415,12 @@ the "compatible" property in the _DSD or a _CID as long as one of their
ancestors provides a _DSD with a valid "compatible" property. Such device
objects are then simply regarded as additional "blocks" providing hierarchical
configuration information to the driver of the composite ancestor device.
However, PRP0001 can only be returned from either _HID or _CID of a device
object if all of the properties returned by the _DSD associated with it (either
the _DSD of the device object itself or the _DSD of its ancestor in the
"composite device" case described above) can be used in the ACPI environment.
Otherwise, the _DSD itself is regarded as invalid and therefore the "compatible"
property returned by it is meaningless.
Refer to DSD-properties-rules.txt for more information.

View File

@ -52,7 +52,7 @@ struct acpi_data_node_attr {
static ssize_t data_node_show_path(struct acpi_data_node *dn, char *buf)
{
return acpi_object_path(dn->handle, buf);
return dn->handle ? acpi_object_path(dn->handle, buf) : 0;
}
DATA_NODE_ATTR(path);
@ -105,10 +105,10 @@ static void acpi_expose_nondev_subnodes(struct kobject *kobj,
init_completion(&dn->kobj_done);
ret = kobject_init_and_add(&dn->kobj, &acpi_data_node_ktype,
kobj, "%s", dn->name);
if (ret)
acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret);
else
if (!ret)
acpi_expose_nondev_subnodes(&dn->kobj, &dn->data);
else if (dn->handle)
acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret);
}
}

View File

@ -41,14 +41,13 @@ static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
static bool acpi_extract_properties(const union acpi_object *desc,
struct acpi_device_data *data);
static bool acpi_nondev_subnode_ok(acpi_handle scope,
const union acpi_object *link,
struct list_head *list)
static bool acpi_nondev_subnode_extract(const union acpi_object *desc,
acpi_handle handle,
const union acpi_object *link,
struct list_head *list)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
struct acpi_data_node *dn;
acpi_handle handle;
acpi_status status;
bool result;
dn = kzalloc(sizeof(*dn), GFP_KERNEL);
if (!dn)
@ -58,43 +57,75 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope,
dn->fwnode.type = FWNODE_ACPI_DATA;
INIT_LIST_HEAD(&dn->data.subnodes);
status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
&handle);
if (ACPI_FAILURE(status))
goto fail;
result = acpi_extract_properties(desc, &dn->data);
status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
ACPI_TYPE_PACKAGE);
if (ACPI_FAILURE(status))
goto fail;
if (handle) {
acpi_handle scope;
acpi_status status;
if (acpi_extract_properties(buf.pointer, &dn->data))
/*
* The scope for the subnode object lookup is the one of the
* namespace node (device) containing the object that has
* returned the package. That is, it's the scope of that
* object's parent.
*/
status = acpi_get_parent(handle, &scope);
if (ACPI_SUCCESS(status)
&& acpi_enumerate_nondev_subnodes(scope, desc, &dn->data))
result = true;
} else if (acpi_enumerate_nondev_subnodes(NULL, desc, &dn->data)) {
result = true;
}
if (result) {
dn->handle = handle;
/*
* The scope for the subnode object lookup is the one of the namespace
* node (device) containing the object that has returned the package.
* That is, it's the scope of that object's parent.
*/
status = acpi_get_parent(handle, &scope);
if (ACPI_SUCCESS(status)
&& acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data))
dn->handle = handle;
if (dn->handle) {
dn->data.pointer = buf.pointer;
dn->data.pointer = desc;
list_add_tail(&dn->sibling, list);
return true;
}
acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
fail:
ACPI_FREE(buf.pointer);
kfree(dn);
acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
return false;
}
static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
const union acpi_object *link,
struct list_head *list)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
acpi_status status;
status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
ACPI_TYPE_PACKAGE);
if (ACPI_FAILURE(status))
return false;
if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list))
return true;
ACPI_FREE(buf.pointer);
return false;
}
static bool acpi_nondev_subnode_ok(acpi_handle scope,
const union acpi_object *link,
struct list_head *list)
{
acpi_handle handle;
acpi_status status;
if (!scope)
return false;
status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
&handle);
if (ACPI_FAILURE(status))
return false;
return acpi_nondev_subnode_data_ok(handle, link, list);
}
static int acpi_add_nondev_subnodes(acpi_handle scope,
const union acpi_object *links,
struct list_head *list)
@ -103,15 +134,37 @@ static int acpi_add_nondev_subnodes(acpi_handle scope,
int i;
for (i = 0; i < links->package.count; i++) {
const union acpi_object *link;
const union acpi_object *link, *desc;
acpi_handle handle;
bool result;
link = &links->package.elements[i];
/* Only two elements allowed, both must be strings. */
if (link->package.count == 2
&& link->package.elements[0].type == ACPI_TYPE_STRING
&& link->package.elements[1].type == ACPI_TYPE_STRING
&& acpi_nondev_subnode_ok(scope, link, list))
ret = true;
/* Only two elements allowed. */
if (link->package.count != 2)
continue;
/* The first one must be a string. */
if (link->package.elements[0].type != ACPI_TYPE_STRING)
continue;
/* The second one may be a string, a reference or a package. */
switch (link->package.elements[1].type) {
case ACPI_TYPE_STRING:
result = acpi_nondev_subnode_ok(scope, link, list);
break;
case ACPI_TYPE_LOCAL_REFERENCE:
handle = link->package.elements[1].reference.handle;
result = acpi_nondev_subnode_data_ok(handle, link, list);
break;
case ACPI_TYPE_PACKAGE:
desc = &link->package.elements[1];
result = acpi_nondev_subnode_extract(desc, NULL, link, list);
break;
default:
result = false;
break;
}
ret = ret || result;
}
return ret;