xref: /openbmc/linux/drivers/misc/fastrpc.c (revision f6f9279f2bf0e37e2f1fb119d8832b8568536a04)
1*f6f9279fSSrinivas Kandagatla // SPDX-License-Identifier: GPL-2.0
2*f6f9279fSSrinivas Kandagatla // Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
3*f6f9279fSSrinivas Kandagatla // Copyright (c) 2018, Linaro Limited
4*f6f9279fSSrinivas Kandagatla 
5*f6f9279fSSrinivas Kandagatla #include <linux/device.h>
6*f6f9279fSSrinivas Kandagatla #include <linux/dma-mapping.h>
7*f6f9279fSSrinivas Kandagatla #include <linux/idr.h>
8*f6f9279fSSrinivas Kandagatla #include <linux/list.h>
9*f6f9279fSSrinivas Kandagatla #include <linux/miscdevice.h>
10*f6f9279fSSrinivas Kandagatla #include <linux/module.h>
11*f6f9279fSSrinivas Kandagatla #include <linux/of_address.h>
12*f6f9279fSSrinivas Kandagatla #include <linux/of.h>
13*f6f9279fSSrinivas Kandagatla #include <linux/of_platform.h>
14*f6f9279fSSrinivas Kandagatla #include <linux/rpmsg.h>
15*f6f9279fSSrinivas Kandagatla #include <linux/scatterlist.h>
16*f6f9279fSSrinivas Kandagatla #include <linux/slab.h>
17*f6f9279fSSrinivas Kandagatla 
18*f6f9279fSSrinivas Kandagatla #define ADSP_DOMAIN_ID (0)
19*f6f9279fSSrinivas Kandagatla #define MDSP_DOMAIN_ID (1)
20*f6f9279fSSrinivas Kandagatla #define SDSP_DOMAIN_ID (2)
21*f6f9279fSSrinivas Kandagatla #define CDSP_DOMAIN_ID (3)
22*f6f9279fSSrinivas Kandagatla #define FASTRPC_DEV_MAX		4 /* adsp, mdsp, slpi, cdsp*/
23*f6f9279fSSrinivas Kandagatla #define FASTRPC_MAX_SESSIONS	9 /*8 compute, 1 cpz*/
24*f6f9279fSSrinivas Kandagatla #define FASTRPC_CTX_MAX (256)
25*f6f9279fSSrinivas Kandagatla #define FASTRPC_CTXID_MASK (0xFF0)
26*f6f9279fSSrinivas Kandagatla #define FASTRPC_DEVICE_NAME	"fastrpc"
27*f6f9279fSSrinivas Kandagatla 
28*f6f9279fSSrinivas Kandagatla #define miscdev_to_cctx(d) container_of(d, struct fastrpc_channel_ctx, miscdev)
29*f6f9279fSSrinivas Kandagatla 
30*f6f9279fSSrinivas Kandagatla static const char *domains[FASTRPC_DEV_MAX] = { "adsp", "mdsp",
31*f6f9279fSSrinivas Kandagatla 						"sdsp", "cdsp"};
32*f6f9279fSSrinivas Kandagatla 
33*f6f9279fSSrinivas Kandagatla struct fastrpc_session_ctx {
34*f6f9279fSSrinivas Kandagatla 	struct device *dev;
35*f6f9279fSSrinivas Kandagatla 	int sid;
36*f6f9279fSSrinivas Kandagatla 	bool used;
37*f6f9279fSSrinivas Kandagatla 	bool valid;
38*f6f9279fSSrinivas Kandagatla };
39*f6f9279fSSrinivas Kandagatla 
40*f6f9279fSSrinivas Kandagatla struct fastrpc_channel_ctx {
41*f6f9279fSSrinivas Kandagatla 	int domain_id;
42*f6f9279fSSrinivas Kandagatla 	int sesscount;
43*f6f9279fSSrinivas Kandagatla 	struct rpmsg_device *rpdev;
44*f6f9279fSSrinivas Kandagatla 	struct fastrpc_session_ctx session[FASTRPC_MAX_SESSIONS];
45*f6f9279fSSrinivas Kandagatla 	spinlock_t lock;
46*f6f9279fSSrinivas Kandagatla 	struct idr ctx_idr;
47*f6f9279fSSrinivas Kandagatla 	struct list_head users;
48*f6f9279fSSrinivas Kandagatla 	struct miscdevice miscdev;
49*f6f9279fSSrinivas Kandagatla };
50*f6f9279fSSrinivas Kandagatla 
51*f6f9279fSSrinivas Kandagatla struct fastrpc_user {
52*f6f9279fSSrinivas Kandagatla 	struct list_head user;
53*f6f9279fSSrinivas Kandagatla 	struct list_head maps;
54*f6f9279fSSrinivas Kandagatla 	struct list_head pending;
55*f6f9279fSSrinivas Kandagatla 
56*f6f9279fSSrinivas Kandagatla 	struct fastrpc_channel_ctx *cctx;
57*f6f9279fSSrinivas Kandagatla 	struct fastrpc_session_ctx *sctx;
58*f6f9279fSSrinivas Kandagatla 
59*f6f9279fSSrinivas Kandagatla 	int tgid;
60*f6f9279fSSrinivas Kandagatla 	int pd;
61*f6f9279fSSrinivas Kandagatla 	/* Lock for lists */
62*f6f9279fSSrinivas Kandagatla 	spinlock_t lock;
63*f6f9279fSSrinivas Kandagatla 	/* lock for allocations */
64*f6f9279fSSrinivas Kandagatla 	struct mutex mutex;
65*f6f9279fSSrinivas Kandagatla };
66*f6f9279fSSrinivas Kandagatla 
67*f6f9279fSSrinivas Kandagatla static struct fastrpc_session_ctx *fastrpc_session_alloc(
68*f6f9279fSSrinivas Kandagatla 					struct fastrpc_channel_ctx *cctx)
69*f6f9279fSSrinivas Kandagatla {
70*f6f9279fSSrinivas Kandagatla 	struct fastrpc_session_ctx *session = NULL;
71*f6f9279fSSrinivas Kandagatla 	int i;
72*f6f9279fSSrinivas Kandagatla 
73*f6f9279fSSrinivas Kandagatla 	spin_lock(&cctx->lock);
74*f6f9279fSSrinivas Kandagatla 	for (i = 0; i < cctx->sesscount; i++) {
75*f6f9279fSSrinivas Kandagatla 		if (!cctx->session[i].used && cctx->session[i].valid) {
76*f6f9279fSSrinivas Kandagatla 			cctx->session[i].used = true;
77*f6f9279fSSrinivas Kandagatla 			session = &cctx->session[i];
78*f6f9279fSSrinivas Kandagatla 			break;
79*f6f9279fSSrinivas Kandagatla 		}
80*f6f9279fSSrinivas Kandagatla 	}
81*f6f9279fSSrinivas Kandagatla 	spin_unlock(&cctx->lock);
82*f6f9279fSSrinivas Kandagatla 
83*f6f9279fSSrinivas Kandagatla 	return session;
84*f6f9279fSSrinivas Kandagatla }
85*f6f9279fSSrinivas Kandagatla 
86*f6f9279fSSrinivas Kandagatla static void fastrpc_session_free(struct fastrpc_channel_ctx *cctx,
87*f6f9279fSSrinivas Kandagatla 				 struct fastrpc_session_ctx *session)
88*f6f9279fSSrinivas Kandagatla {
89*f6f9279fSSrinivas Kandagatla 	spin_lock(&cctx->lock);
90*f6f9279fSSrinivas Kandagatla 	session->used = false;
91*f6f9279fSSrinivas Kandagatla 	spin_unlock(&cctx->lock);
92*f6f9279fSSrinivas Kandagatla }
93*f6f9279fSSrinivas Kandagatla 
94*f6f9279fSSrinivas Kandagatla static int fastrpc_device_release(struct inode *inode, struct file *file)
95*f6f9279fSSrinivas Kandagatla {
96*f6f9279fSSrinivas Kandagatla 	struct fastrpc_user *fl = (struct fastrpc_user *)file->private_data;
97*f6f9279fSSrinivas Kandagatla 	struct fastrpc_channel_ctx *cctx = fl->cctx;
98*f6f9279fSSrinivas Kandagatla 
99*f6f9279fSSrinivas Kandagatla 	spin_lock(&cctx->lock);
100*f6f9279fSSrinivas Kandagatla 	list_del(&fl->user);
101*f6f9279fSSrinivas Kandagatla 	spin_unlock(&cctx->lock);
102*f6f9279fSSrinivas Kandagatla 
103*f6f9279fSSrinivas Kandagatla 	fastrpc_session_free(cctx, fl->sctx);
104*f6f9279fSSrinivas Kandagatla 
105*f6f9279fSSrinivas Kandagatla 	mutex_destroy(&fl->mutex);
106*f6f9279fSSrinivas Kandagatla 	kfree(fl);
107*f6f9279fSSrinivas Kandagatla 	file->private_data = NULL;
108*f6f9279fSSrinivas Kandagatla 
109*f6f9279fSSrinivas Kandagatla 	return 0;
110*f6f9279fSSrinivas Kandagatla }
111*f6f9279fSSrinivas Kandagatla 
112*f6f9279fSSrinivas Kandagatla static int fastrpc_device_open(struct inode *inode, struct file *filp)
113*f6f9279fSSrinivas Kandagatla {
114*f6f9279fSSrinivas Kandagatla 	struct fastrpc_channel_ctx *cctx = miscdev_to_cctx(filp->private_data);
115*f6f9279fSSrinivas Kandagatla 	struct fastrpc_user *fl = NULL;
116*f6f9279fSSrinivas Kandagatla 
117*f6f9279fSSrinivas Kandagatla 	fl = kzalloc(sizeof(*fl), GFP_KERNEL);
118*f6f9279fSSrinivas Kandagatla 	if (!fl)
119*f6f9279fSSrinivas Kandagatla 		return -ENOMEM;
120*f6f9279fSSrinivas Kandagatla 
121*f6f9279fSSrinivas Kandagatla 	filp->private_data = fl;
122*f6f9279fSSrinivas Kandagatla 	spin_lock_init(&fl->lock);
123*f6f9279fSSrinivas Kandagatla 	mutex_init(&fl->mutex);
124*f6f9279fSSrinivas Kandagatla 	INIT_LIST_HEAD(&fl->pending);
125*f6f9279fSSrinivas Kandagatla 	INIT_LIST_HEAD(&fl->maps);
126*f6f9279fSSrinivas Kandagatla 	INIT_LIST_HEAD(&fl->user);
127*f6f9279fSSrinivas Kandagatla 	fl->tgid = current->tgid;
128*f6f9279fSSrinivas Kandagatla 	fl->cctx = cctx;
129*f6f9279fSSrinivas Kandagatla 	spin_lock(&cctx->lock);
130*f6f9279fSSrinivas Kandagatla 	list_add_tail(&fl->user, &cctx->users);
131*f6f9279fSSrinivas Kandagatla 	spin_unlock(&cctx->lock);
132*f6f9279fSSrinivas Kandagatla 	fl->sctx = fastrpc_session_alloc(cctx);
133*f6f9279fSSrinivas Kandagatla 
134*f6f9279fSSrinivas Kandagatla 	return 0;
135*f6f9279fSSrinivas Kandagatla }
136*f6f9279fSSrinivas Kandagatla 
137*f6f9279fSSrinivas Kandagatla static const struct file_operations fastrpc_fops = {
138*f6f9279fSSrinivas Kandagatla 	.open = fastrpc_device_open,
139*f6f9279fSSrinivas Kandagatla 	.release = fastrpc_device_release,
140*f6f9279fSSrinivas Kandagatla };
141*f6f9279fSSrinivas Kandagatla 
142*f6f9279fSSrinivas Kandagatla static int fastrpc_cb_probe(struct platform_device *pdev)
143*f6f9279fSSrinivas Kandagatla {
144*f6f9279fSSrinivas Kandagatla 	struct fastrpc_channel_ctx *cctx;
145*f6f9279fSSrinivas Kandagatla 	struct fastrpc_session_ctx *sess;
146*f6f9279fSSrinivas Kandagatla 	struct device *dev = &pdev->dev;
147*f6f9279fSSrinivas Kandagatla 	int i, sessions = 0;
148*f6f9279fSSrinivas Kandagatla 
149*f6f9279fSSrinivas Kandagatla 	cctx = dev_get_drvdata(dev->parent);
150*f6f9279fSSrinivas Kandagatla 	if (!cctx)
151*f6f9279fSSrinivas Kandagatla 		return -EINVAL;
152*f6f9279fSSrinivas Kandagatla 
153*f6f9279fSSrinivas Kandagatla 	of_property_read_u32(dev->of_node, "qcom,nsessions", &sessions);
154*f6f9279fSSrinivas Kandagatla 
155*f6f9279fSSrinivas Kandagatla 	spin_lock(&cctx->lock);
156*f6f9279fSSrinivas Kandagatla 	sess = &cctx->session[cctx->sesscount];
157*f6f9279fSSrinivas Kandagatla 	sess->used = false;
158*f6f9279fSSrinivas Kandagatla 	sess->valid = true;
159*f6f9279fSSrinivas Kandagatla 	sess->dev = dev;
160*f6f9279fSSrinivas Kandagatla 	dev_set_drvdata(dev, sess);
161*f6f9279fSSrinivas Kandagatla 
162*f6f9279fSSrinivas Kandagatla 	if (of_property_read_u32(dev->of_node, "reg", &sess->sid))
163*f6f9279fSSrinivas Kandagatla 		dev_info(dev, "FastRPC Session ID not specified in DT\n");
164*f6f9279fSSrinivas Kandagatla 
165*f6f9279fSSrinivas Kandagatla 	if (sessions > 0) {
166*f6f9279fSSrinivas Kandagatla 		struct fastrpc_session_ctx *dup_sess;
167*f6f9279fSSrinivas Kandagatla 
168*f6f9279fSSrinivas Kandagatla 		for (i = 1; i < sessions; i++) {
169*f6f9279fSSrinivas Kandagatla 			if (cctx->sesscount++ >= FASTRPC_MAX_SESSIONS)
170*f6f9279fSSrinivas Kandagatla 				break;
171*f6f9279fSSrinivas Kandagatla 			dup_sess = &cctx->session[cctx->sesscount];
172*f6f9279fSSrinivas Kandagatla 			memcpy(dup_sess, sess, sizeof(*dup_sess));
173*f6f9279fSSrinivas Kandagatla 		}
174*f6f9279fSSrinivas Kandagatla 	}
175*f6f9279fSSrinivas Kandagatla 	cctx->sesscount++;
176*f6f9279fSSrinivas Kandagatla 	spin_unlock(&cctx->lock);
177*f6f9279fSSrinivas Kandagatla 	dma_set_mask(dev, DMA_BIT_MASK(32));
178*f6f9279fSSrinivas Kandagatla 
179*f6f9279fSSrinivas Kandagatla 	return 0;
180*f6f9279fSSrinivas Kandagatla }
181*f6f9279fSSrinivas Kandagatla 
182*f6f9279fSSrinivas Kandagatla static int fastrpc_cb_remove(struct platform_device *pdev)
183*f6f9279fSSrinivas Kandagatla {
184*f6f9279fSSrinivas Kandagatla 	struct fastrpc_channel_ctx *cctx = dev_get_drvdata(pdev->dev.parent);
185*f6f9279fSSrinivas Kandagatla 	struct fastrpc_session_ctx *sess = dev_get_drvdata(&pdev->dev);
186*f6f9279fSSrinivas Kandagatla 	int i;
187*f6f9279fSSrinivas Kandagatla 
188*f6f9279fSSrinivas Kandagatla 	spin_lock(&cctx->lock);
189*f6f9279fSSrinivas Kandagatla 	for (i = 1; i < FASTRPC_MAX_SESSIONS; i++) {
190*f6f9279fSSrinivas Kandagatla 		if (cctx->session[i].sid == sess->sid) {
191*f6f9279fSSrinivas Kandagatla 			cctx->session[i].valid = false;
192*f6f9279fSSrinivas Kandagatla 			cctx->sesscount--;
193*f6f9279fSSrinivas Kandagatla 		}
194*f6f9279fSSrinivas Kandagatla 	}
195*f6f9279fSSrinivas Kandagatla 	spin_unlock(&cctx->lock);
196*f6f9279fSSrinivas Kandagatla 
197*f6f9279fSSrinivas Kandagatla 	return 0;
198*f6f9279fSSrinivas Kandagatla }
199*f6f9279fSSrinivas Kandagatla 
200*f6f9279fSSrinivas Kandagatla static const struct of_device_id fastrpc_match_table[] = {
201*f6f9279fSSrinivas Kandagatla 	{ .compatible = "qcom,fastrpc-compute-cb", },
202*f6f9279fSSrinivas Kandagatla 	{}
203*f6f9279fSSrinivas Kandagatla };
204*f6f9279fSSrinivas Kandagatla 
205*f6f9279fSSrinivas Kandagatla static struct platform_driver fastrpc_cb_driver = {
206*f6f9279fSSrinivas Kandagatla 	.probe = fastrpc_cb_probe,
207*f6f9279fSSrinivas Kandagatla 	.remove = fastrpc_cb_remove,
208*f6f9279fSSrinivas Kandagatla 	.driver = {
209*f6f9279fSSrinivas Kandagatla 		.name = "qcom,fastrpc-cb",
210*f6f9279fSSrinivas Kandagatla 		.of_match_table = fastrpc_match_table,
211*f6f9279fSSrinivas Kandagatla 		.suppress_bind_attrs = true,
212*f6f9279fSSrinivas Kandagatla 	},
213*f6f9279fSSrinivas Kandagatla };
214*f6f9279fSSrinivas Kandagatla 
215*f6f9279fSSrinivas Kandagatla static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
216*f6f9279fSSrinivas Kandagatla {
217*f6f9279fSSrinivas Kandagatla 	struct device *rdev = &rpdev->dev;
218*f6f9279fSSrinivas Kandagatla 	struct fastrpc_channel_ctx *data;
219*f6f9279fSSrinivas Kandagatla 	int i, err, domain_id = -1;
220*f6f9279fSSrinivas Kandagatla 	const char *domain;
221*f6f9279fSSrinivas Kandagatla 
222*f6f9279fSSrinivas Kandagatla 	data = devm_kzalloc(rdev, sizeof(*data), GFP_KERNEL);
223*f6f9279fSSrinivas Kandagatla 	if (!data)
224*f6f9279fSSrinivas Kandagatla 		return -ENOMEM;
225*f6f9279fSSrinivas Kandagatla 
226*f6f9279fSSrinivas Kandagatla 	err = of_property_read_string(rdev->of_node, "label", &domain);
227*f6f9279fSSrinivas Kandagatla 	if (err) {
228*f6f9279fSSrinivas Kandagatla 		dev_info(rdev, "FastRPC Domain not specified in DT\n");
229*f6f9279fSSrinivas Kandagatla 		return err;
230*f6f9279fSSrinivas Kandagatla 	}
231*f6f9279fSSrinivas Kandagatla 
232*f6f9279fSSrinivas Kandagatla 	for (i = 0; i <= CDSP_DOMAIN_ID; i++) {
233*f6f9279fSSrinivas Kandagatla 		if (!strcmp(domains[i], domain)) {
234*f6f9279fSSrinivas Kandagatla 			domain_id = i;
235*f6f9279fSSrinivas Kandagatla 			break;
236*f6f9279fSSrinivas Kandagatla 		}
237*f6f9279fSSrinivas Kandagatla 	}
238*f6f9279fSSrinivas Kandagatla 
239*f6f9279fSSrinivas Kandagatla 	if (domain_id < 0) {
240*f6f9279fSSrinivas Kandagatla 		dev_info(rdev, "FastRPC Invalid Domain ID %d\n", domain_id);
241*f6f9279fSSrinivas Kandagatla 		return -EINVAL;
242*f6f9279fSSrinivas Kandagatla 	}
243*f6f9279fSSrinivas Kandagatla 
244*f6f9279fSSrinivas Kandagatla 	data->miscdev.minor = MISC_DYNAMIC_MINOR;
245*f6f9279fSSrinivas Kandagatla 	data->miscdev.name = kasprintf(GFP_KERNEL, "fastrpc-%s",
246*f6f9279fSSrinivas Kandagatla 				domains[domain_id]);
247*f6f9279fSSrinivas Kandagatla 	data->miscdev.fops = &fastrpc_fops;
248*f6f9279fSSrinivas Kandagatla 	err = misc_register(&data->miscdev);
249*f6f9279fSSrinivas Kandagatla 	if (err)
250*f6f9279fSSrinivas Kandagatla 		return err;
251*f6f9279fSSrinivas Kandagatla 
252*f6f9279fSSrinivas Kandagatla 	dev_set_drvdata(&rpdev->dev, data);
253*f6f9279fSSrinivas Kandagatla 	dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
254*f6f9279fSSrinivas Kandagatla 	INIT_LIST_HEAD(&data->users);
255*f6f9279fSSrinivas Kandagatla 	spin_lock_init(&data->lock);
256*f6f9279fSSrinivas Kandagatla 	idr_init(&data->ctx_idr);
257*f6f9279fSSrinivas Kandagatla 	data->domain_id = domain_id;
258*f6f9279fSSrinivas Kandagatla 	data->rpdev = rpdev;
259*f6f9279fSSrinivas Kandagatla 
260*f6f9279fSSrinivas Kandagatla 	return of_platform_populate(rdev->of_node, NULL, NULL, rdev);
261*f6f9279fSSrinivas Kandagatla }
262*f6f9279fSSrinivas Kandagatla 
263*f6f9279fSSrinivas Kandagatla static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
264*f6f9279fSSrinivas Kandagatla {
265*f6f9279fSSrinivas Kandagatla 	struct fastrpc_channel_ctx *cctx = dev_get_drvdata(&rpdev->dev);
266*f6f9279fSSrinivas Kandagatla 
267*f6f9279fSSrinivas Kandagatla 	misc_deregister(&cctx->miscdev);
268*f6f9279fSSrinivas Kandagatla 	of_platform_depopulate(&rpdev->dev);
269*f6f9279fSSrinivas Kandagatla 	kfree(cctx);
270*f6f9279fSSrinivas Kandagatla }
271*f6f9279fSSrinivas Kandagatla 
272*f6f9279fSSrinivas Kandagatla static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
273*f6f9279fSSrinivas Kandagatla 				  int len, void *priv, u32 addr)
274*f6f9279fSSrinivas Kandagatla {
275*f6f9279fSSrinivas Kandagatla 	return 0;
276*f6f9279fSSrinivas Kandagatla }
277*f6f9279fSSrinivas Kandagatla 
278*f6f9279fSSrinivas Kandagatla static const struct of_device_id fastrpc_rpmsg_of_match[] = {
279*f6f9279fSSrinivas Kandagatla 	{ .compatible = "qcom,fastrpc" },
280*f6f9279fSSrinivas Kandagatla 	{ },
281*f6f9279fSSrinivas Kandagatla };
282*f6f9279fSSrinivas Kandagatla MODULE_DEVICE_TABLE(of, fastrpc_rpmsg_of_match);
283*f6f9279fSSrinivas Kandagatla 
284*f6f9279fSSrinivas Kandagatla static struct rpmsg_driver fastrpc_driver = {
285*f6f9279fSSrinivas Kandagatla 	.probe = fastrpc_rpmsg_probe,
286*f6f9279fSSrinivas Kandagatla 	.remove = fastrpc_rpmsg_remove,
287*f6f9279fSSrinivas Kandagatla 	.callback = fastrpc_rpmsg_callback,
288*f6f9279fSSrinivas Kandagatla 	.drv = {
289*f6f9279fSSrinivas Kandagatla 		.name = "qcom,fastrpc",
290*f6f9279fSSrinivas Kandagatla 		.of_match_table = fastrpc_rpmsg_of_match,
291*f6f9279fSSrinivas Kandagatla 	},
292*f6f9279fSSrinivas Kandagatla };
293*f6f9279fSSrinivas Kandagatla 
294*f6f9279fSSrinivas Kandagatla static int fastrpc_init(void)
295*f6f9279fSSrinivas Kandagatla {
296*f6f9279fSSrinivas Kandagatla 	int ret;
297*f6f9279fSSrinivas Kandagatla 
298*f6f9279fSSrinivas Kandagatla 	ret = platform_driver_register(&fastrpc_cb_driver);
299*f6f9279fSSrinivas Kandagatla 	if (ret < 0) {
300*f6f9279fSSrinivas Kandagatla 		pr_err("fastrpc: failed to register cb driver\n");
301*f6f9279fSSrinivas Kandagatla 		return ret;
302*f6f9279fSSrinivas Kandagatla 	}
303*f6f9279fSSrinivas Kandagatla 
304*f6f9279fSSrinivas Kandagatla 	ret = register_rpmsg_driver(&fastrpc_driver);
305*f6f9279fSSrinivas Kandagatla 	if (ret < 0) {
306*f6f9279fSSrinivas Kandagatla 		pr_err("fastrpc: failed to register rpmsg driver\n");
307*f6f9279fSSrinivas Kandagatla 		platform_driver_unregister(&fastrpc_cb_driver);
308*f6f9279fSSrinivas Kandagatla 		return ret;
309*f6f9279fSSrinivas Kandagatla 	}
310*f6f9279fSSrinivas Kandagatla 
311*f6f9279fSSrinivas Kandagatla 	return 0;
312*f6f9279fSSrinivas Kandagatla }
313*f6f9279fSSrinivas Kandagatla module_init(fastrpc_init);
314*f6f9279fSSrinivas Kandagatla 
315*f6f9279fSSrinivas Kandagatla static void fastrpc_exit(void)
316*f6f9279fSSrinivas Kandagatla {
317*f6f9279fSSrinivas Kandagatla 	platform_driver_unregister(&fastrpc_cb_driver);
318*f6f9279fSSrinivas Kandagatla 	unregister_rpmsg_driver(&fastrpc_driver);
319*f6f9279fSSrinivas Kandagatla }
320*f6f9279fSSrinivas Kandagatla module_exit(fastrpc_exit);
321*f6f9279fSSrinivas Kandagatla 
322*f6f9279fSSrinivas Kandagatla MODULE_LICENSE("GPL v2");
323