|
@@ -1283,6 +1283,190 @@ int of_parse_phandle_with_args(const struct device_node *np, const char *list_na
|
|
|
}
|
|
|
EXPORT_SYMBOL(of_parse_phandle_with_args);
|
|
|
|
|
|
+/**
|
|
|
+ * of_parse_phandle_with_args_map() - Find a node pointed by phandle in a list and remap it
|
|
|
+ * @np: pointer to a device tree node containing a list
|
|
|
+ * @list_name: property name that contains a list
|
|
|
+ * @stem_name: stem of property names that specify phandles' arguments count
|
|
|
+ * @index: index of a phandle to parse out
|
|
|
+ * @out_args: optional pointer to output arguments structure (will be filled)
|
|
|
+ *
|
|
|
+ * This function is useful to parse lists of phandles and their arguments.
|
|
|
+ * Returns 0 on success and fills out_args, on error returns appropriate errno
|
|
|
+ * value. The difference between this function and of_parse_phandle_with_args()
|
|
|
+ * is that this API remaps a phandle if the node the phandle points to has
|
|
|
+ * a <@stem_name>-map property.
|
|
|
+ *
|
|
|
+ * Caller is responsible to call of_node_put() on the returned out_args->np
|
|
|
+ * pointer.
|
|
|
+ *
|
|
|
+ * Example:
|
|
|
+ *
|
|
|
+ * phandle1: node1 {
|
|
|
+ * #list-cells = <2>;
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * phandle2: node2 {
|
|
|
+ * #list-cells = <1>;
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * phandle3: node3 {
|
|
|
+ * #list-cells = <1>;
|
|
|
+ * list-map = <0 &phandle2 3>,
|
|
|
+ * <1 &phandle2 2>,
|
|
|
+ * <2 &phandle1 5 1>;
|
|
|
+ * list-map-mask = <0x3>;
|
|
|
+ * };
|
|
|
+ *
|
|
|
+ * node4 {
|
|
|
+ * list = <&phandle1 1 2 &phandle3 0>;
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * To get a device_node of the `node2' node you may call this:
|
|
|
+ * of_parse_phandle_with_args(node4, "list", "list", 1, &args);
|
|
|
+ */
|
|
|
+int of_parse_phandle_with_args_map(const struct device_node *np,
|
|
|
+ const char *list_name,
|
|
|
+ const char *stem_name,
|
|
|
+ int index, struct of_phandle_args *out_args)
|
|
|
+{
|
|
|
+ char *cells_name, *map_name = NULL, *mask_name = NULL;
|
|
|
+ char *pass_name = NULL;
|
|
|
+ struct device_node *cur, *new = NULL;
|
|
|
+ const __be32 *map, *mask, *pass;
|
|
|
+ static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
|
|
|
+ static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = 0 };
|
|
|
+ __be32 initial_match_array[MAX_PHANDLE_ARGS];
|
|
|
+ const __be32 *match_array = initial_match_array;
|
|
|
+ int i, ret, map_len, match;
|
|
|
+ u32 list_size, new_size;
|
|
|
+
|
|
|
+ if (index < 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name);
|
|
|
+ if (!cells_name)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ ret = -ENOMEM;
|
|
|
+ map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name);
|
|
|
+ if (!map_name)
|
|
|
+ goto free;
|
|
|
+
|
|
|
+ mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name);
|
|
|
+ if (!mask_name)
|
|
|
+ goto free;
|
|
|
+
|
|
|
+ pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name);
|
|
|
+ if (!pass_name)
|
|
|
+ goto free;
|
|
|
+
|
|
|
+ ret = __of_parse_phandle_with_args(np, list_name, cells_name, 0, index,
|
|
|
+ out_args);
|
|
|
+ if (ret)
|
|
|
+ goto free;
|
|
|
+
|
|
|
+ /* Get the #<list>-cells property */
|
|
|
+ cur = out_args->np;
|
|
|
+ ret = of_property_read_u32(cur, cells_name, &list_size);
|
|
|
+ if (ret < 0)
|
|
|
+ goto put;
|
|
|
+
|
|
|
+ /* Precalculate the match array - this simplifies match loop */
|
|
|
+ for (i = 0; i < list_size; i++)
|
|
|
+ initial_match_array[i] = cpu_to_be32(out_args->args[i]);
|
|
|
+
|
|
|
+ ret = -EINVAL;
|
|
|
+ while (cur) {
|
|
|
+ /* Get the <list>-map property */
|
|
|
+ map = of_get_property(cur, map_name, &map_len);
|
|
|
+ if (!map) {
|
|
|
+ ret = 0;
|
|
|
+ goto free;
|
|
|
+ }
|
|
|
+ map_len /= sizeof(u32);
|
|
|
+
|
|
|
+ /* Get the <list>-map-mask property (optional) */
|
|
|
+ mask = of_get_property(cur, mask_name, NULL);
|
|
|
+ if (!mask)
|
|
|
+ mask = dummy_mask;
|
|
|
+ /* Iterate through <list>-map property */
|
|
|
+ match = 0;
|
|
|
+ while (map_len > (list_size + 1) && !match) {
|
|
|
+ /* Compare specifiers */
|
|
|
+ match = 1;
|
|
|
+ for (i = 0; i < list_size; i++, map_len--)
|
|
|
+ match &= !((match_array[i] ^ *map++) & mask[i]);
|
|
|
+
|
|
|
+ of_node_put(new);
|
|
|
+ new = of_find_node_by_phandle(be32_to_cpup(map));
|
|
|
+ map++;
|
|
|
+ map_len--;
|
|
|
+
|
|
|
+ /* Check if not found */
|
|
|
+ if (!new)
|
|
|
+ goto put;
|
|
|
+
|
|
|
+ if (!of_device_is_available(new))
|
|
|
+ match = 0;
|
|
|
+
|
|
|
+ ret = of_property_read_u32(new, cells_name, &new_size);
|
|
|
+ if (ret)
|
|
|
+ goto put;
|
|
|
+
|
|
|
+ /* Check for malformed properties */
|
|
|
+ if (WARN_ON(new_size > MAX_PHANDLE_ARGS))
|
|
|
+ goto put;
|
|
|
+ if (map_len < new_size)
|
|
|
+ goto put;
|
|
|
+
|
|
|
+ /* Move forward by new node's #<list>-cells amount */
|
|
|
+ map += new_size;
|
|
|
+ map_len -= new_size;
|
|
|
+ }
|
|
|
+ if (!match)
|
|
|
+ goto put;
|
|
|
+
|
|
|
+ /* Get the <list>-map-pass-thru property (optional) */
|
|
|
+ pass = of_get_property(cur, pass_name, NULL);
|
|
|
+ if (!pass)
|
|
|
+ pass = dummy_pass;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Successfully parsed a <list>-map translation; copy new
|
|
|
+ * specifier into the out_args structure, keeping the
|
|
|
+ * bits specified in <list>-map-pass-thru.
|
|
|
+ */
|
|
|
+ match_array = map - new_size;
|
|
|
+ for (i = 0; i < new_size; i++) {
|
|
|
+ __be32 val = *(map - new_size + i);
|
|
|
+
|
|
|
+ if (i < list_size) {
|
|
|
+ val &= ~pass[i];
|
|
|
+ val |= cpu_to_be32(out_args->args[i]) & pass[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ out_args->args[i] = be32_to_cpu(val);
|
|
|
+ }
|
|
|
+ out_args->args_count = list_size = new_size;
|
|
|
+ /* Iterate again with new provider */
|
|
|
+ out_args->np = new;
|
|
|
+ of_node_put(cur);
|
|
|
+ cur = new;
|
|
|
+ }
|
|
|
+put:
|
|
|
+ of_node_put(cur);
|
|
|
+ of_node_put(new);
|
|
|
+free:
|
|
|
+ kfree(mask_name);
|
|
|
+ kfree(map_name);
|
|
|
+ kfree(cells_name);
|
|
|
+ kfree(pass_name);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(of_parse_phandle_with_args_map);
|
|
|
+
|
|
|
/**
|
|
|
* of_parse_phandle_with_fixed_args() - Find a node pointed by phandle in a list
|
|
|
* @np: pointer to a device tree node containing a list
|