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:
commit
80f1b3dea9
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue