From: Ionut Nicu <ioan.nicu.ext-OYasijW0DpE@public.gmane.org>
To: Alexander Sverdlin <alexander.sverdlin-OYasijW0DpE@public.gmane.org>
Cc: ext Pantelis Antoniou
<panto-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>,
Grant Likely
<grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>,
Rob Herring <robherring2-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>,
Matt Porter <matt.porter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>,
Koen Kooi
<koen-QLwJDigV5abLmq1fohREcCpxlwaOVQ5f@public.gmane.org>,
Alison Chaiken
<Alison_Chaiken-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>,
Dinh Nguyen <dinh.linux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
Jan Lubbe <jluebbe-H4yykcOXDpCzQB+pC5nmwQ@public.gmane.org>,
Michael Stickel <ms-g5CePrrZ5ROELgA04lAiVw@public.gmane.org>,
Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>,
Dirk Behme <dirk.behme-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
Alan Tull
<delicious.quinoa-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>,
Michael Bohan <mbohan-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>,
Michal Simek <monstr-pSz03upnqPeHXe+LvDLADg@public.gmane.org>,
Matt Ranostay <mranostay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: [PATCH 1/3] OF: Introduce Device Tree resolve support.
Date: Wed, 06 Nov 2013 17:24:20 +0100 [thread overview]
Message-ID: <527A6D34.40407@nsn.com> (raw)
In-Reply-To: <527A6777.9080401-OYasijW0DpE@public.gmane.org>
Hi,
On 06.11.2013 16:59, Alexander Sverdlin wrote:
> Hi!
>
> On 05/11/13 19:41, ext Pantelis Antoniou 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.
>>
>> Signed-off-by: Pantelis Antoniou <panto-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
>
> Reviewed-by: Alexander Sverdlin <alexander.sverdlin-OYasijW0DpE@public.gmane.org>
>
I'm using this patch for a few months without any problems, so:
Tested-by: Ionut Nicu <ioan.nicu.ext-OYasijW0DpE@public.gmane.org>
>> ---
>> .../devicetree/dynamic-resolution-notes.txt | 25 ++
>> drivers/of/Kconfig | 9 +
>> drivers/of/Makefile | 1 +
>> drivers/of/resolver.c | 395 +++++++++++++++++++++
>> include/linux/of.h | 17 +
>> 5 files changed, 447 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 78cc760..2a00ae5 100644
>> --- a/drivers/of/Kconfig
>> +++ b/drivers/of/Kconfig
>> @@ -74,4 +74,13 @@ config OF_MTD
>> depends on MTD
>> def_bool y
>>
>> +config OF_RESOLVE
>> + bool "OF Dynamic resolution support"
>> + depends on OF
>> + select OF_DYNAMIC
>> + select OF_DEVICE
>> + help
>> + Enable OF dynamic resolution support. This allows you to
>> + load Device Tree object fragments are run time.
>> +
>> endmenu # OF
>> diff --git a/drivers/of/Makefile b/drivers/of/Makefile
>> index 9bc6d8c..93da457 100644
>> --- a/drivers/of/Makefile
>> +++ b/drivers/of/Makefile
>> @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o
>> 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_RESOLVE) += resolver.o
>> diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
>> new file mode 100644
>> index 0000000..da8cd34
>> --- /dev/null
>> +++ b/drivers/of/resolver.c
>> @@ -0,0 +1,395 @@
>> +/*
>> + * Functions for dealing with DT resolution
>> + *
>> + * Copyright (C) 2012 Pantelis Antoniou <panto-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
>> + * 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/of_fdt.h>
>> +#include <linux/string.h>
>> +#include <linux/ctype.h>
>> +#include <linux/errno.h>
>> +#include <linux/string.h>
>> +#include <linux/slab.h>
>> +
>> +/**
>> + * Find a subtree's maximum phandle value.
>> + */
>> +static phandle __of_get_tree_max_phandle(struct device_node *node,
>> + phandle max_phandle)
>> +{
>> + struct device_node *child;
>> +
>> + if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL &&
>> + node->phandle > max_phandle)
>> + max_phandle = node->phandle;
>> +
>> + __for_each_child_of_node(node, child)
>> + max_phandle = __of_get_tree_max_phandle(child, max_phandle);
>> +
>> + return max_phandle;
>> +}
>> +
>> +/**
>> + * Find live tree's maximum phandle value.
>> + */
>> +static phandle of_get_tree_max_phandle(void)
>> +{
>> + struct device_node *node;
>> + phandle phandle;
>> + unsigned long flags;
>> +
>> + /* get root node */
>> + node = of_find_node_by_path("/");
>> + if (node == NULL)
>> + return OF_PHANDLE_ILLEGAL;
>> +
>> + /* now search recursively */
>> + raw_spin_lock_irqsave(&devtree_lock, flags);
>> + phandle = __of_get_tree_max_phandle(node, 0);
>> + raw_spin_unlock_irqrestore(&devtree_lock, flags);
>> +
>> + of_node_put(node);
>> +
>> + 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_cpu(*(uint32_t *)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 == NULL)
>> + 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 == NULL) {
>> + 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 == NULL) {
>> + 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 == NULL) {
>> + pr_err("%s: Illegal symbol entry '%s' (2)\n",
>> + __func__, (char *)rprop->value);
>> + err = -EINVAL;
>> + goto err_fail;
>> + }
>> +
>> + *s++ = '\0';
>> + offset = simple_strtoul(s, NULL, 10);
>> +
>> + /* look into the resolve node for the full path */
>> + refnode = __of_find_node_by_full_name(node, nodestr);
>> + if (refnode == NULL) {
>> + 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 == NULL) {
>> + pr_err("%s: Could not find property '%s'\n",
>> + __func__, (char *)rprop->value);
>> + err = -ENOENT;
>> + goto err_fail;
>> + }
>> +
>> + phandle = be32_to_cpu(*(uint32_t *)
>> + (sprop->value + offset));
>> + *(uint32_t *)(sprop->value + offset) =
>> + cpu_to_be32(phandle + phandle_delta);
>> + }
>> +
>> + kfree(propval);
>> + }
>> +
>> + 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 == NULL ||
>> + !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 == NULL &&
>> + of_node_cmp(child->name, "__symbols__") == 0)
>> + resolve_sym = child;
>> +
>> + if (resolve_fix == NULL &&
>> + of_node_cmp(child->name, "__fixups__") == 0)
>> + resolve_fix = child;
>> +
>> + /* both found, don't bother anymore */
>> + if (resolve_sym != NULL && resolve_fix != NULL)
>> + break;
>> + }
>> +
>> + /* we do allow for the case where no fixups are needed */
>> + if (resolve_fix == NULL)
>> + goto merge_sym;
>> +
>> + /* we need to fixup, but no root symbols... */
>> + if (root_sym == NULL)
>> + return -EINVAL;
>> +
>> + 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 err_fail;
>> + }
>> +
>> + refnode = of_find_node_by_path(refpath);
>> + if (refnode == NULL) {
>> + pr_err("%s: Could not find node by path '%s'\n",
>> + __func__, refpath);
>> + err = -ENOENT;
>> + goto err_fail;
>> + }
>> +
>> + 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 == NULL) {
>> + pr_err("%s: Could not copy value of '%s'\n",
>> + __func__, rprop->name);
>> + err = -ENOMEM;
>> + goto err_fail;
>> + }
>> +
>> + 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 == NULL) {
>> + pr_err("%s: Illegal symbol "
>> + "entry '%s' (1)\n",
>> + __func__, (char *)rprop->value);
>> + kfree(propval);
>> + err = -EINVAL;
>> + goto err_fail;
>> + }
>> + *s++ = '\0';
>> +
>> + propstr = s;
>> + s = strchr(s, ':');
>> + if (s == NULL) {
>> + pr_err("%s: Illegal symbol "
>> + "entry '%s' (2)\n",
>> + __func__, (char *)rprop->value);
>> + kfree(propval);
>> + err = -EINVAL;
>> + goto err_fail;
>> + }
>> +
>> + *s++ = '\0';
>> + offset = simple_strtoul(s, NULL, 10);
>> +
>> + /* look into the resolve node for the full path */
>> + refnode = __of_find_node_by_full_name(resolve,
>> + nodestr);
>> + if (refnode == NULL) {
>> + pr_err("%s: Could not find refnode '%s'\n",
>> + __func__, (char *)rprop->value);
>> + kfree(propval);
>> + err = -ENOENT;
>> + goto err_fail;
>> + }
>> +
>> + /* now find the property */
>> + for_each_property_of_node(refnode, sprop) {
>> + if (of_prop_cmp(sprop->name, propstr) == 0)
>> + break;
>> + }
>> +
>> + if (sprop == NULL) {
>> + pr_err("%s: Could not find property '%s'\n",
>> + __func__, (char *)rprop->value);
>> + kfree(propval);
>> + err = -ENOENT;
>> + goto err_fail;
>> + }
>> +
>> + *(uint32_t *)(sprop->value + offset) =
>> + cpu_to_be32(phandle);
>> + }
>> +
>> + kfree(propval);
>> + }
>> +
>> +merge_sym:
>> +
>> + of_node_put(root_sym);
>> +
>> + return 0;
>> +
>> +err_fail:
>> +
>> + if (root_sym != NULL)
>> + of_node_put(root_sym);
>> +
>> + return err;
>> +}
>> diff --git a/include/linux/of.h b/include/linux/of.h
>> index 9d69bd2..22d42e5 100644
>> --- a/include/linux/of.h
>> +++ b/include/linux/of.h
>> @@ -721,4 +721,21 @@ static inline int of_multi_prop_cmp(const struct property *prop, const char *val
>>
>> #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 */
>>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
WARNING: multiple messages have this Message-ID (diff)
From: Ionut Nicu <ioan.nicu.ext@nsn.com>
To: Alexander Sverdlin <alexander.sverdlin@nsn.com>
Cc: ext Pantelis Antoniou <panto@antoniou-consulting.com>,
Grant Likely <grant.likely@secretlab.ca>,
Rob Herring <robherring2@gmail.com>,
Stephen Warren <swarren@wwwdotorg.org>,
Matt Porter <matt.porter@linaro.org>,
Koen Kooi <koen@dominion.thruhere.net>,
Alison Chaiken <Alison_Chaiken@mentor.com>,
Dinh Nguyen <dinh.linux@gmail.com>, Jan Lubbe <jluebbe@lasnet.de>,
Michael Stickel <ms@mycable.de>,
Guenter Roeck <linux@roeck-us.net>,
Dirk Behme <dirk.behme@gmail.com>,
Alan Tull <delicious.quinoa@gmail.com>,
Sascha Hauer <s.hauer@pengutronix.de>,
Michael Bohan <mbohan@codeaurora.org>,
Michal Simek <monstr@monstr.eu>,
Matt Ranostay <mranostay@gmail.com>,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/3] OF: Introduce Device Tree resolve support.
Date: Wed, 06 Nov 2013 17:24:20 +0100 [thread overview]
Message-ID: <527A6D34.40407@nsn.com> (raw)
In-Reply-To: <527A6777.9080401@nsn.com>
Hi,
On 06.11.2013 16:59, Alexander Sverdlin wrote:
> Hi!
>
> On 05/11/13 19:41, ext Pantelis Antoniou 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.
>>
>> Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
>
> Reviewed-by: Alexander Sverdlin <alexander.sverdlin@nsn.com>
>
I'm using this patch for a few months without any problems, so:
Tested-by: Ionut Nicu <ioan.nicu.ext@nsn.com>
>> ---
>> .../devicetree/dynamic-resolution-notes.txt | 25 ++
>> drivers/of/Kconfig | 9 +
>> drivers/of/Makefile | 1 +
>> drivers/of/resolver.c | 395 +++++++++++++++++++++
>> include/linux/of.h | 17 +
>> 5 files changed, 447 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 78cc760..2a00ae5 100644
>> --- a/drivers/of/Kconfig
>> +++ b/drivers/of/Kconfig
>> @@ -74,4 +74,13 @@ config OF_MTD
>> depends on MTD
>> def_bool y
>>
>> +config OF_RESOLVE
>> + bool "OF Dynamic resolution support"
>> + depends on OF
>> + select OF_DYNAMIC
>> + select OF_DEVICE
>> + help
>> + Enable OF dynamic resolution support. This allows you to
>> + load Device Tree object fragments are run time.
>> +
>> endmenu # OF
>> diff --git a/drivers/of/Makefile b/drivers/of/Makefile
>> index 9bc6d8c..93da457 100644
>> --- a/drivers/of/Makefile
>> +++ b/drivers/of/Makefile
>> @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o
>> 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_RESOLVE) += resolver.o
>> diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
>> new file mode 100644
>> index 0000000..da8cd34
>> --- /dev/null
>> +++ b/drivers/of/resolver.c
>> @@ -0,0 +1,395 @@
>> +/*
>> + * 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/of_fdt.h>
>> +#include <linux/string.h>
>> +#include <linux/ctype.h>
>> +#include <linux/errno.h>
>> +#include <linux/string.h>
>> +#include <linux/slab.h>
>> +
>> +/**
>> + * Find a subtree's maximum phandle value.
>> + */
>> +static phandle __of_get_tree_max_phandle(struct device_node *node,
>> + phandle max_phandle)
>> +{
>> + struct device_node *child;
>> +
>> + if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL &&
>> + node->phandle > max_phandle)
>> + max_phandle = node->phandle;
>> +
>> + __for_each_child_of_node(node, child)
>> + max_phandle = __of_get_tree_max_phandle(child, max_phandle);
>> +
>> + return max_phandle;
>> +}
>> +
>> +/**
>> + * Find live tree's maximum phandle value.
>> + */
>> +static phandle of_get_tree_max_phandle(void)
>> +{
>> + struct device_node *node;
>> + phandle phandle;
>> + unsigned long flags;
>> +
>> + /* get root node */
>> + node = of_find_node_by_path("/");
>> + if (node == NULL)
>> + return OF_PHANDLE_ILLEGAL;
>> +
>> + /* now search recursively */
>> + raw_spin_lock_irqsave(&devtree_lock, flags);
>> + phandle = __of_get_tree_max_phandle(node, 0);
>> + raw_spin_unlock_irqrestore(&devtree_lock, flags);
>> +
>> + of_node_put(node);
>> +
>> + 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_cpu(*(uint32_t *)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 == NULL)
>> + 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 == NULL) {
>> + 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 == NULL) {
>> + 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 == NULL) {
>> + pr_err("%s: Illegal symbol entry '%s' (2)\n",
>> + __func__, (char *)rprop->value);
>> + err = -EINVAL;
>> + goto err_fail;
>> + }
>> +
>> + *s++ = '\0';
>> + offset = simple_strtoul(s, NULL, 10);
>> +
>> + /* look into the resolve node for the full path */
>> + refnode = __of_find_node_by_full_name(node, nodestr);
>> + if (refnode == NULL) {
>> + 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 == NULL) {
>> + pr_err("%s: Could not find property '%s'\n",
>> + __func__, (char *)rprop->value);
>> + err = -ENOENT;
>> + goto err_fail;
>> + }
>> +
>> + phandle = be32_to_cpu(*(uint32_t *)
>> + (sprop->value + offset));
>> + *(uint32_t *)(sprop->value + offset) =
>> + cpu_to_be32(phandle + phandle_delta);
>> + }
>> +
>> + kfree(propval);
>> + }
>> +
>> + 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 == NULL ||
>> + !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 == NULL &&
>> + of_node_cmp(child->name, "__symbols__") == 0)
>> + resolve_sym = child;
>> +
>> + if (resolve_fix == NULL &&
>> + of_node_cmp(child->name, "__fixups__") == 0)
>> + resolve_fix = child;
>> +
>> + /* both found, don't bother anymore */
>> + if (resolve_sym != NULL && resolve_fix != NULL)
>> + break;
>> + }
>> +
>> + /* we do allow for the case where no fixups are needed */
>> + if (resolve_fix == NULL)
>> + goto merge_sym;
>> +
>> + /* we need to fixup, but no root symbols... */
>> + if (root_sym == NULL)
>> + return -EINVAL;
>> +
>> + 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 err_fail;
>> + }
>> +
>> + refnode = of_find_node_by_path(refpath);
>> + if (refnode == NULL) {
>> + pr_err("%s: Could not find node by path '%s'\n",
>> + __func__, refpath);
>> + err = -ENOENT;
>> + goto err_fail;
>> + }
>> +
>> + 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 == NULL) {
>> + pr_err("%s: Could not copy value of '%s'\n",
>> + __func__, rprop->name);
>> + err = -ENOMEM;
>> + goto err_fail;
>> + }
>> +
>> + 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 == NULL) {
>> + pr_err("%s: Illegal symbol "
>> + "entry '%s' (1)\n",
>> + __func__, (char *)rprop->value);
>> + kfree(propval);
>> + err = -EINVAL;
>> + goto err_fail;
>> + }
>> + *s++ = '\0';
>> +
>> + propstr = s;
>> + s = strchr(s, ':');
>> + if (s == NULL) {
>> + pr_err("%s: Illegal symbol "
>> + "entry '%s' (2)\n",
>> + __func__, (char *)rprop->value);
>> + kfree(propval);
>> + err = -EINVAL;
>> + goto err_fail;
>> + }
>> +
>> + *s++ = '\0';
>> + offset = simple_strtoul(s, NULL, 10);
>> +
>> + /* look into the resolve node for the full path */
>> + refnode = __of_find_node_by_full_name(resolve,
>> + nodestr);
>> + if (refnode == NULL) {
>> + pr_err("%s: Could not find refnode '%s'\n",
>> + __func__, (char *)rprop->value);
>> + kfree(propval);
>> + err = -ENOENT;
>> + goto err_fail;
>> + }
>> +
>> + /* now find the property */
>> + for_each_property_of_node(refnode, sprop) {
>> + if (of_prop_cmp(sprop->name, propstr) == 0)
>> + break;
>> + }
>> +
>> + if (sprop == NULL) {
>> + pr_err("%s: Could not find property '%s'\n",
>> + __func__, (char *)rprop->value);
>> + kfree(propval);
>> + err = -ENOENT;
>> + goto err_fail;
>> + }
>> +
>> + *(uint32_t *)(sprop->value + offset) =
>> + cpu_to_be32(phandle);
>> + }
>> +
>> + kfree(propval);
>> + }
>> +
>> +merge_sym:
>> +
>> + of_node_put(root_sym);
>> +
>> + return 0;
>> +
>> +err_fail:
>> +
>> + if (root_sym != NULL)
>> + of_node_put(root_sym);
>> +
>> + return err;
>> +}
>> diff --git a/include/linux/of.h b/include/linux/of.h
>> index 9d69bd2..22d42e5 100644
>> --- a/include/linux/of.h
>> +++ b/include/linux/of.h
>> @@ -721,4 +721,21 @@ static inline int of_multi_prop_cmp(const struct property *prop, const char *val
>>
>> #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 */
>>
>
next prev parent reply other threads:[~2013-11-06 16:24 UTC|newest]
Thread overview: 74+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-11-05 18:41 [PATCH 0/3 - V2] Introducing Device Tree Overlays Pantelis Antoniou
2013-11-05 18:41 ` Pantelis Antoniou
2013-11-05 18:41 ` [PATCH 1/3] OF: Introduce Device Tree resolve support Pantelis Antoniou
[not found] ` <1383676898-29819-2-git-send-email-panto-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
2013-11-06 15:59 ` Alexander Sverdlin
2013-11-06 15:59 ` Alexander Sverdlin
[not found] ` <527A6777.9080401-OYasijW0DpE@public.gmane.org>
2013-11-06 16:24 ` Ionut Nicu [this message]
2013-11-06 16:24 ` Ionut Nicu
2013-11-05 18:41 ` [PATCH 2/3] OF: Introduce DT overlay support Pantelis Antoniou
[not found] ` <1383676898-29819-3-git-send-email-panto-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
2013-11-06 16:00 ` Alexander Sverdlin
2013-11-06 16:00 ` Alexander Sverdlin
[not found] ` <527A67B7.8040808-OYasijW0DpE@public.gmane.org>
2013-11-06 16:26 ` Ionut Nicu
2013-11-06 16:26 ` Ionut Nicu
2013-11-06 20:41 ` Dinh Nguyen
2013-11-06 20:41 ` Dinh Nguyen
[not found] ` <527AA97F.2050802-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2013-11-07 7:10 ` Pantelis Antoniou
2013-11-07 7:10 ` Pantelis Antoniou
2013-11-05 18:41 ` [PATCH 3/3] DT: proc: Add runtime overlay interface in /proc Pantelis Antoniou
[not found] ` <1383676898-29819-4-git-send-email-panto-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
2013-11-06 9:51 ` Ionut Nicu
2013-11-06 9:51 ` Ionut Nicu
2013-11-06 9:57 ` Pantelis Antoniou
2013-11-07 23:38 ` delicious quinoa
2013-11-07 23:38 ` delicious quinoa
2013-11-08 7:12 ` Pantelis Antoniou
2013-11-06 19:10 ` Rob Herring
[not found] ` <CAL_Jsq+k9NoHxOaNujTG2=0MHm=s4Mh+xhXCZbhPG1EnHvo50A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2013-11-06 19:24 ` Pantelis Antoniou
2013-11-06 19:24 ` Pantelis Antoniou
[not found] ` <8EBC2D5B-74A4-4A85-8BEE-9EAEBFF045D4-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
2013-11-06 20:16 ` Matt Porter
2013-11-06 20:16 ` Matt Porter
2013-11-07 7:44 ` Pantelis Antoniou
2013-11-07 7:44 ` Pantelis Antoniou
[not found] ` <1383676898-29819-1-git-send-email-panto-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
2013-11-05 19:06 ` [PATCH 0/3 - V2] Introducing Device Tree Overlays Guenter Roeck
2013-11-05 19:06 ` Guenter Roeck
2013-11-06 0:01 ` Dinh Nguyen
[not found] ` <20131105190633.GA21178-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2013-11-06 0:15 ` Dinh Nguyen
2013-11-06 0:15 ` Dinh Nguyen
2013-11-06 19:01 ` Sebastian Andrzej Siewior
2013-11-06 19:01 ` Sebastian Andrzej Siewior
2013-11-06 19:08 ` Pantelis Antoniou
[not found] ` <C10E33D7-53F6-4A50-9B84-1149DDBDC46F-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
2013-11-06 20:31 ` Sebastian Andrzej Siewior
2013-11-06 20:31 ` Sebastian Andrzej Siewior
2013-11-06 20:41 ` Sebastian Andrzej Siewior
[not found] ` <20131106204143.GD8662-E0PNVn5OA6ohrxcnuTQ+TQ@public.gmane.org>
2013-11-07 7:24 ` Pantelis Antoniou
2013-11-07 7:24 ` Pantelis Antoniou
[not found] ` <20131106203102.GB8662-E0PNVn5OA6ohrxcnuTQ+TQ@public.gmane.org>
2013-11-07 7:23 ` Pantelis Antoniou
2013-11-07 7:23 ` Pantelis Antoniou
2013-11-07 9:45 ` Alexander Sverdlin
2013-11-07 9:45 ` Alexander Sverdlin
2013-11-06 19:30 ` Guenter Roeck
[not found] ` <20131106193041.GA26796-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2013-11-06 20:38 ` Sebastian Andrzej Siewior
2013-11-06 20:38 ` Sebastian Andrzej Siewior
2013-11-06 21:17 ` Guenter Roeck
[not found] ` <20131106211754.GA29300-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2013-11-07 7:27 ` Pantelis Antoniou
2013-11-07 7:27 ` Pantelis Antoniou
2013-11-07 19:25 ` Sebastian Andrzej Siewior
2013-11-07 19:25 ` Sebastian Andrzej Siewior
[not found] ` <20131107192558.GA11453-E0PNVn5OA6ohrxcnuTQ+TQ@public.gmane.org>
2013-11-07 20:06 ` Pantelis Antoniou
2013-11-07 20:06 ` Pantelis Antoniou
[not found] ` <4F47D5F8-F3C7-45CA-9D88-2D814BDC1BC7-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org>
2013-11-07 20:46 ` Sebastian Andrzej Siewior
2013-11-07 20:46 ` Sebastian Andrzej Siewior
[not found] ` <20131107204626.GA11746-E0PNVn5OA6ohrxcnuTQ+TQ@public.gmane.org>
2013-11-07 23:00 ` Guenter Roeck
2013-11-07 23:00 ` Guenter Roeck
2013-11-08 7:12 ` Pantelis Antoniou
2013-11-08 8:40 ` Sebastian Andrzej Siewior
2013-11-08 7:09 ` Pantelis Antoniou
2013-11-08 7:09 ` Pantelis Antoniou
2013-11-11 17:04 ` Grant Likely
2013-11-11 17:04 ` Grant Likely
[not found] ` <20131111170453.4D999C42336-WNowdnHR2B42iJbIjFUEsiwD8/FfD2ys@public.gmane.org>
2013-11-12 8:16 ` Pantelis Antoniou
2013-11-12 8:16 ` Pantelis Antoniou
2013-11-07 22:51 ` Guenter Roeck
2013-11-07 22:51 ` Guenter Roeck
2013-11-07 22:22 ` Guenter Roeck
2013-11-07 22:22 ` Guenter Roeck
2013-11-06 8:53 ` Alexander Sverdlin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=527A6D34.40407@nsn.com \
--to=ioan.nicu.ext-oyasijw0dpe@public.gmane.org \
--cc=Alison_Chaiken-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org \
--cc=alexander.sverdlin-OYasijW0DpE@public.gmane.org \
--cc=delicious.quinoa-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
--cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=dinh.linux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
--cc=dirk.behme-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
--cc=grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org \
--cc=jluebbe-H4yykcOXDpCzQB+pC5nmwQ@public.gmane.org \
--cc=koen-QLwJDigV5abLmq1fohREcCpxlwaOVQ5f@public.gmane.org \
--cc=linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org \
--cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=matt.porter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
--cc=mbohan-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org \
--cc=monstr-pSz03upnqPeHXe+LvDLADg@public.gmane.org \
--cc=mranostay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
--cc=ms-g5CePrrZ5ROELgA04lAiVw@public.gmane.org \
--cc=panto-wVdstyuyKrO8r51toPun2/C9HSW9iNxf@public.gmane.org \
--cc=robherring2-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
--cc=s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
--cc=swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.