Commit c6e126de authored by Pawel Moll's avatar Pawel Moll

of: Keep track of populated platform devices

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: default avatarPawel Moll <pawel.moll@arm.com>
Reviewed-by: default avatarRob Herring <robh@kernel.org>
Acked-by: default avatarGrant Likely <grant.likely@linaro.org>
parent d6d211db
...@@ -206,12 +206,13 @@ static struct platform_device *of_platform_device_create_pdata( ...@@ -206,12 +206,13 @@ static struct platform_device *of_platform_device_create_pdata(
{ {
struct platform_device *dev; 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; return NULL;
dev = of_device_alloc(np, bus_id, parent); dev = of_device_alloc(np, bus_id, parent);
if (!dev) if (!dev)
return NULL; goto err_clear_flag;
#if defined(CONFIG_MICROBLAZE) #if defined(CONFIG_MICROBLAZE)
dev->archdata.dma_mask = 0xffffffffUL; dev->archdata.dma_mask = 0xffffffffUL;
...@@ -229,10 +230,14 @@ static struct platform_device *of_platform_device_create_pdata( ...@@ -229,10 +230,14 @@ static struct platform_device *of_platform_device_create_pdata(
if (of_device_add(dev) != 0) { if (of_device_add(dev) != 0) {
platform_device_put(dev); platform_device_put(dev);
return NULL; goto err_clear_flag;
} }
return dev; 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, ...@@ -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); 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; return NULL;
dev = amba_device_alloc(NULL, 0, 0); dev = amba_device_alloc(NULL, 0, 0);
if (!dev) { if (!dev) {
pr_err("%s(): amba_device_alloc() failed for %s\n", pr_err("%s(): amba_device_alloc() failed for %s\n",
__func__, node->full_name); __func__, node->full_name);
return NULL; goto err_clear_flag;
} }
/* setup generic device info */ /* setup generic device info */
...@@ -311,6 +317,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node, ...@@ -311,6 +317,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
err_free: err_free:
amba_device_put(dev); amba_device_put(dev);
err_clear_flag:
of_node_clear_flag(node, OF_POPULATED);
return NULL; return NULL;
} }
#else /* CONFIG_ARM_AMBA */ #else /* CONFIG_ARM_AMBA */
...@@ -487,4 +495,60 @@ int of_platform_populate(struct device_node *root, ...@@ -487,4 +495,60 @@ int of_platform_populate(struct device_node *root,
return rc; return rc;
} }
EXPORT_SYMBOL_GPL(of_platform_populate); 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 */ #endif /* CONFIG_OF_ADDRESS */
...@@ -130,6 +130,12 @@ static inline int of_node_check_flag(struct device_node *n, unsigned long flag) ...@@ -130,6 +130,12 @@ static inline int of_node_check_flag(struct device_node *n, unsigned long flag)
return test_bit(flag, &n->_flags); 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) static inline void of_node_set_flag(struct device_node *n, unsigned long flag)
{ {
set_bit(flag, &n->_flags); set_bit(flag, &n->_flags);
...@@ -197,6 +203,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) ...@@ -197,6 +203,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
/* flag descriptions */ /* flag descriptions */
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ #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_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_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
......
...@@ -72,6 +72,7 @@ extern int of_platform_populate(struct device_node *root, ...@@ -72,6 +72,7 @@ extern int of_platform_populate(struct device_node *root,
const struct of_device_id *matches, const struct of_device_id *matches,
const struct of_dev_auxdata *lookup, const struct of_dev_auxdata *lookup,
struct device *parent); struct device *parent);
extern int of_platform_depopulate(struct device *parent);
#else #else
static inline int of_platform_populate(struct device_node *root, static inline int of_platform_populate(struct device_node *root,
const struct of_device_id *matches, const struct of_device_id *matches,
...@@ -80,6 +81,10 @@ static inline int of_platform_populate(struct device_node *root, ...@@ -80,6 +81,10 @@ static inline int of_platform_populate(struct device_node *root,
{ {
return -ENODEV; return -ENODEV;
} }
static inline int of_platform_depopulate(struct device *parent)
{
return -ENODEV;
}
#endif #endif
#endif /* _LINUX_OF_PLATFORM_H */ #endif /* _LINUX_OF_PLATFORM_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment