diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch b/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch new file mode 100644 index 0000000000..1cac2dfcd8 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch @@ -0,0 +1,107 @@ +From 7631cb95056f03136c9e0a35484e8bebe7b52650 Mon Sep 17 00:00:00 2001 +From: Robin Murphy <robin.murphy@arm.com> +Date: Wed, 3 Jul 2019 18:42:20 +0100 +Subject: [PATCH] of: Make of_dma_get_range() work on bus nodes + +commit 951d48855d86e72e0d6de73440fe09d363168064 upstream. + +Since the "dma-ranges" property is only valid for a node representing a +bus, of_dma_get_range() currently assumes the node passed in is a leaf +representing a device, and starts the walk from its parent. In cases +like PCI host controllers on typical FDT systems, however, where the PCI +endpoints are probed dynamically the initial leaf node represents the +'bus' itself, and this logic means we fail to consider any "dma-ranges" +describing the host bridge itself. Rework the logic such that +of_dma_get_range() also works correctly starting from a bus node +containing "dma-ranges". + +While this does mean "dma-ranges" could incorrectly be in a device leaf +node, there isn't really any way in this function to ensure that a leaf +node is or isn't a bus node. + +Signed-off-by: Robin Murphy <robin.murphy@arm.com> +[robh: Allow for the bus child node to still be passed in] +Signed-off-by: Rob Herring <robh@kernel.org> +Reviewed-by: Robin Murphy <robin.murphy@arm.com> +Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +--- + drivers/of/address.c | 44 ++++++++++++++++++-------------------------- + 1 file changed, 18 insertions(+), 26 deletions(-) + +--- a/drivers/of/address.c ++++ b/drivers/of/address.c +@@ -940,47 +940,39 @@ int of_dma_get_range(struct device_node + const __be32 *ranges = NULL; + int len, naddr, nsize, pna; + int ret = 0; ++ bool found_dma_ranges = false; + u64 dmaaddr; + +- if (!node) +- return -EINVAL; +- +- while (1) { +- struct device_node *parent; +- +- naddr = of_n_addr_cells(node); +- nsize = of_n_size_cells(node); +- +- parent = __of_get_dma_parent(node); +- of_node_put(node); +- +- node = parent; +- if (!node) +- break; +- ++ while (node) { + ranges = of_get_property(node, "dma-ranges", &len); + + /* Ignore empty ranges, they imply no translation required */ + if (ranges && len > 0) + break; + +- /* +- * At least empty ranges has to be defined for parent node if +- * DMA is supported +- */ +- if (!ranges) +- break; ++ /* Once we find 'dma-ranges', then a missing one is an error */ ++ if (found_dma_ranges && !ranges) { ++ ret = -ENODEV; ++ goto out; ++ } ++ found_dma_ranges = true; ++ ++ node = of_get_next_dma_parent(node); + } + +- if (!ranges) { ++ if (!node || !ranges) { + pr_debug("no dma-ranges found for node(%pOF)\n", np); + ret = -ENODEV; + goto out; + } + +- len /= sizeof(u32); +- ++ naddr = of_bus_n_addr_cells(node); ++ nsize = of_bus_n_size_cells(node); + pna = of_n_addr_cells(node); ++ if ((len / sizeof(__be32)) % (pna + naddr + nsize)) { ++ ret = -EINVAL; ++ goto out; ++ } + + /* dma-ranges format: + * DMA addr : naddr cells +@@ -988,7 +980,7 @@ int of_dma_get_range(struct device_node + * size : nsize cells + */ + dmaaddr = of_read_number(ranges, naddr); +- *paddr = of_translate_dma_address(np, ranges); ++ *paddr = of_translate_dma_address(node, ranges + naddr); + if (*paddr == OF_BAD_ADDR) { + pr_err("translation of DMA address(%pad) to CPU address failed node(%pOF)\n", + dma_addr, np); |