| From 34c5493a5611f3c1481d98767657ead412c55c18 Mon Sep 17 00:00:00 2001 |
| From: Alan Tull <atull@opensource.altera.com> |
| Date: Tue, 1 Nov 2016 14:14:22 -0500 |
| Subject: [PATCH 024/103] 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> |
| --- |
| drivers/of/overlay.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- |
| include/linux/of.h | 25 +++++++++++++++++++++++++ |
| 2 files changed, 71 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/of/overlay.c |
| +++ b/drivers/of/overlay.c |
| @@ -58,6 +58,41 @@ struct of_overlay { |
| static int of_overlay_apply_one(struct of_overlay *ov, |
| struct device_node *target, const struct device_node *overlay); |
| |
| +static BLOCKING_NOTIFIER_HEAD(of_overlay_chain); |
| + |
| +int of_overlay_notifier_register(struct notifier_block *nb) |
| +{ |
| + return blocking_notifier_chain_register(&of_overlay_chain, nb); |
| +} |
| +EXPORT_SYMBOL_GPL(of_overlay_notifier_register); |
| + |
| +int of_overlay_notifier_unregister(struct notifier_block *nb) |
| +{ |
| + return blocking_notifier_chain_unregister(&of_overlay_chain, nb); |
| +} |
| +EXPORT_SYMBOL_GPL(of_overlay_notifier_unregister); |
| + |
| +static int of_overlay_notify(struct of_overlay *ov, |
| + enum of_overlay_notify_action action) |
| +{ |
| + struct of_overlay_notify_data nd; |
| + int i, ret; |
| + |
| + for (i = 0; i < ov->count; i++) { |
| + struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; |
| + |
| + nd.target = ovinfo->target; |
| + nd.overlay = ovinfo->overlay; |
| + |
| + ret = blocking_notifier_call_chain(&of_overlay_chain, |
| + action, &nd); |
| + if (ret) |
| + return notifier_to_errno(ret); |
| + } |
| + |
| + return 0; |
| +} |
| + |
| static int of_overlay_apply_single_property(struct of_overlay *ov, |
| struct device_node *target, struct property *prop) |
| { |
| @@ -368,6 +403,13 @@ int of_overlay_create(struct device_node |
| goto err_free_idr; |
| } |
| |
| + err = of_overlay_notify(ov, OF_OVERLAY_PRE_APPLY); |
| + if (err < 0) { |
| + pr_err("%s: Pre-apply notifier failed (err=%d)\n", |
| + __func__, err); |
| + goto err_free_idr; |
| + } |
| + |
| /* apply the overlay */ |
| err = of_overlay_apply(ov); |
| if (err) |
| @@ -382,6 +424,8 @@ int of_overlay_create(struct device_node |
| /* add to the tail of the overlay list */ |
| list_add_tail(&ov->node, &ov_list); |
| |
| + of_overlay_notify(ov, OF_OVERLAY_POST_APPLY); |
| + |
| mutex_unlock(&of_mutex); |
| |
| return id; |
| @@ -498,9 +542,10 @@ int of_overlay_destroy(int id) |
| goto out; |
| } |
| |
| - |
| + of_overlay_notify(ov, OF_OVERLAY_PRE_REMOVE); |
| list_del(&ov->node); |
| __of_changeset_revert(&ov->cset); |
| + of_overlay_notify(ov, OF_OVERLAY_POST_REMOVE); |
| of_free_overlay_info(ov); |
| idr_remove(&ov_idr, id); |
| of_changeset_destroy(&ov->cset); |
| --- a/include/linux/of.h |
| +++ b/include/linux/of.h |
| @@ -1266,6 +1266,18 @@ static inline bool of_device_is_system_p |
| * Overlay support |
| */ |
| |
| +enum of_overlay_notify_action { |
| + OF_OVERLAY_PRE_APPLY, |
| + OF_OVERLAY_POST_APPLY, |
| + OF_OVERLAY_PRE_REMOVE, |
| + OF_OVERLAY_POST_REMOVE, |
| +}; |
| + |
| +struct of_overlay_notify_data { |
| + struct device_node *overlay; |
| + struct device_node *target; |
| +}; |
| + |
| #ifdef CONFIG_OF_OVERLAY |
| |
| /* ID based overlays; the API for external users */ |
| @@ -1273,6 +1285,9 @@ int of_overlay_create(struct device_node |
| int of_overlay_destroy(int id); |
| int of_overlay_destroy_all(void); |
| |
| +int of_overlay_notifier_register(struct notifier_block *nb); |
| +int of_overlay_notifier_unregister(struct notifier_block *nb); |
| + |
| #else |
| |
| static inline int of_overlay_create(struct device_node *tree) |
| @@ -1290,6 +1305,16 @@ static inline int of_overlay_destroy_all |
| return -ENOTSUPP; |
| } |
| |
| +static inline int of_overlay_notifier_register(struct notifier_block *nb) |
| +{ |
| + return 0; |
| +} |
| + |
| +static inline int of_overlay_notifier_unregister(struct notifier_block *nb) |
| +{ |
| + return 0; |
| +} |
| + |
| #endif |
| |
| #endif /* _LINUX_OF_H */ |