diff options
Diffstat (limited to 'drivers/of/platform.c')
-rw-r--r-- | drivers/of/platform.c | 74 |
1 files changed, 69 insertions, 5 deletions
diff --git a/drivers/of/platform.c b/drivers/of/platform.c index bd47fbc53dc9..e8376d646d98 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c | |||
@@ -206,12 +206,13 @@ static struct platform_device *of_platform_device_create_pdata( | |||
206 | { | 206 | { |
207 | struct platform_device *dev; | 207 | struct platform_device *dev; |
208 | 208 | ||
209 | if (!of_device_is_available(np)) | 209 | if (!of_device_is_available(np) || |
210 | of_node_test_and_set_flag(np, OF_POPULATED)) | ||
210 | return NULL; | 211 | return NULL; |
211 | 212 | ||
212 | dev = of_device_alloc(np, bus_id, parent); | 213 | dev = of_device_alloc(np, bus_id, parent); |
213 | if (!dev) | 214 | if (!dev) |
214 | return NULL; | 215 | goto err_clear_flag; |
215 | 216 | ||
216 | #if defined(CONFIG_MICROBLAZE) | 217 | #if defined(CONFIG_MICROBLAZE) |
217 | dev->archdata.dma_mask = 0xffffffffUL; | 218 | dev->archdata.dma_mask = 0xffffffffUL; |
@@ -229,10 +230,14 @@ static struct platform_device *of_platform_device_create_pdata( | |||
229 | 230 | ||
230 | if (of_device_add(dev) != 0) { | 231 | if (of_device_add(dev) != 0) { |
231 | platform_device_put(dev); | 232 | platform_device_put(dev); |
232 | return NULL; | 233 | goto err_clear_flag; |
233 | } | 234 | } |
234 | 235 | ||
235 | return dev; | 236 | return dev; |
237 | |||
238 | err_clear_flag: | ||
239 | of_node_clear_flag(np, OF_POPULATED); | ||
240 | return NULL; | ||
236 | } | 241 | } |
237 | 242 | ||
238 | /** | 243 | /** |
@@ -264,14 +269,15 @@ static struct amba_device *of_amba_device_create(struct device_node *node, | |||
264 | 269 | ||
265 | pr_debug("Creating amba device %s\n", node->full_name); | 270 | pr_debug("Creating amba device %s\n", node->full_name); |
266 | 271 | ||
267 | if (!of_device_is_available(node)) | 272 | if (!of_device_is_available(node) || |
273 | of_node_test_and_set_flag(node, OF_POPULATED)) | ||
268 | return NULL; | 274 | return NULL; |
269 | 275 | ||
270 | dev = amba_device_alloc(NULL, 0, 0); | 276 | dev = amba_device_alloc(NULL, 0, 0); |
271 | if (!dev) { | 277 | if (!dev) { |
272 | pr_err("%s(): amba_device_alloc() failed for %s\n", | 278 | pr_err("%s(): amba_device_alloc() failed for %s\n", |
273 | __func__, node->full_name); | 279 | __func__, node->full_name); |
274 | return NULL; | 280 | goto err_clear_flag; |
275 | } | 281 | } |
276 | 282 | ||
277 | /* setup generic device info */ | 283 | /* setup generic device info */ |
@@ -311,6 +317,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node, | |||
311 | 317 | ||
312 | err_free: | 318 | err_free: |
313 | amba_device_put(dev); | 319 | amba_device_put(dev); |
320 | err_clear_flag: | ||
321 | of_node_clear_flag(node, OF_POPULATED); | ||
314 | return NULL; | 322 | return NULL; |
315 | } | 323 | } |
316 | #else /* CONFIG_ARM_AMBA */ | 324 | #else /* CONFIG_ARM_AMBA */ |
@@ -487,4 +495,60 @@ int of_platform_populate(struct device_node *root, | |||
487 | return rc; | 495 | return rc; |
488 | } | 496 | } |
489 | EXPORT_SYMBOL_GPL(of_platform_populate); | 497 | EXPORT_SYMBOL_GPL(of_platform_populate); |
498 | |||
499 | static int of_platform_device_destroy(struct device *dev, void *data) | ||
500 | { | ||
501 | bool *children_left = data; | ||
502 | |||
503 | /* Do not touch devices not populated from the device tree */ | ||
504 | if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { | ||
505 | *children_left = true; | ||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | /* Recurse, but don't touch this device if it has any children left */ | ||
510 | if (of_platform_depopulate(dev) != 0) { | ||
511 | *children_left = true; | ||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | if (dev->bus == &platform_bus_type) | ||
516 | platform_device_unregister(to_platform_device(dev)); | ||
517 | #ifdef CONFIG_ARM_AMBA | ||
518 | else if (dev->bus == &amba_bustype) | ||
519 | amba_device_unregister(to_amba_device(dev)); | ||
520 | #endif | ||
521 | else { | ||
522 | *children_left = true; | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | of_node_clear_flag(dev->of_node, OF_POPULATED); | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | /** | ||
532 | * of_platform_depopulate() - Remove devices populated from device tree | ||
533 | * @parent: device which childred will be removed | ||
534 | * | ||
535 | * Complementary to of_platform_populate(), this function removes children | ||
536 | * of the given device (and, recurrently, their children) that have been | ||
537 | * created from their respective device tree nodes (and only those, | ||
538 | * leaving others - eg. manually created - unharmed). | ||
539 | * | ||
540 | * Returns 0 when all children devices have been removed or | ||
541 | * -EBUSY when some children remained. | ||
542 | */ | ||
543 | int of_platform_depopulate(struct device *parent) | ||
544 | { | ||
545 | bool children_left = false; | ||
546 | |||
547 | device_for_each_child(parent, &children_left, | ||
548 | of_platform_device_destroy); | ||
549 | |||
550 | return children_left ? -EBUSY : 0; | ||
551 | } | ||
552 | EXPORT_SYMBOL_GPL(of_platform_depopulate); | ||
553 | |||
490 | #endif /* CONFIG_OF_ADDRESS */ | 554 | #endif /* CONFIG_OF_ADDRESS */ |