summaryrefslogtreecommitdiff
path: root/drivers/acpi
diff options
context:
space:
mode:
authorSakari Ailus <sakari.ailus@linux.intel.com>2017-06-06 12:37:37 +0300
committerTao Huang <huangtao@rock-chips.com>2017-11-27 14:12:20 +0800
commit2bf7ce381df119e76c19e02076559e2316887f62 (patch)
treecfbf9a52783da84330e3a9cc611cd7bd5db54145 /drivers/acpi
parent66769f86b566c26e6f96ae47807874038fca1cc9 (diff)
BACKPORT: device property: Move FW type specific functionality to FW specific files
The device and fwnode property API supports Devicetree, ACPI and pset properties. The implementation of this functionality for each firmware type was embedded in the fwnode property core. Move it out to firmware type specific locations, making it easier to maintain. Depends-on: ("of: Move OF property and graph API from base.c to property.c") Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> (cherry picked from commit 3708184afc77bb67709a67a35d9f367ebd32cbc4) Signed-off-by: Brian J Lovin <brian.j.lovin@intel.com> Signed-off-by: Marc Herbert <marc.herbert@intel.com> Conflicts: include/linux/acpi.h (Drop update to acpi_alloc_fwnode_static() which is neither present nor used anywhere in this version.) BUG=b:64133998 TEST=media device topology shows subdevs registered successfully TEST=no camera regression Change-Id: Ie432874df71c4af26ab0bd011145b6a120b88f8b Reviewed-on: https://chromium-review.googlesource.com/693676 Commit-Ready: Tomasz Figa <tfiga@chromium.org> Tested-by: Hyungwoo Yang <hyungwoo.yang@intel.com> Reviewed-by: Tomasz Figa <tfiga@chromium.org> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/property.c510
-rw-r--r--drivers/acpi/scan.c1
2 files changed, 416 insertions, 95 deletions
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 83102dae256c..14013f635db6 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -37,18 +37,19 @@ static const u8 ads_uuid[16] = {
static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
const union acpi_object *desc,
- struct acpi_device_data *data);
+ struct acpi_device_data *data,
+ struct fwnode_handle *parent);
static bool acpi_extract_properties(const union acpi_object *desc,
struct acpi_device_data *data);
-static bool acpi_nondev_subnode_ok(acpi_handle scope,
- const union acpi_object *link,
- struct list_head *list)
+static bool acpi_nondev_subnode_extract(const union acpi_object *desc,
+ acpi_handle handle,
+ const union acpi_object *link,
+ struct list_head *list,
+ struct fwnode_handle *parent)
{
- struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
struct acpi_data_node *dn;
- acpi_handle handle;
- acpi_status status;
+ bool result;
dn = kzalloc(sizeof(*dn), GFP_KERNEL);
if (!dn)
@@ -56,62 +57,127 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope,
dn->name = link->package.elements[0].string.pointer;
dn->fwnode.type = FWNODE_ACPI_DATA;
+ dn->fwnode.ops = &acpi_fwnode_ops;
+ dn->parent = parent;
INIT_LIST_HEAD(&dn->data.subnodes);
- status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
- &handle);
- if (ACPI_FAILURE(status))
- goto fail;
+ result = acpi_extract_properties(desc, &dn->data);
- status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
- ACPI_TYPE_PACKAGE);
- if (ACPI_FAILURE(status))
- goto fail;
+ if (handle) {
+ acpi_handle scope;
+ acpi_status status;
- if (acpi_extract_properties(buf.pointer, &dn->data))
- dn->handle = handle;
+ /*
+ * The scope for the subnode object lookup is the one of the
+ * namespace node (device) containing the object that has
+ * returned the package. That is, it's the scope of that
+ * object's parent.
+ */
+ status = acpi_get_parent(handle, &scope);
+ if (ACPI_SUCCESS(status)
+ && acpi_enumerate_nondev_subnodes(scope, desc, &dn->data,
+ &dn->fwnode))
+ result = true;
+ } else if (acpi_enumerate_nondev_subnodes(NULL, desc, &dn->data,
+ &dn->fwnode)) {
+ result = true;
+ }
- /*
- * The scope for the subnode object lookup is the one of the namespace
- * node (device) containing the object that has returned the package.
- * That is, it's the scope of that object's parent.
- */
- status = acpi_get_parent(handle, &scope);
- if (ACPI_SUCCESS(status)
- && acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data))
+ if (result) {
dn->handle = handle;
-
- if (dn->handle) {
- dn->data.pointer = buf.pointer;
+ dn->data.pointer = desc;
list_add_tail(&dn->sibling, list);
return true;
}
+ kfree(dn);
acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
+ return false;
+}
+
+static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
+ const union acpi_object *link,
+ struct list_head *list,
+ struct fwnode_handle *parent)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+ acpi_status status;
+
+ status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
+ ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
+ parent))
+ return true;
- fail:
ACPI_FREE(buf.pointer);
- kfree(dn);
return false;
}
+static bool acpi_nondev_subnode_ok(acpi_handle scope,
+ const union acpi_object *link,
+ struct list_head *list,
+ struct fwnode_handle *parent)
+{
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!scope)
+ return false;
+
+ status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
+ &handle);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ return acpi_nondev_subnode_data_ok(handle, link, list, parent);
+}
+
static int acpi_add_nondev_subnodes(acpi_handle scope,
const union acpi_object *links,
- struct list_head *list)
+ struct list_head *list,
+ struct fwnode_handle *parent)
{
bool ret = false;
int i;
for (i = 0; i < links->package.count; i++) {
- const union acpi_object *link;
+ const union acpi_object *link, *desc;
+ acpi_handle handle;
+ bool result;
link = &links->package.elements[i];
- /* Only two elements allowed, both must be strings. */
- if (link->package.count == 2
- && link->package.elements[0].type == ACPI_TYPE_STRING
- && link->package.elements[1].type == ACPI_TYPE_STRING
- && acpi_nondev_subnode_ok(scope, link, list))
- ret = true;
+ /* Only two elements allowed. */
+ if (link->package.count != 2)
+ continue;
+
+ /* The first one must be a string. */
+ if (link->package.elements[0].type != ACPI_TYPE_STRING)
+ continue;
+
+ /* The second one may be a string, a reference or a package. */
+ switch (link->package.elements[1].type) {
+ case ACPI_TYPE_STRING:
+ result = acpi_nondev_subnode_ok(scope, link, list,
+ parent);
+ break;
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ handle = link->package.elements[1].reference.handle;
+ result = acpi_nondev_subnode_data_ok(handle, link, list,
+ parent);
+ break;
+ case ACPI_TYPE_PACKAGE:
+ desc = &link->package.elements[1];
+ result = acpi_nondev_subnode_extract(desc, NULL, link,
+ list, parent);
+ break;
+ default:
+ result = false;
+ break;
+ }
+ ret = ret || result;
}
return ret;
@@ -119,7 +185,8 @@ static int acpi_add_nondev_subnodes(acpi_handle scope,
static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
const union acpi_object *desc,
- struct acpi_device_data *data)
+ struct acpi_device_data *data,
+ struct fwnode_handle *parent)
{
int i;
@@ -141,7 +208,8 @@ static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
if (memcmp(uuid->buffer.pointer, ads_uuid, sizeof(ads_uuid)))
continue;
- return acpi_add_nondev_subnodes(scope, links, &data->subnodes);
+ return acpi_add_nondev_subnodes(scope, links, &data->subnodes,
+ parent);
}
return false;
@@ -292,7 +360,8 @@ void acpi_init_properties(struct acpi_device *adev)
if (acpi_of)
acpi_init_of_compatible(adev);
}
- if (acpi_enumerate_nondev_subnodes(adev->handle, buf.pointer, &adev->data))
+ if (acpi_enumerate_nondev_subnodes(adev->handle, buf.pointer,
+ &adev->data, acpi_fwnode_handle(adev)))
adev->data.pointer = buf.pointer;
if (!adev->data.pointer) {
@@ -346,7 +415,7 @@ void acpi_free_properties(struct acpi_device *adev)
*
* Return: %0 if property with @name has been found (success),
* %-EINVAL if the arguments are invalid,
- * %-ENODATA if the property doesn't exist,
+ * %-EINVAL if the property doesn't exist,
* %-EPROTO if the property value type doesn't match @type.
*/
static int acpi_data_get_property(struct acpi_device_data *data,
@@ -360,7 +429,7 @@ static int acpi_data_get_property(struct acpi_device_data *data,
return -EINVAL;
if (!data->pointer || !data->properties)
- return -ENODATA;
+ return -EINVAL;
properties = data->properties;
for (i = 0; i < properties->package.count; i++) {
@@ -375,13 +444,13 @@ static int acpi_data_get_property(struct acpi_device_data *data,
if (!strcmp(name, propname->string.pointer)) {
if (type != ACPI_TYPE_ANY && propvalue->type != type)
return -EPROTO;
- else if (obj)
+ if (obj)
*obj = propvalue;
return 0;
}
}
- return -ENODATA;
+ return -EINVAL;
}
/**
@@ -439,7 +508,7 @@ int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname,
*
* Return: %0 if array property (package) with @name has been found (success),
* %-EINVAL if the arguments are invalid,
- * %-ENODATA if the property doesn't exist,
+ * %-EINVAL if the property doesn't exist,
* %-EPROTO if the property is not a package or the type of its elements
* doesn't match @type.
*/
@@ -468,10 +537,11 @@ static int acpi_data_get_property_array(struct acpi_device_data *data,
}
/**
- * acpi_data_get_property_reference - returns handle to the referenced object
- * @data: ACPI device data object containing the property
+ * __acpi_node_get_property_reference - returns handle to the referenced object
+ * @fwnode: Firmware node to get the property from
* @propname: Name of the property
* @index: Index of the reference to return
+ * @num_args: Maximum number of arguments after each reference
* @args: Location to store the returned reference with optional arguments
*
* Find property with @name, verifify that it is a package containing at least
@@ -482,17 +552,40 @@ static int acpi_data_get_property_array(struct acpi_device_data *data,
* If there's more than one reference in the property value package, @index is
* used to select the one to return.
*
+ * It is possible to leave holes in the property value set like in the
+ * example below:
+ *
+ * Package () {
+ * "cs-gpios",
+ * Package () {
+ * ^GPIO, 19, 0, 0,
+ * ^GPIO, 20, 0, 0,
+ * 0,
+ * ^GPIO, 21, 0, 0,
+ * }
+ * }
+ *
+ * Calling this function with index %2 return %-ENOENT and with index %3
+ * returns the last entry. If the property does not contain any more values
+ * %-ENODATA is returned. The NULL entry must be single integer and
+ * preferably contain value %0.
+ *
* Return: %0 on success, negative error code on failure.
*/
-static int acpi_data_get_property_reference(struct acpi_device_data *data,
- const char *propname, size_t index,
- struct acpi_reference_args *args)
+int __acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+ const char *propname, size_t index, size_t num_args,
+ struct acpi_reference_args *args)
{
const union acpi_object *element, *end;
const union acpi_object *obj;
+ struct acpi_device_data *data;
struct acpi_device *device;
int ret, idx = 0;
+ data = acpi_device_data_of_node(fwnode);
+ if (!data)
+ return -EINVAL;
+
ret = acpi_data_get_property(data, propname, ACPI_TYPE_ANY, &obj);
if (ret)
return ret;
@@ -532,59 +625,54 @@ static int acpi_data_get_property_reference(struct acpi_device_data *data,
while (element < end) {
u32 nargs, i;
- if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
- return -EPROTO;
-
- ret = acpi_bus_get_device(element->reference.handle, &device);
- if (ret)
- return -ENODEV;
-
- element++;
- nargs = 0;
-
- /* assume following integer elements are all args */
- for (i = 0; element + i < end; i++) {
- int type = element[i].type;
+ if (element->type == ACPI_TYPE_LOCAL_REFERENCE) {
+ ret = acpi_bus_get_device(element->reference.handle,
+ &device);
+ if (ret)
+ return -ENODEV;
+
+ nargs = 0;
+ element++;
+
+ /* assume following integer elements are all args */
+ for (i = 0; element + i < end && i < num_args; i++) {
+ int type = element[i].type;
+
+ if (type == ACPI_TYPE_INTEGER)
+ nargs++;
+ else if (type == ACPI_TYPE_LOCAL_REFERENCE)
+ break;
+ else
+ return -EPROTO;
+ }
- if (type == ACPI_TYPE_INTEGER)
- nargs++;
- else if (type == ACPI_TYPE_LOCAL_REFERENCE)
- break;
- else
+ if (nargs > MAX_ACPI_REFERENCE_ARGS)
return -EPROTO;
- }
- if (idx++ == index) {
- args->adev = device;
- args->nargs = nargs;
- for (i = 0; i < nargs; i++)
- args->args[i] = element[i].integer.value;
+ if (idx == index) {
+ args->adev = device;
+ args->nargs = nargs;
+ for (i = 0; i < nargs; i++)
+ args->args[i] = element[i].integer.value;
- return 0;
+ return 0;
+ }
+
+ element += nargs;
+ } else if (element->type == ACPI_TYPE_INTEGER) {
+ if (idx == index)
+ return -ENOENT;
+ element++;
+ } else {
+ return -EPROTO;
}
- element += nargs;
+ idx++;
}
- return -EPROTO;
-}
-
-/**
- * acpi_node_get_property_reference - get a handle to the referenced object.
- * @fwnode: Firmware node to get the property from.
- * @propname: Name of the property.
- * @index: Index of the reference to return.
- * @args: Location to store the returned reference with optional arguments.
- */
-int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
- const char *name, size_t index,
- struct acpi_reference_args *args)
-{
- struct acpi_device_data *data = acpi_device_data_of_node(fwnode);
-
- return data ? acpi_data_get_property_reference(data, name, index, args) : -EINVAL;
+ return -ENODATA;
}
-EXPORT_SYMBOL_GPL(acpi_node_get_property_reference);
+EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
static int acpi_data_prop_read_single(struct acpi_device_data *data,
const char *propname,
@@ -867,3 +955,235 @@ struct fwnode_handle *acpi_get_next_subnode(struct fwnode_handle *fwnode,
}
return NULL;
}
+
+/**
+ * acpi_node_get_parent - Return parent fwnode of this fwnode
+ * @fwnode: Firmware node whose parent to get
+ *
+ * Returns parent node of an ACPI device or data firmware node or %NULL if
+ * not available.
+ */
+struct fwnode_handle *acpi_node_get_parent(struct fwnode_handle *fwnode)
+{
+ if (is_acpi_data_node(fwnode)) {
+ /* All data nodes have parent pointer so just return that */
+ return to_acpi_data_node(fwnode)->parent;
+ } else if (is_acpi_device_node(fwnode)) {
+ acpi_handle handle, parent_handle;
+
+ handle = to_acpi_device_node(fwnode)->handle;
+ if (ACPI_SUCCESS(acpi_get_parent(handle, &parent_handle))) {
+ struct acpi_device *adev;
+
+ if (!acpi_bus_get_device(parent_handle, &adev))
+ return acpi_fwnode_handle(adev);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * acpi_graph_get_next_endpoint - Get next endpoint ACPI firmware node
+ * @fwnode: Pointer to the parent firmware node
+ * @prev: Previous endpoint node or %NULL to get the first
+ *
+ * Looks up next endpoint ACPI firmware node below a given @fwnode. Returns
+ * %NULL if there is no next endpoint, ERR_PTR() in case of error. In case
+ * of success the next endpoint is returned.
+ */
+struct fwnode_handle *acpi_graph_get_next_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_handle *prev)
+{
+ struct fwnode_handle *port = NULL;
+ struct fwnode_handle *endpoint;
+
+ if (!prev) {
+ do {
+ port = fwnode_get_next_child_node(fwnode, port);
+ /* Ports must have port property */
+ if (fwnode_property_present(port, "port"))
+ break;
+ } while (port);
+ } else {
+ port = fwnode_get_parent(prev);
+ }
+
+ if (!port)
+ return NULL;
+
+ endpoint = fwnode_get_next_child_node(port, prev);
+ while (!endpoint) {
+ port = fwnode_get_next_child_node(fwnode, port);
+ if (!port)
+ break;
+ if (fwnode_property_present(port, "port"))
+ endpoint = fwnode_get_next_child_node(port, NULL);
+ }
+
+ if (endpoint) {
+ /* Endpoints must have "endpoint" property */
+ if (!fwnode_property_present(endpoint, "endpoint"))
+ return ERR_PTR(-EPROTO);
+ }
+
+ return endpoint;
+}
+
+/**
+ * acpi_graph_get_child_prop_value - Return a child with a given property value
+ * @fwnode: device fwnode
+ * @prop_name: The name of the property to look for
+ * @val: the desired property value
+ *
+ * Return the port node corresponding to a given port number. Returns
+ * the child node on success, NULL otherwise.
+ */
+static struct fwnode_handle *acpi_graph_get_child_prop_value(
+ struct fwnode_handle *fwnode, const char *prop_name, unsigned int val)
+{
+ struct fwnode_handle *child;
+
+ fwnode_for_each_child_node(fwnode, child) {
+ u32 nr;
+
+ if (!fwnode_property_read_u32(fwnode, prop_name, &nr))
+ continue;
+
+ if (val == nr)
+ return child;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * acpi_graph_get_remote_enpoint - Parses and returns remote end of an endpoint
+ * @fwnode: Endpoint firmware node pointing to a remote device
+ * @parent: Firmware node of remote port parent is filled here if not %NULL
+ * @port: Firmware node of remote port is filled here if not %NULL
+ * @endpoint: Firmware node of remote endpoint is filled here if not %NULL
+ *
+ * Function parses remote end of ACPI firmware remote endpoint and fills in
+ * fields requested by the caller. Returns %0 in case of success and
+ * negative errno otherwise.
+ */
+int acpi_graph_get_remote_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_handle **parent,
+ struct fwnode_handle **port,
+ struct fwnode_handle **endpoint)
+{
+ unsigned int port_nr, endpoint_nr;
+ struct acpi_reference_args args;
+ int ret;
+
+ memset(&args, 0, sizeof(args));
+ ret = acpi_node_get_property_reference(fwnode, "remote-endpoint", 0,
+ &args);
+ if (ret)
+ return ret;
+
+ /*
+ * Always require two arguments with the reference: port and
+ * endpoint indices.
+ */
+ if (args.nargs != 2)
+ return -EPROTO;
+
+ fwnode = acpi_fwnode_handle(args.adev);
+ port_nr = args.args[0];
+ endpoint_nr = args.args[1];
+
+ if (parent)
+ *parent = fwnode;
+
+ if (!port && !endpoint)
+ return 0;
+
+ fwnode = acpi_graph_get_child_prop_value(fwnode, "port", port_nr);
+ if (!fwnode)
+ return -EPROTO;
+
+ if (port)
+ *port = fwnode;
+
+ if (!endpoint)
+ return 0;
+
+ fwnode = acpi_graph_get_child_prop_value(fwnode, "endpoint",
+ endpoint_nr);
+ if (!fwnode)
+ return -EPROTO;
+
+ *endpoint = fwnode;
+
+ return 0;
+}
+
+static bool acpi_fwnode_property_present(struct fwnode_handle *fwnode,
+ const char *propname)
+{
+ return !acpi_node_prop_get(fwnode, propname, NULL);
+}
+
+static int acpi_fwnode_property_read_int_array(struct fwnode_handle *fwnode,
+ const char *propname,
+ unsigned int elem_size,
+ void *val, size_t nval)
+{
+ enum dev_prop_type type;
+
+ switch (elem_size) {
+ case sizeof(u8):
+ type = DEV_PROP_U8;
+ break;
+ case sizeof(u16):
+ type = DEV_PROP_U16;
+ break;
+ case sizeof(u32):
+ type = DEV_PROP_U32;
+ break;
+ case sizeof(u64):
+ type = DEV_PROP_U64;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return acpi_node_prop_read(fwnode, propname, type, val, nval);
+}
+
+static int acpi_fwnode_property_read_string_array(struct fwnode_handle *fwnode,
+ const char *propname,
+ const char **val, size_t nval)
+{
+ return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
+ val, nval);
+}
+
+static struct fwnode_handle *
+acpi_fwnode_get_named_child_node(struct fwnode_handle *fwnode,
+ const char *childname)
+{
+ struct fwnode_handle *child;
+
+ /*
+ * Find first matching named child node of this fwnode.
+ * For ACPI this will be a data only sub-node.
+ */
+ fwnode_for_each_child_node(fwnode, child)
+ if (acpi_data_node_match(child, childname))
+ return child;
+
+ return NULL;
+}
+
+const struct fwnode_operations acpi_fwnode_ops = {
+ .property_present = acpi_fwnode_property_present,
+ .property_read_int_array = acpi_fwnode_property_read_int_array,
+ .property_read_string_array = acpi_fwnode_property_read_string_array,
+ .get_parent = acpi_node_get_parent,
+ .get_next_child_node = acpi_get_next_subnode,
+ .get_named_child_node = acpi_fwnode_get_named_child_node,
+};
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index dcb3d6245ca5..8f51dd2cead7 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1391,6 +1391,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
device->handle = handle;
device->parent = acpi_bus_get_parent(handle);
device->fwnode.type = FWNODE_ACPI;
+ device->fwnode.ops = &acpi_fwnode_ops;
acpi_set_device_status(device, sta);
acpi_device_get_busid(device);
acpi_set_pnp_ids(handle, &device->pnp, type);