mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
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: Pawel Moll <pawel.moll@arm.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Grant Likely <grant.likely@linaro.org>
(cherry picked from commit c6e126de43)
Signed-off-by: Mark Brown <broonie@kernel.org>
Conflicts:
drivers/of/platform.c
include/linux/of_platform.h
This commit is contained in:
@@ -204,12 +204,13 @@ 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;
|
||||
@@ -225,10 +226,14 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,12 +265,16 @@ 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)
|
||||
return NULL;
|
||||
if (!dev) {
|
||||
pr_err("%s(): amba_device_alloc() failed for %s\n",
|
||||
__func__, node->full_name);
|
||||
goto err_clear_flag;
|
||||
}
|
||||
|
||||
/* setup generic device info */
|
||||
dev->dev.coherent_dma_mask = ~0;
|
||||
@@ -301,6 +310,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 */
|
||||
@@ -473,4 +484,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 */
|
||||
|
||||
@@ -134,6 +134,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);
|
||||
@@ -199,6 +205,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)
|
||||
|
||||
@@ -95,6 +95,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);
|
||||
#endif /* CONFIG_OF_ADDRESS */
|
||||
|
||||
#endif /* CONFIG_OF_DEVICE */
|
||||
@@ -109,6 +110,10 @@ static inline int of_platform_populate(struct device_node *root,
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif /* !CONFIG_OF_ADDRESS */
|
||||
static inline int of_platform_depopulate(struct device *parent)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_OF_PLATFORM_H */
|
||||
|
||||
Reference in New Issue
Block a user