* [PATCH v5 1/9] OF: Introduce Device Tree resolve support.
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-05-29 15:04 ` Grant Likely
2014-05-28 12:46 ` [PATCH v5 2/9] OF: Introduce DT overlay support Pantelis Antoniou
` (8 subsequent siblings)
9 siblings, 1 reply; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
Introduce support for dynamic device tree resolution.
Using it, it is possible to prepare a device tree that's
been loaded on runtime to be modified and inserted at the kernel
live tree.
Export of of_resolve and bug fix of double free by
Guenter Roeck <groeck@juniper.net>
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
.../devicetree/dynamic-resolution-notes.txt | 25 ++
drivers/of/Kconfig | 6 +
drivers/of/Makefile | 1 +
drivers/of/resolver.c | 378 +++++++++++++++++++++
include/linux/of.h | 17 +
5 files changed, 427 insertions(+)
create mode 100644 Documentation/devicetree/dynamic-resolution-notes.txt
create mode 100644 drivers/of/resolver.c
diff --git a/Documentation/devicetree/dynamic-resolution-notes.txt b/Documentation/devicetree/dynamic-resolution-notes.txt
new file mode 100644
index 0000000..0b396c4
--- /dev/null
+++ b/Documentation/devicetree/dynamic-resolution-notes.txt
@@ -0,0 +1,25 @@
+Device Tree Dynamic Resolver Notes
+----------------------------------
+
+This document describes the implementation of the in-kernel
+Device Tree resolver, residing in drivers/of/resolver.c and is a
+companion document to Documentation/devicetree/dt-object-internal.txt[1]
+
+How the resolver works
+----------------------
+
+The resolver is given as an input an arbitrary tree compiled with the
+proper dtc option and having a /plugin/ tag. This generates the
+appropriate __fixups__ & __local_fixups__ nodes as described in [1].
+
+In sequence the resolver works by the following steps:
+
+1. Get the maximum device tree phandle value from the live tree + 1.
+2. Adjust all the local phandles of the tree to resolve by that amount.
+3. Using the __local__fixups__ node information adjust all local references
+ by the same amount.
+4. For each property in the __fixups__ node locate the node it references
+ in the live tree. This is the label used to tag the node.
+5. Retrieve the phandle of the target of the fixup.
+5. For each fixup in the property locate the node:property:offset location
+ and replace it with the phandle value.
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 2dcb054..0236a4e 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -78,4 +78,10 @@ config OF_RESERVED_MEM
help
Helpers to allow for reservation of memory regions
+config OF_RESOLVE
+ bool
+ depends on OF && !SPARC
+ select OF_DYNAMIC
+ select OF_DEVICE
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index c62fc2a..27e3f29 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
+obj-$(CONFIG_OF_RESOLVE) += resolver.o
CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
new file mode 100644
index 0000000..cbf4180
--- /dev/null
+++ b/drivers/of/resolver.c
@@ -0,0 +1,378 @@
+/*
+ * Functions for dealing with DT resolution
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+/*
+ * Find live tree's maximum phandle value.
+ */
+static phandle of_get_tree_max_phandle(void)
+{
+ struct device_node *node;
+ phandle phandle;
+ unsigned long flags;
+
+ /* now search recursively */
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ phandle = 0;
+ for_each_of_allnodes(node) {
+ if (node->phandle != OF_PHANDLE_ILLEGAL &&
+ node->phandle > phandle)
+ phandle = node->phandle;
+ }
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+ return phandle;
+}
+
+/*
+ * Adjust a subtree's phandle values by a given delta.
+ * Makes sure not to just adjust the device node's phandle value,
+ * but modify the phandle properties values as well.
+ */
+static void __of_adjust_tree_phandles(struct device_node *node,
+ int phandle_delta)
+{
+ struct device_node *child;
+ struct property *prop;
+ phandle phandle;
+
+ /* first adjust the node's phandle direct value */
+ if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
+ node->phandle += phandle_delta;
+
+ /* now adjust phandle & linux,phandle values */
+ for_each_property_of_node(node, prop) {
+
+ /* only look for these two */
+ if (of_prop_cmp(prop->name, "phandle") != 0 &&
+ of_prop_cmp(prop->name, "linux,phandle") != 0)
+ continue;
+
+ /* must be big enough */
+ if (prop->length < 4)
+ continue;
+
+ /* read phandle value */
+ phandle = be32_to_cpup(prop->value);
+ if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */
+ continue;
+
+ /* adjust */
+ *(uint32_t *)prop->value = cpu_to_be32(node->phandle);
+ }
+
+ /* now do the children recursively */
+ __for_each_child_of_node(node, child)
+ __of_adjust_tree_phandles(child, phandle_delta);
+}
+
+/*
+ * Adjust the local phandle references by the given phandle delta.
+ * Assumes the existances of a __local_fixups__ node at the root
+ * of the tree. Does not take any devtree locks so make sure you
+ * call this on a tree which is at the detached state.
+ */
+static int __of_adjust_tree_phandle_references(struct device_node *node,
+ int phandle_delta)
+{
+ phandle phandle;
+ struct device_node *refnode, *child;
+ struct property *rprop, *sprop;
+ char *propval, *propcur, *propend, *nodestr, *propstr, *s;
+ int offset, propcurlen;
+ int err;
+
+ /* locate the symbols & fixups nodes on resolve */
+ __for_each_child_of_node(node, child)
+ if (of_node_cmp(child->name, "__local_fixups__") == 0)
+ break;
+
+ /* no local fixups */
+ if (!child)
+ return 0;
+
+ /* find the local fixups property */
+ for_each_property_of_node(child, rprop) {
+
+ /* skip properties added automatically */
+ if (of_prop_cmp(rprop->name, "name") == 0)
+ continue;
+
+ /* make a copy */
+ propval = kmalloc(rprop->length, GFP_KERNEL);
+ if (!propval) {
+ pr_err("%s: Could not copy value of '%s'\n",
+ __func__, rprop->name);
+ return -ENOMEM;
+ }
+ memcpy(propval, rprop->value, rprop->length);
+
+ propend = propval + rprop->length;
+ for (propcur = propval; propcur < propend;
+ propcur += propcurlen + 1) {
+
+ propcurlen = strlen(propcur);
+
+ nodestr = propcur;
+ s = strchr(propcur, ':');
+ if (!s) {
+ pr_err("%s: Illegal symbol entry '%s' (1)\n",
+ __func__, propcur);
+ err = -EINVAL;
+ goto err_fail;
+ }
+ *s++ = '\0';
+
+ propstr = s;
+ s = strchr(s, ':');
+ if (!s) {
+ pr_err("%s: Illegal symbol entry '%s' (2)\n",
+ __func__, (char *)rprop->value);
+ err = -EINVAL;
+ goto err_fail;
+ }
+
+ *s++ = '\0';
+ err = kstrtoint(s, 10, &offset);
+ if (err != 0) {
+ pr_err("%s: Could get offset '%s'\n",
+ __func__, (char *)rprop->value);
+ goto err_fail;
+ }
+
+ /* look into the resolve node for the full path */
+ refnode = __of_find_node_by_full_name(node, nodestr);
+ if (!refnode) {
+ pr_warn("%s: Could not find refnode '%s'\n",
+ __func__, (char *)rprop->value);
+ continue;
+ }
+
+ /* now find the property */
+ for_each_property_of_node(refnode, sprop) {
+ if (of_prop_cmp(sprop->name, propstr) == 0)
+ break;
+ }
+
+ if (!sprop) {
+ pr_err("%s: Could not find property '%s'\n",
+ __func__, (char *)rprop->value);
+ err = -ENOENT;
+ goto err_fail;
+ }
+
+ phandle = be32_to_cpup(sprop->value + offset);
+ *(__be32 *)(sprop->value + offset) =
+ cpu_to_be32(phandle + phandle_delta);
+ }
+
+ kfree(propval);
+ propval = NULL;
+ }
+
+ return 0;
+
+err_fail:
+ kfree(propval);
+ return err;
+}
+
+/**
+ * of_resolve - Resolve the given node against the live tree.
+ *
+ * @resolve: Node to resolve
+ *
+ * Perform dynamic Device Tree resolution against the live tree
+ * to the given node to resolve. This depends on the live tree
+ * having a __symbols__ node, and the resolve node the __fixups__ &
+ * __local_fixups__ nodes (if needed).
+ * The result of the operation is a resolve node that it's contents
+ * are fit to be inserted or operate upon the live tree.
+ * Returns 0 on success or a negative error value on error.
+ */
+int of_resolve(struct device_node *resolve)
+{
+ struct device_node *child, *refnode;
+ struct device_node *root_sym, *resolve_sym, *resolve_fix;
+ struct property *rprop, *sprop;
+ const char *refpath;
+ char *propval, *propcur, *propend, *nodestr, *propstr, *s;
+ int offset, propcurlen;
+ phandle phandle, phandle_delta;
+ int err;
+
+ /* the resolve node must exist, and be detached */
+ if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
+ return -EINVAL;
+
+ /* first we need to adjust the phandles */
+ phandle_delta = of_get_tree_max_phandle() + 1;
+ __of_adjust_tree_phandles(resolve, phandle_delta);
+ err = __of_adjust_tree_phandle_references(resolve, phandle_delta);
+ if (err != 0)
+ return err;
+
+ root_sym = NULL;
+ resolve_sym = NULL;
+ resolve_fix = NULL;
+
+ /* this may fail (if no fixups are required) */
+ root_sym = of_find_node_by_path("/__symbols__");
+
+ /* locate the symbols & fixups nodes on resolve */
+ __for_each_child_of_node(resolve, child) {
+
+ if (!resolve_sym &&
+ of_node_cmp(child->name, "__symbols__") == 0)
+ resolve_sym = child;
+
+ if (!resolve_fix &&
+ of_node_cmp(child->name, "__fixups__") == 0)
+ resolve_fix = child;
+
+ /* both found, don't bother anymore */
+ if (resolve_sym && resolve_fix)
+ break;
+ }
+
+ /* we do allow for the case where no fixups are needed */
+ if (!resolve_fix) {
+ err = 0; /* no error */
+ goto out;
+ }
+
+ /* we need to fixup, but no root symbols... */
+ if (!root_sym) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ for_each_property_of_node(resolve_fix, rprop) {
+
+ /* skip properties added automatically */
+ if (of_prop_cmp(rprop->name, "name") == 0)
+ continue;
+
+ err = of_property_read_string(root_sym,
+ rprop->name, &refpath);
+ if (err != 0) {
+ pr_err("%s: Could not find symbol '%s'\n",
+ __func__, rprop->name);
+ goto out;
+ }
+
+ refnode = of_find_node_by_path(refpath);
+ if (!refnode) {
+ pr_err("%s: Could not find node by path '%s'\n",
+ __func__, refpath);
+ err = -ENOENT;
+ goto out;
+ }
+
+ phandle = refnode->phandle;
+ of_node_put(refnode);
+
+ pr_debug("%s: %s phandle is 0x%08x\n",
+ __func__, rprop->name, phandle);
+
+ /* make a copy */
+ propval = kmalloc(rprop->length, GFP_KERNEL);
+ if (!propval) {
+ pr_err("%s: Could not copy value of '%s'\n",
+ __func__, rprop->name);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(propval, rprop->value, rprop->length);
+
+ propend = propval + rprop->length;
+ for (propcur = propval; propcur < propend;
+ propcur += propcurlen + 1) {
+ propcurlen = strlen(propcur);
+
+ nodestr = propcur;
+ s = strchr(propcur, ':');
+ if (!s) {
+ pr_err("%s: Illegal symbol entry '%s' (1)\n",
+ __func__, (char *)rprop->value);
+ err = -EINVAL;
+ goto err_fail_free;
+ }
+ *s++ = '\0';
+
+ propstr = s;
+ s = strchr(s, ':');
+ if (!s) {
+ pr_err("%s: Illegal symbol entry '%s' (2)\n",
+ __func__, (char *)rprop->value);
+ err = -EINVAL;
+ goto err_fail_free;
+ }
+
+ *s++ = '\0';
+ err = kstrtoint(s, 10, &offset);
+ if (err != 0) {
+ pr_err("%s: Could get offset '%s'\n",
+ __func__, (char *)rprop->value);
+ goto err_fail_free;
+ }
+
+ /* look into the resolve node for the full path */
+ refnode = __of_find_node_by_full_name(resolve,
+ nodestr);
+ if (!refnode) {
+ pr_err("%s: Could not find refnode '%s'\n",
+ __func__, (char *)rprop->value);
+ err = -ENOENT;
+ goto err_fail_free;
+ }
+
+ /* now find the property */
+ for_each_property_of_node(refnode, sprop) {
+ if (of_prop_cmp(sprop->name, propstr) == 0)
+ break;
+ }
+
+ if (!sprop) {
+ pr_err("%s: Could not find property '%s'\n",
+ __func__, (char *)rprop->value);
+ err = -ENOENT;
+ goto err_fail_free;
+ }
+
+ *(__be32 *)(sprop->value + offset) =
+ cpu_to_be32(phandle);
+ }
+
+ kfree(propval);
+ propval = NULL;
+ }
+
+err_fail_free:
+ kfree(propval);
+
+out:
+ /* NULL is handled by of_node_put as NOP */
+ of_node_put(root_sym);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(of_resolve);
diff --git a/include/linux/of.h b/include/linux/of.h
index 29113d3..9d5795c 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -823,4 +823,21 @@ static inline struct device_node *__of_create_empty_node( const char *name,
#endif /* !CONFIG_OF */
+
+/* illegal phandle value (set when unresolved) */
+#define OF_PHANDLE_ILLEGAL 0xdeadbeef
+
+#ifdef CONFIG_OF_RESOLVE
+
+int of_resolve(struct device_node *resolve);
+
+#else
+
+static inline int of_resolve(struct device_node *resolve)
+{
+ return -ENOTSUPP;
+}
+
+#endif
+
#endif /* _LINUX_OF_H */
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v5 1/9] OF: Introduce Device Tree resolve support.
2014-05-28 12:46 ` [PATCH v5 1/9] OF: Introduce Device Tree resolve support Pantelis Antoniou
@ 2014-05-29 15:04 ` Grant Likely
0 siblings, 0 replies; 13+ messages in thread
From: Grant Likely @ 2014-05-29 15:04 UTC (permalink / raw)
To: Pantelis Antoniou
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
On Wed, 28 May 2014 15:46:25 +0300, Pantelis Antoniou <pantelis.antoniou@konsulko.com> wrote:
> Introduce support for dynamic device tree resolution.
> Using it, it is possible to prepare a device tree that's
> been loaded on runtime to be modified and inserted at the kernel
> live tree.
>
> Export of of_resolve and bug fix of double free by
> Guenter Roeck <groeck@juniper.net>
>
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> ---
> .../devicetree/dynamic-resolution-notes.txt | 25 ++
> drivers/of/Kconfig | 6 +
> drivers/of/Makefile | 1 +
> drivers/of/resolver.c | 378 +++++++++++++++++++++
> include/linux/of.h | 17 +
> 5 files changed, 427 insertions(+)
> create mode 100644 Documentation/devicetree/dynamic-resolution-notes.txt
> create mode 100644 drivers/of/resolver.c
>
> diff --git a/Documentation/devicetree/dynamic-resolution-notes.txt b/Documentation/devicetree/dynamic-resolution-notes.txt
> new file mode 100644
> index 0000000..0b396c4
> --- /dev/null
> +++ b/Documentation/devicetree/dynamic-resolution-notes.txt
> @@ -0,0 +1,25 @@
> +Device Tree Dynamic Resolver Notes
> +----------------------------------
> +
> +This document describes the implementation of the in-kernel
> +Device Tree resolver, residing in drivers/of/resolver.c and is a
> +companion document to Documentation/devicetree/dt-object-internal.txt[1]
> +
> +How the resolver works
> +----------------------
> +
> +The resolver is given as an input an arbitrary tree compiled with the
> +proper dtc option and having a /plugin/ tag. This generates the
> +appropriate __fixups__ & __local_fixups__ nodes as described in [1].
> +
> +In sequence the resolver works by the following steps:
> +
> +1. Get the maximum device tree phandle value from the live tree + 1.
> +2. Adjust all the local phandles of the tree to resolve by that amount.
> +3. Using the __local__fixups__ node information adjust all local references
> + by the same amount.
> +4. For each property in the __fixups__ node locate the node it references
> + in the live tree. This is the label used to tag the node.
> +5. Retrieve the phandle of the target of the fixup.
> +5. For each fixup in the property locate the node:property:offset location
> + and replace it with the phandle value.
> diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
> index 2dcb054..0236a4e 100644
> --- a/drivers/of/Kconfig
> +++ b/drivers/of/Kconfig
> @@ -78,4 +78,10 @@ config OF_RESERVED_MEM
> help
> Helpers to allow for reservation of memory regions
>
> +config OF_RESOLVE
> + bool
> + depends on OF && !SPARC
> + select OF_DYNAMIC
> + select OF_DEVICE
> +
> endmenu # OF
> diff --git a/drivers/of/Makefile b/drivers/of/Makefile
> index c62fc2a..27e3f29 100644
> --- a/drivers/of/Makefile
> +++ b/drivers/of/Makefile
> @@ -10,5 +10,6 @@ obj-$(CONFIG_OF_PCI) += of_pci.o
> obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
> obj-$(CONFIG_OF_MTD) += of_mtd.o
> obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
> +obj-$(CONFIG_OF_RESOLVE) += resolver.o
>
> CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
> diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
> new file mode 100644
> index 0000000..cbf4180
> --- /dev/null
> +++ b/drivers/of/resolver.c
> @@ -0,0 +1,378 @@
> +/*
> + * Functions for dealing with DT resolution
> + *
> + * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
> + * Copyright (C) 2012 Texas Instruments Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/string.h>
> +#include <linux/ctype.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +
> +/*
> + * Find live tree's maximum phandle value.
> + */
> +static phandle of_get_tree_max_phandle(void)
> +{
> + struct device_node *node;
> + phandle phandle;
> + unsigned long flags;
> +
> + /* now search recursively */
> + raw_spin_lock_irqsave(&devtree_lock, flags);
> + phandle = 0;
> + for_each_of_allnodes(node) {
> + if (node->phandle != OF_PHANDLE_ILLEGAL &&
> + node->phandle > phandle)
> + phandle = node->phandle;
> + }
> + raw_spin_unlock_irqrestore(&devtree_lock, flags);
> +
> + return phandle;
> +}
It will be important to hold the tree modification mutex while
resolving an overlay. If two overlays are in flight it could cause
collisions.
> +
> +/*
> + * Adjust a subtree's phandle values by a given delta.
> + * Makes sure not to just adjust the device node's phandle value,
> + * but modify the phandle properties values as well.
> + */
> +static void __of_adjust_tree_phandles(struct device_node *node,
> + int phandle_delta)
> +{
> + struct device_node *child;
> + struct property *prop;
> + phandle phandle;
> +
> + /* first adjust the node's phandle direct value */
> + if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
> + node->phandle += phandle_delta;
> +
> + /* now adjust phandle & linux,phandle values */
> + for_each_property_of_node(node, prop) {
> +
> + /* only look for these two */
> + if (of_prop_cmp(prop->name, "phandle") != 0 &&
> + of_prop_cmp(prop->name, "linux,phandle") != 0)
> + continue;
> +
> + /* must be big enough */
> + if (prop->length < 4)
> + continue;
> +
> + /* read phandle value */
> + phandle = be32_to_cpup(prop->value);
> + if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */
> + continue;
> +
> + /* adjust */
> + *(uint32_t *)prop->value = cpu_to_be32(node->phandle);
> + }
> +
> + /* now do the children recursively */
> + __for_each_child_of_node(node, child)
> + __of_adjust_tree_phandles(child, phandle_delta);
> +}
Instead of recursion, use an allnodes loop:
for (; node; node = node->allnext) {
...
}
> +
> +/*
> + * Adjust the local phandle references by the given phandle delta.
> + * Assumes the existances of a __local_fixups__ node at the root
> + * of the tree. Does not take any devtree locks so make sure you
> + * call this on a tree which is at the detached state.
> + */
> +static int __of_adjust_tree_phandle_references(struct device_node *node,
> + int phandle_delta)
> +{
> + phandle phandle;
> + struct device_node *refnode, *child;
> + struct property *rprop, *sprop;
> + char *propval, *propcur, *propend, *nodestr, *propstr, *s;
> + int offset, propcurlen;
> + int err;
> +
> + /* locate the symbols & fixups nodes on resolve */
> + __for_each_child_of_node(node, child)
> + if (of_node_cmp(child->name, "__local_fixups__") == 0)
> + break;
> +
> + /* no local fixups */
> + if (!child)
> + return 0;
> +
> + /* find the local fixups property */
> + for_each_property_of_node(child, rprop) {
> +
> + /* skip properties added automatically */
> + if (of_prop_cmp(rprop->name, "name") == 0)
> + continue;
> +
> + /* make a copy */
> + propval = kmalloc(rprop->length, GFP_KERNEL);
> + if (!propval) {
> + pr_err("%s: Could not copy value of '%s'\n",
> + __func__, rprop->name);
> + return -ENOMEM;
> + }
> + memcpy(propval, rprop->value, rprop->length);
kmemdup(). I think the parsing can be made more elegant to not require
the copy, but that can be a future enhancement. There isn't anything
incorrect here.
We could just modify the property value in-place since this property is
never used again after the tree has been resolved.
> + propend = propval + rprop->length;
> + for (propcur = propval; propcur < propend;
> + propcur += propcurlen + 1) {
> +
> + propcurlen = strlen(propcur);
Need a check here to make sure the current string doesn't overrun
propend. The for loop only checks if the start of the string overruns,
not the end.
> +
> + nodestr = propcur;
> + s = strchr(propcur, ':');
> + if (!s) {
> + pr_err("%s: Illegal symbol entry '%s' (1)\n",
> + __func__, propcur);
> + err = -EINVAL;
> + goto err_fail;
> + }
> + *s++ = '\0';
> +
> + propstr = s;
> + s = strchr(s, ':');
> + if (!s) {
> + pr_err("%s: Illegal symbol entry '%s' (2)\n",
> + __func__, (char *)rprop->value);
> + err = -EINVAL;
> + goto err_fail;
> + }
> +
> + *s++ = '\0';
> + err = kstrtoint(s, 10, &offset);
> + if (err != 0) {
> + pr_err("%s: Could get offset '%s'\n",
> + __func__, (char *)rprop->value);
> + goto err_fail;
> + }
> +
> + /* look into the resolve node for the full path */
> + refnode = __of_find_node_by_full_name(node, nodestr);
> + if (!refnode) {
> + pr_warn("%s: Could not find refnode '%s'\n",
> + __func__, (char *)rprop->value);
> + continue;
> + }
> +
> + /* now find the property */
> + for_each_property_of_node(refnode, sprop) {
> + if (of_prop_cmp(sprop->name, propstr) == 0)
> + break;
> + }
> +
> + if (!sprop) {
> + pr_err("%s: Could not find property '%s'\n",
> + __func__, (char *)rprop->value);
> + err = -ENOENT;
> + goto err_fail;
> + }
> +
> + phandle = be32_to_cpup(sprop->value + offset);
> + *(__be32 *)(sprop->value + offset) =
> + cpu_to_be32(phandle + phandle_delta);
> + }
> +
> + kfree(propval);
> + propval = NULL;
> + }
> +
> + return 0;
> +
> +err_fail:
> + kfree(propval);
> + return err;
> +}
> +
> +/**
> + * of_resolve - Resolve the given node against the live tree.
> + *
> + * @resolve: Node to resolve
> + *
> + * Perform dynamic Device Tree resolution against the live tree
> + * to the given node to resolve. This depends on the live tree
> + * having a __symbols__ node, and the resolve node the __fixups__ &
> + * __local_fixups__ nodes (if needed).
Is the above still true? I thought the __symbols__ node was obsolete
> + * The result of the operation is a resolve node that it's contents
> + * are fit to be inserted or operate upon the live tree.
> + * Returns 0 on success or a negative error value on error.
Include comment that need to hold the tree modification mutex when
calling this code.
Overall the code looks good. I might pick this one up for merging. If I
do then you can send fixup patches to resolve the comments above.
g.
> + */
> +int of_resolve(struct device_node *resolve)
> +{
> + struct device_node *child, *refnode;
> + struct device_node *root_sym, *resolve_sym, *resolve_fix;
> + struct property *rprop, *sprop;
> + const char *refpath;
> + char *propval, *propcur, *propend, *nodestr, *propstr, *s;
> + int offset, propcurlen;
> + phandle phandle, phandle_delta;
> + int err;
> +
> + /* the resolve node must exist, and be detached */
> + if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
> + return -EINVAL;
> +
> + /* first we need to adjust the phandles */
> + phandle_delta = of_get_tree_max_phandle() + 1;
> + __of_adjust_tree_phandles(resolve, phandle_delta);
> + err = __of_adjust_tree_phandle_references(resolve, phandle_delta);
> + if (err != 0)
> + return err;
> +
> + root_sym = NULL;
> + resolve_sym = NULL;
> + resolve_fix = NULL;
> +
> + /* this may fail (if no fixups are required) */
> + root_sym = of_find_node_by_path("/__symbols__");
> +
> + /* locate the symbols & fixups nodes on resolve */
> + __for_each_child_of_node(resolve, child) {
> +
> + if (!resolve_sym &&
> + of_node_cmp(child->name, "__symbols__") == 0)
> + resolve_sym = child;
> +
> + if (!resolve_fix &&
> + of_node_cmp(child->name, "__fixups__") == 0)
> + resolve_fix = child;
> +
> + /* both found, don't bother anymore */
> + if (resolve_sym && resolve_fix)
> + break;
> + }
> +
> + /* we do allow for the case where no fixups are needed */
> + if (!resolve_fix) {
> + err = 0; /* no error */
> + goto out;
> + }
> +
> + /* we need to fixup, but no root symbols... */
> + if (!root_sym) {
> + err = -EINVAL;
> + goto out;
> + }
> +
> + for_each_property_of_node(resolve_fix, rprop) {
> +
> + /* skip properties added automatically */
> + if (of_prop_cmp(rprop->name, "name") == 0)
> + continue;
> +
> + err = of_property_read_string(root_sym,
> + rprop->name, &refpath);
> + if (err != 0) {
> + pr_err("%s: Could not find symbol '%s'\n",
> + __func__, rprop->name);
> + goto out;
> + }
> +
> + refnode = of_find_node_by_path(refpath);
> + if (!refnode) {
> + pr_err("%s: Could not find node by path '%s'\n",
> + __func__, refpath);
> + err = -ENOENT;
> + goto out;
> + }
> +
> + phandle = refnode->phandle;
> + of_node_put(refnode);
> +
> + pr_debug("%s: %s phandle is 0x%08x\n",
> + __func__, rprop->name, phandle);
> +
> + /* make a copy */
> + propval = kmalloc(rprop->length, GFP_KERNEL);
> + if (!propval) {
> + pr_err("%s: Could not copy value of '%s'\n",
> + __func__, rprop->name);
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + memcpy(propval, rprop->value, rprop->length);
> +
> + propend = propval + rprop->length;
> + for (propcur = propval; propcur < propend;
> + propcur += propcurlen + 1) {
> + propcurlen = strlen(propcur);
> +
> + nodestr = propcur;
> + s = strchr(propcur, ':');
> + if (!s) {
> + pr_err("%s: Illegal symbol entry '%s' (1)\n",
> + __func__, (char *)rprop->value);
> + err = -EINVAL;
> + goto err_fail_free;
> + }
> + *s++ = '\0';
> +
> + propstr = s;
> + s = strchr(s, ':');
> + if (!s) {
> + pr_err("%s: Illegal symbol entry '%s' (2)\n",
> + __func__, (char *)rprop->value);
> + err = -EINVAL;
> + goto err_fail_free;
> + }
> +
> + *s++ = '\0';
> + err = kstrtoint(s, 10, &offset);
> + if (err != 0) {
> + pr_err("%s: Could get offset '%s'\n",
> + __func__, (char *)rprop->value);
> + goto err_fail_free;
> + }
> +
> + /* look into the resolve node for the full path */
> + refnode = __of_find_node_by_full_name(resolve,
> + nodestr);
> + if (!refnode) {
> + pr_err("%s: Could not find refnode '%s'\n",
> + __func__, (char *)rprop->value);
> + err = -ENOENT;
> + goto err_fail_free;
> + }
> +
> + /* now find the property */
> + for_each_property_of_node(refnode, sprop) {
> + if (of_prop_cmp(sprop->name, propstr) == 0)
> + break;
> + }
> +
> + if (!sprop) {
> + pr_err("%s: Could not find property '%s'\n",
> + __func__, (char *)rprop->value);
> + err = -ENOENT;
> + goto err_fail_free;
> + }
> +
> + *(__be32 *)(sprop->value + offset) =
> + cpu_to_be32(phandle);
> + }
> +
> + kfree(propval);
> + propval = NULL;
> + }
> +
> +err_fail_free:
> + kfree(propval);
> +
> +out:
> + /* NULL is handled by of_node_put as NOP */
> + of_node_put(root_sym);
> +
> + return err;
> +}
> +EXPORT_SYMBOL_GPL(of_resolve);
> diff --git a/include/linux/of.h b/include/linux/of.h
> index 29113d3..9d5795c 100644
> --- a/include/linux/of.h
> +++ b/include/linux/of.h
> @@ -823,4 +823,21 @@ static inline struct device_node *__of_create_empty_node( const char *name,
>
> #endif /* !CONFIG_OF */
>
> +
> +/* illegal phandle value (set when unresolved) */
> +#define OF_PHANDLE_ILLEGAL 0xdeadbeef
> +
> +#ifdef CONFIG_OF_RESOLVE
> +
> +int of_resolve(struct device_node *resolve);
> +
> +#else
> +
> +static inline int of_resolve(struct device_node *resolve)
> +{
> + return -ENOTSUPP;
> +}
> +
> +#endif
> +
> #endif /* _LINUX_OF_H */
> --
> 1.7.12
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 2/9] OF: Introduce DT overlay support.
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 1/9] OF: Introduce Device Tree resolve support Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 3/9] OF: DT-Overlay configfs interface Pantelis Antoniou
` (7 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
Introduce DT overlay support.
Using this functionality it is possible to dynamically overlay a part of
the kernel's tree with another tree that's been dynamically loaded.
It is also possible to remove node and properties.
The creation/destruction of the devices is handled by using the standard
of notification framework with two new notifier actions:
OF_RECONFIG_DYNAMIC_CREATE_DEV, when a new bus device should be created
and OF_RECONFIG_DYNAMIC_DESTROY_DEV, when a bus device must be
Documentation about internal and APIs is provided in
Documentation/devicetree/overlay-notes.txt
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
Documentation/devicetree/overlay-notes.txt | 160 +++++
drivers/of/Kconfig | 7 +
drivers/of/Makefile | 1 +
drivers/of/overlay.c | 1001 ++++++++++++++++++++++++++++
include/linux/of.h | 150 +++++
5 files changed, 1319 insertions(+)
create mode 100644 Documentation/devicetree/overlay-notes.txt
create mode 100644 drivers/of/overlay.c
diff --git a/Documentation/devicetree/overlay-notes.txt b/Documentation/devicetree/overlay-notes.txt
new file mode 100644
index 0000000..74e59a8
--- /dev/null
+++ b/Documentation/devicetree/overlay-notes.txt
@@ -0,0 +1,160 @@
+Device Tree Overlay Notes
+-------------------------
+
+This document describes the implementation of the in-kernel
+device tree overlay functionality residing in drivers/of/overlay.c and is a
+companion document to Documentation/devicetree/dt-object-internal.txt[1] &
+Documentation/devicetree/dynamic-resolution-notes.txt[2]
+
+How overlays work
+-----------------
+
+A Device Tree's overlay purpose is to modify the kernel's live tree, and
+have the modification affecting the state of the the kernel in a way that
+is reflecting the changes.
+Since the kernel mainly deals with devices, any new device node that result
+in an active device should have it created while if the device node is either
+disabled or removed all together, the affected device should be deregistered.
+
+Lets take an example where we have a foo board with the following base tree
+which is taken from [1].
+
+---- foo.dts -----------------------------------------------------------------
+ /* FOO platform */
+ / {
+ compatible = "corp,foo";
+
+ /* shared resources */
+ res: res {
+ };
+
+ /* On chip peripherals */
+ ocp: ocp {
+ /* peripherals that are always instantiated */
+ peripheral1 { ... };
+ }
+ };
+---- foo.dts -----------------------------------------------------------------
+
+The overlay bar.dts, when loaded (and resolved as described in [2]) should
+
+---- bar.dts -----------------------------------------------------------------
+/plugin/; /* allow undefined label references and record them */
+/ {
+ .... /* various properties for loader use; i.e. part id etc. */
+ fragment@0 {
+ target = <&ocp>;
+ __overlay__ {
+ /* bar peripheral */
+ bar {
+ compatible = "corp,bar";
+ ... /* various properties and child nodes */
+ }
+ };
+ };
+};
+---- bar.dts -----------------------------------------------------------------
+
+result in foo+bar.dts
+
+---- foo+bar.dts -------------------------------------------------------------
+ /* FOO platform + bar peripheral */
+ / {
+ compatible = "corp,foo";
+
+ /* shared resources */
+ res: res {
+ };
+
+ /* On chip peripherals */
+ ocp: ocp {
+ /* peripherals that are always instantiated */
+ peripheral1 { ... };
+
+ /* bar peripheral */
+ bar {
+ compatible = "corp,bar";
+ ... /* various properties and child nodes */
+ }
+ }
+ };
+---- foo+bar.dts -------------------------------------------------------------
+
+As a result of the the overlay, a new device node (bar) has been created
+so a bar platform device will be registered and if a matching device driver
+is loaded the device will be created as expected.
+
+Overlay in-kernel low-level API
+-------------------------------
+
+The steps typically required to get an overlay to work are as follows:
+
+1. Use of_build_overlay_info() to create an array of initialized and
+ready to use of_overlay_info structures.
+2. Call of_overlay() to apply the overlays declared in the array.
+3. If the overlay needs to be removed, call of_overlay_revert().
+4. Finally release the memory taken by the overlay info array by
+of_free_overlay_info().
+
+This low-level API is for use by components that intend to do their own
+overlay tracking, and is considered more low-level.
+
+Overlay in-kernel high-level API
+--------------------------------
+
+The low level API works but it's cumbersome to use sometimes. On top of
+that it allows removal of overlays without taking into account 'stacking'
+which can lead in the in-kernel live tree to be in an invalid state.
+
+The high-level API is considerably more easy to use:
+
+1. Call of_overlay_create() to create and apply an overlay. The return value
+is a cookie identifying this overlay.
+2. Call of_overlay_destroy() to remove and cleanup the overlay previously
+created via the call to of_overlay_create(). Removal of an overlay that
+is stacked by another will not be permitted.
+
+Finally, if you need to remove all overlays in one-go, just call
+of_overlay_destroy_all() which will remove every single one in the correct
+order.
+
+Overlay DTS Format
+------------------
+
+The DTS of an overlay should have the following format:
+
+{
+ /* ignored properties by the overlay */
+
+ fragment@0 { /* first child node */
+
+ target=<phandle>; /* phandle target of the overlay */
+ or
+ target-path="/path"; /* target path of the overlay */
+
+ __overlay__ {
+ property-a; /* add property-a to the target */
+ -property-b; /* remove property-b from target */
+ node-a { /* add to an existing, or create a node-a */
+ ...
+ };
+ -node-b { /* remove an existing node-b */
+ ...
+ };
+ };
+ }
+ fragment@1 { /* second child node */
+ ...
+ };
+ /* more fragments follow */
+}
+
+It should be noted that the DT overlay format described is the one expected
+by the of_build_overlay_info() function, which is a helper function. There
+is nothing stopping someone coming up with his own DTS format and that will
+end up filling in the fields of the of_overlay_info array.
+
+Using the non-phandle based target method allows one to use a base DT which does
+not contain a __symbols__ node, i.e. it was not compiled with the -@ option.
+The __symbols__ node is only required for the target=<phandle> method, since it
+contains the information required to map from a phandle to a tree location.
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 0236a4e..5ebde98 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -84,4 +84,11 @@ config OF_RESOLVE
select OF_DYNAMIC
select OF_DEVICE
+config OF_OVERLAY
+ bool
+ depends on OF
+ select OF_DYNAMIC
+ select OF_DEVICE
+ select OF_RESOLVE
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 27e3f29..e1f864a 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -11,5 +11,6 @@ obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
obj-$(CONFIG_OF_RESOLVE) += resolver.o
+obj-$(CONFIG_OF_OVERLAY) += overlay.o
CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
new file mode 100644
index 0000000..dbb69ac
--- /dev/null
+++ b/drivers/of/overlay.c
@@ -0,0 +1,1001 @@
+/*
+ * Functions for working with device tree overlays
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+#undef DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+/* forward decl. */
+static int of_overlay_tree_change(struct of_overlay_info *ovinfo,
+ unsigned long action, struct device_node *node,
+ struct property *prop);
+/*
+ * Apply a single overlay node recursively.
+ *
+ * Property or node names that start with '-' signal that
+ * the property/node is to be removed.
+ *
+ * Note that the in case of an error the target node is left
+ * in a inconsistent state. Error recovery should be performed
+ * by using the tree changes list.
+ */
+static int of_overlay_apply_one(struct of_overlay_info *ovinfo,
+ struct device_node *target, const struct device_node *overlay)
+{
+ const char *pname, *cname;
+ struct device_node *child, *tchild;
+ struct property *prop, *propn, *tprop;
+ int remove;
+ char *full_name;
+ const char *suffix;
+ int ret;
+
+ /* sanity checks */
+ if (target == NULL || overlay == NULL)
+ return -EINVAL;
+
+ for_each_property_of_node(overlay, prop) {
+
+ /* don't touch, 'name' */
+ if (of_prop_cmp(prop->name, "name") == 0)
+ continue;
+
+ /* default is add */
+ remove = 0;
+ pname = prop->name;
+ if (*pname == '-') { /* skip, - notes removal */
+ pname++;
+ remove = 1;
+ propn = NULL;
+ } else {
+ propn = __of_copy_property(prop, GFP_KERNEL,
+ OF_PROP_ALLOCALL);
+ if (propn == NULL)
+ return -ENOMEM;
+ }
+
+ tprop = of_find_property(target, pname, NULL);
+
+ /* found? */
+ if (tprop != NULL) {
+ if (propn != NULL) {
+ ret = of_overlay_tree_change(ovinfo,
+ OF_RECONFIG_UPDATE_PROPERTY,
+ target, propn);
+ if (ret == 0)
+ ret = of_update_property(target, propn);
+ } else {
+ ret = of_overlay_tree_change(ovinfo,
+ OF_RECONFIG_REMOVE_PROPERTY,
+ target, tprop);
+ if (ret == 0)
+ ret = of_remove_property(target, tprop);
+ }
+ } else {
+ if (propn != NULL) {
+ ret = of_overlay_tree_change(ovinfo,
+ OF_RECONFIG_ADD_PROPERTY,
+ target, propn);
+ if (ret == 0)
+ ret = of_add_property(target, propn);
+ } else
+ ret = 0;
+ }
+ if (ret != 0)
+ return ret;
+ }
+
+ __for_each_child_of_node(overlay, child) {
+
+ /* default is add */
+ remove = 0;
+ cname = child->name;
+ if (*cname == '-') { /* skip, - notes removal */
+ cname++;
+ remove = 1;
+ }
+
+ /* special case for nodes with a suffix */
+ suffix = strrchr(child->full_name, '@');
+ if (suffix != NULL) {
+ cname = kbasename(child->full_name);
+ WARN_ON(cname == NULL); /* sanity check */
+ if (cname == NULL)
+ continue;
+ if (*cname == '-')
+ cname++;
+ }
+
+ tchild = of_get_child_by_name(target, cname);
+ if (tchild != NULL) {
+
+ if (!remove) {
+
+ /* apply overlay recursively */
+ ret = of_overlay_apply_one(ovinfo, tchild,
+ child);
+ of_node_put(tchild);
+
+ if (ret != 0)
+ return ret;
+
+ } else {
+
+ ret = of_overlay_tree_change(ovinfo,
+ OF_RECONFIG_DETACH_NODE,
+ tchild, NULL);
+ if (ret == 0)
+ ret = of_detach_node(tchild);
+
+ of_node_put(tchild);
+ if (ret != 0)
+ return ret;
+ }
+
+ } else {
+
+ if (!remove) {
+ full_name = kasprintf(GFP_KERNEL, "%s/%s",
+ target->full_name, cname);
+ if (full_name == NULL)
+ return -ENOMEM;
+
+ /* create empty tree as a target */
+ tchild = __of_create_empty_node(cname,
+ child->type, full_name,
+ child->phandle, GFP_KERNEL,
+ OF_NODE_ALLOCALL);
+
+ /* free either way */
+ kfree(full_name);
+
+ if (tchild == NULL)
+ return -ENOMEM;
+
+ /* point to parent */
+ tchild->parent = target;
+
+ ret = of_overlay_tree_change(ovinfo,
+ OF_RECONFIG_ATTACH_NODE,
+ tchild, NULL);
+ if (ret == 0)
+ ret = of_attach_node(tchild);
+
+ if (ret != 0)
+ return ret;
+
+ /* apply the overlay */
+ ret = of_overlay_apply_one(ovinfo, tchild,
+ child);
+ if (ret != 0) {
+ __of_free_tree(tchild);
+ return ret;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Lookup an overlay device entry
+ */
+struct of_overlay_device_entry *of_overlay_device_entry_lookup(
+ struct of_overlay_info *ovinfo, struct device_node *node)
+{
+ struct of_overlay_device_entry *de;
+
+ list_for_each_entry(de, &ovinfo->de_list, node) {
+ if (de->np == node)
+ return de;
+ }
+ return NULL;
+}
+
+/*
+ * Add an overlay log entry
+ */
+static int of_overlay_log_entry_entry_add(struct of_overlay_info *ovinfo,
+ unsigned long action, struct device_node *dn,
+ struct property *prop)
+{
+ struct of_overlay_log_entry *le;
+
+ /* check */
+ if (ovinfo == NULL || dn == NULL)
+ return -EINVAL;
+
+ le = kzalloc(sizeof(*le), GFP_KERNEL);
+ if (le == NULL) {
+ pr_err("%s: Failed to allocate\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* get a reference to the node */
+ le->action = action;
+ le->np = of_node_get(dn);
+ le->prop = prop;
+
+ if (action == OF_RECONFIG_UPDATE_PROPERTY && prop)
+ le->old_prop = of_find_property(dn, prop->name, NULL);
+
+ list_add_tail(&le->node, &ovinfo->le_list);
+
+ return 0;
+}
+
+/*
+ * Add an overlay device entry
+ */
+static void of_overlay_device_entry_entry_add(struct of_overlay_info *ovinfo,
+ struct device_node *node,
+ int prevstate, int state)
+{
+ struct of_overlay_device_entry *de;
+ int fresh;
+
+ /* check */
+ if (ovinfo == NULL)
+ return;
+
+ fresh = 0;
+ de = of_overlay_device_entry_lookup(ovinfo, node);
+ if (de == NULL) {
+ de = kzalloc(sizeof(*de), GFP_KERNEL);
+ if (de == NULL) {
+ pr_err("%s: Failed to allocate\n", __func__);
+ return;
+ }
+ fresh = 1;
+ de->prevstate = -1;
+ }
+
+ if (de->np == NULL)
+ de->np = of_node_get(node);
+ if (fresh)
+ de->prevstate = prevstate;
+ de->state = state;
+
+ if (fresh)
+ list_add_tail(&de->node, &ovinfo->de_list);
+}
+
+/*
+ * Overlay OF change handler
+ *
+ * Called every time there's a property/node modification
+ * Every modification causes a log entry addition, while
+ * any modification that causes a node's state to change
+ * from/to disabled to/from enabled causes a device entry
+ * addition.
+ *
+ * We piggy back on the already defined OF_RECONFIG_* action
+ * defines, since they suffice.
+ */
+static int of_overlay_tree_change(struct of_overlay_info *ovinfo,
+ unsigned long action, struct device_node *node,
+ struct property *prop)
+{
+ struct property *sprop, *cprop;
+ int prevstate, state;
+ int err = 0;
+
+ /* add to the log */
+ err = of_overlay_log_entry_entry_add(ovinfo, action, node, prop);
+ if (err != 0)
+ return err;
+
+ /* come up with the device entry (if any) */
+ state = 0;
+ prevstate = 0;
+
+ /* determine the state the node will end up */
+ switch (action) {
+ case OF_RECONFIG_ATTACH_NODE:
+ /* we demand that a compatible node is present */
+ state = of_find_property(node, "compatible", NULL) &&
+ of_device_is_available(node);
+ break;
+ case OF_RECONFIG_DETACH_NODE:
+ prevstate = of_find_property(node, "compatible", NULL) &&
+ of_device_is_available(node);
+ state = 0;
+ break;
+ case OF_RECONFIG_ADD_PROPERTY:
+ case OF_RECONFIG_REMOVE_PROPERTY:
+ case OF_RECONFIG_UPDATE_PROPERTY:
+ /* either one cause a change in state */
+ if (strcmp(prop->name, "status") != 0 &&
+ strcmp(prop->name, "compatible") != 0)
+ return 0;
+
+ if (strcmp(prop->name, "status") == 0) {
+ /* status */
+ cprop = of_find_property(node, "compatible", NULL);
+ sprop = action != OF_RECONFIG_REMOVE_PROPERTY ?
+ prop : NULL;
+ } else {
+ /* compatible */
+ sprop = of_find_property(node, "status", NULL);
+ cprop = action != OF_RECONFIG_REMOVE_PROPERTY ?
+ prop : NULL;
+ }
+
+ prevstate = of_find_property(node, "compatible", NULL) &&
+ of_device_is_available(node);
+ state = cprop && cprop->length > 0 &&
+ (!sprop || (sprop->length > 0 &&
+ (strcmp(sprop->value, "okay") == 0 ||
+ strcmp(sprop->value, "ok") == 0)));
+ break;
+
+ default:
+ return 0;
+ }
+
+ of_overlay_device_entry_entry_add(ovinfo, node, prevstate, state);
+
+ return err;
+}
+
+static int of_overlay_device_entry_change(struct of_overlay_info *ovinfo,
+ struct of_overlay_device_entry *de, int revert)
+{
+ int state;
+ int ret;
+
+ /* this funky construct is calculating a target state
+ * taking into account the state and revert inputs
+ * and making sure they are 0/1 (!! does the trick).
+ */
+ state = !!de->state ^ !!revert;
+
+ ret = of_reconfig_notify(state ?
+ OF_RECONFIG_DYNAMIC_CREATE_DEV :
+ OF_RECONFIG_DYNAMIC_DESTROY_DEV,
+ de->np);
+
+ if (ret != 0)
+ pr_warn("%s: Failed to %s device for node '%s'\n",
+ __func__, state ? "create" : "remove",
+ de->np->full_name);
+ return 0;
+}
+
+/*
+ * Revert one overlay
+ * Either due to an error, or due to normal overlay removal.
+ * Using the log entries, we revert any change to the live tree.
+ * In the same manner, using the device entries we enable/disable
+ * the devices appropriately.
+ */
+static void of_overlay_revert_one(struct of_overlay_info *ovinfo)
+{
+ struct of_overlay_device_entry *de, *den;
+ struct of_overlay_log_entry *le, *len;
+ struct property *prop, **propp;
+ struct device_node *np;
+ int ret;
+ unsigned long flags;
+
+ if (!ovinfo || !ovinfo->target || !ovinfo->overlay)
+ return;
+
+ pr_debug("%s: Reverting overlay on '%s'\n", __func__,
+ ovinfo->target->full_name);
+
+ /* overlay applied correctly, now create/destroy pdevs */
+ list_for_each_entry_safe_reverse(de, den, &ovinfo->de_list, node) {
+ of_overlay_device_entry_change(ovinfo, de, 1);
+ of_node_put(de->np);
+ list_del(&de->node);
+ kfree(de);
+ }
+
+ list_for_each_entry_safe_reverse(le, len, &ovinfo->le_list, node) {
+
+ /* get node and immediately put */
+ np = le->np;
+ of_node_put(le->np);
+ le->np = NULL;
+
+ ret = 0;
+ switch (le->action) {
+ case OF_RECONFIG_ATTACH_NODE:
+ pr_debug("Reverting ATTACH_NODE %s\n",
+ np->full_name);
+ ret = of_detach_node(np);
+ break;
+
+ case OF_RECONFIG_DETACH_NODE:
+ pr_debug("Reverting DETACH_NODE %s\n",
+ np->full_name);
+ ret = of_attach_node(np);
+ break;
+
+ case OF_RECONFIG_ADD_PROPERTY:
+ pr_debug("Reverting ADD_PROPERTY %s %s\n",
+ np->full_name, le->prop->name);
+ ret = of_remove_property(np, le->prop);
+ break;
+
+ case OF_RECONFIG_REMOVE_PROPERTY:
+ case OF_RECONFIG_UPDATE_PROPERTY:
+
+ pr_debug("Reverting %s_PROPERTY %s %s\n",
+ le->action == OF_RECONFIG_REMOVE_PROPERTY ?
+ "REMOVE" : "UPDATE",
+ np->full_name, le->prop->name);
+
+ /* property is possibly on deadprops (avoid alloc) */
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ prop = le->action == OF_RECONFIG_REMOVE_PROPERTY ?
+ le->prop : le->old_prop;
+ propp = &np->deadprops;
+ while (*propp != NULL) {
+ if (*propp == prop)
+ break;
+ propp = &(*propp)->next;
+ }
+ if (*propp != NULL) {
+ /* remove it from deadprops */
+ (*propp)->next = prop->next;
+ raw_spin_unlock_irqrestore(&devtree_lock,
+ flags);
+ } else {
+ raw_spin_unlock_irqrestore(&devtree_lock,
+ flags);
+ /* not found, just make a copy */
+ prop = __of_copy_property(prop, GFP_KERNEL,
+ OF_PROP_ALLOCALL);
+ if (prop == NULL) {
+ pr_err("%s: Failed to copy property\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (le->action == OF_RECONFIG_REMOVE_PROPERTY)
+ ret = of_add_property(np, prop);
+ else
+ ret = of_update_property(np, prop);
+ break;
+
+ default:
+ /* nothing */
+ break;
+ }
+
+ if (ret != 0)
+ pr_err("%s: revert on node %s Failed!\n",
+ __func__, np->full_name);
+
+ list_del(&le->node);
+
+ kfree(le);
+ }
+}
+
+/*
+ * Perform the post overlay work.
+ *
+ * In the case on an error we revert the overlay.
+ * If the overlay applied correctly, we iterate over the device entries
+ * and create/destroy the devices appropriately.
+ */
+static int of_overlay_post_one(struct of_overlay_info *ovinfo, int err)
+{
+ struct of_overlay_device_entry *de, *den;
+
+ if (err != 0) {
+ /* revert this (possible partially applied) overlay */
+ of_overlay_revert_one(ovinfo);
+ return 0;
+ }
+
+ /* overlay applied correctly, now create/destroy pdevs */
+ list_for_each_entry_safe(de, den, &ovinfo->de_list, node) {
+
+ /* no state change? just remove this entry */
+ if (de->prevstate == de->state) {
+ of_node_put(de->np);
+ list_del(&de->node);
+ kfree(de);
+ continue;
+ }
+
+ of_overlay_device_entry_change(ovinfo, de, 0);
+ }
+
+ return 0;
+}
+
+/**
+ * of_overlay_apply - Apply @count overlays pointed at by @ovinfo_tab
+ * @count: Number of of_overlay_info's
+ * @ovinfo_tab: Array of overlay_info's to apply
+ *
+ * Applies the overlays given, while handling all error conditions
+ * appropriately. Either the operation succeeds, or if it fails the
+ * live tree is reverted to the state before the attempt.
+ * Returns 0, or an error if the overlay attempt failed.
+ */
+int of_overlay_apply(int count, struct of_overlay_info *ovinfo_tab)
+{
+ struct of_overlay_info *ovinfo;
+ int i, err;
+
+ if (!ovinfo_tab)
+ return -EINVAL;
+
+ /* first we apply the overlays atomically */
+ for (i = 0; i < count; i++) {
+
+ ovinfo = &ovinfo_tab[i];
+
+ err = of_overlay_apply_one(ovinfo, ovinfo->target,
+ ovinfo->overlay);
+ of_overlay_post_one(ovinfo, err);
+
+ if (err != 0) {
+ pr_err("%s: overlay failed '%s'\n",
+ __func__, ovinfo->target->full_name);
+ goto err_fail;
+ }
+ }
+
+ return 0;
+
+err_fail:
+ while (--i >= 0) {
+ ovinfo = &ovinfo_tab[i];
+
+ of_overlay_revert_one(ovinfo);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(of_overlay_apply);
+
+/**
+ * of_overlay_revert - Revert a previously applied overlay
+ * @count: Number of of_overlay_info's
+ * @ovinfo_tab: Array of overlay_info's to apply
+ *
+ * Revert a previous overlay. The state of the live tree
+ * is reverted to the one before the overlay.
+ * Returns 0, or an error if the overlay table is not given.
+ */
+int of_overlay_revert(int count, struct of_overlay_info *ovinfo_tab)
+{
+ struct of_overlay_info *ovinfo;
+ int i;
+
+ if (!ovinfo_tab)
+ return -EINVAL;
+
+ /* revert the overlays in reverse */
+ for (i = count - 1; i >= 0; i--) {
+
+ ovinfo = &ovinfo_tab[i];
+
+ of_overlay_revert_one(ovinfo);
+
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_overlay_revert);
+
+/**
+ * of_init_overlay_info - Initialize a single of_overlay_info structure
+ * @ovinfo: Pointer to the overlay info structure to initialize
+ *
+ * Initialize a single overlay info structure.
+ */
+void of_init_overlay_info(struct of_overlay_info *ovinfo)
+{
+ memset(ovinfo, 0, sizeof(*ovinfo));
+ INIT_LIST_HEAD(&ovinfo->de_list);
+ INIT_LIST_HEAD(&ovinfo->le_list);
+}
+
+/*
+ * Find the target node using a number of different strategies
+ * in order of preference
+ *
+ * "target" property containing the phandle of the target
+ * "target-path" property containing the path of the target
+ *
+ */
+struct device_node *find_target_node(struct device_node *info_node)
+{
+ const char *path;
+ u32 val;
+ int ret;
+
+ /* first try to go by using the target as a phandle */
+ ret = of_property_read_u32(info_node, "target", &val);
+ if (ret == 0)
+ return of_find_node_by_phandle(val);
+
+ /* now try to locate by path */
+ ret = of_property_read_string(info_node, "target-path", &path);
+ if (ret == 0)
+ return of_find_node_by_path(path);
+
+ pr_err("%s: Failed to find target for node %p (%s)\n", __func__,
+ info_node, info_node->name);
+
+ return NULL;
+}
+
+/**
+ * of_fill_overlay_info - Fill an overlay info structure
+ * @info_node: Device node containing the overlay
+ * @ovinfo: Pointer to the overlay info structure to fill
+ *
+ * Fills an overlay info structure with the overlay information
+ * from a device node. This device node must have a target property
+ * which contains a phandle of the overlay target node, and an
+ * __overlay__ child node which has the overlay contents.
+ * Both ovinfo->target & ovinfo->overlay have their references taken.
+ *
+ * Returns 0 on success, or a negative error value.
+ */
+int of_fill_overlay_info(struct device_node *info_node,
+ struct of_overlay_info *ovinfo)
+{
+ if (!info_node || !ovinfo)
+ return -EINVAL;
+
+ ovinfo->overlay = of_get_child_by_name(info_node, "__overlay__");
+ if (ovinfo->overlay == NULL)
+ goto err_fail;
+
+ ovinfo->target = find_target_node(info_node);
+ if (ovinfo->target == NULL)
+ goto err_fail;
+
+ return 0;
+
+err_fail:
+ of_node_put(ovinfo->target);
+ of_node_put(ovinfo->overlay);
+
+ memset(ovinfo, 0, sizeof(*ovinfo));
+ return -EINVAL;
+}
+
+/**
+ * of_build_overlay_info - Build an overlay info array
+ * @tree: Device node containing all the overlays
+ * @cntp: Pointer to where the overlay info count will be help
+ * @ovinfop: Pointer to the pointer of an overlay info structure.
+ *
+ * Helper function that given a tree containing overlay information,
+ * allocates and builds an overlay info array containing it, ready
+ * for use using of_overlay_apply.
+ *
+ * Returns 0 on success with the @cntp @ovinfop pointers valid,
+ * while on error a negative error value is returned.
+ */
+int of_build_overlay_info(struct device_node *tree,
+ int *cntp, struct of_overlay_info **ovinfop)
+{
+ struct device_node *node;
+ struct of_overlay_info *ovinfo;
+ int cnt, err;
+
+ if (tree == NULL || cntp == NULL || ovinfop == NULL)
+ return -EINVAL;
+
+ /* worst case; every child is a node */
+ cnt = 0;
+ for_each_child_of_node(tree, node)
+ cnt++;
+
+ ovinfo = kcalloc(cnt, sizeof(*ovinfo), GFP_KERNEL);
+ if (ovinfo == NULL)
+ return -ENOMEM;
+
+ cnt = 0;
+ for_each_child_of_node(tree, node) {
+
+ of_init_overlay_info(&ovinfo[cnt]);
+ err = of_fill_overlay_info(node, &ovinfo[cnt]);
+ if (err == 0)
+ cnt++;
+ }
+
+ /* if nothing filled, return error */
+ if (cnt == 0) {
+ kfree(ovinfo);
+ return -ENODEV;
+ }
+
+ *cntp = cnt;
+ *ovinfop = ovinfo;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_build_overlay_info);
+
+/**
+ * of_free_overlay_info - Free an overlay info array
+ * @count: Number of of_overlay_info's
+ * @ovinfo_tab: Array of overlay_info's to free
+ *
+ * Releases the memory of a previously allocate ovinfo array
+ * by of_build_overlay_info.
+ * Returns 0, or an error if the arguments are bogus.
+ */
+int of_free_overlay_info(int count, struct of_overlay_info *ovinfo_tab)
+{
+ struct of_overlay_info *ovinfo;
+ int i;
+
+ if (!ovinfo_tab || count < 0)
+ return -EINVAL;
+
+ /* do it in reverse */
+ for (i = count - 1; i >= 0; i--) {
+ ovinfo = &ovinfo_tab[i];
+
+ of_node_put(ovinfo->target);
+ of_node_put(ovinfo->overlay);
+ }
+ kfree(ovinfo_tab);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_free_overlay_info);
+
+static LIST_HEAD(ov_list);
+static DEFINE_MUTEX(ov_lock);
+static DEFINE_IDR(ov_idr);
+
+/**
+ * of_overlay_create - Create and apply an overlay
+ * @tree: Device node containing all the overlays
+ *
+ * Creates and applies an overlay while also keeping track
+ * of the overlay in a list. This list can be use to prevent
+ * illegal overlay removals.
+ *
+ * Returns the id of the created overlay, or an negative error number
+ */
+int of_overlay_create(struct device_node *tree)
+{
+ struct of_overlay *ov;
+ int err, id;
+
+ /* allocate the overlay structure */
+ ov = kzalloc(sizeof(*ov), GFP_KERNEL);
+ if (ov == NULL)
+ return -ENOMEM;
+ ov->id = -1;
+
+ INIT_LIST_HEAD(&ov->node);
+
+ mutex_lock(&ov_lock);
+
+ id = idr_alloc(&ov_idr, ov, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ pr_err("%s: idr_alloc() failed for tree@%s\n",
+ __func__, tree->full_name);
+ err = id;
+ goto err_free_ov;
+ }
+ ov->id = id;
+
+ /* build the overlay info structures */
+ err = of_build_overlay_info(tree, &ov->count, &ov->ovinfo_tab);
+ if (err) {
+ pr_err("%s: of_build_overlay_info() failed for tree@%s\n",
+ __func__, tree->full_name);
+ goto err_free_idr;
+ }
+
+ /* apply the overlay */
+ err = of_overlay_apply(ov->count, ov->ovinfo_tab);
+ if (err) {
+ pr_err("%s: of_overlay_apply() failed for tree@%s\n",
+ __func__, tree->full_name);
+ goto err_free_ovinfo;
+ }
+
+ /* add to the tail of the overlay list */
+ list_add_tail(&ov->node, &ov_list);
+
+ mutex_unlock(&ov_lock);
+
+ return id;
+
+err_free_ovinfo:
+ of_free_overlay_info(ov->count, ov->ovinfo_tab);
+err_free_idr:
+ idr_remove(&ov_idr, ov->id);
+ mutex_unlock(&ov_lock);
+err_free_ov:
+ kfree(ov);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(of_overlay_create);
+
+/* check whether the given node, lies under the given tree */
+static int overlay_subtree_check(struct device_node *tree,
+ struct device_node *dn)
+{
+ struct device_node *child;
+
+ /* match? */
+ if (tree == dn)
+ return 1;
+
+ __for_each_child_of_node(tree, child) {
+ if (overlay_subtree_check(child, dn))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* check whether this overlay is the topmost */
+static int overlay_is_topmost(struct of_overlay *ov, struct device_node *dn)
+{
+ struct of_overlay *ovt;
+ struct of_overlay_info *ovinfo;
+ struct of_overlay_log_entry *le;
+ int i;
+
+ list_for_each_entry_reverse(ovt, &ov_list, node) {
+
+ /* if we hit ourselves, we're done */
+ if (ovt == ov)
+ break;
+
+ /* check against each subtree affected by this overlay */
+ for (i = 0; i < ovt->count; i++) {
+ ovinfo = &ovt->ovinfo_tab[i];
+ list_for_each_entry(le, &ovinfo->le_list, node) {
+ if (overlay_subtree_check(le->np, dn)) {
+ pr_err("%s: #%d clashes #%d @%s\n",
+ __func__, ov->id, ovt->id,
+ dn->full_name);
+ return 0;
+ }
+ }
+ }
+ }
+
+ /* overlay is topmost */
+ return 1;
+}
+
+/*
+ * We can safely remove the overlay only if it's the top-most one.
+ * Newly applied overlays are inserted at the tail of the overlay list,
+ * so a top most overlay is the one that is closest to the tail.
+ *
+ * The topmost check is done by exploiting this property. For each
+ * affected device node in the log list we check if this overlay is
+ * the one closest to the tail. If another overlay has affected this
+ * device node and is closest to the tail, then removal is not permited.
+ */
+static int overlay_removal_is_ok(struct of_overlay *ov)
+{
+ struct of_overlay_info *ovinfo;
+ struct of_overlay_log_entry *le;
+ int i;
+
+ for (i = 0; i < ov->count; i++) {
+ ovinfo = &ov->ovinfo_tab[i];
+ list_for_each_entry(le, &ovinfo->le_list, node) {
+ if (!overlay_is_topmost(ov, le->np)) {
+ pr_err("%s: overlay #%d is not topmost\n",
+ __func__, ov->id);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * of_overlay_destroy - Removes an overlay
+ * @id: Overlay id number returned by a previous call to of_overlay_create
+ *
+ * Removes an overlay if it is permissible.
+ *
+ * Returns 0 on success, or an negative error number
+ */
+int of_overlay_destroy(int id)
+{
+ struct of_overlay *ov;
+ int err;
+
+ mutex_lock(&ov_lock);
+ ov = idr_find(&ov_idr, id);
+ if (ov == NULL) {
+ err = -ENODEV;
+ pr_err("%s: Could not find overlay #%d\n",
+ __func__, id);
+ goto out;
+ }
+
+ /* check whether the overlay is safe to remove */
+ if (!overlay_removal_is_ok(ov)) {
+ err = -EBUSY;
+ pr_err("%s: removal check failed for overlay #%d\n",
+ __func__, id);
+ goto out;
+ }
+
+ list_del(&ov->node);
+
+ of_overlay_revert(ov->count, ov->ovinfo_tab);
+ of_free_overlay_info(ov->count, ov->ovinfo_tab);
+
+ idr_remove(&ov_idr, id);
+
+ kfree(ov);
+
+ err = 0;
+
+out:
+ mutex_unlock(&ov_lock);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(of_overlay_destroy);
+
+/**
+ * of_overlay_destroy_all - Removes all overlays from the system
+ *
+ * Removes all overlays from the system in the correct order.
+ *
+ * Returns 0 on success, or an negative error number
+ */
+int of_overlay_destroy_all(void)
+{
+ struct of_overlay *ov, *ovn;
+
+ mutex_lock(&ov_lock);
+
+ /* the tail of list is guaranteed to be safe to remove */
+ list_for_each_entry_safe_reverse(ov, ovn, &ov_list, node) {
+
+ list_del(&ov->node);
+
+ of_overlay_revert(ov->count, ov->ovinfo_tab);
+ of_free_overlay_info(ov->count, ov->ovinfo_tab);
+
+ idr_remove(&ov_idr, ov->id);
+
+ kfree(ov);
+ }
+
+ mutex_unlock(&ov_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_overlay_destroy_all);
diff --git a/include/linux/of.h b/include/linux/of.h
index 9d5795c..61b88ba 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -23,6 +23,7 @@
#include <linux/spinlock.h>
#include <linux/topology.h>
#include <linux/notifier.h>
+#include <linux/list.h>
#include <asm/byteorder.h>
#include <asm/errno.h>
@@ -331,6 +332,8 @@ extern int of_update_property(struct device_node *np, struct property *newprop);
#define OF_RECONFIG_ADD_PROPERTY 0x0003
#define OF_RECONFIG_REMOVE_PROPERTY 0x0004
#define OF_RECONFIG_UPDATE_PROPERTY 0x0005
+#define OF_RECONFIG_DYNAMIC_CREATE_DEV 0x0006
+#define OF_RECONFIG_DYNAMIC_DESTROY_DEV 0x0007
struct of_prop_reconfig {
struct device_node *dn;
@@ -840,4 +843,151 @@ static inline int of_resolve(struct device_node *resolve)
#endif
+/**
+ * Overlay support
+ */
+
+/**
+ * struct of_overlay_log_entry - Holds a DT log entry
+ * @node: list_head for the log list
+ * @action: notifier action
+ * @np: pointer to the device node affected
+ * @prop: pointer to the property affected
+ * @old_prop: hold a pointer to the original property
+ *
+ * Every modification of the device tree during application of the
+ * overlay is held in a list of of_overlay_log_entry structures.
+ * That way we can recover from a partial application, or we can
+ * revert the overlay properly.
+ */
+struct of_overlay_log_entry {
+ struct list_head node;
+ unsigned long action;
+ struct device_node *np;
+ struct property *prop;
+ struct property *old_prop;
+};
+
+struct of_overlay_device_entry;
+
+/**
+ * struct of_overlay_device_entry - Holds an overlay device entry
+ * @node: list_head for the device list
+ * @np: device node pointer to the device node affected
+ * @state: new device state
+ * @prevstate: previous device state
+ * @priv: private pointer for use by bus handlers
+ *
+ * When the overlay results in a device node's state to change this
+ * fact is recorded in a list of device entries. After the overlay
+ * is applied we can create/destroy the devices according
+ * to the new state of the live tree.
+ */
+struct of_overlay_device_entry {
+ struct list_head node;
+ struct device_node *np;
+ int prevstate;
+ int state;
+ void *priv;
+};
+
+/**
+ * struct of_overlay_info - Holds a single overlay info
+ * @target: target of the overlay operation
+ * @overlay: pointer to the overlay contents node
+ * @le_list: List of the overlay logs
+ * @de_list: List of the overlay records
+ *
+ * Holds a single overlay state, including all the overlay logs &
+ * records.
+ */
+struct of_overlay_info {
+ struct device_node *target;
+ struct device_node *overlay;
+ struct list_head le_list;
+ struct list_head de_list;
+};
+
+/**
+ * struct of_overlay - Holds a complete overlay transaction
+ * @node: List on which we are located
+ * @count: Count of ovinfo structures
+ * @ovinfo: Overlay info array (count size)
+ *
+ * Holds a complete overlay transaction
+ */
+struct of_overlay {
+ int id;
+ struct list_head node;
+ int count;
+ struct of_overlay_info *ovinfo_tab;
+ /* optional properties will follow eventually */
+};
+
+#ifdef CONFIG_OF_OVERLAY
+
+/* the following is the internal API */
+int of_overlay_apply(int count, struct of_overlay_info *ovinfo_tab);
+int of_overlay_revert(int count, struct of_overlay_info *ovinfo_tab);
+
+int of_fill_overlay_info(struct device_node *info_node,
+ struct of_overlay_info *ovinfo);
+int of_build_overlay_info(struct device_node *tree,
+ int *cntp, struct of_overlay_info **ovinfop);
+int of_free_overlay_info(int cnt, struct of_overlay_info *ovinfo);
+
+/* ID based overlays; the API for external users */
+int of_overlay_create(struct device_node *tree);
+int of_overlay_destroy(int id);
+int of_overlay_destroy_all(void);
+
+#else
+
+static inline int of_overlay_apply(int count,
+ struct of_overlay_info *ovinfo_tab)
+{
+ return -ENOTSUPP;
+}
+
+static inline int of_overlay_revert(int count,
+ struct of_overlay_info *ovinfo_tab)
+{
+ return -ENOTSUPP;
+}
+
+static inline int of_fill_overlay_info(struct device_node *info_node,
+ struct of_overlay_info *ovinfo)
+{
+ return -ENOTSUPP;
+}
+
+static inline int of_build_overlay_info(struct device_node *tree,
+ int *cntp, struct of_overlay_info **ovinfop)
+{
+ return -ENOTSUPP;
+}
+
+static inline int of_free_overlay_info(int cnt,
+ struct of_overlay_info *ovinfo)
+{
+ return -ENOTSUPP;
+}
+
+int of_overlay_create(struct device_node *tree)
+{
+ return -ENOTSUPP;
+}
+
+int of_overlay_destroy(int id)
+{
+ return -ENOTSUPP;
+}
+
+int of_overlay_destroy_all(void)
+{
+ return -ENOTSUPP;
+}
+
+#endif
+
#endif /* _LINUX_OF_H */
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH v5 3/9] OF: DT-Overlay configfs interface
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 1/9] OF: Introduce Device Tree resolve support Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 2/9] OF: Introduce DT overlay support Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 4/9] OF: platform: Add OF notifier handler Pantelis Antoniou
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
Add a runtime interface to using configfs for generic device tree overlay
usage.
A device-tree configfs entry is created in /config/device-tree/overlays
To create an overlay you mkdir the directory and then echo the overlay
firmware file to the path property file.
# mkdir /config/device-tree/overlays/foo
# echo foo.dtbo >/config/device-tree/overlays/foo/path
The overlay file will be loaded using the standard firmware loader
and will be applied.
To remove it simply rmdir the directory.
# rmdir /config/device-tree/overlays/foo
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/of/Kconfig | 7 ++
drivers/of/Makefile | 1 +
drivers/of/configfs.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 259 insertions(+)
create mode 100644 drivers/of/configfs.c
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 5ebde98..1a2ff48 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -91,4 +91,11 @@ config OF_OVERLAY
select OF_DEVICE
select OF_RESOLVE
+config OF_CONFIGFS
+ bool "OpenFirmware Overlay ConfigFS interface"
+ select CONFIGFS_FS
+ select OF_OVERLAY
+ help
+ Enable a simple user-space driver DT overlay interface.
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index e1f864a..46b3308 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -12,5 +12,6 @@ obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
obj-$(CONFIG_OF_RESOLVE) += resolver.o
obj-$(CONFIG_OF_OVERLAY) += overlay.o
+obj-$(CONFIG_OF_CONFIGFS) += configfs.o
CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c
new file mode 100644
index 0000000..2f5584f
--- /dev/null
+++ b/drivers/of/configfs.c
@@ -0,0 +1,251 @@
+/*
+ * Configfs entries for device-tree
+ *
+ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
+ *
+ * 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/ctype.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/configfs.h>
+#include <linux/types.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/file.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+
+#include "of_private.h"
+
+#ifdef CONFIG_OF_OVERLAY
+
+struct cfs_overlay_item {
+ struct config_item item;
+
+ char path[PATH_MAX];
+
+ const struct firmware *fw;
+ struct device_node *overlay;
+ int ov_id;
+};
+
+static inline struct cfs_overlay_item *to_cfs_overlay_item(
+ struct config_item *item)
+{
+ return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
+}
+
+CONFIGFS_ATTR_STRUCT(cfs_overlay_item);
+#define CFS_OVERLAY_ITEM_ATTR(_name, _mode, _show, _store) \
+struct cfs_overlay_item_attribute cfs_overlay_item_attr_##_name = \
+ __CONFIGFS_ATTR(_name, _mode, _show, _store)
+#define CFS_OVERLAY_ITEM_ATTR_RO(_name, _show) \
+struct cfs_overlay_item_attribute cfs_overlay_item_attr_##_name = \
+ __CONFIGFS_ATTR_RO(_name, _show)
+
+static ssize_t cfs_overlay_item_path_show(struct cfs_overlay_item *overlay,
+ char *page)
+{
+ return sprintf(page, "%s\n", overlay->path);
+}
+
+static ssize_t cfs_overlay_item_path_store(struct cfs_overlay_item *overlay,
+ const char *page, size_t count)
+{
+ const char *p = page;
+ char *s;
+ int err;
+
+ /* if it's set do not allow changes */
+ if (overlay->path[0] != '\0')
+ return -EPERM;
+
+ /* copy to path buffer (and make sure it's always zero terminated */
+ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
+ overlay->path[sizeof(overlay->path) - 1] = '\0';
+
+ /* strip trailing newlines */
+ s = overlay->path + strlen(overlay->path);
+ while (s > overlay->path && *--s == '\n')
+ *s = '\0';
+
+ pr_debug("%s: path is '%s'\n", __func__, overlay->path);
+
+ err = request_firmware(&overlay->fw, overlay->path, NULL);
+ if (err != 0)
+ goto out_err;
+
+ /* unflatten the tree */
+ of_fdt_unflatten_tree((void *)overlay->fw->data, &overlay->overlay);
+ if (overlay->overlay == NULL) {
+ pr_err("%s: failed to unflatten tree\n", __func__);
+ err = -EINVAL;
+ goto out_err;
+ }
+ pr_debug("%s: unflattened OK\n", __func__);
+
+ /* mark it as detached */
+ of_node_set_flag(overlay->overlay, OF_DETACHED);
+
+ /* perform resolution */
+ err = of_resolve(overlay->overlay);
+ if (err != 0) {
+ pr_err("%s: Failed to resolve tree\n", __func__);
+ goto out_err;
+ }
+ pr_debug("%s: resolved OK\n", __func__);
+
+ err = of_overlay_create(overlay->overlay);
+ if (err < 0) {
+ pr_err("%s: Failed to create overlay (err=%d)\n",
+ __func__, err);
+ goto out_err;
+ }
+ overlay->ov_id = err;
+
+ return count;
+
+out_err:
+
+ release_firmware(overlay->fw);
+ overlay->fw = NULL;
+
+ overlay->path[0] = '\0';
+ return err;
+}
+
+static ssize_t cfs_overlay_item_status_show(struct cfs_overlay_item *overlay,
+ char *page)
+{
+ return sprintf(page, "%s\n",
+ overlay->ov_id >= 0 ? "applied" : "unapplied");
+}
+
+CFS_OVERLAY_ITEM_ATTR(path, S_IRUGO | S_IWUSR,
+ cfs_overlay_item_path_show, cfs_overlay_item_path_store);
+CFS_OVERLAY_ITEM_ATTR_RO(status, cfs_overlay_item_status_show);
+
+static struct configfs_attribute *cfs_overlay_attrs[] = {
+ &cfs_overlay_item_attr_path.attr,
+ &cfs_overlay_item_attr_status.attr,
+ NULL,
+};
+
+static void cfs_overlay_release(struct config_item *item)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+ if (overlay->ov_id >= 0)
+ of_overlay_destroy(overlay->ov_id);
+ if (overlay->fw)
+ release_firmware(overlay->fw);
+ kfree(overlay);
+}
+
+CONFIGFS_ATTR_OPS(cfs_overlay_item);
+static struct configfs_item_operations cfs_overlay_item_ops = {
+ .release = cfs_overlay_release,
+ .show_attribute = cfs_overlay_item_attr_show,
+ .store_attribute = cfs_overlay_item_attr_store,
+};
+
+static struct config_item_type cfs_overlay_type = {
+ .ct_item_ops = &cfs_overlay_item_ops,
+ .ct_attrs = cfs_overlay_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *cfs_overlay_group_make_item(
+ struct config_group *group, const char *name)
+{
+ struct cfs_overlay_item *overlay;
+
+ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
+ if (!overlay)
+ return ERR_PTR(-ENOMEM);
+ overlay->ov_id = -1;
+
+ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
+ return &overlay->item;
+}
+
+static void cfs_overlay_group_drop_item(struct config_group *group,
+ struct config_item *item)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+ config_item_put(&overlay->item);
+}
+
+static struct configfs_group_operations overlays_ops = {
+ .make_item = cfs_overlay_group_make_item,
+ .drop_item = cfs_overlay_group_drop_item,
+};
+
+static struct config_item_type overlays_type = {
+ .ct_group_ops = &overlays_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+#endif /* CONFIG_OF_OVERLAY */
+
+static struct configfs_group_operations of_cfs_ops = {
+ /* empty - we don't allow anything to be created */
+};
+
+static struct config_item_type of_cfs_type = {
+ .ct_group_ops = &of_cfs_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+struct config_group of_cfs_overlay_group;
+
+struct config_group *of_cfs_def_groups[] = {
+#ifdef CONFIG_OF_OVERLAY
+ &of_cfs_overlay_group,
+#endif
+ NULL
+};
+
+static struct configfs_subsystem of_cfs_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "device-tree",
+ .ci_type = &of_cfs_type,
+ },
+ .default_groups = of_cfs_def_groups,
+ },
+ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
+};
+
+static int __init of_cfs_init(void)
+{
+ int ret;
+
+ pr_info("%s\n", __func__);
+
+ config_group_init(&of_cfs_subsys.su_group);
+#ifdef CONFIG_OF_OVERLAY
+ config_group_init_type_name(&of_cfs_overlay_group, "overlays",
+ &overlays_type);
+#endif
+
+ ret = configfs_register_subsystem(&of_cfs_subsys);
+ if (ret != 0) {
+ pr_err("%s: failed to register subsys\n", __func__);
+ goto out;
+ }
+ pr_info("%s: OK\n", __func__);
+out:
+ return ret;
+}
+late_initcall(of_cfs_init);
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH v5 4/9] OF: platform: Add OF notifier handler
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
` (2 preceding siblings ...)
2014-05-28 12:46 ` [PATCH v5 3/9] OF: DT-Overlay configfs interface Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 5/9] of: i2c: Export single device registration method Pantelis Antoniou
` (5 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
Add OF notifier handler needed for creating/destroying platform devices
according to dynamic runtime changes in the DT live tree.
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/base/platform.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 66 insertions(+), 3 deletions(-)
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 9e9227e..8d64605 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -930,6 +930,56 @@ struct bus_type platform_bus_type = {
};
EXPORT_SYMBOL_GPL(platform_bus_type);
+static struct notifier_block platform_of_notifier;
+
+static int of_platform_notify(struct notifier_block *nb,
+ unsigned long action, void *arg)
+{
+ struct device_node *dn;
+ struct platform_device *pdev_parent, *pdev;
+
+ if (action == OF_RECONFIG_DYNAMIC_CREATE_DEV) {
+
+ dn = arg;
+
+ /* verify that the parent is a bus */
+ if (!of_match_node(of_default_bus_match_table, dn->parent))
+ return NOTIFY_OK; /* not for us */
+
+ /* pdev_parent may be NULL when no bus platform device */
+ pdev_parent = of_find_device_by_node(dn->parent);
+ pdev = of_platform_device_create(dn, NULL,
+ pdev_parent ? &pdev_parent->dev : NULL);
+ of_dev_put(pdev_parent);
+
+ if (pdev == NULL) {
+ pr_err("%s: failed to create for '%s'\n",
+ __func__, dn->full_name);
+ /* of_platform_device_create tosses the error code */
+ return notifier_from_errno(-EINVAL);
+ }
+
+ } else if (action == OF_RECONFIG_DYNAMIC_DESTROY_DEV) {
+
+ dn = arg;
+
+ /* find our device by node */
+ pdev = of_find_device_by_node(dn);
+ if (pdev == NULL)
+ return NOTIFY_OK; /* no? not meant for us */
+
+ /* unregister takes one ref away */
+ platform_device_unregister(pdev);
+
+ /* and put the reference of the find */
+ of_dev_put(pdev);
+
+ } else
+ return NOTIFY_OK;
+
+ return NOTIFY_STOP;
+}
+
int __init platform_bus_init(void)
{
int error;
@@ -938,10 +988,23 @@ int __init platform_bus_init(void)
error = device_register(&platform_bus);
if (error)
- return error;
- error = bus_register(&platform_bus_type);
+ goto err_out;
+
+ error = bus_register(&platform_bus_type);
+ if (error)
+ goto err_unreg_dev;
+
+ platform_of_notifier.notifier_call = of_platform_notify;
+ error = of_reconfig_notifier_register(&platform_of_notifier);
if (error)
- device_unregister(&platform_bus);
+ goto err_unreg_bus;
+
+ return 0;
+err_unreg_bus:
+ bus_unregister(&platform_bus_type);
+err_unreg_dev:
+ device_unregister(&platform_bus);
+err_out:
return error;
}
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH v5 5/9] of: i2c: Export single device registration method
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
` (3 preceding siblings ...)
2014-05-28 12:46 ` [PATCH v5 4/9] OF: platform: Add OF notifier handler Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 6/9] OF: i2c: Add OF notifier handler Pantelis Antoniou
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
Dynamically inserting i2c client device nodes requires the use
of a single device registration method. Rework and export it.
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/i2c/i2c-core.c | 99 +++++++++++++++++++++++++++-----------------------
include/linux/i2c.h | 10 +++++
2 files changed, 64 insertions(+), 45 deletions(-)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 7c7f4b8..6e47f73 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -50,6 +50,7 @@
#include <linux/acpi.h>
#include <linux/jump_label.h>
#include <asm/uaccess.h>
+#include <linux/err.h>
#include "i2c-core.h"
@@ -997,63 +998,71 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
/* OF support code */
#if IS_ENABLED(CONFIG_OF)
-static void of_i2c_register_devices(struct i2c_adapter *adap)
+struct i2c_client *
+of_i2c_register_device(struct i2c_adapter *adap,
+ struct device_node *node)
{
- void *result;
- struct device_node *node;
+ struct i2c_client *result;
+ struct i2c_board_info info = {};
+ struct dev_archdata dev_ad = {};
+ const __be32 *addr;
+ int len;
- /* Only register child devices if the adapter has a node pointer set */
- if (!adap->dev.of_node)
- return;
+ dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
- dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
+ if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
+ dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
+ node->full_name);
+ return ERR_PTR(-EINVAL);
+ }
- for_each_available_child_of_node(adap->dev.of_node, node) {
- struct i2c_board_info info = {};
- struct dev_archdata dev_ad = {};
- const __be32 *addr;
- int len;
+ addr = of_get_property(node, "reg", &len);
+ if (!addr || (len < sizeof(int))) {
+ dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
+ node->full_name);
+ return ERR_PTR(-EINVAL);
+ }
- dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
+ info.addr = be32_to_cpup(addr);
+ if (info.addr > (1 << 10) - 1) {
+ dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
+ info.addr, node->full_name);
+ return ERR_PTR(-EINVAL);
+ }
- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
- dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
- node->full_name);
- continue;
- }
+ info.irq = irq_of_parse_and_map(node, 0);
+ info.of_node = of_node_get(node);
+ info.archdata = &dev_ad;
- addr = of_get_property(node, "reg", &len);
- if (!addr || (len < sizeof(int))) {
- dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
- node->full_name);
- continue;
- }
+ if (of_get_property(node, "wakeup-source", NULL))
+ info.flags |= I2C_CLIENT_WAKE;
- info.addr = be32_to_cpup(addr);
- if (info.addr > (1 << 10) - 1) {
- dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, node->full_name);
- continue;
- }
+ request_module("%s%s", I2C_MODULE_PREFIX, info.type);
- info.irq = irq_of_parse_and_map(node, 0);
- info.of_node = of_node_get(node);
- info.archdata = &dev_ad;
+ result = i2c_new_device(adap, &info);
+ if (result == NULL) {
+ dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
+ node->full_name);
+ of_node_put(node);
+ irq_dispose_mapping(info.irq);
+ return ERR_PTR(-EINVAL);
+ }
+ return result;
+}
+EXPORT_SYMBOL(of_i2c_register_device);
- if (of_get_property(node, "wakeup-source", NULL))
- info.flags |= I2C_CLIENT_WAKE;
+static void of_i2c_register_devices(struct i2c_adapter *adap)
+{
+ struct device_node *node;
- request_module("%s%s", I2C_MODULE_PREFIX, info.type);
+ /* Only register child devices if the adapter has a node pointer set */
+ if (!adap->dev.of_node)
+ return;
- result = i2c_new_device(adap, &info);
- if (result == NULL) {
- dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
- node->full_name);
- of_node_put(node);
- irq_dispose_mapping(info.irq);
- continue;
- }
- }
+ dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
+
+ for_each_available_child_of_node(adap->dev.of_node, node)
+ of_i2c_register_device(adap, node);
}
static int of_dev_node_match(struct device *dev, void *data)
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index b556e0a..22a8f44 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -558,6 +558,9 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap)
#endif /* I2C */
#if IS_ENABLED(CONFIG_OF)
+struct i2c_client *
+of_i2c_register_device(struct i2c_adapter *adap, struct device_node *node);
+
/* must call put_device() when done with returned i2c_client device */
extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node);
@@ -566,6 +569,13 @@ extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
#else
+static inline struct i2c_client *
+of_i2c_register_device(struct i2c_adapter *adap,
+ struct device_node *node)
+{
+ return ERR_PTR(-ENODEV);
+}
+
static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
{
return NULL;
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH v5 6/9] OF: i2c: Add OF notifier handler
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
` (4 preceding siblings ...)
2014-05-28 12:46 ` [PATCH v5 5/9] of: i2c: Export single device registration method Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 7/9] of: spi: Export single device registration method and accessors Pantelis Antoniou
` (3 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
Add OF notifier handler needed for creating/destroying i2c devices
according to dynamic runtime changes in the DT live tree.
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/i2c/i2c-core.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 6e47f73..ce78388 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -1083,7 +1083,6 @@ struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
return i2c_verify_client(dev);
}
EXPORT_SYMBOL(of_find_i2c_device_by_node);
-
/* must call put_device() when done with returned i2c_adapter device */
struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
{
@@ -1097,6 +1096,7 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
return i2c_verify_adapter(dev);
}
EXPORT_SYMBOL(of_find_i2c_adapter_by_node);
+
#else
static void of_i2c_register_devices(struct i2c_adapter *adap) { }
#endif /* CONFIG_OF */
@@ -1675,6 +1675,57 @@ void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg)
}
EXPORT_SYMBOL(i2c_clients_command);
+#if IS_ENABLED(CONFIG_OF)
+
+static int of_i2c_notify(struct notifier_block *nb,
+ unsigned long action, void *arg)
+{
+ struct device_node *dn;
+ struct i2c_adapter *adap;
+ struct i2c_client *client;
+
+ if (action == OF_RECONFIG_DYNAMIC_CREATE_DEV) {
+
+ dn = arg;
+
+ adap = of_find_i2c_adapter_by_node(dn->parent);
+ if (adap == NULL)
+ return NOTIFY_OK; /* not for us */
+
+ client = of_i2c_register_device(adap, dn);
+ put_device(&adap->dev);
+
+ if (IS_ERR(client)) {
+ pr_err("%s: failed to create for '%s'\n",
+ __func__, dn->full_name);
+ return notifier_from_errno(PTR_ERR(client));
+ }
+
+ } else if (action == OF_RECONFIG_DYNAMIC_DESTROY_DEV) {
+
+ dn = arg;
+
+ /* find our device by node */
+ client = of_find_i2c_device_by_node(dn);
+ if (client == NULL)
+ return NOTIFY_OK; /* no? not meant for us */
+
+ /* unregister takes one ref away */
+ i2c_unregister_device(client);
+
+ /* and put the reference of the find */
+ put_device(&client->dev);
+
+ } else
+ return NOTIFY_OK;
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block i2c_of_notifier;
+
+#endif
+
static int __init i2c_init(void)
{
int retval;
@@ -1692,8 +1743,19 @@ static int __init i2c_init(void)
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
- return 0;
+#if IS_ENABLED(CONFIG_OF)
+ i2c_of_notifier.notifier_call = of_i2c_notify;
+ retval = of_reconfig_notifier_register(&i2c_of_notifier);
+ if (retval)
+ goto notifier_err;
+#endif
+
+ return 0;
+#if IS_ENABLED(CONFIG_OF)
+notifier_err:
+ i2c_del_driver(&dummy_driver);
+#endif
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH v5 7/9] of: spi: Export single device registration method and accessors
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
` (5 preceding siblings ...)
2014-05-28 12:46 ` [PATCH v5 6/9] OF: i2c: Add OF notifier handler Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-07-01 11:27 ` Zhenghui Zhou
2014-05-28 12:46 ` [PATCH v5 8/9] OF: spi: Add OF notifier handler Pantelis Antoniou
` (2 subsequent siblings)
9 siblings, 1 reply; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
Dynamically inserting spi device nodes requires the use of a single
device registration method. Rework and export it.
Methods to lookup a device/master using a device node are added
as well, of_find_spi_master_by_node() & of_find_spi_device_by_node().
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/spi/spi.c | 259 ++++++++++++++++++++++++++++++++----------------------
1 file changed, 156 insertions(+), 103 deletions(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d4f9670..a98d236 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1200,6 +1200,121 @@ err_init_queue:
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_OF)
+
+static struct spi_device *
+of_register_spi_device(struct spi_master *master, struct device_node *node)
+{
+ struct spi_device *spi;
+ struct device_node *nc;
+ int err;
+ u32 value;
+
+ /* Alloc an spi_device */
+ spi = spi_alloc_device(master);
+ if (!spi) {
+ dev_err(&master->dev, "spi_device alloc error for %s\n",
+ nc->full_name);
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ /* Select device driver */
+ err = of_modalias_node(nc, spi->modalias,
+ sizeof(spi->modalias));
+ if (err) {
+ dev_err(&master->dev, "cannot find modalias for %s\n",
+ nc->full_name);
+ goto err_out;
+ }
+
+ /* Device address */
+ err = of_property_read_u32(nc, "reg", &value);
+ if (err) {
+ dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
+ nc->full_name, err);
+ goto err_out;
+ }
+ spi->chip_select = value;
+
+ /* Mode (clock phase/polarity/etc.) */
+ if (of_find_property(nc, "spi-cpha", NULL))
+ spi->mode |= SPI_CPHA;
+ if (of_find_property(nc, "spi-cpol", NULL))
+ spi->mode |= SPI_CPOL;
+ if (of_find_property(nc, "spi-cs-high", NULL))
+ spi->mode |= SPI_CS_HIGH;
+ if (of_find_property(nc, "spi-3wire", NULL))
+ spi->mode |= SPI_3WIRE;
+
+ /* Device DUAL/QUAD mode */
+ if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
+ switch (value) {
+ case 1:
+ break;
+ case 2:
+ spi->mode |= SPI_TX_DUAL;
+ break;
+ case 4:
+ spi->mode |= SPI_TX_QUAD;
+ break;
+ default:
+ dev_warn(&master->dev,
+ "spi-tx-bus-width %d not supported\n",
+ value);
+ break;
+ }
+ }
+
+ if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
+ switch (value) {
+ case 1:
+ break;
+ case 2:
+ spi->mode |= SPI_RX_DUAL;
+ break;
+ case 4:
+ spi->mode |= SPI_RX_QUAD;
+ break;
+ default:
+ dev_warn(&master->dev,
+ "spi-rx-bus-width %d not supported\n",
+ value);
+ break;
+ }
+ }
+
+ /* Device speed */
+ err = of_property_read_u32(nc, "spi-max-frequency", &value);
+ if (err) {
+ dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
+ nc->full_name, err);
+ goto err_out;
+ }
+ spi->max_speed_hz = value;
+
+ /* IRQ */
+ spi->irq = irq_of_parse_and_map(nc, 0);
+
+ /* Store a pointer to the node in the device structure */
+ of_node_get(nc);
+ spi->dev.of_node = nc;
+
+ /* Register the new device */
+ request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias);
+ err = spi_add_device(spi);
+ if (err) {
+ dev_err(&master->dev, "spi_device register error %s\n",
+ nc->full_name);
+ goto err_out;
+ }
+
+ return spi;
+
+err_out:
+ spi_dev_put(spi);
+ return ERR_PTR(err);
+}
+
/**
* of_register_spi_devices() - Register child devices onto the SPI bus
* @master: Pointer to spi_master device
@@ -1209,120 +1324,58 @@ err_init_queue:
*/
static void of_register_spi_devices(struct spi_master *master)
{
- struct spi_device *spi;
struct device_node *nc;
- int rc;
- u32 value;
if (!master->dev.of_node)
return;
- for_each_available_child_of_node(master->dev.of_node, nc) {
- /* Alloc an spi_device */
- spi = spi_alloc_device(master);
- if (!spi) {
- dev_err(&master->dev, "spi_device alloc error for %s\n",
- nc->full_name);
- spi_dev_put(spi);
- continue;
- }
+ for_each_available_child_of_node(master->dev.of_node, nc)
+ of_register_spi_device(master, nc);
+}
- /* Select device driver */
- if (of_modalias_node(nc, spi->modalias,
- sizeof(spi->modalias)) < 0) {
- dev_err(&master->dev, "cannot find modalias for %s\n",
- nc->full_name);
- spi_dev_put(spi);
- continue;
- }
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
- /* Device address */
- rc = of_property_read_u32(nc, "reg", &value);
- if (rc) {
- dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
- nc->full_name, rc);
- spi_dev_put(spi);
- continue;
- }
- spi->chip_select = value;
-
- /* Mode (clock phase/polarity/etc.) */
- if (of_find_property(nc, "spi-cpha", NULL))
- spi->mode |= SPI_CPHA;
- if (of_find_property(nc, "spi-cpol", NULL))
- spi->mode |= SPI_CPOL;
- if (of_find_property(nc, "spi-cs-high", NULL))
- spi->mode |= SPI_CS_HIGH;
- if (of_find_property(nc, "spi-3wire", NULL))
- spi->mode |= SPI_3WIRE;
- if (of_find_property(nc, "spi-lsb-first", NULL))
- spi->mode |= SPI_LSB_FIRST;
-
- /* Device DUAL/QUAD mode */
- if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
- switch (value) {
- case 1:
- break;
- case 2:
- spi->mode |= SPI_TX_DUAL;
- break;
- case 4:
- spi->mode |= SPI_TX_QUAD;
- break;
- default:
- dev_warn(&master->dev,
- "spi-tx-bus-width %d not supported\n",
- value);
- break;
- }
- }
+/* bah; the match functions differ just by const-ness */
+static int of_dev_node_match_const(struct device *dev, const void *data)
+{
+ return dev->of_node == data;
+}
- if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
- switch (value) {
- case 1:
- break;
- case 2:
- spi->mode |= SPI_RX_DUAL;
- break;
- case 4:
- spi->mode |= SPI_RX_QUAD;
- break;
- default:
- dev_warn(&master->dev,
- "spi-rx-bus-width %d not supported\n",
- value);
- break;
- }
- }
+/* must call put_device() when done with returned spi_device device */
+struct spi_device *of_find_spi_device_by_node(struct device_node *node)
+{
+ struct device *dev;
- /* Device speed */
- rc = of_property_read_u32(nc, "spi-max-frequency", &value);
- if (rc) {
- dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
- nc->full_name, rc);
- spi_dev_put(spi);
- continue;
- }
- spi->max_speed_hz = value;
-
- /* IRQ */
- spi->irq = irq_of_parse_and_map(nc, 0);
-
- /* Store a pointer to the node in the device structure */
- of_node_get(nc);
- spi->dev.of_node = nc;
-
- /* Register the new device */
- request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias);
- rc = spi_add_device(spi);
- if (rc) {
- dev_err(&master->dev, "spi_device register error %s\n",
- nc->full_name);
- spi_dev_put(spi);
- }
+ dev = bus_find_device(&spi_bus_type, NULL, node,
+ of_dev_node_match);
+ if (!dev)
+ return NULL;
- }
+ return to_spi_device(dev);
}
+EXPORT_SYMBOL(of_find_spi_device_by_node);
+
+/* forward decl */
+static struct class spi_master_class;
+
+/* the spi masters are not using spi_bus, so we find it with another way */
+struct spi_master *of_find_spi_master_by_node(struct device_node *node)
+{
+ struct device *dev;
+
+ dev = class_find_device(&spi_master_class, NULL, node,
+ of_dev_node_match_const);
+ if (!dev)
+ return NULL;
+
+ /* reference got in class_find_device */
+ return container_of(dev, struct spi_master, dev);
+}
+EXPORT_SYMBOL(of_find_spi_master_by_node);
+
#else
static void of_register_spi_devices(struct spi_master *master) { }
#endif
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH v5 8/9] OF: spi: Add OF notifier handler
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
` (6 preceding siblings ...)
2014-05-28 12:46 ` [PATCH v5 7/9] of: spi: Export single device registration method and accessors Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-05-28 12:46 ` [PATCH v5 9/9] OF: selftest: Add overlay self-test support Pantelis Antoniou
2014-05-29 13:56 ` [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Grant Likely
9 siblings, 0 replies; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
Add OF notifier handler needed for creating/destroying spi devices
according to dynamic runtime changes in the DT live tree.
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/spi/spi.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index a98d236..3d8feca 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2336,6 +2336,57 @@ EXPORT_SYMBOL_GPL(spi_write_then_read);
/*-------------------------------------------------------------------------*/
+#if IS_ENABLED(CONFIG_OF)
+
+static int of_spi_notify(struct notifier_block *nb,
+ unsigned long action, void *arg)
+{
+ struct device_node *dn;
+ struct spi_master *master;
+ struct spi_device *spi;
+
+ if (action == OF_RECONFIG_DYNAMIC_CREATE_DEV) {
+
+ dn = arg;
+
+ master = of_find_spi_master_by_node(dn->parent);
+ if (master == NULL)
+ return NOTIFY_OK; /* not for us */
+
+ spi = of_register_spi_device(master, dn);
+ put_device(&master->dev);
+
+ if (IS_ERR(spi)) {
+ pr_err("%s: failed to create for '%s'\n",
+ __func__, dn->full_name);
+ return notifier_from_errno(PTR_ERR(spi));
+ }
+
+ } else if (action == OF_RECONFIG_DYNAMIC_DESTROY_DEV) {
+
+ dn = arg;
+
+ /* find our device by node */
+ spi = of_find_spi_device_by_node(dn);
+ if (spi == NULL)
+ return NOTIFY_OK; /* no? not meant for us */
+
+ /* unregister takes one ref away */
+ spi_unregister_device(spi);
+
+ /* and put the reference of the find */
+ put_device(&spi->dev);
+
+ } else
+ return NOTIFY_OK;
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block spi_of_notifier;
+
+#endif
+
static int __init spi_init(void)
{
int status;
@@ -2353,8 +2404,19 @@ static int __init spi_init(void)
status = class_register(&spi_master_class);
if (status < 0)
goto err2;
- return 0;
+#if IS_ENABLED(CONFIG_OF)
+ spi_of_notifier.notifier_call = of_spi_notify;
+ status = of_reconfig_notifier_register(&spi_of_notifier);
+ if (status)
+ goto err3;
+#endif
+
+ return 0;
+#if IS_ENABLED(CONFIG_OF)
+err3:
+ class_unregister(&spi_master_class);
+#endif
err2:
bus_unregister(&spi_bus_type);
err1:
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH v5 9/9] OF: selftest: Add overlay self-test support.
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
` (7 preceding siblings ...)
2014-05-28 12:46 ` [PATCH v5 8/9] OF: spi: Add OF notifier handler Pantelis Antoniou
@ 2014-05-28 12:46 ` Pantelis Antoniou
2014-05-29 13:56 ` [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Grant Likely
9 siblings, 0 replies; 13+ messages in thread
From: Pantelis Antoniou @ 2014-05-28 12:46 UTC (permalink / raw)
To: Grant Likely
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
This patch adds overlay tests to the OF selftest.
It tests overlay device addition/removal and whether
the apply revert sequence is correct.
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
Documentation/devicetree/bindings/selftest.txt | 14 +
drivers/of/selftest.c | 481 +++++++++++++++++++++++++
drivers/of/testcase-data/testcases.dtsi | 1 +
drivers/of/testcase-data/tests-overlay.dtsi | 180 +++++++++
4 files changed, 676 insertions(+)
create mode 100644 Documentation/devicetree/bindings/selftest.txt
create mode 100644 drivers/of/testcase-data/tests-overlay.dtsi
diff --git a/Documentation/devicetree/bindings/selftest.txt b/Documentation/devicetree/bindings/selftest.txt
new file mode 100644
index 0000000..0f92a22
--- /dev/null
+++ b/Documentation/devicetree/bindings/selftest.txt
@@ -0,0 +1,14 @@
+* OF selftest platform device
+
+** selftest
+
+Required properties:
+- compatible: must be "selftest"
+
+All other properties are optional.
+
+Example:
+ selftest {
+ compatible = "selftest";
+ status = "okay";
+ };
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c
index 077314e..1c74676 100644
--- a/drivers/of/selftest.c
+++ b/drivers/of/selftest.c
@@ -15,6 +15,8 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
static struct selftest_results {
int passed;
@@ -517,6 +519,484 @@ static void __init of_selftest_platform_populate(void)
}
}
+#ifdef CONFIG_OF_OVERLAY
+
+static int selftest_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ if (np == NULL) {
+ dev_err(dev, "No OF data for device\n");
+ return -EINVAL;
+
+ }
+
+ dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
+ return 0;
+}
+
+static int selftest_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
+ return 0;
+}
+
+static struct of_device_id selftest_match[] = {
+ { .compatible = "selftest", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altera_jtaguart_match);
+
+static struct platform_driver selftest_driver = {
+ .probe = selftest_probe,
+ .remove = selftest_remove,
+ .driver = {
+ .name = "selftest",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(selftest_match),
+ },
+};
+
+/* get the platform device instantiated at the path */
+static struct platform_device *of_path_to_platform_device(const char *path)
+{
+ struct device_node *np;
+ struct platform_device *pdev;
+
+ np = of_find_node_by_path(path);
+ if (np == NULL)
+ return NULL;
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+
+ return pdev;
+}
+
+/* find out if a platform device exists at that path */
+static int of_path_platform_device_exists(const char *path)
+{
+ struct platform_device *pdev;
+
+ pdev = of_path_to_platform_device(path);
+ platform_device_put(pdev);
+ return pdev != NULL;
+}
+
+static const char *selftest_path(int nr)
+{
+ static char buf[256];
+
+ snprintf(buf, sizeof(buf) - 1,
+ "/testcase-data/overlay-node/test-bus/test-selftest%d", nr);
+ buf[sizeof(buf) - 1] = '\0';
+
+ return buf;
+}
+
+static const char *overlay_path(int nr)
+{
+ static char buf[256];
+
+ snprintf(buf, sizeof(buf) - 1,
+ "/testcase-data/overlay%d", nr);
+ buf[sizeof(buf) - 1] = '\0';
+
+ return buf;
+}
+
+static const char *bus_path = "/testcase-data/overlay-node/test-bus";
+
+static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr,
+ int *overlay_id)
+{
+ struct device_node *np = NULL;
+ int ret, id = -1;
+
+ np = of_find_node_by_path(overlay_path(overlay_nr));
+ if (np == NULL) {
+ selftest(0, "could not find overlay node @\"%s\"\n",
+ overlay_path(overlay_nr));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = of_overlay_create(np);
+ if (ret < 0) {
+ selftest(0, "could not create overlay from \"%s\"\n",
+ overlay_path(overlay_nr));
+ goto out;
+ }
+ id = ret;
+
+ ret = 0;
+
+out:
+ of_node_put(np);
+
+ if (overlay_id)
+ *overlay_id = id;
+
+ return ret;
+}
+
+/* apply an overlay while checking before and after states */
+static int of_selftest_apply_overlay_check(int overlay_nr, int selftest_nr,
+ int before, int after)
+{
+ int ret;
+
+ /* selftest device must not be in before state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !before ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, NULL);
+ if (ret != 0) {
+ /* of_selftest_apply_overlay already called selftest() */
+ return ret;
+ }
+
+ /* selftest device must be to set to after state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != after) {
+ selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !after ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* apply an overlay and then revert it while checking before, after states */
+static int of_selftest_apply_revert_overlay_check(int overlay_nr,
+ int selftest_nr, int before, int after)
+{
+ int ret, ov_id;
+
+ /* selftest device must be in before state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !before ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ /* apply the overlay */
+ ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, &ov_id);
+ if (ret != 0) {
+ /* of_selftest_apply_overlay already called selftest() */
+ return ret;
+ }
+
+ /* selftest device must be in after state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != after) {
+ selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !after ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ ret = of_overlay_destroy(ov_id);
+ if (ret != 0) {
+ selftest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr));
+ return ret;
+ }
+
+ /* selftest device must be again in before state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !before ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* test activation of device */
+static void of_selftest_overlay_0(void)
+{
+ int ret;
+
+ /* device should enable */
+ ret = of_selftest_apply_overlay_check(0, 0, 0, 1);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 0);
+}
+
+/* test deactivation of device */
+static void of_selftest_overlay_1(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_overlay_check(1, 1, 1, 0);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 1);
+}
+
+/* test activation of device */
+static void of_selftest_overlay_2(void)
+{
+ int ret;
+
+ /* device should enable */
+ ret = of_selftest_apply_overlay_check(2, 2, 0, 1);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 2);
+}
+
+/* test deactivation of device */
+static void of_selftest_overlay_3(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_overlay_check(3, 3, 1, 0);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 3);
+}
+
+/* test activation of a full device node */
+static void of_selftest_overlay_4(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_overlay_check(4, 4, 0, 1);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 4);
+}
+
+/* test overlay apply/revert sequence */
+static void of_selftest_overlay_5(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_revert_overlay_check(5, 5, 0, 1);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 5);
+}
+
+/* test overlay application in sequence */
+static void of_selftest_overlay_6(void)
+{
+ struct device_node *np;
+ int ret, i, ov_id[2];
+ int overlay_nr = 6, selftest_nr = 6;
+ int before = 0, after = 1;
+
+ /* selftest device must be in before state */
+ for (i = 0; i < 2; i++) {
+ if (of_path_platform_device_exists(
+ selftest_path(selftest_nr + i))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr + i),
+ !before ? "enabled" : "disabled");
+ return;
+ }
+ }
+
+ /* apply the overlays */
+ for (i = 0; i < 2; i++) {
+
+ np = of_find_node_by_path(overlay_path(overlay_nr + i));
+ if (np == NULL) {
+ selftest(0, "could not find overlay node @\"%s\"\n",
+ overlay_path(overlay_nr + i));
+ return;
+ }
+
+ ret = of_overlay_create(np);
+ if (ret < 0) {
+ selftest(0, "could not create overlay from \"%s\"\n",
+ overlay_path(overlay_nr + i));
+ return;
+ }
+ ov_id[i] = ret;
+ }
+
+ for (i = 0; i < 2; i++) {
+ /* selftest device must be in after state */
+ if (of_path_platform_device_exists(
+ selftest_path(selftest_nr + i))
+ != after) {
+ selftest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr + i),
+ !after ? "enabled" : "disabled");
+ return;
+ }
+ }
+
+ for (i = 1; i >= 0; i--) {
+ ret = of_overlay_destroy(ov_id[i]);
+ if (ret != 0) {
+ selftest(0, "overlay @\"%s\" failed destroy @\"%s\"\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr + i));
+ return;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ /* selftest device must be again in before state */
+ if (of_path_platform_device_exists(
+ selftest_path(selftest_nr + i))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr + i),
+ !before ? "enabled" : "disabled");
+ return;
+ }
+ }
+
+ selftest(1, "overlay test %d passed\n", 6);
+}
+
+/* test overlay application in sequence */
+static void of_selftest_overlay_8(void)
+{
+ struct device_node *np;
+ int ret, i, ov_id[2];
+ int overlay_nr = 8, selftest_nr = 8;
+
+ /* we don't care about device state in this test */
+
+ /* apply the overlays */
+ for (i = 0; i < 2; i++) {
+
+ np = of_find_node_by_path(overlay_path(overlay_nr + i));
+ if (np == NULL) {
+ selftest(0, "could not find overlay node @\"%s\"\n",
+ overlay_path(overlay_nr + i));
+ return;
+ }
+
+ ret = of_overlay_create(np);
+ if (ret < 0) {
+ selftest(0, "could not create overlay from \"%s\"\n",
+ overlay_path(overlay_nr + i));
+ return;
+ }
+ ov_id[i] = ret;
+ }
+
+ /* now try to remove first overlay (it should fail) */
+ ret = of_overlay_destroy(ov_id[0]);
+ if (ret == 0) {
+ selftest(0, "overlay @\"%s\" was destroyed @\"%s\"\n",
+ overlay_path(overlay_nr + 0),
+ selftest_path(selftest_nr));
+ return;
+ }
+
+ /* removing them in order should work */
+ for (i = 1; i >= 0; i--) {
+ ret = of_overlay_destroy(ov_id[i]);
+ if (ret != 0) {
+ selftest(0, "overlay @\"%s\" not destroyed @\"%s\"\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr));
+ return;
+ }
+ }
+
+ selftest(1, "overlay test %d passed\n", 8);
+}
+
+static void __init of_selftest_overlay(void)
+{
+ struct device_node *bus_np = NULL;
+ int ret;
+
+ ret = platform_driver_register(&selftest_driver);
+ if (ret != 0) {
+ selftest(0, "could not register selftest driver\n");
+ goto out;
+ }
+
+ bus_np = of_find_node_by_path(bus_path);
+ if (bus_np == NULL) {
+ selftest(0, "could not find bus_path \"%s\"\n", bus_path);
+ goto out;
+ }
+
+ ret = of_platform_populate(bus_np, of_default_bus_match_table,
+ NULL, NULL);
+ if (ret != 0) {
+ selftest(0, "could not populate bus @ \"%s\"\n", bus_path);
+ goto out;
+ }
+
+ if (!of_path_platform_device_exists(selftest_path(100))) {
+ selftest(0, "could not find selftest0 @ \"%s\"\n",
+ selftest_path(100));
+ goto out;
+ }
+
+ if (of_path_platform_device_exists(selftest_path(101))) {
+ selftest(0, "selftest1 @ \"%s\" should not exist\n",
+ selftest_path(101));
+ goto out;
+ }
+
+ selftest(1, "basic infrastructure of overlays passed");
+
+ /* tests in sequence */
+ of_selftest_overlay_0();
+ of_selftest_overlay_1();
+ of_selftest_overlay_2();
+ of_selftest_overlay_3();
+ of_selftest_overlay_4();
+ of_selftest_overlay_5();
+ of_selftest_overlay_6();
+ of_selftest_overlay_8();
+
+out:
+ of_node_put(bus_np);
+}
+
+#else
+static inline void __init of_selftest_overlay(void) { }
+#endif
+
static int __init of_selftest(void)
{
struct device_node *np;
@@ -537,6 +1017,7 @@ static int __init of_selftest(void)
of_selftest_parse_interrupts_extended();
of_selftest_match_node();
of_selftest_platform_populate();
+ of_selftest_overlay();
pr_info("end of selftest - %i passed, %i failed\n",
selftest_results.passed, selftest_results.failed);
return 0;
diff --git a/drivers/of/testcase-data/testcases.dtsi b/drivers/of/testcase-data/testcases.dtsi
index 6d8d980a..a54fd68 100644
--- a/drivers/of/testcase-data/testcases.dtsi
+++ b/drivers/of/testcase-data/testcases.dtsi
@@ -2,3 +2,4 @@
#include "tests-interrupts.dtsi"
#include "tests-match.dtsi"
#include "tests-platform.dtsi"
+#include "tests-overlay.dtsi"
diff --git a/drivers/of/testcase-data/tests-overlay.dtsi b/drivers/of/testcase-data/tests-overlay.dtsi
new file mode 100644
index 0000000..75976da
--- /dev/null
+++ b/drivers/of/testcase-data/tests-overlay.dtsi
@@ -0,0 +1,180 @@
+
+/ {
+ testcase-data {
+ overlay-node {
+
+ /* test bus */
+ selftestbus: test-bus {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ selftest100: test-selftest100 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <100>;
+ };
+
+ selftest101: test-selftest101 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <101>;
+ };
+
+ selftest0: test-selftest0 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <0>;
+ };
+
+ selftest1: test-selftest1 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <1>;
+ };
+
+ selftest2: test-selftest2 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <2>;
+ };
+
+ selftest3: test-selftest3 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <3>;
+ };
+
+ selftest5: test-selftest5 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <5>;
+ };
+
+ selftest6: test-selftest6 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <6>;
+ };
+
+ selftest7: test-selftest7 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <7>;
+ };
+
+ selftest8: test-selftest8 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <8>;
+ };
+ };
+ };
+
+ /* test enable using absolute target path */
+ overlay0 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest0";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+
+ /* test disable using absolute target path */
+ overlay1 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest1";
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+ };
+
+ /* test enable using label */
+ overlay2 {
+ fragment@0 {
+ target = <&selftest2>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+
+ /* test disable using label */
+ overlay3 {
+ fragment@0 {
+ target = <&selftest3>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+ };
+
+ /* test insertion of a full node */
+ overlay4 {
+ fragment@0 {
+ target = <&selftestbus>;
+ __overlay__ {
+
+ /* suppress DTC warning */
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ test-selftest4 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <4>;
+ };
+ };
+ };
+ };
+
+ /* test overlay apply revert */
+ overlay5 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest5";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+
+ /* test overlays application and removal in sequence */
+ overlay6 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest6";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+ overlay7 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest7";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+
+ /* test overlays application and removal in bad sequence */
+ overlay8 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+ overlay9 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
+ __overlay__ {
+ property-foo = "bar";
+ };
+ };
+ };
+
+ };
+};
--
1.7.12
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays
2014-05-28 12:46 [PATCH v5 0/9] Introducing (yet again) Device Tree Overlays Pantelis Antoniou
` (8 preceding siblings ...)
2014-05-28 12:46 ` [PATCH v5 9/9] OF: selftest: Add overlay self-test support Pantelis Antoniou
@ 2014-05-29 13:56 ` Grant Likely
9 siblings, 0 replies; 13+ messages in thread
From: Grant Likely @ 2014-05-29 13:56 UTC (permalink / raw)
To: Pantelis Antoniou
Cc: Rob Herring, Stephen Warren, Matt Porter, Koen Kooi,
Alison Chaiken, Dinh Nguyen, Jan Lubbe, Alexander Sverdlin,
Michael Stickel, Guenter Roeck, Dirk Behme, Alan Tull,
Sascha Hauer, Michael Bohan, Ionut Nicu, Michal Simek,
Matt Ranostay, devicetree, linux-kernel, Pete Popov, Dan Malek,
Georgi Vlaev, Pantelis Antoniou, Pantelis Antoniou
On Wed, 28 May 2014 15:46:24 +0300, Pantelis Antoniou <pantelis.antoniou@konsulko.com> wrote:
> The following patchset introduces Device Tree overlays, a method
> of dynamically altering the kernel's live Device Tree, along with
> a generic interface to use it in a board agnostic manner.
>
> It is against linux-next as of today 28/5/2014
> "134a9c91121b1081bcf737045a11b0510c5c9dfb - Add linux-next specific files for 20140528"
>
> It relies on the following previously submitted patches/patchsets:
>
> * OF: Add [__]of_find_node_by_full_name
> * OF: Utility helper functions for dynamic nodes
In the next version, can you include the patches you depend on that
aren't already in my tree?
> To compile overlays you need the DTC compiler patch
>
> * "dtc: Dynamic symbols & fixup support (v2)"
>
> Changes since V4:
> * New API of_overlay_create/destroy being able to support stacked
> overlays correctly.
> * Removed use of notifiers internally.
> * Removed own-grown bus handler notifiers; using already in-place DT
> notification infrastructure.
> * Split SPI notifier patch to one patch of generic changes and one
> for DT overlay notifier.
> * Removed unused overlay depth feature.
> * Updated documentation.
> * Moved configfs based interface to using the new API.
> * Added overlay removal stacking tests.
Good changes. This version is considerably better than the past one.
I've still got comments, but I'm a lot more comfortable with the
approach.
> Changes since V3:
> * Added overlay self-tests.
> * Fix bug in of_init_overlay_info (wrong sizeof)
> * Platform bus handler handles parent_pdev == NULL
> * of_resolve fixes according to comments by robh
> + changed if (foo == NULL) to if (!foo)
> + changed if (foo != NULL) to if (foo)
> + drivers/of/Kconfig added dep on OF && !SPARC
> + convert to using be32_to_cpup
> + u32 -> __be32 when modifying property values
> + cosmetic fixes
>
> Changes since V2:
> * Use of a configfs board agnostic overlay method
> * Use of per bus handlers instead of hardcoded behaviour
> * Optional target-path overlay target, which allows one to use standard
> DTBs without resolution options.
>
> Changes since V1:
>
> * Removal of any bits related to a specific board (beaglebone).
> * Introduced a platform agnostic interface using /proc/device-tree-overlay
> * Various bug fixes related to i2c device handling have been squashed in.
>
> Pantelis Antoniou (9):
> OF: Introduce Device Tree resolve support.
> OF: Introduce DT overlay support.
> OF: DT-Overlay configfs interface
> OF: platform: Add OF notifier handler
> of: i2c: Export single device registration method
> OF: i2c: Add OF notifier handler
> of: spi: Export single device registration method and accessors
> OF: spi: Add OF notifier handler
> OF: selftest: Add overlay self-test support.
>
> Documentation/devicetree/bindings/selftest.txt | 14 +
> .../devicetree/dynamic-resolution-notes.txt | 25 +
> Documentation/devicetree/overlay-notes.txt | 160 ++++
> drivers/base/platform.c | 69 +-
> drivers/i2c/i2c-core.c | 165 +++-
> drivers/of/Kconfig | 20 +
> drivers/of/Makefile | 3 +
> drivers/of/configfs.c | 251 +++++
> drivers/of/overlay.c | 1001 ++++++++++++++++++++
> drivers/of/resolver.c | 378 ++++++++
> drivers/of/selftest.c | 481 ++++++++++
> drivers/of/testcase-data/testcases.dtsi | 1 +
> drivers/of/testcase-data/tests-overlay.dtsi | 180 ++++
> drivers/spi/spi.c | 323 +++++--
> include/linux/i2c.h | 10 +
> include/linux/of.h | 167 ++++
> 16 files changed, 3094 insertions(+), 154 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/selftest.txt
> create mode 100644 Documentation/devicetree/dynamic-resolution-notes.txt
> create mode 100644 Documentation/devicetree/overlay-notes.txt
> create mode 100644 drivers/of/configfs.c
> create mode 100644 drivers/of/overlay.c
> create mode 100644 drivers/of/resolver.c
> create mode 100644 drivers/of/testcase-data/tests-overlay.dtsi
>
> --
> 1.7.12
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply [flat|nested] 13+ messages in thread