Generic device properties framework updates for v4.12-rc1

- 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).
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJZB5jZAAoJEILEb/54YlRxPU0P/34yV01FwLCnOGDWACRq62Iv
 yfW+uSb3x679sk4DBZfmuruE7N96fWtXS+Pbt+a1A62md421r9iQCKSh7o9Q8lcX
 hUxJt/6NF6hbOygpvDxUXA3XpsV37hKUtVAlqln95JiwgFERBm9TT7GqmTutZaue
 vbidgp0f2H75eM4pzhLCl7xgjSuwNe7GU60uVtLUSREoaGtylkcjNjdJmLFF518K
 uMDttQOwX7c0S3/IctAuIax4se6xFXMw0AfiRJA5KoLRFRIkrMVb+YlaqhaITD9t
 SnjjBQoBQ2z09hLdTQrw0k2qfSkqGx34Qw/JikFLpyUpvJT5tOhtCgR6czLHDkN2
 Cz22xWfiHWQS8IH14urxdwhIiLI+VRcrU5GdlP9Rwj9wQCF8W2nWujg6X7gyb2Sg
 cV82pAbD9wXVPXcZddbXSOhiGExQLlD6EAQ/+Dw73LPU4biV8aBB4GBalrKeHUIq
 SiYaSvHdwHCv0LxTM3aWoxE2qLh0g+8e1K9vJQnh8BQM6q14qQeWvbq5Gmv+T5LZ
 FDT/LUCMUx85yiaJ+PU4DFiQ1kutRxzUqtUrFRUCWdHwKd6JnNnEcXM4ouwelJsf
 8PuFOLzHLzXXfAbx7EJP7daBbrOmhJs7Y/9UzTnJSquO/1F+k3a26uPfJP2349I+
 IfNWjnjjY3QRLYwLKfqF
 =Ihme
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2017-05-01 14:18:05 -07:00
commit 5fab10041b
8 changed files with 761 additions and 105 deletions

View file

@ -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;
return array_len;
}
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;
}
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);
@ -931,6 +908,75 @@ int device_add_properties(struct device *dev,
}
EXPORT_SYMBOL_GPL(device_add_properties);
/**
* 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 *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_of_node(fwnode)) {
struct device_node *node;
node = of_get_next_available_child(to_of_node(fwnode),
to_of_node(child));
if (node)
return &node->fwnode;
} 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.
@ -939,34 +985,33 @@ EXPORT_SYMBOL_GPL(device_add_properties);
struct fwnode_handle *device_get_next_child_node(struct device *dev,
struct fwnode_handle *child)
{
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
struct device_node *node;
struct acpi_device *adev = ACPI_COMPANION(dev);
struct fwnode_handle *fwnode = NULL;
node = of_get_next_available_child(dev->of_node, to_of_node(child));
if (node)
return &node->fwnode;
} else if (IS_ENABLED(CONFIG_ACPI)) {
return acpi_get_next_subnode(dev, child);
}
return 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,8 +1023,31 @@ 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);