Hi Nicolas,
On Thu, Nov 19, 2020 at 5:50 PM Nicolas Saenz Julienne <nsaenzjulie...@suse.de> wrote: > > Add the follwing functions to get a specific device's DMA ranges: > - dev_get_dma_range() > - ofnode_get_dma_range() > - of_get_dma_range() > - fdt_get_dma_range() > They are specially useful in oder to be able validate a physical address > space range into a bus's and to convert addresses from and to address > spaces. I'm seeing issues with this patch, with the just posted v2 as well (it's not arrived in my inbox as yet). I get the following linking error when building across a number of devices such as Jetson boards like jetson-tk1, p3450-0000, as well as puma-rk3399, marsboard, udoo, udoo_neo. The error is: /usr/bin/arm-linux-gnu-ld.bfd -pie --gc-sections -Bstatic --no-dynamic-linker -Ttext 0x87800000 -o u-boot -T u-boot.lds arch/arm/cpu/armv7/start.o --start-group arch/arm/cpu/built-in.o arch/arm/cpu/armv7/built-in.o arch/arm/lib/built-in.o arch/arm/mach-imx/built-in.o board/udoo/neo/built-in.o cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o drivers/i2c/built-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/power/built-in.o drivers/power/battery/built-in.o drivers/power/domain/built-in.o drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o drivers/power/pmic/built-in.o drivers/power/regulator/built-in.o drivers/serial/built-in.o drivers/spi/built-in.o drivers/usb/cdns3/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/host/built-in.o drivers/usb/mtu3/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o env/built-in.o fs/built-in.o lib/built-in.o net/built-in.o --end-group arch/arm/lib/eabi_compat.o arch/arm/lib/lib.a -Map u-boot.map; true /usr/bin/arm-linux-gnu-ld.bfd: drivers/built-in.o: in function `dev_get_dma_range': /home/perobins/fedora/packages/uboot-tools/u-boot-2021.01-rc3/include/dm/read.h:1025: undefined reference to `ofnode_get_dma_range' make[1]: *** [/home/perobins/fedora/packages/uboot-tools/u-boot-2021.01-rc3/Makefile:1757: u-boot] Error 1 make[1]: Leaving directory '/home/perobins/fedora/packages/uboot-tools/u-boot-2021.01-rc3/builds/udoo_neo' Peter > Signed-off-by: Nicolas Saenz Julienne <nsaenzjulie...@suse.de> > --- > common/fdt_support.c | 72 ++++++++++++++++++++++++++++++++++++++++++ > drivers/core/of_addr.c | 68 +++++++++++++++++++++++++++++++++++++++ > drivers/core/ofnode.c | 9 ++++++ > drivers/core/read.c | 5 +++ > include/dm/of_addr.h | 17 ++++++++++ > include/dm/ofnode.h | 16 ++++++++++ > include/dm/read.h | 6 ++++ > include/fdt_support.h | 14 ++++++++ > 8 files changed, 207 insertions(+) > > diff --git a/common/fdt_support.c b/common/fdt_support.c > index 5ae75df3c6..78dc7906bd 100644 > --- a/common/fdt_support.c > +++ b/common/fdt_support.c > @@ -1342,6 +1342,78 @@ u64 fdt_translate_dma_address(const void *blob, int > node_offset, > return __of_translate_address(blob, node_offset, in_addr, > "dma-ranges"); > } > > +int fdt_get_dma_range(const void *blob, int node, phys_addr_t *cpu, > + dma_addr_t *bus, u64 *size) > +{ > + bool found_dma_ranges = false; > + const fdt32_t *ranges; > + int na, ns, pna, pns; > + int parent = node; > + u64 cpu_addr; > + int ret = 0; > + int len; > + > + /* Find the closest dma-ranges property */ > + while (parent >= 0) { > + ranges = fdt_getprop(blob, parent, "dma-ranges", &len); > + > + /* Ignore empty ranges, they imply no translation required */ > + if (ranges && len > 0) > + break; > + > + /* Once we find 'dma-ranges', then a missing one is an error > */ > + if (found_dma_ranges && !ranges) { > + ret = -ENODEV; > + goto out; > + } > + > + if (ranges) > + found_dma_ranges = true; > + > + parent = fdt_parent_offset(blob, parent); > + } > + > + if (!ranges || parent < 0) { > + debug("no dma-ranges found for node %s\n", > + fdt_get_name(blob, node, NULL)); > + ret = -ENODEV; > + goto out; > + } > + > + /* switch to that node */ > + node = parent; > + parent = fdt_parent_offset(blob, node); > + if (parent < 0) { > + printf("Found dma-ranges in root node, shoudln't happen\n"); > + ret = -EINVAL; > + goto out; > + } > + > + /* Get the address sizes both for the bus and its parent */ > + of_match_bus(blob, node)->count_cells(blob, node, &na, &ns); > + if (!OF_CHECK_COUNTS(na, ns)) { > + printf("%s: Bad cell count for %s\n", __FUNCTION__, > + fdt_get_name(blob, node, NULL)); > + return -EINVAL; > + goto out; > + } > + > + of_match_bus(blob, parent)->count_cells(blob, parent, &pna, &pns); > + if (!OF_CHECK_COUNTS(pna, pns)) { > + printf("%s: Bad cell count for %s\n", __FUNCTION__, > + fdt_get_name(blob, parent, NULL)); > + return -EINVAL; > + goto out; > + } > + > + *bus = fdt_read_number(ranges, na); > + cpu_addr = fdt_read_number(ranges + na, pna); > + *cpu = fdt_translate_dma_address(blob, node, (const > fdt32_t*)&cpu_addr); > + *size = fdt_read_number(ranges + na + pna, ns); > +out: > + return ret; > +} > + > /** > * fdt_node_offset_by_compat_reg: Find a node that matches compatiable and > * who's reg property matches a physical cpu address > diff --git a/drivers/core/of_addr.c b/drivers/core/of_addr.c > index ca34d84922..8457e04a25 100644 > --- a/drivers/core/of_addr.c > +++ b/drivers/core/of_addr.c > @@ -325,6 +325,74 @@ u64 of_translate_dma_address(const struct device_node > *dev, const __be32 *in_add > return __of_translate_address(dev, in_addr, "dma-ranges"); > } > > +int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu, > + dma_addr_t *bus, u64 *size) > +{ > + bool found_dma_ranges = false; > + struct device_node parent; > + int na, ns, pna, pns; > + const __be32 *ranges; > + int ret = 0; > + int len; > + > + /* Find the closest dma-ranges property */ > + while (dev) { > + ranges = of_get_property(dev, "dma-ranges", &len); > + > + /* Ignore empty ranges, they imply no translation required */ > + if (ranges && len > 0) > + break; > + > + /* Once we find 'dma-ranges', then a missing one is an error > */ > + if (found_dma_ranges && !ranges) { > + ret = -ENODEV; > + goto out; > + } > + > + if (ranges) > + found_dma_ranges = true; > + > + dev = of_get_parent(dev); > + } > + > + if (!dev || !ranges) { > + debug("no dma-ranges found for node %s\n", > + of_node_full_name(dev)); > + ret = -ENODEV > + goto out; > + } > + > + /* switch to that node */ > + parent = of_get_parent(dev); > + if (!parent) { > + printf("Found dma-ranges in root node, shoudln't happen\n"); > + ret = -EINVAL; > + goto out; > + } > + > + /* Get the address sizes both for the bus and its parent */ > + of_match_bus(dev)->count_cells(dev, &na, &ns); > + if (!OF_CHECK_COUNTS(na, ns)) { > + printf("Bad cell count for %s\n", of_node_full_name(dev)); > + return -EINVAL; > + goto out; > + } > + > + of_match_bus(parent)->count_cells(parent, &pna, &pns); > + if (!OF_CHECK_COUNTS(pna, pns)) { > + printf("Bad cell count for %s\n", of_node_full_name(parent)); > + return -EINVAL; > + goto out; > + } > + > + *bus = of_read_number(ranges, na); > + *cpu = of_translate_dma_address(dev, of_read_number(ranges + na, > pna)); > + *size = of_read_number(ranges + na + pna, ns); > +out: > + return ret; > +} > + > + > static int __of_address_to_resource(const struct device_node *dev, > const __be32 *addrp, u64 size, unsigned int flags, > const char *name, struct resource *r) > diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c > index a68076bf35..15470d4875 100644 > --- a/drivers/core/ofnode.c > +++ b/drivers/core/ofnode.c > @@ -911,6 +911,15 @@ u64 ofnode_translate_dma_address(ofnode node, const > fdt32_t *in_addr) > return fdt_translate_dma_address(gd->fdt_blob, > ofnode_to_offset(node), in_addr); > } > > +int ofnode_get_dma_range(ofnode node, phys_addr_t *cpu, dma_addr_t *bus, u64 > *size) > +{ > + if (ofnode_is_np(node)) > + return of_get_dma_range(ofnode_to_np(node), cpu, bus, size); > + else > + return fdt_get_dma_range(gd->fdt_blob, ofnode_to_offset(node), > + cpu, bus, size); > +} > + > int ofnode_device_is_compatible(ofnode node, const char *compat) > { > if (ofnode_is_np(node)) > diff --git a/drivers/core/read.c b/drivers/core/read.c > index 076125824c..b835e82be9 100644 > --- a/drivers/core/read.c > +++ b/drivers/core/read.c > @@ -338,6 +338,11 @@ u64 dev_translate_dma_address(const struct udevice *dev, > const fdt32_t *in_addr) > return ofnode_translate_dma_address(dev_ofnode(dev), in_addr); > } > > +u64 dev_translate_cpu_address(const struct udevice *dev, const fdt32_t > *in_addr) > +{ > + return ofnode_translate_cpu_address(dev_ofnode(dev), in_addr); > +} > + > int dev_read_alias_highest_id(const char *stem) > { > if (of_live_active()) > diff --git a/include/dm/of_addr.h b/include/dm/of_addr.h > index 3fa1ffce81..ee21d5cf4f 100644 > --- a/include/dm/of_addr.h > +++ b/include/dm/of_addr.h > @@ -44,6 +44,23 @@ u64 of_translate_address(const struct device_node *no, > const __be32 *in_addr); > */ > u64 of_translate_dma_address(const struct device_node *no, const __be32 > *in_addr); > > + > +/** > + * of_get_dma_range() - get dma-ranges for a specific DT node > + * > + * Get DMA ranges for a specifc node, this is useful to perform bus->cpu and > + * cpu->bus address translations > + * > + * @param blob Pointer to device tree blob > + * @param node_offset Node DT offset > + * @param cpu Pointer to variable storing the range's cpu address > + * @param bus Pointer to variable storing the range's bus address > + * @param size Pointer to variable storing the range's size > + * @return translated DMA address or OF_BAD_ADDR on error > + */ > +int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu, > + dma_addr_t *bus, u64 *size); > + > /** > * of_get_address() - obtain an address from a node > * > diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h > index ced7f6ffb2..dc3dd84d9f 100644 > --- a/include/dm/ofnode.h > +++ b/include/dm/ofnode.h > @@ -939,6 +939,22 @@ u64 ofnode_translate_address(ofnode node, const fdt32_t > *in_addr); > */ > u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr); > > +/** > + * ofnode_get_dma_range() - get dma-ranges for a specific DT node > + * > + * Get DMA ranges for a specifc node, this is useful to perform bus->cpu and > + * cpu->bus address translations > + * > + * @param blob Pointer to device tree blob > + * @param node_offset Node DT offset > + * @param cpu Pointer to variable storing the range's cpu address > + * @param bus Pointer to variable storing the range's bus address > + * @param size Pointer to variable storing the range's size > + * @return translated DMA address or OF_BAD_ADDR on error > + */ > +int ofnode_get_dma_range(ofnode node, phys_addr_t *cpu, dma_addr_t *bus, > + u64 *size); > + > /** > * ofnode_device_is_compatible() - check if the node is compatible with > compat > * > diff --git a/include/dm/read.h b/include/dm/read.h > index 0585eb1228..9e1f3f5f12 100644 > --- a/include/dm/read.h > +++ b/include/dm/read.h > @@ -1004,6 +1004,12 @@ static inline u64 dev_translate_dma_address(const > struct udevice *dev, > return ofnode_translate_dma_address(dev_ofnode(dev), in_addr); > } > > +static inline u64 dev_get_dma_range(const struct udevice *dev, phys_addr_t > *cpu, > + dma_addr_t *bus, u64 *size) > +{ > + return ofnode_get_dma_range(dev_ofnode(dev), cpu, bus, size); > +} > + > static inline int dev_read_alias_highest_id(const char *stem) > { > if (!CONFIG_IS_ENABLED(OF_LIBFDT)) > diff --git a/include/fdt_support.h b/include/fdt_support.h > index dbbac0fb6a..46eb1dbbb2 100644 > --- a/include/fdt_support.h > +++ b/include/fdt_support.h > @@ -260,6 +260,20 @@ u64 fdt_translate_address(const void *blob, int > node_offset, > u64 fdt_translate_dma_address(const void *blob, int node_offset, > const __be32 *in_addr); > > +/** > + * Get DMA ranges for a specifc node, this is useful to perform bus->cpu and > + * cpu->bus address translations > + * > + * @param blob Pointer to device tree blob > + * @param node_offset Node DT offset > + * @param cpu Pointer to variable storing the range's cpu address > + * @param bus Pointer to variable storing the range's bus address > + * @param size Pointer to variable storing the range's size > + * @return translated DMA address or OF_BAD_ADDR on error > + */ > +int fdt_get_dma_range(const void *blob, int node_offset, phys_addr_t *cpu, > + dma_addr_t *bus, u64 *size); > + > int fdt_node_offset_by_compat_reg(void *blob, const char *compat, > phys_addr_t compat_off); > int fdt_alloc_phandle(void *blob); > -- > 2.29.2 >