mirror of
https://github.com/dgibson/dtc.git
synced 2025-10-13 16:27:39 -04:00
Allow device tree to be modified by additonal device tree sections
This patch allows the following construct: / { property-a = "old"; property-b = "does not change"; }; / { property-a = "changed"; property-c = "new"; node-a { }; }; Where the later device tree overrides the properties found in the earlier tree. This is useful for laying down a template device tree in an include file and modifying it for a specific board without having to clone the entire tree. Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
This commit is contained in:
parent
716418849a
commit
83da1b2a4e
7 changed files with 214 additions and 6 deletions
14
dtc-parser.y
14
dtc-parser.y
|
@ -75,6 +75,7 @@ static unsigned long long eval_literal(const char *s, int base, int bits);
|
||||||
%type <proplist> proplist
|
%type <proplist> proplist
|
||||||
|
|
||||||
%type <node> devicetree
|
%type <node> devicetree
|
||||||
|
%type <node> devicetrees
|
||||||
%type <node> nodedef
|
%type <node> nodedef
|
||||||
%type <node> subnode
|
%type <node> subnode
|
||||||
%type <nodelist> subnodes
|
%type <nodelist> subnodes
|
||||||
|
@ -82,7 +83,7 @@ static unsigned long long eval_literal(const char *s, int base, int bits);
|
||||||
%%
|
%%
|
||||||
|
|
||||||
sourcefile:
|
sourcefile:
|
||||||
DT_V1 ';' memreserves devicetree
|
DT_V1 ';' memreserves devicetrees
|
||||||
{
|
{
|
||||||
the_boot_info = build_boot_info($3, $4,
|
the_boot_info = build_boot_info($3, $4,
|
||||||
guess_boot_cpuid($4));
|
guess_boot_cpuid($4));
|
||||||
|
@ -119,6 +120,17 @@ addr:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
devicetrees:
|
||||||
|
devicetree
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| devicetrees devicetree
|
||||||
|
{
|
||||||
|
$$ = merge_nodes($1, $2);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
devicetree:
|
devicetree:
|
||||||
'/' nodedef
|
'/' nodedef
|
||||||
{
|
{
|
||||||
|
|
1
dtc.h
1
dtc.h
|
@ -174,6 +174,7 @@ struct property *reverse_properties(struct property *first);
|
||||||
struct node *build_node(struct property *proplist, struct node *children);
|
struct node *build_node(struct property *proplist, struct node *children);
|
||||||
struct node *name_node(struct node *node, char *name);
|
struct node *name_node(struct node *node, char *name);
|
||||||
struct node *chain_node(struct node *first, struct node *list);
|
struct node *chain_node(struct node *first, struct node *list);
|
||||||
|
struct node *merge_nodes(struct node *old_node, struct node *new_node);
|
||||||
|
|
||||||
void add_property(struct node *node, struct property *prop);
|
void add_property(struct node *node, struct property *prop);
|
||||||
void add_child(struct node *parent, struct node *child);
|
void add_child(struct node *parent, struct node *child);
|
||||||
|
|
75
livetree.c
75
livetree.c
|
@ -26,8 +26,14 @@
|
||||||
|
|
||||||
void add_label(struct label **labels, char *label)
|
void add_label(struct label **labels, char *label)
|
||||||
{
|
{
|
||||||
struct label *new = xmalloc(sizeof(*new));
|
struct label *new;
|
||||||
|
|
||||||
|
/* Make sure the label isn't already there */
|
||||||
|
for_each_label(*labels, new)
|
||||||
|
if (streq(new->label, label))
|
||||||
|
return;
|
||||||
|
|
||||||
|
new = xmalloc(sizeof(*new));
|
||||||
new->label = label;
|
new->label = label;
|
||||||
new->next = *labels;
|
new->next = *labels;
|
||||||
*labels = new;
|
*labels = new;
|
||||||
|
@ -94,6 +100,73 @@ struct node *name_node(struct node *node, char *name)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct node *merge_nodes(struct node *old_node, struct node *new_node)
|
||||||
|
{
|
||||||
|
struct property *new_prop, *old_prop;
|
||||||
|
struct node *new_child, *old_child;
|
||||||
|
struct label *l;
|
||||||
|
|
||||||
|
/* Add new node labels to old node */
|
||||||
|
for_each_label(new_node->labels, l)
|
||||||
|
add_label(&old_node->labels, l->label);
|
||||||
|
|
||||||
|
/* Move properties from the new node to the old node. If there
|
||||||
|
* is a collision, replace the old value with the new */
|
||||||
|
while (new_node->proplist) {
|
||||||
|
/* Pop the property off the list */
|
||||||
|
new_prop = new_node->proplist;
|
||||||
|
new_node->proplist = new_prop->next;
|
||||||
|
new_prop->next = NULL;
|
||||||
|
|
||||||
|
/* Look for a collision, set new value if there is */
|
||||||
|
for_each_property(old_node, old_prop) {
|
||||||
|
if (streq(old_prop->name, new_prop->name)) {
|
||||||
|
/* Add new labels to old property */
|
||||||
|
for_each_label(new_prop->labels, l)
|
||||||
|
add_label(&old_prop->labels, l->label);
|
||||||
|
|
||||||
|
old_prop->val = new_prop->val;
|
||||||
|
free(new_prop);
|
||||||
|
new_prop = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no collision occurred, add property to the old node. */
|
||||||
|
if (new_prop)
|
||||||
|
add_property(old_node, new_prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move the override child nodes into the primary node. If
|
||||||
|
* there is a collision, then merge the nodes. */
|
||||||
|
while (new_node->children) {
|
||||||
|
/* Pop the child node off the list */
|
||||||
|
new_child = new_node->children;
|
||||||
|
new_node->children = new_child->next_sibling;
|
||||||
|
new_child->parent = NULL;
|
||||||
|
new_child->next_sibling = NULL;
|
||||||
|
|
||||||
|
/* Search for a collision. Merge if there is */
|
||||||
|
for_each_child(old_node, old_child) {
|
||||||
|
if (streq(old_child->name, new_child->name)) {
|
||||||
|
merge_nodes(old_child, new_child);
|
||||||
|
new_child = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no collision occured, add child to the old node. */
|
||||||
|
if (new_child)
|
||||||
|
add_child(old_node, new_child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The new node contents are now merged into the old node. Free
|
||||||
|
* the new node. */
|
||||||
|
free(new_node);
|
||||||
|
|
||||||
|
return old_node;
|
||||||
|
}
|
||||||
|
|
||||||
struct node *chain_node(struct node *first, struct node *list)
|
struct node *chain_node(struct node *first, struct node *list)
|
||||||
{
|
{
|
||||||
assert(first->next_sibling == NULL);
|
assert(first->next_sibling == NULL);
|
||||||
|
|
|
@ -3,26 +3,27 @@
|
||||||
m1: mq: /memreserve/ 0 0x1000;
|
m1: mq: /memreserve/ 0 0x1000;
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
p1: px: prop = "foo";
|
p0: pw: prop = "foo";
|
||||||
|
|
||||||
/* Explicit phandles */
|
/* Explicit phandles */
|
||||||
n1: nx: node1 {
|
n1: nx: node1 {
|
||||||
linux,phandle = <0x2000>;
|
linux,phandle = <0x2000>;
|
||||||
ref = <&{/node2}>; /* reference precedes target */
|
ref = <&{/node2}>; /* reference precedes target */
|
||||||
lref = <&ny>;
|
p1: px: lref = <&ny>;
|
||||||
};
|
};
|
||||||
ny: n2: node2 {
|
ny: n2: node2 {
|
||||||
phandle = <0x1>;
|
p2: py: phandle = <0x1>;
|
||||||
ref = <&{/node1}>; /* reference after target */
|
ref = <&{/node1}>; /* reference after target */
|
||||||
lref = <&nx>;
|
lref = <&nx>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Implicit phandles */
|
/* Implicit phandles */
|
||||||
n3: node3 {
|
n3: node3 {
|
||||||
ref = <&{/node4}>;
|
p3: ref = <&{/node4}>;
|
||||||
lref = <&n4>;
|
lref = <&n4>;
|
||||||
};
|
};
|
||||||
n4: node4 {
|
n4: node4 {
|
||||||
|
p4: prop;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Explicit phandle with implicit value */
|
/* Explicit phandle with implicit value */
|
||||||
|
@ -34,5 +35,8 @@ m1: mq: /memreserve/ 0 0x1000;
|
||||||
n5: nz: node5 {
|
n5: nz: node5 {
|
||||||
linux,phandle = <&n5>;
|
linux,phandle = <&n5>;
|
||||||
phandle = <&nz>;
|
phandle = <&nz>;
|
||||||
|
n1 = &n1;
|
||||||
|
n2 = &n2;
|
||||||
|
n3 = &n3;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
66
tests/multilabel_merge.dts
Normal file
66
tests/multilabel_merge.dts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
m1: mq: /memreserve/ 0 0x1000;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
p0: pw: prop = "foo";
|
||||||
|
|
||||||
|
/* Explicit phandles */
|
||||||
|
n1: node1 {
|
||||||
|
linux,phandle = <0x2000>;
|
||||||
|
ref = <&{/node2}>; /* reference precedes target */
|
||||||
|
p1: lref;
|
||||||
|
};
|
||||||
|
node2 {
|
||||||
|
phandle = <0x1>;
|
||||||
|
ref = <&{/node1}>; /* reference after target */
|
||||||
|
lref = <&nx>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Implicit phandles */
|
||||||
|
n3: node3 {
|
||||||
|
p3: ref = <&{/node4}>;
|
||||||
|
lref = <&n4>;
|
||||||
|
};
|
||||||
|
n4: node4 {
|
||||||
|
p4: prop = "foo";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Explicit phandle with implicit value */
|
||||||
|
/* This self-reference is the standard way to tag a node as requiring
|
||||||
|
* a phandle (perhaps for reference by nodes that will be dynamically
|
||||||
|
* added) without explicitly allocating it a phandle.
|
||||||
|
* The self-reference requires some special internal handling, though
|
||||||
|
* so check it actually works */
|
||||||
|
n5: nz: node5 {
|
||||||
|
linux,phandle = <&n5>;
|
||||||
|
phandle = <&nz>;
|
||||||
|
n1 = &n1;
|
||||||
|
n2 = &n2;
|
||||||
|
n3 = &n3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/ {
|
||||||
|
/* Append labels (also changes property content) */
|
||||||
|
nx: node1 {
|
||||||
|
px: lref = <&ny>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Add multiple labels */
|
||||||
|
ny: n2: node2 {
|
||||||
|
/* Add a label to a property */
|
||||||
|
p2: py: phandle = <0x1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Reassigning the same label should be a no-op */
|
||||||
|
n3: node3 {
|
||||||
|
p3: ref = <&{/node4}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Redefining a node/property should not remove labels */
|
||||||
|
node4 {
|
||||||
|
prop;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -297,6 +297,13 @@ dtc_tests () {
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Check merge/overlay functionality
|
||||||
|
run_dtc_test -I dts -O dtb -o dtc_tree1_merge.test.dtb test_tree1_merge.dts
|
||||||
|
tree1_tests dtc_tree1_merge.test.dtb test_tree1.dtb
|
||||||
|
run_dtc_test -I dts -O dtb -o multilabel_merge.test.dtb multilabel_merge.dts
|
||||||
|
run_test references multilabel.test.dtb
|
||||||
|
run_test dtbs_equal_ordered multilabel.test.dtb multilabel_merge.test.dtb
|
||||||
|
|
||||||
# Check some checks
|
# Check some checks
|
||||||
check_tests dup-nodename.dts duplicate_node_names
|
check_tests dup-nodename.dts duplicate_node_names
|
||||||
check_tests dup-propname.dts duplicate_property_names
|
check_tests dup-propname.dts duplicate_property_names
|
||||||
|
|
45
tests/test_tree1_merge.dts
Normal file
45
tests/test_tree1_merge.dts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/dts-v1/;
|
||||||
|
/memreserve/ 0xdeadbeef00000000 0x100000;
|
||||||
|
/memreserve/ 123456789 010000;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
compatible = "test_tree1";
|
||||||
|
prop-int = "wrong!";
|
||||||
|
prop-str = "hello world";
|
||||||
|
|
||||||
|
subnode@1 {
|
||||||
|
compatible = "subnode1";
|
||||||
|
|
||||||
|
subsubnode {
|
||||||
|
compatible = "subsubnode1", "subsubnode";
|
||||||
|
prop-int = <0xdeadbeef>;
|
||||||
|
};
|
||||||
|
|
||||||
|
ss1 {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
subnode@2 {
|
||||||
|
linux,phandle = <0x2000>;
|
||||||
|
prop-int = <123456789>;
|
||||||
|
|
||||||
|
ss2 {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/ {
|
||||||
|
prop-int = <0xdeadbeef>;
|
||||||
|
subnode@1 {
|
||||||
|
prop-int = [deadbeef];
|
||||||
|
};
|
||||||
|
subnode@2 {
|
||||||
|
subsubnode@0 {
|
||||||
|
phandle = <0x2001>;
|
||||||
|
compatible = "subsubnode2", "subsubnode";
|
||||||
|
prop-int = <0726746425>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue