xref: /openbmc/linux/drivers/acpi/property.c (revision afc7dd4e)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ffdcd955SMika Westerberg /*
3ffdcd955SMika Westerberg  * ACPI device specific properties support.
4ffdcd955SMika Westerberg  *
5ffdcd955SMika Westerberg  * Copyright (C) 2014, Intel Corporation
6ffdcd955SMika Westerberg  * All rights reserved.
7ffdcd955SMika Westerberg  *
8ffdcd955SMika Westerberg  * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
9ffdcd955SMika Westerberg  *          Darren Hart <dvhart@linux.intel.com>
10ffdcd955SMika Westerberg  *          Rafael J. Wysocki <rafael.j.wysocki@intel.com>
11ffdcd955SMika Westerberg  */
12ffdcd955SMika Westerberg 
13ffdcd955SMika Westerberg #include <linux/acpi.h>
14ffdcd955SMika Westerberg #include <linux/device.h>
15ffdcd955SMika Westerberg #include <linux/export.h>
16ffdcd955SMika Westerberg 
17ffdcd955SMika Westerberg #include "internal.h"
18ffdcd955SMika Westerberg 
1999a85464SSakari Ailus static int acpi_data_get_property_array(const struct acpi_device_data *data,
203a7a2ab8SRafael J. Wysocki 					const char *name,
213a7a2ab8SRafael J. Wysocki 					acpi_object_type type,
223a7a2ab8SRafael J. Wysocki 					const union acpi_object **obj);
233a7a2ab8SRafael J. Wysocki 
24617654aaSMika Westerberg /*
25617654aaSMika Westerberg  * The GUIDs here are made equivalent to each other in order to avoid extra
26617654aaSMika Westerberg  * complexity in the properties handling code, with the caveat that the
27617654aaSMika Westerberg  * kernel will accept certain combinations of GUID and properties that are
28617654aaSMika Westerberg  * not defined without a warning. For instance if any of the properties
29617654aaSMika Westerberg  * from different GUID appear in a property list of another, it will be
30617654aaSMika Westerberg  * accepted by the kernel. Firmware validation tools should catch these.
31617654aaSMika Westerberg  */
325f5e4890SMika Westerberg static const guid_t prp_guids[] = {
333689d3d6SAndy Shevchenko 	/* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
343689d3d6SAndy Shevchenko 	GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
355f5e4890SMika Westerberg 		  0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01),
3626ad34d5SMika Westerberg 	/* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */
3726ad34d5SMika Westerberg 	GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3,
3826ad34d5SMika Westerberg 		  0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4),
39617654aaSMika Westerberg 	/* External facing port GUID: efcc06cc-73ac-4bc3-bff0-76143807c389 */
40617654aaSMika Westerberg 	GUID_INIT(0xefcc06cc, 0x73ac, 0x4bc3,
41617654aaSMika Westerberg 		  0xbf, 0xf0, 0x76, 0x14, 0x38, 0x07, 0xc3, 0x89),
42dfda2041SMika Westerberg 	/* Thunderbolt GUID for IMR_VALID: c44d002f-69f9-4e7d-a904-a7baabdf43f7 */
43dfda2041SMika Westerberg 	GUID_INIT(0xc44d002f, 0x69f9, 0x4e7d,
44dfda2041SMika Westerberg 		  0xa9, 0x04, 0xa7, 0xba, 0xab, 0xdf, 0x43, 0xf7),
45dfda2041SMika Westerberg 	/* Thunderbolt GUID for WAKE_SUPPORTED: 6c501103-c189-4296-ba72-9bf5a26ebe5d */
46dfda2041SMika Westerberg 	GUID_INIT(0x6c501103, 0xc189, 0x4296,
47dfda2041SMika Westerberg 		  0xba, 0x72, 0x9b, 0xf5, 0xa2, 0x6e, 0xbe, 0x5d),
48df4f9bc4SDavid E. Box 	/* Storage device needs D3 GUID: 5025030f-842f-4ab4-a561-99a5189762d0 */
49df4f9bc4SDavid E. Box 	GUID_INIT(0x5025030f, 0x842f, 0x4ab4,
50df4f9bc4SDavid E. Box 		  0xa5, 0x61, 0x99, 0xa5, 0x18, 0x97, 0x62, 0xd0),
515f5e4890SMika Westerberg };
525f5e4890SMika Westerberg 
535f21f305SShunyong Yang /* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
543689d3d6SAndy Shevchenko static const guid_t ads_guid =
553689d3d6SAndy Shevchenko 	GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6,
563689d3d6SAndy Shevchenko 		  0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b);
57445b0eb0SRafael J. Wysocki 
58103e10c6SSakari Ailus static const guid_t buffer_prop_guid =
59103e10c6SSakari Ailus 	GUID_INIT(0xedb12dd0, 0x363d, 0x4085,
60103e10c6SSakari Ailus 		  0xa3, 0xd2, 0x49, 0x52, 0x2c, 0xa1, 0x60, 0xc4);
61103e10c6SSakari Ailus 
62445b0eb0SRafael J. Wysocki static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
63103e10c6SSakari Ailus 					   union acpi_object *desc,
64dfa672fbSMika Westerberg 					   struct acpi_device_data *data,
65dfa672fbSMika Westerberg 					   struct fwnode_handle *parent);
66103e10c6SSakari Ailus static bool acpi_extract_properties(acpi_handle handle,
67103e10c6SSakari Ailus 				    union acpi_object *desc,
68445b0eb0SRafael J. Wysocki 				    struct acpi_device_data *data);
69445b0eb0SRafael J. Wysocki 
acpi_nondev_subnode_extract(union acpi_object * desc,acpi_handle handle,const union acpi_object * link,struct list_head * list,struct fwnode_handle * parent)70103e10c6SSakari Ailus static bool acpi_nondev_subnode_extract(union acpi_object *desc,
7199db5ff7SRafael J. Wysocki 					acpi_handle handle,
72445b0eb0SRafael J. Wysocki 					const union acpi_object *link,
73dfa672fbSMika Westerberg 					struct list_head *list,
74dfa672fbSMika Westerberg 					struct fwnode_handle *parent)
75445b0eb0SRafael J. Wysocki {
76445b0eb0SRafael J. Wysocki 	struct acpi_data_node *dn;
7799db5ff7SRafael J. Wysocki 	bool result;
78445b0eb0SRafael J. Wysocki 
79445b0eb0SRafael J. Wysocki 	dn = kzalloc(sizeof(*dn), GFP_KERNEL);
80445b0eb0SRafael J. Wysocki 	if (!dn)
81445b0eb0SRafael J. Wysocki 		return false;
82445b0eb0SRafael J. Wysocki 
83445b0eb0SRafael J. Wysocki 	dn->name = link->package.elements[0].string.pointer;
8401bb86b3SSaravana Kannan 	fwnode_init(&dn->fwnode, &acpi_data_fwnode_ops);
85dfa672fbSMika Westerberg 	dn->parent = parent;
865f5e4890SMika Westerberg 	INIT_LIST_HEAD(&dn->data.properties);
87445b0eb0SRafael J. Wysocki 	INIT_LIST_HEAD(&dn->data.subnodes);
88445b0eb0SRafael J. Wysocki 
89103e10c6SSakari Ailus 	result = acpi_extract_properties(handle, desc, &dn->data);
90445b0eb0SRafael J. Wysocki 
9199db5ff7SRafael J. Wysocki 	if (handle) {
9299db5ff7SRafael J. Wysocki 		acpi_handle scope;
9399db5ff7SRafael J. Wysocki 		acpi_status status;
94445b0eb0SRafael J. Wysocki 
95205ad97fSRafael J. Wysocki 		/*
9699db5ff7SRafael J. Wysocki 		 * The scope for the subnode object lookup is the one of the
9799db5ff7SRafael J. Wysocki 		 * namespace node (device) containing the object that has
9899db5ff7SRafael J. Wysocki 		 * returned the package.  That is, it's the scope of that
9999db5ff7SRafael J. Wysocki 		 * object's parent.
100205ad97fSRafael J. Wysocki 		 */
101205ad97fSRafael J. Wysocki 		status = acpi_get_parent(handle, &scope);
102205ad97fSRafael J. Wysocki 		if (ACPI_SUCCESS(status)
103dfa672fbSMika Westerberg 		    && acpi_enumerate_nondev_subnodes(scope, desc, &dn->data,
104dfa672fbSMika Westerberg 						      &dn->fwnode))
10599db5ff7SRafael J. Wysocki 			result = true;
106dfa672fbSMika Westerberg 	} else if (acpi_enumerate_nondev_subnodes(NULL, desc, &dn->data,
107dfa672fbSMika Westerberg 						  &dn->fwnode)) {
10899db5ff7SRafael J. Wysocki 		result = true;
10999db5ff7SRafael J. Wysocki 	}
110445b0eb0SRafael J. Wysocki 
11199db5ff7SRafael J. Wysocki 	if (result) {
11299db5ff7SRafael J. Wysocki 		dn->handle = handle;
11399db5ff7SRafael J. Wysocki 		dn->data.pointer = desc;
114445b0eb0SRafael J. Wysocki 		list_add_tail(&dn->sibling, list);
115445b0eb0SRafael J. Wysocki 		return true;
116445b0eb0SRafael J. Wysocki 	}
117445b0eb0SRafael J. Wysocki 
118445b0eb0SRafael J. Wysocki 	kfree(dn);
11999db5ff7SRafael J. Wysocki 	acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
120445b0eb0SRafael J. Wysocki 	return false;
121445b0eb0SRafael J. Wysocki }
122445b0eb0SRafael J. Wysocki 
acpi_nondev_subnode_data_ok(acpi_handle handle,const union acpi_object * link,struct list_head * list,struct fwnode_handle * parent)12399db5ff7SRafael J. Wysocki static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
12499db5ff7SRafael J. Wysocki 					const union acpi_object *link,
125dfa672fbSMika Westerberg 					struct list_head *list,
126dfa672fbSMika Westerberg 					struct fwnode_handle *parent)
12799db5ff7SRafael J. Wysocki {
12899db5ff7SRafael J. Wysocki 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
12999db5ff7SRafael J. Wysocki 	acpi_status status;
13099db5ff7SRafael J. Wysocki 
13199db5ff7SRafael J. Wysocki 	status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
13299db5ff7SRafael J. Wysocki 					    ACPI_TYPE_PACKAGE);
13399db5ff7SRafael J. Wysocki 	if (ACPI_FAILURE(status))
13499db5ff7SRafael J. Wysocki 		return false;
13599db5ff7SRafael J. Wysocki 
136dfa672fbSMika Westerberg 	if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
137dfa672fbSMika Westerberg 					parent))
13899db5ff7SRafael J. Wysocki 		return true;
13999db5ff7SRafael J. Wysocki 
14099db5ff7SRafael J. Wysocki 	ACPI_FREE(buf.pointer);
14199db5ff7SRafael J. Wysocki 	return false;
14299db5ff7SRafael J. Wysocki }
14399db5ff7SRafael J. Wysocki 
acpi_nondev_subnode_ok(acpi_handle scope,const union acpi_object * link,struct list_head * list,struct fwnode_handle * parent)14499db5ff7SRafael J. Wysocki static bool acpi_nondev_subnode_ok(acpi_handle scope,
14599db5ff7SRafael J. Wysocki 				   const union acpi_object *link,
146dfa672fbSMika Westerberg 				   struct list_head *list,
147dfa672fbSMika Westerberg 				   struct fwnode_handle *parent)
14899db5ff7SRafael J. Wysocki {
14999db5ff7SRafael J. Wysocki 	acpi_handle handle;
15099db5ff7SRafael J. Wysocki 	acpi_status status;
15199db5ff7SRafael J. Wysocki 
15299db5ff7SRafael J. Wysocki 	if (!scope)
15399db5ff7SRafael J. Wysocki 		return false;
15499db5ff7SRafael J. Wysocki 
15599db5ff7SRafael J. Wysocki 	status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
15699db5ff7SRafael J. Wysocki 				 &handle);
15799db5ff7SRafael J. Wysocki 	if (ACPI_FAILURE(status))
15899db5ff7SRafael J. Wysocki 		return false;
15999db5ff7SRafael J. Wysocki 
160dfa672fbSMika Westerberg 	return acpi_nondev_subnode_data_ok(handle, link, list, parent);
16199db5ff7SRafael J. Wysocki }
16299db5ff7SRafael J. Wysocki 
acpi_add_nondev_subnodes(acpi_handle scope,union acpi_object * links,struct list_head * list,struct fwnode_handle * parent)16385140ef2SSakari Ailus static bool acpi_add_nondev_subnodes(acpi_handle scope,
164103e10c6SSakari Ailus 				     union acpi_object *links,
165dfa672fbSMika Westerberg 				     struct list_head *list,
166dfa672fbSMika Westerberg 				     struct fwnode_handle *parent)
167445b0eb0SRafael J. Wysocki {
168445b0eb0SRafael J. Wysocki 	bool ret = false;
169445b0eb0SRafael J. Wysocki 	int i;
170445b0eb0SRafael J. Wysocki 
171445b0eb0SRafael J. Wysocki 	for (i = 0; i < links->package.count; i++) {
172103e10c6SSakari Ailus 		union acpi_object *link, *desc;
17399db5ff7SRafael J. Wysocki 		acpi_handle handle;
17499db5ff7SRafael J. Wysocki 		bool result;
175445b0eb0SRafael J. Wysocki 
176445b0eb0SRafael J. Wysocki 		link = &links->package.elements[i];
17799db5ff7SRafael J. Wysocki 		/* Only two elements allowed. */
17899db5ff7SRafael J. Wysocki 		if (link->package.count != 2)
17999db5ff7SRafael J. Wysocki 			continue;
18099db5ff7SRafael J. Wysocki 
18199db5ff7SRafael J. Wysocki 		/* The first one must be a string. */
18299db5ff7SRafael J. Wysocki 		if (link->package.elements[0].type != ACPI_TYPE_STRING)
18399db5ff7SRafael J. Wysocki 			continue;
18499db5ff7SRafael J. Wysocki 
18599db5ff7SRafael J. Wysocki 		/* The second one may be a string, a reference or a package. */
18699db5ff7SRafael J. Wysocki 		switch (link->package.elements[1].type) {
18799db5ff7SRafael J. Wysocki 		case ACPI_TYPE_STRING:
188dfa672fbSMika Westerberg 			result = acpi_nondev_subnode_ok(scope, link, list,
189dfa672fbSMika Westerberg 							 parent);
19099db5ff7SRafael J. Wysocki 			break;
19199db5ff7SRafael J. Wysocki 		case ACPI_TYPE_LOCAL_REFERENCE:
19299db5ff7SRafael J. Wysocki 			handle = link->package.elements[1].reference.handle;
193dfa672fbSMika Westerberg 			result = acpi_nondev_subnode_data_ok(handle, link, list,
194dfa672fbSMika Westerberg 							     parent);
19599db5ff7SRafael J. Wysocki 			break;
19699db5ff7SRafael J. Wysocki 		case ACPI_TYPE_PACKAGE:
19799db5ff7SRafael J. Wysocki 			desc = &link->package.elements[1];
198dfa672fbSMika Westerberg 			result = acpi_nondev_subnode_extract(desc, NULL, link,
199dfa672fbSMika Westerberg 							     list, parent);
20099db5ff7SRafael J. Wysocki 			break;
20199db5ff7SRafael J. Wysocki 		default:
20299db5ff7SRafael J. Wysocki 			result = false;
20399db5ff7SRafael J. Wysocki 			break;
20499db5ff7SRafael J. Wysocki 		}
20599db5ff7SRafael J. Wysocki 		ret = ret || result;
206445b0eb0SRafael J. Wysocki 	}
207445b0eb0SRafael J. Wysocki 
208445b0eb0SRafael J. Wysocki 	return ret;
209445b0eb0SRafael J. Wysocki }
210445b0eb0SRafael J. Wysocki 
acpi_enumerate_nondev_subnodes(acpi_handle scope,union acpi_object * desc,struct acpi_device_data * data,struct fwnode_handle * parent)211445b0eb0SRafael J. Wysocki static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
212103e10c6SSakari Ailus 					   union acpi_object *desc,
213dfa672fbSMika Westerberg 					   struct acpi_device_data *data,
214dfa672fbSMika Westerberg 					   struct fwnode_handle *parent)
215445b0eb0SRafael J. Wysocki {
216445b0eb0SRafael J. Wysocki 	int i;
217445b0eb0SRafael J. Wysocki 
2183689d3d6SAndy Shevchenko 	/* Look for the ACPI data subnodes GUID. */
219445b0eb0SRafael J. Wysocki 	for (i = 0; i < desc->package.count; i += 2) {
220103e10c6SSakari Ailus 		const union acpi_object *guid;
221103e10c6SSakari Ailus 		union acpi_object *links;
222445b0eb0SRafael J. Wysocki 
2233689d3d6SAndy Shevchenko 		guid = &desc->package.elements[i];
224445b0eb0SRafael J. Wysocki 		links = &desc->package.elements[i + 1];
225445b0eb0SRafael J. Wysocki 
226445b0eb0SRafael J. Wysocki 		/*
2273689d3d6SAndy Shevchenko 		 * The first element must be a GUID and the second one must be
228445b0eb0SRafael J. Wysocki 		 * a package.
229445b0eb0SRafael J. Wysocki 		 */
2303689d3d6SAndy Shevchenko 		if (guid->type != ACPI_TYPE_BUFFER ||
2313689d3d6SAndy Shevchenko 		    guid->buffer.length != 16 ||
2323689d3d6SAndy Shevchenko 		    links->type != ACPI_TYPE_PACKAGE)
233445b0eb0SRafael J. Wysocki 			break;
234445b0eb0SRafael J. Wysocki 
2353689d3d6SAndy Shevchenko 		if (!guid_equal((guid_t *)guid->buffer.pointer, &ads_guid))
236445b0eb0SRafael J. Wysocki 			continue;
237445b0eb0SRafael J. Wysocki 
238dfa672fbSMika Westerberg 		return acpi_add_nondev_subnodes(scope, links, &data->subnodes,
239dfa672fbSMika Westerberg 						parent);
240445b0eb0SRafael J. Wysocki 	}
241445b0eb0SRafael J. Wysocki 
242445b0eb0SRafael J. Wysocki 	return false;
243445b0eb0SRafael J. Wysocki }
244ffdcd955SMika Westerberg 
acpi_property_value_ok(const union acpi_object * value)245ffdcd955SMika Westerberg static bool acpi_property_value_ok(const union acpi_object *value)
246ffdcd955SMika Westerberg {
247ffdcd955SMika Westerberg 	int j;
248ffdcd955SMika Westerberg 
249ffdcd955SMika Westerberg 	/*
250ffdcd955SMika Westerberg 	 * The value must be an integer, a string, a reference, or a package
251ffdcd955SMika Westerberg 	 * whose every element must be an integer, a string, or a reference.
252ffdcd955SMika Westerberg 	 */
253ffdcd955SMika Westerberg 	switch (value->type) {
254ffdcd955SMika Westerberg 	case ACPI_TYPE_INTEGER:
255ffdcd955SMika Westerberg 	case ACPI_TYPE_STRING:
256ffdcd955SMika Westerberg 	case ACPI_TYPE_LOCAL_REFERENCE:
257ffdcd955SMika Westerberg 		return true;
258ffdcd955SMika Westerberg 
259ffdcd955SMika Westerberg 	case ACPI_TYPE_PACKAGE:
260ffdcd955SMika Westerberg 		for (j = 0; j < value->package.count; j++)
261ffdcd955SMika Westerberg 			switch (value->package.elements[j].type) {
262ffdcd955SMika Westerberg 			case ACPI_TYPE_INTEGER:
263ffdcd955SMika Westerberg 			case ACPI_TYPE_STRING:
264ffdcd955SMika Westerberg 			case ACPI_TYPE_LOCAL_REFERENCE:
265ffdcd955SMika Westerberg 				continue;
266ffdcd955SMika Westerberg 
267ffdcd955SMika Westerberg 			default:
268ffdcd955SMika Westerberg 				return false;
269ffdcd955SMika Westerberg 			}
270ffdcd955SMika Westerberg 
271ffdcd955SMika Westerberg 		return true;
272ffdcd955SMika Westerberg 	}
273ffdcd955SMika Westerberg 	return false;
274ffdcd955SMika Westerberg }
275ffdcd955SMika Westerberg 
acpi_properties_format_valid(const union acpi_object * properties)276ffdcd955SMika Westerberg static bool acpi_properties_format_valid(const union acpi_object *properties)
277ffdcd955SMika Westerberg {
278ffdcd955SMika Westerberg 	int i;
279ffdcd955SMika Westerberg 
280ffdcd955SMika Westerberg 	for (i = 0; i < properties->package.count; i++) {
281ffdcd955SMika Westerberg 		const union acpi_object *property;
282ffdcd955SMika Westerberg 
283ffdcd955SMika Westerberg 		property = &properties->package.elements[i];
284ffdcd955SMika Westerberg 		/*
285ffdcd955SMika Westerberg 		 * Only two elements allowed, the first one must be a string and
286ffdcd955SMika Westerberg 		 * the second one has to satisfy certain conditions.
287ffdcd955SMika Westerberg 		 */
288ffdcd955SMika Westerberg 		if (property->package.count != 2
289ffdcd955SMika Westerberg 		    || property->package.elements[0].type != ACPI_TYPE_STRING
290ffdcd955SMika Westerberg 		    || !acpi_property_value_ok(&property->package.elements[1]))
291ffdcd955SMika Westerberg 			return false;
292ffdcd955SMika Westerberg 	}
293ffdcd955SMika Westerberg 	return true;
294ffdcd955SMika Westerberg }
295ffdcd955SMika Westerberg 
acpi_init_of_compatible(struct acpi_device * adev)296733e6251SMika Westerberg static void acpi_init_of_compatible(struct acpi_device *adev)
297733e6251SMika Westerberg {
298733e6251SMika Westerberg 	const union acpi_object *of_compatible;
299733e6251SMika Westerberg 	int ret;
300733e6251SMika Westerberg 
3013a7a2ab8SRafael J. Wysocki 	ret = acpi_data_get_property_array(&adev->data, "compatible",
3023a7a2ab8SRafael J. Wysocki 					   ACPI_TYPE_STRING, &of_compatible);
3035c53b262SRafael J. Wysocki 	if (ret) {
3045c53b262SRafael J. Wysocki 		ret = acpi_dev_get_property(adev, "compatible",
3055c53b262SRafael J. Wysocki 					    ACPI_TYPE_STRING, &of_compatible);
3065c53b262SRafael J. Wysocki 		if (ret) {
30762fcb99bSRafael J. Wysocki 			struct acpi_device *parent;
30862fcb99bSRafael J. Wysocki 
30962fcb99bSRafael J. Wysocki 			parent = acpi_dev_parent(adev);
31062fcb99bSRafael J. Wysocki 			if (parent && parent->flags.of_compatible_ok)
3115c53b262SRafael J. Wysocki 				goto out;
3125c53b262SRafael J. Wysocki 
3135c53b262SRafael J. Wysocki 			return;
3145c53b262SRafael J. Wysocki 		}
3155c53b262SRafael J. Wysocki 	}
3165c53b262SRafael J. Wysocki 	adev->data.of_compatible = of_compatible;
3175c53b262SRafael J. Wysocki 
3185c53b262SRafael J. Wysocki  out:
3195c53b262SRafael J. Wysocki 	adev->flags.of_compatible_ok = 1;
3205c53b262SRafael J. Wysocki }
3215c53b262SRafael J. Wysocki 
acpi_is_property_guid(const guid_t * guid)3225f5e4890SMika Westerberg static bool acpi_is_property_guid(const guid_t *guid)
3235f5e4890SMika Westerberg {
3245f5e4890SMika Westerberg 	int i;
3255f5e4890SMika Westerberg 
3265f5e4890SMika Westerberg 	for (i = 0; i < ARRAY_SIZE(prp_guids); i++) {
3275f5e4890SMika Westerberg 		if (guid_equal(guid, &prp_guids[i]))
3285f5e4890SMika Westerberg 			return true;
3295f5e4890SMika Westerberg 	}
3305f5e4890SMika Westerberg 
3315f5e4890SMika Westerberg 	return false;
3325f5e4890SMika Westerberg }
3335f5e4890SMika Westerberg 
3345f5e4890SMika Westerberg struct acpi_device_properties *
acpi_data_add_props(struct acpi_device_data * data,const guid_t * guid,union acpi_object * properties)3355f5e4890SMika Westerberg acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid,
336103e10c6SSakari Ailus 		    union acpi_object *properties)
3375f5e4890SMika Westerberg {
3385f5e4890SMika Westerberg 	struct acpi_device_properties *props;
3395f5e4890SMika Westerberg 
3405f5e4890SMika Westerberg 	props = kzalloc(sizeof(*props), GFP_KERNEL);
3415f5e4890SMika Westerberg 	if (props) {
3425f5e4890SMika Westerberg 		INIT_LIST_HEAD(&props->list);
3435f5e4890SMika Westerberg 		props->guid = guid;
3445f5e4890SMika Westerberg 		props->properties = properties;
3455f5e4890SMika Westerberg 		list_add_tail(&props->list, &data->properties);
3465f5e4890SMika Westerberg 	}
3475f5e4890SMika Westerberg 
3485f5e4890SMika Westerberg 	return props;
3495f5e4890SMika Westerberg }
3505f5e4890SMika Westerberg 
acpi_nondev_subnode_tag(acpi_handle handle,void * context)3511d52f109SSakari Ailus static void acpi_nondev_subnode_tag(acpi_handle handle, void *context)
3521d52f109SSakari Ailus {
3531d52f109SSakari Ailus }
3541d52f109SSakari Ailus 
acpi_untie_nondev_subnodes(struct acpi_device_data * data)3551d52f109SSakari Ailus static void acpi_untie_nondev_subnodes(struct acpi_device_data *data)
3561d52f109SSakari Ailus {
3571d52f109SSakari Ailus 	struct acpi_data_node *dn;
3581d52f109SSakari Ailus 
3591d52f109SSakari Ailus 	list_for_each_entry(dn, &data->subnodes, sibling) {
3601d52f109SSakari Ailus 		acpi_detach_data(dn->handle, acpi_nondev_subnode_tag);
3611d52f109SSakari Ailus 
3621d52f109SSakari Ailus 		acpi_untie_nondev_subnodes(&dn->data);
3631d52f109SSakari Ailus 	}
3641d52f109SSakari Ailus }
3651d52f109SSakari Ailus 
acpi_tie_nondev_subnodes(struct acpi_device_data * data)3661d52f109SSakari Ailus static bool acpi_tie_nondev_subnodes(struct acpi_device_data *data)
3671d52f109SSakari Ailus {
3681d52f109SSakari Ailus 	struct acpi_data_node *dn;
3691d52f109SSakari Ailus 
3701d52f109SSakari Ailus 	list_for_each_entry(dn, &data->subnodes, sibling) {
3711d52f109SSakari Ailus 		acpi_status status;
3721d52f109SSakari Ailus 		bool ret;
3731d52f109SSakari Ailus 
3741d52f109SSakari Ailus 		status = acpi_attach_data(dn->handle, acpi_nondev_subnode_tag, dn);
3752ea3b197SSakari Ailus 		if (ACPI_FAILURE(status) && status != AE_ALREADY_EXISTS) {
3761d52f109SSakari Ailus 			acpi_handle_err(dn->handle, "Can't tag data node\n");
3771d52f109SSakari Ailus 			return false;
3781d52f109SSakari Ailus 		}
3791d52f109SSakari Ailus 
3801d52f109SSakari Ailus 		ret = acpi_tie_nondev_subnodes(&dn->data);
3811d52f109SSakari Ailus 		if (!ret)
3821d52f109SSakari Ailus 			return ret;
3831d52f109SSakari Ailus 	}
3841d52f109SSakari Ailus 
3851d52f109SSakari Ailus 	return true;
3861d52f109SSakari Ailus }
3871d52f109SSakari Ailus 
acpi_data_add_buffer_props(acpi_handle handle,struct acpi_device_data * data,union acpi_object * properties)388103e10c6SSakari Ailus static void acpi_data_add_buffer_props(acpi_handle handle,
389103e10c6SSakari Ailus 				       struct acpi_device_data *data,
390103e10c6SSakari Ailus 				       union acpi_object *properties)
391103e10c6SSakari Ailus {
392103e10c6SSakari Ailus 	struct acpi_device_properties *props;
393103e10c6SSakari Ailus 	union acpi_object *package;
394103e10c6SSakari Ailus 	size_t alloc_size;
395103e10c6SSakari Ailus 	unsigned int i;
396103e10c6SSakari Ailus 	u32 *count;
397103e10c6SSakari Ailus 
398103e10c6SSakari Ailus 	if (check_mul_overflow((size_t)properties->package.count,
399103e10c6SSakari Ailus 			       sizeof(*package) + sizeof(void *),
400103e10c6SSakari Ailus 			       &alloc_size) ||
401103e10c6SSakari Ailus 	    check_add_overflow(sizeof(*props) + sizeof(*package), alloc_size,
402103e10c6SSakari Ailus 			       &alloc_size)) {
403103e10c6SSakari Ailus 		acpi_handle_warn(handle,
404103e10c6SSakari Ailus 				 "can't allocate memory for %u buffer props",
405103e10c6SSakari Ailus 				 properties->package.count);
406103e10c6SSakari Ailus 		return;
407103e10c6SSakari Ailus 	}
408103e10c6SSakari Ailus 
409103e10c6SSakari Ailus 	props = kvzalloc(alloc_size, GFP_KERNEL);
410103e10c6SSakari Ailus 	if (!props)
411103e10c6SSakari Ailus 		return;
412103e10c6SSakari Ailus 
413103e10c6SSakari Ailus 	props->guid = &buffer_prop_guid;
414103e10c6SSakari Ailus 	props->bufs = (void *)(props + 1);
415103e10c6SSakari Ailus 	props->properties = (void *)(props->bufs + properties->package.count);
416103e10c6SSakari Ailus 
417103e10c6SSakari Ailus 	/* Outer package */
418103e10c6SSakari Ailus 	package = props->properties;
419103e10c6SSakari Ailus 	package->type = ACPI_TYPE_PACKAGE;
420103e10c6SSakari Ailus 	package->package.elements = package + 1;
421103e10c6SSakari Ailus 	count = &package->package.count;
422103e10c6SSakari Ailus 	*count = 0;
423103e10c6SSakari Ailus 
424103e10c6SSakari Ailus 	/* Inner packages */
425103e10c6SSakari Ailus 	package++;
426103e10c6SSakari Ailus 
427103e10c6SSakari Ailus 	for (i = 0; i < properties->package.count; i++) {
428103e10c6SSakari Ailus 		struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
429103e10c6SSakari Ailus 		union acpi_object *property = &properties->package.elements[i];
430103e10c6SSakari Ailus 		union acpi_object *prop, *obj, *buf_obj;
431103e10c6SSakari Ailus 		acpi_status status;
432103e10c6SSakari Ailus 
433103e10c6SSakari Ailus 		if (property->type != ACPI_TYPE_PACKAGE ||
434103e10c6SSakari Ailus 		    property->package.count != 2) {
435103e10c6SSakari Ailus 			acpi_handle_warn(handle,
436103e10c6SSakari Ailus 					 "buffer property %u has %u entries\n",
437103e10c6SSakari Ailus 					 i, property->package.count);
438103e10c6SSakari Ailus 			continue;
439103e10c6SSakari Ailus 		}
440103e10c6SSakari Ailus 
441103e10c6SSakari Ailus 		prop = &property->package.elements[0];
442103e10c6SSakari Ailus 		obj = &property->package.elements[1];
443103e10c6SSakari Ailus 
444103e10c6SSakari Ailus 		if (prop->type != ACPI_TYPE_STRING ||
445103e10c6SSakari Ailus 		    obj->type != ACPI_TYPE_STRING) {
446103e10c6SSakari Ailus 			acpi_handle_warn(handle,
447103e10c6SSakari Ailus 					 "wrong object types %u and %u\n",
448103e10c6SSakari Ailus 					 prop->type, obj->type);
449103e10c6SSakari Ailus 			continue;
450103e10c6SSakari Ailus 		}
451103e10c6SSakari Ailus 
452103e10c6SSakari Ailus 		status = acpi_evaluate_object_typed(handle, obj->string.pointer,
453103e10c6SSakari Ailus 						    NULL, &buf,
454103e10c6SSakari Ailus 						    ACPI_TYPE_BUFFER);
455103e10c6SSakari Ailus 		if (ACPI_FAILURE(status)) {
456103e10c6SSakari Ailus 			acpi_handle_warn(handle,
457103e10c6SSakari Ailus 					 "can't evaluate \"%*pE\" as buffer\n",
458103e10c6SSakari Ailus 					 obj->string.length,
459103e10c6SSakari Ailus 					 obj->string.pointer);
460103e10c6SSakari Ailus 			continue;
461103e10c6SSakari Ailus 		}
462103e10c6SSakari Ailus 
463103e10c6SSakari Ailus 		package->type = ACPI_TYPE_PACKAGE;
464103e10c6SSakari Ailus 		package->package.elements = prop;
465103e10c6SSakari Ailus 		package->package.count = 2;
466103e10c6SSakari Ailus 
467103e10c6SSakari Ailus 		buf_obj = buf.pointer;
468103e10c6SSakari Ailus 
469103e10c6SSakari Ailus 		/* Replace the string object with a buffer object */
470103e10c6SSakari Ailus 		obj->type = ACPI_TYPE_BUFFER;
471103e10c6SSakari Ailus 		obj->buffer.length = buf_obj->buffer.length;
472103e10c6SSakari Ailus 		obj->buffer.pointer = buf_obj->buffer.pointer;
473103e10c6SSakari Ailus 
474103e10c6SSakari Ailus 		props->bufs[i] = buf.pointer;
475103e10c6SSakari Ailus 		package++;
476103e10c6SSakari Ailus 		(*count)++;
477103e10c6SSakari Ailus 	}
478103e10c6SSakari Ailus 
479103e10c6SSakari Ailus 	if (*count)
480103e10c6SSakari Ailus 		list_add(&props->list, &data->properties);
481103e10c6SSakari Ailus 	else
482103e10c6SSakari Ailus 		kvfree(props);
483103e10c6SSakari Ailus }
484103e10c6SSakari Ailus 
acpi_extract_properties(acpi_handle scope,union acpi_object * desc,struct acpi_device_data * data)485103e10c6SSakari Ailus static bool acpi_extract_properties(acpi_handle scope, union acpi_object *desc,
486bd8191ccSRafael J. Wysocki 				    struct acpi_device_data *data)
4875c53b262SRafael J. Wysocki {
4885c53b262SRafael J. Wysocki 	int i;
4895c53b262SRafael J. Wysocki 
490ffdcd955SMika Westerberg 	if (desc->package.count % 2)
491bd8191ccSRafael J. Wysocki 		return false;
492ffdcd955SMika Westerberg 
4933689d3d6SAndy Shevchenko 	/* Look for the device properties GUID. */
494ffdcd955SMika Westerberg 	for (i = 0; i < desc->package.count; i += 2) {
495103e10c6SSakari Ailus 		const union acpi_object *guid;
496103e10c6SSakari Ailus 		union acpi_object *properties;
497ffdcd955SMika Westerberg 
4983689d3d6SAndy Shevchenko 		guid = &desc->package.elements[i];
499ffdcd955SMika Westerberg 		properties = &desc->package.elements[i + 1];
500ffdcd955SMika Westerberg 
501ffdcd955SMika Westerberg 		/*
5023689d3d6SAndy Shevchenko 		 * The first element must be a GUID and the second one must be
503ffdcd955SMika Westerberg 		 * a package.
504ffdcd955SMika Westerberg 		 */
5053689d3d6SAndy Shevchenko 		if (guid->type != ACPI_TYPE_BUFFER ||
5063689d3d6SAndy Shevchenko 		    guid->buffer.length != 16 ||
5073689d3d6SAndy Shevchenko 		    properties->type != ACPI_TYPE_PACKAGE)
508ffdcd955SMika Westerberg 			break;
509ffdcd955SMika Westerberg 
510103e10c6SSakari Ailus 		if (guid_equal((guid_t *)guid->buffer.pointer,
511103e10c6SSakari Ailus 			       &buffer_prop_guid)) {
512103e10c6SSakari Ailus 			acpi_data_add_buffer_props(scope, data, properties);
513103e10c6SSakari Ailus 			continue;
514103e10c6SSakari Ailus 		}
515103e10c6SSakari Ailus 
5165f5e4890SMika Westerberg 		if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer))
517ffdcd955SMika Westerberg 			continue;
518ffdcd955SMika Westerberg 
519ffdcd955SMika Westerberg 		/*
5203689d3d6SAndy Shevchenko 		 * We found the matching GUID. Now validate the format of the
521ffdcd955SMika Westerberg 		 * package immediately following it.
522ffdcd955SMika Westerberg 		 */
523ffdcd955SMika Westerberg 		if (!acpi_properties_format_valid(properties))
5245f5e4890SMika Westerberg 			continue;
525ffdcd955SMika Westerberg 
5265f5e4890SMika Westerberg 		acpi_data_add_props(data, (const guid_t *)guid->buffer.pointer,
5275f5e4890SMika Westerberg 				    properties);
528ffdcd955SMika Westerberg 	}
529ffdcd955SMika Westerberg 
5305f5e4890SMika Westerberg 	return !list_empty(&data->properties);
531bd8191ccSRafael J. Wysocki }
532bd8191ccSRafael J. Wysocki 
acpi_init_properties(struct acpi_device * adev)533bd8191ccSRafael J. Wysocki void acpi_init_properties(struct acpi_device *adev)
534bd8191ccSRafael J. Wysocki {
535bd8191ccSRafael J. Wysocki 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
536bd8191ccSRafael J. Wysocki 	struct acpi_hardware_id *hwid;
537bd8191ccSRafael J. Wysocki 	acpi_status status;
538bd8191ccSRafael J. Wysocki 	bool acpi_of = false;
539bd8191ccSRafael J. Wysocki 
5405f5e4890SMika Westerberg 	INIT_LIST_HEAD(&adev->data.properties);
541445b0eb0SRafael J. Wysocki 	INIT_LIST_HEAD(&adev->data.subnodes);
542445b0eb0SRafael J. Wysocki 
54375fc70e0SLukas Wunner 	if (!adev->handle)
54475fc70e0SLukas Wunner 		return;
54575fc70e0SLukas Wunner 
546bd8191ccSRafael J. Wysocki 	/*
547bd8191ccSRafael J. Wysocki 	 * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in
548bd8191ccSRafael J. Wysocki 	 * Device Tree compatible properties for this device.
549bd8191ccSRafael J. Wysocki 	 */
550bd8191ccSRafael J. Wysocki 	list_for_each_entry(hwid, &adev->pnp.ids, list) {
551bd8191ccSRafael J. Wysocki 		if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) {
552bd8191ccSRafael J. Wysocki 			acpi_of = true;
553bd8191ccSRafael J. Wysocki 			break;
554bd8191ccSRafael J. Wysocki 		}
555bd8191ccSRafael J. Wysocki 	}
556bd8191ccSRafael J. Wysocki 
557bd8191ccSRafael J. Wysocki 	status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
558bd8191ccSRafael J. Wysocki 					    ACPI_TYPE_PACKAGE);
559bd8191ccSRafael J. Wysocki 	if (ACPI_FAILURE(status))
560bd8191ccSRafael J. Wysocki 		goto out;
561bd8191ccSRafael J. Wysocki 
562103e10c6SSakari Ailus 	if (acpi_extract_properties(adev->handle, buf.pointer, &adev->data)) {
563bd8191ccSRafael J. Wysocki 		adev->data.pointer = buf.pointer;
564bd8191ccSRafael J. Wysocki 		if (acpi_of)
565bd8191ccSRafael J. Wysocki 			acpi_init_of_compatible(adev);
566445b0eb0SRafael J. Wysocki 	}
567dfa672fbSMika Westerberg 	if (acpi_enumerate_nondev_subnodes(adev->handle, buf.pointer,
568dfa672fbSMika Westerberg 					&adev->data, acpi_fwnode_handle(adev)))
569445b0eb0SRafael J. Wysocki 		adev->data.pointer = buf.pointer;
570445b0eb0SRafael J. Wysocki 
571445b0eb0SRafael J. Wysocki 	if (!adev->data.pointer) {
572bd8191ccSRafael J. Wysocki 		acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n");
573ffdcd955SMika Westerberg 		ACPI_FREE(buf.pointer);
57446981fa7SSakari Ailus 	} else {
57546981fa7SSakari Ailus 		if (!acpi_tie_nondev_subnodes(&adev->data))
57646981fa7SSakari Ailus 			acpi_untie_nondev_subnodes(&adev->data);
577bd8191ccSRafael J. Wysocki 	}
5785c53b262SRafael J. Wysocki 
5795c53b262SRafael J. Wysocki  out:
5805c53b262SRafael J. Wysocki 	if (acpi_of && !adev->flags.of_compatible_ok)
5815c53b262SRafael J. Wysocki 		acpi_handle_info(adev->handle,
582ee892094SRafael J. Wysocki 			 ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n");
583899596e0SLukas Wunner 
584899596e0SLukas Wunner 	if (!adev->data.pointer)
585899596e0SLukas Wunner 		acpi_extract_apple_properties(adev);
586ffdcd955SMika Westerberg }
587ffdcd955SMika Westerberg 
acpi_free_device_properties(struct list_head * list)5883bd561e1SSakari Ailus static void acpi_free_device_properties(struct list_head *list)
5893bd561e1SSakari Ailus {
5903bd561e1SSakari Ailus 	struct acpi_device_properties *props, *tmp;
5913bd561e1SSakari Ailus 
5923bd561e1SSakari Ailus 	list_for_each_entry_safe(props, tmp, list, list) {
593103e10c6SSakari Ailus 		u32 i;
594103e10c6SSakari Ailus 
5953bd561e1SSakari Ailus 		list_del(&props->list);
596103e10c6SSakari Ailus 		/* Buffer data properties were separately allocated */
597103e10c6SSakari Ailus 		if (props->bufs)
598103e10c6SSakari Ailus 			for (i = 0; i < props->properties->package.count; i++)
599103e10c6SSakari Ailus 				ACPI_FREE(props->bufs[i]);
600103e10c6SSakari Ailus 		kvfree(props);
6013bd561e1SSakari Ailus 	}
6023bd561e1SSakari Ailus }
6033bd561e1SSakari Ailus 
acpi_destroy_nondev_subnodes(struct list_head * list)604445b0eb0SRafael J. Wysocki static void acpi_destroy_nondev_subnodes(struct list_head *list)
605445b0eb0SRafael J. Wysocki {
606445b0eb0SRafael J. Wysocki 	struct acpi_data_node *dn, *next;
607445b0eb0SRafael J. Wysocki 
608445b0eb0SRafael J. Wysocki 	if (list_empty(list))
609445b0eb0SRafael J. Wysocki 		return;
610445b0eb0SRafael J. Wysocki 
611445b0eb0SRafael J. Wysocki 	list_for_each_entry_safe_reverse(dn, next, list, sibling) {
612445b0eb0SRafael J. Wysocki 		acpi_destroy_nondev_subnodes(&dn->data.subnodes);
613263b4c1aSRafael J. Wysocki 		wait_for_completion(&dn->kobj_done);
614445b0eb0SRafael J. Wysocki 		list_del(&dn->sibling);
615445b0eb0SRafael J. Wysocki 		ACPI_FREE((void *)dn->data.pointer);
6163bd561e1SSakari Ailus 		acpi_free_device_properties(&dn->data.properties);
617445b0eb0SRafael J. Wysocki 		kfree(dn);
618445b0eb0SRafael J. Wysocki 	}
619445b0eb0SRafael J. Wysocki }
620445b0eb0SRafael J. Wysocki 
acpi_free_properties(struct acpi_device * adev)621ffdcd955SMika Westerberg void acpi_free_properties(struct acpi_device *adev)
622ffdcd955SMika Westerberg {
6231d52f109SSakari Ailus 	acpi_untie_nondev_subnodes(&adev->data);
624445b0eb0SRafael J. Wysocki 	acpi_destroy_nondev_subnodes(&adev->data.subnodes);
625ffdcd955SMika Westerberg 	ACPI_FREE((void *)adev->data.pointer);
626733e6251SMika Westerberg 	adev->data.of_compatible = NULL;
627ffdcd955SMika Westerberg 	adev->data.pointer = NULL;
6283bd561e1SSakari Ailus 	acpi_free_device_properties(&adev->data.properties);
629ffdcd955SMika Westerberg }
630ffdcd955SMika Westerberg 
631ffdcd955SMika Westerberg /**
6323a7a2ab8SRafael J. Wysocki  * acpi_data_get_property - return an ACPI property with given name
6333a7a2ab8SRafael J. Wysocki  * @data: ACPI device deta object to get the property from
634ffdcd955SMika Westerberg  * @name: Name of the property
635ffdcd955SMika Westerberg  * @type: Expected property type
636ffdcd955SMika Westerberg  * @obj: Location to store the property value (if not %NULL)
637ffdcd955SMika Westerberg  *
638ffdcd955SMika Westerberg  * Look up a property with @name and store a pointer to the resulting ACPI
639ffdcd955SMika Westerberg  * object at the location pointed to by @obj if found.
640ffdcd955SMika Westerberg  *
641ffdcd955SMika Westerberg  * Callers must not attempt to free the returned objects.  These objects will be
6423a7a2ab8SRafael J. Wysocki  * freed by the ACPI core automatically during the removal of @data.
643ffdcd955SMika Westerberg  *
644ffdcd955SMika Westerberg  * Return: %0 if property with @name has been found (success),
645ffdcd955SMika Westerberg  *         %-EINVAL if the arguments are invalid,
6463c60f114SAndy Shevchenko  *         %-EINVAL if the property doesn't exist,
647ffdcd955SMika Westerberg  *         %-EPROTO if the property value type doesn't match @type.
648ffdcd955SMika Westerberg  */
acpi_data_get_property(const struct acpi_device_data * data,const char * name,acpi_object_type type,const union acpi_object ** obj)64999a85464SSakari Ailus static int acpi_data_get_property(const struct acpi_device_data *data,
6503a7a2ab8SRafael J. Wysocki 				  const char *name, acpi_object_type type,
6513a7a2ab8SRafael J. Wysocki 				  const union acpi_object **obj)
652ffdcd955SMika Westerberg {
6535f5e4890SMika Westerberg 	const struct acpi_device_properties *props;
654ffdcd955SMika Westerberg 
6553a7a2ab8SRafael J. Wysocki 	if (!data || !name)
656ffdcd955SMika Westerberg 		return -EINVAL;
657ffdcd955SMika Westerberg 
6585f5e4890SMika Westerberg 	if (!data->pointer || list_empty(&data->properties))
6593c60f114SAndy Shevchenko 		return -EINVAL;
660ffdcd955SMika Westerberg 
6615f5e4890SMika Westerberg 	list_for_each_entry(props, &data->properties, list) {
6625f5e4890SMika Westerberg 		const union acpi_object *properties;
6635f5e4890SMika Westerberg 		unsigned int i;
6645f5e4890SMika Westerberg 
6655f5e4890SMika Westerberg 		properties = props->properties;
666ffdcd955SMika Westerberg 		for (i = 0; i < properties->package.count; i++) {
667ffdcd955SMika Westerberg 			const union acpi_object *propname, *propvalue;
668ffdcd955SMika Westerberg 			const union acpi_object *property;
669ffdcd955SMika Westerberg 
670ffdcd955SMika Westerberg 			property = &properties->package.elements[i];
671ffdcd955SMika Westerberg 
672ffdcd955SMika Westerberg 			propname = &property->package.elements[0];
673ffdcd955SMika Westerberg 			propvalue = &property->package.elements[1];
674ffdcd955SMika Westerberg 
675ffdcd955SMika Westerberg 			if (!strcmp(name, propname->string.pointer)) {
6765f5e4890SMika Westerberg 				if (type != ACPI_TYPE_ANY &&
6775f5e4890SMika Westerberg 				    propvalue->type != type)
678ffdcd955SMika Westerberg 					return -EPROTO;
6793c60f114SAndy Shevchenko 				if (obj)
680ffdcd955SMika Westerberg 					*obj = propvalue;
681ffdcd955SMika Westerberg 
682ffdcd955SMika Westerberg 				return 0;
683ffdcd955SMika Westerberg 			}
684ffdcd955SMika Westerberg 		}
6855f5e4890SMika Westerberg 	}
6863c60f114SAndy Shevchenko 	return -EINVAL;
687ffdcd955SMika Westerberg }
688ffdcd955SMika Westerberg 
689ffdcd955SMika Westerberg /**
6903a7a2ab8SRafael J. Wysocki  * acpi_dev_get_property - return an ACPI property with given name.
6913a7a2ab8SRafael J. Wysocki  * @adev: ACPI device to get the property from.
6923a7a2ab8SRafael J. Wysocki  * @name: Name of the property.
6933a7a2ab8SRafael J. Wysocki  * @type: Expected property type.
6943a7a2ab8SRafael J. Wysocki  * @obj: Location to store the property value (if not %NULL).
6953a7a2ab8SRafael J. Wysocki  */
acpi_dev_get_property(const struct acpi_device * adev,const char * name,acpi_object_type type,const union acpi_object ** obj)69699a85464SSakari Ailus int acpi_dev_get_property(const struct acpi_device *adev, const char *name,
6973a7a2ab8SRafael J. Wysocki 			  acpi_object_type type, const union acpi_object **obj)
6983a7a2ab8SRafael J. Wysocki {
6993a7a2ab8SRafael J. Wysocki 	return adev ? acpi_data_get_property(&adev->data, name, type, obj) : -EINVAL;
7003a7a2ab8SRafael J. Wysocki }
7013a7a2ab8SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_get_property);
7023a7a2ab8SRafael J. Wysocki 
70399a85464SSakari Ailus static const struct acpi_device_data *
acpi_device_data_of_node(const struct fwnode_handle * fwnode)70499a85464SSakari Ailus acpi_device_data_of_node(const struct fwnode_handle *fwnode)
7053a7a2ab8SRafael J. Wysocki {
706db3e50f3SSakari Ailus 	if (is_acpi_device_node(fwnode)) {
70799a85464SSakari Ailus 		const struct acpi_device *adev = to_acpi_device_node(fwnode);
7083a7a2ab8SRafael J. Wysocki 		return &adev->data;
7099978f446SAndy Shevchenko 	}
7109978f446SAndy Shevchenko 	if (is_acpi_data_node(fwnode)) {
71199a85464SSakari Ailus 		const struct acpi_data_node *dn = to_acpi_data_node(fwnode);
7123a7a2ab8SRafael J. Wysocki 		return &dn->data;
7133a7a2ab8SRafael J. Wysocki 	}
7143a7a2ab8SRafael J. Wysocki 	return NULL;
7153a7a2ab8SRafael J. Wysocki }
7163a7a2ab8SRafael J. Wysocki 
7173a7a2ab8SRafael J. Wysocki /**
7183a7a2ab8SRafael J. Wysocki  * acpi_node_prop_get - return an ACPI property with given name.
7193a7a2ab8SRafael J. Wysocki  * @fwnode: Firmware node to get the property from.
7203a7a2ab8SRafael J. Wysocki  * @propname: Name of the property.
7213a7a2ab8SRafael J. Wysocki  * @valptr: Location to store a pointer to the property value (if not %NULL).
7223a7a2ab8SRafael J. Wysocki  */
acpi_node_prop_get(const struct fwnode_handle * fwnode,const char * propname,void ** valptr)72399a85464SSakari Ailus int acpi_node_prop_get(const struct fwnode_handle *fwnode,
72499a85464SSakari Ailus 		       const char *propname, void **valptr)
7253a7a2ab8SRafael J. Wysocki {
7263a7a2ab8SRafael J. Wysocki 	return acpi_data_get_property(acpi_device_data_of_node(fwnode),
7273a7a2ab8SRafael J. Wysocki 				      propname, ACPI_TYPE_ANY,
7283a7a2ab8SRafael J. Wysocki 				      (const union acpi_object **)valptr);
7293a7a2ab8SRafael J. Wysocki }
7303a7a2ab8SRafael J. Wysocki 
7313a7a2ab8SRafael J. Wysocki /**
7323a7a2ab8SRafael J. Wysocki  * acpi_data_get_property_array - return an ACPI array property with given name
733c82ff99eSAndy Shevchenko  * @data: ACPI data object to get the property from
734ffdcd955SMika Westerberg  * @name: Name of the property
735ffdcd955SMika Westerberg  * @type: Expected type of array elements
736ffdcd955SMika Westerberg  * @obj: Location to store a pointer to the property value (if not NULL)
737ffdcd955SMika Westerberg  *
738ffdcd955SMika Westerberg  * Look up an array property with @name and store a pointer to the resulting
739ffdcd955SMika Westerberg  * ACPI object at the location pointed to by @obj if found.
740ffdcd955SMika Westerberg  *
741ffdcd955SMika Westerberg  * Callers must not attempt to free the returned objects.  Those objects will be
7423a7a2ab8SRafael J. Wysocki  * freed by the ACPI core automatically during the removal of @data.
743ffdcd955SMika Westerberg  *
744ffdcd955SMika Westerberg  * Return: %0 if array property (package) with @name has been found (success),
745ffdcd955SMika Westerberg  *         %-EINVAL if the arguments are invalid,
7463c60f114SAndy Shevchenko  *         %-EINVAL if the property doesn't exist,
747ffdcd955SMika Westerberg  *         %-EPROTO if the property is not a package or the type of its elements
748ffdcd955SMika Westerberg  *           doesn't match @type.
749ffdcd955SMika Westerberg  */
acpi_data_get_property_array(const struct acpi_device_data * data,const char * name,acpi_object_type type,const union acpi_object ** obj)75099a85464SSakari Ailus static int acpi_data_get_property_array(const struct acpi_device_data *data,
7513a7a2ab8SRafael J. Wysocki 					const char *name,
752ffdcd955SMika Westerberg 					acpi_object_type type,
753ffdcd955SMika Westerberg 					const union acpi_object **obj)
754ffdcd955SMika Westerberg {
755ffdcd955SMika Westerberg 	const union acpi_object *prop;
756ffdcd955SMika Westerberg 	int ret, i;
757ffdcd955SMika Westerberg 
7583a7a2ab8SRafael J. Wysocki 	ret = acpi_data_get_property(data, name, ACPI_TYPE_PACKAGE, &prop);
759ffdcd955SMika Westerberg 	if (ret)
760ffdcd955SMika Westerberg 		return ret;
761ffdcd955SMika Westerberg 
762ffdcd955SMika Westerberg 	if (type != ACPI_TYPE_ANY) {
763ffdcd955SMika Westerberg 		/* Check that all elements are of correct type. */
764ffdcd955SMika Westerberg 		for (i = 0; i < prop->package.count; i++)
765ffdcd955SMika Westerberg 			if (prop->package.elements[i].type != type)
766ffdcd955SMika Westerberg 				return -EPROTO;
767ffdcd955SMika Westerberg 	}
768ffdcd955SMika Westerberg 	if (obj)
769ffdcd955SMika Westerberg 		*obj = prop;
770ffdcd955SMika Westerberg 
771ffdcd955SMika Westerberg 	return 0;
772ffdcd955SMika Westerberg }
773ffdcd955SMika Westerberg 
7744eb0c3bfSSakari Ailus static struct fwnode_handle *
acpi_fwnode_get_named_child_node(const struct fwnode_handle * fwnode,const char * childname)7754eb0c3bfSSakari Ailus acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
7764eb0c3bfSSakari Ailus 				 const char *childname)
7774eb0c3bfSSakari Ailus {
7784eb0c3bfSSakari Ailus 	struct fwnode_handle *child;
779ee48cef6SHeikki Krogerus 
780ee48cef6SHeikki Krogerus 	fwnode_for_each_child_node(fwnode, child) {
781ee48cef6SHeikki Krogerus 		if (is_acpi_data_node(child)) {
7824eb0c3bfSSakari Ailus 			if (acpi_data_node_match(child, childname))
7834eb0c3bfSSakari Ailus 				return child;
784ee48cef6SHeikki Krogerus 			continue;
785ee48cef6SHeikki Krogerus 		}
786ee48cef6SHeikki Krogerus 
787cbdd865aSHeikki Krogerus 		if (!strncmp(acpi_device_bid(to_acpi_device_node(child)),
788cbdd865aSHeikki Krogerus 			     childname, ACPI_NAMESEG_SIZE))
789ee48cef6SHeikki Krogerus 			return child;
790ee48cef6SHeikki Krogerus 	}
7914eb0c3bfSSakari Ailus 
7924eb0c3bfSSakari Ailus 	return NULL;
7934eb0c3bfSSakari Ailus }
7944eb0c3bfSSakari Ailus 
acpi_get_ref_args(struct fwnode_reference_args * args,struct fwnode_handle * ref_fwnode,const union acpi_object ** element,const union acpi_object * end,size_t num_args)7951aef25d9SSakari Ailus static int acpi_get_ref_args(struct fwnode_reference_args *args,
7961aef25d9SSakari Ailus 			     struct fwnode_handle *ref_fwnode,
7971aef25d9SSakari Ailus 			     const union acpi_object **element,
7981aef25d9SSakari Ailus 			     const union acpi_object *end, size_t num_args)
7991aef25d9SSakari Ailus {
8001aef25d9SSakari Ailus 	u32 nargs = 0, i;
8011aef25d9SSakari Ailus 
8021aef25d9SSakari Ailus 	/*
8031aef25d9SSakari Ailus 	 * Find the referred data extension node under the
8041aef25d9SSakari Ailus 	 * referred device node.
8051aef25d9SSakari Ailus 	 */
8061aef25d9SSakari Ailus 	for (; *element < end && (*element)->type == ACPI_TYPE_STRING;
8071aef25d9SSakari Ailus 	     (*element)++) {
8081aef25d9SSakari Ailus 		const char *child_name = (*element)->string.pointer;
8091aef25d9SSakari Ailus 
8101aef25d9SSakari Ailus 		ref_fwnode = acpi_fwnode_get_named_child_node(ref_fwnode, child_name);
8111aef25d9SSakari Ailus 		if (!ref_fwnode)
8121aef25d9SSakari Ailus 			return -EINVAL;
8131aef25d9SSakari Ailus 	}
8141aef25d9SSakari Ailus 
8151aef25d9SSakari Ailus 	/*
8161aef25d9SSakari Ailus 	 * Assume the following integer elements are all args. Stop counting on
8171aef25d9SSakari Ailus 	 * the first reference or end of the package arguments. In case of
8181aef25d9SSakari Ailus 	 * neither reference, nor integer, return an error, we can't parse it.
8191aef25d9SSakari Ailus 	 */
8201aef25d9SSakari Ailus 	for (i = 0; (*element) + i < end && i < num_args; i++) {
8211aef25d9SSakari Ailus 		acpi_object_type type = (*element)[i].type;
8221aef25d9SSakari Ailus 
8231aef25d9SSakari Ailus 		if (type == ACPI_TYPE_LOCAL_REFERENCE)
8241aef25d9SSakari Ailus 			break;
8251aef25d9SSakari Ailus 
8261aef25d9SSakari Ailus 		if (type == ACPI_TYPE_INTEGER)
8271aef25d9SSakari Ailus 			nargs++;
8281aef25d9SSakari Ailus 		else
8291aef25d9SSakari Ailus 			return -EINVAL;
8301aef25d9SSakari Ailus 	}
8311aef25d9SSakari Ailus 
8321aef25d9SSakari Ailus 	if (nargs > NR_FWNODE_REFERENCE_ARGS)
8331aef25d9SSakari Ailus 		return -EINVAL;
8341aef25d9SSakari Ailus 
8351aef25d9SSakari Ailus 	if (args) {
8361aef25d9SSakari Ailus 		args->fwnode = ref_fwnode;
8371aef25d9SSakari Ailus 		args->nargs = nargs;
8381aef25d9SSakari Ailus 		for (i = 0; i < nargs; i++)
8391aef25d9SSakari Ailus 			args->args[i] = (*element)[i].integer.value;
8401aef25d9SSakari Ailus 	}
8411aef25d9SSakari Ailus 
8421aef25d9SSakari Ailus 	(*element) += nargs;
8431aef25d9SSakari Ailus 
8441aef25d9SSakari Ailus 	return 0;
8451aef25d9SSakari Ailus }
8461aef25d9SSakari Ailus 
847ffdcd955SMika Westerberg /**
848b60e4ea4SMika Westerberg  * __acpi_node_get_property_reference - returns handle to the referenced object
849b60e4ea4SMika Westerberg  * @fwnode: Firmware node to get the property from
850504a3374SRafael J. Wysocki  * @propname: Name of the property
851ffdcd955SMika Westerberg  * @index: Index of the reference to return
852b60e4ea4SMika Westerberg  * @num_args: Maximum number of arguments after each reference
853ffdcd955SMika Westerberg  * @args: Location to store the returned reference with optional arguments
854*afc7dd4eSSakari Ailus  *	  (may be NULL)
855ffdcd955SMika Westerberg  *
856ffdcd955SMika Westerberg  * Find property with @name, verifify that it is a package containing at least
857ffdcd955SMika Westerberg  * one object reference and if so, store the ACPI device object pointer to the
85860ba032eSRafael J. Wysocki  * target object in @args->adev.  If the reference includes arguments, store
85960ba032eSRafael J. Wysocki  * them in the @args->args[] array.
860ffdcd955SMika Westerberg  *
86160ba032eSRafael J. Wysocki  * If there's more than one reference in the property value package, @index is
86260ba032eSRafael J. Wysocki  * used to select the one to return.
863ffdcd955SMika Westerberg  *
864b60e4ea4SMika Westerberg  * It is possible to leave holes in the property value set like in the
865b60e4ea4SMika Westerberg  * example below:
866b60e4ea4SMika Westerberg  *
867b60e4ea4SMika Westerberg  * Package () {
868b60e4ea4SMika Westerberg  *     "cs-gpios",
869b60e4ea4SMika Westerberg  *     Package () {
870b60e4ea4SMika Westerberg  *        ^GPIO, 19, 0, 0,
871b60e4ea4SMika Westerberg  *        ^GPIO, 20, 0, 0,
872b60e4ea4SMika Westerberg  *        0,
873b60e4ea4SMika Westerberg  *        ^GPIO, 21, 0, 0,
874b60e4ea4SMika Westerberg  *     }
875b60e4ea4SMika Westerberg  * }
876b60e4ea4SMika Westerberg  *
877c343bc2cSSakari Ailus  * Calling this function with index %2 or index %3 return %-ENOENT. If the
878c343bc2cSSakari Ailus  * property does not contain any more values %-ENOENT is returned. The NULL
879c343bc2cSSakari Ailus  * entry must be single integer and preferably contain value %0.
880b60e4ea4SMika Westerberg  *
881ffdcd955SMika Westerberg  * Return: %0 on success, negative error code on failure.
882ffdcd955SMika Westerberg  */
__acpi_node_get_property_reference(const struct fwnode_handle * fwnode,const char * propname,size_t index,size_t num_args,struct fwnode_reference_args * args)88399a85464SSakari Ailus int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
884b60e4ea4SMika Westerberg 	const char *propname, size_t index, size_t num_args,
885977d5ad3SSakari Ailus 	struct fwnode_reference_args *args)
886ffdcd955SMika Westerberg {
887ffdcd955SMika Westerberg 	const union acpi_object *element, *end;
888ffdcd955SMika Westerberg 	const union acpi_object *obj;
88999a85464SSakari Ailus 	const struct acpi_device_data *data;
890ffdcd955SMika Westerberg 	struct acpi_device *device;
891ffdcd955SMika Westerberg 	int ret, idx = 0;
892ffdcd955SMika Westerberg 
893b60e4ea4SMika Westerberg 	data = acpi_device_data_of_node(fwnode);
894b60e4ea4SMika Westerberg 	if (!data)
895c343bc2cSSakari Ailus 		return -ENOENT;
896b60e4ea4SMika Westerberg 
897504a3374SRafael J. Wysocki 	ret = acpi_data_get_property(data, propname, ACPI_TYPE_ANY, &obj);
898ffdcd955SMika Westerberg 	if (ret)
89951858a27SSakari Ailus 		return ret == -EINVAL ? -ENOENT : -EINVAL;
900ffdcd955SMika Westerberg 
90188af7bbdSSakari Ailus 	switch (obj->type) {
90288af7bbdSSakari Ailus 	case ACPI_TYPE_LOCAL_REFERENCE:
90388af7bbdSSakari Ailus 		/* Plain single reference without arguments. */
90460ba032eSRafael J. Wysocki 		if (index)
905babc92daSSakari Ailus 			return -ENOENT;
906ffdcd955SMika Westerberg 
90799ece713SRafael J. Wysocki 		device = acpi_fetch_acpi_dev(obj->reference.handle);
90899ece713SRafael J. Wysocki 		if (!device)
90999ece713SRafael J. Wysocki 			return -EINVAL;
910ffdcd955SMika Westerberg 
911*afc7dd4eSSakari Ailus 		if (!args)
912*afc7dd4eSSakari Ailus 			return 0;
913*afc7dd4eSSakari Ailus 
914977d5ad3SSakari Ailus 		args->fwnode = acpi_fwnode_handle(device);
915ffdcd955SMika Westerberg 		args->nargs = 0;
916ffdcd955SMika Westerberg 		return 0;
91788af7bbdSSakari Ailus 	case ACPI_TYPE_PACKAGE:
918ffdcd955SMika Westerberg 		/*
919ffdcd955SMika Westerberg 		 * If it is not a single reference, then it is a package of
920ffdcd955SMika Westerberg 		 * references followed by number of ints as follows:
921ffdcd955SMika Westerberg 		 *
922ffdcd955SMika Westerberg 		 *  Package () { REF, INT, REF, INT, INT }
923ffdcd955SMika Westerberg 		 *
924ffdcd955SMika Westerberg 		 * The index argument is then used to determine which reference
925ffdcd955SMika Westerberg 		 * the caller wants (along with the arguments).
926ffdcd955SMika Westerberg 		 */
92788af7bbdSSakari Ailus 		break;
92888af7bbdSSakari Ailus 	default:
92951858a27SSakari Ailus 		return -EINVAL;
93088af7bbdSSakari Ailus 	}
93188af7bbdSSakari Ailus 
93251858a27SSakari Ailus 	if (index >= obj->package.count)
93351858a27SSakari Ailus 		return -ENOENT;
934ffdcd955SMika Westerberg 
935ffdcd955SMika Westerberg 	element = obj->package.elements;
936ffdcd955SMika Westerberg 	end = element + obj->package.count;
937ffdcd955SMika Westerberg 
938ffdcd955SMika Westerberg 	while (element < end) {
93988af7bbdSSakari Ailus 		switch (element->type) {
94088af7bbdSSakari Ailus 		case ACPI_TYPE_LOCAL_REFERENCE:
94199ece713SRafael J. Wysocki 			device = acpi_fetch_acpi_dev(element->reference.handle);
94299ece713SRafael J. Wysocki 			if (!device)
943c343bc2cSSakari Ailus 				return -EINVAL;
944ffdcd955SMika Westerberg 
945b60e4ea4SMika Westerberg 			element++;
946ffdcd955SMika Westerberg 
9471aef25d9SSakari Ailus 			ret = acpi_get_ref_args(idx == index ? args : NULL,
9481aef25d9SSakari Ailus 						acpi_fwnode_handle(device),
9491aef25d9SSakari Ailus 						&element, end, num_args);
9501aef25d9SSakari Ailus 			if (ret < 0)
9511aef25d9SSakari Ailus 				return ret;
9524eb0c3bfSSakari Ailus 
9531aef25d9SSakari Ailus 			if (idx == index)
954ffdcd955SMika Westerberg 				return 0;
955ffdcd955SMika Westerberg 
95688af7bbdSSakari Ailus 			break;
95788af7bbdSSakari Ailus 		case ACPI_TYPE_INTEGER:
958b60e4ea4SMika Westerberg 			if (idx == index)
959b60e4ea4SMika Westerberg 				return -ENOENT;
960b60e4ea4SMika Westerberg 			element++;
96188af7bbdSSakari Ailus 			break;
96288af7bbdSSakari Ailus 		default:
963c343bc2cSSakari Ailus 			return -EINVAL;
964ffdcd955SMika Westerberg 		}
965504a3374SRafael J. Wysocki 
966b60e4ea4SMika Westerberg 		idx++;
967504a3374SRafael J. Wysocki 	}
968b60e4ea4SMika Westerberg 
969c343bc2cSSakari Ailus 	return -ENOENT;
970b60e4ea4SMika Westerberg }
971b60e4ea4SMika Westerberg EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
972b31384faSRafael J. Wysocki 
acpi_data_prop_read_single(const struct acpi_device_data * data,const char * propname,enum dev_prop_type proptype,void * val)97399a85464SSakari Ailus static int acpi_data_prop_read_single(const struct acpi_device_data *data,
9743a7a2ab8SRafael J. Wysocki 				      const char *propname,
975b31384faSRafael J. Wysocki 				      enum dev_prop_type proptype, void *val)
976b31384faSRafael J. Wysocki {
977b31384faSRafael J. Wysocki 	const union acpi_object *obj;
9781fbd9029SAndy Shevchenko 	int ret = 0;
979b31384faSRafael J. Wysocki 
9801fbd9029SAndy Shevchenko 	if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64)
9813a7a2ab8SRafael J. Wysocki 		ret = acpi_data_get_property(data, propname, ACPI_TYPE_INTEGER, &obj);
9821fbd9029SAndy Shevchenko 	else if (proptype == DEV_PROP_STRING)
9831fbd9029SAndy Shevchenko 		ret = acpi_data_get_property(data, propname, ACPI_TYPE_STRING, &obj);
984b31384faSRafael J. Wysocki 	if (ret)
985b31384faSRafael J. Wysocki 		return ret;
986b31384faSRafael J. Wysocki 
987b31384faSRafael J. Wysocki 	switch (proptype) {
988b31384faSRafael J. Wysocki 	case DEV_PROP_U8:
989b31384faSRafael J. Wysocki 		if (obj->integer.value > U8_MAX)
990b31384faSRafael J. Wysocki 			return -EOVERFLOW;
991e1e6bd29SRafael J. Wysocki 		if (val)
992b31384faSRafael J. Wysocki 			*(u8 *)val = obj->integer.value;
993b31384faSRafael J. Wysocki 		break;
994b31384faSRafael J. Wysocki 	case DEV_PROP_U16:
995b31384faSRafael J. Wysocki 		if (obj->integer.value > U16_MAX)
996b31384faSRafael J. Wysocki 			return -EOVERFLOW;
997e1e6bd29SRafael J. Wysocki 		if (val)
998b31384faSRafael J. Wysocki 			*(u16 *)val = obj->integer.value;
999b31384faSRafael J. Wysocki 		break;
1000b31384faSRafael J. Wysocki 	case DEV_PROP_U32:
1001b31384faSRafael J. Wysocki 		if (obj->integer.value > U32_MAX)
1002b31384faSRafael J. Wysocki 			return -EOVERFLOW;
1003e1e6bd29SRafael J. Wysocki 		if (val)
1004b31384faSRafael J. Wysocki 			*(u32 *)val = obj->integer.value;
1005b31384faSRafael J. Wysocki 		break;
10061fbd9029SAndy Shevchenko 	case DEV_PROP_U64:
1007e1e6bd29SRafael J. Wysocki 		if (val)
1008b31384faSRafael J. Wysocki 			*(u64 *)val = obj->integer.value;
1009b31384faSRafael J. Wysocki 		break;
10101fbd9029SAndy Shevchenko 	case DEV_PROP_STRING:
1011e1e6bd29SRafael J. Wysocki 		if (val)
1012b31384faSRafael J. Wysocki 			*(char **)val = obj->string.pointer;
1013b0b027ceSSakari Ailus 		return 1;
10141fbd9029SAndy Shevchenko 	default:
10151fbd9029SAndy Shevchenko 		return -EINVAL;
1016b31384faSRafael J. Wysocki 	}
10171fbd9029SAndy Shevchenko 
10181fbd9029SAndy Shevchenko 	/* When no storage provided return number of available values */
10191fbd9029SAndy Shevchenko 	return val ? 0 : 1;
1020b31384faSRafael J. Wysocki }
1021b31384faSRafael J. Wysocki 
102292304413SSakari Ailus #define acpi_copy_property_array_uint(items, val, nval)			\
102392304413SSakari Ailus 	({								\
102492304413SSakari Ailus 		typeof(items) __items = items;				\
102592304413SSakari Ailus 		typeof(val) __val = val;				\
102692304413SSakari Ailus 		typeof(nval) __nval = nval;				\
102792304413SSakari Ailus 		size_t i;						\
102892304413SSakari Ailus 		int ret = 0;						\
102992304413SSakari Ailus 									\
103092304413SSakari Ailus 		for (i = 0; i < __nval; i++) {				\
1031369af6bfSSakari Ailus 			if (__items->type == ACPI_TYPE_BUFFER) {	\
1032369af6bfSSakari Ailus 				__val[i] = __items->buffer.pointer[i];	\
1033369af6bfSSakari Ailus 				continue;				\
1034369af6bfSSakari Ailus 			}						\
103592304413SSakari Ailus 			if (__items[i].type != ACPI_TYPE_INTEGER) {	\
103692304413SSakari Ailus 				ret = -EPROTO;				\
103792304413SSakari Ailus 				break;					\
103892304413SSakari Ailus 			}						\
103992304413SSakari Ailus 			if (__items[i].integer.value > _Generic(__val,	\
104006865077SStefan Binding 								u8 *: U8_MAX, \
104106865077SStefan Binding 								u16 *: U16_MAX, \
104206865077SStefan Binding 								u32 *: U32_MAX, \
1043bd9594aeSSakari Ailus 								u64 *: U64_MAX)) { \
104492304413SSakari Ailus 				ret = -EOVERFLOW;			\
104592304413SSakari Ailus 				break;					\
104692304413SSakari Ailus 			}						\
104792304413SSakari Ailus 									\
104892304413SSakari Ailus 			__val[i] = __items[i].integer.value;		\
104992304413SSakari Ailus 		}							\
105092304413SSakari Ailus 		ret;							\
105192304413SSakari Ailus 	})
1052b31384faSRafael J. Wysocki 
acpi_copy_property_array_string(const union acpi_object * items,char ** val,size_t nval)1053b31384faSRafael J. Wysocki static int acpi_copy_property_array_string(const union acpi_object *items,
1054b31384faSRafael J. Wysocki 					   char **val, size_t nval)
1055b31384faSRafael J. Wysocki {
1056b31384faSRafael J. Wysocki 	int i;
1057b31384faSRafael J. Wysocki 
1058b31384faSRafael J. Wysocki 	for (i = 0; i < nval; i++) {
1059b31384faSRafael J. Wysocki 		if (items[i].type != ACPI_TYPE_STRING)
1060b31384faSRafael J. Wysocki 			return -EPROTO;
1061b31384faSRafael J. Wysocki 
1062b31384faSRafael J. Wysocki 		val[i] = items[i].string.pointer;
1063b31384faSRafael J. Wysocki 	}
1064b0b027ceSSakari Ailus 	return nval;
1065b31384faSRafael J. Wysocki }
1066b31384faSRafael J. Wysocki 
acpi_data_prop_read(const struct acpi_device_data * data,const char * propname,enum dev_prop_type proptype,void * val,size_t nval)106799a85464SSakari Ailus static int acpi_data_prop_read(const struct acpi_device_data *data,
10683a7a2ab8SRafael J. Wysocki 			       const char *propname,
10693a7a2ab8SRafael J. Wysocki 			       enum dev_prop_type proptype,
10703a7a2ab8SRafael J. Wysocki 			       void *val, size_t nval)
1071b31384faSRafael J. Wysocki {
1072b31384faSRafael J. Wysocki 	const union acpi_object *obj;
1073b31384faSRafael J. Wysocki 	const union acpi_object *items;
1074b31384faSRafael J. Wysocki 	int ret;
1075b31384faSRafael J. Wysocki 
1076e1e6bd29SRafael J. Wysocki 	if (nval == 1 || !val) {
10773a7a2ab8SRafael J. Wysocki 		ret = acpi_data_prop_read_single(data, propname, proptype, val);
1078e1e6bd29SRafael J. Wysocki 		/*
1079e1e6bd29SRafael J. Wysocki 		 * The overflow error means that the property is there and it is
1080e1e6bd29SRafael J. Wysocki 		 * single-value, but its type does not match, so return.
1081e1e6bd29SRafael J. Wysocki 		 */
1082e1e6bd29SRafael J. Wysocki 		if (ret >= 0 || ret == -EOVERFLOW)
1083b31384faSRafael J. Wysocki 			return ret;
1084e1e6bd29SRafael J. Wysocki 
1085e1e6bd29SRafael J. Wysocki 		/*
1086e1e6bd29SRafael J. Wysocki 		 * Reading this property as a single-value one failed, but its
1087e1e6bd29SRafael J. Wysocki 		 * value may still be represented as one-element array, so
1088e1e6bd29SRafael J. Wysocki 		 * continue.
1089e1e6bd29SRafael J. Wysocki 		 */
1090b31384faSRafael J. Wysocki 	}
1091b31384faSRafael J. Wysocki 
10923a7a2ab8SRafael J. Wysocki 	ret = acpi_data_get_property_array(data, propname, ACPI_TYPE_ANY, &obj);
1093369af6bfSSakari Ailus 	if (ret && proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64)
1094369af6bfSSakari Ailus 		ret = acpi_data_get_property(data, propname, ACPI_TYPE_BUFFER,
1095369af6bfSSakari Ailus 					     &obj);
1096b31384faSRafael J. Wysocki 	if (ret)
1097b31384faSRafael J. Wysocki 		return ret;
1098b31384faSRafael J. Wysocki 
1099369af6bfSSakari Ailus 	if (!val) {
1100369af6bfSSakari Ailus 		if (obj->type == ACPI_TYPE_BUFFER)
1101369af6bfSSakari Ailus 			return obj->buffer.length;
1102b31384faSRafael J. Wysocki 
1103369af6bfSSakari Ailus 		return obj->package.count;
1104369af6bfSSakari Ailus 	}
1105369af6bfSSakari Ailus 
1106369af6bfSSakari Ailus 	switch (proptype) {
1107369af6bfSSakari Ailus 	case DEV_PROP_STRING:
1108369af6bfSSakari Ailus 		break;
1109282026abSAndy Shevchenko 	default:
1110369af6bfSSakari Ailus 		if (obj->type == ACPI_TYPE_BUFFER) {
1111369af6bfSSakari Ailus 			if (nval > obj->buffer.length)
1112b31384faSRafael J. Wysocki 				return -EOVERFLOW;
1113282026abSAndy Shevchenko 		} else {
1114369af6bfSSakari Ailus 			if (nval > obj->package.count)
1115369af6bfSSakari Ailus 				return -EOVERFLOW;
1116282026abSAndy Shevchenko 		}
1117369af6bfSSakari Ailus 		break;
1118369af6bfSSakari Ailus 	}
11199978f446SAndy Shevchenko 	if (nval == 0)
11207dc59dc9SAndy Shevchenko 		return -EINVAL;
1121b31384faSRafael J. Wysocki 
1122282026abSAndy Shevchenko 	if (obj->type == ACPI_TYPE_BUFFER) {
1123282026abSAndy Shevchenko 		if (proptype != DEV_PROP_U8)
1124282026abSAndy Shevchenko 			return -EPROTO;
1125369af6bfSSakari Ailus 		items = obj;
1126282026abSAndy Shevchenko 	} else {
1127282026abSAndy Shevchenko 		items = obj->package.elements;
1128282026abSAndy Shevchenko 	}
11297dc59dc9SAndy Shevchenko 
1130b31384faSRafael J. Wysocki 	switch (proptype) {
1131b31384faSRafael J. Wysocki 	case DEV_PROP_U8:
113292304413SSakari Ailus 		ret = acpi_copy_property_array_uint(items, (u8 *)val, nval);
1133b31384faSRafael J. Wysocki 		break;
1134b31384faSRafael J. Wysocki 	case DEV_PROP_U16:
113592304413SSakari Ailus 		ret = acpi_copy_property_array_uint(items, (u16 *)val, nval);
1136b31384faSRafael J. Wysocki 		break;
1137b31384faSRafael J. Wysocki 	case DEV_PROP_U32:
113892304413SSakari Ailus 		ret = acpi_copy_property_array_uint(items, (u32 *)val, nval);
1139b31384faSRafael J. Wysocki 		break;
1140b31384faSRafael J. Wysocki 	case DEV_PROP_U64:
114192304413SSakari Ailus 		ret = acpi_copy_property_array_uint(items, (u64 *)val, nval);
1142b31384faSRafael J. Wysocki 		break;
1143b31384faSRafael J. Wysocki 	case DEV_PROP_STRING:
1144b0b027ceSSakari Ailus 		ret = acpi_copy_property_array_string(
1145b0b027ceSSakari Ailus 			items, (char **)val,
1146b0b027ceSSakari Ailus 			min_t(u32, nval, obj->package.count));
1147b31384faSRafael J. Wysocki 		break;
1148b31384faSRafael J. Wysocki 	default:
1149b31384faSRafael J. Wysocki 		ret = -EINVAL;
1150b31384faSRafael J. Wysocki 		break;
1151b31384faSRafael J. Wysocki 	}
1152b31384faSRafael J. Wysocki 	return ret;
1153b31384faSRafael J. Wysocki }
11543a7a2ab8SRafael J. Wysocki 
11553a7a2ab8SRafael J. Wysocki /**
11563a7a2ab8SRafael J. Wysocki  * acpi_node_prop_read - retrieve the value of an ACPI property with given name.
11573a7a2ab8SRafael J. Wysocki  * @fwnode: Firmware node to get the property from.
11583a7a2ab8SRafael J. Wysocki  * @propname: Name of the property.
11593a7a2ab8SRafael J. Wysocki  * @proptype: Expected property type.
11603a7a2ab8SRafael J. Wysocki  * @val: Location to store the property value (if not %NULL).
11613a7a2ab8SRafael J. Wysocki  * @nval: Size of the array pointed to by @val.
11623a7a2ab8SRafael J. Wysocki  *
11633a7a2ab8SRafael J. Wysocki  * If @val is %NULL, return the number of array elements comprising the value
11643a7a2ab8SRafael J. Wysocki  * of the property.  Otherwise, read at most @nval values to the array at the
11653a7a2ab8SRafael J. Wysocki  * location pointed to by @val.
11663a7a2ab8SRafael J. Wysocki  */
acpi_node_prop_read(const struct fwnode_handle * fwnode,const char * propname,enum dev_prop_type proptype,void * val,size_t nval)1167325aa816SAndy Shevchenko static int acpi_node_prop_read(const struct fwnode_handle *fwnode,
116899a85464SSakari Ailus 			       const char *propname, enum dev_prop_type proptype,
116999a85464SSakari Ailus 			       void *val, size_t nval)
11703a7a2ab8SRafael J. Wysocki {
11713a7a2ab8SRafael J. Wysocki 	return acpi_data_prop_read(acpi_device_data_of_node(fwnode),
11723a7a2ab8SRafael J. Wysocki 				   propname, proptype, val, nval);
11733a7a2ab8SRafael J. Wysocki }
1174504a3374SRafael J. Wysocki 
stop_on_next(struct acpi_device * adev,void * data)1175fa98b398SRafael J. Wysocki static int stop_on_next(struct acpi_device *adev, void *data)
1176fa98b398SRafael J. Wysocki {
1177fa98b398SRafael J. Wysocki 	struct acpi_device **ret_p = data;
1178fa98b398SRafael J. Wysocki 
1179fa98b398SRafael J. Wysocki 	if (!*ret_p) {
1180fa98b398SRafael J. Wysocki 		*ret_p = adev;
1181fa98b398SRafael J. Wysocki 		return 1;
1182fa98b398SRafael J. Wysocki 	}
1183fa98b398SRafael J. Wysocki 
1184fa98b398SRafael J. Wysocki 	/* Skip until the "previous" object is found. */
1185fa98b398SRafael J. Wysocki 	if (*ret_p == adev)
1186fa98b398SRafael J. Wysocki 		*ret_p = NULL;
1187fa98b398SRafael J. Wysocki 
1188fa98b398SRafael J. Wysocki 	return 0;
1189fa98b398SRafael J. Wysocki }
1190fa98b398SRafael J. Wysocki 
1191504a3374SRafael J. Wysocki /**
119234055190SMika Westerberg  * acpi_get_next_subnode - Return the next child node handle for a fwnode
119334055190SMika Westerberg  * @fwnode: Firmware node to find the next child node for.
1194504a3374SRafael J. Wysocki  * @child: Handle to one of the device's child nodes or a null handle.
1195504a3374SRafael J. Wysocki  */
acpi_get_next_subnode(const struct fwnode_handle * fwnode,struct fwnode_handle * child)119637ba983cSSakari Ailus struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode,
1197504a3374SRafael J. Wysocki 					    struct fwnode_handle *child)
1198504a3374SRafael J. Wysocki {
1199fa98b398SRafael J. Wysocki 	struct acpi_device *adev = to_acpi_device_node(fwnode);
1200504a3374SRafael J. Wysocki 
12019978f446SAndy Shevchenko 	if ((!child || is_acpi_device_node(child)) && adev) {
1202fa98b398SRafael J. Wysocki 		struct acpi_device *child_adev = to_acpi_device_node(child);
12030c0bceb7SSakari Ailus 
1204fa98b398SRafael J. Wysocki 		acpi_dev_for_each_child(adev, stop_on_next, &child_adev);
1205fa98b398SRafael J. Wysocki 		if (child_adev)
120601c1da28SSakari Ailus 			return acpi_fwnode_handle(child_adev);
1207fa98b398SRafael J. Wysocki 
1208fa98b398SRafael J. Wysocki 		child = NULL;
1209504a3374SRafael J. Wysocki 	}
1210504a3374SRafael J. Wysocki 
1211db3e50f3SSakari Ailus 	if (!child || is_acpi_data_node(child)) {
121201c1da28SSakari Ailus 		const struct acpi_data_node *data = to_acpi_data_node(fwnode);
1213fa98b398SRafael J. Wysocki 		const struct list_head *head;
1214fa98b398SRafael J. Wysocki 		struct list_head *next;
1215504a3374SRafael J. Wysocki 		struct acpi_data_node *dn;
1216504a3374SRafael J. Wysocki 
121723583f77SPierre-Louis Bossart 		/*
121823583f77SPierre-Louis Bossart 		 * We can have a combination of device and data nodes, e.g. with
121923583f77SPierre-Louis Bossart 		 * hierarchical _DSD properties. Make sure the adev pointer is
122023583f77SPierre-Louis Bossart 		 * restored before going through data nodes, otherwise we will
122123583f77SPierre-Louis Bossart 		 * be looking for data_nodes below the last device found instead
122223583f77SPierre-Louis Bossart 		 * of the common fwnode shared by device_nodes and data_nodes.
122323583f77SPierre-Louis Bossart 		 */
122423583f77SPierre-Louis Bossart 		adev = to_acpi_device_node(fwnode);
12250c0bceb7SSakari Ailus 		if (adev)
12260c0bceb7SSakari Ailus 			head = &adev->data.subnodes;
122734055190SMika Westerberg 		else if (data)
122834055190SMika Westerberg 			head = &data->data.subnodes;
122934055190SMika Westerberg 		else
123034055190SMika Westerberg 			return NULL;
123134055190SMika Westerberg 
1232504a3374SRafael J. Wysocki 		if (list_empty(head))
1233504a3374SRafael J. Wysocki 			return NULL;
1234504a3374SRafael J. Wysocki 
1235504a3374SRafael J. Wysocki 		if (child) {
1236504a3374SRafael J. Wysocki 			dn = to_acpi_data_node(child);
1237504a3374SRafael J. Wysocki 			next = dn->sibling.next;
1238504a3374SRafael J. Wysocki 			if (next == head)
1239504a3374SRafael J. Wysocki 				return NULL;
1240504a3374SRafael J. Wysocki 
1241504a3374SRafael J. Wysocki 			dn = list_entry(next, struct acpi_data_node, sibling);
1242504a3374SRafael J. Wysocki 		} else {
1243504a3374SRafael J. Wysocki 			dn = list_first_entry(head, struct acpi_data_node, sibling);
1244504a3374SRafael J. Wysocki 		}
1245504a3374SRafael J. Wysocki 		return &dn->fwnode;
1246504a3374SRafael J. Wysocki 	}
1247504a3374SRafael J. Wysocki 	return NULL;
1248504a3374SRafael J. Wysocki }
1249dfa672fbSMika Westerberg 
1250dfa672fbSMika Westerberg /**
1251dfa672fbSMika Westerberg  * acpi_node_get_parent - Return parent fwnode of this fwnode
1252dfa672fbSMika Westerberg  * @fwnode: Firmware node whose parent to get
1253dfa672fbSMika Westerberg  *
1254dfa672fbSMika Westerberg  * Returns parent node of an ACPI device or data firmware node or %NULL if
1255dfa672fbSMika Westerberg  * not available.
1256dfa672fbSMika Westerberg  */
1257985e9eceSSakari Ailus static struct fwnode_handle *
acpi_node_get_parent(const struct fwnode_handle * fwnode)1258985e9eceSSakari Ailus acpi_node_get_parent(const struct fwnode_handle *fwnode)
1259dfa672fbSMika Westerberg {
1260dfa672fbSMika Westerberg 	if (is_acpi_data_node(fwnode)) {
1261dfa672fbSMika Westerberg 		/* All data nodes have parent pointer so just return that */
1262dfa672fbSMika Westerberg 		return to_acpi_data_node(fwnode)->parent;
12639978f446SAndy Shevchenko 	}
12649978f446SAndy Shevchenko 	if (is_acpi_device_node(fwnode)) {
126598378956SRafael J. Wysocki 		struct acpi_device *parent;
1266dfa672fbSMika Westerberg 
126798378956SRafael J. Wysocki 		parent = acpi_dev_parent(to_acpi_device_node(fwnode));
126898378956SRafael J. Wysocki 		if (parent)
126998378956SRafael J. Wysocki 			return acpi_fwnode_handle(parent);
1270dfa672fbSMika Westerberg 	}
1271dfa672fbSMika Westerberg 
1272dfa672fbSMika Westerberg 	return NULL;
1273dfa672fbSMika Westerberg }
127479389a83SMika Westerberg 
127518f1e58dSSakari Ailus /*
127618f1e58dSSakari Ailus  * Return true if the node is an ACPI graph node. Called on either ports
127718f1e58dSSakari Ailus  * or endpoints.
127818f1e58dSSakari Ailus  */
is_acpi_graph_node(struct fwnode_handle * fwnode,const char * str)127918f1e58dSSakari Ailus static bool is_acpi_graph_node(struct fwnode_handle *fwnode,
128018f1e58dSSakari Ailus 			       const char *str)
128118f1e58dSSakari Ailus {
128218f1e58dSSakari Ailus 	unsigned int len = strlen(str);
128318f1e58dSSakari Ailus 	const char *name;
128418f1e58dSSakari Ailus 
128518f1e58dSSakari Ailus 	if (!len || !is_acpi_data_node(fwnode))
128618f1e58dSSakari Ailus 		return false;
128718f1e58dSSakari Ailus 
128818f1e58dSSakari Ailus 	name = to_acpi_data_node(fwnode)->name;
128918f1e58dSSakari Ailus 
129018f1e58dSSakari Ailus 	return (fwnode_property_present(fwnode, "reg") &&
129118f1e58dSSakari Ailus 		!strncmp(name, str, len) && name[len] == '@') ||
129218f1e58dSSakari Ailus 		fwnode_property_present(fwnode, str);
129318f1e58dSSakari Ailus }
129418f1e58dSSakari Ailus 
129579389a83SMika Westerberg /**
129679389a83SMika Westerberg  * acpi_graph_get_next_endpoint - Get next endpoint ACPI firmware node
129779389a83SMika Westerberg  * @fwnode: Pointer to the parent firmware node
129879389a83SMika Westerberg  * @prev: Previous endpoint node or %NULL to get the first
129979389a83SMika Westerberg  *
130079389a83SMika Westerberg  * Looks up next endpoint ACPI firmware node below a given @fwnode. Returns
13010ef74786SSakari Ailus  * %NULL if there is no next endpoint or in case of error. In case of success
13020ef74786SSakari Ailus  * the next endpoint is returned.
130379389a83SMika Westerberg  */
acpi_graph_get_next_endpoint(const struct fwnode_handle * fwnode,struct fwnode_handle * prev)13040ef74786SSakari Ailus static struct fwnode_handle *acpi_graph_get_next_endpoint(
130537ba983cSSakari Ailus 	const struct fwnode_handle *fwnode, struct fwnode_handle *prev)
130679389a83SMika Westerberg {
130779389a83SMika Westerberg 	struct fwnode_handle *port = NULL;
130879389a83SMika Westerberg 	struct fwnode_handle *endpoint;
130979389a83SMika Westerberg 
131079389a83SMika Westerberg 	if (!prev) {
131179389a83SMika Westerberg 		do {
131279389a83SMika Westerberg 			port = fwnode_get_next_child_node(fwnode, port);
131318f1e58dSSakari Ailus 			/*
131418f1e58dSSakari Ailus 			 * The names of the port nodes begin with "port@"
131518f1e58dSSakari Ailus 			 * followed by the number of the port node and they also
131618f1e58dSSakari Ailus 			 * have a "reg" property that also has the number of the
131718f1e58dSSakari Ailus 			 * port node. For compatibility reasons a node is also
131818f1e58dSSakari Ailus 			 * recognised as a port node from the "port" property.
131918f1e58dSSakari Ailus 			 */
132018f1e58dSSakari Ailus 			if (is_acpi_graph_node(port, "port"))
132179389a83SMika Westerberg 				break;
132279389a83SMika Westerberg 		} while (port);
132379389a83SMika Westerberg 	} else {
132479389a83SMika Westerberg 		port = fwnode_get_parent(prev);
132579389a83SMika Westerberg 	}
132679389a83SMika Westerberg 
132779389a83SMika Westerberg 	if (!port)
132879389a83SMika Westerberg 		return NULL;
132979389a83SMika Westerberg 
133079389a83SMika Westerberg 	endpoint = fwnode_get_next_child_node(port, prev);
133179389a83SMika Westerberg 	while (!endpoint) {
133279389a83SMika Westerberg 		port = fwnode_get_next_child_node(fwnode, port);
133379389a83SMika Westerberg 		if (!port)
133479389a83SMika Westerberg 			break;
133518f1e58dSSakari Ailus 		if (is_acpi_graph_node(port, "port"))
133679389a83SMika Westerberg 			endpoint = fwnode_get_next_child_node(port, NULL);
133779389a83SMika Westerberg 	}
133879389a83SMika Westerberg 
133918f1e58dSSakari Ailus 	/*
134018f1e58dSSakari Ailus 	 * The names of the endpoint nodes begin with "endpoint@" followed by
134118f1e58dSSakari Ailus 	 * the number of the endpoint node and they also have a "reg" property
134218f1e58dSSakari Ailus 	 * that also has the number of the endpoint node. For compatibility
134318f1e58dSSakari Ailus 	 * reasons a node is also recognised as an endpoint node from the
134418f1e58dSSakari Ailus 	 * "endpoint" property.
134518f1e58dSSakari Ailus 	 */
134618f1e58dSSakari Ailus 	if (!is_acpi_graph_node(endpoint, "endpoint"))
13470ef74786SSakari Ailus 		return NULL;
134879389a83SMika Westerberg 
134979389a83SMika Westerberg 	return endpoint;
135079389a83SMika Westerberg }
135179389a83SMika Westerberg 
135279389a83SMika Westerberg /**
135379389a83SMika Westerberg  * acpi_graph_get_child_prop_value - Return a child with a given property value
135479389a83SMika Westerberg  * @fwnode: device fwnode
135579389a83SMika Westerberg  * @prop_name: The name of the property to look for
135679389a83SMika Westerberg  * @val: the desired property value
135779389a83SMika Westerberg  *
135879389a83SMika Westerberg  * Return the port node corresponding to a given port number. Returns
135979389a83SMika Westerberg  * the child node on success, NULL otherwise.
136079389a83SMika Westerberg  */
acpi_graph_get_child_prop_value(const struct fwnode_handle * fwnode,const char * prop_name,unsigned int val)136179389a83SMika Westerberg static struct fwnode_handle *acpi_graph_get_child_prop_value(
136237ba983cSSakari Ailus 	const struct fwnode_handle *fwnode, const char *prop_name,
136337ba983cSSakari Ailus 	unsigned int val)
136479389a83SMika Westerberg {
136579389a83SMika Westerberg 	struct fwnode_handle *child;
136679389a83SMika Westerberg 
136779389a83SMika Westerberg 	fwnode_for_each_child_node(fwnode, child) {
136879389a83SMika Westerberg 		u32 nr;
136979389a83SMika Westerberg 
1370b5212f57SSakari Ailus 		if (fwnode_property_read_u32(child, prop_name, &nr))
137179389a83SMika Westerberg 			continue;
137279389a83SMika Westerberg 
137379389a83SMika Westerberg 		if (val == nr)
137479389a83SMika Westerberg 			return child;
137579389a83SMika Westerberg 	}
137679389a83SMika Westerberg 
137779389a83SMika Westerberg 	return NULL;
137879389a83SMika Westerberg }
137979389a83SMika Westerberg 
138079389a83SMika Westerberg 
138179389a83SMika Westerberg /**
13823a2650a8SGeert Uytterhoeven  * acpi_graph_get_remote_endpoint - Parses and returns remote end of an endpoint
13831de359d8SAndy Shevchenko  * @__fwnode: Endpoint firmware node pointing to a remote device
138479389a83SMika Westerberg  *
13850ef74786SSakari Ailus  * Returns the remote endpoint corresponding to @__fwnode. NULL on error.
138679389a83SMika Westerberg  */
13870ef74786SSakari Ailus static struct fwnode_handle *
acpi_graph_get_remote_endpoint(const struct fwnode_handle * __fwnode)13880ef74786SSakari Ailus acpi_graph_get_remote_endpoint(const struct fwnode_handle *__fwnode)
138979389a83SMika Westerberg {
139037ba983cSSakari Ailus 	struct fwnode_handle *fwnode;
139179389a83SMika Westerberg 	unsigned int port_nr, endpoint_nr;
1392977d5ad3SSakari Ailus 	struct fwnode_reference_args args;
139379389a83SMika Westerberg 	int ret;
139479389a83SMika Westerberg 
139579389a83SMika Westerberg 	memset(&args, 0, sizeof(args));
139637ba983cSSakari Ailus 	ret = acpi_node_get_property_reference(__fwnode, "remote-endpoint", 0,
139779389a83SMika Westerberg 					       &args);
139879389a83SMika Westerberg 	if (ret)
13990ef74786SSakari Ailus 		return NULL;
140079389a83SMika Westerberg 
14016561eb3dSSakari Ailus 	/* Direct endpoint reference? */
1402977d5ad3SSakari Ailus 	if (!is_acpi_device_node(args.fwnode))
14036561eb3dSSakari Ailus 		return args.nargs ? NULL : args.fwnode;
1404977d5ad3SSakari Ailus 
140579389a83SMika Westerberg 	/*
140679389a83SMika Westerberg 	 * Always require two arguments with the reference: port and
140779389a83SMika Westerberg 	 * endpoint indices.
140879389a83SMika Westerberg 	 */
140979389a83SMika Westerberg 	if (args.nargs != 2)
14100ef74786SSakari Ailus 		return NULL;
141179389a83SMika Westerberg 
1412977d5ad3SSakari Ailus 	fwnode = args.fwnode;
141379389a83SMika Westerberg 	port_nr = args.args[0];
141479389a83SMika Westerberg 	endpoint_nr = args.args[1];
141579389a83SMika Westerberg 
141679389a83SMika Westerberg 	fwnode = acpi_graph_get_child_prop_value(fwnode, "port", port_nr);
141779389a83SMika Westerberg 
141818f1e58dSSakari Ailus 	return acpi_graph_get_child_prop_value(fwnode, "endpoint", endpoint_nr);
141979389a83SMika Westerberg }
14203708184aSSakari Ailus 
acpi_fwnode_device_is_available(const struct fwnode_handle * fwnode)142137ba983cSSakari Ailus static bool acpi_fwnode_device_is_available(const struct fwnode_handle *fwnode)
14222294b3afSSakari Ailus {
14232294b3afSSakari Ailus 	if (!is_acpi_device_node(fwnode))
14242294b3afSSakari Ailus 		return false;
14252294b3afSSakari Ailus 
14262294b3afSSakari Ailus 	return acpi_device_is_present(to_acpi_device_node(fwnode));
14272294b3afSSakari Ailus }
14282294b3afSSakari Ailus 
142955dcbc05SSakari Ailus static const void *
acpi_fwnode_device_get_match_data(const struct fwnode_handle * fwnode,const struct device * dev)143055dcbc05SSakari Ailus acpi_fwnode_device_get_match_data(const struct fwnode_handle *fwnode,
143155dcbc05SSakari Ailus 				  const struct device *dev)
143255dcbc05SSakari Ailus {
143355dcbc05SSakari Ailus 	return acpi_device_get_match_data(dev);
143455dcbc05SSakari Ailus }
143555dcbc05SSakari Ailus 
acpi_fwnode_device_dma_supported(const struct fwnode_handle * fwnode)14368c756a0aSSakari Ailus static bool acpi_fwnode_device_dma_supported(const struct fwnode_handle *fwnode)
14378c756a0aSSakari Ailus {
14388c756a0aSSakari Ailus 	return acpi_dma_supported(to_acpi_device_node(fwnode));
14398c756a0aSSakari Ailus }
14408c756a0aSSakari Ailus 
14418c756a0aSSakari Ailus static enum dev_dma_attr
acpi_fwnode_device_get_dma_attr(const struct fwnode_handle * fwnode)14428c756a0aSSakari Ailus acpi_fwnode_device_get_dma_attr(const struct fwnode_handle *fwnode)
14438c756a0aSSakari Ailus {
14448c756a0aSSakari Ailus 	return acpi_get_dma_attr(to_acpi_device_node(fwnode));
14458c756a0aSSakari Ailus }
14468c756a0aSSakari Ailus 
acpi_fwnode_property_present(const struct fwnode_handle * fwnode,const char * propname)144737ba983cSSakari Ailus static bool acpi_fwnode_property_present(const struct fwnode_handle *fwnode,
14483708184aSSakari Ailus 					 const char *propname)
14493708184aSSakari Ailus {
14503708184aSSakari Ailus 	return !acpi_node_prop_get(fwnode, propname, NULL);
14513708184aSSakari Ailus }
14523708184aSSakari Ailus 
145337ba983cSSakari Ailus static int
acpi_fwnode_property_read_int_array(const struct fwnode_handle * fwnode,const char * propname,unsigned int elem_size,void * val,size_t nval)145437ba983cSSakari Ailus acpi_fwnode_property_read_int_array(const struct fwnode_handle *fwnode,
14553708184aSSakari Ailus 				    const char *propname,
145637ba983cSSakari Ailus 				    unsigned int elem_size, void *val,
145737ba983cSSakari Ailus 				    size_t nval)
14583708184aSSakari Ailus {
14593708184aSSakari Ailus 	enum dev_prop_type type;
14603708184aSSakari Ailus 
14613708184aSSakari Ailus 	switch (elem_size) {
14623708184aSSakari Ailus 	case sizeof(u8):
14633708184aSSakari Ailus 		type = DEV_PROP_U8;
14643708184aSSakari Ailus 		break;
14653708184aSSakari Ailus 	case sizeof(u16):
14663708184aSSakari Ailus 		type = DEV_PROP_U16;
14673708184aSSakari Ailus 		break;
14683708184aSSakari Ailus 	case sizeof(u32):
14693708184aSSakari Ailus 		type = DEV_PROP_U32;
14703708184aSSakari Ailus 		break;
14713708184aSSakari Ailus 	case sizeof(u64):
14723708184aSSakari Ailus 		type = DEV_PROP_U64;
14733708184aSSakari Ailus 		break;
14743708184aSSakari Ailus 	default:
14753708184aSSakari Ailus 		return -ENXIO;
14763708184aSSakari Ailus 	}
14773708184aSSakari Ailus 
14783708184aSSakari Ailus 	return acpi_node_prop_read(fwnode, propname, type, val, nval);
14793708184aSSakari Ailus }
14803708184aSSakari Ailus 
148137ba983cSSakari Ailus static int
acpi_fwnode_property_read_string_array(const struct fwnode_handle * fwnode,const char * propname,const char ** val,size_t nval)148237ba983cSSakari Ailus acpi_fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
148337ba983cSSakari Ailus 				       const char *propname, const char **val,
148437ba983cSSakari Ailus 				       size_t nval)
14853708184aSSakari Ailus {
14863708184aSSakari Ailus 	return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
14873708184aSSakari Ailus 				   val, nval);
14883708184aSSakari Ailus }
14893708184aSSakari Ailus 
14903e3119d3SSakari Ailus static int
acpi_fwnode_get_reference_args(const struct fwnode_handle * fwnode,const char * prop,const char * nargs_prop,unsigned int args_count,unsigned int index,struct fwnode_reference_args * args)14913e3119d3SSakari Ailus acpi_fwnode_get_reference_args(const struct fwnode_handle *fwnode,
14923e3119d3SSakari Ailus 			       const char *prop, const char *nargs_prop,
14933e3119d3SSakari Ailus 			       unsigned int args_count, unsigned int index,
14943e3119d3SSakari Ailus 			       struct fwnode_reference_args *args)
14953e3119d3SSakari Ailus {
1496977d5ad3SSakari Ailus 	return __acpi_node_get_property_reference(fwnode, prop, index,
1497977d5ad3SSakari Ailus 						  args_count, args);
14983e3119d3SSakari Ailus }
14993e3119d3SSakari Ailus 
acpi_fwnode_get_name(const struct fwnode_handle * fwnode)1500bc0500c1SSakari Ailus static const char *acpi_fwnode_get_name(const struct fwnode_handle *fwnode)
1501bc0500c1SSakari Ailus {
1502bc0500c1SSakari Ailus 	const struct acpi_device *adev;
1503bc0500c1SSakari Ailus 	struct fwnode_handle *parent;
1504bc0500c1SSakari Ailus 
1505bc0500c1SSakari Ailus 	/* Is this the root node? */
1506bc0500c1SSakari Ailus 	parent = fwnode_get_parent(fwnode);
1507bc0500c1SSakari Ailus 	if (!parent)
1508bc0500c1SSakari Ailus 		return "\\";
1509bc0500c1SSakari Ailus 
1510bc0500c1SSakari Ailus 	fwnode_handle_put(parent);
1511bc0500c1SSakari Ailus 
1512bc0500c1SSakari Ailus 	if (is_acpi_data_node(fwnode)) {
1513bc0500c1SSakari Ailus 		const struct acpi_data_node *dn = to_acpi_data_node(fwnode);
1514bc0500c1SSakari Ailus 
1515bc0500c1SSakari Ailus 		return dn->name;
1516bc0500c1SSakari Ailus 	}
1517bc0500c1SSakari Ailus 
1518bc0500c1SSakari Ailus 	adev = to_acpi_device_node(fwnode);
1519bc0500c1SSakari Ailus 	if (WARN_ON(!adev))
1520bc0500c1SSakari Ailus 		return NULL;
1521bc0500c1SSakari Ailus 
1522bc0500c1SSakari Ailus 	return acpi_device_bid(adev);
1523bc0500c1SSakari Ailus }
1524bc0500c1SSakari Ailus 
1525e7e242bcSSakari Ailus static const char *
acpi_fwnode_get_name_prefix(const struct fwnode_handle * fwnode)1526e7e242bcSSakari Ailus acpi_fwnode_get_name_prefix(const struct fwnode_handle *fwnode)
1527e7e242bcSSakari Ailus {
1528e7e242bcSSakari Ailus 	struct fwnode_handle *parent;
1529e7e242bcSSakari Ailus 
1530e7e242bcSSakari Ailus 	/* Is this the root node? */
1531e7e242bcSSakari Ailus 	parent = fwnode_get_parent(fwnode);
1532e7e242bcSSakari Ailus 	if (!parent)
1533e7e242bcSSakari Ailus 		return "";
1534e7e242bcSSakari Ailus 
1535e7e242bcSSakari Ailus 	/* Is this 2nd node from the root? */
1536e7e242bcSSakari Ailus 	parent = fwnode_get_next_parent(parent);
1537e7e242bcSSakari Ailus 	if (!parent)
1538e7e242bcSSakari Ailus 		return "";
1539e7e242bcSSakari Ailus 
1540e7e242bcSSakari Ailus 	fwnode_handle_put(parent);
1541e7e242bcSSakari Ailus 
1542e7e242bcSSakari Ailus 	/* ACPI device or data node. */
1543e7e242bcSSakari Ailus 	return ".";
1544e7e242bcSSakari Ailus }
1545e7e242bcSSakari Ailus 
15463b27d00eSSakari Ailus static struct fwnode_handle *
acpi_fwnode_get_parent(struct fwnode_handle * fwnode)154737ba983cSSakari Ailus acpi_fwnode_get_parent(struct fwnode_handle *fwnode)
154837ba983cSSakari Ailus {
154937ba983cSSakari Ailus 	return acpi_node_get_parent(fwnode);
155037ba983cSSakari Ailus }
155137ba983cSSakari Ailus 
acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle * fwnode,struct fwnode_endpoint * endpoint)155237ba983cSSakari Ailus static int acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
15533b27d00eSSakari Ailus 					    struct fwnode_endpoint *endpoint)
15543b27d00eSSakari Ailus {
15553b27d00eSSakari Ailus 	struct fwnode_handle *port_fwnode = fwnode_get_parent(fwnode);
15563b27d00eSSakari Ailus 
15573b27d00eSSakari Ailus 	endpoint->local_fwnode = fwnode;
15583b27d00eSSakari Ailus 
155918f1e58dSSakari Ailus 	if (fwnode_property_read_u32(port_fwnode, "reg", &endpoint->port))
15603b27d00eSSakari Ailus 		fwnode_property_read_u32(port_fwnode, "port", &endpoint->port);
156118f1e58dSSakari Ailus 	if (fwnode_property_read_u32(fwnode, "reg", &endpoint->id))
15623b27d00eSSakari Ailus 		fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id);
15633b27d00eSSakari Ailus 
15643b27d00eSSakari Ailus 	return 0;
15653b27d00eSSakari Ailus }
15663b27d00eSSakari Ailus 
acpi_fwnode_irq_get(const struct fwnode_handle * fwnode,unsigned int index)156799c63707SSakari Ailus static int acpi_fwnode_irq_get(const struct fwnode_handle *fwnode,
156899c63707SSakari Ailus 			       unsigned int index)
156999c63707SSakari Ailus {
157099c63707SSakari Ailus 	struct resource res;
157199c63707SSakari Ailus 	int ret;
157299c63707SSakari Ailus 
157399c63707SSakari Ailus 	ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), index, &res);
157499c63707SSakari Ailus 	if (ret)
157599c63707SSakari Ailus 		return ret;
157699c63707SSakari Ailus 
157799c63707SSakari Ailus 	return res.start;
157899c63707SSakari Ailus }
157999c63707SSakari Ailus 
1580db3e50f3SSakari Ailus #define DECLARE_ACPI_FWNODE_OPS(ops) \
1581db3e50f3SSakari Ailus 	const struct fwnode_operations ops = {				\
1582db3e50f3SSakari Ailus 		.device_is_available = acpi_fwnode_device_is_available, \
1583146b4dbbSSinan Kaya 		.device_get_match_data = acpi_fwnode_device_get_match_data, \
15848c756a0aSSakari Ailus 		.device_dma_supported =				\
15858c756a0aSSakari Ailus 			acpi_fwnode_device_dma_supported,		\
15868c756a0aSSakari Ailus 		.device_get_dma_attr = acpi_fwnode_device_get_dma_attr,	\
1587db3e50f3SSakari Ailus 		.property_present = acpi_fwnode_property_present,	\
1588db3e50f3SSakari Ailus 		.property_read_int_array =				\
1589db3e50f3SSakari Ailus 			acpi_fwnode_property_read_int_array,		\
1590db3e50f3SSakari Ailus 		.property_read_string_array =				\
1591db3e50f3SSakari Ailus 			acpi_fwnode_property_read_string_array,		\
1592db3e50f3SSakari Ailus 		.get_parent = acpi_node_get_parent,			\
1593db3e50f3SSakari Ailus 		.get_next_child_node = acpi_get_next_subnode,		\
1594db3e50f3SSakari Ailus 		.get_named_child_node = acpi_fwnode_get_named_child_node, \
1595bc0500c1SSakari Ailus 		.get_name = acpi_fwnode_get_name,			\
1596e7e242bcSSakari Ailus 		.get_name_prefix = acpi_fwnode_get_name_prefix,		\
15973e3119d3SSakari Ailus 		.get_reference_args = acpi_fwnode_get_reference_args,	\
1598db3e50f3SSakari Ailus 		.graph_get_next_endpoint =				\
15990ef74786SSakari Ailus 			acpi_graph_get_next_endpoint,			\
1600db3e50f3SSakari Ailus 		.graph_get_remote_endpoint =				\
16010ef74786SSakari Ailus 			acpi_graph_get_remote_endpoint,			\
160237ba983cSSakari Ailus 		.graph_get_port_parent = acpi_fwnode_get_parent,	\
1603db3e50f3SSakari Ailus 		.graph_parse_endpoint = acpi_fwnode_graph_parse_endpoint, \
160499c63707SSakari Ailus 		.irq_get = acpi_fwnode_irq_get,				\
1605db3e50f3SSakari Ailus 	};								\
1606db3e50f3SSakari Ailus 	EXPORT_SYMBOL_GPL(ops)
1607db3e50f3SSakari Ailus 
1608db3e50f3SSakari Ailus DECLARE_ACPI_FWNODE_OPS(acpi_device_fwnode_ops);
1609db3e50f3SSakari Ailus DECLARE_ACPI_FWNODE_OPS(acpi_data_fwnode_ops);
1610db3e50f3SSakari Ailus const struct fwnode_operations acpi_static_fwnode_ops;
16119e987b70SJohn Hubbard 
is_acpi_device_node(const struct fwnode_handle * fwnode)16129e987b70SJohn Hubbard bool is_acpi_device_node(const struct fwnode_handle *fwnode)
16139e987b70SJohn Hubbard {
16149e987b70SJohn Hubbard 	return !IS_ERR_OR_NULL(fwnode) &&
16159e987b70SJohn Hubbard 		fwnode->ops == &acpi_device_fwnode_ops;
16169e987b70SJohn Hubbard }
16179e987b70SJohn Hubbard EXPORT_SYMBOL(is_acpi_device_node);
16189e987b70SJohn Hubbard 
is_acpi_data_node(const struct fwnode_handle * fwnode)16199e987b70SJohn Hubbard bool is_acpi_data_node(const struct fwnode_handle *fwnode)
16209e987b70SJohn Hubbard {
16219e987b70SJohn Hubbard 	return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &acpi_data_fwnode_ops;
16229e987b70SJohn Hubbard }
16239e987b70SJohn Hubbard EXPORT_SYMBOL(is_acpi_data_node);
1624