dm: Introduce xxx_get_dma_range() 48/267748/3
authorNicolas Saenz Julienne <nsaenzjulienne@suse.de>
Tue, 12 Jan 2021 12:55:22 +0000 (13:55 +0100)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Mon, 13 Dec 2021 01:42:53 +0000 (01:42 +0000)
Add the following 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.

Change-Id: Ib15b8a346390bf698dbc274f58ae646ba0fd6711
Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Peter Robinson <pbrobinson@gmail.com>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
common/fdt_support.c
drivers/core/of_addr.c
drivers/core/ofnode.c
drivers/core/read.c
include/dm/of_addr.h
include/dm/ofnode.h
include/dm/read.h
include/fdt_support.h

index ee4176f2b2d679f2fed8ba23b56d08a27e2a998b..ff2a32b9bccc662fc2511b11761acf892fff5dd3 100644 (file)
@@ -1347,6 +1347,79 @@ 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;
+       struct of_bus *bus_node;
+       const fdt32_t *ranges;
+       int na, ns, pna, pns;
+       int parent = node;
+       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 = -EINVAL;
+                       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 = -ENOENT;
+               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 */
+       bus_node = of_match_bus(blob, node);
+       bus_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;
+       }
+
+       bus_node = of_match_bus(blob, parent);
+       bus_node->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 = fdt_translate_dma_address(blob, node, ranges + na);
+       *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
index ca34d84922b58cef201e9fb17102a5b93ffd6f88..703bc3e3f59ef39088c18276a73d728ee3c6b0aa 100644 (file)
@@ -325,6 +325,84 @@ 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;
+       struct of_bus *bus_node;
+       int na, ns, pna, pns;
+       const __be32 *ranges;
+       int ret = 0;
+       int len;
+
+       /* Find the closest dma-ranges property */
+       dev = of_node_get(dev);
+       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 = -EINVAL;
+                       goto out;
+               }
+
+               if (ranges)
+                       found_dma_ranges = true;
+
+               parent = of_get_parent(dev);
+               of_node_put(dev);
+               dev = parent;
+       }
+
+       if (!dev || !ranges) {
+               debug("no dma-ranges found for node %s\n",
+                     of_node_full_name(dev));
+               ret = -ENOENT;
+               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 */
+       bus_node = of_match_bus((struct device_node*)dev);
+       bus_node->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_parent;
+       }
+
+       bus_node = of_match_bus(parent);
+       bus_node->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_parent;
+       }
+
+       *bus = of_read_number(ranges, na);
+       *cpu = of_translate_dma_address(dev, ranges + na);
+       *size = of_read_number(ranges + na + pna, ns);
+
+out_parent:
+       of_node_put(parent);
+out:
+       of_node_put(dev);
+       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)
index d02d8d33fef178410bae3a9c1327a7b12bb3542b..d4e69b0074d8c3caeb244e18c17f55e528c431b4 100644 (file)
@@ -888,6 +888,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))
index 86f3f881706e046bffc8338502922a6a8e40c194..333e011fef946329a644a428ef6c81643b292e39 100644 (file)
@@ -337,6 +337,12 @@ u64 dev_translate_dma_address(const struct udevice *dev, const fdt32_t *in_addr)
        return ofnode_translate_dma_address(dev_ofnode(dev), in_addr);
 }
 
+int 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);
+}
+
 int dev_read_alias_highest_id(const char *stem)
 {
        if (of_live_active())
index 3fa1ffce81a69587893e1af0f0547bc52ef51b4e..ee21d5cf4fc03afbbc458fceeb27e2bffbb19f9b 100644 (file)
@@ -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
  *
index 8df2facf99851d97b5fb76086c37355293c49061..d1be553b37da295aae6437de5f2ad8610bfb1ed8 100644 (file)
@@ -915,6 +915,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
  *
index 67db94adfc14b0870ed89b348538e40f486840cc..6ee7099bd408a03e454d75156ab27d0f55b8fdb5 100644 (file)
@@ -662,6 +662,21 @@ u64 dev_translate_address(const struct udevice *dev, const fdt32_t *in_addr);
 u64 dev_translate_dma_address(const struct udevice *dev,
                              const fdt32_t *in_addr);
 
+/**
+ * dev_get_dma_range() - Get a device's DMA constraints
+ *
+ * Provide the address bases and size of the linear mapping between the CPU and
+ * a device's BUS address space.
+ *
+ * @dev: device giving the context in which to translate the DMA address
+ * @cpu: base address for CPU's view of memory
+ * @bus: base address for BUS's view of memory
+ * @size: size of the address space
+ * @return 0 if ok, negative on error
+ */
+int dev_get_dma_range(const struct udevice *dev, phys_addr_t *cpu,
+                     dma_addr_t *bus, u64 *size);
+
 /**
  * dev_read_alias_highest_id - Get highest alias id for the given stem
  * @stem:      Alias stem to be examined
@@ -1002,6 +1017,12 @@ static inline u64 dev_translate_dma_address(const struct udevice *dev,
        return ofnode_translate_dma_address(dev_ofnode(dev), in_addr);
 }
 
+static inline int 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))
index 8fc84cc87c8ada2646e1ee7e30442669886f2a51..49da0ee5ef1e5893530fa9ce73274d7256bbfde1 100644 (file)
@@ -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);