diff options
author | Alan Tull <atull@opensource.altera.com> | 2016-11-01 15:14:22 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-11-10 11:03:35 -0500 |
commit | 39a842e22c1bf3ec3dce36e01fe8ba8ee66c80c8 (patch) | |
tree | 06def1c123cec59f796d6c1ff983ad768a67eafb | |
parent | c1eac0d034d36cfa0da3e35f2add6d6de1e696e5 (diff) |
of/overlay: add of overlay notifications
This patch add of overlay notifications.
When DT overlays are being added, some drivers/subsystems
need to see device tree overlays before the changes go into
the live tree.
This is distinct from reconfig notifiers that are
post-apply or post-remove and which issue very granular
notifications without providing access to the context
of a whole overlay.
The following 4 notificatons are issued:
OF_OVERLAY_PRE_APPLY
OF_OVERLAY_POST_APPLY
OF_OVERLAY_PRE_REMOVE
OF_OVERLAY_POST_REMOVE
In the case of pre-apply notification, if the notifier
returns error, the overlay will be rejected.
This patch exports two functions for registering/unregistering
notifications:
of_overlay_notifier_register(struct notifier_block *nb)
of_overlay_notifier_unregister(struct notifier_block *nb)
The of_mutex is held during these notifications. The
notification data includes pointers to the overlay target
and the overlay:
struct of_overlay_notify_data {
struct device_node *overlay;
struct device_node *target;
};
Signed-off-by: Alan Tull <atull@opensource.altera.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Moritz Fischer <moritz.fischer@ettus.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/of/overlay.c | 47 | ||||
-rw-r--r-- | include/linux/of.h | 25 |
2 files changed, 71 insertions, 1 deletions
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 318dbb51e7a2..0d4cda7050e0 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c | |||
@@ -58,6 +58,41 @@ struct of_overlay { | |||
58 | static int of_overlay_apply_one(struct of_overlay *ov, | 58 | static int of_overlay_apply_one(struct of_overlay *ov, |
59 | struct device_node *target, const struct device_node *overlay); | 59 | struct device_node *target, const struct device_node *overlay); |
60 | 60 | ||
61 | static BLOCKING_NOTIFIER_HEAD(of_overlay_chain); | ||
62 | |||
63 | int of_overlay_notifier_register(struct notifier_block *nb) | ||
64 | { | ||
65 | return blocking_notifier_chain_register(&of_overlay_chain, nb); | ||
66 | } | ||
67 | EXPORT_SYMBOL_GPL(of_overlay_notifier_register); | ||
68 | |||
69 | int of_overlay_notifier_unregister(struct notifier_block *nb) | ||
70 | { | ||
71 | return blocking_notifier_chain_unregister(&of_overlay_chain, nb); | ||
72 | } | ||
73 | EXPORT_SYMBOL_GPL(of_overlay_notifier_unregister); | ||
74 | |||
75 | static int of_overlay_notify(struct of_overlay *ov, | ||
76 | enum of_overlay_notify_action action) | ||
77 | { | ||
78 | struct of_overlay_notify_data nd; | ||
79 | int i, ret; | ||
80 | |||
81 | for (i = 0; i < ov->count; i++) { | ||
82 | struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; | ||
83 | |||
84 | nd.target = ovinfo->target; | ||
85 | nd.overlay = ovinfo->overlay; | ||
86 | |||
87 | ret = blocking_notifier_call_chain(&of_overlay_chain, | ||
88 | action, &nd); | ||
89 | if (ret) | ||
90 | return notifier_to_errno(ret); | ||
91 | } | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
61 | static int of_overlay_apply_single_property(struct of_overlay *ov, | 96 | static int of_overlay_apply_single_property(struct of_overlay *ov, |
62 | struct device_node *target, struct property *prop) | 97 | struct device_node *target, struct property *prop) |
63 | { | 98 | { |
@@ -368,6 +403,13 @@ int of_overlay_create(struct device_node *tree) | |||
368 | goto err_free_idr; | 403 | goto err_free_idr; |
369 | } | 404 | } |
370 | 405 | ||
406 | err = of_overlay_notify(ov, OF_OVERLAY_PRE_APPLY); | ||
407 | if (err < 0) { | ||
408 | pr_err("%s: Pre-apply notifier failed (err=%d)\n", | ||
409 | __func__, err); | ||
410 | goto err_free_idr; | ||
411 | } | ||
412 | |||
371 | /* apply the overlay */ | 413 | /* apply the overlay */ |
372 | err = of_overlay_apply(ov); | 414 | err = of_overlay_apply(ov); |
373 | if (err) | 415 | if (err) |
@@ -382,6 +424,8 @@ int of_overlay_create(struct device_node *tree) | |||
382 | /* add to the tail of the overlay list */ | 424 | /* add to the tail of the overlay list */ |
383 | list_add_tail(&ov->node, &ov_list); | 425 | list_add_tail(&ov->node, &ov_list); |
384 | 426 | ||
427 | of_overlay_notify(ov, OF_OVERLAY_POST_APPLY); | ||
428 | |||
385 | mutex_unlock(&of_mutex); | 429 | mutex_unlock(&of_mutex); |
386 | 430 | ||
387 | return id; | 431 | return id; |
@@ -498,9 +542,10 @@ int of_overlay_destroy(int id) | |||
498 | goto out; | 542 | goto out; |
499 | } | 543 | } |
500 | 544 | ||
501 | 545 | of_overlay_notify(ov, OF_OVERLAY_PRE_REMOVE); | |
502 | list_del(&ov->node); | 546 | list_del(&ov->node); |
503 | __of_changeset_revert(&ov->cset); | 547 | __of_changeset_revert(&ov->cset); |
548 | of_overlay_notify(ov, OF_OVERLAY_POST_REMOVE); | ||
504 | of_free_overlay_info(ov); | 549 | of_free_overlay_info(ov); |
505 | idr_remove(&ov_idr, id); | 550 | idr_remove(&ov_idr, id); |
506 | of_changeset_destroy(&ov->cset); | 551 | of_changeset_destroy(&ov->cset); |
diff --git a/include/linux/of.h b/include/linux/of.h index 299aeb192727..d72f01009297 100644 --- a/include/linux/of.h +++ b/include/linux/of.h | |||
@@ -1266,6 +1266,18 @@ static inline bool of_device_is_system_power_controller(const struct device_node | |||
1266 | * Overlay support | 1266 | * Overlay support |
1267 | */ | 1267 | */ |
1268 | 1268 | ||
1269 | enum of_overlay_notify_action { | ||
1270 | OF_OVERLAY_PRE_APPLY, | ||
1271 | OF_OVERLAY_POST_APPLY, | ||
1272 | OF_OVERLAY_PRE_REMOVE, | ||
1273 | OF_OVERLAY_POST_REMOVE, | ||
1274 | }; | ||
1275 | |||
1276 | struct of_overlay_notify_data { | ||
1277 | struct device_node *overlay; | ||
1278 | struct device_node *target; | ||
1279 | }; | ||
1280 | |||
1269 | #ifdef CONFIG_OF_OVERLAY | 1281 | #ifdef CONFIG_OF_OVERLAY |
1270 | 1282 | ||
1271 | /* ID based overlays; the API for external users */ | 1283 | /* ID based overlays; the API for external users */ |
@@ -1273,6 +1285,9 @@ int of_overlay_create(struct device_node *tree); | |||
1273 | int of_overlay_destroy(int id); | 1285 | int of_overlay_destroy(int id); |
1274 | int of_overlay_destroy_all(void); | 1286 | int of_overlay_destroy_all(void); |
1275 | 1287 | ||
1288 | int of_overlay_notifier_register(struct notifier_block *nb); | ||
1289 | int of_overlay_notifier_unregister(struct notifier_block *nb); | ||
1290 | |||
1276 | #else | 1291 | #else |
1277 | 1292 | ||
1278 | static inline int of_overlay_create(struct device_node *tree) | 1293 | static inline int of_overlay_create(struct device_node *tree) |
@@ -1290,6 +1305,16 @@ static inline int of_overlay_destroy_all(void) | |||
1290 | return -ENOTSUPP; | 1305 | return -ENOTSUPP; |
1291 | } | 1306 | } |
1292 | 1307 | ||
1308 | static inline int of_overlay_notifier_register(struct notifier_block *nb) | ||
1309 | { | ||
1310 | return 0; | ||
1311 | } | ||
1312 | |||
1313 | static inline int of_overlay_notifier_unregister(struct notifier_block *nb) | ||
1314 | { | ||
1315 | return 0; | ||
1316 | } | ||
1317 | |||
1293 | #endif | 1318 | #endif |
1294 | 1319 | ||
1295 | #endif /* _LINUX_OF_H */ | 1320 | #endif /* _LINUX_OF_H */ |