checks: Add an interrupt-map check
Add a check for parsing 'interrupt-map' properties. The check primarily tests parsing 'interrupt-map' properties which depends on and the parent interrupt controller (or another map) node. Note that this does not require '#address-cells' in the interrupt-map parent, but treats missing '#address-cells' as 0 which is how the Linux kernel parses it. There's numerous cases that expect this behavior. Cc: Andre Przywara <andre.przywara@arm.com> Signed-off-by: Rob Herring <robh@kernel.org> Message-Id: <20211015213527.2237774-1-robh@kernel.org> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
8fd24744e3
commit
0a3a9d3449
5 changed files with 144 additions and 0 deletions
85
checks.c
85
checks.c
|
@ -1590,6 +1590,90 @@ static void check_interrupt_provider(struct check *c,
|
||||||
}
|
}
|
||||||
WARNING(interrupt_provider, check_interrupt_provider, NULL, &interrupts_extended_is_cell);
|
WARNING(interrupt_provider, check_interrupt_provider, NULL, &interrupts_extended_is_cell);
|
||||||
|
|
||||||
|
static void check_interrupt_map(struct check *c,
|
||||||
|
struct dt_info *dti,
|
||||||
|
struct node *node)
|
||||||
|
{
|
||||||
|
struct node *root = dti->dt;
|
||||||
|
struct property *prop, *irq_map_prop;
|
||||||
|
size_t cellsize, cell, map_cells;
|
||||||
|
|
||||||
|
irq_map_prop = get_property(node, "interrupt-map");
|
||||||
|
if (!irq_map_prop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (node->addr_cells < 0) {
|
||||||
|
FAIL(c, dti, node,
|
||||||
|
"Missing '#address-cells' in interrupt-map provider");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cellsize = node_addr_cells(node);
|
||||||
|
cellsize += propval_cell(get_property(node, "#interrupt-cells"));
|
||||||
|
|
||||||
|
prop = get_property(node, "interrupt-map-mask");
|
||||||
|
if (prop && (prop->val.len != (cellsize * sizeof(cell_t))))
|
||||||
|
FAIL_PROP(c, dti, node, prop,
|
||||||
|
"property size (%d) is invalid, expected %zu",
|
||||||
|
prop->val.len, cellsize * sizeof(cell_t));
|
||||||
|
|
||||||
|
if (!is_multiple_of(irq_map_prop->val.len, sizeof(cell_t))) {
|
||||||
|
FAIL_PROP(c, dti, node, irq_map_prop,
|
||||||
|
"property size (%d) is invalid, expected multiple of %zu",
|
||||||
|
irq_map_prop->val.len, sizeof(cell_t));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_cells = irq_map_prop->val.len / sizeof(cell_t);
|
||||||
|
for (cell = 0; cell < map_cells; ) {
|
||||||
|
struct node *provider_node;
|
||||||
|
struct property *cellprop;
|
||||||
|
int phandle;
|
||||||
|
size_t parent_cellsize;
|
||||||
|
|
||||||
|
if ((cell + cellsize) >= map_cells) {
|
||||||
|
FAIL_PROP(c, dti, node, irq_map_prop,
|
||||||
|
"property size (%d) too small, expected > %zu",
|
||||||
|
irq_map_prop->val.len, (cell + cellsize) * sizeof(cell_t));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cell += cellsize;
|
||||||
|
|
||||||
|
phandle = propval_cell_n(irq_map_prop, cell);
|
||||||
|
if (!phandle_is_valid(phandle)) {
|
||||||
|
/* Give up if this is an overlay with external references */
|
||||||
|
if (!(dti->dtsflags & DTSF_PLUGIN))
|
||||||
|
FAIL_PROP(c, dti, node, irq_map_prop,
|
||||||
|
"Cell %zu is not a phandle(%d)",
|
||||||
|
cell, phandle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
provider_node = get_node_by_phandle(root, phandle);
|
||||||
|
if (!provider_node) {
|
||||||
|
FAIL_PROP(c, dti, node, irq_map_prop,
|
||||||
|
"Could not get phandle(%d) node for (cell %zu)",
|
||||||
|
phandle, cell);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cellprop = get_property(provider_node, "#interrupt-cells");
|
||||||
|
if (cellprop) {
|
||||||
|
parent_cellsize = propval_cell(cellprop);
|
||||||
|
} else {
|
||||||
|
FAIL(c, dti, node, "Missing property '#interrupt-cells' in node %s or bad phandle (referred from interrupt-map[%zu])",
|
||||||
|
provider_node->fullpath, cell);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cellprop = get_property(provider_node, "#address-cells");
|
||||||
|
if (cellprop)
|
||||||
|
parent_cellsize += propval_cell(cellprop);
|
||||||
|
|
||||||
|
cell += 1 + parent_cellsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WARNING(interrupt_map, check_interrupt_map, NULL, &phandle_references, &addr_size_cells, &interrupt_provider);
|
||||||
|
|
||||||
static void check_interrupts_property(struct check *c,
|
static void check_interrupts_property(struct check *c,
|
||||||
struct dt_info *dti,
|
struct dt_info *dti,
|
||||||
struct node *node)
|
struct node *node)
|
||||||
|
@ -1888,6 +1972,7 @@ static struct check *check_table[] = {
|
||||||
&gpios_property,
|
&gpios_property,
|
||||||
&interrupts_property,
|
&interrupts_property,
|
||||||
&interrupt_provider,
|
&interrupt_provider,
|
||||||
|
&interrupt_map,
|
||||||
|
|
||||||
&alias_paths,
|
&alias_paths,
|
||||||
|
|
||||||
|
|
20
tests/bad-interrupt-map-mask.dts
Normal file
20
tests/bad-interrupt-map-mask.dts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
intc: interrupt-controller {
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-controller;
|
||||||
|
};
|
||||||
|
|
||||||
|
node {
|
||||||
|
#address-cells = <0>;
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
interrupt-map = <1 &intc 1 2 3>;
|
||||||
|
interrupt-map-mask = <0 0>;
|
||||||
|
|
||||||
|
child {
|
||||||
|
interrupts = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
17
tests/bad-interrupt-map-parent.dts
Normal file
17
tests/bad-interrupt-map-parent.dts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
intc: interrupt-controller {
|
||||||
|
};
|
||||||
|
|
||||||
|
node {
|
||||||
|
#address-cells = <0>;
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
interrupt-map = <1 &intc 1 2 3>;
|
||||||
|
|
||||||
|
child {
|
||||||
|
interrupts = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
19
tests/bad-interrupt-map.dts
Normal file
19
tests/bad-interrupt-map.dts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
intc: interrupt-controller {
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-controller;
|
||||||
|
};
|
||||||
|
|
||||||
|
node {
|
||||||
|
/* Missing #address-cells = <0>; */
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
interrupt-map = <1 &intc 1 2 3>;
|
||||||
|
|
||||||
|
child {
|
||||||
|
interrupts = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -717,6 +717,9 @@ dtc_tests () {
|
||||||
run_sh_test "$SRCDIR/dtc-checkfails.sh" -n deprecated_gpio_property -- -Wdeprecated_gpio_property -I dts -O dtb "$SRCDIR/good-gpio.dts"
|
run_sh_test "$SRCDIR/dtc-checkfails.sh" -n deprecated_gpio_property -- -Wdeprecated_gpio_property -I dts -O dtb "$SRCDIR/good-gpio.dts"
|
||||||
check_tests "$SRCDIR/bad-interrupt-cells.dts" interrupts_property
|
check_tests "$SRCDIR/bad-interrupt-cells.dts" interrupts_property
|
||||||
check_tests "$SRCDIR/bad-interrupt-controller.dts" interrupt_provider
|
check_tests "$SRCDIR/bad-interrupt-controller.dts" interrupt_provider
|
||||||
|
check_tests "$SRCDIR/bad-interrupt-map.dts" interrupt_map
|
||||||
|
check_tests "$SRCDIR/bad-interrupt-map-parent.dts" interrupt_map
|
||||||
|
check_tests "$SRCDIR/bad-interrupt-map-mask.dts" interrupt_map
|
||||||
run_sh_test "$SRCDIR/dtc-checkfails.sh" node_name_chars -- -I dtb -O dtb bad_node_char.dtb
|
run_sh_test "$SRCDIR/dtc-checkfails.sh" node_name_chars -- -I dtb -O dtb bad_node_char.dtb
|
||||||
run_sh_test "$SRCDIR/dtc-checkfails.sh" node_name_format -- -I dtb -O dtb bad_node_format.dtb
|
run_sh_test "$SRCDIR/dtc-checkfails.sh" node_name_format -- -I dtb -O dtb bad_node_format.dtb
|
||||||
run_sh_test "$SRCDIR/dtc-checkfails.sh" property_name_chars -- -I dtb -O dtb bad_prop_char.dtb
|
run_sh_test "$SRCDIR/dtc-checkfails.sh" property_name_chars -- -I dtb -O dtb bad_prop_char.dtb
|
||||||
|
|
Loading…
Reference in a new issue