Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

of: Fix "dma-ranges" handling for bus controllers

Commit 951d48855d86 ("of: Make of_dma_get_range() work on bus nodes")
relaxed the handling of "dma-ranges" for any leaf node on the assumption
that it would still represent a usage error for the property to be
present on a non-bus leaf node. However there turns out to be a fiddly
case where a bus also represents a DMA-capable device in its own right,
such as a PCIe root complex with an integrated DMA engine on its
platform side. In such cases, "dma-ranges" translation is entirely valid
for devices discovered behind the bus, but should not be erroneously
applied to the bus controller device itself which operates in its
parent's address space. Fix this by restoring the previous behaviour for
the specific case where a device is configured via its own OF node,
since it is logical to assume that a device should never represent its
own parent bus.

Reported-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Link: https://lore.kernel.org/r/112e8f3d3e7c054ecf5e12b5ac0aa5596ec00681.1664455433.git.robin.murphy@arm.com
Signed-off-by: Rob Herring <robh@kernel.org>

authored by

Robin Murphy and committed by
Rob Herring
f1ad5338 17005609

+16 -2
+3 -1
drivers/of/address.c
··· 579 579 } 580 580 EXPORT_SYMBOL(of_translate_address); 581 581 582 - static struct device_node *__of_get_dma_parent(const struct device_node *np) 582 + #ifdef CONFIG_HAS_DMA 583 + struct device_node *__of_get_dma_parent(const struct device_node *np) 583 584 { 584 585 struct of_phandle_args args; 585 586 int ret, index; ··· 597 596 598 597 return of_node_get(args.np); 599 598 } 599 + #endif 600 600 601 601 static struct device_node *of_get_next_dma_parent(struct device_node *np) 602 602 {
+8 -1
drivers/of/device.c
··· 116 116 { 117 117 const struct iommu_ops *iommu; 118 118 const struct bus_dma_region *map = NULL; 119 + struct device_node *bus_np; 119 120 u64 dma_start = 0; 120 121 u64 mask, end, size = 0; 121 122 bool coherent; 122 123 int ret; 123 124 124 - ret = of_dma_get_range(np, &map); 125 + if (np == dev->of_node) 126 + bus_np = __of_get_dma_parent(np); 127 + else 128 + bus_np = of_node_get(np); 129 + 130 + ret = of_dma_get_range(bus_np, &map); 131 + of_node_put(bus_np); 125 132 if (ret < 0) { 126 133 /* 127 134 * For legacy reasons, we have to assume some devices need
+5
drivers/of/of_private.h
··· 155 155 #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA) 156 156 int of_dma_get_range(struct device_node *np, 157 157 const struct bus_dma_region **map); 158 + struct device_node *__of_get_dma_parent(const struct device_node *np); 158 159 #else 159 160 static inline int of_dma_get_range(struct device_node *np, 160 161 const struct bus_dma_region **map) 161 162 { 162 163 return -ENODEV; 164 + } 165 + static inline struct device_node *__of_get_dma_parent(const struct device_node *np) 166 + { 167 + return of_get_parent(np); 163 168 } 164 169 #endif 165 170