Commit 39a842e2 authored by Alan Tull's avatar Alan Tull Committed by Greg Kroah-Hartman

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: default avatarAlan Tull <atull@opensource.altera.com>
Acked-by: default avatarRob Herring <robh@kernel.org>
Acked-by: default avatarMoritz Fischer <moritz.fischer@ettus.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c1eac0d0
...@@ -58,6 +58,41 @@ struct of_overlay { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 ...@@ -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); ...@@ -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) ...@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment