of: create of_phandle_args to simplify return of phandle parsing data

of_parse_phandle_with_args() needs to return quite a bit of data.  Rather
than making each datum a separate **out_ argument, this patch creates
struct of_phandle_args to contain all the returned data and reworks the
user of the function.  This patch also enables of_parse_phandle_with_args()
to return the device node pointer for the phandle node.

This patch also ends up being fairly major surgery to
of_parse_handle_with_args().  The existing structure didn't work well
when extending to use of_phandle_args, and I discovered bugs during testing.
I also took the opportunity to rename the function to be like the
existing of_parse_phandle().

v2: - moved declaration of of_phandle_args to fix compile on non-DT builds
    - fixed incorrect index in example usage
    - fixed incorrect return code handling for empty entries

Reviewed-by: Shawn Guo <shawn.guo@freescale.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
This commit is contained in:
Grant Likely 2011-12-12 09:25:57 -07:00
parent 1a2d397a6e
commit 15c9a0acc3
5 changed files with 111 additions and 102 deletions

View File

@ -824,17 +824,19 @@ of_parse_phandle(struct device_node *np, const char *phandle_name, int index)
EXPORT_SYMBOL(of_parse_phandle); EXPORT_SYMBOL(of_parse_phandle);
/** /**
* of_parse_phandles_with_args - Find a node pointed by phandle in a list * of_parse_phandle_with_args() - Find a node pointed by phandle in a list
* @np: pointer to a device tree node containing a list * @np: pointer to a device tree node containing a list
* @list_name: property name that contains a list * @list_name: property name that contains a list
* @cells_name: property name that specifies phandles' arguments count * @cells_name: property name that specifies phandles' arguments count
* @index: index of a phandle to parse out * @index: index of a phandle to parse out
* @out_node: optional pointer to device_node struct pointer (will be filled) * @out_args: optional pointer to output arguments structure (will be filled)
* @out_args: optional pointer to arguments pointer (will be filled)
* *
* This function is useful to parse lists of phandles and their arguments. * This function is useful to parse lists of phandles and their arguments.
* Returns 0 on success and fills out_node and out_args, on error returns * Returns 0 on success and fills out_args, on error returns appropriate
* appropriate errno value. * errno value.
*
* Caller is responsible to call of_node_put() on the returned out_args->node
* pointer.
* *
* Example: * Example:
* *
@ -851,94 +853,96 @@ EXPORT_SYMBOL(of_parse_phandle);
* } * }
* *
* To get a device_node of the `node2' node you may call this: * To get a device_node of the `node2' node you may call this:
* of_parse_phandles_with_args(node3, "list", "#list-cells", 2, &node2, &args); * of_parse_phandle_with_args(node3, "list", "#list-cells", 1, &args);
*/ */
int of_parse_phandles_with_args(struct device_node *np, const char *list_name, int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
const char *cells_name, int index, const char *cells_name, int index,
struct device_node **out_node, struct of_phandle_args *out_args)
const void **out_args)
{ {
int ret = -EINVAL; const __be32 *list, *list_end;
const __be32 *list; int size, cur_index = 0;
const __be32 *list_end; uint32_t count = 0;
int size;
int cur_index = 0;
struct device_node *node = NULL; struct device_node *node = NULL;
const void *args = NULL; phandle phandle;
/* Retrieve the phandle list property */
list = of_get_property(np, list_name, &size); list = of_get_property(np, list_name, &size);
if (!list) { if (!list)
ret = -ENOENT; return -EINVAL;
goto err0;
}
list_end = list + size / sizeof(*list); list_end = list + size / sizeof(*list);
/* Loop over the phandles until all the requested entry is found */
while (list < list_end) { while (list < list_end) {
const __be32 *cells; count = 0;
phandle phandle;
/*
* If phandle is 0, then it is an empty entry with no
* arguments. Skip forward to the next entry.
*/
phandle = be32_to_cpup(list++); phandle = be32_to_cpup(list++);
args = list; if (phandle) {
/*
* Find the provider node and parse the #*-cells
* property to determine the argument length
*/
node = of_find_node_by_phandle(phandle);
if (!node) {
pr_err("%s: could not find phandle\n",
np->full_name);
break;
}
if (of_property_read_u32(node, cells_name, &count)) {
pr_err("%s: could not get %s for %s\n",
np->full_name, cells_name,
node->full_name);
break;
}
/* one cell hole in the list = <>; */ /*
if (!phandle) * Make sure that the arguments actually fit in the
goto next; * remaining property data length
*/
node = of_find_node_by_phandle(phandle); if (list + count > list_end) {
if (!node) { pr_err("%s: arguments longer than property\n",
pr_debug("%s: could not find phandle\n", np->full_name);
np->full_name); break;
goto err0; }
} }
cells = of_get_property(node, cells_name, &size); /*
if (!cells || size != sizeof(*cells)) { * All of the error cases above bail out of the loop, so at
pr_debug("%s: could not get %s for %s\n", * this point, the parsing is successful. If the requested
np->full_name, cells_name, node->full_name); * index matches, then fill the out_args structure and return,
goto err1; * or return -ENOENT for an empty entry.
} */
if (cur_index == index) {
if (!phandle)
return -ENOENT;
list += be32_to_cpup(cells); if (out_args) {
if (list > list_end) { int i;
pr_debug("%s: insufficient arguments length\n", if (WARN_ON(count > MAX_PHANDLE_ARGS))
np->full_name); count = MAX_PHANDLE_ARGS;
goto err1; out_args->np = node;
out_args->args_count = count;
for (i = 0; i < count; i++)
out_args->args[i] = be32_to_cpup(list++);
}
return 0;
} }
next:
if (cur_index == index)
break;
of_node_put(node); of_node_put(node);
node = NULL; node = NULL;
args = NULL; list += count;
cur_index++; cur_index++;
} }
if (!node) { /* Loop exited without finding a valid entry; return an error */
/* if (node)
* args w/o node indicates that the loop above has stopped at of_node_put(node);
* the 'hole' cell. Report this differently. return -EINVAL;
*/
if (args)
ret = -EEXIST;
else
ret = -ENOENT;
goto err0;
}
if (out_node)
*out_node = node;
if (out_args)
*out_args = args;
return 0;
err1:
of_node_put(node);
err0:
pr_debug("%s failed with status %d\n", __func__, ret);
return ret;
} }
EXPORT_SYMBOL(of_parse_phandles_with_args); EXPORT_SYMBOL(of_parse_phandle_with_args);
/** /**
* prom_add_property - Add a property to a node * prom_add_property - Add a property to a node

View File

@ -35,32 +35,27 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname,
int index, enum of_gpio_flags *flags) int index, enum of_gpio_flags *flags)
{ {
int ret; int ret;
struct device_node *gpio_np;
struct gpio_chip *gc; struct gpio_chip *gc;
int size; struct of_phandle_args gpiospec;
const void *gpio_spec;
const __be32 *gpio_cells;
ret = of_parse_phandles_with_args(np, propname, "#gpio-cells", index, ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
&gpio_np, &gpio_spec); &gpiospec);
if (ret) { if (ret) {
pr_debug("%s: can't parse gpios property\n", __func__); pr_debug("%s: can't parse gpios property\n", __func__);
goto err0; goto err0;
} }
gc = of_node_to_gpiochip(gpio_np); gc = of_node_to_gpiochip(gpiospec.np);
if (!gc) { if (!gc) {
pr_debug("%s: gpio controller %s isn't registered\n", pr_debug("%s: gpio controller %s isn't registered\n",
np->full_name, gpio_np->full_name); np->full_name, gpiospec.np->full_name);
ret = -ENODEV; ret = -ENODEV;
goto err1; goto err1;
} }
gpio_cells = of_get_property(gpio_np, "#gpio-cells", &size); if (gpiospec.args_count != gc->of_gpio_n_cells) {
if (!gpio_cells || size != sizeof(*gpio_cells) ||
be32_to_cpup(gpio_cells) != gc->of_gpio_n_cells) {
pr_debug("%s: wrong #gpio-cells for %s\n", pr_debug("%s: wrong #gpio-cells for %s\n",
np->full_name, gpio_np->full_name); np->full_name, gpiospec.np->full_name);
ret = -EINVAL; ret = -EINVAL;
goto err1; goto err1;
} }
@ -69,13 +64,13 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname,
if (flags) if (flags)
*flags = 0; *flags = 0;
ret = gc->of_xlate(gc, np, gpio_spec, flags); ret = gc->of_xlate(gc, &gpiospec, flags);
if (ret < 0) if (ret < 0)
goto err1; goto err1;
ret += gc->base; ret += gc->base;
err1: err1:
of_node_put(gpio_np); of_node_put(gpiospec.np);
err0: err0:
pr_debug("%s exited with status %d\n", __func__, ret); pr_debug("%s exited with status %d\n", __func__, ret);
return ret; return ret;
@ -105,8 +100,8 @@ unsigned int of_gpio_count(struct device_node *np)
do { do {
int ret; int ret;
ret = of_parse_phandles_with_args(np, "gpios", "#gpio-cells", ret = of_parse_phandle_with_args(np, "gpios", "#gpio-cells",
cnt, NULL, NULL); cnt, NULL);
/* A hole in the gpios = <> counts anyway. */ /* A hole in the gpios = <> counts anyway. */
if (ret < 0 && ret != -EEXIST) if (ret < 0 && ret != -EEXIST)
break; break;
@ -127,12 +122,9 @@ EXPORT_SYMBOL(of_gpio_count);
* gpio chips. This function performs only one sanity check: whether gpio * gpio chips. This function performs only one sanity check: whether gpio
* is less than ngpios (that is specified in the gpio_chip). * is less than ngpios (that is specified in the gpio_chip).
*/ */
int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np, int of_gpio_simple_xlate(struct gpio_chip *gc,
const void *gpio_spec, u32 *flags) const struct of_phandle_args *gpiospec, u32 *flags)
{ {
const __be32 *gpio = gpio_spec;
const u32 n = be32_to_cpup(gpio);
/* /*
* We're discouraging gpio_cells < 2, since that way you'll have to * We're discouraging gpio_cells < 2, since that way you'll have to
* write your own xlate function (that will have to retrive the GPIO * write your own xlate function (that will have to retrive the GPIO
@ -144,13 +136,16 @@ int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np,
return -EINVAL; return -EINVAL;
} }
if (n > gc->ngpio) if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
return -EINVAL;
if (gpiospec->args[0] > gc->ngpio)
return -EINVAL; return -EINVAL;
if (flags) if (flags)
*flags = be32_to_cpu(gpio[1]); *flags = gpiospec->args[1];
return n; return gpiospec->args[0];
} }
EXPORT_SYMBOL(of_gpio_simple_xlate); EXPORT_SYMBOL(of_gpio_simple_xlate);

View File

@ -4,6 +4,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/of.h>
#ifdef CONFIG_GPIOLIB #ifdef CONFIG_GPIOLIB
@ -128,8 +129,8 @@ struct gpio_chip {
*/ */
struct device_node *of_node; struct device_node *of_node;
int of_gpio_n_cells; int of_gpio_n_cells;
int (*of_xlate)(struct gpio_chip *gc, struct device_node *np, int (*of_xlate)(struct gpio_chip *gc,
const void *gpio_spec, u32 *flags); const struct of_phandle_args *gpiospec, u32 *flags);
#endif #endif
}; };

