diff mbox series

[4/8] dm: Introduce xxx_get_dma_range()

Message ID 20201119174820.7820-5-nsaenzjulienne@suse.de
State Superseded
Delegated to: Matthias Brugger
Headers show
Series [1/8] rpi: Add identifier for the new RPi400 | expand

Commit Message

Nicolas Saenz Julienne Nov. 19, 2020, 5:48 p.m. UTC
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.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@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(+)

Comments

Matthias Brugger Dec. 9, 2020, 12:58 p.m. UTC | #1
On 19/11/2020 18:48, Nicolas Saenz Julienne 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.
> 
> Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@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);

Please see comment in of_get_dma_range().

> +	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;
> +	}

Aren't we missing a of_node_put(parent) here?

> +
> +	/* Get the address sizes both for the bus and its parent */
> +	of_match_bus(dev)->count_cells(dev, &na, &ns);

of_bus = of_match_bus(dev);
of_bus->count_cells(...)

> +	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);
> +}
> +

Dead code, please delete.

>  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);
>
Nicolas Saenz Julienne Dec. 9, 2020, 4:30 p.m. UTC | #2
On Wed, 2020-12-09 at 13:58 +0100, Matthias Brugger wrote:
[...]
> > +
> > +	/* 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;
> > +	}
> 
> Aren't we missing a of_node_put(parent) here?

Yes, that's right.

> > +
> > +	/* Get the address sizes both for the bus and its parent */
> > +	of_match_bus(dev)->count_cells(dev, &na, &ns);
> 
> of_bus = of_match_bus(dev);
> of_bus->count_cells(...)

OK

[...]

> > 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);
> > +}
> > +
> 
> Dead code, please delete.

Yes, sorry for that.

Regards,
Nicolas
Nicolas Saenz Julienne Dec. 9, 2020, 4:47 p.m. UTC | #3
On Wed, 2020-12-09 at 17:30 +0100, Nicolas Saenz Julienne wrote:
> On Wed, 2020-12-09 at 13:58 +0100, Matthias Brugger wrote:
> [...]
> > > +
> > > +	/* 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;
> > > +	}
> > 
> > Aren't we missing a of_node_put(parent) here?
> 
> Yes, that's right.

Actually I now see why I didn't do it, as there is no need:

/* Dummy functions to mirror Linux. These are not used in U-Boot */
#define of_node_get(x) (x)
static inline void of_node_put(const struct device_node *np) { }

Regards,
Nicolas
Peter Robinson Dec. 10, 2020, 5:03 p.m. UTC | #4
Hi Nicolas,


On Thu, Nov 19, 2020 at 5:50 PM Nicolas Saenz Julienne
<nsaenzjulienne@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 <nsaenzjulienne@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
>
Nicolas Saenz Julienne Dec. 10, 2020, 6:36 p.m. UTC | #5
Hi Peter,

On Thu, 2020-12-10 at 17:03 +0000, Peter Robinson wrote:
> Hi Nicolas,
> 
> 
> On Thu, Nov 19, 2020 at 5:50 PM Nicolas Saenz Julienne
> <nsaenzjulienne@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 think didn't directly CC you, sorry for that. I'll keep it in mind for the
next round.

> 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:

Thanks for having a lookg at this. I've been strugling to test my series
against all posible configurations. What are you using catch the regressions?
Is something I can run locally?

>   /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'

I reproduced it locally, thanks! I'll fit it.

Regards,
Nicolas
Peter Robinson Dec. 11, 2020, 6:49 p.m. UTC | #6
Hi Nicolas,

> > > 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 think didn't directly CC you, sorry for that. I'll keep it in mind for the
> next round.
>
> > 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:
>
> Thanks for having a lookg at this. I've been strugling to test my series
> against all posible configurations. What are you using catch the regressions?
> Is something I can run locally?

It's just the list of configs we build for Fedora.

> >   /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'
>
> I reproduced it locally, thanks! I'll fit it.
>
> Regards,
> Nicolas
>
Simon Glass Dec. 12, 2020, 3:39 p.m. UTC | #7
Hi Nicolas,

On Thu, 19 Nov 2020 at 10:49, Nicolas Saenz Julienne
<nsaenzjulienne@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.
>
> Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@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(+)

Please add sandbox tests for this, e.g. if you add some tests in a new
test/dm/read.c file that call the read() API that should be enough to
test your flattree and livetree calls.

Without tests we'll never know if the code works, or be able to change
it safely later.

Also, -ENODEV means there is no device and is reserved for driver
model. How about -ENOENT?

Regards,
Simon
diff mbox series

Patch

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);