summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-02 00:18:05 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-02 00:18:05 +0300
commit5fab10041b4389b61de7e7a49893190bae686241 (patch)
tree36538b7dc537a75a6a05e4b7a7aecf06658ff100
parent08be881064da126b8df4b96b0b3e2e307969a4a1 (diff)
parente48174779440c79ff5dd75f0e6b169d4e184262d (diff)
downloadlinux-5fab10041b4389b61de7e7a49893190bae686241.tar.xz
Merge tag 'devprop-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull generic device properties framework updates from Rafael Wysocki: "These add support for the ports and endpoints concepts, based on the existing DT support for them, to the generic device properties framework and update the ACPI _DSD properties code to recognize ports and endpoints accordingly. Specifics: - Extend the ACPI _DSD properties code and the generic device properties framework to support the concept of remote endponts (Mika Westerberg, Sakari Ailus). - Document the support for ports and endpoints in _DSD properties and extend the generic device properties framework to make it more suitable for the handling of ports and endpoints (Sakari Ailus)" * tag 'devprop-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: device property: Read strings using string array reading functions device property: fwnode_property_read_string_array() returns nr of strings device property: Fix reading pset strings using array access functions device property: fwnode_property_read_string_array() may return -EILSEQ ACPI / DSD: Document references, ports and endpoints device property: Add fwnode_get_next_parent() device property: Add support for fwnode endpoints device property: Make dev_fwnode() public of: Add of_fwnode_handle() to convert device nodes to fwnode_handle device property: Add fwnode_handle_get() device property: Add support for remote endpoints ACPI / property: Add support for remote endpoints device property: Add fwnode_get_named_child_node() ACPI / property: Add fwnode_get_next_child_node() device property: Add fwnode_get_parent() ACPI / property: Add possiblity to retrieve parent firmware node
-rw-r--r--Documentation/acpi/dsd/graph.txt162
-rw-r--r--drivers/acpi/property.c259
-rw-r--r--drivers/base/property.c364
-rw-r--r--include/acpi/acpi_bus.h1
-rw-r--r--include/linux/acpi.h38
-rw-r--r--include/linux/fwnode.h12
-rw-r--r--include/linux/of.h4
-rw-r--r--include/linux/property.h26
8 files changed, 761 insertions, 105 deletions
diff --git a/Documentation/acpi/dsd/graph.txt b/Documentation/acpi/dsd/graph.txt
new file mode 100644
index 000000000000..ac09e3138b79
--- /dev/null
+++ b/Documentation/acpi/dsd/graph.txt
@@ -0,0 +1,162 @@
+Graphs
+
+
+_DSD
+----
+
+_DSD (Device Specific Data) [7] is a predefined ACPI device
+configuration object that can be used to convey information on
+hardware features which are not specifically covered by the ACPI
+specification [1][6]. There are two _DSD extensions that are relevant
+for graphs: property [4] and hierarchical data extensions [5]. The
+property extension provides generic key-value pairs whereas the
+hierarchical data extension supports nodes with references to other
+nodes, forming a tree. The nodes in the tree may contain properties as
+defined by the property extension. The two extensions together provide
+a tree-like structure with zero or more properties (key-value pairs)
+in each node of the tree.
+
+The data structure may be accessed at runtime by using the device_*
+and fwnode_* functions defined in include/linux/fwnode.h .
+
+Fwnode represents a generic firmware node object. It is independent on
+the firmware type. In ACPI, fwnodes are _DSD hierarchical data
+extensions objects. A device's _DSD object is represented by an
+fwnode.
+
+The data structure may be referenced to elsewhere in the ACPI tables
+by using a hard reference to the device itself and an index to the
+hierarchical data extension array on each depth.
+
+
+Ports and endpoints
+-------------------
+
+The port and endpoint concepts are very similar to those in Devicetree
+[3]. A port represents an interface in a device, and an endpoint
+represents a connection to that interface.
+
+All port nodes are located under the device's "_DSD" node in the
+hierarchical data extension tree. The property extension related to
+each port node must contain the key "port" and an integer value which
+is the number of the port. The object it refers to should be called "PRTX",
+where "X" is the number of the port.
+
+Further on, endpoints are located under the individual port nodes. The
+first hierarchical data extension package list entry of the endpoint
+nodes must begin with "endpoint" and must be followed by the number
+of the endpoint. The object it refers to should be called "EPXY", where
+"X" is the number of the port and "Y" is the number of the endpoint.
+
+Each port node contains a property extension key "port", the value of
+which is the number of the port node. The each endpoint is similarly numbered
+with a property extension key "endpoint". Port numbers must be unique within a
+device and endpoint numbers must be unique within a port.
+
+The endpoint reference uses property extension with "remote-endpoint" property
+name followed by a reference in the same package. Such references consist of the
+the remote device reference, number of the port in the device and finally the
+number of the endpoint in that port. Individual references thus appear as:
+
+ Package() { device, port_number, endpoint_number }
+
+The references to endpoints must be always done both ways, to the
+remote endpoint and back from the referred remote endpoint node.
+
+A simple example of this is show below:
+
+ Scope (\_SB.PCI0.I2C2)
+ {
+ Device (CAM0)
+ {
+ Name (_DSD, Package () {
+ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+ Package () {
+ Package () { "compatible", Package () { "nokia,smia" } },
+ },
+ ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
+ Package () {
+ Package () { "port0", "PRT0" },
+ }
+ })
+ Name (PRT0, Package() {
+ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+ Package () {
+ Package () { "port", 0 },
+ },
+ ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
+ Package () {
+ Package () { "endpoint0", "EP00" },
+ }
+ })
+ Name (EP00, Package() {
+ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+ Package () {
+ Package () { "endpoint", 0 },
+ Package () { "remote-endpoint", Package() { \_SB.PCI0.ISP, 4, 0 } },
+ }
+ })
+ }
+ }
+
+ Scope (\_SB.PCI0)
+ {
+ Device (ISP)
+ {
+ Name (_DSD, Package () {
+ ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
+ Package () {
+ Package () { "port4", "PRT4" },
+ }
+ })
+
+ Name (PRT4, Package() {
+ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+ Package () {
+ Package () { "port", 4 }, /* CSI-2 port number */
+ },
+ ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
+ Package () {
+ Package () { "endpoint0", "EP40" },
+ }
+ })
+
+ Name (EP40, Package() {
+ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+ Package () {
+ Package () { "endpoint", 0 },
+ Package () { "remote-endpoint", Package () { \_SB.PCI0.I2C2.CAM0, 0, 0 } },
+ }
+ })
+ }
+ }
+
+Here, the port 0 of the "CAM0" device is connected to the port 4 of
+the "ISP" device and vice versa.
+
+
+References
+----------
+
+[1] _DSD (Device Specific Data) Implementation Guide.
+ <URL:http://www.uefi.org/sites/default/files/resources/_DSD-implementation-guide-toplevel-1_1.htm>,
+ referenced 2016-10-03.
+
+[2] Devicetree. <URL:http://www.devicetree.org>, referenced 2016-10-03.
+
+[3] Documentation/devicetree/bindings/graph.txt
+
+[4] Device Properties UUID For _DSD.
+ <URL:http://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf>,
+ referenced 2016-10-04.
+
+[5] Hierarchical Data Extension UUID For _DSD.
+ <URL:http://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.pdf>,
+ referenced 2016-10-04.
+
+[6] Advanced Configuration and Power Interface Specification.
+ <URL:http://www.uefi.org/sites/default/files/resources/ACPI_6_1.pdf>,
+ referenced 2016-10-04.
+
+[7] _DSD Device Properties Usage Rules.
+ Documentation/acpi/DSD-properties-rules.txt
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 3afddcd834ef..9364398204e9 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -37,14 +37,16 @@ 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_extract(const union acpi_object *desc,
acpi_handle handle,
const union acpi_object *link,
- struct list_head *list)
+ struct list_head *list,
+ struct fwnode_handle *parent)
{
struct acpi_data_node *dn;
bool result;
@@ -55,6 +57,7 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc,
dn->name = link->package.elements[0].string.pointer;
dn->fwnode.type = FWNODE_ACPI_DATA;
+ dn->parent = parent;
INIT_LIST_HEAD(&dn->data.subnodes);
result = acpi_extract_properties(desc, &dn->data);
@@ -71,9 +74,11 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc,
*/
status = acpi_get_parent(handle, &scope);
if (ACPI_SUCCESS(status)
- && acpi_enumerate_nondev_subnodes(scope, desc, &dn->data))
+ && acpi_enumerate_nondev_subnodes(scope, desc, &dn->data,
+ &dn->fwnode))
result = true;
- } else if (acpi_enumerate_nondev_subnodes(NULL, desc, &dn->data)) {
+ } else if (acpi_enumerate_nondev_subnodes(NULL, desc, &dn->data,
+ &dn->fwnode)) {
result = true;
}
@@ -91,7 +96,8 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc,
static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
const union acpi_object *link,
- struct list_head *list)
+ struct list_head *list,
+ struct fwnode_handle *parent)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
acpi_status status;
@@ -101,7 +107,8 @@ static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
if (ACPI_FAILURE(status))
return false;
- if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list))
+ if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
+ parent))
return true;
ACPI_FREE(buf.pointer);
@@ -110,7 +117,8 @@ static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
static bool acpi_nondev_subnode_ok(acpi_handle scope,
const union acpi_object *link,
- struct list_head *list)
+ struct list_head *list,
+ struct fwnode_handle *parent)
{
acpi_handle handle;
acpi_status status;
@@ -123,12 +131,13 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope,
if (ACPI_FAILURE(status))
return false;
- return acpi_nondev_subnode_data_ok(handle, link, list);
+ 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;
@@ -150,15 +159,18 @@ static int acpi_add_nondev_subnodes(acpi_handle scope,
/* 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);
+ 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);
+ 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);
+ result = acpi_nondev_subnode_extract(desc, NULL, link,
+ list, parent);
break;
default:
result = false;
@@ -172,7 +184,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;
@@ -194,7 +207,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;
@@ -345,7 +359,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) {
@@ -699,6 +714,8 @@ static int acpi_data_prop_read_single(struct acpi_device_data *data,
return ret;
*(char **)val = obj->string.pointer;
+
+ return 1;
} else {
ret = -EINVAL;
}
@@ -708,7 +725,15 @@ static int acpi_data_prop_read_single(struct acpi_device_data *data,
int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname,
enum dev_prop_type proptype, void *val)
{
- return adev ? acpi_data_prop_read_single(&adev->data, propname, proptype, val) : -EINVAL;
+ int ret;
+
+ if (!adev)
+ return -EINVAL;
+
+ ret = acpi_data_prop_read_single(&adev->data, propname, proptype, val);
+ if (ret < 0 || proptype != ACPI_TYPE_STRING)
+ return ret;
+ return 0;
}
static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val,
@@ -784,7 +809,7 @@ static int acpi_copy_property_array_string(const union acpi_object *items,
val[i] = items[i].string.pointer;
}
- return 0;
+ return nval;
}
static int acpi_data_prop_read(struct acpi_device_data *data,
@@ -798,7 +823,7 @@ static int acpi_data_prop_read(struct acpi_device_data *data,
if (val && nval == 1) {
ret = acpi_data_prop_read_single(data, propname, proptype, val);
- if (!ret)
+ if (ret >= 0)
return ret;
}
@@ -809,7 +834,7 @@ static int acpi_data_prop_read(struct acpi_device_data *data,
if (!val)
return obj->package.count;
- if (nval > obj->package.count)
+ if (proptype != DEV_PROP_STRING && nval > obj->package.count)
return -EOVERFLOW;
else if (nval <= 0)
return -EINVAL;
@@ -830,7 +855,9 @@ static int acpi_data_prop_read(struct acpi_device_data *data,
ret = acpi_copy_property_array_u64(items, (u64 *)val, nval);
break;
case DEV_PROP_STRING:
- ret = acpi_copy_property_array_string(items, (char **)val, nval);
+ ret = acpi_copy_property_array_string(
+ items, (char **)val,
+ min_t(u32, nval, obj->package.count));
break;
default:
ret = -EINVAL;
@@ -865,21 +892,22 @@ int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname,
}
/**
- * acpi_get_next_subnode - Return the next child node handle for a device.
- * @dev: Device to find the next child node for.
+ * acpi_get_next_subnode - Return the next child node handle for a fwnode
+ * @fwnode: Firmware node to find the next child node for.
* @child: Handle to one of the device's child nodes or a null handle.
*/
-struct fwnode_handle *acpi_get_next_subnode(struct device *dev,
+struct fwnode_handle *acpi_get_next_subnode(struct fwnode_handle *fwnode,
struct fwnode_handle *child)
{
- struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct acpi_device *adev = to_acpi_device_node(fwnode);
struct list_head *head, *next;
- if (!adev)
- return NULL;
-
if (!child || child->type == FWNODE_ACPI) {
- head = &adev->children;
+ if (adev)
+ head = &adev->children;
+ else
+ goto nondev;
+
if (list_empty(head))
goto nondev;
@@ -888,7 +916,6 @@ struct fwnode_handle *acpi_get_next_subnode(struct device *dev,
next = adev->node.next;
if (next == head) {
child = NULL;
- adev = ACPI_COMPANION(dev);
goto nondev;
}
adev = list_entry(next, struct acpi_device, node);
@@ -900,9 +927,16 @@ struct fwnode_handle *acpi_get_next_subnode(struct device *dev,
nondev:
if (!child || child->type == FWNODE_ACPI_DATA) {
+ struct acpi_data_node *data = to_acpi_data_node(fwnode);
struct acpi_data_node *dn;
- head = &adev->data.subnodes;
+ if (adev)
+ head = &adev->data.subnodes;
+ else if (data)
+ head = &data->data.subnodes;
+ else
+ return NULL;
+
if (list_empty(head))
return NULL;
@@ -920,3 +954,168 @@ struct fwnode_handle *acpi_get_next_subnode(struct device *dev,
}
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;
+}
diff --git a/drivers/base/property.c b/drivers/base/property.c
index c458c63e353f..149de311a10e 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_graph.h>
#include <linux/property.h>
#include <linux/etherdevice.h>
#include <linux/phy.h>
@@ -146,47 +147,45 @@ static int pset_prop_read_string_array(struct property_set *pset,
const char *propname,
const char **strings, size_t nval)
{
+ const struct property_entry *prop;
const void *pointer;
- size_t length = nval * sizeof(*strings);
+ size_t array_len, length;
+
+ /* Find out the array length. */
+ prop = pset_prop_get(pset, propname);
+ if (!prop)
+ return -EINVAL;
+
+ if (!prop->is_array)
+ /* The array length for a non-array string property is 1. */
+ array_len = 1;
+ else
+ /* Find the length of an array. */
+ array_len = pset_prop_count_elems_of_size(pset, propname,
+ sizeof(const char *));
+
+ /* Return how many there are if strings is NULL. */
+ if (!strings)
+ return array_len;
+
+ array_len = min(nval, array_len);
+ length = array_len * sizeof(*strings);
pointer = pset_prop_find(pset, propname, length);
if (IS_ERR(pointer))
return PTR_ERR(pointer);
memcpy(strings, pointer, length);
- return 0;
-}
-static int pset_prop_read_string(struct property_set *pset,
- const char *propname, const char **strings)
-{
- const struct property_entry *prop;
- const char * const *pointer;
-
- prop = pset_prop_get(pset, propname);
- if (!prop)
- return -EINVAL;
- if (!prop->is_string)
- return -EILSEQ;
- if (prop->is_array) {
- pointer = prop->pointer.str;
- if (!pointer)
- return -ENODATA;
- } else {
- pointer = &prop->value.str;
- if (*pointer && strnlen(*pointer, prop->length) >= prop->length)
- return -EILSEQ;
- }
-
- *strings = *pointer;
- return 0;
+ return array_len;
}
-static inline struct fwnode_handle *dev_fwnode(struct device *dev)
+struct fwnode_handle *dev_fwnode(struct device *dev)
{
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
&dev->of_node->fwnode : dev->fwnode;
}
+EXPORT_SYMBOL_GPL(dev_fwnode);
/**
* device_property_present - check if a property of a device is present
@@ -340,8 +339,8 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array);
* Function reads an array of string properties with @propname from the device
* firmware description and stores them to @val if found.
*
- * Return: number of values if @val was %NULL,
- * %0 if the property was found (success),
+ * Return: number of values read on success if @val is non-NULL,
+ * number of values available on success if @val is NULL,
* %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value,
* %-EPROTO or %-EILSEQ if the property is not an array of strings,
@@ -553,25 +552,8 @@ static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode,
return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
val, nval);
else if (is_pset_node(fwnode))
- return val ?
- pset_prop_read_string_array(to_pset_node(fwnode),
- propname, val, nval) :
- pset_prop_count_elems_of_size(to_pset_node(fwnode),
- propname,
- sizeof(const char *));
- return -ENXIO;
-}
-
-static int __fwnode_property_read_string(struct fwnode_handle *fwnode,
- const char *propname, const char **val)
-{
- if (is_of_node(fwnode))
- return of_property_read_string(to_of_node(fwnode), propname, val);
- else if (is_acpi_node(fwnode))
- return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
- val, 1);
- else if (is_pset_node(fwnode))
- return pset_prop_read_string(to_pset_node(fwnode), propname, val);
+ return pset_prop_read_string_array(to_pset_node(fwnode),
+ propname, val, nval);
return -ENXIO;
}
@@ -585,11 +567,11 @@ static int __fwnode_property_read_string(struct fwnode_handle *fwnode,
* Read an string list property @propname from the given firmware node and store
* them to @val if found.
*
- * Return: number of values if @val was %NULL,
- * %0 if the property was found (success),
+ * Return: number of values read on success if @val is non-NULL,
+ * number of values available on success if @val is NULL,
* %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value,
- * %-EPROTO if the property is not an array of strings,
+ * %-EPROTO or %-EILSEQ if the property is not an array of strings,
* %-EOVERFLOW if the size of the property is not as expected,
* %-ENXIO if no suitable firmware interface is present.
*/
@@ -626,14 +608,9 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
int fwnode_property_read_string(struct fwnode_handle *fwnode,
const char *propname, const char **val)
{
- int ret;
+ int ret = fwnode_property_read_string_array(fwnode, propname, val, 1);
- ret = __fwnode_property_read_string(fwnode, propname, val);
- if (ret == -EINVAL && !IS_ERR_OR_NULL(fwnode) &&
- !IS_ERR_OR_NULL(fwnode->secondary))
- ret = __fwnode_property_read_string(fwnode->secondary,
- propname, val);
- return ret;
+ return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL_GPL(fwnode_property_read_string);
@@ -932,41 +909,109 @@ int device_add_properties(struct device *dev,
EXPORT_SYMBOL_GPL(device_add_properties);
/**
- * device_get_next_child_node - Return the next child node handle for a device
- * @dev: Device to find the next child node for.
- * @child: Handle to one of the device's child nodes or a null handle.
+ * fwnode_get_next_parent - Iterate to the node's parent
+ * @fwnode: Firmware whose parent is retrieved
+ *
+ * This is like fwnode_get_parent() except that it drops the refcount
+ * on the passed node, making it suitable for iterating through a
+ * node's parents.
+ *
+ * Returns a node pointer with refcount incremented, use
+ * fwnode_handle_node() on it when done.
*/
-struct fwnode_handle *device_get_next_child_node(struct device *dev,
+struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *parent = fwnode_get_parent(fwnode);
+
+ fwnode_handle_put(fwnode);
+
+ return parent;
+}
+EXPORT_SYMBOL_GPL(fwnode_get_next_parent);
+
+/**
+ * fwnode_get_parent - Return parent firwmare node
+ * @fwnode: Firmware whose parent is retrieved
+ *
+ * Return parent firmware node of the given node if possible or %NULL if no
+ * parent was available.
+ */
+struct fwnode_handle *fwnode_get_parent(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *parent = NULL;
+
+ if (is_of_node(fwnode)) {
+ struct device_node *node;
+
+ node = of_get_parent(to_of_node(fwnode));
+ if (node)
+ parent = &node->fwnode;
+ } else if (is_acpi_node(fwnode)) {
+ parent = acpi_node_get_parent(fwnode);
+ }
+
+ return parent;
+}
+EXPORT_SYMBOL_GPL(fwnode_get_parent);
+
+/**
+ * fwnode_get_next_child_node - Return the next child node handle for a node
+ * @fwnode: Firmware node to find the next child node for.
+ * @child: Handle to one of the node's child nodes or a %NULL handle.
+ */
+struct fwnode_handle *fwnode_get_next_child_node(struct fwnode_handle *fwnode,
struct fwnode_handle *child)
{
- if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
+ if (is_of_node(fwnode)) {
struct device_node *node;
- node = of_get_next_available_child(dev->of_node, to_of_node(child));
+ node = of_get_next_available_child(to_of_node(fwnode),
+ to_of_node(child));
if (node)
return &node->fwnode;
- } else if (IS_ENABLED(CONFIG_ACPI)) {
- return acpi_get_next_subnode(dev, child);
+ } else if (is_acpi_node(fwnode)) {
+ return acpi_get_next_subnode(fwnode, child);
}
+
return NULL;
}
+EXPORT_SYMBOL_GPL(fwnode_get_next_child_node);
+
+/**
+ * device_get_next_child_node - Return the next child node handle for a device
+ * @dev: Device to find the next child node for.
+ * @child: Handle to one of the device's child nodes or a null handle.
+ */
+struct fwnode_handle *device_get_next_child_node(struct device *dev,
+ struct fwnode_handle *child)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct fwnode_handle *fwnode = NULL;
+
+ if (dev->of_node)
+ fwnode = &dev->of_node->fwnode;
+ else if (adev)
+ fwnode = acpi_fwnode_handle(adev);
+
+ return fwnode_get_next_child_node(fwnode, child);
+}
EXPORT_SYMBOL_GPL(device_get_next_child_node);
/**
- * device_get_named_child_node - Return first matching named child node handle
- * @dev: Device to find the named child node for.
+ * fwnode_get_named_child_node - Return first matching named child node handle
+ * @fwnode: Firmware node to find the named child node for.
* @childname: String to match child node name against.
*/
-struct fwnode_handle *device_get_named_child_node(struct device *dev,
+struct fwnode_handle *fwnode_get_named_child_node(struct fwnode_handle *fwnode,
const char *childname)
{
struct fwnode_handle *child;
/*
- * Find first matching named child node of this device.
+ * Find first matching named child node of this fwnode.
* For ACPI this will be a data only sub-node.
*/
- device_for_each_child_node(dev, child) {
+ fwnode_for_each_child_node(fwnode, child) {
if (is_of_node(child)) {
if (!of_node_cmp(to_of_node(child)->name, childname))
return child;
@@ -978,9 +1023,32 @@ struct fwnode_handle *device_get_named_child_node(struct device *dev,
return NULL;
}
+EXPORT_SYMBOL_GPL(fwnode_get_named_child_node);
+
+/**
+ * device_get_named_child_node - Return first matching named child node handle
+ * @dev: Device to find the named child node for.
+ * @childname: String to match child node name against.
+ */
+struct fwnode_handle *device_get_named_child_node(struct device *dev,
+ const char *childname)
+{
+ return fwnode_get_named_child_node(dev_fwnode(dev), childname);
+}
EXPORT_SYMBOL_GPL(device_get_named_child_node);
/**
+ * fwnode_handle_get - Obtain a reference to a device node
+ * @fwnode: Pointer to the device node to obtain the reference to.
+ */
+void fwnode_handle_get(struct fwnode_handle *fwnode)
+{
+ if (is_of_node(fwnode))
+ of_node_get(to_of_node(fwnode));
+}
+EXPORT_SYMBOL_GPL(fwnode_handle_get);
+
+/**
* fwnode_handle_put - Drop reference to a device node
* @fwnode: Pointer to the device node to drop the reference to.
*
@@ -1117,3 +1185,157 @@ void *device_get_mac_address(struct device *dev, char *addr, int alen)
return device_get_mac_addr(dev, "address", addr, alen);
}
EXPORT_SYMBOL(device_get_mac_address);
+
+/**
+ * device_graph_get_next_endpoint - Get next endpoint firmware node
+ * @fwnode: Pointer to the parent firmware node
+ * @prev: Previous endpoint node or %NULL to get the first
+ *
+ * Returns an endpoint firmware node pointer or %NULL if no more endpoints
+ * are available.
+ */
+struct fwnode_handle *
+fwnode_graph_get_next_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_handle *prev)
+{
+ struct fwnode_handle *endpoint = NULL;
+
+ if (is_of_node(fwnode)) {
+ struct device_node *node;
+
+ node = of_graph_get_next_endpoint(to_of_node(fwnode),
+ to_of_node(prev));
+
+ if (node)
+ endpoint = &node->fwnode;
+ } else if (is_acpi_node(fwnode)) {
+ endpoint = acpi_graph_get_next_endpoint(fwnode, prev);
+ if (IS_ERR(endpoint))
+ endpoint = NULL;
+ }
+
+ return endpoint;
+
+}
+EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
+
+/**
+ * fwnode_graph_get_remote_port_parent - Return fwnode of a remote device
+ * @fwnode: Endpoint firmware node pointing to the remote endpoint
+ *
+ * Extracts firmware node of a remote device the @fwnode points to.
+ */
+struct fwnode_handle *
+fwnode_graph_get_remote_port_parent(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *parent = NULL;
+
+ if (is_of_node(fwnode)) {
+ struct device_node *node;
+
+ node = of_graph_get_remote_port_parent(to_of_node(fwnode));
+ if (node)
+ parent = &node->fwnode;
+ } else if (is_acpi_node(fwnode)) {
+ int ret;
+
+ ret = acpi_graph_get_remote_endpoint(fwnode, &parent, NULL,
+ NULL);
+ if (ret)
+ return NULL;
+ }
+
+ return parent;
+}
+EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port_parent);
+
+/**
+ * fwnode_graph_get_remote_port - Return fwnode of a remote port
+ * @fwnode: Endpoint firmware node pointing to the remote endpoint
+ *
+ * Extracts firmware node of a remote port the @fwnode points to.
+ */
+struct fwnode_handle *fwnode_graph_get_remote_port(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *port = NULL;
+
+ if (is_of_node(fwnode)) {
+ struct device_node *node;
+
+ node = of_graph_get_remote_port(to_of_node(fwnode));
+ if (node)
+ port = &node->fwnode;
+ } else if (is_acpi_node(fwnode)) {
+ int ret;
+
+ ret = acpi_graph_get_remote_endpoint(fwnode, NULL, &port, NULL);
+ if (ret)
+ return NULL;
+ }
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port);
+
+/**
+ * fwnode_graph_get_remote_endpoint - Return fwnode of a remote endpoint
+ * @fwnode: Endpoint firmware node pointing to the remote endpoint
+ *
+ * Extracts firmware node of a remote endpoint the @fwnode points to.
+ */
+struct fwnode_handle *
+fwnode_graph_get_remote_endpoint(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *endpoint = NULL;
+
+ if (is_of_node(fwnode)) {
+ struct device_node *node;
+
+ node = of_parse_phandle(to_of_node(fwnode), "remote-endpoint",
+ 0);
+ if (node)
+ endpoint = &node->fwnode;
+ } else if (is_acpi_node(fwnode)) {
+ int ret;
+
+ ret = acpi_graph_get_remote_endpoint(fwnode, NULL, NULL,
+ &endpoint);
+ if (ret)
+ return NULL;
+ }
+
+ return endpoint;
+}
+EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_endpoint);
+
+/**
+ * fwnode_graph_parse_endpoint - parse common endpoint node properties
+ * @fwnode: pointer to endpoint fwnode_handle
+ * @endpoint: pointer to the fwnode endpoint data structure
+ *
+ * Parse @fwnode representing a graph endpoint node and store the
+ * information in @endpoint. The caller must hold a reference to
+ * @fwnode.
+ */
+int fwnode_graph_parse_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_endpoint *endpoint)
+{
+ struct fwnode_handle *port_fwnode = fwnode_get_parent(fwnode);
+
+ memset(endpoint, 0, sizeof(*endpoint));
+
+ endpoint->local_fwnode = fwnode;
+
+ if (is_acpi_node(port_fwnode)) {
+ fwnode_property_read_u32(port_fwnode, "port", &endpoint->port);
+ fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id);
+ } else {
+ fwnode_property_read_u32(port_fwnode, "reg", &endpoint->port);
+ fwnode_property_read_u32(fwnode, "reg", &endpoint->id);
+ }
+
+ fwnode_handle_put(port_fwnode);
+
+ return 0;
+}
+EXPORT_SYMBOL(fwnode_graph_parse_endpoint);
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index b53c058e7009..2fc678e08d8d 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -387,6 +387,7 @@ struct acpi_data_node {
const char *name;
acpi_handle handle;
struct fwnode_handle fwnode;
+ struct fwnode_handle *parent;
struct acpi_device_data data;
struct list_head sibling;
struct kobject kobj;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 07f8924a2dfd..91407f2d549f 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -998,8 +998,16 @@ int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname,
int acpi_dev_prop_read(struct acpi_device *adev, const char *propname,
enum dev_prop_type proptype, void *val, size_t nval);
-struct fwnode_handle *acpi_get_next_subnode(struct device *dev,
- struct fwnode_handle *subnode);
+struct fwnode_handle *acpi_get_next_subnode(struct fwnode_handle *fwnode,
+ struct fwnode_handle *child);
+struct fwnode_handle *acpi_node_get_parent(struct fwnode_handle *fwnode);
+
+struct fwnode_handle *acpi_graph_get_next_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_handle *prev);
+int acpi_graph_get_remote_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_handle **remote,
+ struct fwnode_handle **port,
+ struct fwnode_handle **endpoint);
struct acpi_probe_entry;
typedef bool (*acpi_probe_entry_validate_subtbl)(struct acpi_subtable_header *,
@@ -1116,12 +1124,34 @@ static inline int acpi_dev_prop_read(struct acpi_device *adev,
return -ENXIO;
}
-static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev,
- struct fwnode_handle *subnode)
+static inline struct fwnode_handle *
+acpi_get_next_subnode(struct fwnode_handle *fwnode, struct fwnode_handle *child)
{
return NULL;
}
+static inline struct fwnode_handle *
+acpi_node_get_parent(struct fwnode_handle *fwnode)
+{
+ return NULL;
+}
+
+static inline struct fwnode_handle *
+acpi_graph_get_next_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_handle *prev)
+{
+ return ERR_PTR(-ENXIO);
+}
+
+static inline int
+acpi_graph_get_remote_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_handle **remote,
+ struct fwnode_handle **port,
+ struct fwnode_handle **endpoint)
+{
+ return -ENXIO;
+}
+
#define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, valid, data, fn) \
static const void * __acpi_table_##name[] \
__attribute__((unused)) \
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 8bd28ce6d76e..3dff2398a5f0 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -27,4 +27,16 @@ struct fwnode_handle {
struct fwnode_handle *secondary;
};
+/**
+ * struct fwnode_endpoint - Fwnode graph endpoint
+ * @port: Port number
+ * @id: Endpoint id
+ * @local_fwnode: reference to the related fwnode
+ */
+struct fwnode_endpoint {
+ unsigned int port;
+ unsigned int id;
+ const struct fwnode_handle *local_fwnode;
+};
+
#endif
diff --git a/include/linux/of.h b/include/linux/of.h
index 21e6323de0f3..e5d4225fda35 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -159,6 +159,8 @@ static inline struct device_node *to_of_node(struct fwnode_handle *fwnode)
container_of(fwnode, struct device_node, fwnode) : NULL;
}
+#define of_fwnode_handle(node) (&(node)->fwnode)
+
static inline bool of_have_populated_dt(void)
{
return of_root != NULL;
@@ -602,6 +604,8 @@ static inline struct device_node *of_find_node_with_property(
return NULL;
}
+#define of_fwnode_handle(node) NULL
+
static inline bool of_have_populated_dt(void)
{
return false;
diff --git a/include/linux/property.h b/include/linux/property.h
index 64e3a9c6d95f..2f482616a2f2 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -33,6 +33,8 @@ enum dev_dma_attr {
DEV_DMA_COHERENT,
};
+struct fwnode_handle *dev_fwnode(struct device *dev);
+
bool device_property_present(struct device *dev, const char *propname);
int device_property_read_u8_array(struct device *dev, const char *propname,
u8 *val, size_t nval);
@@ -70,6 +72,15 @@ int fwnode_property_read_string(struct fwnode_handle *fwnode,
int fwnode_property_match_string(struct fwnode_handle *fwnode,
const char *propname, const char *string);
+struct fwnode_handle *fwnode_get_parent(struct fwnode_handle *fwnode);
+struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode);
+struct fwnode_handle *fwnode_get_next_child_node(struct fwnode_handle *fwnode,
+ struct fwnode_handle *child);
+
+#define fwnode_for_each_child_node(fwnode, child) \
+ for (child = fwnode_get_next_child_node(fwnode, NULL); child; \
+ child = fwnode_get_next_child_node(fwnode, child))
+
struct fwnode_handle *device_get_next_child_node(struct device *dev,
struct fwnode_handle *child);
@@ -77,9 +88,12 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
for (child = device_get_next_child_node(dev, NULL); child; \
child = device_get_next_child_node(dev, child))
+struct fwnode_handle *fwnode_get_named_child_node(struct fwnode_handle *fwnode,
+ const char *childname);
struct fwnode_handle *device_get_named_child_node(struct device *dev,
const char *childname);
+void fwnode_handle_get(struct fwnode_handle *fwnode);
void fwnode_handle_put(struct fwnode_handle *fwnode);
unsigned int device_get_child_node_count(struct device *dev);
@@ -258,4 +272,16 @@ int device_get_phy_mode(struct device *dev);
void *device_get_mac_address(struct device *dev, char *addr, int alen);
+struct fwnode_handle *fwnode_graph_get_next_endpoint(
+ struct fwnode_handle *fwnode, struct fwnode_handle *prev);
+struct fwnode_handle *fwnode_graph_get_remote_port_parent(
+ struct fwnode_handle *fwnode);
+struct fwnode_handle *fwnode_graph_get_remote_port(
+ struct fwnode_handle *fwnode);
+struct fwnode_handle *fwnode_graph_get_remote_endpoint(
+ struct fwnode_handle *fwnode);
+
+int fwnode_graph_parse_endpoint(struct fwnode_handle *fwnode,
+ struct fwnode_endpoint *endpoint);
+
#endif /* _LINUX_PROPERTY_H_ */