Merge branch 'devicetree/next-overlay' into devicetree/next
[cascardo/linux.git] / drivers / of / selftest.c
index 04e39a1..d410026 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_fdt.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/list.h>
@@ -23,6 +24,10 @@ static struct selftest_results {
        int failed;
 } selftest_results;
 
+#define NO_OF_NODES 2
+static struct device_node *nodes[NO_OF_NODES];
+static int last_node_index;
+
 #define selftest(result, fmt, ...) { \
        if (!(result)) { \
                selftest_results.failed++; \
@@ -594,9 +599,156 @@ static void __init of_selftest_platform_populate(void)
        }
 }
 
+/**
+ *     update_node_properties - adds the properties
+ *     of np into dup node (present in live tree) and
+ *     updates parent of children of np to dup.
+ *
+ *     @np:    node already present in live tree
+ *     @dup:   node present in live tree to be updated
+ */
+static void update_node_properties(struct device_node *np,
+                                       struct device_node *dup)
+{
+       struct property *prop;
+       struct device_node *child;
+
+       for_each_property_of_node(np, prop)
+               of_add_property(dup, prop);
+
+       for_each_child_of_node(np, child)
+               child->parent = dup;
+}
+
+/**
+ *     attach_node_and_children - attaches nodes
+ *     and its children to live tree
+ *
+ *     @np:    Node to attach to live tree
+ */
+static int attach_node_and_children(struct device_node *np)
+{
+       struct device_node *next, *root = np, *dup;
+
+       if (!np) {
+               pr_warn("%s: No tree to attach; not running tests\n",
+                       __func__);
+               return -ENODATA;
+       }
+
+
+       /* skip root node */
+       np = np->child;
+       /* storing a copy in temporary node */
+       dup = np;
+
+       while (dup) {
+               nodes[last_node_index++] = dup;
+               dup = dup->sibling;
+       }
+       dup = NULL;
+
+       while (np) {
+               next = np->allnext;
+               dup = of_find_node_by_path(np->full_name);
+               if (dup)
+                       update_node_properties(np, dup);
+               else {
+                       np->child = NULL;
+                       if (np->parent == root)
+                               np->parent = of_allnodes;
+                       of_attach_node(np);
+               }
+               np = next;
+       }
+
+       return 0;
+}
+
+/**
+ *     selftest_data_add - Reads, copies data from
+ *     linked tree and attaches it to the live tree
+ */
+static int __init selftest_data_add(void)
+{
+       void *selftest_data;
+       struct device_node *selftest_data_node;
+       extern uint8_t __dtb_testcases_begin[];
+       extern uint8_t __dtb_testcases_end[];
+       const int size = __dtb_testcases_end - __dtb_testcases_begin;
+
+       if (!size || !of_allnodes) {
+               pr_warn("%s: No testcase data to attach; not running tests\n",
+                       __func__);
+               return -ENODATA;
+       }
+
+       /* creating copy */
+       selftest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL);
+
+       if (!selftest_data) {
+               pr_warn("%s: Failed to allocate memory for selftest_data; "
+                       "not running tests\n", __func__);
+               return -ENOMEM;
+       }
+       of_fdt_unflatten_tree(selftest_data, &selftest_data_node);
+
+       /* attach the sub-tree to live tree */
+       return attach_node_and_children(selftest_data_node);
+}
+
+/**
+ *     detach_node_and_children - detaches node
+ *     and its children from live tree
+ *
+ *     @np:    Node to detach from live tree
+ */
+static void detach_node_and_children(struct device_node *np)
+{
+       while (np->child)
+               detach_node_and_children(np->child);
+
+       while (np->sibling)
+               detach_node_and_children(np->sibling);
+
+       of_detach_node(np);
+}
+
+/**
+ *     selftest_data_remove - removes the selftest data
+ *     nodes from the live tree
+ */
+static void selftest_data_remove(void)
+{
+       struct device_node *np;
+       struct property *prop;
+
+       while (last_node_index >= 0) {
+               if (nodes[last_node_index]) {
+                       np = of_find_node_by_path(nodes[last_node_index]->full_name);
+                       if (strcmp(np->full_name, "/aliases") != 0) {
+                               detach_node_and_children(np->child);
+                               of_detach_node(np);
+                       } else {
+                               for_each_property_of_node(np, prop) {
+                                       if (strcmp(prop->name, "testcase-alias") == 0)
+                                               of_remove_property(np, prop);
+                               }
+                       }
+               }
+               last_node_index--;
+       }
+}
+
 static int __init of_selftest(void)
 {
        struct device_node *np;
+       int res;
+
+       /* adding data for selftest */
+       res = selftest_data_add();
+       if (res)
+               return res;
 
        np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
        if (!np) {
@@ -618,6 +770,10 @@ static int __init of_selftest(void)
        of_selftest_platform_populate();
        pr_info("end of selftest - %i passed, %i failed\n",
                selftest_results.passed, selftest_results.failed);
+
+       /* removing selftest data from live tree */
+       selftest_data_remove();
+
        return 0;
 }
 late_initcall(of_selftest);