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>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
c1eac0d034
commit
39a842e22c
@@ -58,6 +58,41 @@ struct of_overlay {
|
|||||||
static int of_overlay_apply_one(struct of_overlay *ov,
|
static int of_overlay_apply_one(struct of_overlay *ov,
|
||||||
struct device_node *target, const struct device_node *overlay);
|
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,
|
static int of_overlay_apply_single_property(struct of_overlay *ov,
|
||||||
struct device_node *target, struct property *prop)
|
struct device_node *target, struct property *prop)
|
||||||
{
|
{
|
||||||
@@ -368,6 +403,13 @@ int of_overlay_create(struct device_node *tree)
|
|||||||
goto err_free_idr;
|
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 */
|
/* apply the overlay */
|
||||||
err = of_overlay_apply(ov);
|
err = of_overlay_apply(ov);
|
||||||
if (err)
|
if (err)
|
||||||
@@ -382,6 +424,8 @@ int of_overlay_create(struct device_node *tree)
|
|||||||
/* add to the tail of the overlay list */
|
/* add to the tail of the overlay list */
|
||||||
list_add_tail(&ov->node, &ov_list);
|
list_add_tail(&ov->node, &ov_list);
|
||||||
|
|
||||||
|
of_overlay_notify(ov, OF_OVERLAY_POST_APPLY);
|
||||||
|
|
||||||
mutex_unlock(&of_mutex);
|
mutex_unlock(&of_mutex);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
@@ -498,9 +542,10 @@ int of_overlay_destroy(int id)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
of_overlay_notify(ov, OF_OVERLAY_PRE_REMOVE);
|
||||||
list_del(&ov->node);
|
list_del(&ov->node);
|
||||||
__of_changeset_revert(&ov->cset);
|
__of_changeset_revert(&ov->cset);
|
||||||
|
of_overlay_notify(ov, OF_OVERLAY_POST_REMOVE);
|
||||||
of_free_overlay_info(ov);
|
of_free_overlay_info(ov);
|
||||||
idr_remove(&ov_idr, id);
|
idr_remove(&ov_idr, id);
|
||||||
of_changeset_destroy(&ov->cset);
|
of_changeset_destroy(&ov->cset);
|
||||||
|
@@ -1266,6 +1266,18 @@ static inline bool of_device_is_system_power_controller(const struct device_node
|
|||||||
* Overlay support
|
* 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
|
#ifdef CONFIG_OF_OVERLAY
|
||||||
|
|
||||||
/* ID based overlays; the API for external users */
|
/* 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(int id);
|
||||||
int of_overlay_destroy_all(void);
|
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
|
#else
|
||||||
|
|
||||||
static inline int of_overlay_create(struct device_node *tree)
|
static inline int of_overlay_create(struct device_node *tree)
|
||||||
@@ -1290,6 +1305,16 @@ static inline int of_overlay_destroy_all(void)
|
|||||||
return -ENOTSUPP;
|
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
|
||||||
|
|
||||||
#endif /* _LINUX_OF_H */
|
#endif /* _LINUX_OF_H */
|
||||||
|
Reference in New Issue
Block a user