Merge tag 'v3.10.13' into lsk/v3.10/topic/kvm
[firefly-linux-kernel-4.4.55.git] / drivers / of / fdt.c
index b6e4d7fcd316fd0911305bf8c3cb48fbf28d3422..4911158cba8a154b80aaeac44ef39a05741d93df 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/memblock.h>
+#include <linux/libfdt.h>
 
 #include <asm/setup.h>  /* for COMMAND_LINE_SIZE */
 #ifdef CONFIG_PPC
 
 #include <asm/page.h>
 
-char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)
-{
-       return ((char *)blob) +
-               be32_to_cpu(blob->off_dt_strings) + offset;
-}
-
-/**
- * of_fdt_get_property - Given a node in the given flat blob, return
- * the property ptr
- */
-void *of_fdt_get_property(struct boot_param_header *blob,
-                      unsigned long node, const char *name,
-                      unsigned long *size)
-{
-       unsigned long p = node;
-
-       do {
-               u32 tag = be32_to_cpup((__be32 *)p);
-               u32 sz, noff;
-               const char *nstr;
-
-               p += 4;
-               if (tag == OF_DT_NOP)
-                       continue;
-               if (tag != OF_DT_PROP)
-                       return NULL;
-
-               sz = be32_to_cpup((__be32 *)p);
-               noff = be32_to_cpup((__be32 *)(p + 4));
-               p += 8;
-               if (be32_to_cpu(blob->version) < 0x10)
-                       p = ALIGN(p, sz >= 8 ? 8 : 4);
-
-               nstr = of_fdt_get_string(blob, noff);
-               if (nstr == NULL) {
-                       pr_warning("Can't find property index name !\n");
-                       return NULL;
-               }
-               if (strcmp(name, nstr) == 0) {
-                       if (size)
-                               *size = sz;
-                       return (void *)p;
-               }
-               p += sz;
-               p = ALIGN(p, 4);
-       } while (1);
-}
-
 /**
  * of_fdt_is_compatible - Return true if given node from the given blob has
  * compat in its compatible list
@@ -90,9 +43,10 @@ int of_fdt_is_compatible(struct boot_param_header *blob,
                      unsigned long node, const char *compat)
 {
        const char *cp;
-       unsigned long cplen, l, score = 0;
+       int cplen;
+       unsigned long l, score = 0;
 
-       cp = of_fdt_get_property(blob, node, "compatible", &cplen);
+       cp = fdt_getprop(blob, node, "compatible", &cplen);
        if (cp == NULL)
                return 0;
        while (cplen > 0) {
@@ -128,12 +82,12 @@ int of_fdt_match(struct boot_param_header *blob, unsigned long node,
        return score;
 }
 
-static void *unflatten_dt_alloc(unsigned long *mem, unsigned long size,
+static void *unflatten_dt_alloc(void **mem, unsigned long size,
                                       unsigned long align)
 {
        void *res;
 
-       *mem = ALIGN(*mem, align);
+       *mem = PTR_ALIGN(*mem, align);
        res = (void *)*mem;
        *mem += size;
 
@@ -149,30 +103,29 @@ static void *unflatten_dt_alloc(unsigned long *mem, unsigned long size,
  * @allnextpp: pointer to ->allnext from last allocated device_node
  * @fpsize: Size of the node path up at the current depth.
  */
