All of lore.kernel.org
 help / color / mirror / Atom feed
From: Nathan Fontenot <nfont@austin.ibm.com>
To: devicetree-discuss@lists.ozlabs.org, linuxppc-dev@ozlabs.org,
	microblaze-uclinux@itee.uq.edu.au
Subject: [RFC PATCH 2/5] Merge dynamic OF code to of_dynamic.c
Date: Wed, 04 Nov 2009 16:16:38 -0600	[thread overview]
Message-ID: <4AF1FD46.9050101@austin.ibm.com> (raw)
In-Reply-To: <4AF1FB29.50905@austin.ibm.com>

Creation of the OF dynamic device tree update code in drivers/of.  This
merges the common device tree updating routines to add/remove nodes and
properties from powerpc and microblaze.  All of the new code is conditional
based on a new OF_DYNAMIC config option.

There are two updates to the code.  First, the routines to update properties
are re-named from prom_* to of_*.  This seems correct as the routines no longer
reside in prom.c files.  Second, the addition of a notifier chain for when
nodes are added removed from the device tree.  This is a feature that currently
exists in powerpc.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---

Index: linux-next/drivers/of/of_dynamic.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-next/drivers/of/of_dynamic.c	2009-11-04 14:45:11.000000000 -0600
@@ -0,0 +1,387 @@
+/*
+ * Definitions for talking to the Open Firmware PROM on
+ * Power Macintosh and other computers.
+ *
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/of.h>
+
+BLOCKING_NOTIFIER_HEAD(of_update_chain);
+
+/**
+ *	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)
+		kref_get(&node->kref);
+	return node;
+}
+EXPORT_SYMBOL(of_node_get);
+
+static inline struct device_node *kref_to_device_node(struct kref *kref)
+{
+	return container_of(kref, struct device_node, kref);
+}
+
+/**
+ *	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 kref *kref)
+{
+	struct device_node *node = kref_to_device_node(kref);
+	struct property *prop = node->properties;
+
+	/* We should never be releasing nodes that haven't been detached. */
+	if (!of_node_check_flag(node, OF_DETACHED)) {
+		printk(KERN_WARNING "Bad of_node_put() on %s\n",
+		       node->full_name);
+		dump_stack();
+		kref_init(&node->kref);
+		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)
+		kref_put(&node->kref, of_node_release);
+}
+EXPORT_SYMBOL(of_node_put);
+
+static struct device_node *of_derive_parent(char *path)
+{
+	struct device_node *parent = NULL;
+	char *parent_path = "/";
+	size_t parent_path_len = strrchr(path, '/') - path + 1;
+
+	/* reject if path is "/" */
+	if (!strcmp(path, "/"))
+		return ERR_PTR(-EINVAL);
+
+	if (strrchr(path, '/') != path) {
+		parent_path = kmalloc(parent_path_len, GFP_KERNEL);
+		if (!parent_path)
+			return ERR_PTR(-ENOMEM);
+		strlcpy(parent_path, path, parent_path_len);
+	}
+
+	parent = of_find_node_by_path(parent_path);
+	if (!parent)
+		return ERR_PTR(-EINVAL);
+
+	if (strcmp(parent_path, "/"))
+		kfree(parent_path);
+	return parent;
+}
+
+static int of_attach_one_node(struct device_node *np)
+{
+	struct proc_dir_entry *ent;
+	unsigned long flags;
+	int rc;
+
+	of_node_set_flag(np, OF_DYNAMIC);
+	kref_init(&np->kref);
+
+	np->parent = of_derive_parent(np->full_name);
+	if (IS_ERR(np->parent))
+		return PTR_ERR(np->parent);
+
+	rc = of_update_notifier_call(OF_ATTACH_NODE, np);
+	if (rc == NOTIFY_BAD) {
+		printk(KERN_ERR "Failed to add device node %s\n",
+		       np->full_name);
+		return -ENOMEM;  /* For now, safe to assume kmalloc failure */
+	}
+
+	write_lock_irqsave(&devtree_lock, flags);
+	np->sibling = np->parent->child;
+	np->allnext = allnodes;
+	np->parent->child = np;
+
+	allnodes = np;
+	write_unlock_irqrestore(&devtree_lock, flags);
+
+#ifdef CONFIG_PROC_DEVICETREE
+	ent = proc_mkdir(strrchr(np->full_name, '/') + 1, np->parent->pde);
+	if (ent)
+		proc_device_tree_add_node(np, ent);
+#endif
+
+	of_node_put(np->parent);
+	return 0;
+}
+
+int of_attach_node(struct device_node *np)
+{
+	struct device_node *child = np->child;
+	struct device_node *sibling = np->sibling;
+	int rc;
+
+	np->child = NULL;
+	np->sibling = NULL;
+	np->parent = NULL;
+
+	rc = of_attach_one_node(np);
+	if (rc)
+		return rc;
+
+	if (child) {
+		rc = of_attach_node(child);
+		if (rc)
+			return rc;
+	}
+
+	if (sibling)
+		rc = of_attach_node(sibling);
+
+	return rc;
+}
+EXPORT_SYMBOL(of_attach_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.
+ */
+static int of_detach_one_node(struct device_node *np)
+{
+	struct device_node *parent = np->parent;
+	struct property *prop = np->properties;
+	unsigned long flags;
+
+	if (!parent)
+		return -1;
+
+#ifdef CONFIG_PROC_DEVICETREE
+	while (prop) {
+		remove_proc_entry(prop->name, np->pde);
+		prop = prop->next;
+	}
+
+	if (np->pde)
+		remove_proc_entry(np->pde->name, parent->pde);
+#endif
+
+	of_update_notifier_call(OF_DETACH_NODE, np);
+
+	write_lock_irqsave(&devtree_lock, flags);
+
+	if (allnodes == np)
+		allnodes = np->allnext;
+	else {
+		struct device_node *prev;
+		for (prev = 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);
+	write_unlock_irqrestore(&devtree_lock, flags);
+	of_node_put(np);
+	return 0;
+}
+
+static int _of_detach_node(struct device_node *np)
+{
+	int rc;
+
+	if (np->child) {
+		rc = _of_detach_node(np->child);
+		if (rc)
+			return rc;
+	}
+
+	if (np->sibling) {
+		rc = _of_detach_node(np->sibling);
+		if (rc)
+			return rc;
+	}
+
+	rc = of_detach_one_node(np);
+	return rc;
+}
+
+int of_detach_node(struct device_node *np)
+{
+	int rc;
+
+	if (np->child) {
+		rc = _of_detach_node(np->child);
+		if (rc)
+			return rc;
+	}
+
+	rc = of_detach_one_node(np);
+	return rc;
+}
+EXPORT_SYMBOL(of_detach_node);
+
+/*
+ * Add a property to a node
+ */
+int of_property_attach(struct device_node *np, struct property* prop)
+{
+	struct property **next;
+	unsigned long flags;
+
+	prop->next = NULL;
+
+	write_lock_irqsave(&devtree_lock, flags);
+	next = &np->properties;
+	while (*next) {
+		if (strcmp(prop->name, (*next)->name) == 0) {
+			/* duplicate ! don't insert it */
+			write_unlock_irqrestore(&devtree_lock, flags);
+			return -1;
+		}
+		next = &(*next)->next;
+	}
+	*next = prop;
+	write_unlock_irqrestore(&devtree_lock, flags);
+
+#ifdef CONFIG_PROC_DEVICETREE
+	/* try to add to proc as well if it was initialized */
+	if (np->pde)
+		proc_device_tree_add_prop(np->pde, prop);
+#endif /* CONFIG_PROC_DEVICETREE */
+
+	return 0;
+}
+EXPORT_SYMBOL(of_property_attach);
+
+/*
+ * Remove a property from a node.  Note that we don't actually
+ * remove it, since we have given out who-knows-how-many pointers
+ * to the data using get-property.  Instead we just move the property
+ * to the "dead properties" list, so it won't be found any more.
+ */
+int of_property_detach(struct device_node *np, struct property *prop)
+{
+	struct property **next;
+	unsigned long flags;
+	int found = 0;
+
+	write_lock_irqsave(&devtree_lock, flags);
+	next = &np->properties;
+	while (*next) {
+		if (*next == prop) {
+			/* found the node */
+			*next = prop->next;
+			prop->next = np->deadprops;
+			np->deadprops = prop;
+			found = 1;
+			break;
+		}
+		next = &(*next)->next;
+	}
+	write_unlock_irqrestore(&devtree_lock, flags);
+
+	if (!found)
+		return -ENODEV;
+
+#ifdef CONFIG_PROC_DEVICETREE
+	/* try to remove the proc node as well */
+	if (np->pde)
+		proc_device_tree_remove_prop(np->pde, prop);
+#endif /* CONFIG_PROC_DEVICETREE */
+
+	return 0;
+}
+EXPORT_SYMBOL(of_property_detach);
+
+/*
+ * Update a property in a node.  Note that we don't actually
+ * remove it, since we have given out who-knows-how-many pointers
+ * to the data using get-property.  Instead we just move the property
+ * to the "dead properties" list, and add the new property to the
+ * property list
+ */
+int of_property_update(struct device_node *np, struct property *newprop,
+		       struct property *oldprop)
+{
+	struct property **next;
+	unsigned long flags;
+	int found = 0;
+
+	write_lock_irqsave(&devtree_lock, flags);
+	next = &np->properties;
+	while (*next) {
+		if (*next == oldprop) {
+			/* found the node */
+			newprop->next = oldprop->next;
+			*next = newprop;
+			oldprop->next = np->deadprops;
+			np->deadprops = oldprop;
+			found = 1;
+			break;
+		}
+		next = &(*next)->next;
+	}
+	write_unlock_irqrestore(&devtree_lock, flags);
+
+	if (!found)
+		return -ENODEV;
+
+#ifdef CONFIG_PROC_DEVICETREE
+	/* try to add to proc as well if it was initialized */
+	if (np->pde)
+		proc_device_tree_update_prop(np->pde, newprop, oldprop);
+#endif /* CONFIG_PROC_DEVICETREE */
+
+	return 0;
+}
+EXPORT_SYMBOL(of_property_update);
Index: linux-next/include/linux/of.h
===================================================================
--- linux-next.orig/include/linux/of.h	2009-11-03 11:18:08.000000000 -0600
+++ linux-next/include/linux/of.h	2009-11-03 13:42:38.000000000 -0600
@@ -20,6 +20,8 @@
 #include <linux/kref.h>
 #include <linux/mod_devicetable.h>
 #include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/notifier.h>
 
 typedef u32 phandle;
 typedef u32 ihandle;
@@ -191,4 +193,34 @@
 	const char *list_name, const char *cells_name, int index,
 	struct device_node **out_node, const void **out_args);
 
+#ifdef CONFIG_OF_DYNAMIC
+extern int of_attach_node(struct device_node *np);
+extern int of_detach_node(struct device_node *np);
+extern int of_property_attach(struct device_node *np, struct property *prop);
+extern int of_property_detach(struct device_node *np, struct property *prop);
+extern int of_property_update(struct device_node *np, struct property *newprop,
+			      struct property *oldprop);
+
+/* Dynamic Update Notifier Chain */
+extern struct blocking_notifier_head of_update_chain;
+
+#define OF_ATTACH_NODE		1
+#define OF_DETACH_NODE		2
+
+static inline int of_update_notifier_register(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&of_update_chain, nb);
+}
+
+static inline int of_update_notifier_unregister(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&of_update_chain, nb);
+}
+
+static inline int of_update_notifier_call(unsigned int value, void *data)
+{
+	return blocking_notifier_call_chain(&of_update_chain, value, data);
+}
+#endif /* CONFIG_OF_DYNAMIC */
+
 #endif /* _LINUX_OF_H */
