fdt_support: Add helper function to read "ranges" property
authorAlexander Graf <agraf@suse.de>
Fri, 11 Apr 2014 15:09:41 +0000 (17:09 +0200)
committerYork Sun <yorksun@freescale.com>
Wed, 23 Apr 2014 00:58:45 +0000 (17:58 -0700)
This patch adds a helper function that can be used to interpret most
"ranges" properties in the device tree.

It reads the n'th range out of a "ranges" array and returns the node's
virtual address of the range, the physical address that range starts at
and the size of the range.

Signed-off-by: Alexander Graf <agraf@suse.de>
Acked-by: Scott Wood <scottwood@freescale.com>
Reviewed-by: York Sun <yorksun@freescale.com>
common/fdt_support.c
include/fdt_support.h

index cc0bf76595be947e2b399098f8d7ea24a5ffd688..fcd252336cdf15d8cebe503e4025ef2f07d41b09 100644 (file)
@@ -1435,3 +1435,97 @@ u64 fdt_get_base_address(void *fdt, int node)
 
        return prop ? fdt_translate_address(fdt, node, prop + naddr) : 0;
 }
+
+/*
+ * Read a property of size <prop_len>. Currently only supports 1 or 2 cells.
+ */
+static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off,
+                        uint64_t *val, int cells)
+{
+       const fdt32_t *prop32 = &prop[cell_off];
+       const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off];
+
+       if ((cell_off + cells) > prop_len)
+               return -FDT_ERR_NOSPACE;
+
+       switch (cells) {
+       case 1:
+               *val = fdt32_to_cpu(*prop32);
+               break;
+       case 2:
+               *val = fdt64_to_cpu(*prop64);
+               break;
+       default:
+               return -FDT_ERR_NOSPACE;
+       }
+
+       return 0;
+}
+
+/**
+ * fdt_read_range - Read a node's n'th range property
+ *
+ * @fdt: ptr to device tree
+ * @node: offset of node
+ * @n: range index
+ * @child_addr: pointer to storage for the "child address" field
+ * @addr: pointer to storage for the CPU view translated physical start
+ * @len: pointer to storage for the range length
+ *
+ * Convenience function that reads and interprets a specific range out of
+ * a number of the "ranges" property array.
+ */
+int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr,
+                  uint64_t *addr, uint64_t *len)
+{
+       int pnode = fdt_parent_offset(fdt, node);
+       const fdt32_t *ranges;
+       int pacells;
+       int acells;
+       int scells;
+       int ranges_len;
+       int cell = 0;
+       int r = 0;
+
+       /*
+        * The "ranges" property is an array of
+        * { <child address> <parent address> <size in child address space> }
+        *
+        * All 3 elements can span a diffent number of cells. Fetch their size.
+        */
+       pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1);
+       acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1);
+       scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1);
+
+       /* Now try to get the ranges property */
+       ranges = fdt_getprop(fdt, node, "ranges", &ranges_len);
+       if (!ranges)
+               return -FDT_ERR_NOTFOUND;
+       ranges_len /= sizeof(uint32_t);
+
+       /* Jump to the n'th entry */
+       cell = n * (pacells + acells + scells);
+
+       /* Read <child address> */
+       if (child_addr) {
+               r = fdt_read_prop(ranges, ranges_len, cell, child_addr,
+                                 acells);
+               if (r)
+                       return r;
+       }
+       cell += acells;
+
+       /* Read <parent address> */
+       if (addr)
+               *addr = fdt_translate_address(fdt, node, ranges + cell);
+       cell += pacells;
+
+       /* Read <size in child address space> */
+       if (len) {
+               r = fdt_read_prop(ranges, ranges_len, cell, len, scells);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
index 61383ddd43f07c7df239cd50c684e658f69e5f6d..b77a0f1f89dc648f0fa376cda3751a746edd8b57 100644 (file)
@@ -87,6 +87,8 @@ int fdt_add_edid(void *blob, const char *compat, unsigned char *buf);
 int fdt_verify_alias_address(void *fdt, int anode, const char *alias,
                              u64 addr);
 u64 fdt_get_base_address(void *fdt, int node);
+int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr,
+                  uint64_t *addr, uint64_t *len);
 
 enum fdt_status {
        FDT_STATUS_OKAY,