-static unsigned long unflatten_dt_node(struct boot_param_header *blob,
-                               unsigned long mem,
-                               unsigned long *p,
+static void * unflatten_dt_node(struct boot_param_header *blob,
+                               void *mem,
+                               int *poffset,
                                struct device_node *dad,
                                struct device_node ***allnextpp,
                                unsigned long fpsize)
 {
+       const __be32 *p;
        struct device_node *np;
        struct property *pp, **prev_pp = NULL;
-       char *pathp;
-       u32 tag;
+       const char *pathp;
        unsigned int l, allocl;
+       static int depth = 0;
+       int old_depth;
+       int offset;
        int has_name = 0;
        int new_format = 0;
 
-       tag = be32_to_cpup((__be32 *)(*p));
-       if (tag != OF_DT_BEGIN_NODE) {
-               pr_err("Weird tag at start of node: %x\n", tag);
+       pathp = fdt_get_name(blob, *poffset, &l);
+       if (!pathp)
                return mem;
-       }
-       *p += 4;
-       pathp = (char *)*p;
-       l = allocl = strlen(pathp) + 1;
-       *p = ALIGN(*p + l, 4);
+
+       allocl = l++;
 
        /* version 0x10 has a more compact unit name here instead of the full
         * path. we accumulate the full path size using "fpsize", we'll rebuild
@@ -190,7 +143,7 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
                        fpsize = 1;
                        allocl = 2;
                        l = 1;
-                       *pathp = '\0';
+                       pathp = "";
                } else {
                        /* account for '/' and path size minus terminal 0
                         * already in 'l'
@@ -238,32 +191,23 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
                kref_init(&np->kref);
        }
        /* process properties */
-       while (1) {
-               u32 sz, noff;
-               char *pname;
-
-               tag = be32_to_cpup((__be32 *)(*p));
-               if (tag == OF_DT_NOP) {
-                       *p += 4;
-                       continue;
-               }
-               if (tag != OF_DT_PROP)
+       for (offset = fdt_first_property_offset(blob, *poffset);
+            (offset >= 0);
+            (offset = fdt_next_property_offset(blob, offset))) {
+               const char *pname;
+               u32 sz;
+
+               if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
+                       offset = -FDT_ERR_INTERNAL;
                        break;
-               *p += 4;
-               sz = be32_to_cpup((__be32 *)(*p));
-               noff = be32_to_cpup((__be32 *)((*p) + 4));
-               *p += 8;
-               if (be32_to_cpu(blob->version) < 0x10)
-                       *p = ALIGN(*p, sz >= 8 ? 8 : 4);
-
-               pname = of_fdt_get_string(blob, noff);
+               }
+
                if (pname == NULL) {
                        pr_info("Can't find property name in list !\n");
                        break;
                }
                if (strcmp(pname, "name") == 0)
                        has_name = 1;
-               l = strlen(pname) + 1;
                pp = unflatten_dt_alloc(&mem, sizeof(struct property),
                                        __alignof__(struct property));
                if (allnextpp) {
@@ -275,26 +219,25 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
                        if ((strcmp(pname, "phandle") == 0) ||
                            (strcmp(pname, "linux,phandle") == 0)) {
                                if (np->phandle == 0)
-                                       np->phandle = be32_to_cpup((__be32*)*p);
+                                       np->phandle = be32_to_cpup(p);
                        }
                        /* And we process the "ibm,phandle" property
                         * used in pSeries dynamic device tree
                         * stuff */
                        if (strcmp(pname, "ibm,phandle") == 0)
-                               np->phandle = be32_to_cpup((__be32 *)*p);
-                       pp->name = pname;
+                               np->phandle = be32_to_cpup(p);
+                       pp->name = (char *)pname;
                        pp->length = sz;
-                       pp->value = (void *)*p;
+                       pp->value = (__be32 *)p;
                        *prev_pp = pp;
                        prev_pp = &pp->next;
                }
-               *p = ALIGN((*p) + sz, 4);
        }
        /* with version 0x10 we may not have the name property, recreate
         * it here from the unit name if absent
         */
        if (!has_name) {
-               char *p1 = pathp, *ps = pathp, *pa = NULL;
+               const char *p1 = pathp, *ps = pathp, *pa = NULL;
                int sz;
 
                while (*p1) {
@@ -331,19 +274,18 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
                if (!np->type)
                        np->type = "<NULL>";
        }
-       while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {
-               if (tag == OF_DT_NOP)
-                       *p += 4;
-               else
-                       mem = unflatten_dt_node(blob, mem, p, np, allnextpp,
-                                               fpsize);
-               tag = be32_to_cpup((__be32 *)(*p));
-       }
-       if (tag != OF_DT_END_NODE) {
-               pr_err("Weird tag at end of node: %x\n", tag);
-               return mem;
-       }
-       *p += 4;
+
+       old_depth = depth;
+       *poffset = fdt_next_node(blob, *poffset, &depth);
+       if (depth < 0)
+               depth = 0;
+       while (*poffset > 0 && depth > old_depth)
+               mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
+                                       fpsize);
+
+       if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
+               pr_err("unflatten: error %d processing FDT\n", *poffset);
+
        return mem;
 }
 
@@ -363,7 +305,9 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
                             struct device_node **mynodes,
                             void * (*dt_alloc)(u64 size, u64 align))
 {
-       unsigned long start, mem, size;
+       unsigned long size;
+       int start;
+       void *mem;
        struct device_node **allnextp = mynodes;
 
        pr_debug(" -> unflatten_device_tree()\n");
@@ -384,28 +328,25 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
        }
 
        /* First pass, scan for size */