Index: linux-next/drivers/of/Makefile
===================================================================
--- linux-next.orig/drivers/of/Makefile	2009-11-03 11:18:08.000000000 -0600
+++ linux-next/drivers/of/Makefile	2009-11-03 13:42:35.000000000 -0600
@@ -1,6 +1,7 @@
 obj-y = base.o
-obj-$(CONFIG_OF_DEVICE) += device.o platform.o
-obj-$(CONFIG_OF_GPIO)   += gpio.o
-obj-$(CONFIG_OF_I2C)	+= of_i2c.o
-obj-$(CONFIG_OF_SPI)	+= of_spi.o
-obj-$(CONFIG_OF_MDIO)	+= of_mdio.o
+obj-$(CONFIG_OF_DEVICE)		+= device.o platform.o
+obj-$(CONFIG_OF_GPIO)		+= gpio.o
+obj-$(CONFIG_OF_I2C)		+= of_i2c.o
+obj-$(CONFIG_OF_SPI)		+= of_spi.o
+obj-$(CONFIG_OF_MDIO)		+= of_mdio.o
+obj-$(CONFIG_OF_DYNAMIC)	+= of_dynamic.o

  parent reply	other threads:[~2009-11-04 22:16 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-11-04 22:07 [RFC PATCH 0/5] Merge common dynamic OF device tree code Nathan Fontenot
2009-11-04 22:14 ` [RFC PATCH 1/5] Move devtree_lock and allnodes declaration to of.h Nathan Fontenot
2009-11-04 22:14   ` Nathan Fontenot
2009-11-04 22:16 ` Nathan Fontenot [this message]
2009-11-05  7:55   ` [RFC PATCH 2/5] Merge dynamic OF code to of_dynamic.c Grant Likely
2009-11-05  7:55     ` Grant Likely
2009-11-05 16:59     ` Nathan Fontenot
2009-11-04 22:18 ` [RFC PATCH 3/5] powerpc and pseries updates for new OF dynamic code Nathan Fontenot
2009-11-04 22:19 ` [RFC PATCH 4/5] Microblaze updates for " Nathan Fontenot
2009-11-04 22:19   ` Nathan Fontenot
2009-11-04 22:20 ` [RFC PATCH 5/5] powerpc/iseries updates for new " Nathan Fontenot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4AF1FD46.9050101@austin.ibm.com \
    --to=nfont@austin.ibm.com \
    --cc=devicetree-discuss@lists.ozlabs.org \
    --cc=linuxppc-dev@ozlabs.org \
    --cc=microblaze-uclinux@itee.uq.edu.au \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.