[RFC PATCH v1 07/20] drivers: core: of_addr: fix of_get_dma_range translation

Oleksii Moisieiev Oleksii_Moisieiev at epam.com
Wed Feb 5 11:15:44 CET 2025


According to the Section 2.3.9 of the Device Tree specification [0]
the format of the value of the dma-ranges property is an arbitrary
number of triplets of (child-bus-address,
parent-bus-address, length).
Where:
* The child-bus-address is a physical address within the child bus’
  address space. The number of cells to represent the address depends on
  the bus and can be determined from the #address-cells of this node
  (the node in which the dma-ranges property appears).
* The parent-bus-address is a physical address within the parent bus’
  address space. The number of cells to represent the parent address
  is bus dependent and can be determined from the #address-cells
  property of the node that defines the parent’s address space.
* The length specifies the size of the range in the child’s address
  space. The number of cells to represent the size can be determined
  from the #size-cells of this node (the node in which the dma-ranges
  property appears)

This description matches comments in the of_addr source code. But
actual implementation do not match the specification.
'of_match_bus' call returns address-cells and size-cells of the parent
device. That's why the following calls were used to receive addr_cells
and size_cells of the current device:
- of_simple_addr_cells
- of_simple_size_cells

This fix was tested on RPI5 board with both device types: directly
connected (e.g Serial) and connected over PCIe (e.g ethernet).

For example when dma_ranges are parsing the following device tree
structure:
/ {
	pcie2: {
		#address-cells = <3>;
		#size-cells = <2>;
		// 64GB system RAM space at PCIe 10_00000000
		dma-ranges =
			   <0x02000000 0x00 0x00000000
			    0x1f 0x00000000
			    0x00 0x00400000>,
			   <0x43000000 0x10 0x00000000
			    0x00 0x00000000
			    0x10 0x00000000>;
		rp1: {
			#address-cells = <2>;
			#size-cells = <2>;
			dma-ranges =
			// inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx
			  <0x10 0x00000000
			  0x43000000 0x10 0x00000000
			  0x10 0x00000000>,

			// inbound RP1 c0_40xxxxxx -> PCIe 00_00xxxxxx
			  <0xc0 0x40000000
			  0x02000000 0x0 0x00000000
			  0x0 0x00400000>,

			// inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx
			  <0x00 0x00000000
			  0x02000000 0x10 0x00000000
			  0x10 0x00000000>;
		};
	};
};

So when we parse ranges on rp1 - #address-cells of the rp1 node should
be taken into account.
In the provided example we have the following format:
pcie2:
   #addres-cells = <0x3>;
   #size-cells = <0x2>;
   dma-ranges = < addr_hi addr_mid addr_lo
                  trans_addr_hi trans_addr_lo
		  size_hi size_lo>;

rp1:
   #addres-cells = <0x2>;
   #size-cells = <0x2>;
   dma-ranges = < addr_hi addr_lo
                  trans_addr_hi trans_addr_mid trans_addr_lo
		  size_hi size_lo>;

of_get_dma_range searches for the first dev with dma_ranges
and then executes of_match_bus which follows by count_cells call
for both device and parent.

count_cells callback returns #address-cells for the parent device to
the caller.
So in the provided example address cells should be returned 2 for rp1,
but we get 3 when using count_cells callback.
This breaks address translation and was fixed in the provided patch
by using of_simple_addr_cells call. Which will return correct
address-cells for the node. This matches the requirements of the
device-tree specification.

[0] https://readthedocs.org/projects/devicetree-specification/downloads/pdf/latest/

Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev at epam.com>
Reviewed-by: Volodymyr Babchuk <volodymyr_babchuk at epam.com>
---

 drivers/core/of_addr.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/core/of_addr.c b/drivers/core/of_addr.c
index b3b3d7ccdd..4a321a8263 100644
--- a/drivers/core/of_addr.c
+++ b/drivers/core/of_addr.c
@@ -374,16 +374,16 @@ int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu,
 	}
 
 	/* Get the address sizes both for the bus and its parent */
-	bus_node = of_match_bus((struct device_node*)dev);
-	bus_node->count_cells(dev, &na, &ns);
+	na = of_simple_addr_cells(dev);
+	ns = of_simple_size_cells(dev);
 	if (!OF_CHECK_COUNTS(na, ns)) {
 		printf("Bad cell count for %s\n", of_node_full_name(dev));
 		ret = -EINVAL;
 		goto out_parent;
 	}
 
-	bus_node = of_match_bus(parent);
-	bus_node->count_cells(parent, &pna, &pns);
+	bus_node = of_match_bus((struct device_node *)dev);
+	bus_node->count_cells(dev, &pna, &pns);
 	if (!OF_CHECK_COUNTS(pna, pns)) {
 		printf("Bad cell count for %s\n", of_node_full_name(parent));
 		ret = -EINVAL;
-- 
2.34.1


More information about the U-Boot mailing list