-       start = ((unsigned long)blob) +
-               be32_to_cpu(blob->off_dt_struct);
-       size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
-       size = (size | 3) + 1;
+       start = 0;
+       size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
+       size = ALIGN(size, 4);
 
        pr_debug("  size is %lx, allocating...\n", size);
 
        /* Allocate memory for the expanded device tree */
-       mem = (unsigned long)
-               dt_alloc(size + 4, __alignof__(struct device_node));
+       mem = dt_alloc(size + 4, __alignof__(struct device_node));
+
+       memset((void *)mem, 0, size);
 
        ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
 
-       pr_debug("  unflattening %lx...\n", mem);
+       pr_debug("  unflattening %p...\n", mem);
 
        /* Second pass, do actual unflattening */
-       start = ((unsigned long)blob) +
-               be32_to_cpu(blob->off_dt_struct);
+       start = 0;
        unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
-       if (be32_to_cpup((__be32 *)start) != OF_DT_END)
-               pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
-       if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef)
+       if (be32_to_cpup(mem + size) != 0xdeadbeef)
                pr_warning("End of tree marker overwritten: %08x\n",
                           be32_to_cpu(((__be32 *)mem)[size / 4]));
        *allnextp = NULL;
@@ -451,8 +392,8 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
 {
        int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
        phys_addr_t base, size;
-       unsigned long len;
-       __be32 *prop;
+       int len;
+       const __be32 *prop;
        int nomap, first = 1;
 
        prop = of_get_flat_dt_prop(node, "reg", &len);
@@ -493,9 +434,9 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
  * in /reserved-memory matches the values supported by the current implementation,
  * also check if ranges property has been provided
  */
-static int __reserved_mem_check_root(unsigned long node)
+static int __init __reserved_mem_check_root(unsigned long node)
 {
-       __be32 *prop;
+       const __be32 *prop;
 
        prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
        if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
@@ -559,6 +500,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
  */
 void __init early_init_fdt_scan_reserved_mem(void)
 {
+       if (!initial_boot_params)
+               return;
+
        of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
        fdt_init_reserved_mem();
 }
@@ -577,47 +521,19 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
                                     void *data),
                           void *data)
 {
-       unsigned long p = ((unsigned long)initial_boot_params) +
-               be32_to_cpu(initial_boot_params->off_dt_struct);
-       int rc = 0;
-       int depth = -1;
-
-       do {
-               u32 tag = be32_to_cpup((__be32 *)p);
-               const char *pathp;
-
-               p += 4;
-               if (tag == OF_DT_END_NODE) {
-                       depth--;
-                       continue;
-               }
-               if (tag == OF_DT_NOP)
-                       continue;
-               if (tag == OF_DT_END)
-                       break;
-               if (tag == OF_DT_PROP) {
-                       u32 sz = be32_to_cpup((__be32 *)p);
-                       p += 8;
-                       if (be32_to_cpu(initial_boot_params->version) < 0x10)
-                               p = ALIGN(p, sz >= 8 ? 8 : 4);
-                       p += sz;
-                       p = ALIGN(p, 4);
-                       continue;
-               }
-               if (tag != OF_DT_BEGIN_NODE) {
-                       pr_err("Invalid tag %x in flat device tree!\n", tag);
-                       return -EINVAL;
-               }
-               depth++;
-               pathp = (char *)p;
-               p = ALIGN(p + strlen(pathp) + 1, 4);
+       const void *blob = initial_boot_params;
+       const char *pathp;
+       int offset, rc = 0, depth = -1;
+
+        for (offset = fdt_next_node(blob, -1, &depth);
+             offset >= 0 && depth >= 0 && !rc;
+             offset = fdt_next_node(blob, offset, &depth)) {
+
+               pathp = fdt_get_name(blob, offset, NULL);
                if (*pathp == '/')
                        pathp = kbasename(pathp);
-               rc = it(p, pathp, depth, data);
-               if (rc != 0)
-                       break;
-       } while (1);
-
+               rc = it(offset, pathp, depth, data);
+       }
        return rc;
 }
 
@@ -626,14 +542,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
  */
 unsigned long __init of_get_flat_dt_root(void)
 {
-       unsigned long p = ((unsigned long)initial_boot_params) +
-               be32_to_cpu(initial_boot_params->off_dt_struct);
-
-       while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
-               p += 4;
-       BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
-       p += 4;
-       return ALIGN(p + strlen((char *)p) + 1, 4);
+       return 0;
 }
 
 /**
@@ -642,10 +551,10 @@ unsigned long __init of_get_flat_dt_root(void)
  * This function can be used within scan_flattened_dt callback to get
  * access to properties
  */
