[PATCH 08/11] ACPI: property: Parse data node string references in properties

Sakari Ailus
2022-05-06T12:58:43Z

Add support for parsing property references using strings, besides
reference objects that were previously supported. This allows also
referencing data nodes which was not possible with reference objects.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
drivers/acpi/property.c | 103 ++++++++++++++++++++++++++++++++++------
1 file changed, 89 insertions(+), 14 deletions(-)

diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index a8e8a214a524f..9325a9b2c0543 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -677,7 +677,8 @@ static int
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)
+ const union acpi_object *end, size_t num_args,
+ bool subnode_string)
{
u32 nargs = 0, i;

@@ -685,14 +686,17 @@ acpi_get_ref_args(struct fwnode_reference_args *args,
* Find the referred data extension node under the
* referred device node.
*/
- for (; *element < end && (*element)->type == ACPI_TYPE_STRING;
- (*element)++) {
- const char *child_name = (*element)->string.pointer;
-
- ref_fwnode = acpi_fwnode_get_named_child_node(ref_fwnode,
- child_name);
- if (!ref_fwnode)
- return -EINVAL;
+ if (subnode_string) {
+ for (; *element < end && (*element)->type == ACPI_TYPE_STRING;
+ (*element)++) {
+ const char *child_name = (*element)->string.pointer;
+
+ ref_fwnode =
+ acpi_fwnode_get_named_child_node(ref_fwnode,
+ child_name);
+ if (!ref_fwnode)
+ return -EINVAL;
+ }
}

/*
@@ -703,7 +707,8 @@ acpi_get_ref_args(struct fwnode_reference_args *args,
for (i = 0; (*element) + i < end && i < num_args; i++) {
acpi_object_type type = (*element)[i].type;

- if (type == ACPI_TYPE_LOCAL_REFERENCE)
+ if (type == ACPI_TYPE_LOCAL_REFERENCE ||
+ (!subnode_string && type == ACPI_TYPE_STRING))
break;

if (type == ACPI_TYPE_INTEGER)
@@ -727,6 +732,43 @@ acpi_get_ref_args(struct fwnode_reference_args *args,
return 0;
}

+static struct fwnode_handle *
+acpi_parse_string_ref(const struct fwnode_handle *fwnode, const char *refstring)
+{
+ acpi_handle scope, handle;
+ struct acpi_data_node *dn;
+ struct acpi_device *device;
+ acpi_status status;
+
+ if (is_acpi_device_node(fwnode)) {
+ scope = to_acpi_device_node(fwnode)->handle;
+ } else if (is_acpi_data_node(fwnode)) {
+ scope = to_acpi_data_node(fwnode)->handle;
+ } else {
+ pr_err("ACPI: bad node type\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ status = acpi_get_handle(scope, refstring, &handle);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(scope, "can't get handle for %s", refstring);
+ return ERR_PTR(-EINVAL);
+ }
+
+ device = acpi_fetch_acpi_dev(handle);
+ if (device)
+ return acpi_fwnode_handle(device);
+
+ status = acpi_get_data_full(handle, acpi_nondev_subnode_tag,
+ (void **)&dn, NULL);
+ if (ACPI_FAILURE(status) || !dn) {
+ acpi_handle_debug(handle, "can't find subnode");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &dn->fwnode;
+}
+
/**
* __acpi_node_get_property_reference - returns handle to the referenced object
* @fwnode: Firmware node to get the property from
@@ -769,6 +811,7 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
const union acpi_object *element, *end;
const union acpi_object *obj;
const struct acpi_device_data *data;
+ struct fwnode_handle *ref_fwnode;
struct acpi_device *device;
int ret, idx = 0;

@@ -792,16 +835,29 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,

args->fwnode = acpi_fwnode_handle(device);
args->nargs = 0;
+ return 0;
+ case ACPI_TYPE_STRING:
+ if (index)
+ return -ENOENT;
+
+ ref_fwnode = acpi_parse_string_ref(fwnode, obj->string.pointer);
+ if (IS_ERR(ref_fwnode))
+ return PTR_ERR(ref_fwnode);
+
+ args->fwnode = ref_fwnode;
+ args->nargs = 0;
+
return 0;
case ACPI_TYPE_PACKAGE:
/*
* If it is not a single reference, then it is a package of
- * references followed by number of ints as follows:
+ * references, followed by number of ints as follows:
*
* Package () { REF, INT, REF, INT, INT }
*
- * The index argument is then used to determine which reference
- * the caller wants (along with the arguments).
+ * Here, REF may be either a local reference or a string. The
+ * index argument is then used to determine which reference the
+ * caller wants (along with the arguments).
*/
break;
default:
@@ -825,7 +881,26 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,

ret = acpi_get_ref_args(idx == index ? args : NULL,
acpi_fwnode_handle(device),
- &element, end, num_args);
+ &element, end, num_args, true);
+ if (ret < 0)
+ return ret;
+
+ if (idx == index)
+ return 0;
+
+ break;
+ case ACPI_TYPE_STRING:
+ ref_fwnode =
+ acpi_parse_string_ref(fwnode,
+ element->string.pointer);
+ if (IS_ERR(ref_fwnode))
+ return PTR_ERR(ref_fwnode);
+
+ element++;
+
+ ret = acpi_get_ref_args(idx == index ? args : NULL,
+ ref_fwnode, &element, end,
+ num_args, false);
if (ret < 0)
return ret;

--
2.30.2