diff options
Diffstat (limited to 'scripts/dtc/checks.c')
-rw-r--r-- | scripts/dtc/checks.c | 199 |
1 files changed, 198 insertions, 1 deletions
diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c index 815eaf140ab5..a2cc1036c915 100644 --- a/scripts/dtc/checks.c +++ b/scripts/dtc/checks.c @@ -255,7 +255,7 @@ static void check_duplicate_node_names(struct check *c, struct dt_info *dti, child2; child2 = child2->next_sibling) if (streq(child->name, child2->name)) - FAIL(c, dti, node, "Duplicate node name"); + FAIL(c, dti, child2, "Duplicate node name"); } ERROR(duplicate_node_names, check_duplicate_node_names, NULL); @@ -317,6 +317,11 @@ static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti, const char *unitname = get_unitname(node); struct property *prop = get_property(node, "reg"); + if (get_subnode(node, "__overlay__")) { + /* HACK: Overlay fragments are a special case */ + return; + } + if (!prop) { prop = get_property(node, "ranges"); if (prop && !prop->val.len) @@ -579,6 +584,8 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti, phandle = get_node_phandle(dt, refnode); *((fdt32_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); + + reference_node(refnode); } } } @@ -609,11 +616,21 @@ static void fixup_path_references(struct check *c, struct dt_info *dti, path = refnode->fullpath; prop->val = data_insert_at_marker(prop->val, m, path, strlen(path) + 1); + + reference_node(refnode); } } } ERROR(path_references, fixup_path_references, NULL, &duplicate_node_names); +static void fixup_omit_unused_nodes(struct check *c, struct dt_info *dti, + struct node *node) +{ + if (node->omit_if_unused && !node->is_referenced) + delete_node(node); +} +ERROR(omit_unused_nodes, fixup_omit_unused_nodes, NULL, &phandle_references, &path_references); + /* * Semantic checks */ @@ -1017,6 +1034,36 @@ static void check_avoid_unnecessary_addr_size(struct check *c, struct dt_info *d } WARNING(avoid_unnecessary_addr_size, check_avoid_unnecessary_addr_size, NULL, &avoid_default_addr_size); +static void check_unique_unit_address(struct check *c, struct dt_info *dti, + struct node *node) +{ + struct node *childa; + + if (node->addr_cells < 0 || node->size_cells < 0) + return; + + if (!node->children) + return; + + for_each_child(node, childa) { + struct node *childb; + const char *addr_a = get_unitname(childa); + + if (!strlen(addr_a)) + continue; + + for_each_child(node, childb) { + const char *addr_b = get_unitname(childb); + if (childa == childb) + break; + + if (streq(addr_a, addr_b)) + FAIL(c, dti, childb, "duplicate unit-address (also used in node %s)", childa->fullpath); + } + } +} +WARNING(unique_unit_address, check_unique_unit_address, NULL, &avoid_default_addr_size); + static void check_obsolete_chosen_interrupt_controller(struct check *c, struct dt_info *dti, struct node *node) @@ -1357,6 +1404,152 @@ static void check_interrupts_property(struct check *c, } WARNING(interrupts_property, check_interrupts_property, &phandle_references); +static const struct bus_type graph_port_bus = { + .name = "graph-port", +}; + +static const struct bus_type graph_ports_bus = { + .name = "graph-ports", +}; + +static void check_graph_nodes(struct check *c, struct dt_info *dti, + struct node *node) +{ + struct node *child; + + for_each_child(node, child) { + if (!(strprefixeq(child->name, child->basenamelen, "endpoint") || + get_property(child, "remote-endpoint"))) + continue; + + node->bus = &graph_port_bus; + + /* The parent of 'port' nodes can be either 'ports' or a device */ + if (!node->parent->bus && + (streq(node->parent->name, "ports") || get_property(node, "reg"))) + node->parent->bus = &graph_ports_bus; + + break; + } + +} +WARNING(graph_nodes, check_graph_nodes, NULL); + +static void check_graph_child_address(struct check *c, struct dt_info *dti, + struct node *node) +{ + int cnt = 0; + struct node *child; + + if (node->bus != &graph_ports_bus && node->bus != &graph_port_bus) + return; + + for_each_child(node, child) { + struct property *prop = get_property(child, "reg"); + + /* No error if we have any non-zero unit address */ + if (prop && propval_cell(prop) != 0) + return; + + cnt++; + } + + if (cnt == 1 && node->addr_cells != -1) + FAIL(c, dti, node, "graph node has single child node '%s', #address-cells/#size-cells are not necessary", + node->children->name); +} +WARNING(graph_child_address, check_graph_child_address, NULL, &graph_nodes); + +static void check_graph_reg(struct check *c, struct dt_info *dti, + struct node *node) +{ + char unit_addr[9]; + const char *unitname = get_unitname(node); + struct property *prop; + + prop = get_property(node, "reg"); + if (!prop || !unitname) + return; + + if (!(prop->val.val && prop->val.len == sizeof(cell_t))) { + FAIL(c, dti, node, "graph node malformed 'reg' property"); + return; + } + + snprintf(unit_addr, sizeof(unit_addr), "%x", propval_cell(prop)); + if (!streq(unitname, unit_addr)) + FAIL(c, dti, node, "graph node unit address error, expected \"%s\"", + unit_addr); + + if (node->parent->addr_cells != 1) + FAIL_PROP(c, dti, node, get_property(node, "#address-cells"), + "graph node '#address-cells' is %d, must be 1", + node->parent->addr_cells); + if (node->parent->size_cells != 0) + FAIL_PROP(c, dti, node, get_property(node, "#size-cells"), + "graph node '#size-cells' is %d, must be 0", + node->parent->size_cells); +} + +static void check_graph_port(struct check *c, struct dt_info *dti, + struct node *node) +{ + if (node->bus != &graph_port_bus) + return; + + if (!strprefixeq(node->name, node->basenamelen, "port")) + FAIL(c, dti, node, "graph port node name should be 'port'"); + + check_graph_reg(c, dti, node); +} +WARNING(graph_port, check_graph_port, NULL, &graph_nodes); + +static struct node *get_remote_endpoint(struct check *c, struct dt_info *dti, + struct node *endpoint) +{ + int phandle; + struct node *node; + struct property *prop; + + prop = get_property(endpoint, "remote-endpoint"); + if (!prop) + return NULL; + + phandle = propval_cell(prop); + /* Give up if this is an overlay with external references */ + if (phandle == 0 || phandle == -1) + return NULL; + + node = get_node_by_phandle(dti->dt, phandle); + if (!node) + FAIL_PROP(c, dti, endpoint, prop, "graph phandle is not valid"); + + return node; +} + +static void check_graph_endpoint(struct check *c, struct dt_info *dti, + struct node *node) +{ + struct node *remote_node; + + if (!node->parent || node->parent->bus != &graph_port_bus) + return; + + if (!strprefixeq(node->name, node->basenamelen, "endpoint")) + FAIL(c, dti, node, "graph endpont node name should be 'endpoint'"); + + check_graph_reg(c, dti, node); + + remote_node = get_remote_endpoint(c, dti, node); + if (!remote_node) + return; + + if (get_remote_endpoint(c, dti, remote_node) != node) + FAIL(c, dti, node, "graph connection to node '%s' is not bidirectional", + remote_node->fullpath); +} +WARNING(graph_endpoint, check_graph_endpoint, NULL, &graph_nodes); + static struct check *check_table[] = { &duplicate_node_names, &duplicate_property_names, &node_name_chars, &node_name_format, &property_name_chars, @@ -1366,6 +1559,7 @@ static struct check *check_table[] = { &explicit_phandles, &phandle_references, &path_references, + &omit_unused_nodes, &address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell, &device_type_is_string, &model_is_string, &status_is_string, @@ -1390,6 +1584,7 @@ static struct check *check_table[] = { &avoid_default_addr_size, &avoid_unnecessary_addr_size, + &unique_unit_address, &obsolete_chosen_interrupt_controller, &chosen_node_is_root, &chosen_node_bootargs, &chosen_node_stdout_path, @@ -1416,6 +1611,8 @@ static struct check *check_table[] = { &alias_paths, + &graph_nodes, &graph_child_address, &graph_port, &graph_endpoint, + &always_fail, }; |