From: Pantelis Antoniou <pantelis.antoniou@konsulko.com> To: Grant Likely <grant.likely@secretlab.ca> Cc: Matt Porter <matt.porter@linaro.org>, Koen Kooi <koen@dominion.thruhere.net>, Guenter Roeck <linux@roeck-us.net>, Ludovic Desroches <ludovic.desroches@atmel.com>, Rob Herring <robherring2@gmail.com>, Tony Lindgren <tony@atomide.com>, Nicolas Ferre <nicolas.ferre@atmel.com>, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Pantelis Antoniou <pantelis.antoniou@konsulko.com>, Pantelis Antoniou <panto@antoniou-consulting.com> Subject: [PATCH 2/4] of: DT quirks infrastructure Date: Wed, 18 Feb 2015 16:59:34 +0200 [thread overview] Message-ID: <1424271576-1952-3-git-send-email-pantelis.antoniou@konsulko.com> (raw) In-Reply-To: <1424271576-1952-1-git-send-email-pantelis.antoniou@konsulko.com> Implement a method of applying DT quirks early in the boot sequence. A DT quirk is a subtree of the boot DT that can be applied to a target in the base DT resulting in a modification of the live tree. The format of the quirk nodes is that of a device tree overlay. For details please refer to Documentation/devicetree/quirks.txt Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> --- Documentation/devicetree/quirks.txt | 101 ++++++++++ drivers/of/dynamic.c | 358 ++++++++++++++++++++++++++++++++++++ include/linux/of.h | 16 ++ 3 files changed, 475 insertions(+) create mode 100644 Documentation/devicetree/quirks.txt diff --git a/Documentation/devicetree/quirks.txt b/Documentation/devicetree/quirks.txt new file mode 100644 index 0000000..789319a --- /dev/null +++ b/Documentation/devicetree/quirks.txt @@ -0,0 +1,101 @@ +A Device Tree quirk is the way which allows modification of the +boot device tree under the control of a per-platform specific method. + +Take for instance the case of a board family that comprises of a +number of different board revisions, all being incremental changes +after an initial release. + +Since all board revisions must be supported via a single software image +the only way to support this scheme is by having a different DTB for each +revision with the bootloader selecting which one to use at boot time. + +While this may in theory work, in practice it is very cumbersome +for the following reasons: + +1. The act of selecting a different boot device tree blob requires +a reasonably advanced bootloader with some kind of configuration or +scripting capabilities. Sadly this is not the case many times, the +bootloader is extremely dumb and can only use a single dt blob. + +2. On many instances boot time is extremely critical; in some cases +there are hard requirements like having working video feeds in under +2 seconds from power-up. This leaves an extremely small time budget for +boot-up, as low as 500ms to kernel entry. The sanest way to get there +is by removing the standard bootloader from the normal boot sequence +altogether by having a very small boot shim that loads the kernel and +immediately jumps to kernel, like falcon-boot mode in u-boot does. + +3. Having different DTBs/DTSs for different board revisions easily leads to +drift between versions. Since no developer is expected to have every single +board revision at hand, things are easy to get out of sync, with board versions +failing to boot even though the kernel is up to date. + +4. One final problem is the static way that device tree works. +For example it might be desirable for various boards to have a way to +selectively configure the boot device tree, possibly by the use of command +line options. For instance a device might be disabled if a given command line +option is present, different configuration to various devices for debugging +purposes can be selected and so on. Currently the only way to do so is by +recompiling the DTS and installing, which is an chore for developers and +a completely unreasonable expectation from end-users. + +Device Tree quirks solve all those problems by having an in-kernel interface +which per-board/platform method can use to selectively modify the device tree +right after unflattening. + +A DT quirk is a subtree of the boot DT that can be applied to +a target in the base DT resulting in a modification of the live +tree. The format of the quirk nodes is that of a device tree overlay. + +As an example the following DTS contains a quirk. + +/ { + foo: foo-node { + bar = <10>; + }; + + select-quirk = <&quirk>; + + quirk: quirk { + fragment@0 { + target = <&foo>; + __overlay { + bar = <0xf00>; + baz = <11>; + }; + }; + }; +}; + +The quirk when applied would result at the following tree: + +/ { + foo: foo-node { + bar = <0xf00>; + baz = <11>; + }; + + select-quirk = <&quirk>; + + quirk: quirk { + fragment@0 { + target = <&foo>; + __overlay { + bar = <0xf00>; + baz = <11>; + }; + }; + }; + +}; + +The two public method used to accomplish this are of_quirk_apply_by_node() +and of_quirk_apply_by_phandle(); + +To apply the quirk, a per-platform method can retrieve the phandle from the +select-quirk property and pass it to the of_quirk_apply_by_phandle() node. + +The method which the per-platform method is using to select the quirk to apply +is out of the scope of the DT quirk definition, but possible methods include +and are not limited to: revision encoding in a GPIO input range, board id +located in external or internal EEPROM or flash, DMI board ids, etc. diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 3351ef4..d275dc7 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -7,6 +7,7 @@ */ #include <linux/of.h> +#include <linux/of_fdt.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/string.h> @@ -779,3 +780,360 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action, list_add_tail(&ce->node, &ocs->entries); return 0; } + +/* fixup a symbol entry for a quirk if it exists */ +static int quirk_fixup_symbol(struct device_node *dns, struct device_node *dnp) +{ + struct device_node *dn; + struct property *prop; + const char *names, *namep; + int lens, lenp; + char *p; + + dn = of_find_node_by_path("/__symbols__"); + if (!dn) + return 0; + + names = of_node_full_name(dns); + lens = strlen(names); + namep = of_node_full_name(dnp); + lenp = strlen(namep); + for_each_property_of_node(dn, prop) { + /* be very concervative at matching */ + if (lens == (prop->length - 1) && + ((const char *)prop->value)[prop->length] == '\0' && + strcmp(prop->value, names) == 0) + break; + } + if (prop == NULL) + return 0; + p = early_init_dt_alloc_memory_arch(lenp + 1, __alignof__(char)); + if (!p) { + pr_err("%s: symbol fixup %s failed\n", __func__, prop->name); + return -ENOMEM; + } + strcpy(p, namep); + + pr_debug("%s: symbol fixup %s: %s -> %s\n", __func__, + prop->name, names, namep); + + prop->value = p; + prop->length = lenp + 1; + + return 0; +} + +/* create a new quirk node */ +static struct device_node *new_quirk_node( + struct device_node *dns, + struct device_node *dnt, + const char *name) +{ + struct device_node *dnp; + int dnlen, len, ret; + struct property **pp, *prop; + char *p; + + dnlen = strlen(dnt->full_name); + len = dnlen + 1 + strlen(name) + 1; + dnp = early_init_dt_alloc_memory_arch( + sizeof(struct device_node) + len, + __alignof__(struct device_node)); + if (dnp == NULL) { + pr_err("%s: allocation failure at %pO\n", __func__, + dns); + return NULL; + } + memset(dnp, 0, sizeof(*dnp)); + of_node_init(dnp); + p = (char *)dnp + sizeof(*dnp); + + /* build full name */ + dnp->full_name = p; + memcpy(p, dnt->full_name, dnlen); + p += dnlen; + if (dnlen != 1) + *p++ = '/'; + strcpy(p, name); + + dnp->parent = dnt; + + /* we now move the phandle properties */ + for (pp = &dns->properties; (prop = *pp) != NULL; ) { + + /* do not touch normal properties */ + if (strcmp(prop->name, "name") && + strcmp(prop->name, "phandle") && + strcmp(prop->name, "linux,phandle") && + strcmp(prop->name, "ibm,phandle")) { + pp = &(*pp)->next; + continue; + } + + /* move to the new node */ + *pp = prop->next; + /* don't advance */ + + prop->next = dnp->properties; + dnp->properties = prop; + + if ((strcmp(prop->name, "phandle") == 0 || + strcmp(prop->name, "linux,phandle") == 0 || + strcmp(prop->name, "ibm,phandle") == 0) && + dnp->phandle == 0) { + dnp->phandle = be32_to_cpup(prop->value); + /* remove the phandle from the source */ + dns->phandle = 0; + } + } + + dnp->name = of_get_property(dnp, "name", NULL); + dnp->type = of_get_property(dnp, "device_type", NULL); + if (!dnp->name) + dnp->name = "<NULL>"; + if (!dnp->type) + dnp->type = "<NULL>"; + + ret = quirk_fixup_symbol(dns, dnp); + if (ret != 0) + pr_warn("%s: Failed to fixup symbol %pO\n", __func__, dnp); + + return dnp; +} + +/* apply a quirk fragment node recursively */ +static int of_apply_quirk_fragment_node(struct device_node *dn, + struct device_node *dnt) +{ + struct property *prop, *tprop, **pp; + struct device_node *dnp, **dnpp, *child; + const char *name, *namet; + int i, ret; + + if (!dn || !dnt) + return -EINVAL; + + /* iterate over all properties */ + for (pp = &dn->properties; (prop = *pp) != NULL; pp = &prop->next) { + + /* do not touch auto-generated properties */ + if (!strcmp(prop->name, "name") || + !strcmp(prop->name, "phandle") || + !strcmp(prop->name, "linux,phandle") || + !strcmp(prop->name, "ibm,phandle") || + !strcmp(prop->name, "__remove_property__") || + !strcmp(prop->name, "__remove_node__")) + continue; + + pr_debug("%s: change property %s from %pO to %pO\n", + __func__, prop->name, dn, dnt); + + tprop = of_find_property(dnt, prop->name, NULL); + if (tprop) { + tprop->value = prop->value; + tprop->length = prop->length; + continue; + } + tprop = early_init_dt_alloc_memory_arch( + sizeof(struct property), + __alignof__(struct property)); + if (!tprop) { + pr_err("%s: allocation failure at %pO\n", __func__, + dn); + return -ENOMEM; + } + tprop->name = prop->name; + tprop->value = prop->value; + tprop->length = prop->length; + + /* link */ + tprop->next = dnt->properties; + dnt->properties = tprop; + } + + /* now handle property removals (if any) */ + for (i = 0; of_property_read_string_index(dn, "__remove_property__", + i, &name) == 0; i++) { + + /* remove property directly (we don't care about dead props) */ + for (pp = &dnt->properties; (prop = *pp) != NULL; + pp = &prop->next) { + if (!strcmp(prop->name, name)) { + *pp = prop->next; + pr_info("%s: remove property %s at %pO\n", + __func__, name, dnt); + break; + } + } + } + + /* now handle node removals (if any) */ + for (i = 0; of_property_read_string_index(dn, "__remove_node__", + i, &name) == 0; i++) { + + /* remove node directly (we don't care about dead props) */ + for (dnpp = &dnt->child; (dnp = *dnpp) != NULL; + dnpp = &dnp->sibling) { + + /* find path component */ + namet = strrchr(dnp->full_name, '/'); + if (!namet) /* root */ + namet = dnp->full_name; + else + namet++; + if (!strcmp(namet, name)) { + *dnpp = dnp->sibling; + pr_info("%s: remove node %s at %pO\n", + __func__, namet, dnt); + break; + } + } + } + + /* now iterate over childen */ + for_each_child_of_node(dn, child) { + /* locate path component */ + name = strrchr(child->full_name, '/'); + if (name == NULL) /* root? */ + name = child->full_name; + else + name++; + + /* find node (if it exists) */ + for (dnpp = &dnt->child; (dnp = *dnpp) != NULL; + dnpp = &dnp->sibling) { + + namet = strrchr(dnp->full_name, '/'); + if (!namet) /* root */ + namet = dnp->full_name; + else + namet++; + + if (!strcmp(namet, name)) + break; + } + + /* not found, create node */ + if (dnp == NULL) { + dnp = new_quirk_node(child, dnt, name); + if (dnp == NULL) { + pr_err("%s: allocation failure at %pO\n", + __func__, dn); + of_node_put(child); + return -ENOMEM; + } + dnp->sibling = *dnpp; + *dnpp = dnp; + + pr_debug("%s: new node %pO\n", __func__, dnp); + } + pr_debug("%s: recursing %pO\n", __func__, dnp); + + ret = of_apply_quirk_fragment_node(child, dnp); + if (ret != 0) { + of_node_put(child); + return ret; + } + } + + return 0; +} + +/* apply a single quirk fragment located at dn */ +static int of_apply_single_quirk_fragment(struct device_node *dn) +{ + struct device_node *dnt, *dno; + const char *path; + u32 val; + int ret; + + /* first try to go by using the target as a phandle */ + dno = NULL; + dnt = NULL; + ret = of_property_read_u32(dn, "target", &val); + if (ret == 0) + dnt = of_find_node_by_phandle(val); + + if (dnt == NULL) { + /* now try to locate by path */ + ret = of_property_read_string(dn, "target-path", + &path); + if (ret == 0) + dnt = of_find_node_by_path(path); + } + + if (dnt == NULL) { + pr_err("%s: Failed to find target for node %pO\n", + __func__, dn); + ret = -ENODEV; + goto out; + } + + pr_debug("%s: Found target at %pO\n", __func__, dnt); + dno = of_get_child_by_name(dn, "__overlay__"); + if (!dno) { + pr_err("%s: Failed to find overlay node %pO\n", __func__, dn); + ret = -ENODEV; + goto out; + } + + ret = of_apply_quirk_fragment_node(dno, dnt); +out: + of_node_put(dno); + of_node_put(dnt); + + return ret; +} + +/** + * of_quirk_apply_by_node - Apply a DT quirk found at the given node + * + * @dn: device node pointer to the quirk + * + * Returns 0 on success, a negative error value in case of an error. + */ +int of_quirk_apply_by_node(struct device_node *dn) +{ + struct device_node *child; + int ret; + + if (!dn) + return -ENODEV; + + pr_debug("Apply quirk at %pO\n", dn); + + ret = 0; + for_each_child_of_node(dn, child) { + ret = of_apply_single_quirk_fragment(child); + if (ret != 0) { + of_node_put(child); + break; + } + } + + return ret; +} + +/** + * of_quirk_apply_by_node - Apply a DT quirk found by the given phandle + * + * ph: phandle of the quirk node + * + * Returns 0 on success, a negative error value in case of an error. + */ +int of_quirk_apply_by_phandle(phandle ph) +{ + struct device_node *dn; + int ret; + + dn = of_find_node_by_phandle(ph); + if (!dn) { + pr_err("Failed to find node with phandle %u\n", ph); + return -ENODEV; + } + + ret = of_quirk_apply_by_node(dn); + of_node_put(dn); + + return ret; +} diff --git a/include/linux/of.h b/include/linux/of.h index 7ede449..02d8988 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1075,4 +1075,20 @@ static inline int of_overlay_destroy_all(void) #endif +/* early boot quirks */ +#ifdef CONFIG_OF_DYNAMIC +int of_quirk_apply_by_node(struct device_node *dn); +int of_quirk_apply_by_phandle(phandle ph); +#else +static inline int of_quirk_apply_by_node(struct device_node *dn) +{ + return -ENOTSUPP; +} + +int of_quirk_apply_by_phandle(phandle ph) +{ + return -ENOTSUPP; +} +#endif + #endif /* _LINUX_OF_H */ -- 1.7.12
WARNING: multiple messages have this Message-ID (diff)
From: pantelis.antoniou@konsulko.com (Pantelis Antoniou) To: linux-arm-kernel@lists.infradead.org Subject: [PATCH 2/4] of: DT quirks infrastructure Date: Wed, 18 Feb 2015 16:59:34 +0200 [thread overview] Message-ID: <1424271576-1952-3-git-send-email-pantelis.antoniou@konsulko.com> (raw) In-Reply-To: <1424271576-1952-1-git-send-email-pantelis.antoniou@konsulko.com> Implement a method of applying DT quirks early in the boot sequence. A DT quirk is a subtree of the boot DT that can be applied to a target in the base DT resulting in a modification of the live tree. The format of the quirk nodes is that of a device tree overlay. For details please refer to Documentation/devicetree/quirks.txt Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> --- Documentation/devicetree/quirks.txt | 101 ++++++++++ drivers/of/dynamic.c | 358 ++++++++++++++++++++++++++++++++++++ include/linux/of.h | 16 ++ 3 files changed, 475 insertions(+) create mode 100644 Documentation/devicetree/quirks.txt diff --git a/Documentation/devicetree/quirks.txt b/Documentation/devicetree/quirks.txt new file mode 100644 index 0000000..789319a --- /dev/null +++ b/Documentation/devicetree/quirks.txt @@ -0,0 +1,101 @@ +A Device Tree quirk is the way which allows modification of the +boot device tree under the control of a per-platform specific method. + +Take for instance the case of a board family that comprises of a +number of different board revisions, all being incremental changes +after an initial release. + +Since all board revisions must be supported via a single software image +the only way to support this scheme is by having a different DTB for each +revision with the bootloader selecting which one to use at boot time. + +While this may in theory work, in practice it is very cumbersome +for the following reasons: + +1. The act of selecting a different boot device tree blob requires +a reasonably advanced bootloader with some kind of configuration or +scripting capabilities. Sadly this is not the case many times, the +bootloader is extremely dumb and can only use a single dt blob. + +2. On many instances boot time is extremely critical; in some cases +there are hard requirements like having working video feeds in under +2 seconds from power-up. This leaves an extremely small time budget for +boot-up, as low as 500ms to kernel entry. The sanest way to get there +is by removing the standard bootloader from the normal boot sequence +altogether by having a very small boot shim that loads the kernel and +immediately jumps to kernel, like falcon-boot mode in u-boot does. + +3. Having different DTBs/DTSs for different board revisions easily leads to +drift between versions. Since no developer is expected to have every single +board revision at hand, things are easy to get out of sync, with board versions +failing to boot even though the kernel is up to date. + +4. One final problem is the static way that device tree works. +For example it might be desirable for various boards to have a way to +selectively configure the boot device tree, possibly by the use of command +line options. For instance a device might be disabled if a given command line +option is present, different configuration to various devices for debugging +purposes can be selected and so on. Currently the only way to do so is by +recompiling the DTS and installing, which is an chore for developers and +a completely unreasonable expectation from end-users. + +Device Tree quirks solve all those problems by having an in-kernel interface +which per-board/platform method can use to selectively modify the device tree +right after unflattening. + +A DT quirk is a subtree of the boot DT that can be applied to +a target in the base DT resulting in a modification of the live +tree. The format of the quirk nodes is that of a device tree overlay. + +As an example the following DTS contains a quirk. + +/ { + foo: foo-node { + bar = <10>; + }; + + select-quirk = <&quirk>; + + quirk: quirk { + fragment at 0 { + target = <&foo>; + __overlay { + bar = <0xf00>; + baz = <11>; + }; + }; + }; +}; + +The quirk when applied would result at the following tree: + +/ { + foo: foo-node { + bar = <0xf00>; + baz = <11>; + }; + + select-quirk = <&quirk>; + + quirk: quirk { + fragment at 0 { + target = <&foo>; + __overlay { + bar = <0xf00>; + baz = <11>; + }; + }; + }; + +}; + +The two public method used to accomplish this are of_quirk_apply_by_node() +and of_quirk_apply_by_phandle(); + +To apply the quirk, a per-platform method can retrieve the phandle from the +select-quirk property and pass it to the of_quirk_apply_by_phandle() node. + +The method which the per-platform method is using to select the quirk to apply +is out of the scope of the DT quirk definition, but possible methods include +and are not limited to: revision encoding in a GPIO input range, board id +located in external or internal EEPROM or flash, DMI board ids, etc. diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 3351ef4..d275dc7 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -7,6 +7,7 @@ */ #include <linux/of.h> +#include <linux/of_fdt.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/string.h> @@ -779,3 +780,360 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action, list_add_tail(&ce->node, &ocs->entries); return 0; } + +/* fixup a symbol entry for a quirk if it exists */ +static int quirk_fixup_symbol(struct device_node *dns, struct device_node *dnp) +{ + struct device_node *dn; + struct property *prop; + const char *names, *namep; + int lens, lenp; + char *p; + + dn = of_find_node_by_path("/__symbols__"); + if (!dn) + return 0; + + names = of_node_full_name(dns); + lens = strlen(names); + namep = of_node_full_name(dnp); + lenp = strlen(namep); + for_each_property_of_node(dn, prop) { + /* be very concervative at matching */ + if (lens == (prop->length - 1) && + ((const char *)prop->value)[prop->length] == '\0' && + strcmp(prop->value, names) == 0) + break; + } + if (prop == NULL) + return 0; + p = early_init_dt_alloc_memory_arch(lenp + 1, __alignof__(char)); + if (!p) { + pr_err("%s: symbol fixup %s failed\n", __func__, prop->name); + return -ENOMEM; + } + strcpy(p, namep); + + pr_debug("%s: symbol fixup %s: %s -> %s\n", __func__, + prop->name, names, namep); + + prop->value = p; + prop->length = lenp + 1; + + return 0; +} + +/* create a new quirk node */ +static struct device_node *new_quirk_node( + struct device_node *dns, + struct device_node *dnt, + const char *name) +{ + struct device_node *dnp; + int dnlen, len, ret; + struct property **pp, *prop; + char *p; + + dnlen = strlen(dnt->full_name); + len = dnlen + 1 + strlen(name) + 1; + dnp = early_init_dt_alloc_memory_arch( + sizeof(struct device_node) + len, + __alignof__(struct device_node)); + if (dnp == NULL) { + pr_err("%s: allocation failure at %pO\n", __func__, + dns); + return NULL; + } + memset(dnp, 0, sizeof(*dnp)); + of_node_init(dnp); + p = (char *)dnp + sizeof(*dnp); + + /* build full name */ + dnp->full_name = p; + memcpy(p, dnt->full_name, dnlen); + p += dnlen; + if (dnlen != 1) + *p++ = '/'; + strcpy(p, name); + + dnp->parent = dnt; + + /* we now move the phandle properties */ + for (pp = &dns->properties; (prop = *pp) != NULL; ) { + + /* do not touch normal properties */ + if (strcmp(prop->name, "name") && + strcmp(prop->name, "phandle") && + strcmp(prop->name, "linux,phandle") && + strcmp(prop->name, "ibm,phandle")) { + pp = &(*pp)->next; + continue; + } + + /* move to the new node */ + *pp = prop->next; + /* don't advance */ + + prop->next = dnp->properties; + dnp->properties = prop; + + if ((strcmp(prop->name, "phandle") == 0 || + strcmp(prop->name, "linux,phandle") == 0 || + strcmp(prop->name, "ibm,phandle") == 0) && + dnp->phandle == 0) { + dnp->phandle = be32_to_cpup(prop->value); + /* remove the phandle from the source */ + dns->phandle = 0; + } + } + + dnp->name = of_get_property(dnp, "name", NULL); + dnp->type = of_get_property(dnp, "device_type", NULL); + if (!dnp->name) + dnp->name = "<NULL>"; + if (!dnp->type) + dnp->type = "<NULL>"; + + ret = quirk_fixup_symbol(dns, dnp); + if (ret != 0) + pr_warn("%s: Failed to fixup symbol %pO\n", __func__, dnp); + + return dnp; +} + +/* apply a quirk fragment node recursively */ +static int of_apply_quirk_fragment_node(struct device_node *dn, + struct device_node *dnt) +{ + struct property *prop, *tprop, **pp; + struct device_node *dnp, **dnpp, *child; + const char *name, *namet; + int i, ret; + + if (!dn || !dnt) + return -EINVAL; + + /* iterate over all properties */ + for (pp = &dn->properties; (prop = *pp) != NULL; pp = &prop->next) { + + /* do not touch auto-generated properties */ + if (!strcmp(prop->name, "name") || + !strcmp(prop->name, "phandle") || + !strcmp(prop->name, "linux,phandle") || + !strcmp(prop->name, "ibm,phandle") || + !strcmp(prop->name, "__remove_property__") || + !strcmp(prop->name, "__remove_node__")) + continue; + + pr_debug("%s: change property %s from %pO to %pO\n", + __func__, prop->name, dn, dnt); + + tprop = of_find_property(dnt, prop->name, NULL); + if (tprop) { + tprop->value = prop->value; + tprop->length = prop->length; + continue; + } + tprop = early_init_dt_alloc_memory_arch( + sizeof(struct property), + __alignof__(struct property)); + if (!tprop) { + pr_err("%s: allocation failure at %pO\n", __func__, + dn); + return -ENOMEM; + } + tprop->name = prop->name; + tprop->value = prop->value; + tprop->length = prop->length; + + /* link */ + tprop->next = dnt->properties; + dnt->properties = tprop; + } + + /* now handle property removals (if any) */ + for (i = 0; of_property_read_string_index(dn, "__remove_property__", + i, &name) == 0; i++) { + + /* remove property directly (we don't care about dead props) */ + for (pp = &dnt->properties; (prop = *pp) != NULL; + pp = &prop->next) { + if (!strcmp(prop->name, name)) { + *pp = prop->next; + pr_info("%s: remove property %s at %pO\n", + __func__, name, dnt); + break; + } + } + } + + /* now handle node removals (if any) */ + for (i = 0; of_property_read_string_index(dn, "__remove_node__", + i, &name) == 0; i++) { + + /* remove node directly (we don't care about dead props) */ + for (dnpp = &dnt->child; (dnp = *dnpp) != NULL; + dnpp = &dnp->sibling) { + + /* find path component */ + namet = strrchr(dnp->full_name, '/'); + if (!namet) /* root */ + namet = dnp->full_name; + else + namet++; + if (!strcmp(namet, name)) { + *dnpp = dnp->sibling; + pr_info("%s: remove node %s at %pO\n", + __func__, namet, dnt); + break; + } + } + } + + /* now iterate over childen */ + for_each_child_of_node(dn, child) { + /* locate path component */ + name = strrchr(child->full_name, '/'); + if (name == NULL) /* root? */ + name = child->full_name; + else + name++; + + /* find node (if it exists) */ + for (dnpp = &dnt->child; (dnp = *dnpp) != NULL; + dnpp = &dnp->sibling) { + + namet = strrchr(dnp->full_name, '/'); + if (!namet) /* root */ + namet = dnp->full_name; + else + namet++; + + if (!strcmp(namet, name)) + break; + } + + /* not found, create node */ + if (dnp == NULL) { + dnp = new_quirk_node(child, dnt, name); + if (dnp == NULL) { + pr_err("%s: allocation failure at %pO\n", + __func__, dn); + of_node_put(child); + return -ENOMEM; + } + dnp->sibling = *dnpp; + *dnpp = dnp; + + pr_debug("%s: new node %pO\n", __func__, dnp); + } + pr_debug("%s: recursing %pO\n", __func__, dnp); + + ret = of_apply_quirk_fragment_node(child, dnp); + if (ret != 0) { + of_node_put(child); + return ret; + } + } + + return 0; +} + +/* apply a single quirk fragment located at dn */ +static int of_apply_single_quirk_fragment(struct device_node *dn) +{ + struct device_node *dnt, *dno; + const char *path; + u32 val; + int ret; + + /* first try to go by using the target as a phandle */ + dno = NULL; + dnt = NULL; + ret = of_property_read_u32(dn, "target", &val); + if (ret == 0) + dnt = of_find_node_by_phandle(val); + + if (dnt == NULL) { + /* now try to locate by path */ + ret = of_property_read_string(dn, "target-path", + &path); + if (ret == 0) + dnt = of_find_node_by_path(path); + } + + if (dnt == NULL) { + pr_err("%s: Failed to find target for node %pO\n", + __func__, dn); + ret = -ENODEV; + goto out; + } + + pr_debug("%s: Found target at %pO\n", __func__, dnt); + dno = of_get_child_by_name(dn, "__overlay__"); + if (!dno) { + pr_err("%s: Failed to find overlay node %pO\n", __func__, dn); + ret = -ENODEV; + goto out; + } + + ret = of_apply_quirk_fragment_node(dno, dnt); +out: + of_node_put(dno); + of_node_put(dnt); + + return ret; +} + +/** + * of_quirk_apply_by_node - Apply a DT quirk found at the given node + * + * @dn: device node pointer to the quirk + * + * Returns 0 on success, a negative error value in case of an error. + */ +int of_quirk_apply_by_node(struct device_node *dn) +{ + struct device_node *child; + int ret; + + if (!dn) + return -ENODEV; + + pr_debug("Apply quirk at %pO\n", dn); + + ret = 0; + for_each_child_of_node(dn, child) { + ret = of_apply_single_quirk_fragment(child); + if (ret != 0) { + of_node_put(child); + break; + } + } + + return ret; +} + +/** + * of_quirk_apply_by_node - Apply a DT quirk found by the given phandle + * + * ph: phandle of the quirk node + * + * Returns 0 on success, a negative error value in case of an error. + */ +int of_quirk_apply_by_phandle(phandle ph) +{ + struct device_node *dn; + int ret; + + dn = of_find_node_by_phandle(ph); + if (!dn) { + pr_err("Failed to find node with phandle %u\n", ph); + return -ENODEV; + } + + ret = of_quirk_apply_by_node(dn); + of_node_put(dn); + + return ret; +} diff --git a/include/linux/of.h b/include/linux/of.h index 7ede449..02d8988 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1075,4 +1075,20 @@ static inline int of_overlay_destroy_all(void) #endif +/* early boot quirks */ +#ifdef CONFIG_OF_DYNAMIC +int of_quirk_apply_by_node(struct device_node *dn); +int of_quirk_apply_by_phandle(phandle ph); +#else +static inline int of_quirk_apply_by_node(struct device_node *dn) +{ + return -ENOTSUPP; +} + +int of_quirk_apply_by_phandle(phandle ph) +{ + return -ENOTSUPP; +} +#endif + #endif /* _LINUX_OF_H */ -- 1.7.12
next prev parent reply other threads:[~2015-02-18 15:01 UTC|newest] Thread overview: 150+ messages / expand[flat|nested] mbox.gz Atom feed top 2015-02-18 14:59 [PATCH 0/4] Device Tree Quirks & the Beaglebone Pantelis Antoniou 2015-02-18 14:59 ` Pantelis Antoniou 2015-02-18 14:59 ` Pantelis Antoniou 2015-02-18 14:59 ` [PATCH 1/4] arm: of: Add a DT quirk method after unflattening Pantelis Antoniou 2015-02-18 14:59 ` Pantelis Antoniou 2015-02-18 14:59 ` Pantelis Antoniou [this message] 2015-02-18 14:59 ` [PATCH 2/4] of: DT quirks infrastructure Pantelis Antoniou 2015-02-18 15:41 ` Mark Rutland 2015-02-18 15:41 ` Mark Rutland 2015-02-18 15:41 ` Mark Rutland 2015-02-18 15:53 ` Pantelis Antoniou 2015-02-18 15:53 ` Pantelis Antoniou 2015-02-18 15:53 ` Pantelis Antoniou 2015-02-18 16:32 ` Ludovic Desroches 2015-02-18 16:32 ` Ludovic Desroches 2015-02-18 16:32 ` Ludovic Desroches 2015-02-18 16:39 ` Pantelis Antoniou 2015-02-18 16:39 ` Pantelis Antoniou 2015-02-18 16:39 ` Pantelis Antoniou 2015-02-18 16:47 ` Ludovic Desroches 2015-02-18 16:47 ` Ludovic Desroches 2015-02-18 16:47 ` Ludovic Desroches 2015-02-18 16:46 ` Matt Porter 2015-02-18 16:46 ` Matt Porter 2015-02-18 16:46 ` Matt Porter 2015-02-18 17:31 ` Mark Rutland 2015-02-18 17:31 ` Mark Rutland 2015-02-18 17:31 ` Mark Rutland 2015-02-18 19:32 ` Guenter Roeck 2015-02-18 19:32 ` Guenter Roeck 2015-02-18 19:32 ` Guenter Roeck 2015-02-19 14:29 ` Pantelis Antoniou 2015-02-19 14:29 ` Pantelis Antoniou 2015-02-19 14:29 ` Pantelis Antoniou 2015-02-19 16:48 ` Frank Rowand 2015-02-19 16:48 ` Frank Rowand 2015-02-19 16:48 ` Frank Rowand 2015-02-19 17:00 ` Pantelis Antoniou 2015-02-19 17:00 ` Pantelis Antoniou 2015-02-19 17:00 ` Pantelis Antoniou 2015-02-19 17:30 ` Frank Rowand 2015-02-19 17:30 ` Frank Rowand 2015-02-19 17:30 ` Frank Rowand 2015-02-19 17:38 ` Pantelis Antoniou 2015-02-19 17:38 ` Pantelis Antoniou 2015-02-19 17:38 ` Pantelis Antoniou 2015-02-19 18:01 ` Maxime Bizon 2015-02-19 18:01 ` Maxime Bizon 2015-02-19 18:01 ` Maxime Bizon 2015-02-19 18:12 ` Sylvain Rochet 2015-02-19 18:12 ` Sylvain Rochet 2015-02-19 18:12 ` Sylvain Rochet 2015-02-19 18:22 ` Maxime Bizon 2015-02-19 18:22 ` Maxime Bizon 2015-02-19 18:22 ` Maxime Bizon 2015-02-20 14:21 ` Peter Hurley 2015-02-20 14:21 ` Peter Hurley 2015-02-20 14:21 ` Peter Hurley 2015-02-20 14:35 ` Ludovic Desroches 2015-02-20 14:35 ` Ludovic Desroches 2015-02-20 14:35 ` Ludovic Desroches 2015-02-20 15:00 ` Peter Hurley 2015-02-20 15:00 ` Peter Hurley 2015-02-20 15:00 ` Peter Hurley 2015-02-20 15:02 ` Pantelis Antoniou 2015-02-20 15:02 ` Pantelis Antoniou 2015-02-20 15:02 ` Pantelis Antoniou 2015-02-20 15:24 ` Peter Hurley 2015-02-20 15:24 ` Peter Hurley 2015-02-20 15:24 ` Peter Hurley 2015-02-20 15:38 ` Pantelis Antoniou 2015-02-20 15:38 ` Pantelis Antoniou 2015-02-20 15:38 ` Pantelis Antoniou 2015-02-20 16:34 ` Peter Hurley 2015-02-20 16:34 ` Peter Hurley 2015-02-20 16:34 ` Peter Hurley 2015-02-20 16:49 ` Pantelis Antoniou 2015-02-20 16:49 ` Pantelis Antoniou 2015-02-20 16:49 ` Pantelis Antoniou 2015-02-20 17:30 ` Rob Herring 2015-02-20 17:30 ` Rob Herring 2015-02-20 17:30 ` Rob Herring 2015-02-20 17:37 ` Pantelis Antoniou 2015-02-20 17:37 ` Pantelis Antoniou 2015-02-20 17:37 ` Pantelis Antoniou 2015-02-23 7:00 ` Ludovic Desroches 2015-02-23 7:00 ` Ludovic Desroches 2015-02-23 7:00 ` Ludovic Desroches 2015-02-20 14:38 ` Pantelis Antoniou 2015-02-20 14:38 ` Pantelis Antoniou 2015-02-20 14:38 ` Pantelis Antoniou 2015-02-20 16:47 ` Guenter Roeck 2015-02-20 16:47 ` Guenter Roeck 2015-02-20 16:47 ` Guenter Roeck 2015-02-20 18:09 ` Peter Hurley 2015-02-20 18:09 ` Peter Hurley 2015-02-20 18:09 ` Peter Hurley 2015-02-20 18:48 ` Guenter Roeck 2015-02-20 18:48 ` Guenter Roeck 2015-02-20 18:48 ` Guenter Roeck 2015-02-23 7:30 ` Ludovic Desroches 2015-02-23 7:30 ` Ludovic Desroches 2015-02-23 7:30 ` Ludovic Desroches 2015-02-20 8:04 ` Ludovic Desroches 2015-02-20 8:04 ` Ludovic Desroches 2015-02-20 8:04 ` Ludovic Desroches 2015-02-19 2:08 ` Frank Rowand 2015-02-19 2:08 ` Frank Rowand 2015-02-19 14:41 ` Pantelis Antoniou 2015-02-19 14:41 ` Pantelis Antoniou 2015-02-19 16:40 ` Frank Rowand 2015-02-19 16:40 ` Frank Rowand 2015-02-19 16:51 ` Frank Rowand 2015-02-19 16:51 ` Frank Rowand 2015-02-19 16:51 ` Frank Rowand 2015-02-20 13:23 ` Peter Hurley 2015-02-20 13:23 ` Peter Hurley 2015-02-19 18:01 ` Rob Herring 2015-02-19 18:01 ` Rob Herring 2015-02-19 18:01 ` Rob Herring 2015-02-19 18:12 ` Guenter Roeck 2015-02-19 18:12 ` Guenter Roeck 2015-02-19 18:12 ` Guenter Roeck 2015-02-20 8:16 ` Ludovic Desroches 2015-02-20 8:16 ` Ludovic Desroches 2015-02-20 8:16 ` Ludovic Desroches 2015-02-18 14:59 ` [PATCH 3/4] arm: am33xx: DT quirks for am33xx based beaglebone variants Pantelis Antoniou 2015-02-18 14:59 ` Pantelis Antoniou 2015-02-19 18:16 ` Tony Lindgren 2015-02-19 18:16 ` Tony Lindgren 2015-02-19 18:16 ` Tony Lindgren 2015-02-19 18:28 ` Pantelis Antoniou 2015-02-19 18:28 ` Pantelis Antoniou 2015-02-19 18:36 ` Tony Lindgren 2015-02-19 18:36 ` Tony Lindgren 2015-02-19 18:36 ` Tony Lindgren 2015-02-19 18:44 ` Pantelis Antoniou 2015-02-19 18:44 ` Pantelis Antoniou 2015-02-19 18:44 ` Pantelis Antoniou 2015-02-23 18:39 ` Peter Hurley 2015-02-23 18:39 ` Peter Hurley 2015-02-23 18:48 ` Pantelis Antoniou 2015-02-23 18:48 ` Pantelis Antoniou 2015-02-19 18:57 ` Guenter Roeck 2015-02-19 18:57 ` Guenter Roeck 2015-02-20 16:13 ` Jon Hunter 2015-02-20 16:13 ` Jon Hunter 2015-02-18 14:59 ` [PATCH 4/4] arm: dts: Common Black/White Beaglebone DTS using quirks Pantelis Antoniou 2015-02-18 14:59 ` Pantelis Antoniou 2015-02-18 14:59 ` Pantelis Antoniou
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1424271576-1952-3-git-send-email-pantelis.antoniou@konsulko.com \ --to=pantelis.antoniou@konsulko.com \ --cc=devicetree@vger.kernel.org \ --cc=grant.likely@secretlab.ca \ --cc=koen@dominion.thruhere.net \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux@roeck-us.net \ --cc=ludovic.desroches@atmel.com \ --cc=matt.porter@linaro.org \ --cc=nicolas.ferre@atmel.com \ --cc=panto@antoniou-consulting.com \ --cc=robherring2@gmail.com \ --cc=tony@atomide.com \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.