Просмотр исходного кода

[FEATURE/OFW] add address reverse/translate for DMA/CPU's address

DMA and CPU address view is different, we need to convert them:

    +--------+    +--------+  +---------+          +--------+
    |        |    |        |  |         |          |        |
    |  CPUs  |    |  DEV0  |  |  IOMMU  <----+     |  DEV1  |
    |        |    |        |  |         |    |     |        |
    +----+---+    +----+---+  +----+----+    |     +----+---+
         |             |           |         |          |
0x200000 |      0x1000 |    0x1000 |         |   0x8000 |
         |             |           |         |          |
         +-------------+-----------+         +----------+
         |
         |
+--------v----------------------------------------------------+
|                                                             |
|                          Address BUS                        |
|                                                             |
+-------------------------------------------------------------+

Signed-off-by: GuEe-GUI <2991707448@qq.com>
GuEe-GUI 11 месяцев назад
Родитель
Сommit
2d026a316a
2 измененных файлов с 90 добавлено и 0 удалено
  1. 21 0
      components/drivers/include/drivers/ofw_io.h
  2. 69 0
      components/drivers/ofw/io.c

+ 21 - 0
components/drivers/include/drivers/ofw_io.h

@@ -26,6 +26,27 @@ rt_err_t rt_ofw_get_address_by_name(struct rt_ofw_node *np, const char *name,
 int rt_ofw_get_address_array(struct rt_ofw_node *np, int nr, rt_uint64_t *out_regs);
 
 rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address);
+rt_uint64_t rt_ofw_reverse_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address);
+
+rt_inline rt_uint64_t rt_ofw_translate_dma2cpu(struct rt_ofw_node *np, rt_uint64_t address)
+{
+    rt_uint64_t bus_addr, cpu_addr;
+
+    bus_addr = rt_ofw_reverse_address(np, "dma-ranges", address);
+    cpu_addr = rt_ofw_translate_address(np, "ranges", bus_addr);
+
+    return cpu_addr != ~0ULL ? cpu_addr : address;
+}
+
+rt_inline rt_uint64_t rt_ofw_translate_cpu2dma(struct rt_ofw_node *np, rt_uint64_t address)
+{
+    rt_uint64_t bus_addr, dma_addr;
+
+    bus_addr = rt_ofw_reverse_address(np, "ranges", address);
+    dma_addr = rt_ofw_translate_address(np, "dma-ranges", bus_addr);
+
+    return dma_addr != ~0ULL ? dma_addr : address;
+}
 
 void *rt_ofw_iomap(struct rt_ofw_node *np, int index);
 void *rt_ofw_iomap_by_name(struct rt_ofw_node *np, const char *name);

+ 69 - 0
components/drivers/ofw/io.c

@@ -407,6 +407,75 @@ rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_t
     return cpu_addr;
 }
 
+rt_uint64_t rt_ofw_reverse_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address)
+{
+    rt_uint64_t bus_addr = address;
+
+    if (!range_type)
+    {
+        range_type = "ranges";
+    }
+
+    rt_ofw_foreach_parent_node(np)
+    {
+        rt_ssize_t len;
+        struct rt_ofw_prop *prop;
+        struct bus_ranges *ranges = RT_NULL;
+
+        prop = rt_ofw_get_prop(np, range_type, &len);
+
+        if (!prop || !len)
+        {
+            continue;
+        }
+
+        for (int i = 0; i < RT_ARRAY_SIZE(_bus_ranges); ++i)
+        {
+            if (!_bus_ranges[i])
+            {
+                break;
+            }
+
+            if (_bus_ranges[i]->np == np)
+            {
+                ranges = _bus_ranges[i];
+                break;
+            }
+        }
+
+        if (!ranges)
+        {
+            ranges = ofw_bus_ranges(np, prop);
+        }
+
+        if (ranges)
+        {
+            for (int i = 0; i < ranges->nr; ++i)
+            {
+                rt_uint64_t parent_addr = ranges->parent_addr[i];
+                rt_uint64_t child_size = ranges->child_size[i];
+
+                if (address >= parent_addr && address < parent_addr + child_size)
+                {
+                    bus_addr = ranges->child_addr[i] + (address - parent_addr);
+
+                    break;
+                }
+            }
+        }
+        else
+        {
+            bus_addr = ~0ULL;
+        }
+
+        rt_ofw_node_put(np);
+
+        break;
+    }
+
+    return bus_addr;
+}
+
 #ifdef ARCH_CPU_64BIT
 #define ofw_address_cpu_cast(np, address) (void *)(address)
 #else