Merge tag 'pwm/for-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
[cascardo/linux.git] / drivers / of / fdt.c
index d134710..5100742 100644 (file)
@@ -9,6 +9,7 @@
  * version 2 as published by the Free Software Foundation.
  */
 
+#include <linux/crc32.h>
 #include <linux/kernel.h>
 #include <linux/initrd.h>
 #include <linux/memblock.h>
@@ -22,6 +23,7 @@
 #include <linux/libfdt.h>
 #include <linux/debugfs.h>
 #include <linux/serial_core.h>
+#include <linux/sysfs.h>
 
 #include <asm/setup.h>  /* for COMMAND_LINE_SIZE */
 #include <asm/page.h>
@@ -145,15 +147,15 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size,
  * @mem: Memory chunk to use for allocating device nodes and properties
  * @p: pointer to node in flat tree
  * @dad: Parent struct device_node
- * @allnextpp: pointer to ->allnext from last allocated device_node
  * @fpsize: Size of the node path up at the current depth.
  */
 static void * unflatten_dt_node(void *blob,
                                void *mem,
                                int *poffset,
                                struct device_node *dad,
-                               struct device_node ***allnextpp,
-                               unsigned long fpsize)
+                               struct device_node **nodepp,
+                               unsigned long fpsize,
+                               bool dryrun)
 {
        const __be32 *p;
        struct device_node *np;
@@ -200,7 +202,7 @@ static void * unflatten_dt_node(void *blob,
 
        np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
                                __alignof__(struct device_node));
-       if (allnextpp) {
+       if (!dryrun) {
                char *fn;
                of_node_init(np);
                np->full_name = fn = ((char *)np) + sizeof(*np);
@@ -222,16 +224,10 @@ static void * unflatten_dt_node(void *blob,
                memcpy(fn, pathp, l);
 
                prev_pp = &np->properties;
-               **allnextpp = np;
-               *allnextpp = &np->allnext;
                if (dad != NULL) {
                        np->parent = dad;
-                       /* we temporarily use the next field as `last_child'*/
-                       if (dad->next == NULL)
-                               dad->child = np;
-                       else
-                               dad->next->sibling = np;
-                       dad->next = np;
+                       np->sibling = dad->child;
+                       dad->child = np;
                }
        }
        /* process properties */
@@ -254,7 +250,7 @@ static void * unflatten_dt_node(void *blob,
                        has_name = 1;
                pp = unflatten_dt_alloc(&mem, sizeof(struct property),
                                        __alignof__(struct property));
-               if (allnextpp) {
+               if (!dryrun) {
                        /* We accept flattened tree phandles either in
                         * ePAPR-style "phandle" properties, or the
                         * legacy "linux,phandle" properties.  If both
@@ -296,7 +292,7 @@ static void * unflatten_dt_node(void *blob,
                sz = (pa - ps) + 1;
                pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
                                        __alignof__(struct property));
-               if (allnextpp) {
+               if (!dryrun) {
                        pp->name = "name";
                        pp->length = sz;
                        pp->value = pp + 1;
@@ -308,7 +304,7 @@ static void * unflatten_dt_node(void *blob,
                                (char *)pp->value);
                }
        }
-       if (allnextpp) {
+       if (!dryrun) {
                *prev_pp = NULL;
                np->name = of_get_property(np, "name", NULL);
                np->type = of_get_property(np, "device_type", NULL);
@@ -324,12 +320,30 @@ static void * unflatten_dt_node(void *blob,
        if (depth < 0)
                depth = 0;
        while (*poffset > 0 && depth > old_depth)
-               mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
-                                       fpsize);
+               mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
+                                       fpsize, dryrun);
 
        if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
                pr_err("unflatten: error %d processing FDT\n", *poffset);
 
+       /*
+        * Reverse the child list. Some drivers assumes node order matches .dts
+        * node order
+        */
+       if (!dryrun && np->child) {
+               struct device_node *child = np->child;
+               np->child = NULL;
+               while (child) {
+                       struct device_node *next = child->sibling;
+                       child->sibling = np->child;
+                       np->child = child;
+                       child = next;
+               }
+       }
+
+       if (nodepp)
+               *nodepp = np;
+
        return mem;
 }
 
@@ -352,7 +366,6 @@ static void __unflatten_device_tree(void *blob,
        unsigned long size;
        int start;
        void *mem;
-       struct device_node **allnextp = mynodes;
 
        pr_debug(" -> unflatten_device_tree()\n");
 
@@ -373,7 +386,7 @@ static void __unflatten_device_tree(void *blob,
 
        /* First pass, scan for size */
        start = 0;
-       size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0);
+       size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
        size = ALIGN(size, 4);
 
        pr_debug("  size is %lx, allocating...\n", size);
@@ -388,11 +401,10 @@ static void __unflatten_device_tree(void *blob,
 
        /* Second pass, do actual unflattening */
        start = 0;
-       unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
+       unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
        if (be32_to_cpup(mem + size) != 0xdeadbeef)
                pr_warning("End of tree marker overwritten: %08x\n",
                           be32_to_cpup(mem + size));
-       *allnextp = NULL;
 
        pr_debug(" <- unflatten_device_tree()\n");
 }
@@ -425,6 +437,8 @@ void *initial_boot_params;
 
 #ifdef CONFIG_OF_EARLY_FLATTREE
 
+static u32 of_fdt_crc32;
+
 /**
  * res_mem_reserve_reg() - reserve all memory described in 'reg' property
  */
@@ -930,6 +944,11 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
        const u64 phys_offset = __pa(PAGE_OFFSET);
 
        if (!PAGE_ALIGNED(base)) {
+               if (size < PAGE_SIZE - (base & ~PAGE_MASK)) {
+                       pr_warn("Ignoring memory block 0x%llx - 0x%llx\n",
+                               base, base + size);
+                       return;
+               }
                size -= PAGE_SIZE - (base & ~PAGE_MASK);
                base = PAGE_ALIGN(base);
        }
@@ -992,15 +1011,14 @@ bool __init early_init_dt_verify(void *params)
        if (!params)
                return false;
 
-       /* Setup flat device-tree pointer */
-       initial_boot_params = params;
-
        /* check device tree validity */
-       if (fdt_check_header(params)) {
-               initial_boot_params = NULL;
+       if (fdt_check_header(params))
                return false;
-       }
 
+       /* Setup flat device-tree pointer */
+       initial_boot_params = params;
+       of_fdt_crc32 = crc32_be(~0, initial_boot_params,
+                               fdt_totalsize(initial_boot_params));
        return true;
 }
 
@@ -1039,7 +1057,7 @@ bool __init early_init_dt_scan(void *params)
  */
 void __init unflatten_device_tree(void)
 {
-       __unflatten_device_tree(initial_boot_params, &of_allnodes,
+       __unflatten_device_tree(initial_boot_params, &of_root,
                                early_init_dt_alloc_memory_arch);
 
        /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
@@ -1078,27 +1096,32 @@ void __init unflatten_and_copy_device_tree(void)
        unflatten_device_tree();
 }
 
-#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
-static struct debugfs_blob_wrapper flat_dt_blob;
-
-static int __init of_flat_dt_debugfs_export_fdt(void)
+#ifdef CONFIG_SYSFS
+static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *bin_attr,
+                              char *buf, loff_t off, size_t count)
 {
-       struct dentry *d = debugfs_create_dir("device-tree", NULL);
-
-       if (!d)
-               return -ENOENT;
+       memcpy(buf, initial_boot_params + off, count);
+       return count;
+}
 
-       flat_dt_blob.data = initial_boot_params;
-       flat_dt_blob.size = fdt_totalsize(initial_boot_params);
+static int __init of_fdt_raw_init(void)
+{
+       static struct bin_attribute of_fdt_raw_attr =
+               __BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, 0);
 
-       d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
-                               d, &flat_dt_blob);
-       if (!d)
-               return -ENOENT;
+       if (!initial_boot_params)
+               return 0;
 
-       return 0;
+       if (of_fdt_crc32 != crc32_be(~0, initial_boot_params,
+                                    fdt_totalsize(initial_boot_params))) {
+               pr_warn("fdt: not creating '/sys/firmware/fdt': CRC check failed\n");
+               return 0;
+       }
+       of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params);
+       return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr);
 }
-module_init(of_flat_dt_debugfs_export_fdt);
+late_initcall(of_fdt_raw_init);
 #endif
 
 #endif /* CONFIG_OF_EARLY_FLATTREE */