mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-26 14:17:26 -04:00
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>
This commit is contained in:
parent
36e738bdab
commit
f6f9279f2b
3 changed files with 333 additions and 0 deletions
|
@ -295,6 +295,16 @@ config QCOM_COINCELL
|
||||||
to maintain PMIC register and RTC state in the absence of
|
to maintain PMIC register and RTC state in the absence of
|
||||||
external power.
|
external power.
|
||||||
|
|
||||||
|
config QCOM_FASTRPC
|
||||||
|
tristate "Qualcomm FastRPC"
|
||||||
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
|
depends on RPMSG
|
||||||
|
help
|
||||||
|
Provides a communication mechanism that allows for clients to
|
||||||
|
make remote method invocations across processor boundary to
|
||||||
|
applications DSP processor. Say M if you want to enable this
|
||||||
|
module.
|
||||||
|
|
||||||
config SGI_GRU
|
config SGI_GRU
|
||||||
tristate "SGI GRU driver"
|
tristate "SGI GRU driver"
|
||||||
depends on X86_UV && SMP
|
depends on X86_UV && SMP
|
||||||
|
|
|
@ -18,6 +18,7 @@ obj-$(CONFIG_TIFM_CORE) += tifm_core.o
|
||||||
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
|
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
|
||||||
obj-$(CONFIG_PHANTOM) += phantom.o
|
obj-$(CONFIG_PHANTOM) += phantom.o
|
||||||
obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o
|
obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o
|
||||||
|
obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o
|
||||||
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
|
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
|
||||||
obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
|
obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
|
||||||
obj-$(CONFIG_SGI_IOC4) += ioc4.o
|
obj-$(CONFIG_SGI_IOC4) += ioc4.o
|
||||||
|
|
322
drivers/misc/fastrpc.c
Normal file
322
drivers/misc/fastrpc.c
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
|
||||||
|
// Copyright (c) 2018, Linaro Limited
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/idr.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/miscdevice.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/rpmsg.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define ADSP_DOMAIN_ID (0)
|
||||||
|
#define MDSP_DOMAIN_ID (1)
|
||||||
|
#define SDSP_DOMAIN_ID (2)
|
||||||
|
#define CDSP_DOMAIN_ID (3)
|
||||||
|
#define FASTRPC_DEV_MAX 4 /* adsp, mdsp, slpi, cdsp*/
|
||||||
|
#define FASTRPC_MAX_SESSIONS 9 /*8 compute, 1 cpz*/
|
||||||
|
#define FASTRPC_CTX_MAX (256)
|
||||||
|
#define FASTRPC_CTXID_MASK (0xFF0)
|
||||||
|
#define FASTRPC_DEVICE_NAME "fastrpc"
|
||||||
|
|
||||||
|
#define miscdev_to_cctx(d) container_of(d, struct fastrpc_channel_ctx, miscdev)
|
||||||
|
|
||||||
|
static const char *domains[FASTRPC_DEV_MAX] = { "adsp", "mdsp",
|
||||||
|
"sdsp", "cdsp"};
|
||||||
|
|
||||||
|
struct fastrpc_session_ctx {
|
||||||
|
struct device *dev;
|
||||||
|
int sid;
|
||||||
|
bool used;
|
||||||
|
bool valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fastrpc_channel_ctx {
|
||||||
|
int domain_id;
|
||||||
|
int sesscount;
|
||||||
|
struct rpmsg_device *rpdev;
|
||||||
|
struct fastrpc_session_ctx session[FASTRPC_MAX_SESSIONS];
|
||||||
|
spinlock_t lock;
|
||||||
|
struct idr ctx_idr;
|
||||||
|
struct list_head users;
|
||||||
|
struct miscdevice miscdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fastrpc_user {
|
||||||
|
struct list_head user;
|
||||||
|
struct list_head maps;
|
||||||
|
struct list_head pending;
|
||||||
|
|
||||||
|
struct fastrpc_channel_ctx *cctx;
|
||||||
|
struct fastrpc_session_ctx *sctx;
|
||||||
|
|
||||||
|
int tgid;
|
||||||
|
int pd;
|
||||||
|
/* Lock for lists */
|
||||||
|
spinlock_t lock;
|
||||||
|
/* lock for allocations */
|
||||||
|
struct mutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fastrpc_session_ctx *fastrpc_session_alloc(
|
||||||
|
struct fastrpc_channel_ctx *cctx)
|
||||||
|
{
|
||||||
|
struct fastrpc_session_ctx *session = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spin_lock(&cctx->lock);
|
||||||
|
for (i = 0; i < cctx->sesscount; i++) {
|
||||||
|
if (!cctx->session[i].used && cctx->session[i].valid) {
|
||||||
|
cctx->session[i].used = true;
|
||||||
|
session = &cctx->session[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&cctx->lock);
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fastrpc_session_free(struct fastrpc_channel_ctx *cctx,
|
||||||
|
struct fastrpc_session_ctx *session)
|
||||||
|
{
|
||||||
|
spin_lock(&cctx->lock);
|
||||||
|
session->used = false;
|
||||||
|
spin_unlock(&cctx->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fastrpc_device_release(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
struct fastrpc_user *fl = (struct fastrpc_user *)file->private_data;
|
||||||
|
struct fastrpc_channel_ctx *cctx = fl->cctx;
|
||||||
|
|
||||||
|
spin_lock(&cctx->lock);
|
||||||
|
list_del(&fl->user);
|
||||||
|
spin_unlock(&cctx->lock);
|
||||||
|
|
||||||
|
fastrpc_session_free(cctx, fl->sctx);
|
||||||
|
|
||||||
|
mutex_destroy(&fl->mutex);
|
||||||
|
kfree(fl);
|
||||||
|
file->private_data = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fastrpc_device_open(struct inode *inode, struct file *filp)
|
||||||
|
{
|
||||||
|
struct fastrpc_channel_ctx *cctx = miscdev_to_cctx(filp->private_data);
|
||||||
|
struct fastrpc_user *fl = NULL;
|
||||||
|
|
||||||
|
fl = kzalloc(sizeof(*fl), GFP_KERNEL);
|
||||||
|
if (!fl)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
filp->private_data = fl;
|
||||||
|
spin_lock_init(&fl->lock);
|
||||||
|
mutex_init(&fl->mutex);
|
||||||
|
INIT_LIST_HEAD(&fl->pending);
|
||||||
|
INIT_LIST_HEAD(&fl->maps);
|
||||||
|
INIT_LIST_HEAD(&fl->user);
|
||||||
|
fl->tgid = current->tgid;
|
||||||
|
fl->cctx = cctx;
|
||||||
|
spin_lock(&cctx->lock);
|
||||||
|
list_add_tail(&fl->user, &cctx->users);
|
||||||
|
spin_unlock(&cctx->lock);
|
||||||
|
fl->sctx = fastrpc_session_alloc(cctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations fastrpc_fops = {
|
||||||
|
.open = fastrpc_device_open,
|
||||||
|
.release = fastrpc_device_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fastrpc_cb_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct fastrpc_channel_ctx *cctx;
|
||||||
|
struct fastrpc_session_ctx *sess;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
int i, sessions = 0;
|
||||||
|
|
||||||
|
cctx = dev_get_drvdata(dev->parent);
|
||||||
|
if (!cctx)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
of_property_read_u32(dev->of_node, "qcom,nsessions", &sessions);
|
||||||
|
|
||||||
|
spin_lock(&cctx->lock);
|
||||||
|
sess = &cctx->session[cctx->sesscount];
|
||||||
|
sess->used = false;
|
||||||
|
sess->valid = true;
|
||||||
|
sess->dev = dev;
|
||||||
|
dev_set_drvdata(dev, sess);
|
||||||
|
|
||||||
|
if (of_property_read_u32(dev->of_node, "reg", &sess->sid))
|
||||||
|
dev_info(dev, "FastRPC Session ID not specified in DT\n");
|
||||||
|
|
||||||
|
if (sessions > 0) {
|
||||||
|
struct fastrpc_session_ctx *dup_sess;
|
||||||
|
|
||||||
|
for (i = 1; i < sessions; i++) {
|
||||||
|
if (cctx->sesscount++ >= FASTRPC_MAX_SESSIONS)
|
||||||
|
break;
|
||||||
|
dup_sess = &cctx->session[cctx->sesscount];
|
||||||
|
memcpy(dup_sess, sess, sizeof(*dup_sess));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cctx->sesscount++;
|
||||||
|
spin_unlock(&cctx->lock);
|
||||||
|
dma_set_mask(dev, DMA_BIT_MASK(32));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fastrpc_cb_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct fastrpc_channel_ctx *cctx = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
struct fastrpc_session_ctx *sess = dev_get_drvdata(&pdev->dev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spin_lock(&cctx->lock);
|
||||||
|
for (i = 1; i < FASTRPC_MAX_SESSIONS; i++) {
|
||||||
|
if (cctx->session[i].sid == sess->sid) {
|
||||||
|
cctx->session[i].valid = false;
|
||||||
|
cctx->sesscount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&cctx->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id fastrpc_match_table[] = {
|
||||||
|
{ .compatible = "qcom,fastrpc-compute-cb", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver fastrpc_cb_driver = {
|
||||||
|
.probe = fastrpc_cb_probe,
|
||||||
|
.remove = fastrpc_cb_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "qcom,fastrpc-cb",
|
||||||
|
.of_match_table = fastrpc_match_table,
|
||||||
|
.suppress_bind_attrs = true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
|
||||||
|
{
|
||||||
|
struct device *rdev = &rpdev->dev;
|
||||||
|
struct fastrpc_channel_ctx *data;
|
||||||
|
int i, err, domain_id = -1;
|
||||||
|
const char *domain;
|
||||||
|
|
||||||
|
data = devm_kzalloc(rdev, sizeof(*data), GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
err = of_property_read_string(rdev->of_node, "label", &domain);
|
||||||
|
if (err) {
|
||||||
|
dev_info(rdev, "FastRPC Domain not specified in DT\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i <= CDSP_DOMAIN_ID; i++) {
|
||||||
|
if (!strcmp(domains[i], domain)) {
|
||||||
|
domain_id = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain_id < 0) {
|
||||||
|
dev_info(rdev, "FastRPC Invalid Domain ID %d\n", domain_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->miscdev.minor = MISC_DYNAMIC_MINOR;
|
||||||
|
data->miscdev.name = kasprintf(GFP_KERNEL, "fastrpc-%s",
|
||||||
|
domains[domain_id]);
|
||||||
|
data->miscdev.fops = &fastrpc_fops;
|
||||||
|
err = misc_register(&data->miscdev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
dev_set_drvdata(&rpdev->dev, data);
|
||||||
|
dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
|
||||||
|
INIT_LIST_HEAD(&data->users);
|
||||||
|
spin_lock_init(&data->lock);
|
||||||
|
idr_init(&data->ctx_idr);
|
||||||
|
data->domain_id = domain_id;
|
||||||
|
data->rpdev = rpdev;
|
||||||
|
|
||||||
|
return of_platform_populate(rdev->of_node, NULL, NULL, rdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
|
||||||
|
{
|
||||||
|
struct fastrpc_channel_ctx *cctx = dev_get_drvdata(&rpdev->dev);
|
||||||
|
|
||||||
|
misc_deregister(&cctx->miscdev);
|
||||||
|
of_platform_depopulate(&rpdev->dev);
|
||||||
|
kfree(cctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
|
||||||
|
int len, void *priv, u32 addr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id fastrpc_rpmsg_of_match[] = {
|
||||||
|
{ .compatible = "qcom,fastrpc" },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, fastrpc_rpmsg_of_match);
|
||||||
|
|
||||||
|
static struct rpmsg_driver fastrpc_driver = {
|
||||||
|
.probe = fastrpc_rpmsg_probe,
|
||||||
|
.remove = fastrpc_rpmsg_remove,
|
||||||
|
.callback = fastrpc_rpmsg_callback,
|
||||||
|
.drv = {
|
||||||
|
.name = "qcom,fastrpc",
|
||||||
|
.of_match_table = fastrpc_rpmsg_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fastrpc_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = platform_driver_register(&fastrpc_cb_driver);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("fastrpc: failed to register cb driver\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = register_rpmsg_driver(&fastrpc_driver);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("fastrpc: failed to register rpmsg driver\n");
|
||||||
|
platform_driver_unregister(&fastrpc_cb_driver);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
module_init(fastrpc_init);
|
||||||
|
|
||||||
|
static void fastrpc_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&fastrpc_cb_driver);
|
||||||
|
unregister_rpmsg_driver(&fastrpc_driver);
|
||||||
|
}
|
||||||
|
module_exit(fastrpc_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
Loading…
Add table
Add a link
Reference in a new issue