Add overlay callback functionality. When DT overlays are being added, some drivers/subsystems will want to know about the changes before they go into the live tree. Similarly there is a need for post-remove callbacks. Each handler is registered with a of_device_id. When an overlay target matches a handler's id, the handler gets called. The following 4 cases are handled: pre-apply, post-apply, pre-remove, and post-remove. Signed-off-by: Alan Tull <atull@xxxxxxxxxxxxxxxxxxxxx> --- drivers/of/overlay.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/of.h | 31 +++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 8225081..ee88e4e 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -20,9 +20,13 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/idr.h> +#include <linux/list.h> #include "of_private.h" +static DEFINE_SPINLOCK(of_overlay_handler_lock); +static LIST_HEAD(of_overlay_handler_list); + /** * struct of_overlay_info - Holds a single overlay info * @target: target of the overlay operation @@ -56,6 +60,80 @@ struct of_overlay { static int of_overlay_apply_one(struct of_overlay *ov, struct device_node *target, const struct device_node *overlay); +/* + * Send overlay callbacks to handlers that match. This call is blocking. In + * the case OF_OVERLAY_PRE_APPLY, return error for the first handler that fails. + * Otherwise, notify all the matching handlers and return success. + */ +static int send_overlay_callbacks(struct of_overlay *ov, int type) +{ + int i, ret; + + spin_lock(&of_overlay_handler_lock); + + for (i = 0; i < ov->count; i++) { + struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; + struct of_overlay_handler *handler; + int (*callback)(struct of_overlay_msg *msg); + struct of_overlay_msg msg_data; + + msg_data.target = ovinfo->target; + msg_data.overlay = ovinfo->overlay; + + list_for_each_entry(handler, &of_overlay_handler_list, node) { + msg_data.priv = handler->priv; + switch (type) { + case OF_OVERLAY_PRE_APPLY: + callback = handler->pre_apply_callback; + break; + case OF_OVERLAY_POST_APPLY: + callback = handler->post_apply_callback; + break; + case OF_OVERLAY_PRE_REMOVE: + callback = handler->pre_remove_callback; + break; + case OF_OVERLAY_POST_REMOVE: + callback = handler->post_remove_callback; + break; + default: + continue; + }; + if (!callback) + continue; + if (of_match_node(handler->of_match, ovinfo->target)) { + ret = callback(&msg_data); + if ((ret < 0) && (type == OF_OVERLAY_PRE_APPLY)) { + spin_unlock(&of_overlay_handler_lock); + return ret; + } + continue; + } + } + } + + spin_unlock(&of_overlay_handler_lock); + + return 0; +} + +int of_overlay_handler_register(struct of_overlay_handler *handler) +{ + spin_lock(&of_overlay_handler_lock); + list_add_tail(&handler->node, &of_overlay_handler_list); + spin_unlock(&of_overlay_handler_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(of_overlay_handler_register); + +void of_overlay_handler_unregister(struct of_overlay_handler *handler) +{ + spin_lock(&of_overlay_handler_lock); + list_del(&handler->node); + spin_unlock(&of_overlay_handler_lock); +} +EXPORT_SYMBOL_GPL(of_overlay_handler_unregister); + static int of_overlay_apply_single_property(struct of_overlay *ov, struct device_node *target, struct property *prop) { @@ -370,6 +448,13 @@ int of_overlay_create(struct device_node *tree) goto err_free_idr; } + err = send_overlay_callbacks(ov, OF_OVERLAY_PRE_APPLY); + if (err < 0) { + pr_err("%s: Pre apply handler failed (err=%d)\n", + __func__, err); + goto err_free_idr; + } + /* apply the overlay */ err = of_overlay_apply(ov); if (err) { @@ -389,6 +474,8 @@ int of_overlay_create(struct device_node *tree) /* add to the tail of the overlay list */ list_add_tail(&ov->node, &ov_list); + send_overlay_callbacks(ov, OF_OVERLAY_POST_APPLY); + mutex_unlock(&of_mutex); return id; @@ -509,9 +596,10 @@ int of_overlay_destroy(int id) goto out; } - + send_overlay_callbacks(ov, OF_OVERLAY_PRE_REMOVE); list_del(&ov->node); __of_changeset_revert(&ov->cset); + send_overlay_callbacks(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 dc6e396..def9481 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -101,9 +101,33 @@ static inline int of_node_is_attached(struct device_node *node) return node && node->kobj.state_in_sysfs; } +/* Callback types */ +#define OF_OVERLAY_PRE_APPLY (0) +#define OF_OVERLAY_POST_APPLY (1) +#define OF_OVERLAY_PRE_REMOVE (2) +#define OF_OVERLAY_POST_REMOVE (3) + +struct of_overlay_msg { + struct device_node *overlay; + struct device_node *target; + void *priv; +}; + +struct of_overlay_handler { + int (*pre_apply_callback)(struct of_overlay_msg *msg); + int (*post_apply_callback)(struct of_overlay_msg *msg); + int (*pre_remove_callback)(struct of_overlay_msg *msg); + int (*post_remove_callback)(struct of_overlay_msg *msg); + const struct of_device_id *of_match; + struct list_head node; + void *priv; +}; + #ifdef CONFIG_OF_DYNAMIC extern struct device_node *of_node_get(struct device_node *node); extern void of_node_put(struct device_node *node); +extern int of_overlay_handler_register(struct of_overlay_handler *handler); +extern void of_overlay_handler_unregister(struct of_overlay_handler *handler); #else /* CONFIG_OF_DYNAMIC */ /* Dummy ref counting routines - to be implemented later */ static inline struct device_node *of_node_get(struct device_node *node) @@ -111,6 +135,13 @@ static inline struct device_node *of_node_get(struct device_node *node) return node; } static inline void of_node_put(struct device_node *node) { } +static int of_overlay_handler_register(struct of_overlay_handler *handler) +{ + return -EINVAL; +} +static void of_overlay_handler_unregister(struct of_overlay_handler *handler) +{ +} #endif /* !CONFIG_OF_DYNAMIC */ /* Pointer for first entry in chain of all nodes. */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html