Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5fab1004 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 08be8810 e4817477
Loading
Loading
Loading
Loading
+162 −0
Original line number Diff line number Diff line
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
+229 −30
Original line number Diff line number Diff line
@@ -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) {
		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;

		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;
}
+293 −71

File changed.

Preview size limit exceeded, changes collapsed.

+1 −0
Original line number Diff line number Diff line
@@ -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;
+34 −4
Original line number Diff line number Diff line
@@ -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))					\
Loading