[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