diff options
Diffstat (limited to 'drivers/base/property.c')
-rw-r--r-- | drivers/base/property.c | 364 |
1 files changed, 293 insertions, 71 deletions
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); |