1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
3 // Copyright (c) 2018, Linaro Limited
5 #include <linux/kernel.h>
6 #include <linux/module.h>
7 #include <linux/device.h>
8 #include <linux/spinlock.h>
10 #include <linux/slab.h>
11 #include <linux/of_device.h>
12 #include <linux/soc/qcom/apr.h>
13 #include <linux/rpmsg.h>
17 struct rpmsg_endpoint
*ch
;
25 * apr_send_pkt() - Send a apr message from apr device
27 * @adev: Pointer to previously registered apr device.
28 * @pkt: Pointer to apr packet to send
30 * Return: Will be an negative on packet size on success.
32 int apr_send_pkt(struct apr_device
*adev
, struct apr_pkt
*pkt
)
34 struct apr
*apr
= dev_get_drvdata(adev
->dev
.parent
);
39 spin_lock_irqsave(&adev
->lock
, flags
);
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
;
47 ret
= rpmsg_trysend(apr
->ch
, pkt
, hdr
->pkt_size
);
48 spin_unlock_irqrestore(&adev
->lock
, flags
);
50 return ret
? ret
: hdr
->pkt_size
;
52 EXPORT_SYMBOL_GPL(apr_send_pkt
);
54 static void apr_dev_release(struct device
*dev
)
56 struct apr_device
*adev
= to_apr_device(dev
);
61 static int apr_callback(struct rpmsg_device
*rpdev
, void *buf
,
62 int len
, void *priv
, u32 addr
)
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
;
72 if (len
<= APR_HDR_SIZE
) {
73 dev_err(apr
->dev
, "APR: Improper apr pkt received:%p %d\n",
79 ver
= APR_HDR_FIELD_VER(hdr
->hdr_field
);
80 if (ver
> APR_PKT_VER
+ 1)
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
);
89 if (hdr
->pkt_size
< APR_HDR_SIZE
|| hdr
->pkt_size
!= len
) {
90 dev_err(apr
->dev
, "APR: Wrong packet size\n");
94 msg_type
= APR_HDR_FIELD_MT(hdr
->hdr_field
);
95 if (msg_type
>= APR_MSG_TYPE_MAX
) {
96 dev_err(apr
->dev
, "APR: Wrong message type: %d\n", msg_type
);
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");
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
);
116 dev_err(apr
->dev
, "APR: service is not registered\n");
121 resp
.payload_size
= hdr
->pkt_size
- hdr_size
;
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
127 if (resp
.payload_size
> 0)
128 resp
.payload
= buf
+ hdr_size
;
130 adrv
->callback(svc
, &resp
);
135 static int apr_device_match(struct device
*dev
, struct device_driver
*drv
)
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
;
141 /* Attempt an OF style match first */
142 if (of_driver_match_device(dev
, drv
))
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
)
158 static int apr_device_probe(struct device
*dev
)
160 struct apr_device
*adev
= to_apr_device(dev
);
161 struct apr_driver
*adrv
= to_apr_driver(dev
->driver
);
163 return adrv
->probe(adev
);
166 static int apr_device_remove(struct device
*dev
)
168 struct apr_device
*adev
= to_apr_device(dev
);
169 struct apr_driver
*adrv
;
170 struct apr
*apr
= dev_get_drvdata(adev
->dev
.parent
);
173 adrv
= to_apr_driver(dev
->driver
);
176 spin_lock(&apr
->svcs_lock
);
177 idr_remove(&apr
->svcs_idr
, adev
->svc_id
);
178 spin_unlock(&apr
->svcs_lock
);
184 static int apr_uevent(struct device
*dev
, struct kobj_uevent_env
*env
)
186 struct apr_device
*adev
= to_apr_device(dev
);
189 ret
= of_device_uevent_modalias(dev
, env
);
193 return add_uevent_var(env
, "MODALIAS=apr:%s", adev
->name
);
196 struct bus_type aprbus
= {
198 .match
= apr_device_match
,
199 .probe
= apr_device_probe
,
200 .uevent
= apr_uevent
,
201 .remove
= apr_device_remove
,
203 EXPORT_SYMBOL_GPL(aprbus
);
205 static int apr_add_device(struct device
*dev
, struct device_node
*np
,
206 const struct apr_device_id
*id
)
208 struct apr
*apr
= dev_get_drvdata(dev
);
209 struct apr_device
*adev
= NULL
;
212 adev
= kzalloc(sizeof(*adev
), GFP_KERNEL
);
216 spin_lock_init(&adev
->lock
);
218 adev
->svc_id
= id
->svc_id
;
219 adev
->domain_id
= id
->domain_id
;
220 adev
->version
= id
->svc_version
;
222 snprintf(adev
->name
, APR_NAME_SIZE
, "%pOFn", np
);
224 strscpy(adev
->name
, id
->name
, APR_NAME_SIZE
);
226 dev_set_name(&adev
->dev
, "aprsvc:%s:%x:%x", adev
->name
,
227 id
->domain_id
, id
->svc_id
);
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
;
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
);
240 dev_info(dev
, "Adding APR dev: %s\n", dev_name(&adev
->dev
));
242 ret
= device_register(&adev
->dev
);
244 dev_err(dev
, "device_register failed: %d\n", ret
);
245 put_device(&adev
->dev
);
251 static void of_register_apr_devices(struct device
*dev
)
253 struct apr
*apr
= dev_get_drvdata(dev
);
254 struct device_node
*node
;
256 for_each_child_of_node(dev
->of_node
, node
) {
257 struct apr_device_id id
= { {0} };
259 if (of_property_read_u32(node
, "reg", &id
.svc_id
))
262 id
.domain_id
= apr
->dest_domain_id
;
264 if (apr_add_device(dev
, node
, &id
))
265 dev_err(dev
, "Failed to add apr %d svc\n", id
.svc_id
);
269 static int apr_probe(struct rpmsg_device
*rpdev
)
271 struct device
*dev
= &rpdev
->dev
;
275 apr
= devm_kzalloc(dev
, sizeof(*apr
), GFP_KERNEL
);
279 ret
= of_property_read_u32(dev
->of_node
, "reg", &apr
->dest_domain_id
);
281 dev_err(dev
, "APR Domain ID not specified in DT\n");
285 dev_set_drvdata(dev
, apr
);
286 apr
->ch
= rpdev
->ept
;
288 spin_lock_init(&apr
->svcs_lock
);
289 idr_init(&apr
->svcs_idr
);
290 of_register_apr_devices(dev
);
295 static int apr_remove_device(struct device
*dev
, void *null
)
297 struct apr_device
*adev
= to_apr_device(dev
);
299 device_unregister(&adev
->dev
);
304 static void apr_remove(struct rpmsg_device
*rpdev
)
306 device_for_each_child(&rpdev
->dev
, NULL
, apr_remove_device
);
310 * __apr_driver_register() - Client driver registration with aprbus
312 * @drv:Client driver to be associated with client-device.
313 * @owner: owning module/driver
315 * This API will register the client driver with the aprbus
316 * It is called from the driver's module-init function.
318 int __apr_driver_register(struct apr_driver
*drv
, struct module
*owner
)
320 drv
->driver
.bus
= &aprbus
;
321 drv
->driver
.owner
= owner
;
323 return driver_register(&drv
->driver
);
325 EXPORT_SYMBOL_GPL(__apr_driver_register
);
328 * apr_driver_unregister() - Undo effect of apr_driver_register
330 * @drv: Client driver to be unregistered
332 void apr_driver_unregister(struct apr_driver
*drv
)
334 driver_unregister(&drv
->driver
);
336 EXPORT_SYMBOL_GPL(apr_driver_unregister
);
338 static const struct of_device_id apr_of_match
[] = {
339 { .compatible
= "qcom,apr"},
340 { .compatible
= "qcom,apr-v2"},
343 MODULE_DEVICE_TABLE(of
, apr_of_match
);
345 static struct rpmsg_driver apr_driver
= {
347 .remove
= apr_remove
,
348 .callback
= apr_callback
,
351 .of_match_table
= apr_of_match
,
355 static int __init
apr_init(void)
359 ret
= bus_register(&aprbus
);
361 ret
= register_rpmsg_driver(&apr_driver
);
363 bus_unregister(&aprbus
);
368 static void __exit
apr_exit(void)
370 bus_unregister(&aprbus
);
371 unregister_rpmsg_driver(&apr_driver
);
374 subsys_initcall(apr_init
);
375 module_exit(apr_exit
);
377 MODULE_LICENSE("GPL v2");
378 MODULE_DESCRIPTION("Qualcomm APR Bus");