]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
of: Keep track of populated platform devices
authorPawel Moll <pawel.moll@arm.com>
Thu, 15 May 2014 15:55:24 +0000 (16:55 +0100)
committerPawel Moll <pawel.moll@arm.com>
Thu, 15 May 2014 16:02:14 +0000 (17:02 +0100)
In "Device Tree powered" systems, platform devices are usually massively
populated with of_platform_populate() call, executed at some level of
initcalls, either by generic architecture or by platform-specific code.

There are situations though where certain devices must be created (and
bound with drivers) before all the others. This presents a challenge,
as devices created explicitly would be created again by
of_platform_populate().

This patch tries to solve that issue in a generic way, adding a
"populated" flag for a DT node description. Subsequent
of_platform_populate() will skip such nodes (and its children) in
a similar way to the non-available ones.

This patch also adds of_platform_depopulate() as an operation
complementary to the _populate() one. It removes a platform or an amba
device populated from the Device Tree, together with its all children
(leaving, however, devices without associated of_node untouched)
clearing the "populated" flag on the way.

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Grant Likely <grant.likely@linaro.org>
drivers/of/platform.c
include/linux/of.h
include/linux/of_platform.h

index bd47fbc53dc96258fba942d073ee320f25e5627f..e8376d646d98ff66594cd838a11918ff6486b7f5 100644 (file)
@@ -206,12 +206,13 @@ static struct platform_device *of_platform_device_create_pdata(
 {
        struct platform_device *dev;
 
-       if (!of_device_is_available(np))
+       if (!of_device_is_available(np) ||
+           of_node_test_and_set_flag(np, OF_POPULATED))
                return NULL;
 
        dev = of_device_alloc(np, bus_id, parent);
        if (!dev)
-               return NULL;
+               goto err_clear_flag;
 
 #if defined(CONFIG_MICROBLAZE)
        dev->archdata.dma_mask = 0xffffffffUL;
@@ -229,10 +230,14 @@ static struct platform_device *of_platform_device_create_pdata(
 
        if (of_device_add(dev) != 0) {
                platform_device_put(dev);
-               return NULL;
+               goto err_clear_flag;
        }
 
        return dev;
+
+err_clear_flag:
+       of_node_clear_flag(np, OF_POPULATED);
+       return NULL;
 }
 
 /**
@@ -264,14 +269,15 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
 
        pr_debug("Creating amba device %s\n", node->full_name);
 
-       if (!of_device_is_available(node))
+       if (!of_device_is_available(node) ||
+           of_node_test_and_set_flag(node, OF_POPULATED))
                return NULL;
 
        dev = amba_device_alloc(NULL, 0, 0);
        if (!dev) {
                pr_err("%s(): amba_device_alloc() failed for %s\n",
                       __func__, node->full_name);
-               return NULL;
+               goto err_clear_flag;
        }
 
        /* setup generic device info */
@@ -311,6 +317,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
 
 err_free:
        amba_device_put(dev);
+err_clear_flag:
+       of_node_clear_flag(node, OF_POPULATED);
        return NULL;
 }
 #else /* CONFIG_ARM_AMBA */
@@ -487,4 +495,60 @@ int of_platform_populate(struct device_node *root,
        return rc;
 }
 EXPORT_SYMBOL_GPL(of_platform_populate);
+
+static int of_platform_device_destroy(struct device *dev, void *data)
+{
+       bool *children_left = data;
+
+       /* Do not touch devices not populated from the device tree */
+       if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) {
+               *children_left = true;
+               return 0;
+       }
+
+       /* Recurse, but don't touch this device if it has any children left */
+       if (of_platform_depopulate(dev) != 0) {
+               *children_left = true;
+               return 0;
+       }
+
+       if (dev->bus == &platform_bus_type)
+               platform_device_unregister(to_platform_device(dev));
+#ifdef CONFIG_ARM_AMBA
+       else if (dev->bus == &amba_bustype)
+               amba_device_unregister(to_amba_device(dev));
+#endif
+       else {
+               *children_left = true;
+               return 0;
+       }
+
+       of_node_clear_flag(dev->of_node, OF_POPULATED);
+
+       return 0;
+}
+
+/**
+ * of_platform_depopulate() - Remove devices populated from device tree
+ * @parent: device which childred will be removed
+ *
+ * Complementary to of_platform_populate(), this function removes children
+ * of the given device (and, recurrently, their children) that have been
+ * created from their respective device tree nodes (and only those,
+ * leaving others - eg. manually created - unharmed).
+ *
+ * Returns 0 when all children devices have been removed or
+ * -EBUSY when some children remained.
+ */
+int of_platform_depopulate(struct device *parent)
+{
+       bool children_left = false;
+
+       device_for_each_child(parent, &children_left,
+                             of_platform_device_destroy);
+
+       return children_left ? -EBUSY : 0;
+}
+EXPORT_SYMBOL_GPL(of_platform_depopulate);
+
 #endif /* CONFIG_OF_ADDRESS */
index 3bad8d106e0ea01b2ae7524e216c83b4e654ced3..4c50d0b78b8900b4ce671b3d71f3df4800dbeeb7 100644 (file)
@@ -130,6 +130,12 @@ static inline int of_node_check_flag(struct device_node *n, unsigned long flag)
        return test_bit(flag, &n->_flags);
 }
 
+static inline int of_node_test_and_set_flag(struct device_node *n,
+                                           unsigned long flag)
+{
+       return test_and_set_bit(flag, &n->_flags);
+}
+
 static inline void of_node_set_flag(struct device_node *n, unsigned long flag)
 {
        set_bit(flag, &n->_flags);
@@ -197,6 +203,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
 /* flag descriptions */
 #define OF_DYNAMIC     1 /* node and properties were allocated via kmalloc */
 #define OF_DETACHED    2 /* node has been detached from the device tree */
+#define OF_POPULATED   3 /* device already created for the node */
 
 #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
 #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
index 05cb4a928252259b9e1a3a62df682c4963ef3b91..b1010eeaac0d39107424ea30d9ce071912564294 100644 (file)
@@ -72,6 +72,7 @@ extern int of_platform_populate(struct device_node *root,
                                const struct of_device_id *matches,
                                const struct of_dev_auxdata *lookup,
                                struct device *parent);
+extern int of_platform_depopulate(struct device *parent);
 #else
 static inline int of_platform_populate(struct device_node *root,
                                        const struct of_device_id *matches,
@@ -80,6 +81,10 @@ static inline int of_platform_populate(struct device_node *root,
 {
        return -ENODEV;
 }
+static inline int of_platform_depopulate(struct device *parent)
+{
+       return -ENODEV;
+}
 #endif
 
 #endif /* _LINUX_OF_PLATFORM_H */