Commit | Line | Data |
---|---|---|
6adba21e SK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. | |
3 | // Copyright (c) 2018, Linaro Limited | |
4 | ||
5 | #include <linux/kernel.h> | |
6 | #include <linux/module.h> | |
7 | #include <linux/device.h> | |
8 | #include <linux/spinlock.h> | |
9 | #include <linux/idr.h> | |
10 | #include <linux/slab.h> | |
11 | #include <linux/of_device.h> | |
12 | #include <linux/soc/qcom/apr.h> | |
13 | #include <linux/rpmsg.h> | |
14 | #include <linux/of.h> | |
15 | ||
16 | struct apr { | |
17 | struct rpmsg_endpoint *ch; | |
18 | struct device *dev; | |
19 | spinlock_t svcs_lock; | |
20 | struct idr svcs_idr; | |
21 | int dest_domain_id; | |
22 | }; | |
23 | ||
24 | /** | |
25 | * apr_send_pkt() - Send a apr message from apr device | |
26 | * | |
27 | * @adev: Pointer to previously registered apr device. | |
28 | * @pkt: Pointer to apr packet to send | |
29 | * | |
30 | * Return: Will be an negative on packet size on success. | |
31 | */ | |
32 | int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt) | |
33 | { | |
34 | struct apr *apr = dev_get_drvdata(adev->dev.parent); | |
35 | struct apr_hdr *hdr; | |
36 | unsigned long flags; | |
37 | int ret; | |
38 | ||
39 | spin_lock_irqsave(&adev->lock, flags); | |
40 | ||
41 | hdr = &pkt->hdr; | |
42 | hdr->src_domain = APR_DOMAIN_APPS; | |
43 | hdr->src_svc = adev->svc_id; | |
44 | hdr->dest_domain = adev->domain_id; | |
45 | hdr->dest_svc = adev->svc_id; | |
46 | ||
47 | ret = rpmsg_trysend(apr->ch, pkt, hdr->pkt_size); | |
48 | spin_unlock_irqrestore(&adev->lock, flags); | |
49 | ||
50 | return ret ? ret : hdr->pkt_size; | |
51 | } | |
52 | EXPORT_SYMBOL_GPL(apr_send_pkt); | |
53 | ||
54 | static void apr_dev_release(struct device *dev) | |
55 | { | |
56 | struct apr_device *adev = to_apr_device(dev); | |
57 | ||
58 | kfree(adev); | |
59 | } | |
60 | ||
61 | static int apr_callback(struct rpmsg_device *rpdev, void *buf, | |
62 | int len, void *priv, u32 addr) | |
63 | { | |
64 | struct apr *apr = dev_get_drvdata(&rpdev->dev); | |
65 | uint16_t hdr_size, msg_type, ver, svc_id; | |
66 | struct apr_device *svc = NULL; | |
67 | struct apr_driver *adrv = NULL; | |
68 | struct apr_resp_pkt resp; | |
69 | struct apr_hdr *hdr; | |
70 | unsigned long flags; | |
71 | ||
72 | if (len <= APR_HDR_SIZE) { | |
73 | dev_err(apr->dev, "APR: Improper apr pkt received:%p %d\n", | |
74 | buf, len); | |
75 | return -EINVAL; | |
76 | } | |
77 | ||
78 | hdr = buf; | |
79 | ver = APR_HDR_FIELD_VER(hdr->hdr_field); | |
80 | if (ver > APR_PKT_VER + 1) | |
81 | return -EINVAL; | |
82 | ||
83 | hdr_size = APR_HDR_FIELD_SIZE_BYTES(hdr->hdr_field); | |
84 | if (hdr_size < APR_HDR_SIZE) { | |
85 | dev_err(apr->dev, "APR: Wrong hdr size:%d\n", hdr_size); | |
86 | return -EINVAL; | |
87 | } | |
88 | ||
89 | if (hdr->pkt_size < APR_HDR_SIZE || hdr->pkt_size != len) { | |
35aac0ba | 90 | dev_err(apr->dev, "APR: Wrong packet size\n"); |
6adba21e SK |
91 | return -EINVAL; |
92 | } | |
93 | ||
94 | msg_type = APR_HDR_FIELD_MT(hdr->hdr_field); | |
e744619d | 95 | if (msg_type >= APR_MSG_TYPE_MAX) { |
6adba21e SK |
96 | dev_err(apr->dev, "APR: Wrong message type: %d\n", msg_type); |
97 | return -EINVAL; | |
98 | } | |
99 | ||
100 | if (hdr->src_domain >= APR_DOMAIN_MAX || | |
101 | hdr->dest_domain >= APR_DOMAIN_MAX || | |
102 | hdr->src_svc >= APR_SVC_MAX || | |
103 | hdr->dest_svc >= APR_SVC_MAX) { | |
104 | dev_err(apr->dev, "APR: Wrong APR header\n"); | |
105 | return -EINVAL; | |
106 | } | |
107 | ||
108 | svc_id = hdr->dest_svc; | |
109 | spin_lock_irqsave(&apr->svcs_lock, flags); | |
110 | svc = idr_find(&apr->svcs_idr, svc_id); | |
111 | if (svc && svc->dev.driver) | |
112 | adrv = to_apr_driver(svc->dev.driver); | |
113 | spin_unlock_irqrestore(&apr->svcs_lock, flags); | |
114 | ||
115 | if (!adrv) { | |
116 | dev_err(apr->dev, "APR: service is not registered\n"); | |
117 | return -EINVAL; | |
118 | } | |
119 | ||
120 | resp.hdr = *hdr; | |
121 | resp.payload_size = hdr->pkt_size - hdr_size; | |
122 | ||
123 | /* | |
124 | * NOTE: hdr_size is not same as APR_HDR_SIZE as remote can include | |
125 | * optional headers in to apr_hdr which should be ignored | |
126 | */ | |
127 | if (resp.payload_size > 0) | |
128 | resp.payload = buf + hdr_size; | |
129 | ||
130 | adrv->callback(svc, &resp); | |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
135 | static int apr_device_match(struct device *dev, struct device_driver *drv) | |
136 | { | |
137 | struct apr_device *adev = to_apr_device(dev); | |
138 | struct apr_driver *adrv = to_apr_driver(drv); | |
139 | const struct apr_device_id *id = adrv->id_table; | |
140 | ||
141 | /* Attempt an OF style match first */ | |
142 | if (of_driver_match_device(dev, drv)) | |
143 | return 1; | |
144 | ||
145 | if (!id) | |
146 | return 0; | |
147 | ||
148 | while (id->domain_id != 0 || id->svc_id != 0) { | |
149 | if (id->domain_id == adev->domain_id && | |
150 | id->svc_id == adev->svc_id) | |
151 | return 1; | |
152 | id++; | |
153 | } | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | static int apr_device_probe(struct device *dev) | |
159 | { | |
160 | struct apr_device *adev = to_apr_device(dev); | |
161 | struct apr_driver *adrv = to_apr_driver(dev->driver); | |
162 | ||
163 | return adrv->probe(adev); | |
164 | } | |
165 | ||
166 | static int apr_device_remove(struct device *dev) | |
167 | { | |
168 | struct apr_device *adev = to_apr_device(dev); | |
169 | struct apr_driver *adrv; | |
170 | struct apr *apr = dev_get_drvdata(adev->dev.parent); | |
171 | ||
172 | if (dev->driver) { | |
173 | adrv = to_apr_driver(dev->driver); | |
174 | if (adrv->remove) | |
175 | adrv->remove(adev); | |
176 | spin_lock(&apr->svcs_lock); | |
177 | idr_remove(&apr->svcs_idr, adev->svc_id); | |
178 | spin_unlock(&apr->svcs_lock); | |
179 | } | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | static int apr_uevent(struct device *dev, struct kobj_uevent_env *env) | |
185 | { | |
186 | struct apr_device *adev = to_apr_device(dev); | |
187 | int ret; | |
188 | ||
189 | ret = of_device_uevent_modalias(dev, env); | |
190 | if (ret != -ENODEV) | |
191 | return ret; | |
192 | ||
193 | return add_uevent_var(env, "MODALIAS=apr:%s", adev->name); | |
194 | } | |
195 | ||
196 | struct bus_type aprbus = { | |
197 | .name = "aprbus", | |
198 | .match = apr_device_match, | |
199 | .probe = apr_device_probe, | |
200 | .uevent = apr_uevent, | |
201 | .remove = apr_device_remove, | |
202 | }; | |
203 | EXPORT_SYMBOL_GPL(aprbus); | |
204 | ||
205 | static int apr_add_device(struct device *dev, struct device_node *np, | |
206 | const struct apr_device_id *id) | |
207 | { | |
208 | struct apr *apr = dev_get_drvdata(dev); | |
209 | struct apr_device *adev = NULL; | |
210 | int ret; | |
211 | ||
212 | adev = kzalloc(sizeof(*adev), GFP_KERNEL); | |
213 | if (!adev) | |
214 | return -ENOMEM; | |
215 | ||
216 | spin_lock_init(&adev->lock); | |
217 | ||
218 | adev->svc_id = id->svc_id; | |
219 | adev->domain_id = id->domain_id; | |
220 | adev->version = id->svc_version; | |
221 | if (np) | |
dc37a252 | 222 | snprintf(adev->name, APR_NAME_SIZE, "%pOFn", np); |
6adba21e | 223 | else |
4fadb265 | 224 | strscpy(adev->name, id->name, APR_NAME_SIZE); |
6adba21e SK |
225 | |
226 | dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name, | |
227 | id->domain_id, id->svc_id); | |
228 | ||
229 | adev->dev.bus = &aprbus; | |
230 | adev->dev.parent = dev; | |
231 | adev->dev.of_node = np; | |
232 | adev->dev.release = apr_dev_release; | |
233 | adev->dev.driver = NULL; | |
234 | ||
235 | spin_lock(&apr->svcs_lock); | |
236 | idr_alloc(&apr->svcs_idr, adev, id->svc_id, | |
237 | id->svc_id + 1, GFP_ATOMIC); | |
238 | spin_unlock(&apr->svcs_lock); | |
239 | ||
240 | dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev)); | |
241 | ||
242 | ret = device_register(&adev->dev); | |
243 | if (ret) { | |
244 | dev_err(dev, "device_register failed: %d\n", ret); | |
245 | put_device(&adev->dev); | |
246 | } | |
247 | ||
248 | return ret; | |
249 | } | |
250 | ||
251 | static void of_register_apr_devices(struct device *dev) | |
252 | { | |
253 | struct apr *apr = dev_get_drvdata(dev); | |
254 | struct device_node *node; | |
255 | ||
256 | for_each_child_of_node(dev->of_node, node) { | |
257 | struct apr_device_id id = { {0} }; | |
258 | ||
259 | if (of_property_read_u32(node, "reg", &id.svc_id)) | |
260 | continue; | |
261 | ||
262 | id.domain_id = apr->dest_domain_id; | |
263 | ||
264 | if (apr_add_device(dev, node, &id)) | |
265 | dev_err(dev, "Failed to add apr %d svc\n", id.svc_id); | |
266 | } | |
267 | } | |
268 | ||
269 | static int apr_probe(struct rpmsg_device *rpdev) | |
270 | { | |
271 | struct device *dev = &rpdev->dev; | |
272 | struct apr *apr; | |
273 | int ret; | |
274 | ||
275 | apr = devm_kzalloc(dev, sizeof(*apr), GFP_KERNEL); | |
276 | if (!apr) | |
277 | return -ENOMEM; | |
278 | ||
279 | ret = of_property_read_u32(dev->of_node, "reg", &apr->dest_domain_id); | |
280 | if (ret) { | |
281 | dev_err(dev, "APR Domain ID not specified in DT\n"); | |
282 | return ret; | |
283 | } | |
284 | ||
285 | dev_set_drvdata(dev, apr); | |
286 | apr->ch = rpdev->ept; | |
287 | apr->dev = dev; | |
288 | spin_lock_init(&apr->svcs_lock); | |
289 | idr_init(&apr->svcs_idr); | |
290 | of_register_apr_devices(dev); | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | static int apr_remove_device(struct device *dev, void *null) | |
296 | { | |
297 | struct apr_device *adev = to_apr_device(dev); | |
298 | ||
299 | device_unregister(&adev->dev); | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | static void apr_remove(struct rpmsg_device *rpdev) | |
305 | { | |
306 | device_for_each_child(&rpdev->dev, NULL, apr_remove_device); | |
307 | } | |
308 | ||
309 | /* | |
310 | * __apr_driver_register() - Client driver registration with aprbus | |
311 | * | |
312 | * @drv:Client driver to be associated with client-device. | |
313 | * @owner: owning module/driver | |
314 | * | |
315 | * This API will register the client driver with the aprbus | |
316 | * It is called from the driver's module-init function. | |
317 | */ | |
318 | int __apr_driver_register(struct apr_driver *drv, struct module *owner) | |
319 | { | |
320 | drv->driver.bus = &aprbus; | |
321 | drv->driver.owner = owner; | |
322 | ||
323 | return driver_register(&drv->driver); | |
324 | } | |
325 | EXPORT_SYMBOL_GPL(__apr_driver_register); | |
326 | ||
327 | /* | |
328 | * apr_driver_unregister() - Undo effect of apr_driver_register | |
329 | * | |
330 | * @drv: Client driver to be unregistered | |
331 | */ | |
332 | void apr_driver_unregister(struct apr_driver *drv) | |
333 | { | |
334 | driver_unregister(&drv->driver); | |
335 | } | |
336 | EXPORT_SYMBOL_GPL(apr_driver_unregister); | |
337 | ||
338 | static const struct of_device_id apr_of_match[] = { | |
339 | { .compatible = "qcom,apr"}, | |
340 | { .compatible = "qcom,apr-v2"}, | |
341 | {} | |
342 | }; | |
343 | MODULE_DEVICE_TABLE(of, apr_of_match); | |
344 | ||
345 | static struct rpmsg_driver apr_driver = { | |
346 | .probe = apr_probe, | |
347 | .remove = apr_remove, | |
348 | .callback = apr_callback, | |
349 | .drv = { | |
350 | .name = "qcom,apr", | |
351 | .of_match_table = apr_of_match, | |
352 | }, | |
353 | }; | |
354 | ||
355 | static int __init apr_init(void) | |
356 | { | |
357 | int ret; | |
358 | ||
359 | ret = bus_register(&aprbus); | |
360 | if (!ret) | |
361 | ret = register_rpmsg_driver(&apr_driver); | |
362 | else | |
363 | bus_unregister(&aprbus); | |
364 | ||
365 | return ret; | |
366 | } | |
367 | ||
368 | static void __exit apr_exit(void) | |
369 | { | |
370 | bus_unregister(&aprbus); | |
371 | unregister_rpmsg_driver(&apr_driver); | |
372 | } | |
373 | ||
374 | subsys_initcall(apr_init); | |
375 | module_exit(apr_exit); | |
376 | ||
377 | MODULE_LICENSE("GPL v2"); | |
378 | MODULE_DESCRIPTION("Qualcomm APR Bus"); |