View File

@ -65,6 +65,13 @@ struct device_node {
#endif #endif
}; };
#define MAX_PHANDLE_ARGS 8
struct of_phandle_args {
struct device_node *np;
int args_count;
uint32_t args[MAX_PHANDLE_ARGS];
};
#ifdef CONFIG_OF #ifdef CONFIG_OF
/* Pointer for first entry in chain of all nodes. */ /* Pointer for first entry in chain of all nodes. */
@ -230,9 +237,9 @@ extern int of_modalias_node(struct device_node *node, char *modalias, int len);
extern struct device_node *of_parse_phandle(struct device_node *np, extern struct device_node *of_parse_phandle(struct device_node *np,
const char *phandle_name, const char *phandle_name,
int index); int index);
extern int of_parse_phandles_with_args(struct device_node *np, extern int of_parse_phandle_with_args(struct device_node *np,
const char *list_name, const char *cells_name, int index, const char *list_name, const char *cells_name, int index,
struct device_node **out_node, const void **out_args); struct of_phandle_args *out_args);
extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align));
extern int of_alias_get_id(struct device_node *np, const char *stem); extern int of_alias_get_id(struct device_node *np, const char *stem);

View File

@ -18,6 +18,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of.h>
struct device_node; struct device_node;
@ -57,8 +58,9 @@ extern int of_mm_gpiochip_add(struct device_node *np,
extern void of_gpiochip_add(struct gpio_chip *gc); extern void of_gpiochip_add(struct gpio_chip *gc);
extern void of_gpiochip_remove(struct gpio_chip *gc); extern void of_gpiochip_remove(struct gpio_chip *gc);
extern struct gpio_chip *of_node_to_gpiochip(struct device_node *np); extern struct gpio_chip *of_node_to_gpiochip(struct device_node *np);
extern int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np, extern int of_gpio_simple_xlate(struct gpio_chip *gc,
const void *gpio_spec, u32 *flags); const struct of_phandle_args *gpiospec,
u32 *flags);
#else /* CONFIG_OF_GPIO */ #else /* CONFIG_OF_GPIO */
@ -75,8 +77,8 @@ static inline unsigned int of_gpio_count(struct device_node *np)
} }
static inline int of_gpio_simple_xlate(struct gpio_chip *gc, static inline int of_gpio_simple_xlate(struct gpio_chip *gc,
struct device_node *np, const struct of_phandle_args *gpiospec,
const void *gpio_spec, u32 *flags) u32 *flags)
{ {
return -ENOSYS; return -ENOSYS;
} }