summaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
authorAlan Tull <atull@opensource.altera.com>2016-11-01 14:14:22 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-11-10 17:03:35 +0100
commit39a842e22c1bf3ec3dce36e01fe8ba8ee66c80c8 (patch)
tree06def1c123cec59f796d6c1ff983ad768a67eafb /drivers/of
parentc1eac0d034d36cfa0da3e35f2add6d6de1e696e5 (diff)
downloadlwn-39a842e22c1bf3ec3dce36e01fe8ba8ee66c80c8.tar.gz
lwn-39a842e22c1bf3ec3dce36e01fe8ba8ee66c80c8.zip
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>
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/overlay.c47
1 files changed, 46 insertions, 1 deletions
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);