of: Move CONFIG_OF_DYNAMIC code into a separate file
authorGrant Likely <grant.likely@linaro.org>
Thu, 26 Jun 2014 14:40:48 +0000 (15:40 +0100)
committerGrant Likely <grant.likely@linaro.org>
Mon, 7 Jul 2014 12:59:45 +0000 (13:59 +0100)
Split the dynamic device tree code into a separate file to make it
really clear what features CONFIF_OF_DYNAMIC add to the kernel. Without
CONFIG_OF_DYNAMIC only properties can be changed, and notifiers do not
get sent. Enabling it turns on reference counting, notifiers and the
ability to add and remove nodes.

v2: Moved of_node_release() into dynamic.c

Signed-off-by: Grant Likely <grant.likely@linaro.org>
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
Cc: Rob Herring <robh+dt@kernel.org>
drivers/of/Makefile
drivers/of/base.c
drivers/of/dynamic.c [new file with mode: 0644]
drivers/of/of_private.h

index 099b1fb..08e6c0f 100644 (file)
@@ -1,4 +1,5 @@
 obj-y = base.o device.o platform.o
+obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
 obj-$(CONFIG_OF_FLATTREE) += fdt.o
 obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
 obj-$(CONFIG_OF_PROMTREE) += pdt.o
index e48a1b9..0d89556 100644 (file)
@@ -88,79 +88,7 @@ int __weak of_node_to_nid(struct device_node *np)
 }
 #endif
 
-#if defined(CONFIG_OF_DYNAMIC)
-/**
- *     of_node_get - Increment refcount of a node
- *     @node:  Node to inc refcount, NULL is supported to
- *             simplify writing of callers
- *
- *     Returns node.
- */
-struct device_node *of_node_get(struct device_node *node)
-{
-       if (node)
-               kobject_get(&node->kobj);
-       return node;
-}
-EXPORT_SYMBOL(of_node_get);
-
-static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
-{
-       return container_of(kobj, struct device_node, kobj);
-}
-
-/**
- *     of_node_release - release a dynamically allocated node
- *     @kref:  kref element of the node to be released
- *
- *     In of_node_put() this function is passed to kref_put()
- *     as the destructor.
- */
-static void of_node_release(struct kobject *kobj)
-{
-       struct device_node *node = kobj_to_device_node(kobj);
-       struct property *prop = node->properties;
-
-       /* We should never be releasing nodes that haven't been detached. */
-       if (!of_node_check_flag(node, OF_DETACHED)) {
-               pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
-               dump_stack();
-               return;
-       }
-
-       if (!of_node_check_flag(node, OF_DYNAMIC))
-               return;
-
-       while (prop) {
-               struct property *next = prop->next;
-               kfree(prop->name);
-               kfree(prop->value);
-               kfree(prop);
-               prop = next;
-
-               if (!prop) {
-                       prop = node->deadprops;
-                       node->deadprops = NULL;
-               }
-       }
-       kfree(node->full_name);
-       kfree(node->data);
-       kfree(node);
-}
-
-/**
- *     of_node_put - Decrement refcount of a node
- *     @node:  Node to dec refcount, NULL is supported to
- *             simplify writing of callers
- *
- */
-void of_node_put(struct device_node *node)
-{
-       if (node)
-               kobject_put(&node->kobj);
-}
-EXPORT_SYMBOL(of_node_put);
-#else
+#ifndef CONFIG_OF_DYNAMIC
 static void of_node_release(struct kobject *kobj)
 {
        /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
@@ -264,25 +192,6 @@ int of_node_add(struct device_node *np)
        return rc;
 }
 
-#if defined(CONFIG_OF_DYNAMIC)
-static void of_node_remove(struct device_node *np)
-{
-       struct property *pp;
-
-       BUG_ON(!of_node_is_initialized(np));
-
-       /* only remove properties if on sysfs */
-       if (of_node_is_attached(np)) {
-               for_each_property_of_node(np, pp)
-                       sysfs_remove_bin_file(&np->kobj, &pp->attr);
-               kobject_del(&np->kobj);
-       }
-
-       /* finally remove the kobj_init ref */
-       of_node_put(np);
-}
-#endif
-
 static int __init of_init(void)
 {
        struct device_node *np;
@@ -1747,28 +1656,6 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_na
 }
 EXPORT_SYMBOL(of_count_phandle_with_args);
 
-#if defined(CONFIG_OF_DYNAMIC)
-static int of_property_notify(int action, struct device_node *np,
-                             struct property *prop)
-{
-       struct of_prop_reconfig pr;
-
-       /* only call notifiers if the node is attached */
-       if (!of_node_is_attached(np))
-               return 0;
-
-       pr.dn = np;
-       pr.prop = prop;
-       return of_reconfig_notify(action, &pr);
-}
-#else
-static int of_property_notify(int action, struct device_node *np,
-                             struct property *prop)
-{
-       return 0;
-}
-#endif
-
 /**
  * __of_add_property - Add a property to a node without lock operations
  */
