diff options
Diffstat (limited to 'drivers/of/overlay.c')
-rw-r--r-- | drivers/of/overlay.c | 142 |
1 files changed, 121 insertions, 21 deletions
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index c0e4ee1cd1ba..8ecfee31ab6d 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -35,6 +35,7 @@ struct of_overlay_info { struct device_node *target; struct device_node *overlay; + bool is_symbols_node; }; /** @@ -55,7 +56,8 @@ struct of_overlay { }; static int of_overlay_apply_one(struct of_overlay *ov, - struct device_node *target, const struct device_node *overlay); + struct device_node *target, const struct device_node *overlay, + bool is_symbols_node); static BLOCKING_NOTIFIER_HEAD(of_overlay_chain); @@ -92,10 +94,74 @@ static int of_overlay_notify(struct of_overlay *ov, return 0; } +static struct property *dup_and_fixup_symbol_prop(struct of_overlay *ov, + const struct property *prop) +{ + struct of_overlay_info *ovinfo; + struct property *new; + const char *overlay_name; + char *label_path; + char *symbol_path; + const char *target_path; + int k; + int label_path_len; + int overlay_name_len; + int target_path_len; + + if (!prop->value) + return NULL; + symbol_path = prop->value; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + for (k = 0; k < ov->count; k++) { + ovinfo = &ov->ovinfo_tab[k]; + overlay_name = ovinfo->overlay->full_name; + overlay_name_len = strlen(overlay_name); + if (!strncasecmp(symbol_path, overlay_name, overlay_name_len)) + break; + } + + if (k >= ov->count) + goto err_free; + + target_path = ovinfo->target->full_name; + target_path_len = strlen(target_path); + + label_path = symbol_path + overlay_name_len; + label_path_len = strlen(label_path); + + new->name = kstrdup(prop->name, GFP_KERNEL); + new->length = target_path_len + label_path_len + 1; + new->value = kzalloc(new->length, GFP_KERNEL); + + if (!new->name || !new->value) + goto err_free; + + strcpy(new->value, target_path); + strcpy(new->value + target_path_len, label_path); + + /* mark the property as dynamic */ + of_property_set_flag(new, OF_DYNAMIC); + + return new; + + err_free: + kfree(new->name); + kfree(new->value); + kfree(new); + return NULL; + + +} + static int of_overlay_apply_single_property(struct of_overlay *ov, - struct device_node *target, struct property *prop) + struct device_node *target, struct property *prop, + bool is_symbols_node) { - struct property *propn, *tprop; + struct property *propn = NULL, *tprop; /* NOTE: Multiple changes of single properties not supported */ tprop = of_find_property(target, prop->name, NULL); @@ -106,7 +172,15 @@ static int of_overlay_apply_single_property(struct of_overlay *ov, of_prop_cmp(prop->name, "linux,phandle") == 0) return 0; - propn = __of_prop_dup(prop, GFP_KERNEL); + if (is_symbols_node) { + /* changing a property in __symbols__ node not allowed */ + if (tprop) + return -EINVAL; + propn = dup_and_fixup_symbol_prop(ov, prop); + } else { + propn = __of_prop_dup(prop, GFP_KERNEL); + } + if (propn == NULL) return -ENOMEM; @@ -130,18 +204,21 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, return -ENOMEM; /* NOTE: Multiple mods of created nodes not supported */ - tchild = of_get_child_by_name(target, cname); + for_each_child_of_node(target, tchild) + if (!of_node_cmp(cname, kbasename(tchild->full_name))) + break; + if (tchild != NULL) { /* new overlay phandle value conflicts with existing value */ if (child->phandle) return -EINVAL; /* apply overlay recursively */ - ret = of_overlay_apply_one(ov, tchild, child); + ret = of_overlay_apply_one(ov, tchild, child, 0); of_node_put(tchild); } else { /* create empty tree as a target */ - tchild = __of_node_dup(child, "%s/%s", target->full_name, cname); + tchild = __of_node_dup(child, "%pOF/%s", target, cname); if (!tchild) return -ENOMEM; @@ -152,7 +229,7 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, if (ret) return ret; - ret = of_overlay_apply_one(ov, tchild, child); + ret = of_overlay_apply_one(ov, tchild, child, 0); if (ret) return ret; } @@ -168,26 +245,32 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, * by using the changeset. */ static int of_overlay_apply_one(struct of_overlay *ov, - struct device_node *target, const struct device_node *overlay) + struct device_node *target, const struct device_node *overlay, + bool is_symbols_node) { struct device_node *child; struct property *prop; int ret; for_each_property_of_node(overlay, prop) { - ret = of_overlay_apply_single_property(ov, target, prop); + ret = of_overlay_apply_single_property(ov, target, prop, + is_symbols_node); if (ret) { - pr_err("Failed to apply prop @%s/%s\n", - target->full_name, prop->name); + pr_err("Failed to apply prop @%pOF/%s\n", + target, prop->name); return ret; } } + /* do not allow symbols node to have any children */ + if (is_symbols_node) + return 0; + for_each_child_of_node(overlay, child) { ret = of_overlay_apply_single_device_node(ov, target, child); if (ret != 0) { - pr_err("Failed to apply single node @%s/%s\n", - target->full_name, child->name); + pr_err("Failed to apply single node @%pOF/%s\n", + target, child->name); of_node_put(child); return ret; } @@ -213,9 +296,10 @@ static int of_overlay_apply(struct of_overlay *ov) for (i = 0; i < ov->count; i++) { struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; - err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay); + err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay, + ovinfo->is_symbols_node); if (err != 0) { - pr_err("apply failed '%s'\n", ovinfo->target->full_name); + pr_err("apply failed '%pOF'\n", ovinfo->target); return err; } } @@ -311,6 +395,9 @@ static int of_build_overlay_info(struct of_overlay *ov, for_each_child_of_node(tree, node) cnt++; + if (of_get_child_by_name(tree, "__symbols__")) + cnt++; + ovinfo = kcalloc(cnt, sizeof(*ovinfo), GFP_KERNEL); if (ovinfo == NULL) return -ENOMEM; @@ -322,6 +409,20 @@ static int of_build_overlay_info(struct of_overlay *ov, cnt++; } + node = of_get_child_by_name(tree, "__symbols__"); + if (node) { + ovinfo[cnt].overlay = node; + ovinfo[cnt].target = of_find_node_by_path("/__symbols__"); + ovinfo[cnt].is_symbols_node = 1; + + if (!ovinfo[cnt].target) { + pr_err("no symbols in root of device tree.\n"); + return -EINVAL; + } + + cnt++; + } + /* if nothing filled, return error */ if (cnt == 0) { kfree(ovinfo); @@ -400,8 +501,8 @@ int of_overlay_create(struct device_node *tree) /* build the overlay info structures */ err = of_build_overlay_info(ov, tree); if (err) { - pr_err("of_build_overlay_info() failed for tree@%s\n", - tree->full_name); + pr_err("of_build_overlay_info() failed for tree@%pOF\n", + tree); goto err_free_idr; } @@ -480,9 +581,8 @@ static int overlay_is_topmost(struct of_overlay *ov, struct device_node *dn) /* check against each subtree affected by this overlay */ list_for_each_entry(ce, &ovt->cset.entries, node) { if (overlay_subtree_check(ce->np, dn)) { - pr_err("%s: #%d clashes #%d @%s\n", - __func__, ov->id, ovt->id, - dn->full_name); + pr_err("%s: #%d clashes #%d @%pOF\n", + __func__, ov->id, ovt->id, dn); return 0; } } |