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