diff options
Diffstat (limited to 'drivers/of/base.c')
-rw-r--r-- | drivers/of/base.c | 201 |
1 files changed, 199 insertions, 2 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index 74eaedd5b860..09692c9b32a7 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/bitmap.h> #include <linux/console.h> #include <linux/ctype.h> #include <linux/cpu.h> @@ -67,6 +68,7 @@ bool of_node_name_eq(const struct device_node *np, const char *name) return (strlen(name) == len) && (strncmp(node_name, name, len) == 0); } +EXPORT_SYMBOL(of_node_name_eq); bool of_node_name_prefix(const struct device_node *np, const char *prefix) { @@ -75,6 +77,7 @@ bool of_node_name_prefix(const struct device_node *np, const char *prefix) return strncmp(kbasename(np->full_name), prefix, strlen(prefix)) == 0; } +EXPORT_SYMBOL(of_node_name_prefix); int of_n_addr_cells(struct device_node *np) { @@ -330,6 +333,8 @@ static bool __of_find_n_match_cpu_property(struct device_node *cpun, ac = of_n_addr_cells(cpun); cell = of_get_property(cpun, prop_name, &prop_len); + if (!cell && !ac && arch_match_cpu_phys_id(cpu, 0)) + return true; if (!cell || !ac) return false; prop_len /= sizeof(*cell) * ac; @@ -390,7 +395,7 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) { struct device_node *cpun; - for_each_node_by_type(cpun, "cpu") { + for_each_of_cpu_node(cpun) { if (arch_find_n_match_cpu_physical_id(cpun, cpu, thread)) return cpun; } @@ -745,6 +750,43 @@ struct device_node *of_get_next_available_child(const struct device_node *node, EXPORT_SYMBOL(of_get_next_available_child); /** + * of_get_next_cpu_node - Iterate on cpu nodes + * @prev: previous child of the /cpus node, or NULL to get first + * + * Returns a cpu node pointer with refcount incremented, use of_node_put() + * on it when done. Returns NULL when prev is the last child. Decrements + * the refcount of prev. + */ +struct device_node *of_get_next_cpu_node(struct device_node *prev) +{ + struct device_node *next = NULL; + unsigned long flags; + struct device_node *node; + + if (!prev) + node = of_find_node_by_path("/cpus"); + + raw_spin_lock_irqsave(&devtree_lock, flags); + if (prev) + next = prev->sibling; + else if (node) { + next = node->child; + of_node_put(node); + } + for (; next; next = next->sibling) { + if (!(of_node_name_eq(next, "cpu") || + (next->type && !of_node_cmp(next->type, "cpu")))) + continue; + if (of_node_get(next)) + break; + } + of_node_put(prev); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return next; +} +EXPORT_SYMBOL(of_get_next_cpu_node); + +/** * of_get_compatible_child - Find compatible child node * @parent: parent node * @compatible: compatible string @@ -1943,6 +1985,59 @@ int of_alias_get_id(struct device_node *np, const char *stem) EXPORT_SYMBOL_GPL(of_alias_get_id); /** + * of_alias_get_alias_list - Get alias list for the given device driver + * @matches: Array of OF device match structures to search in + * @stem: Alias stem of the given device_node + * @bitmap: Bitmap field pointer + * @nbits: Maximum number of alias IDs which can be recorded in bitmap + * + * The function travels the lookup table to record alias ids for the given + * device match structures and alias stem. + * + * Return: 0 or -ENOSYS when !CONFIG_OF or + * -EOVERFLOW if alias ID is greater then allocated nbits + */ +int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits) +{ + struct alias_prop *app; + int ret = 0; + + /* Zero bitmap field to make sure that all the time it is clean */ + bitmap_zero(bitmap, nbits); + + mutex_lock(&of_mutex); + pr_debug("%s: Looking for stem: %s\n", __func__, stem); + list_for_each_entry(app, &aliases_lookup, link) { + pr_debug("%s: stem: %s, id: %d\n", + __func__, app->stem, app->id); + + if (strcmp(app->stem, stem) != 0) { + pr_debug("%s: stem comparison didn't pass %s\n", + __func__, app->stem); + continue; + } + + if (of_match_node(matches, app->np)) { + pr_debug("%s: Allocated ID %d\n", __func__, app->id); + + if (app->id >= nbits) { + pr_warn("%s: ID %d >= than bitmap field %d\n", + __func__, app->id, nbits); + ret = -EOVERFLOW; + } else { + set_bit(app->id, bitmap); + } + } + } + mutex_unlock(&of_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(of_alias_get_alias_list); + +/** * of_alias_get_highest_id - Get highest alias id for the given stem * @stem: Alias stem to be examined * @@ -2013,7 +2108,7 @@ struct device_node *of_find_next_cache_node(const struct device_node *np) /* OF on pmac has nodes instead of properties named "l2-cache" * beneath CPU nodes. */ - if (!strcmp(np->type, "cpu")) + if (IS_ENABLED(CONFIG_PPC_PMAC) && !strcmp(np->type, "cpu")) for_each_child_of_node(np, child) if (!strcmp(child->type, "cache")) return child; @@ -2045,3 +2140,105 @@ int of_find_last_cache_level(unsigned int cpu) return cache_level; } + +/** + * of_map_rid - Translate a requester ID through a downstream mapping. + * @np: root complex device node. + * @rid: device requester ID to map. + * @map_name: property name of the map to use. + * @map_mask_name: optional property name of the mask to use. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Given a device requester ID, look up the appropriate implementation-defined + * platform ID and/or the target device which receives transactions on that + * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or + * @id_out may be NULL if only the other is required. If @target points to + * a non-NULL device node pointer, only entries targeting that node will be + * matched; if it points to a NULL value, it will receive the device node of + * the first matching target phandle, with a reference held. + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_rid(struct device_node *np, u32 rid, + const char *map_name, const char *map_mask_name, + struct device_node **target, u32 *id_out) +{ + u32 map_mask, masked_rid; + int map_len; + const __be32 *map = NULL; + + if (!np || !map_name || (!target && !id_out)) + return -EINVAL; + + map = of_get_property(np, map_name, &map_len); + if (!map) { + if (target) + return -ENODEV; + /* Otherwise, no map implies no translation */ + *id_out = rid; + return 0; + } + + if (!map_len || map_len % (4 * sizeof(*map))) { + pr_err("%pOF: Error: Bad %s length: %d\n", np, + map_name, map_len); + return -EINVAL; + } + + /* The default is to select all bits. */ + map_mask = 0xffffffff; + + /* + * Can be overridden by "{iommu,msi}-map-mask" property. + * If of_property_read_u32() fails, the default is used. + */ + if (map_mask_name) + of_property_read_u32(np, map_mask_name, &map_mask); + + masked_rid = map_mask & rid; + for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) { + struct device_node *phandle_node; + u32 rid_base = be32_to_cpup(map + 0); + u32 phandle = be32_to_cpup(map + 1); + u32 out_base = be32_to_cpup(map + 2); + u32 rid_len = be32_to_cpup(map + 3); + + if (rid_base & ~map_mask) { + pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores rid-base (0x%x)\n", + np, map_name, map_name, + map_mask, rid_base); + return -EFAULT; + } + + if (masked_rid < rid_base || masked_rid >= rid_base + rid_len) + continue; + + phandle_node = of_find_node_by_phandle(phandle); + if (!phandle_node) + return -ENODEV; + + if (target) { + if (*target) + of_node_put(phandle_node); + else + *target = phandle_node; + + if (*target != phandle_node) + continue; + } + + if (id_out) + *id_out = masked_rid - rid_base + out_base; + + pr_debug("%pOF: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n", + np, map_name, map_mask, rid_base, out_base, + rid_len, rid, masked_rid - rid_base + out_base); + return 0; + } + + pr_err("%pOF: Invalid %s translation - no match for rid 0x%x on %pOF\n", + np, map_name, rid, target && *target ? *target : NULL); + return -EFAULT; +} +EXPORT_SYMBOL_GPL(of_map_rid); |