1 /*
2  * Qualcomm Peripheral Image Loader helpers
3  *
4  * Copyright (C) 2016 Linaro Ltd
5  * Copyright (C) 2015 Sony Mobile Communications Inc
6  * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * version 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17 
18 #include <linux/firmware.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/notifier.h>
22 #include <linux/remoteproc.h>
23 #include <linux/rpmsg/qcom_glink.h>
24 #include <linux/rpmsg/qcom_smd.h>
25 
26 #include "remoteproc_internal.h"
27 #include "qcom_common.h"
28 
29 #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
30 #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
31 #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
32 
33 static BLOCKING_NOTIFIER_HEAD(ssr_notifiers);
34 
35 static int glink_subdev_probe(struct rproc_subdev *subdev)
36 {
37 	struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
38 
39 	glink->edge = qcom_glink_smem_register(glink->dev, glink->node);
40 
41 	return PTR_ERR_OR_ZERO(glink->edge);
42 }
43 
44 static void glink_subdev_remove(struct rproc_subdev *subdev)
45 {
46 	struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
47 
48 	qcom_glink_smem_unregister(glink->edge);
49 	glink->edge = NULL;
50 }
51 
52 /**
53  * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc
54  * @rproc:	rproc handle to parent the subdevice
55  * @glink:	reference to a GLINK subdev context
56  */
57 void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
58 {
59 	struct device *dev = &rproc->dev;
60 
61 	glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge");
62 	if (!glink->node)
63 		return;
64 
65 	glink->dev = dev;
66 	rproc_add_subdev(rproc, &glink->subdev, glink_subdev_probe, glink_subdev_remove);
67 }
68 EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
69 
70 /**
71  * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc
72  * @rproc:	rproc handle
73  * @glink:	reference to a GLINK subdev context
74  */
75 void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
76 {
77 	rproc_remove_subdev(rproc, &glink->subdev);
78 	of_node_put(glink->node);
79 }
80 EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
81 
82 static int smd_subdev_probe(struct rproc_subdev *subdev)
83 {
84 	struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
85 
86 	smd->edge = qcom_smd_register_edge(smd->dev, smd->node);
87 
88 	return PTR_ERR_OR_ZERO(smd->edge);
89 }
90 
91 static void smd_subdev_remove(struct rproc_subdev *subdev)
92 {
93 	struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
94 
95 	qcom_smd_unregister_edge(smd->edge);
96 	smd->edge = NULL;
97 }
98 
99 /**
100  * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc
101  * @rproc:	rproc handle to parent the subdevice
102  * @smd:	reference to a Qualcomm subdev context
103  */
104 void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
105 {
106 	struct device *dev = &rproc->dev;
107 
108 	smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge");
109 	if (!smd->node)
110 		return;
111 
112 	smd->dev = dev;
113 	rproc_add_subdev(rproc, &smd->subdev, smd_subdev_probe, smd_subdev_remove);
114 }
115 EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
116 
117 /**
118  * qcom_remove_smd_subdev() - remove the smd subdevice from rproc
119  * @rproc:	rproc handle
120  * @smd:	the SMD subdevice to remove
121  */
122 void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
123 {
124 	rproc_remove_subdev(rproc, &smd->subdev);
125 	of_node_put(smd->node);
126 }
127 EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
128 
129 /**
130  * qcom_register_ssr_notifier() - register SSR notification handler
131  * @nb:		notifier_block to notify for restart notifications
132  *
133  * Returns 0 on success, negative errno on failure.
134  *
135  * This register the @notify function as handler for restart notifications. As
136  * remote processors are stopped this function will be called, with the SSR
137  * name passed as a parameter.
138  */
139 int qcom_register_ssr_notifier(struct notifier_block *nb)
140 {
141 	return blocking_notifier_chain_register(&ssr_notifiers, nb);
142 }
143 EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
144 
145 /**
146  * qcom_unregister_ssr_notifier() - unregister SSR notification handler
147  * @nb:		notifier_block to unregister
148  */
149 void qcom_unregister_ssr_notifier(struct notifier_block *nb)
150 {
151 	blocking_notifier_chain_unregister(&ssr_notifiers, nb);
152 }
153 EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
154 
155 static int ssr_notify_start(struct rproc_subdev *subdev)
156 {
157 	return  0;
158 }
159 
160 static void ssr_notify_stop(struct rproc_subdev *subdev)
161 {
162 	struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
163 
164 	blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr->name);
165 }
166 
167 /**
168  * qcom_add_ssr_subdev() - register subdevice as restart notification source
169  * @rproc:	rproc handle
170  * @ssr:	SSR subdevice handle
171  * @ssr_name:	identifier to use for notifications originating from @rproc
172  *
173  * As the @ssr is registered with the @rproc SSR events will be sent to all
174  * registered listeners in the system as the remoteproc is shut down.
175  */
176 void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
177 			 const char *ssr_name)
178 {
179 	ssr->name = ssr_name;
180 
181 	rproc_add_subdev(rproc, &ssr->subdev, ssr_notify_start, ssr_notify_stop);
182 }
183 EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
184 
185 /**
186  * qcom_remove_ssr_subdev() - remove subdevice as restart notification source
187  * @rproc:	rproc handle
188  * @ssr:	SSR subdevice handle
189  */
190 void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
191 {
192 	rproc_remove_subdev(rproc, &ssr->subdev);
193 }
194 EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
195 
196 MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
197 MODULE_LICENSE("GPL v2");
198