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 { 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 *tree) 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 *tree) /* 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); 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 * 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 *tree); 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(void) 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 */