diff options
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/Kconfig | 9 | ||||
-rw-r--r-- | drivers/of/Makefile | 1 | ||||
-rw-r--r-- | drivers/of/address.c | 16 | ||||
-rw-r--r-- | drivers/of/base.c | 378 | ||||
-rw-r--r-- | drivers/of/fdt.c | 19 | ||||
-rw-r--r-- | drivers/of/gpio.c | 45 | ||||
-rw-r--r-- | drivers/of/irq.c | 133 | ||||
-rw-r--r-- | drivers/of/of_mdio.c | 1 | ||||
-rw-r--r-- | drivers/of/of_net.c | 1 | ||||
-rw-r--r-- | drivers/of/of_pci.c | 1 | ||||
-rw-r--r-- | drivers/of/of_pci_irq.c | 1 | ||||
-rw-r--r-- | drivers/of/of_spi.c | 1 | ||||
-rw-r--r-- | drivers/of/pdt.c | 8 | ||||
-rw-r--r-- | drivers/of/platform.c | 28 | ||||
-rw-r--r-- | drivers/of/selftest.c | 139 |
15 files changed, 649 insertions, 132 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index cac63c9f49ae..268163dd71c7 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -15,6 +15,15 @@ config PROC_DEVICETREE an image of the device tree that the kernel copies from Open Firmware or other boot firmware. If unsure, say Y here. +config OF_SELFTEST + bool "Device Tree Runtime self tests" + help + This option builds in test cases for the device tree infrastructure + that are executed one at boot time, and the results dumped to the + console. + + If unsure, say N here, but this option is safe to enable. + config OF_FLATTREE bool select DTC diff --git a/drivers/of/Makefile b/drivers/of/Makefile index dccb1176be57..a73f5a51ff4c 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_OF_GPIO) += gpio.o obj-$(CONFIG_OF_I2C) += of_i2c.o obj-$(CONFIG_OF_NET) += of_net.o obj-$(CONFIG_OF_SPI) += of_spi.o +obj-$(CONFIG_OF_SELFTEST) += selftest.o obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o diff --git a/drivers/of/address.c b/drivers/of/address.c index 72c33fbe451d..66d96f14c274 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -14,7 +14,7 @@ static struct of_bus *of_match_bus(struct device_node *np); static int __of_address_to_resource(struct device_node *dev, const __be32 *addrp, u64 size, unsigned int flags, - struct resource *r); + const char *name, struct resource *r); /* Debug utility */ #ifdef DEBUG @@ -215,7 +215,7 @@ int of_pci_address_to_resource(struct device_node *dev, int bar, addrp = of_get_pci_address(dev, bar, &size, &flags); if (addrp == NULL) return -EINVAL; - return __of_address_to_resource(dev, addrp, size, flags, r); + return __of_address_to_resource(dev, addrp, size, flags, NULL, r); } EXPORT_SYMBOL_GPL(of_pci_address_to_resource); #endif /* CONFIG_PCI */ @@ -529,7 +529,7 @@ EXPORT_SYMBOL(of_get_address); static int __of_address_to_resource(struct device_node *dev, const __be32 *addrp, u64 size, unsigned int flags, - struct resource *r) + const char *name, struct resource *r) { u64 taddr; @@ -551,7 +551,8 @@ static int __of_address_to_resource(struct device_node *dev, r->end = taddr + size - 1; } r->flags = flags; - r->name = dev->full_name; + r->name = name ? name : dev->full_name; + return 0; } @@ -569,11 +570,16 @@ int of_address_to_resource(struct device_node *dev, int index, const __be32 *addrp; u64 size; unsigned int flags; + const char *name = NULL; addrp = of_get_address(dev, index, &size, &flags); if (addrp == NULL) return -EINVAL; - return __of_address_to_resource(dev, addrp, size, flags, r); + + /* Get optional "reg-names" property to add a name to a resource */ + of_property_read_string_index(dev, "reg-names", index, &name); + + return __of_address_to_resource(dev, addrp, size, flags, name, r); } EXPORT_SYMBOL_GPL(of_address_to_resource); diff --git a/drivers/of/base.c b/drivers/of/base.c index 3ff22e32b602..133908a6fd8d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -17,14 +17,39 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include <linux/ctype.h> #include <linux/module.h> #include <linux/of.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/proc_fs.h> +/** + * struct alias_prop - Alias property in 'aliases' node + * @link: List node to link the structure in aliases_lookup list + * @alias: Alias property name + * @np: Pointer to device_node that the alias stands for + * @id: Index value from end of alias name + * @stem: Alias string without the index + * + * The structure represents one alias property of 'aliases' node as + * an entry in aliases_lookup list. + */ +struct alias_prop { + struct list_head link; + const char *alias; + struct device_node *np; + int id; + char stem[0]; +}; + +static LIST_HEAD(aliases_lookup); + struct device_node *allnodes; struct device_node *of_chosen; +struct device_node *of_aliases; + +static DEFINE_MUTEX(of_aliases_mutex); /* use when traversing tree through the allnext, child, sibling, * or parent members of struct device_node. @@ -632,6 +657,35 @@ int of_property_read_u32_array(const struct device_node *np, EXPORT_SYMBOL_GPL(of_property_read_u32_array); /** + * of_property_read_u64 - Find and read a 64 bit integer from a property + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_value: pointer to return value, modified only if return value is 0. + * + * Search for a property in a device node and read a 64-bit value from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_value is modified only if a valid u64 value can be decoded. + */ +int of_property_read_u64(const struct device_node *np, const char *propname, + u64 *out_value) +{ + struct property *prop = of_find_property(np, propname, NULL); + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if (sizeof(*out_value) > prop->length) + return -EOVERFLOW; + *out_value = of_read_number(prop->value, 2); + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u64); + +/** * of_property_read_string - Find and read a string from a property * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. @@ -662,6 +716,88 @@ int of_property_read_string(struct device_node *np, const char *propname, EXPORT_SYMBOL_GPL(of_property_read_string); /** + * of_property_read_string_index - Find and read a string from a multiple + * strings property. + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @index: index of the string in the list of strings + * @out_string: pointer to null terminated return string, modified only if + * return value is 0. + * + * Search for a property in a device tree node and retrieve a null + * terminated string value (pointer to data, not a copy) in the list of strings + * contained in that property. + * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if + * property does not have a value, and -EILSEQ if the string is not + * null-terminated within the length of the property data. + * + * The out_string pointer is modified only if a valid string can be decoded. + */ +int of_property_read_string_index(struct device_node *np, const char *propname, + int index, const char **output) +{ + struct property *prop = of_find_property(np, propname, NULL); + int i = 0; + size_t l = 0, total = 0; + const char *p; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if (strnlen(prop->value, prop->length) >= prop->length) + return -EILSEQ; + + p = prop->value; + + for (i = 0; total < prop->length; total += l, p += l) { + l = strlen(p) + 1; + if (i++ == index) { + *output = p; + return 0; + } + } + return -ENODATA; +} +EXPORT_SYMBOL_GPL(of_property_read_string_index); + + +/** + * of_property_count_strings - Find and return the number of strings from a + * multiple strings property. + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a property in a device tree node and retrieve the number of null + * terminated string contain in it. Returns the number of strings on + * success, -EINVAL if the property does not exist, -ENODATA if property + * does not have a value, and -EILSEQ if the string is not null-terminated + * within the length of the property data. + */ +int of_property_count_strings(struct device_node *np, const char *propname) +{ + struct property *prop = of_find_property(np, propname, NULL); + int i = 0; + size_t l = 0, total = 0; + const char *p; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if (strnlen(prop->value, prop->length) >= prop->length) + return -EILSEQ; + + p = prop->value; + + for (i = 0; total < prop->length; total += l, p += l, i++) + l = strlen(p) + 1; + + return i; +} +EXPORT_SYMBOL_GPL(of_property_count_strings); + +/** * of_parse_phandle - Resolve a phandle property to a device_node pointer * @np: Pointer to device node holding phandle property * @phandle_name: Name of property holding a phandle value @@ -686,17 +822,19 @@ of_parse_phandle(struct device_node *np, const char *phandle_name, int index) 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 * @list_name: property name that contains a list * @cells_name: property name that specifies phandles' arguments count * @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 arguments pointer (will be filled) + * @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_node and out_args, on error returns - * appropriate errno value. + * Returns 0 on success and fills out_args, on error returns appropriate + * errno value. + * + * Caller is responsible to call of_node_put() on the returned out_args->node + * pointer. * * Example: * @@ -713,94 +851,96 @@ EXPORT_SYMBOL(of_parse_phandle); * } * * 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, - struct device_node **out_node, - const void **out_args) + struct of_phandle_args *out_args) { - int ret = -EINVAL; - const __be32 *list; - const __be32 *list_end; - int size; - int cur_index = 0; + const __be32 *list, *list_end; + int size, cur_index = 0; + uint32_t count = 0; struct device_node *node = NULL; - const void *args = NULL; + phandle phandle; + /* Retrieve the phandle list property */ list = of_get_property(np, list_name, &size); - if (!list) { - ret = -ENOENT; - goto err0; - } + if (!list) + return -EINVAL; list_end = list + size / sizeof(*list); + /* Loop over the phandles until all the requested entry is found */ while (list < list_end) { - const __be32 *cells; - phandle phandle; + count = 0; + /* + * If phandle is 0, then it is an empty entry with no + * arguments. Skip forward to the next entry. + */ phandle = be32_to_cpup(list++); - args = list; - - /* one cell hole in the list = <>; */ - if (!phandle) - goto next; - - node = of_find_node_by_phandle(phandle); - if (!node) { - pr_debug("%s: could not find phandle\n", - np->full_name); - goto err0; - } + 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; + } - cells = of_get_property(node, cells_name, &size); - if (!cells || size != sizeof(*cells)) { - pr_debug("%s: could not get %s for %s\n", - np->full_name, cells_name, node->full_name); - goto err1; + /* + * Make sure that the arguments actually fit in the + * remaining property data length + */ + if (list + count > list_end) { + pr_err("%s: arguments longer than property\n", + np->full_name); + break; + } } - list += be32_to_cpup(cells); - if (list > list_end) { - pr_debug("%s: insufficient arguments length\n", - np->full_name); - goto err1; + /* + * All of the error cases above bail out of the loop, so at + * this point, the parsing is successful. If the requested + * index matches, then fill the out_args structure and return, + * or return -ENOENT for an empty entry. + */ + if (cur_index == index) { + if (!phandle) + return -ENOENT; + + if (out_args) { + int i; + if (WARN_ON(count > MAX_PHANDLE_ARGS)) + count = MAX_PHANDLE_ARGS; + 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); node = NULL; - args = NULL; + list += count; cur_index++; } - if (!node) { - /* - * args w/o node indicates that the loop above has stopped at - * the 'hole' cell. Report this differently. - */ - 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; + /* Loop exited without finding a valid entry; return an error */ + if (node) + of_node_put(node); + return -EINVAL; } -EXPORT_SYMBOL(of_parse_phandles_with_args); +EXPORT_SYMBOL(of_parse_phandle_with_args); /** * prom_add_property - Add a property to a node @@ -988,3 +1128,99 @@ out_unlock: } #endif /* defined(CONFIG_OF_DYNAMIC) */ +static void of_alias_add(struct alias_prop *ap, struct device_node *np, + int id, const char *stem, int stem_len) +{ + ap->np = np; + ap->id = id; + strncpy(ap->stem, stem, stem_len); + ap->stem[stem_len] = 0; + list_add_tail(&ap->link, &aliases_lookup); + pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", + ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); +} + +/** + * of_alias_scan - Scan all properties of 'aliases' node + * + * The function scans all the properties of 'aliases' node and populate + * the the global lookup table with the properties. It returns the + * number of alias_prop found, or error code in error case. + * + * @dt_alloc: An allocator that provides a virtual address to memory + * for the resulting tree + */ +void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) +{ + struct property *pp; + + of_chosen = of_find_node_by_path("/chosen"); + if (of_chosen == NULL) + of_chosen = of_find_node_by_path("/chosen@0"); + of_aliases = of_find_node_by_path("/aliases"); + if (!of_aliases) + return; + + for_each_property_of_node(of_aliases, pp) { + const char *start = pp->name; + const char *end = start + strlen(start); + struct device_node *np; + struct alias_prop *ap; + int id, len; + + /* Skip those we do not want to proceed */ + if (!strcmp(pp->name, "name") || + !strcmp(pp->name, "phandle") || + !strcmp(pp->name, "linux,phandle")) + continue; + + np = of_find_node_by_path(pp->value); + if (!np) + continue; + + /* walk the alias backwards to extract the id and work out + * the 'stem' string */ + while (isdigit(*(end-1)) && end > start) + end--; + len = end - start; + + if (kstrtoint(end, 10, &id) < 0) + continue; + + /* Allocate an alias_prop with enough space for the stem */ + ap = dt_alloc(sizeof(*ap) + len + 1, 4); + if (!ap) + continue; + ap->alias = start; + of_alias_add(ap, np, id, start, len); + } +} + +/** + * of_alias_get_id - Get alias id for the given device_node + * @np: Pointer to the given device_node + * @stem: Alias stem of the given device_node + * + * The function travels the lookup table to get alias id for the given + * device_node and alias stem. It returns the alias id if find it. + */ +int of_alias_get_id(struct device_node *np, const char *stem) +{ + struct alias_prop *app; + int id = -ENODEV; + + mutex_lock(&of_aliases_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (strcmp(app->stem, stem) != 0) + continue; + + if (np == app->np) { + id = app->id; + break; + } + } + mutex_unlock(&of_aliases_mutex); + + return id; +} +EXPORT_SYMBOL_GPL(of_alias_get_id); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 65200af29c52..ea2bd1be2640 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -18,10 +18,12 @@ #include <linux/errno.h> #include <linux/slab.h> +#include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #ifdef CONFIG_PPC #include <asm/machdep.h> #endif /* CONFIG_PPC */ +#include <asm/setup.h> #include <asm/page.h> char *of_fdt_get_string(struct boot_param_header *blob, u32 offset) @@ -107,7 +109,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob, * of_fdt_match - Return true if node matches a list of compatible values */ int of_fdt_match(struct boot_param_header *blob, unsigned long node, - const char **compat) + const char *const *compat) { unsigned int tmp, score = 0; @@ -541,7 +543,7 @@ int __init of_flat_dt_is_compatible(unsigned long node, const char *compat) /** * of_flat_dt_match - Return true if node matches a list of compatible values */ -int __init of_flat_dt_match(unsigned long node, const char **compat) +int __init of_flat_dt_match(unsigned long node, const char *const *compat) { return of_fdt_match(initial_boot_params, node, compat); } @@ -681,9 +683,14 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, if (p != NULL && l > 0) strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); + /* + * CONFIG_CMDLINE is meant to be a default in case nothing else + * managed to set the command line, unless CONFIG_CMDLINE_FORCE + * is set in which case we override whatever was found earlier. + */ #ifdef CONFIG_CMDLINE #ifndef CONFIG_CMDLINE_FORCE - if (p == NULL || l == 0 || (l == 1 && (*p) == 0)) + if (!((char *)data)[0]) #endif strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif /* CONFIG_CMDLINE */ @@ -707,10 +714,8 @@ void __init unflatten_device_tree(void) __unflatten_device_tree(initial_boot_params, &allnodes, early_init_dt_alloc_memory_arch); - /* Get pointer to OF "/chosen" node for use everywhere */ - of_chosen = of_find_node_by_path("/chosen"); - if (of_chosen == NULL) - of_chosen = of_find_node_by_path("/chosen@0"); + /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */ + of_alias_scan(early_init_dt_alloc_memory_arch); } #endif /* CONFIG_OF_EARLY_FLATTREE */ diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c index ef0105fa52b1..7e62d15d60f6 100644 --- a/drivers/of/gpio.c +++ b/drivers/of/gpio.c @@ -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 ret; - struct device_node *gpio_np; struct gpio_chip *gc; - int size; - const void *gpio_spec; - const __be32 *gpio_cells; + struct of_phandle_args gpiospec; - ret = of_parse_phandles_with_args(np, propname, "#gpio-cells", index, - &gpio_np, &gpio_spec); + ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, + &gpiospec); if (ret) { pr_debug("%s: can't parse gpios property\n", __func__); goto err0; } - gc = of_node_to_gpiochip(gpio_np); + gc = of_node_to_gpiochip(gpiospec.np); if (!gc) { 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; goto err1; } - gpio_cells = of_get_property(gpio_np, "#gpio-cells", &size); - if (!gpio_cells || size != sizeof(*gpio_cells) || - be32_to_cpup(gpio_cells) != gc->of_gpio_n_cells) { + if (gpiospec.args_count != gc->of_gpio_n_cells) { 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; goto err1; } @@ -69,13 +64,13 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname, if (flags) *flags = 0; - ret = gc->of_xlate(gc, np, gpio_spec, flags); + ret = gc->of_xlate(gc, &gpiospec, flags); if (ret < 0) goto err1; ret += gc->base; err1: - of_node_put(gpio_np); + of_node_put(gpiospec.np); err0: pr_debug("%s exited with status %d\n", __func__, ret); return ret; @@ -105,8 +100,8 @@ unsigned int of_gpio_count(struct device_node *np) do { int ret; - ret = of_parse_phandles_with_args(np, "gpios", "#gpio-cells", - cnt, NULL, NULL); + ret = of_parse_phandle_with_args(np, "gpios", "#gpio-cells", + cnt, NULL); /* A hole in the gpios = <> counts anyway. */ if (ret < 0 && ret != -EEXIST) break; @@ -127,12 +122,9 @@ EXPORT_SYMBOL(of_gpio_count); * gpio chips. This function performs only one sanity check: whether gpio * is less than ngpios (that is specified in the gpio_chip). */ -int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np, - const void *gpio_spec, u32 *flags) +int of_gpio_simple_xlate(struct gpio_chip *gc, + 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 * 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; } - 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; if (flags) - *flags = be32_to_cpu(gpio[1]); + *flags = gpiospec->args[1]; - return n; + return gpiospec->args[0]; } EXPORT_SYMBOL(of_gpio_simple_xlate); @@ -198,8 +193,6 @@ int of_mm_gpiochip_add(struct device_node *np, if (ret) goto err2; - pr_debug("%s: registered as generic GPIO chip, base is %d\n", - np->full_name, gc->base); return 0; err2: iounmap(mm_gc->regs); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 9f689f1da0fc..9cf00602f566 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -19,15 +19,12 @@ */ #include <linux/errno.h> +#include <linux/list.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/string.h> - -/* For archs that don't support NO_IRQ (such as x86), provide a dummy value */ -#ifndef NO_IRQ -#define NO_IRQ 0 -#endif +#include <linux/slab.h> /** * irq_of_parse_and_map - Parse and map an interrupt into linux virq space @@ -42,7 +39,7 @@ unsigned int irq_of_parse_and_map(struct device_node *dev, int index) struct of_irq oirq; if (of_irq_map_one(dev, index, &oirq)) - return NO_IRQ; + return 0; return irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); @@ -343,10 +340,19 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) /* Only dereference the resource if both the * resource and the irq are valid. */ - if (r && irq != NO_IRQ) { + if (r && irq) { + const char *name = NULL; + + /* + * Get optional "interrupts-names" property to add a name + * to the resource. + */ + of_property_read_string_index(dev, "interrupt-names", index, + &name); + r->start = r->end = irq; r->flags = IORESOURCE_IRQ; - r->name = dev->full_name; + r->name = name ? name : dev->full_name; } return irq; @@ -361,7 +367,7 @@ int of_irq_count(struct device_node *dev) { int nr = 0; - while (of_irq_to_resource(dev, nr, NULL) != NO_IRQ) + while (of_irq_to_resource(dev, nr, NULL)) nr++; return nr; @@ -381,8 +387,115 @@ int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int i; for (i = 0; i < nr_irqs; i++, res++) - if (of_irq_to_resource(dev, i, res) == NO_IRQ) + if (!of_irq_to_resource(dev, i, res)) break; return i; } + +struct intc_desc { + struct list_head list; + struct device_node *dev; + struct device_node *interrupt_parent; +}; + +/** + * of_irq_init - Scan and init matching interrupt controllers in DT + * @matches: 0 terminated array of nodes to match and init function to call + * + * This function scans the device tree for matching interrupt controller nodes, + * and calls their initialization functions in order with parents first. + */ +void __init of_irq_init(const struct of_device_id *matches) +{ + struct device_node *np, *parent = NULL; + struct intc_desc *desc, *temp_desc; + struct list_head intc_desc_list, intc_parent_list; + + INIT_LIST_HEAD(&intc_desc_list); + INIT_LIST_HEAD(&intc_parent_list); + + for_each_matching_node(np, matches) { + if (!of_find_property(np, "interrupt-controller", NULL)) + continue; + /* + * Here, we allocate and populate an intc_desc with the node + * pointer, interrupt-parent device_node etc. + */ + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (WARN_ON(!desc)) + goto err; + + desc->dev = np; + desc->interrupt_parent = of_irq_find_parent(np); + if (desc->interrupt_parent == np) + desc->interrupt_parent = NULL; + list_add_tail(&desc->list, &intc_desc_list); + } + + /* + * The root irq controller is the one without an interrupt-parent. + * That one goes first, followed by the controllers that reference it, + * followed by the ones that reference the 2nd level controllers, etc. + */ + while (!list_empty(&intc_desc_list)) { + /* + * Process all controllers with the current 'parent'. + * First pass will be looking for NULL as the parent. + * The assumption is that NULL parent means a root controller. + */ + list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { + const struct of_device_id *match; + int ret; + of_irq_init_cb_t irq_init_cb; + + if (desc->interrupt_parent != parent) + continue; + + list_del(&desc->list); + match = of_match_node(matches, desc->dev); + if (WARN(!match->data, + "of_irq_init: no init function for %s\n", + match->compatible)) { + kfree(desc); + continue; + } + + pr_debug("of_irq_init: init %s @ %p, parent %p\n", + match->compatible, + desc->dev, desc->interrupt_parent); + irq_init_cb = match->data; + ret = irq_init_cb(desc->dev, desc->interrupt_parent); + if (ret) { + kfree(desc); + continue; + } + + /* + * This one is now set up; add it to the parent list so + * its children can get processed in a subsequent pass. + */ + list_add_tail(&desc->list, &intc_parent_list); + } + + /* Get the next pending parent that might have children */ + desc = list_first_entry(&intc_parent_list, typeof(*desc), list); + if (list_empty(&intc_parent_list) || !desc) { + pr_err("of_irq_init: children remain, but no parents\n"); + break; + } + list_del(&desc->list); + parent = desc->dev; + kfree(desc); + } + + list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) { + list_del(&desc->list); + kfree(desc); + } +err: + list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { + list_del(&desc->list); + kfree(desc); + } +} diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index d35e300b0ad1..980c079e4443 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -83,7 +83,6 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) addr); continue; } - phy_scan_fixups(phy); /* Associate the OF node with the device structure so it * can be looked up later */ diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c index bb184717588f..ffab033d207e 100644 --- a/drivers/of/of_net.c +++ b/drivers/of/of_net.c @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include <linux/of_net.h> #include <linux/phy.h> +#include <linux/export.h> /** * It maps 'enum phy_interface_t' found in include/linux/phy.h diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 3701b62c1d5e..13e37e2d8ec1 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -1,4 +1,5 @@ #include <linux/kernel.h> +#include <linux/export.h> #include <linux/of.h> #include <linux/of_pci.h> #include <asm/prom.h> diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index ac1ec54e4fd5..93125163dea2 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -1,6 +1,7 @@ #include <linux/kernel.h> #include <linux/of_pci.h> #include <linux/of_irq.h> +#include <linux/export.h> #include <asm/prom.h> /** diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c index 1dbce58a58b0..6dbc074e4876 100644 --- a/drivers/of/of_spi.c +++ b/drivers/of/of_spi.c @@ -6,6 +6,7 @@ * tree. */ +#include <linux/module.h> #include <linux/of.h> #include <linux/device.h> #include <linux/spi/spi.h> diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c index 4d87b5dc9284..07cc1d678e4d 100644 --- a/drivers/of/pdt.c +++ b/drivers/of/pdt.c @@ -229,6 +229,11 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, return ret; } +static void * __init kernel_tree_alloc(u64 size, u64 align) +{ + return prom_early_alloc(size); +} + void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) { struct device_node **nextp; @@ -245,4 +250,7 @@ void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) nextp = &allnodes->allnext; allnodes->child = of_pdt_build_tree(allnodes, of_pdt_prom_ops->getchild(allnodes->phandle), &nextp); + + /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */ + of_alias_scan(kernel_tree_alloc); } diff --git a/drivers/of/platform.c b/drivers/of/platform.c index ed5a6d3c26aa..63b3ec48c203 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -310,18 +310,21 @@ static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *l struct device_node *np) { struct resource res; - if (lookup) { - for(; lookup->name != NULL; lookup++) { - if (!of_device_is_compatible(np, lookup->compatible)) - continue; - if (of_address_to_resource(np, 0, &res)) - continue; - if (res.start != lookup->phys_addr) - continue; - pr_debug("%s: devname=%s\n", np->full_name, lookup->name); - return lookup; - } + + if (!lookup) + return NULL; + + for(; lookup->compatible != NULL; lookup++) { + if (!of_device_is_compatible(np, lookup->compatible)) + continue; + if (of_address_to_resource(np, 0, &res)) + continue; + if (res.start != lookup->phys_addr) + continue; + pr_debug("%s: devname=%s\n", np->full_name, lookup->name); + return lookup; } + return NULL; } @@ -329,8 +332,9 @@ static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *l * of_platform_bus_create() - Create a device for a node and its children. * @bus: device node of the bus to instantiate * @matches: match table for bus nodes - * disallow recursive creation of child buses + * @lookup: auxdata table for matching id and platform_data with device nodes * @parent: parent for new device, or NULL for top level. + * @strict: require compatible property * * Creates a platform_device for the provided device_node, and optionally * recursively create devices for all the child nodes. diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c new file mode 100644 index 000000000000..9d2b4803a9d6 --- /dev/null +++ b/drivers/of/selftest.c @@ -0,0 +1,139 @@ +/* + * Self tests for device tree subsystem + */ + +#define pr_fmt(fmt) "### %s(): " fmt, __func__ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/device.h> + +static bool selftest_passed = true; +#define selftest(result, fmt, ...) { \ + selftest_passed &= (result); \ + if (!(result)) \ + pr_err("FAIL %s:%i " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ +} + +static void __init of_selftest_parse_phandle_with_args(void) +{ + struct device_node *np; + struct of_phandle_args args; + int rc, i; + bool passed_all = true; + + pr_info("start\n"); + np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); + if (!np) { + pr_err("missing testcase data\n"); + return; + } + + for (i = 0; i < 7; i++) { + bool passed = true; + rc = of_parse_phandle_with_args(np, "phandle-list", + "#phandle-cells", i, &args); + + /* Test the values from tests-phandle.dtsi */ + switch (i) { + case 0: + passed &= !rc; + passed &= (args.args_count == 1); + passed &= (args.args[0] == (i + 1)); + break; + case 1: + passed &= !rc; + passed &= (args.args_count == 2); + passed &= (args.args[0] == (i + 1)); + passed &= (args.args[1] == 0); + break; + case 2: + passed &= (rc == -ENOENT); + break; + case 3: + passed &= !rc; + passed &= (args.args_count == 3); + passed &= (args.args[0] == (i + 1)); + passed &= (args.args[1] == 4); + passed &= (args.args[2] == 3); + break; + case 4: + passed &= !rc; + passed &= (args.args_count == 2); + passed &= (args.args[0] == (i + 1)); + passed &= (args.args[1] == 100); + break; + case 5: + passed &= !rc; + passed &= (args.args_count == 0); + break; + case 6: + passed &= !rc; + passed &= (args.args_count == 1); + passed &= (args.args[0] == (i + 1)); + break; + case 7: + passed &= (rc == -EINVAL); + break; + default: + passed = false; + } + + if (!passed) { + int j; + pr_err("index %i - data error on node %s rc=%i regs=[", + i, args.np->full_name, rc); + for (j = 0; j < args.args_count; j++) + printk(" %i", args.args[j]); + printk(" ]\n"); + + passed_all = false; + } + } + + /* Check for missing list property */ + rc = of_parse_phandle_with_args(np, "phandle-list-missing", + "#phandle-cells", 0, &args); + passed_all &= (rc == -EINVAL); + + /* Check for missing cells property */ + rc = of_parse_phandle_with_args(np, "phandle-list", + "#phandle-cells-missing", 0, &args); + passed_all &= (rc == -EINVAL); + + /* Check for bad phandle in list */ + rc = of_parse_phandle_with_args(np, "phandle-list-bad-phandle", + "#phandle-cells", 0, &args); + passed_all &= (rc == -EINVAL); + + /* Check for incorrectly formed argument list */ + rc = of_parse_phandle_with_args(np, "phandle-list-bad-args", + "#phandle-cells", 1, &args); + passed_all &= (rc == -EINVAL); + + pr_info("end - %s\n", passed_all ? "PASS" : "FAIL"); +} + +static int __init of_selftest(void) +{ + struct device_node *np; + + np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); + if (!np) { + pr_info("No testcase data in device tree; not running tests\n"); + return 0; + } + of_node_put(np); + + pr_info("start of selftest - you will see error messages\n"); + of_selftest_parse_phandle_with_args(); + pr_info("end of selftest - %s\n", selftest_passed ? "PASS" : "FAIL"); + return 0; +} +late_initcall(of_selftest); |