Skip to content

Commit f6f9279

Browse files
Srinivas-Kandagatlagregkh
authored andcommitted
misc: fastrpc: Add Qualcomm fastrpc basic driver model
This patch adds basic driver model for Qualcomm FastRPC driver which implements an IPC (Inter-Processor Communication) mechanism that allows for clients to transparently make remote method invocations across processor boundaries. Each DSP rpmsg channel is represented as fastrpc channel context and is exposed as a character device for userspace interface. Each compute context bank is represented as fastrpc-session-context, which are dynamically managed by the channel context char device. Co-developed-by: Thierry Escande <thierry.escande@linaro.org> Signed-off-by: Thierry Escande <thierry.escande@linaro.org> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 36e738b commit f6f9279

File tree

3 files changed

+333
-0
lines changed

3 files changed

+333
-0
lines changed

drivers/misc/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,16 @@ config QCOM_COINCELL
295295
to maintain PMIC register and RTC state in the absence of
296296
external power.
297297

298+
config QCOM_FASTRPC
299+
tristate "Qualcomm FastRPC"
300+
depends on ARCH_QCOM || COMPILE_TEST
301+
depends on RPMSG
302+
help
303+
Provides a communication mechanism that allows for clients to
304+
make remote method invocations across processor boundary to
305+
applications DSP processor. Say M if you want to enable this
306+
module.
307+
298308
config SGI_GRU
299309
tristate "SGI GRU driver"
300310
depends on X86_UV && SMP

drivers/misc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ obj-$(CONFIG_TIFM_CORE) += tifm_core.o
1818
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
1919
obj-$(CONFIG_PHANTOM) += phantom.o
2020
obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o
21+
obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o
2122
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
2223
obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
2324
obj-$(CONFIG_SGI_IOC4) += ioc4.o

drivers/misc/fastrpc.c

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

0 commit comments

Comments
 (0)