aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-08-26 22:36:14 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-09-14 19:47:34 -0400
commit445b0eb058f5f31c844a731cb82e7441d0d9e578 (patch)
treece7bc79930dbd23127e0126420fc5abf0fa3f3df
parentbd8191cc8a74018e255eb3efff5e02dc305a5ed1 (diff)
ACPI / property: Add support for data-only subnodes
In some cases, the information expressed via device properties is hierarchical by nature. For example, the properties of a composite device consisting of multiple semi-dependent components may need to be represented in the form of a tree of property data sets corresponding to specific components of the device. Unfortunately, using ACPI device objects for this purpose turns out to be problematic, mostly due to the assumption made by some operating systems (that platform firmware generally needs to work with) that each device object in the ACPI namespace represents a device requiring a separate driver. That assumption leads to complications which reportedly are impractically difficult to overcome and a different approach is needed for the sake of interoperability. The approach implemented here is based on extending _DSD via pointers (links) to additional ACPI objects returning data packages formatted in accordance with the _DSD formatting rules defined by Section 6.2.5 of ACPI 6. Those additional objects are referred to as data-only subnodes of the device object containing the _DSD pointing to them. The links to them need to be located in a separate section of the _DSD data package following UUID dbb8e3e6-5886-4ba6-8795-1319f52a966b referred to as the Hierarchical Data Extension UUID as defined in [1]. Each of them is represented by a package of two strings. The first string in that package (the key) is regarded as the name of the data-only subnode pointed to by the link. The second string in it (the target) is expected to hold the ACPI namespace path (possibly utilizing the usual ACPI namespace search rules) of an ACPI object evaluating to a data package extending the _DSD. The device properties initialization code follows those links, creates a struct acpi_data_node object for each of them to store the data returned by the ACPI object pointed to by it and processes those data recursively (which may lead to the creation of more struct acpi_data_node objects if the returned data package contains the Hierarchical Data Extension UUID section with more links in it). All of the struct acpi_data_node objects are present until the the ACPI device object containing the _DSD with links to them is deleted and they are deleted along with that object. [1]: http://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.pdf Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-rw-r--r--drivers/acpi/property.c133
-rw-r--r--include/acpi/acpi_bus.h9
-rw-r--r--include/linux/fwnode.h1
3 files changed, 142 insertions, 1 deletions
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 8163b4bd7a61..17c436de376b 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -24,6 +24,115 @@ static const u8 prp_uuid[16] = {
24 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d, 24 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
25 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01 25 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01
26}; 26};
27/* ACPI _DSD data subnodes UUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
28static const u8 ads_uuid[16] = {
29 0xe6, 0xe3, 0xb8, 0xdb, 0x86, 0x58, 0xa6, 0x4b,
30 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b
31};
32
33static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
34 const union acpi_object *desc,
35 struct acpi_device_data *data);
36static bool acpi_extract_properties(const union acpi_object *desc,
37 struct acpi_device_data *data);
38
39static bool acpi_nondev_subnode_ok(acpi_handle scope,
40 const union acpi_object *link,
41 struct list_head *list)
42{
43 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
44 struct acpi_data_node *dn;
45 acpi_handle handle;
46 acpi_status status;
47
48 dn = kzalloc(sizeof(*dn), GFP_KERNEL);
49 if (!dn)
50 return false;
51
52 dn->name = link->package.elements[0].string.pointer;
53 dn->fwnode.type = FWNODE_ACPI_DATA;
54 INIT_LIST_HEAD(&dn->data.subnodes);
55
56 status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
57 &handle);
58 if (ACPI_FAILURE(status))
59 goto fail;
60
61 status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
62 ACPI_TYPE_PACKAGE);
63 if (ACPI_FAILURE(status))
64 goto fail;
65
66 if (acpi_extract_properties(buf.pointer, &dn->data))
67 dn->data.pointer = buf.pointer;
68
69 if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data))
70 dn->data.pointer = buf.pointer;
71
72 if (dn->data.pointer) {
73 list_add_tail(&dn->sibling, list);
74 return true;
75 }
76
77 acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
78
79 fail:
80 ACPI_FREE(buf.pointer);
81 kfree(dn);
82 return false;
83}
84
85static int acpi_add_nondev_subnodes(acpi_handle scope,
86 const union acpi_object *links,
87 struct list_head *list)
88{
89 bool ret = false;
90 int i;
91
92 for (i = 0; i < links->package.count; i++) {
93 const union acpi_object *link;
94
95 link = &links->package.elements[i];
96 /* Only two elements allowed, both must be strings. */
97 if (link->package.count == 2
98 && link->package.elements[0].type == ACPI_TYPE_STRING
99 && link->package.elements[1].type == ACPI_TYPE_STRING
100 && acpi_nondev_subnode_ok(scope, link, list))
101 ret = true;
102 }
103
104 return ret;
105}
106
107static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
108 const union acpi_object *desc,
109 struct acpi_device_data *data)
110{
111 int i;
112
113 /* Look for the ACPI data subnodes UUID. */
114 for (i = 0; i < desc->package.count; i += 2) {
115 const union acpi_object *uuid, *links;
116
117 uuid = &desc->package.elements[i];
118 links = &desc->package.elements[i + 1];
119
120 /*
121 * The first element must be a UUID and the second one must be
122 * a package.
123 */
124 if (uuid->type != ACPI_TYPE_BUFFER || uuid->buffer.length != 16
125 || links->type != ACPI_TYPE_PACKAGE)
126 break;
127
128 if (memcmp(uuid->buffer.pointer, ads_uuid, sizeof(ads_uuid)))
129 continue;
130
131 return acpi_add_nondev_subnodes(scope, links, &data->subnodes);
132 }
133
134 return false;
135}
27 136
28static bool acpi_property_value_ok(const union acpi_object *value) 137static bool acpi_property_value_ok(const union acpi_object *value)
29{ 138{
@@ -147,6 +256,8 @@ void acpi_init_properties(struct acpi_device *adev)
147 acpi_status status; 256 acpi_status status;
148 bool acpi_of = false; 257 bool acpi_of = false;
149 258
259 INIT_LIST_HEAD(&adev->data.subnodes);
260
150 /* 261 /*
151 * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in 262 * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in
152 * Device Tree compatible properties for this device. 263 * Device Tree compatible properties for this device.
@@ -167,7 +278,11 @@ void acpi_init_properties(struct acpi_device *adev)
167 adev->data.pointer = buf.pointer; 278 adev->data.pointer = buf.pointer;
168 if (acpi_of) 279 if (acpi_of)
169 acpi_init_of_compatible(adev); 280 acpi_init_of_compatible(adev);
170 } else { 281 }
282 if (acpi_enumerate_nondev_subnodes(adev->handle, buf.pointer, &adev->data))
283 adev->data.pointer = buf.pointer;
284
285 if (!adev->data.pointer) {
171 acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n"); 286 acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n");
172 ACPI_FREE(buf.pointer); 287 ACPI_FREE(buf.pointer);
173 } 288 }
@@ -178,8 +293,24 @@ void acpi_init_properties(struct acpi_device *adev)
178 ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n"); 293 ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n");
179} 294}
180 295
296static void acpi_destroy_nondev_subnodes(struct list_head *list)
297{
298 struct acpi_data_node *dn, *next;
299
300 if (list_empty(list))
301 return;
302
303 list_for_each_entry_safe_reverse(dn, next, list, sibling) {
304 acpi_destroy_nondev_subnodes(&dn->data.subnodes);
305 list_del(&dn->sibling);
306 ACPI_FREE((void *)dn->data.pointer);
307 kfree(dn);
308 }
309}
310
181void acpi_free_properties(struct acpi_device *adev) 311void acpi_free_properties(struct acpi_device *adev)
182{ 312{
313 acpi_destroy_nondev_subnodes(&adev->data.subnodes);
183 ACPI_FREE((void *)adev->data.pointer); 314 ACPI_FREE((void *)adev->data.pointer);
184 adev->data.of_compatible = NULL; 315 adev->data.of_compatible = NULL;
185 adev->data.pointer = NULL; 316 adev->data.pointer = NULL;
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 5ba8fb64f664..79cfee646d6b 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -343,6 +343,7 @@ struct acpi_device_data {
343 const union acpi_object *pointer; 343 const union acpi_object *pointer;
344 const union acpi_object *properties; 344 const union acpi_object *properties;
345 const union acpi_object *of_compatible; 345 const union acpi_object *of_compatible;
346 struct list_head subnodes;
346}; 347};
347 348
348struct acpi_gpio_mapping; 349struct acpi_gpio_mapping;
@@ -378,6 +379,14 @@ struct acpi_device {
378 void (*remove)(struct acpi_device *); 379 void (*remove)(struct acpi_device *);
379}; 380};
380 381
382/* Non-device subnode */
383struct acpi_data_node {
384 const char *name;
385 struct fwnode_handle fwnode;
386 struct acpi_device_data data;
387 struct list_head sibling;
388};
389
381static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) 390static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent)
382{ 391{
383 bool ret = false; 392 bool ret = false;
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 0408545bce42..b08d6ba5c1e6 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -16,6 +16,7 @@ enum fwnode_type {
16 FWNODE_INVALID = 0, 16 FWNODE_INVALID = 0,
17 FWNODE_OF, 17 FWNODE_OF,
18 FWNODE_ACPI, 18 FWNODE_ACPI,
19 FWNODE_ACPI_DATA,
19 FWNODE_PDATA, 20 FWNODE_PDATA,
20}; 21};
21 22