]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/base/property.c
Merge branches 'pm-domains', 'pm-sleep' and 'pm-cpufreq'
[karo-tx-linux.git] / drivers / base / property.c
index c458c63e353f1d62f5c73ef0c91f826d5323088a..149de311a10e63fc9636401771365684985f18a3 100644 (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;
-}
 
-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,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);