Technically only the last two patches are v2 patches, the first three are
cleanups written when they were refactored, and this is a first
posting for them.
The most interesting part of this series is the addition of
of_platform_prepare() which is useful to allow DT and non-DT board
support to share the same initialization code for on-chip platform
devices.
---
Grant Likely (5):
dt: Refactor of_platform_bus_probe()
dt: protect against NULL matches passed to of_match_node()
dt: eliminate OF_NO_DEEP_PROBE and test for NULL match table
dt: add of_platform_populate() for creating device from the device tree
dt: add of_platform_prepare() to match nodes with static platform_devices
arch/arm/mach-versatile/core.c | 1
arch/powerpc/platforms/52xx/mpc52xx_common.c | 10 -
drivers/of/address.c | 14 +
drivers/of/base.c | 3
drivers/of/platform.c | 347 ++++++++++++++++++++++----
include/linux/of_address.h | 1
include/linux/of_platform.h | 8 -
7 files changed, 326 insertions(+), 58 deletions(-)
--
Signature
The current implementation uses three copies of of basically identical
code. This patch consolidates them to make the code simpler.
Signed-off-by: Grant Likely <[email protected]>
---
drivers/of/platform.c | 55 ++++++++++++++++---------------------------------
1 files changed, 18 insertions(+), 37 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 1ce4c45..f489e36 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -210,13 +210,16 @@ struct platform_device *of_platform_device_create(struct device_node *np,
EXPORT_SYMBOL(of_platform_device_create);
/**
- * of_platform_bus_create - Create an OF device for a bus node and all its
- * children. Optionally recursively instantiate matching busses.
+ * of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
* @matches: match table, NULL to use the default, OF_NO_DEEP_PROBE to
- * disallow recursive creation of child busses
+ * disallow recursive creation of child buses
+ * @parent: parent for new device, or NULL for top level.
+ *
+ * Creates a platform_device for the provided device_node, and optionally
+ * recursively create devices for all the child nodes.
*/
-static int of_platform_bus_create(const struct device_node *bus,
+static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
struct device *parent)
{
@@ -224,18 +227,13 @@ static int of_platform_bus_create(const struct device_node *bus,
struct platform_device *dev;
int rc = 0;
+ dev = of_platform_device_create(bus, NULL, parent);
+ if (!dev || !of_match_node(matches, bus))
+ return 0;
+
for_each_child_of_node(bus, child) {
pr_debug(" create child: %s\n", child->full_name);
- dev = of_platform_device_create(child, NULL, parent);
- if (dev == NULL)
- continue;
-
- if (!of_match_node(matches, child))
- continue;
- if (rc == 0) {
- pr_debug(" and sub busses\n");
- rc = of_platform_bus_create(child, matches, &dev->dev);
- }
+ rc = of_platform_bus_create(child, matches, &dev->dev);
if (rc) {
of_node_put(child);
break;
@@ -245,7 +243,7 @@ static int of_platform_bus_create(const struct device_node *bus,
}
/**
- * of_platform_bus_probe - Probe the device-tree for platform busses
+ * of_platform_bus_probe() - Probe the device-tree for platform buses
* @root: parent of the first level to probe or NULL for the root of the tree
* @matches: match table, NULL to use the default
* @parent: parent to hook devices from, NULL for toplevel
@@ -258,7 +256,6 @@ int of_platform_bus_probe(struct device_node *root,
struct device *parent)
{
struct device_node *child;
- struct platform_device *dev;
int rc = 0;
if (WARN_ON(!matches || matches == OF_NO_DEEP_PROBE))
@@ -277,31 +274,15 @@ int of_platform_bus_probe(struct device_node *root,
* children
*/
if (of_match_node(matches, root)) {
- pr_debug(" root match, create all sub devices\n");
- dev = of_platform_device_create(root, NULL, parent);
- if (dev == NULL)
- goto bail;
-
- pr_debug(" create all sub busses\n");
- rc = of_platform_bus_create(root, matches, &dev->dev);
- goto bail;
- }
- for_each_child_of_node(root, child) {
+ rc = of_platform_bus_create(root, matches, parent);
+ } else for_each_child_of_node(root, child) {
if (!of_match_node(matches, child))
continue;
-
- pr_debug(" match: %s\n", child->full_name);
- dev = of_platform_device_create(child, NULL, parent);
- if (dev == NULL)
- continue;
-
- rc = of_platform_bus_create(child, matches, &dev->dev);
- if (rc) {
- of_node_put(child);
+ rc = of_platform_bus_create(child, matches, parent);
+ if (rc)
break;
- }
}
- bail:
+
of_node_put(root);
return rc;
}
There are a few use cases where it is convenient to pass NULL to
of_match_node() and have it fail gracefully. The patch adds a null
check to the beginning so taht it does so.
Signed-off-by: Grant Likely <[email protected]>
---
drivers/of/base.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 710b53b..632ebae7 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -496,6 +496,9 @@ EXPORT_SYMBOL(of_find_node_with_property);
const struct of_device_id *of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
+ if (!matches)
+ return NULL;
+
while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
int match = 1;
if (matches->name[0])
There are no users of OF_NO_DEEP_PROBE, and of_match_node() now
gracefully handles being passed a NULL pointer, so the checks at the
top of of_platform_bus_probe can be dropped.
While at it, consolidate the root node pointer check to be easier to
read and tidy up related comments.
Signed-off-by: Grant Likely <[email protected]>
---
drivers/of/platform.c | 17 +++++------------
include/linux/of_platform.h | 3 ---
2 files changed, 5 insertions(+), 15 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index f489e36..63d3cb7 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -212,7 +212,7 @@ EXPORT_SYMBOL(of_platform_device_create);
/**
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
- * @matches: match table, NULL to use the default, OF_NO_DEEP_PROBE to
+ * @matches: match table for bus nodes
* disallow recursive creation of child buses
* @parent: parent for new device, or NULL for top level.
*
@@ -245,7 +245,7 @@ static int of_platform_bus_create(struct device_node *bus,
/**
* of_platform_bus_probe() - Probe the device-tree for platform buses
* @root: parent of the first level to probe or NULL for the root of the tree
- * @matches: match table, NULL to use the default
+ * @matches: match table for bus nodes
* @parent: parent to hook devices from, NULL for toplevel
*
* Note that children of the provided root are not instantiated as devices
@@ -258,21 +258,14 @@ int of_platform_bus_probe(struct device_node *root,
struct device_node *child;
int rc = 0;
- if (WARN_ON(!matches || matches == OF_NO_DEEP_PROBE))
- return -EINVAL;
- if (root == NULL)
- root = of_find_node_by_path("/");
- else
- of_node_get(root);
- if (root == NULL)
+ root = root ? of_node_get(root) : of_find_node_by_path("/");
+ if (!root)
return -EINVAL;
pr_debug("of_platform_bus_probe()\n");
pr_debug(" starting at: %s\n", root->full_name);
- /* Do a self check of bus type, if there's a match, create
- * children
- */
+ /* Do a self check of bus type, if there's a match, create children */
if (of_match_node(matches, root)) {
rc = of_platform_bus_create(root, matches, parent);
} else for_each_child_of_node(root, child) {
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
index 17c7e21..fb51ae3 100644
--- a/include/linux/of_platform.h
+++ b/include/linux/of_platform.h
@@ -52,9 +52,6 @@ extern struct platform_device *of_platform_device_create(struct device_node *np,
const char *bus_id,
struct device *parent);
-/* pseudo "matches" value to not do deep probe */
-#define OF_NO_DEEP_PROBE ((struct of_device_id *)-1)
-
extern int of_platform_bus_probe(struct device_node *root,
const struct of_device_id *matches,
struct device *parent);
This patch implements an alternate method for using device tree data
for populating machine device registration. Traditionally, board
support has directly generated and registered devices based on nodes
in the device tree. The board support code starts at the root of the
tree and begins allocating devices for each device node it finds.
Similarly, bus drivers (i2c, spi, etc.) use their child nodes to
register child devices. This model can be seen in almost all the powerpc
board ports (arch/powerpc/platforms/*).
However, for many of the ARM SoCs, there already exists complete board
support for many SoCs that have their own code for registering the
basic set of platform devices with non-trivial dependencies on clock
structure and machine specific platform code. While starting at the
base of the tree and working up is certainly possible, it requires
modifying a lot of machine support code to get it working.
This patch adds the function of_platform_prepare() to declare which
nodes will be used to create platform_devices without actually
creating them yet. Then it uses a bus notifier to inspect
platform_device registrations. If any match a node declared with
of_platform_prepare(), it will set the of_node pointer before the
device gets bound to a driver, making the device tree data available
at probe time.
After all the static devices are registered, the platform code can
still call either of_platform_populate or of_platform_bus_probe() on
the same nodes that were declared with of_platform_prepare(). Any
nodes that *were not* matched to a static platform_device will get
created in the normal way.
Note: Board ports using this facility are still required to provide a
fully populated device tree blob. It is not a shortcut to providing
an accurate device tree model of the machine to the point that it
would be reasonably possible to switch to a direct registration model
for all devices without change the device tree. ie. The SoC still
needs to be correctly identified and there should be nodes for all the
discrete devices.
v2: Complete revamp to make of_platform_populate() and
of_platform_bus_probe() respect the prepared nodes.
Signed-off-by: Grant Likely <[email protected]>
---
arch/arm/mach-versatile/core.c | 1
drivers/of/address.c | 14 ++
drivers/of/platform.c | 229 ++++++++++++++++++++++++++++++++++++++++
include/linux/of_address.h | 1
include/linux/of_platform.h | 2
5 files changed, 247 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index 136c32e..79f933c 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/interrupt.h>
diff --git a/drivers/of/address.c b/drivers/of/address.c
index b4559c5..b43ff66 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -556,6 +556,20 @@ static int __of_address_to_resource(struct device_node *dev,
}
/**
+ * of_address_count - Return the number of entries in the reg property
+ */
+int of_address_count(struct device_node *np)
+{
+ struct resource temp_res;
+ int num_reg = 0;
+
+ while (of_address_to_resource(np, num_reg, &temp_res) == 0)
+ num_reg++;
+ return num_reg;
+}
+EXPORT_SYMBOL_GPL(of_address_count);
+
+/**
* of_address_to_resource - Translate device tree address and return as resource
*
* Note that if your address is a PIO address, the conversion will fail if
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 9b785be..899e959 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/notifier.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
@@ -209,6 +210,225 @@ struct platform_device *of_platform_device_create(struct device_node *np,
}
EXPORT_SYMBOL(of_platform_device_create);
+struct of_platform_prepare_data {
+ struct list_head list;
+ struct device_node *node;
+ struct device *dev; /* assigned device */
+
+ int num_resources;
+ struct resource resource[0];
+};
+
+static LIST_HEAD(of_platform_prepare_list);
+static struct notifier_block of_platform_nb;
+
+static struct of_platform_prepare_data *of_platform_find_prepare_data(
+ struct device_node *node)
+{
+ struct of_platform_prepare_data *prep;
+ list_for_each_entry(prep, &of_platform_prepare_list, list)
+ if (prep->node == node)
+ return prep;
+ return NULL;
+}
+
+static bool of_pdev_match_resources(struct platform_device *pdev,
+ struct of_platform_prepare_data *prep)
+{
+ struct resource *node_res = prep->resource;
+ struct resource *pdev_res;
+ int i, j;
+
+ if (prep->num_resources == 0 || pdev->num_resources == 0)
+ return false;
+
+ dev_dbg(&pdev->dev, "compare dt node %s\n", prep->node->full_name);
+
+ /* Compare both resource tables and make sure every node resource
+ * is represented by the platform device. Here we check that each
+ * resource has corresponding entry with the same type and start
+ * values, and the end value falls inside the range specified
+ * in the device tree node. */
+ for (i = 0; i < prep->num_resources; i++, node_res++) {
+ pr_debug(" node res %2i:%.8x..%.8x[%lx]...\n", i,
+ node_res->start, node_res->end, node_res->flags);
+ pdev_res = pdev->resource;
+ for (j = 0; j < pdev->num_resources; j++, pdev_res++) {
+ pr_debug(" pdev res %2i:%.8x..%.8x[%lx]\n", j,
+ pdev_res->start, pdev_res->end, pdev_res->flags);
+ if ((pdev_res->start == node_res->start) &&
+ (pdev_res->end >= node_res->start) &&
+ (pdev_res->end <= node_res->end) &&
+ (pdev_res->flags == node_res->flags)) {
+ pr_debug(" ...MATCH! :-)\n");
+ break;
+ }
+ }
+ if (j >= pdev->num_resources)
+ return false;
+ }
+ return true;
+}
+
+static int of_platform_device_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *_dev)
+{
+ struct platform_device *pdev = to_platform_device(_dev);
+ struct of_platform_prepare_data *prep;
+
+ switch (event) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (pdev->dev.of_node)
+ return NOTIFY_DONE;
+
+ list_for_each_entry(prep, &of_platform_prepare_list, list) {
+ if (prep->dev)
+ continue;
+
+ if (!of_pdev_match_resources(pdev, prep))
+ continue;
+
+ /* If disabled, don't let the device bind */
+ if (!of_device_is_available(prep->node)) {
+ char buf[strlen(pdev->name) + 12];
+ dev_info(&pdev->dev, "disabled by dt node %s\n",
+ prep->node->full_name);
+ sprintf(buf, "%s-disabled", pdev->name);
+ pdev->name = kstrdup(buf, GFP_KERNEL);
+ continue;
+ }
+
+ dev_info(&pdev->dev, "attaching dt node %s\n",
+ prep->node->full_name);
+ prep->dev = get_device(&pdev->dev);
+ pdev->dev.of_node = of_node_get(prep->node);
+ return NOTIFY_OK;
+ }
+ break;
+
+ case BUS_NOTIFY_DEL_DEVICE:
+ list_for_each_entry(prep, &of_platform_prepare_list, list) {
+ if (prep->dev == &pdev->dev) {
+ dev_info(&pdev->dev, "detaching dt node %s\n",
+ prep->node->full_name);
+ of_node_put(pdev->dev.of_node);
+ put_device(prep->dev);
+ pdev->dev.of_node = NULL;
+ prep->dev = NULL;
+ return NOTIFY_OK;
+ }
+ }
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/**
+ * of_platform_prepare - Flag nodes to be used for creating devices
+ * @root: parent of the first level to probe or NULL for the root of the tree
+ * @bus_match: match table for child bus nodes, or NULL
+ *
+ * This function sets up 'snooping' of device tree registrations and
+ * when a device registration is found that matches a node in the
+ * device tree, it populates the platform_device with a pointer to the
+ * matching node.
+ *
+ * A bus notifier is used to implement this behaviour. When this
+ * function is called, it will parse all the child nodes of @root and
+ * create a lookup table of eligible device nodes. A device node is
+ * considered eligible if it:
+ * a) has a compatible property,
+ * b) has memory mapped registers, and
+ * c) has a mappable interrupt.
+ *
+ * It will also recursively parse child buses providing
+ * a) the child bus node has a ranges property (children have
+ * memory-mapped registers), and
+ * b) it is compatible with the @matches list.
+ *
+ * The lookup table will be used as data for a platform bus notifier
+ * that will compare each new device registration with the table
+ * before a device driver is bound to it. If there is a match, then
+ * the of_node pointer will be added to the device. Therefore it is
+ * important to call this function *before* any platform devices get
+ * registered.
+ */
+void of_platform_prepare(struct device_node *root,
+ const struct of_device_id *matches)
+{
+ struct device_node *child;
+ struct of_platform_prepare_data *prep;
+
+ /* register the notifier if it isn't already */
+ if (!of_platform_nb.notifier_call) {
+ of_platform_nb.notifier_call = of_platform_device_notifier_call;
+ bus_register_notifier(&platform_bus_type, &of_platform_nb);
+ }
+
+ /* If root is null, then start at the root of the tree */
+ root = root ? of_node_get(root) : of_find_node_by_path("/");
+ if (!root)
+ return;
+
+ pr_debug("of_platform_prepare()\n");
+ pr_debug(" starting at: %s\n", root->full_name);
+
+ /* Loop over children and record the details */
+ for_each_child_of_node(root, child) {
+ struct resource *res;
+ int num_irq, num_reg, i;
+
+ /* If this is a bus node, recursively inspect the children,
+ * but *don't* prepare it. Prepare only concerns
+ * itself with leaf-nodes. */
+ if (of_match_node(matches, child)) {
+ of_platform_prepare(child, matches);
+ continue;
+ }
+
+ /* Is it already in the list? */
+ if (of_platform_find_prepare_data(child))
+ continue;
+
+ /* Make sure it has a compatible property */
+ if (!of_get_property(child, "compatible", NULL))
+ continue;
+
+ /*
+ * Count the resources. If the device doesn't have any
+ * register ranges, then it gets skipped because there is no
+ * way to match such a device against static registration
+ */
+ num_irq = of_irq_count(child);
+ num_reg = of_address_count(child);
+ if (!num_reg)
+ continue;
+
+ /* Device node looks valid; record the details */
+ prep = kzalloc(sizeof(*prep) +
+ (sizeof(prep->resource[0]) * (num_irq + num_reg)),
+ GFP_KERNEL);
+ if (!prep)
+ return; /* We're screwed if malloc doesn't work. */
+
+ INIT_LIST_HEAD(&prep->list);
+
+ res = &prep->resource[0];
+ for (i = 0; i < num_reg; i++, res++)
+ WARN_ON(of_address_to_resource(child, i, res));
+ WARN_ON(of_irq_to_resource_table(child, res, num_irq) != num_irq);
+ prep->num_resources = num_reg + num_irq;
+ prep->node = of_node_get(child);
+
+ list_add_tail(&prep->list, &of_platform_prepare_list);
+
+ pr_debug("%s() - %s prepared (%i regs, %i irqs)\n",
+ __func__, prep->node->full_name, num_reg, num_irq);
+ }
+
+}
+
/**
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
@@ -223,6 +443,7 @@ static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
struct device *parent, bool strict)
{
+ struct of_platform_prepare_data *prep;
struct device_node *child;
struct platform_device *dev;
int rc = 0;
@@ -234,6 +455,14 @@ static int of_platform_bus_create(struct device_node *bus,
return 0;
}
+ /* Has the device already been registered manually? */
+ prep = of_platform_find_prepare_data(bus);
+ if (prep && prep->dev) {
+ pr_debug("%s() - skipping %s, already registered\n",
+ __func__, bus->full_name);
+ return 0;
+ }
+
dev = of_platform_device_create(bus, NULL, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 2feda6e..6711d5f 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -3,6 +3,7 @@
#include <linux/ioport.h>
#include <linux/of.h>
+extern int of_address_count(struct device_node *np);
extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
extern int of_address_to_resource(struct device_node *dev, int index,
struct resource *r);
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
index 43c723d..b8fa37b 100644
--- a/include/linux/of_platform.h
+++ b/include/linux/of_platform.h
@@ -52,6 +52,8 @@ extern struct platform_device *of_platform_device_create(struct device_node *np,
const char *bus_id,
struct device *parent);
+extern void of_platform_prepare(struct device_node *root,
+ const struct of_device_id *matches);
extern int of_platform_bus_probe(struct device_node *root,
const struct of_device_id *matches,
struct device *parent);
of_platform_populate() is similar to of_platform_bus_probe() except
that it strictly enforces that all device nodes must have a compatible
property, and it can be used to register devices (not buses) which are
children of the root node.
This patch also modifies MPC5200 support to use the new function.
Signed-off-by: Grant Likely <[email protected]>
---
arch/powerpc/platforms/52xx/mpc52xx_common.c | 10 ++---
drivers/of/platform.c | 54 ++++++++++++++++++++++++--
include/linux/of_platform.h | 3 +
3 files changed, 57 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c
index 41f3a7e..5767a8a 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
@@ -97,13 +97,11 @@ struct mpc52xx_gpio_wkup __iomem *wkup_gpio;
* of the localplus bus to the of_platform
* bus.
*/
-void __init
-mpc52xx_declare_of_platform_devices(void)
+void __init mpc52xx_declare_of_platform_devices(void)
{
- /* Find every child of the SOC node and add it to of_platform */
- if (of_platform_bus_probe(NULL, mpc52xx_bus_ids, NULL))
- printk(KERN_ERR __FILE__ ": "
- "Error while probing of_platform bus\n");
+ /* Find all the 'platform' devices and register them. */
+ if (of_platform_populate(NULL, mpc52xx_bus_ids, NULL))
+ pr_err(__FILE__ ": Error while populating devices from DT\n");
}
/*
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 63d3cb7..9b785be 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -221,19 +221,26 @@ EXPORT_SYMBOL(of_platform_device_create);
*/
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
- struct device *parent)
+ struct device *parent, bool strict)
{
struct device_node *child;
struct platform_device *dev;
int rc = 0;
+ /* Make sure it has a compatible property */
+ if (strict && (!of_get_property(bus, "compatible", NULL))) {
+ pr_debug("%s() - skipping %s, no compatible prop\n",
+ __func__, bus->full_name);
+ return 0;
+ }
+
dev = of_platform_device_create(bus, NULL, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
for_each_child_of_node(bus, child) {
pr_debug(" create child: %s\n", child->full_name);
- rc = of_platform_bus_create(child, matches, &dev->dev);
+ rc = of_platform_bus_create(child, matches, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
@@ -267,11 +274,11 @@ int of_platform_bus_probe(struct device_node *root,
/* Do a self check of bus type, if there's a match, create children */
if (of_match_node(matches, root)) {
- rc = of_platform_bus_create(root, matches, parent);
+ rc = of_platform_bus_create(root, matches, parent, false);
} else for_each_child_of_node(root, child) {
if (!of_match_node(matches, child))
continue;
- rc = of_platform_bus_create(child, matches, parent);
+ rc = of_platform_bus_create(child, matches, parent, false);
if (rc)
break;
}
@@ -280,4 +287,43 @@ int of_platform_bus_probe(struct device_node *root,
return rc;
}
EXPORT_SYMBOL(of_platform_bus_probe);
+
+/**
+ * of_platform_populate() - Populate platform_devices from device tree data
+ * @root: parent of the first level to probe or NULL for the root of the tree
+ * @matches: match table, NULL to use the default
+ * @parent: parent to hook devices from, NULL for toplevel
+ *
+ * Similar to of_platform_bus_probe(), this function walks the device tree
+ * and creates devices from nodes. It differs in that it follows the modern
+ * convention of requiring all device nodes to have a 'compatible' property,
+ * and it is suitable for creating devices which are children of the root
+ * node (of_platform_bus_probe will only create children of the root which
+ * are selected by the @matches argument).
+ *
+ * New board support should be using this function instead of
+ * of_platform_bus_probe().
+ *
+ * Returns 0 on success, < 0 on failure.
+ */
+int of_platform_populate(struct device_node *root,
+ const struct of_device_id *matches,
+ struct device *parent)
+{
+ struct device_node *child;
+ int rc = 0;
+
+ root = root ? of_node_get(root) : of_find_node_by_path("/");
+ if (!root)
+ return -EINVAL;
+
+ for_each_child_of_node(root, child) {
+ rc = of_platform_bus_create(child, matches, parent, true);
+ if (rc)
+ break;
+ }
+
+ of_node_put(root);
+ return rc;
+}
#endif /* !CONFIG_SPARC */
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
index fb51ae3..43c723d 100644
--- a/include/linux/of_platform.h
+++ b/include/linux/of_platform.h
@@ -55,6 +55,9 @@ extern struct platform_device *of_platform_device_create(struct device_node *np,
extern int of_platform_bus_probe(struct device_node *root,
const struct of_device_id *matches,
struct device *parent);
+extern int of_platform_populate(struct device_node *root,
+ const struct of_device_id *matches,
+ struct device *parent);
#endif /* !CONFIG_SPARC */
#endif /* CONFIG_OF_DEVICE */
Grant,
This is a bit old, but my testing has uncovered a few issues.
On 03/16/2011 03:33 AM, Grant Likely wrote:
> of_platform_populate() is similar to of_platform_bus_probe() except
> that it strictly enforces that all device nodes must have a compatible
> property, and it can be used to register devices (not buses) which are
> children of the root node.
>
> This patch also modifies MPC5200 support to use the new function.
>
> Signed-off-by: Grant Likely<[email protected]>
> ---
> arch/powerpc/platforms/52xx/mpc52xx_common.c | 10 ++---
> drivers/of/platform.c | 54 ++++++++++++++++++++++++--
> include/linux/of_platform.h | 3 +
> 3 files changed, 57 insertions(+), 10 deletions(-)
>
> diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c
> index 41f3a7e..5767a8a 100644
> --- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
> +++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
> @@ -97,13 +97,11 @@ struct mpc52xx_gpio_wkup __iomem *wkup_gpio;
> * of the localplus bus to the of_platform
> * bus.
> */
> -void __init
> -mpc52xx_declare_of_platform_devices(void)
> +void __init mpc52xx_declare_of_platform_devices(void)
> {
> - /* Find every child of the SOC node and add it to of_platform */
> - if (of_platform_bus_probe(NULL, mpc52xx_bus_ids, NULL))
> - printk(KERN_ERR __FILE__ ": "
> - "Error while probing of_platform bus\n");
> + /* Find all the 'platform' devices and register them. */
> + if (of_platform_populate(NULL, mpc52xx_bus_ids, NULL))
> + pr_err(__FILE__ ": Error while populating devices from DT\n");
> }
>
> /*
> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
> index 63d3cb7..9b785be 100644
> --- a/drivers/of/platform.c
> +++ b/drivers/of/platform.c
> @@ -221,19 +221,26 @@ EXPORT_SYMBOL(of_platform_device_create);
> */
> static int of_platform_bus_create(struct device_node *bus,
> const struct of_device_id *matches,
> - struct device *parent)
> + struct device *parent, bool strict)
> {
> struct device_node *child;
> struct platform_device *dev;
> int rc = 0;
>
> + /* Make sure it has a compatible property */
> + if (strict&& (!of_get_property(bus, "compatible", NULL))) {
> + pr_debug("%s() - skipping %s, no compatible prop\n",
> + __func__, bus->full_name);
> + return 0;
> + }
> +
> dev = of_platform_device_create(bus, NULL, parent);
> if (!dev || !of_match_node(matches, bus))
> return 0;
>
> for_each_child_of_node(bus, child) {
> pr_debug(" create child: %s\n", child->full_name);
> - rc = of_platform_bus_create(child, matches,&dev->dev);
> + rc = of_platform_bus_create(child, matches,&dev->dev, strict);
> if (rc) {
> of_node_put(child);
> break;
> @@ -267,11 +274,11 @@ int of_platform_bus_probe(struct device_node *root,
>
> /* Do a self check of bus type, if there's a match, create children */
> if (of_match_node(matches, root)) {
> - rc = of_platform_bus_create(root, matches, parent);
> + rc = of_platform_bus_create(root, matches, parent, false);
> } else for_each_child_of_node(root, child) {
> if (!of_match_node(matches, child))
> continue;
> - rc = of_platform_bus_create(child, matches, parent);
> + rc = of_platform_bus_create(child, matches, parent, false);
> if (rc)
> break;
> }
> @@ -280,4 +287,43 @@ int of_platform_bus_probe(struct device_node *root,
> return rc;
> }
> EXPORT_SYMBOL(of_platform_bus_probe);
> +
> +/**
> + * of_platform_populate() - Populate platform_devices from device tree data
> + * @root: parent of the first level to probe or NULL for the root of the tree
> + * @matches: match table, NULL to use the default
> + * @parent: parent to hook devices from, NULL for toplevel
> + *
> + * Similar to of_platform_bus_probe(), this function walks the device tree
> + * and creates devices from nodes. It differs in that it follows the modern
> + * convention of requiring all device nodes to have a 'compatible' property,
> + * and it is suitable for creating devices which are children of the root
> + * node (of_platform_bus_probe will only create children of the root which
> + * are selected by the @matches argument).
> + *
> + * New board support should be using this function instead of
> + * of_platform_bus_probe().
> + *
> + * Returns 0 on success,< 0 on failure.
> + */
> +int of_platform_populate(struct device_node *root,
> + const struct of_device_id *matches,
> + struct device *parent)
> +{
> + struct device_node *child;
> + int rc = 0;
> +
> + root = root ? of_node_get(root) : of_find_node_by_path("/");
> + if (!root)
> + return -EINVAL;
> +
> + for_each_child_of_node(root, child) {
This needs a check of of_match_node. Otherwise, a device is created for
all top level nodes, not just the matching node.
if (!of_match_node(matches, child))
continue;
> + rc = of_platform_bus_create(child, matches, parent, true);
> + if (rc)
> + break;
> + }
> +
> + of_node_put(root);
> + return rc;
> +}
Missing EXPORT_SYMBOL.
Since this is only in next, do you want to fix it or I can submit a patch.
Rob