diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 17:47:44 +0200 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 17:47:44 +0200 |
commit | bf61c8840efe60fd8f91446860b63338fb424158 (patch) | |
tree | 7a71832407a4f0d6346db773343f4c3ae2257b19 /drivers/of | |
parent | Input: wacom - fix "can not retrieve extra class descriptor" for DTH2242 (diff) | |
parent | Input: trackpoint - Optimize trackpoint init to use power-on reset (diff) | |
download | linux-bf61c8840efe60fd8f91446860b63338fb424158.tar.xz linux-bf61c8840efe60fd8f91446860b63338fb424158.zip |
Merge branch 'next' into for-linus
Prepare first set of updates for 3.10 merge window.
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/Kconfig | 2 | ||||
-rw-r--r-- | drivers/of/address.c | 2 | ||||
-rw-r--r-- | drivers/of/base.c | 565 | ||||
-rw-r--r-- | drivers/of/device.c | 13 | ||||
-rw-r--r-- | drivers/of/fdt.c | 26 | ||||
-rw-r--r-- | drivers/of/of_i2c.c | 2 | ||||
-rw-r--r-- | drivers/of/of_mdio.c | 6 | ||||
-rw-r--r-- | drivers/of/of_private.h | 36 | ||||
-rw-r--r-- | drivers/of/pdt.c | 12 | ||||
-rw-r--r-- | drivers/of/platform.c | 1 | ||||
-rw-r--r-- | drivers/of/selftest.c | 54 |
11 files changed, 540 insertions, 179 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index dfba3e64d595..d37bfcf5a3a2 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -53,7 +53,7 @@ config OF_DEVICE config OF_I2C def_tristate I2C - depends on I2C && !SPARC + depends on I2C help OpenFirmware I2C accessors diff --git a/drivers/of/address.c b/drivers/of/address.c index 0125524c08c4..04da786c84d2 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -429,7 +429,7 @@ static u64 __of_translate_address(struct device_node *dev, goto bail; bus = of_match_bus(parent); - /* Cound address cells & copy address locally */ + /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { printk(KERN_ERR "prom_parse: Bad cell count for %s\n", diff --git a/drivers/of/base.c b/drivers/of/base.c index af3b22ac7627..321d3ef05006 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -24,37 +24,21 @@ #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]; -}; +#include "of_private.h" -static LIST_HEAD(aliases_lookup); +LIST_HEAD(aliases_lookup); -struct device_node *allnodes; +struct device_node *of_allnodes; +EXPORT_SYMBOL(of_allnodes); struct device_node *of_chosen; struct device_node *of_aliases; -static DEFINE_MUTEX(of_aliases_mutex); +DEFINE_MUTEX(of_aliases_mutex); /* use when traversing tree through the allnext, child, sibling, * or parent members of struct device_node. */ -DEFINE_RWLOCK(devtree_lock); +DEFINE_RAW_SPINLOCK(devtree_lock); int of_n_addr_cells(struct device_node *np) { @@ -163,16 +147,14 @@ void of_node_put(struct device_node *node) EXPORT_SYMBOL(of_node_put); #endif /* CONFIG_OF_DYNAMIC */ -struct property *of_find_property(const struct device_node *np, - const char *name, - int *lenp) +static struct property *__of_find_property(const struct device_node *np, + const char *name, int *lenp) { struct property *pp; if (!np) return NULL; - read_lock(&devtree_lock); for (pp = np->properties; pp; pp = pp->next) { if (of_prop_cmp(pp->name, name) == 0) { if (lenp) @@ -180,7 +162,20 @@ struct property *of_find_property(const struct device_node *np, break; } } - read_unlock(&devtree_lock); + + return pp; +} + +struct property *of_find_property(const struct device_node *np, + const char *name, + int *lenp) +{ + struct property *pp; + unsigned long flags; + + raw_spin_lock_irqsave(&devtree_lock, flags); + pp = __of_find_property(np, name, lenp); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return pp; } @@ -198,13 +193,13 @@ struct device_node *of_find_all_nodes(struct device_node *prev) { struct device_node *np; - read_lock(&devtree_lock); - np = prev ? prev->allnext : allnodes; + raw_spin_lock(&devtree_lock); + np = prev ? prev->allnext : of_allnodes; for (; np != NULL; np = np->allnext) if (of_node_get(np)) break; of_node_put(prev); - read_unlock(&devtree_lock); + raw_spin_unlock(&devtree_lock); return np; } EXPORT_SYMBOL(of_find_all_nodes); @@ -213,8 +208,20 @@ EXPORT_SYMBOL(of_find_all_nodes); * Find a property with a given name for a given node * and return the value. */ +static const void *__of_get_property(const struct device_node *np, + const char *name, int *lenp) +{ + struct property *pp = __of_find_property(np, name, lenp); + + return pp ? pp->value : NULL; +} + +/* + * Find a property with a given name for a given node + * and return the value. + */ const void *of_get_property(const struct device_node *np, const char *name, - int *lenp) + int *lenp) { struct property *pp = of_find_property(np, name, lenp); @@ -225,13 +232,13 @@ EXPORT_SYMBOL(of_get_property); /** Checks if the given "compat" string matches one of the strings in * the device's "compatible" property */ -int of_device_is_compatible(const struct device_node *device, - const char *compat) +static int __of_device_is_compatible(const struct device_node *device, + const char *compat) { const char* cp; int cplen, l; - cp = of_get_property(device, "compatible", &cplen); + cp = __of_get_property(device, "compatible", &cplen); if (cp == NULL) return 0; while (cplen > 0) { @@ -244,6 +251,21 @@ int of_device_is_compatible(const struct device_node *device, return 0; } + +/** Checks if the given "compat" string matches one of the strings in + * the device's "compatible" property + */ +int of_device_is_compatible(const struct device_node *device, + const char *compat) +{ + unsigned long flags; + int res; + + raw_spin_lock_irqsave(&devtree_lock, flags); + res = __of_device_is_compatible(device, compat); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return res; +} EXPORT_SYMBOL(of_device_is_compatible); /** @@ -268,19 +290,19 @@ int of_machine_is_compatible(const char *compat) EXPORT_SYMBOL(of_machine_is_compatible); /** - * of_device_is_available - check if a device is available for use + * __of_device_is_available - check if a device is available for use * - * @device: Node to check for availability + * @device: Node to check for availability, with locks already held * * Returns 1 if the status property is absent or set to "okay" or "ok", * 0 otherwise */ -int of_device_is_available(const struct device_node *device) +static int __of_device_is_available(const struct device_node *device) { const char *status; int statlen; - status = of_get_property(device, "status", &statlen); + status = __of_get_property(device, "status", &statlen); if (status == NULL) return 1; @@ -291,6 +313,26 @@ int of_device_is_available(const struct device_node *device) return 0; } + +/** + * of_device_is_available - check if a device is available for use + * + * @device: Node to check for availability + * + * Returns 1 if the status property is absent or set to "okay" or "ok", + * 0 otherwise + */ +int of_device_is_available(const struct device_node *device) +{ + unsigned long flags; + int res; + + raw_spin_lock_irqsave(&devtree_lock, flags); + res = __of_device_is_available(device); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return res; + +} EXPORT_SYMBOL(of_device_is_available); /** @@ -303,13 +345,14 @@ EXPORT_SYMBOL(of_device_is_available); struct device_node *of_get_parent(const struct device_node *node) { struct device_node *np; + unsigned long flags; if (!node) return NULL; - read_lock(&devtree_lock); + raw_spin_lock_irqsave(&devtree_lock, flags); np = of_node_get(node->parent); - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; } EXPORT_SYMBOL(of_get_parent); @@ -328,14 +371,15 @@ EXPORT_SYMBOL(of_get_parent); struct device_node *of_get_next_parent(struct device_node *node) { struct device_node *parent; + unsigned long flags; if (!node) return NULL; - read_lock(&devtree_lock); + raw_spin_lock_irqsave(&devtree_lock, flags); parent = of_node_get(node->parent); of_node_put(node); - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return parent; } @@ -351,14 +395,15 @@ struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev) { struct device_node *next; + unsigned long flags; - read_lock(&devtree_lock); + raw_spin_lock_irqsave(&devtree_lock, flags); next = prev ? prev->sibling : node->child; for (; next; next = next->sibling) if (of_node_get(next)) break; of_node_put(prev); - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return next; } EXPORT_SYMBOL(of_get_next_child); @@ -376,16 +421,16 @@ struct device_node *of_get_next_available_child(const struct device_node *node, { struct device_node *next; - read_lock(&devtree_lock); + raw_spin_lock(&devtree_lock); next = prev ? prev->sibling : node->child; for (; next; next = next->sibling) { - if (!of_device_is_available(next)) + if (!__of_device_is_available(next)) continue; if (of_node_get(next)) break; } of_node_put(prev); - read_unlock(&devtree_lock); + raw_spin_unlock(&devtree_lock); return next; } EXPORT_SYMBOL(of_get_next_available_child); @@ -422,15 +467,16 @@ EXPORT_SYMBOL(of_get_child_by_name); */ struct device_node *of_find_node_by_path(const char *path) { - struct device_node *np = allnodes; + struct device_node *np = of_allnodes; + unsigned long flags; - read_lock(&devtree_lock); + raw_spin_lock_irqsave(&devtree_lock, flags); for (; np; np = np->allnext) { if (np->full_name && (of_node_cmp(np->full_name, path) == 0) && of_node_get(np)) break; } - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; } EXPORT_SYMBOL(of_find_node_by_path); @@ -450,15 +496,16 @@ struct device_node *of_find_node_by_name(struct device_node *from, const char *name) { struct device_node *np; + unsigned long flags; - read_lock(&devtree_lock); - np = from ? from->allnext : allnodes; + raw_spin_lock_irqsave(&devtree_lock, flags); + np = from ? from->allnext : of_allnodes; for (; np; np = np->allnext) if (np->name && (of_node_cmp(np->name, name) == 0) && of_node_get(np)) break; of_node_put(from); - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; } EXPORT_SYMBOL(of_find_node_by_name); @@ -479,15 +526,16 @@ struct device_node *of_find_node_by_type(struct device_node *from, const char *type) { struct device_node *np; + unsigned long flags; - read_lock(&devtree_lock); - np = from ? from->allnext : allnodes; + raw_spin_lock_irqsave(&devtree_lock, flags); + np = from ? from->allnext : of_allnodes; for (; np; np = np->allnext) if (np->type && (of_node_cmp(np->type, type) == 0) && of_node_get(np)) break; of_node_put(from); - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; } EXPORT_SYMBOL(of_find_node_by_type); @@ -510,18 +558,20 @@ struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible) { struct device_node *np; + unsigned long flags; - read_lock(&devtree_lock); - np = from ? from->allnext : allnodes; + raw_spin_lock_irqsave(&devtree_lock, flags); + np = from ? from->allnext : of_allnodes; for (; np; np = np->allnext) { if (type && !(np->type && (of_node_cmp(np->type, type) == 0))) continue; - if (of_device_is_compatible(np, compatible) && of_node_get(np)) + if (__of_device_is_compatible(np, compatible) && + of_node_get(np)) break; } of_node_put(from); - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; } EXPORT_SYMBOL(of_find_compatible_node); @@ -543,9 +593,10 @@ struct device_node *of_find_node_with_property(struct device_node *from, { struct device_node *np; struct property *pp; + unsigned long flags; - read_lock(&devtree_lock); - np = from ? from->allnext : allnodes; + raw_spin_lock_irqsave(&devtree_lock, flags); + np = from ? from->allnext : of_allnodes; for (; np; np = np->allnext) { for (pp = np->properties; pp; pp = pp->next) { if (of_prop_cmp(pp->name, prop_name) == 0) { @@ -556,20 +607,14 @@ struct device_node *of_find_node_with_property(struct device_node *from, } out: of_node_put(from); - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; } EXPORT_SYMBOL(of_find_node_with_property); -/** - * of_match_node - Tell if an device_node has a matching of_match structure - * @matches: array of of device match structures to search in - * @node: the of device structure to match against - * - * Low level utility function used by device matching. - */ -const struct of_device_id *of_match_node(const struct of_device_id *matches, - const struct device_node *node) +static +const struct of_device_id *__of_match_node(const struct of_device_id *matches, + const struct device_node *node) { if (!matches) return NULL; @@ -583,44 +628,74 @@ const struct of_device_id *of_match_node(const struct of_device_id *matches, match &= node->type && !strcmp(matches->type, node->type); if (matches->compatible[0]) - match &= of_device_is_compatible(node, - matches->compatible); + match &= __of_device_is_compatible(node, + matches->compatible); if (match) return matches; matches++; } return NULL; } + +/** + * of_match_node - Tell if an device_node has a matching of_match structure + * @matches: array of of device match structures to search in + * @node: the of device structure to match against + * + * Low level utility function used by device matching. + */ +const struct of_device_id *of_match_node(const struct of_device_id *matches, + const struct device_node *node) +{ + const struct of_device_id *match; + unsigned long flags; + + raw_spin_lock_irqsave(&devtree_lock, flags); + match = __of_match_node(matches, node); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return match; +} EXPORT_SYMBOL(of_match_node); /** - * of_find_matching_node - Find a node based on an of_device_id match - * table. + * of_find_matching_node_and_match - Find a node based on an of_device_id + * match table. * @from: The node to start searching from or NULL, the node * you pass will not be searched, only the next one * will; typically, you pass what the previous call * returned. of_node_put() will be called on it * @matches: array of of device match structures to search in + * @match Updated to point at the matches entry which matched * * Returns a node pointer with refcount incremented, use * of_node_put() on it when done. */ -struct device_node *of_find_matching_node(struct device_node *from, - const struct of_device_id *matches) +struct device_node *of_find_matching_node_and_match(struct device_node *from, + const struct of_device_id *matches, + const struct of_device_id **match) { struct device_node *np; + const struct of_device_id *m; + unsigned long flags; + + if (match) + *match = NULL; - read_lock(&devtree_lock); - np = from ? from->allnext : allnodes; + raw_spin_lock_irqsave(&devtree_lock, flags); + np = from ? from->allnext : of_allnodes; for (; np; np = np->allnext) { - if (of_match_node(matches, np) && of_node_get(np)) + m = __of_match_node(matches, np); + if (m && of_node_get(np)) { + if (match) + *match = m; break; + } } of_node_put(from); - read_unlock(&devtree_lock); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; } -EXPORT_SYMBOL(of_find_matching_node); +EXPORT_SYMBOL(of_find_matching_node_and_match); /** * of_modalias_node - Lookup appropriate modalias for a device node @@ -660,23 +735,100 @@ struct device_node *of_find_node_by_phandle(phandle handle) { struct device_node *np; - read_lock(&devtree_lock); - for (np = allnodes; np; np = np->allnext) + raw_spin_lock(&devtree_lock); + for (np = of_allnodes; np; np = np->allnext) if (np->phandle == handle) break; of_node_get(np); - read_unlock(&devtree_lock); + raw_spin_unlock(&devtree_lock); return np; } EXPORT_SYMBOL(of_find_node_by_phandle); /** + * of_property_read_u8_array - Find and read an array of u8 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. + * @sz: number of array elements to read + * + * Search for a property in a device node and read 8-bit value(s) 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. + * + * dts entry of array should be like: + * property = /bits/ 8 <0x50 0x60 0x70>; + * + * The out_value is modified only if a valid u8 value can be decoded. + */ +int of_property_read_u8_array(const struct device_node *np, + const char *propname, u8 *out_values, size_t sz) +{ + struct property *prop = of_find_property(np, propname, NULL); + const u8 *val; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if ((sz * sizeof(*out_values)) > prop->length) + return -EOVERFLOW; + + val = prop->value; + while (sz--) + *out_values++ = *val++; + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u8_array); + +/** + * of_property_read_u16_array - Find and read an array of u16 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. + * @sz: number of array elements to read + * + * Search for a property in a device node and read 16-bit value(s) 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. + * + * dts entry of array should be like: + * property = /bits/ 16 <0x5000 0x6000 0x7000>; + * + * The out_value is modified only if a valid u16 value can be decoded. + */ +int of_property_read_u16_array(const struct device_node *np, + const char *propname, u16 *out_values, size_t sz) +{ + struct property *prop = of_find_property(np, propname, NULL); + const __be16 *val; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if ((sz * sizeof(*out_values)) > prop->length) + return -EOVERFLOW; + + val = prop->value; + while (sz--) + *out_values++ = be16_to_cpup(val++); + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u16_array); + +/** * of_property_read_u32_array - Find and read an array of 32 bit integers * 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. + * @sz: number of array elements to read * * Search for a property in a device node and read 32-bit value(s) from * it. Returns 0 on success, -EINVAL if the property does not exist, @@ -893,8 +1045,8 @@ EXPORT_SYMBOL_GPL(of_property_count_strings); * Returns the device_node pointer with refcount incremented. Use * of_node_put() on it when done. */ -struct device_node * -of_parse_phandle(struct device_node *np, const char *phandle_name, int index) +struct device_node *of_parse_phandle(const struct device_node *np, + const char *phandle_name, int index) { const __be32 *phandle; int size; @@ -939,12 +1091,13 @@ EXPORT_SYMBOL(of_parse_phandle); * To get a device_node of the `node2' node you may call this: * of_parse_phandle_with_args(node3, "list", "#list-cells", 1, &args); */ -int of_parse_phandle_with_args(struct device_node *np, const char *list_name, - const char *cells_name, int index, - struct of_phandle_args *out_args) +static int __of_parse_phandle_with_args(const struct device_node *np, + const char *list_name, + const char *cells_name, int index, + struct of_phandle_args *out_args) { const __be32 *list, *list_end; - int size, cur_index = 0; + int rc = 0, size, cur_index = 0; uint32_t count = 0; struct device_node *node = NULL; phandle phandle; @@ -957,6 +1110,7 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name, /* Loop over the phandles until all the requested entry is found */ while (list < list_end) { + rc = -EINVAL; count = 0; /* @@ -973,13 +1127,13 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name, if (!node) { pr_err("%s: could not find phandle\n", np->full_name); - break; + goto err; } 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; + goto err; } /* @@ -989,7 +1143,7 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name, if (list + count > list_end) { pr_err("%s: arguments longer than property\n", np->full_name); - break; + goto err; } } @@ -999,9 +1153,10 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name, * index matches, then fill the out_args structure and return, * or return -ENOENT for an empty entry. */ + rc = -ENOENT; if (cur_index == index) { if (!phandle) - return -ENOENT; + goto err; if (out_args) { int i; @@ -1012,6 +1167,10 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name, for (i = 0; i < count; i++) out_args->args[i] = be32_to_cpup(list++); } + + /* Found it! return success */ + if (node) + of_node_put(node); return 0; } @@ -1021,34 +1180,95 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name, cur_index++; } - /* Loop exited without finding a valid entry; return an error */ + /* + * Unlock node before returning result; will be one of: + * -ENOENT : index is for empty phandle + * -EINVAL : parsing error on data + * [1..n] : Number of phandle (count mode; when index = -1) + */ + rc = index < 0 ? cur_index : -ENOENT; + err: if (node) of_node_put(node); - return -EINVAL; + return rc; +} + +int of_parse_phandle_with_args(const struct device_node *np, const char *list_name, + const char *cells_name, int index, + struct of_phandle_args *out_args) +{ + if (index < 0) + return -EINVAL; + return __of_parse_phandle_with_args(np, list_name, cells_name, index, out_args); } EXPORT_SYMBOL(of_parse_phandle_with_args); /** - * prom_add_property - Add a property to a node + * of_count_phandle_with_args() - Find the number of phandles references in a property + * @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 + * + * Returns the number of phandle + argument tuples within a property. It + * is a typical pattern to encode a list of phandle and variable + * arguments into a single property. The number of arguments is encoded + * by a property in the phandle-target node. For example, a gpios + * property would contain a list of GPIO specifies consisting of a + * phandle and 1 or more arguments. The number of arguments are + * determined by the #gpio-cells property in the node pointed to by the + * phandle. + */ +int of_count_phandle_with_args(const struct device_node *np, const char *list_name, + const char *cells_name) +{ + return __of_parse_phandle_with_args(np, list_name, cells_name, -1, NULL); +} +EXPORT_SYMBOL(of_count_phandle_with_args); + +#if defined(CONFIG_OF_DYNAMIC) +static int of_property_notify(int action, struct device_node *np, + struct property *prop) +{ + struct of_prop_reconfig pr; + + pr.dn = np; + pr.prop = prop; + return of_reconfig_notify(action, &pr); +} +#else +static int of_property_notify(int action, struct device_node *np, + struct property *prop) +{ + return 0; +} +#endif + +/** + * of_add_property - Add a property to a node */ -int prom_add_property(struct device_node *np, struct property *prop) +int of_add_property(struct device_node *np, struct property *prop) { struct property **next; unsigned long flags; + int rc; + + rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop); + if (rc) + return rc; prop->next = NULL; - write_lock_irqsave(&devtree_lock, flags); + raw_spin_lock_irqsave(&devtree_lock, flags); next = &np->properties; while (*next) { if (strcmp(prop->name, (*next)->name) == 0) { /* duplicate ! don't insert it */ - write_unlock_irqrestore(&devtree_lock, flags); + raw_spin_unlock_irqrestore(&devtree_lock, flags); return -1; } next = &(*next)->next; } *next = prop; - write_unlock_irqrestore(&devtree_lock, flags); + raw_spin_unlock_irqrestore(&devtree_lock, flags); #ifdef CONFIG_PROC_DEVICETREE /* try to add to proc as well if it was initialized */ @@ -1060,20 +1280,25 @@ int prom_add_property(struct device_node *np, struct property *prop) } /** - * prom_remove_property - Remove a property from a node. + * of_remove_property - Remove a property from a node. * * Note that we don't actually remove it, since we have given out * who-knows-how-many pointers to the data using get-property. * Instead we just move the property to the "dead properties" * list, so it won't be found any more. */ -int prom_remove_property(struct device_node *np, struct property *prop) +int of_remove_property(struct device_node *np, struct property *prop) { struct property **next; unsigned long flags; int found = 0; + int rc; + + rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop); + if (rc) + return rc; - write_lock_irqsave(&devtree_lock, flags); + raw_spin_lock_irqsave(&devtree_lock, flags); next = &np->properties; while (*next) { if (*next == prop) { @@ -1086,7 +1311,7 @@ int prom_remove_property(struct device_node *np, struct property *prop) } next = &(*next)->next; } - write_unlock_irqrestore(&devtree_lock, flags); + raw_spin_unlock_irqrestore(&devtree_lock, flags); if (!found) return -ENODEV; @@ -1101,7 +1326,7 @@ int prom_remove_property(struct device_node *np, struct property *prop) } /* - * prom_update_property - Update a property in a node, if the property does + * of_update_property - Update a property in a node, if the property does * not exist, add it. * * Note that we don't actually remove it, since we have given out @@ -1109,21 +1334,24 @@ int prom_remove_property(struct device_node *np, struct property *prop) * Instead we just move the property to the "dead properties" list, * and add the new property to the property list */ -int prom_update_property(struct device_node *np, - struct property *newprop) +int of_update_property(struct device_node *np, struct property *newprop) { struct property **next, *oldprop; unsigned long flags; - int found = 0; + int rc, found = 0; + + rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop); + if (rc) + return rc; if (!newprop->name) return -EINVAL; oldprop = of_find_property(np, newprop->name, NULL); if (!oldprop) - return prom_add_property(np, newprop); + return of_add_property(np, newprop); - write_lock_irqsave(&devtree_lock, flags); + raw_spin_lock_irqsave(&devtree_lock, flags); next = &np->properties; while (*next) { if (*next == oldprop) { @@ -1137,7 +1365,7 @@ int prom_update_property(struct device_node *np, } next = &(*next)->next; } - write_unlock_irqrestore(&devtree_lock, flags); + raw_spin_unlock_irqrestore(&devtree_lock, flags); if (!found) return -ENODEV; @@ -1160,20 +1388,87 @@ int prom_update_property(struct device_node *np, * device tree nodes. */ +static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain); + +int of_reconfig_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&of_reconfig_chain, nb); +} +EXPORT_SYMBOL_GPL(of_reconfig_notifier_register); + +int of_reconfig_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&of_reconfig_chain, nb); +} +EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister); + +int of_reconfig_notify(unsigned long action, void *p) +{ + int rc; + + rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p); + return notifier_to_errno(rc); +} + +#ifdef CONFIG_PROC_DEVICETREE +static void of_add_proc_dt_entry(struct device_node *dn) +{ + struct proc_dir_entry *ent; + + ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde); + if (ent) + proc_device_tree_add_node(dn, ent); +} +#else +static void of_add_proc_dt_entry(struct device_node *dn) +{ + return; +} +#endif + /** * of_attach_node - Plug a device node into the tree and global list. */ -void of_attach_node(struct device_node *np) +int of_attach_node(struct device_node *np) { unsigned long flags; + int rc; - write_lock_irqsave(&devtree_lock, flags); + rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); + if (rc) + return rc; + + raw_spin_lock_irqsave(&devtree_lock, flags); np->sibling = np->parent->child; - np->allnext = allnodes; + np->allnext = of_allnodes; np->parent->child = np; - allnodes = np; - write_unlock_irqrestore(&devtree_lock, flags); + of_allnodes = np; + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + of_add_proc_dt_entry(np); + return 0; +} + +#ifdef CONFIG_PROC_DEVICETREE +static void of_remove_proc_dt_entry(struct device_node *dn) +{ + struct device_node *parent = dn->parent; + struct property *prop = dn->properties; + + while (prop) { + remove_proc_entry(prop->name, dn->pde); + prop = prop->next; + } + + if (dn->pde) + remove_proc_entry(dn->pde->name, parent->pde); +} +#else +static void of_remove_proc_dt_entry(struct device_node *dn) +{ + return; } +#endif /** * of_detach_node - "Unplug" a node from the device tree. @@ -1181,22 +1476,35 @@ void of_attach_node(struct device_node *np) * The caller must hold a reference to the node. The memory associated with * the node is not freed until its refcount goes to zero. */ -void of_detach_node(struct device_node *np) +int of_detach_node(struct device_node *np) { struct device_node *parent; unsigned long flags; + int rc = 0; + + rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); + if (rc) + return rc; + + raw_spin_lock_irqsave(&devtree_lock, flags); - write_lock_irqsave(&devtree_lock, flags); + if (of_node_check_flag(np, OF_DETACHED)) { + /* someone already detached it */ + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return rc; + } parent = np->parent; - if (!parent) - goto out_unlock; + if (!parent) { + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return rc; + } - if (allnodes == np) - allnodes = np->allnext; + if (of_allnodes == np) + of_allnodes = np->allnext; else { struct device_node *prev; - for (prev = allnodes; + for (prev = of_allnodes; prev->allnext != np; prev = prev->allnext) ; @@ -1215,9 +1523,10 @@ void of_detach_node(struct device_node *np) } of_node_set_flag(np, OF_DETACHED); + raw_spin_unlock_irqrestore(&devtree_lock, flags); -out_unlock: - write_unlock_irqrestore(&devtree_lock, flags); + of_remove_proc_dt_entry(np); + return rc; } #endif /* defined(CONFIG_OF_DYNAMIC) */ diff --git a/drivers/of/device.c b/drivers/of/device.c index 4c74e4fc5a51..f685e55e0717 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -8,6 +8,7 @@ #include <linux/slab.h> #include <asm/errno.h> +#include "of_private.h" /** * of_match_device - Tell if a struct device matches an of_device_id list @@ -131,6 +132,7 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len) void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) { const char *compat; + struct alias_prop *app; int seen = 0, cplen, sl; if ((!dev) || (!dev->of_node)) @@ -153,6 +155,17 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) seen++; } add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen); + + seen = 0; + mutex_lock(&of_aliases_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (dev->of_node == app->np) { + add_uevent_var(env, "OF_ALIAS_%d=%s", seen, + app->alias); + seen++; + } + } + mutex_unlock(&of_aliases_mutex); } int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 91a375fb6ae6..808be06bb67e 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -186,6 +186,8 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob, */ fpsize = 1; allocl = 2; + l = 1; + *pathp = '\0'; } else { /* account for '/' and path size minus terminal 0 * already in 'l' @@ -198,10 +200,10 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob, np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, __alignof__(struct device_node)); if (allnextpp) { + char *fn; memset(np, 0, sizeof(*np)); - np->full_name = ((char *)np) + sizeof(struct device_node); + np->full_name = fn = ((char *)np) + sizeof(*np); if (new_format) { - char *fn = np->full_name; /* rebuild full path for new format */ if (dad && dad->parent) { strcpy(fn, dad->full_name); @@ -215,9 +217,9 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob, fn += strlen(fn); } *(fn++) = '/'; - memcpy(fn, pathp, l); - } else - memcpy(np->full_name, pathp, l); + } + memcpy(fn, pathp, l); + prev_pp = &np->properties; **allnextpp = np; *allnextpp = &np->allnext; @@ -459,7 +461,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, do { u32 tag = be32_to_cpup((__be32 *)p); - char *pathp; + const char *pathp; p += 4; if (tag == OF_DT_END_NODE) { @@ -486,14 +488,8 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, depth++; pathp = (char *)p; p = ALIGN(p + strlen(pathp) + 1, 4); - if ((*pathp) == '/') { - char *lp, *np; - for (lp = NULL, np = pathp; *np; np++) - if ((*np) == '/') - lp = np+1; - if (lp != NULL) - pathp = lp; - } + if (*pathp == '/') + pathp = kbasename(pathp); rc = it(p, pathp, depth, data); if (rc != 0) break; @@ -710,7 +706,7 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, */ void __init unflatten_device_tree(void) { - __unflatten_device_tree(initial_boot_params, &allnodes, + __unflatten_device_tree(initial_boot_params, &of_allnodes, early_init_dt_alloc_memory_arch); /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */ diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c index 3550f3bf4f92..b667264222cc 100644 --- a/drivers/of/of_i2c.c +++ b/drivers/of/of_i2c.c @@ -29,7 +29,7 @@ void of_i2c_register_devices(struct i2c_adapter *adap) dev_dbg(&adap->dev, "of_i2c: walking child nodes\n"); - for_each_child_of_node(adap->dev.of_node, node) { + for_each_available_child_of_node(adap->dev.of_node, node) { struct i2c_board_info info = {}; struct dev_archdata dev_ad = {}; const __be32 *addr; diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 8e6c25f35040..e3a8b22ef9dd 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -53,7 +53,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) return rc; /* Loop over the child nodes and register a phy_device for each one */ - for_each_child_of_node(np, child) { + for_each_available_child_of_node(np, child) { const __be32 *paddr; u32 addr; int len; @@ -157,7 +157,7 @@ struct phy_device *of_phy_connect(struct net_device *dev, if (!phy) return NULL; - return phy_connect_direct(dev, phy, hndlr, flags, iface) ? NULL : phy; + return phy_connect_direct(dev, phy, hndlr, iface) ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect); @@ -194,7 +194,7 @@ struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0])); - phy = phy_connect(dev, bus_id, hndlr, 0, iface); + phy = phy_connect(dev, bus_id, hndlr, iface); return IS_ERR(phy) ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect_fixed_link); diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h new file mode 100644 index 000000000000..ff350c8fa7ac --- /dev/null +++ b/drivers/of/of_private.h @@ -0,0 +1,36 @@ +#ifndef _LINUX_OF_PRIVATE_H +#define _LINUX_OF_PRIVATE_H +/* + * Private symbols used by OF support code + * + * Paul Mackerras August 1996. + * Copyright (C) 1996-2005 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/** + * 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]; +}; + +extern struct mutex of_aliases_mutex; +extern struct list_head aliases_lookup; +#endif /* _LINUX_OF_PRIVATE_H */ diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c index 07cc1d678e4d..37b56fd716e6 100644 --- a/drivers/of/pdt.c +++ b/drivers/of/pdt.c @@ -241,15 +241,15 @@ void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) BUG_ON(!ops); of_pdt_prom_ops = ops; - allnodes = of_pdt_create_node(root_node, NULL); + of_allnodes = of_pdt_create_node(root_node, NULL); #if defined(CONFIG_SPARC) - allnodes->path_component_name = ""; + of_allnodes->path_component_name = ""; #endif - allnodes->full_name = "/"; + of_allnodes->full_name = "/"; - nextp = &allnodes->allnext; - allnodes->child = of_pdt_build_tree(allnodes, - of_pdt_prom_ops->getchild(allnodes->phandle), &nextp); + nextp = &of_allnodes->allnext; + of_allnodes->child = of_pdt_build_tree(of_allnodes, + of_pdt_prom_ops->getchild(of_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 b80891b43816..e0a6514ab46c 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -436,6 +436,7 @@ EXPORT_SYMBOL(of_platform_bus_probe); * of_platform_populate() - Populate platform_devices from device tree data * @root: parent of the first level to probe or NULL for the root of the tree * @matches: match table, NULL to use the default + * @lookup: auxdata table for matching id and platform_data with device nodes * @parent: parent to hook devices from, NULL for toplevel * * Similar to of_platform_bus_probe(), this function walks the device tree diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index f24ffd7088d2..0eb5c38b4e07 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -2,7 +2,7 @@ * Self tests for device tree subsystem */ -#define pr_fmt(fmt) "### %s(): " fmt, __func__ +#define pr_fmt(fmt) "### dt-test ### " fmt #include <linux/clk.h> #include <linux/err.h> @@ -16,26 +16,30 @@ static bool selftest_passed = true; #define selftest(result, fmt, ...) { \ - selftest_passed &= (result); \ - if (!(result)) \ + if (!(result)) { \ pr_err("FAIL %s:%i " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ + selftest_passed = false; \ + } else { \ + pr_info("pass %s:%i\n", __FILE__, __LINE__); \ + } \ } 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; + int i, rc; - 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++) { + rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells"); + selftest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc); + + for (i = 0; i < 8; i++) { bool passed = true; rc = of_parse_phandle_with_args(np, "phandle-list", "#phandle-cells", i, &args); @@ -79,45 +83,47 @@ static void __init of_selftest_parse_phandle_with_args(void) passed &= (args.args[0] == (i + 1)); break; case 7: - passed &= (rc == -EINVAL); + passed &= (rc == -ENOENT); 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; - } + selftest(passed, "index %i - data error on node %s rc=%i\n", + i, args.np->full_name, rc); } /* Check for missing list property */ rc = of_parse_phandle_with_args(np, "phandle-list-missing", "#phandle-cells", 0, &args); - passed_all &= (rc == -EINVAL); + selftest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); + rc = of_count_phandle_with_args(np, "phandle-list-missing", + "#phandle-cells"); + selftest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); /* Check for missing cells property */ rc = of_parse_phandle_with_args(np, "phandle-list", "#phandle-cells-missing", 0, &args); - passed_all &= (rc == -EINVAL); + selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + rc = of_count_phandle_with_args(np, "phandle-list", + "#phandle-cells-missing"); + selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); /* 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); + selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + rc = of_count_phandle_with_args(np, "phandle-list-bad-phandle", + "#phandle-cells"); + selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); /* 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"); + selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + rc = of_count_phandle_with_args(np, "phandle-list-bad-args", + "#phandle-cells"); + selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); } static void __init of_selftest_property_match_string(void) |