-void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
-                                unsigned long *size)
+const void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
+                                      int *size)
 {
-       return of_fdt_get_property(initial_boot_params, node, name, size);
+       return fdt_getprop(initial_boot_params, node, name, size);
 }
 
 /**
@@ -675,73 +584,6 @@ struct fdt_scan_status {
        void *data;
 };
 
-/**
- * fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function
- */
-static int __init fdt_scan_node_by_path(unsigned long node, const char *uname,
-                                       int depth, void *data)
-{
-       struct fdt_scan_status *st = data;
-
-       /*
-        * if scan at the requested fdt node has been completed,
-        * return -ENXIO to abort further scanning
-        */
-       if (depth <= st->depth)
-               return -ENXIO;
-
-       /* requested fdt node has been found, so call iterator function */
-       if (st->found)
-               return st->iterator(node, uname, depth, st->data);
-
-       /* check if scanning automata is entering next level of fdt nodes */
-       if (depth == st->depth + 1 &&
-           strncmp(st->name, uname, st->namelen) == 0 &&
-           uname[st->namelen] == 0) {
-               st->depth += 1;
-               if (st->name[st->namelen] == 0) {
-                       st->found = 1;
-               } else {
-                       const char *next = st->name + st->namelen + 1;
-                       st->name = next;
-                       st->namelen = strcspn(next, "/");
-               }
-               return 0;
-       }
-
-       /* scan next fdt node */
-       return 0;
-}
-
-/**
- * of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each
- *                          child of the given path.
- * @path: path to start searching for children
- * @it: callback function
- * @data: context data pointer
- *
- * This function is used to scan the flattened device-tree starting from the
- * node given by path. It is used to extract information (like reserved
- * memory), which is required on ealy boot before we can unflatten the tree.
- */
-int __init of_scan_flat_dt_by_path(const char *path,
-       int (*it)(unsigned long node, const char *name, int depth, void *data),
-       void *data)
-{
-       struct fdt_scan_status st = {path, 0, -1, 0, it, data};
-       int ret = 0;
-
-       if (initial_boot_params)
-                ret = of_scan_flat_dt(fdt_scan_node_by_path, &st);
-
-       if (!st.found)
-               return -ENOENT;
-       else if (ret == -ENXIO) /* scan has been completed */
-               return 0;
-       else
-               return ret;
-}
-
 #ifdef CONFIG_BLK_DEV_INITRD
 /**
  * early_init_dt_check_for_initrd - Decode initrd location from flat tree
@@ -750,8 +592,8 @@ int __init of_scan_flat_dt_by_path(const char *path,
 void __init early_init_dt_check_for_initrd(unsigned long node)
 {
        u64 start, end;
-       unsigned long len;
-       __be32 *prop;
+       int len;
+       const __be32 *prop;
 
        pr_debug("Looking for initrd properties... ");
 
@@ -781,7 +623,7 @@ inline void early_init_dt_check_for_initrd(unsigned long node)
 int __init early_init_dt_scan_root(unsigned long node, const char *uname,
                                   int depth, void *data)
 {
-       __be32 *prop;
+       const __be32 *prop;
 
        if (depth != 0)
                return 0;
@@ -803,9 +645,9 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname,
        return 1;
 }
 
-u64 __init dt_mem_next_cell(int s, __be32 **cellp)
+u64 __init dt_mem_next_cell(int s, const __be32 **cellp)
 {
-       __be32 *p = *cellp;
+       const __be32 *p = *cellp;
 
        *cellp = p + s;
        return of_read_number(p, s);
@@ -817,9 +659,9 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp)
 int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
                                     int depth, void *data)
 {
-       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
-       __be32 *reg, *endp;
-       unsigned long l;
+       const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+       const __be32 *reg, *endp;
+       int l;
 
        /* We are scanning "memory" nodes only */
        if (type == NULL) {
@@ -840,7 +682,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
 
        endp = reg + (l / sizeof(__be32));
 
-       pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
+       pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,\n",
            uname, l, reg[0], reg[1], reg[2], reg[3]);
 
        while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
@@ -863,8 +705,8 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
 int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
                                     int depth, void *data)
 {
-       unsigned long l;
-       char *p;
+       int l;
+       const char *p;
 
        pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);