@@ -1915,121 +1802,6 @@ int of_update_property(struct device_node *np, struct property *newprop)
        return 0;
 }
 
-#if defined(CONFIG_OF_DYNAMIC)
-/*
- * Support for dynamic device trees.
- *
- * On some platforms, the device tree can be manipulated at runtime.
- * The routines in this section support adding, removing and changing
- * device tree nodes.
- */
-
-static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
-
-int of_reconfig_notifier_register(struct notifier_block *nb)
-{
-       return blocking_notifier_chain_register(&of_reconfig_chain, nb);
-}
-EXPORT_SYMBOL_GPL(of_reconfig_notifier_register);
-
-int of_reconfig_notifier_unregister(struct notifier_block *nb)
-{
-       return blocking_notifier_chain_unregister(&of_reconfig_chain, nb);
-}
-EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
-
-int of_reconfig_notify(unsigned long action, void *p)
-{
-       int rc;
-
-       rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
-       return notifier_to_errno(rc);
-}
-
-/**
- * of_attach_node - Plug a device node into the tree and global list.
- */
-int of_attach_node(struct device_node *np)
-{
-       unsigned long flags;
-       int rc;
-
-       rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
-       if (rc)
-               return rc;
-
-       raw_spin_lock_irqsave(&devtree_lock, flags);
-       np->sibling = np->parent->child;
-       np->allnext = np->parent->allnext;
-       np->parent->allnext = np;
-       np->parent->child = np;
-       of_node_clear_flag(np, OF_DETACHED);
-       raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
-       of_node_add(np);
-       return 0;
-}
-
-/**
- * of_detach_node - "Unplug" a node from the device tree.
- *
- * The caller must hold a reference to the node.  The memory associated with
- * the node is not freed until its refcount goes to zero.
- */
-int of_detach_node(struct device_node *np)
-{
-       struct device_node *parent;
-       unsigned long flags;
-       int rc = 0;
-
-       rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
-       if (rc)
-               return rc;
-
-       raw_spin_lock_irqsave(&devtree_lock, flags);
-
-       if (of_node_check_flag(np, OF_DETACHED)) {
-               /* someone already detached it */
-               raw_spin_unlock_irqrestore(&devtree_lock, flags);
-               return rc;
-       }
-
-       parent = np->parent;
-       if (!parent) {
-               raw_spin_unlock_irqrestore(&devtree_lock, flags);
-               return rc;
-       }
-
-       if (of_allnodes == np)
-               of_allnodes = np->allnext;
-       else {
-               struct device_node *prev;
-               for (prev = of_allnodes;
-                    prev->allnext != np;
-                    prev = prev->allnext)
-                       ;
-               prev->allnext = np->allnext;
-       }
-
-       if (parent->child == np)
-               parent->child = np->sibling;
-       else {
-               struct device_node *prevsib;
-               for (prevsib = np->parent->child;
-                    prevsib->sibling != np;
-                    prevsib = prevsib->sibling)
-                       ;
-               prevsib->sibling = np->sibling;
-       }
-
-       of_node_set_flag(np, OF_DETACHED);
-       raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
-       of_node_remove(np);
-       return rc;
-}
-#endif /* defined(CONFIG_OF_DYNAMIC) */
-
 static void of_alias_add(struct alias_prop *ap, struct device_node *np,
                         int id, const char *stem, int stem_len)
 {
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
new file mode 100644 (file)
index 0000000..1259943
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Support for dynamic device trees.
+ *
+ * On some platforms, the device tree can be manipulated at runtime.
+ * The routines in this section support adding, removing and changing
+ * device tree nodes.
+ */
+
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+
+#include "of_private.h"
+
+/**
+ * of_node_get() - Increment refcount of a node
+ * @node:      Node to inc refcount, NULL is supported to simplify writing of
+ *             callers
+ *
+ * Returns node.
+ */
+struct device_node *of_node_get(struct device_node *node)
+{
+       if (node)
+               kobject_get(&node->kobj);
+       return node;
+}
+EXPORT_SYMBOL(of_node_get);
+
+/**
+ * of_node_put() - Decrement refcount of a node
+ * @node:      Node to dec refcount, NULL is supported to simplify writing of
+ *             callers
+ */
+void of_node_put(struct device_node *node)
+{
+       if (node)
+               kobject_put(&node->kobj);
+}
+EXPORT_SYMBOL(of_node_put);
+
+static void of_node_remove(struct device_node *np)
+{
+       struct property *pp;
+
+       BUG_ON(!of_node_is_initialized(np));
+
+       /* only remove properties if on sysfs */
+       if (of_node_is_attached(np)) {
+               for_each_property_of_node(np, pp)
+                       sysfs_remove_bin_file(&np->kobj, &pp->attr);
+               kobject_del(&np->kobj);
+       }
+
+       /* finally remove the kobj_init ref */
+       of_node_put(np);
+}
+
+static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
+
+int of_reconfig_notifier_register(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&of_reconfig_chain, nb);
+}
+EXPORT_SYMBOL_GPL(of_reconfig_notifier_register);
+
+int of_reconfig_notifier_unregister(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&of_reconfig_chain, nb);
+}
+EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
+
+int of_reconfig_notify(unsigned long action, void *p)
+{
+       int rc;
+
+       rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
+       return notifier_to_errno(rc);
+}
+
+int of_property_notify(int action, struct device_node *np,
+                      struct property *prop)
+{
+       struct of_prop_reconfig pr;
+
+       /* only call notifiers if the node is attached */
+       if (!of_node_is_attached(np))
+               return 0;
+
+       pr.dn = np;
+       pr.prop = prop;
+       return of_reconfig_notify(action, &pr);
+}
+
+/**
+ * of_attach_node() - Plug a device node into the tree and global list.
+ */
+int of_attach_node(struct device_node *np)
+{
+       unsigned long flags;
+       int rc;
+
+       rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
+       if (rc)
+               return rc;
+
+       raw_spin_lock_irqsave(&devtree_lock, flags);
+       np->sibling = np->parent->child;
+       np->allnext = np->parent->allnext;
+       np->parent->allnext = np;
+       np->parent->child = np;
+       of_node_clear_flag(np, OF_DETACHED);
+       raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+       of_node_add(np);
+       return 0;
+}
+
+/**
+ * of_detach_node() - "Unplug" a node from the device tree.
+ *
+ * The caller must hold a reference to the node.  The memory associated with
+ * the node is not freed until its refcount goes to zero.
+ */
+int of_detach_node(struct device_node *np)
+{
+       struct device_node *parent;
+       unsigned long flags;
+       int rc = 0;
+
+       rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
+       if (rc)
+               return rc;
+
+       raw_spin_lock_irqsave(&devtree_lock, flags);
+
+       if (of_node_check_flag(np, OF_DETACHED)) {
+               /* someone already detached it */
+               raw_spin_unlock_irqrestore(&devtree_lock, flags);
+               return rc;
+       }
+
+       parent = np->parent;
+       if (!parent) {
+               raw_spin_unlock_irqrestore(&devtree_lock, flags);
+               return rc;
+       }
+
+       if (of_allnodes == np)
+               of_allnodes = np->allnext;
+       else {
+               struct device_node *prev;
+               for (prev = of_allnodes;
+                    prev->allnext != np;
+                    prev = prev->allnext)
+                       ;
+               prev->allnext = np->allnext;
+       }
+
+       if (parent->child == np)
+               parent->child = np->sibling;
+       else {
+               struct device_node *prevsib;
+               for (prevsib = np->parent->child;
+                    prevsib->sibling != np;
+                    prevsib = prevsib->sibling)
+                       ;
+               prevsib->sibling = np->sibling;
+       }
+
+       of_node_set_flag(np, OF_DETACHED);
+       raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+       of_node_remove(np);
+       return rc;
+}
+
+/**
+ * of_node_release() - release a dynamically allocated node
+ * @kref: kref element of the node to be released
+ *
+ * In of_node_put() this function is passed to kref_put() as the destructor.
+ */
+void of_node_release(struct kobject *kobj)
+{
+       struct device_node *node = kobj_to_device_node(kobj);
+       struct property *prop = node->properties;
+
+       /* We should never be releasing nodes that haven't been detached. */
+       if (!of_node_check_flag(node, OF_DETACHED)) {
+               pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
+               dump_stack();
+               return;
+       }
+
+       if (!of_node_check_flag(node, OF_DYNAMIC))
+               return;
+
+       while (prop) {
+               struct property *next = prop->next;
+               kfree(prop->name);
+               kfree(prop->value);
+               kfree(prop);
+               prop = next;
+
+               if (!prop) {
+                       prop = node->deadprops;
+                       node->deadprops = NULL;
+               }
+       }
+       kfree(node->full_name);
+       kfree(node->data);
+       kfree(node);
+}
index fcc70e7..c270f20 100644 (file)
@@ -33,4 +33,22 @@ struct alias_prop {
 
 extern struct mutex of_mutex;
 extern struct list_head aliases_lookup;
+
+static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
+{
+       return container_of(kobj, struct device_node, kobj);
+}
+
+#if defined(CONFIG_OF_DYNAMIC)
+extern int of_property_notify(int action, struct device_node *np,
+                             struct property *prop);
+extern void of_node_release(struct kobject *kobj);
+#else /* CONFIG_OF_DYNAMIC */
+static inline int of_property_notify(int action, struct device_node *np,
+                                    struct property *prop)
+{
+       return 0;
+}
+#endif /* CONFIG_OF_DYNAMIC */
+
 #endif /* _LINUX_OF_PRIVATE_H */