mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
drivers/platform/tegra: add tegra-fsicom driver
Add tegra-fsicom driver. It provides interface to communicate with Functional Safety Island (FSI). Bug 3583609 Change-Id: I0cc45de69ca73092e13ee17fec4abd3f9c9e0468 Signed-off-by: Rahul Bedarkar <rabedarkar@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2707054 Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com> GVS: Gerrit_Virtual_Submit
This commit is contained in:
committed by
mobile promotions
parent
6e66abaff3
commit
b41ef0cf5e
@@ -5,3 +5,4 @@ obj-m += bad.o
|
||||
tegra-cactmon-objs := cactmon.o
|
||||
tegra-cactmon-objs += actmon_common.o
|
||||
obj-m += tegra-cactmon.o
|
||||
obj-m += tegra-fsicom.o
|
||||
|
||||
259
drivers/platform/tegra/tegra-fsicom.c
Normal file
259
drivers/platform/tegra/tegra-fsicom.c
Normal file
@@ -0,0 +1,259 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <uapi/linux/tegra-fsicom.h>
|
||||
|
||||
|
||||
/* Timeout in milliseconds */
|
||||
#define TIMEOUT 1000
|
||||
|
||||
/* Data type for mailbox client and channel details */
|
||||
struct fsi_hsp_sm {
|
||||
struct mbox_client client;
|
||||
struct mbox_chan *chan;
|
||||
};
|
||||
|
||||
/* Data type for accessing TOP2 HSP */
|
||||
struct fsi_hsp {
|
||||
struct fsi_hsp_sm rx;
|
||||
struct fsi_hsp_sm tx;
|
||||
struct device dev;
|
||||
};
|
||||
|
||||
static int device_file_major_number;
|
||||
static const char device_name[] = "fsicom-client";
|
||||
|
||||
static struct platform_device *pdev_local;
|
||||
static struct dma_buf_attachment *attach;
|
||||
static struct sg_table *sgt;
|
||||
static struct dma_buf *dmabuf;
|
||||
|
||||
/* Signaling to Application */
|
||||
static struct task_struct *task;
|
||||
|
||||
static struct fsi_hsp *fsi_hsp_v;
|
||||
|
||||
static void fsicom_send_signal(int32_t data)
|
||||
{
|
||||
|
||||
struct siginfo info;
|
||||
|
||||
/*Sending signal to app */
|
||||
memset(&info, 0, sizeof(struct siginfo));
|
||||
info.si_signo = SIG_FSI_DAEMON;
|
||||
info.si_code = SI_QUEUE;
|
||||
info.si_int = (u32) (unsigned long) data;
|
||||
if (task != NULL) {
|
||||
if (send_sig_info(SIG_FSI_DAEMON,
|
||||
(struct kernel_siginfo *)&info, task) < 0)
|
||||
pr_err("Unable to send signal\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_hsp_rx_notify(struct mbox_client *cl, void *msg)
|
||||
{
|
||||
|
||||
fsicom_send_signal(*((uint32_t *)msg));
|
||||
}
|
||||
|
||||
static void tegra_hsp_tx_empty_notify(struct mbox_client *cl,
|
||||
void *data, int empty_value)
|
||||
{
|
||||
pr_debug("TX empty callback came\n");
|
||||
}
|
||||
static int tegra_hsp_mb_init(struct device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
fsi_hsp_v = devm_kzalloc(dev, sizeof(*fsi_hsp_v), GFP_KERNEL);
|
||||
if (!fsi_hsp_v)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)))
|
||||
dev_err(dev, "FsiCom: setting DMA MASK failed!\n");
|
||||
|
||||
fsi_hsp_v->tx.client.dev = dev;
|
||||
fsi_hsp_v->rx.client.dev = dev;
|
||||
fsi_hsp_v->tx.client.tx_block = true;
|
||||
fsi_hsp_v->tx.client.tx_tout = TIMEOUT;
|
||||
fsi_hsp_v->rx.client.rx_callback = tegra_hsp_rx_notify;
|
||||
fsi_hsp_v->tx.client.tx_done = tegra_hsp_tx_empty_notify;
|
||||
|
||||
fsi_hsp_v->tx.chan = mbox_request_channel_byname(&fsi_hsp_v->tx.client,
|
||||
"fsi-tx");
|
||||
if (IS_ERR(fsi_hsp_v->tx.chan)) {
|
||||
err = PTR_ERR(fsi_hsp_v->tx.chan);
|
||||
dev_err(dev, "failed to get tx mailbox: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
fsi_hsp_v->rx.chan = mbox_request_channel_byname(&fsi_hsp_v->rx.client,
|
||||
"fsi-rx");
|
||||
if (IS_ERR(fsi_hsp_v->rx.chan)) {
|
||||
err = PTR_ERR(fsi_hsp_v->rx.chan);
|
||||
dev_err(dev, "failed to get rx mailbox: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t device_file_ioctl(
|
||||
struct file *fp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct rw_data input;
|
||||
dma_addr_t dma_addr;
|
||||
dma_addr_t phys_addr;
|
||||
struct rw_data *user_input;
|
||||
int ret = 0;
|
||||
uint32_t pdata[4] = {0};
|
||||
|
||||
user_input = (struct rw_data *)arg;
|
||||
if (copy_from_user(&input, (void __user *)arg,
|
||||
sizeof(struct rw_data)))
|
||||
return -EACCES;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case NVMAP_SMMU_MAP:
|
||||
dmabuf = dma_buf_get(input.handle);
|
||||
|
||||
if (IS_ERR_OR_NULL(dmabuf))
|
||||
return -EINVAL;
|
||||
attach = dma_buf_attach(dmabuf, &pdev_local->dev);
|
||||
|
||||
if (IS_ERR_OR_NULL(attach)) {
|
||||
pr_err("error : %lld\n", (signed long long)attach);
|
||||
return -1;
|
||||
}
|
||||
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR_OR_NULL(sgt)) {
|
||||
pr_err("error: %lld\n", (signed long long)sgt);
|
||||
return -1;
|
||||
}
|
||||
phys_addr = sg_phys(sgt->sgl);
|
||||
dma_addr = sg_dma_address(sgt->sgl);
|
||||
if (copy_to_user((void __user *)&user_input->pa,
|
||||
(void *)&phys_addr, sizeof(uint64_t)))
|
||||
return -EACCES;
|
||||
if (copy_to_user((void __user *)&user_input->iova,
|
||||
(void *)&dma_addr, sizeof(uint64_t)))
|
||||
return -EACCES;
|
||||
break;
|
||||
|
||||
case NVMAP_SMMU_UNMAP:
|
||||
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
|
||||
dma_buf_detach(dmabuf, attach);
|
||||
dma_buf_put(dmabuf);
|
||||
break;
|
||||
|
||||
case TEGRA_HSP_WRITE:
|
||||
pdata[0] = input.handle;
|
||||
ret = mbox_send_message(fsi_hsp_v->tx.chan,
|
||||
(void *)pdata);
|
||||
break;
|
||||
|
||||
case TEGRA_SIGNAL_REG:
|
||||
task = get_current();
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* File operations */
|
||||
static const struct file_operations fsicom_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = device_file_ioctl,
|
||||
};
|
||||
|
||||
static int fsicom_register_device(void)
|
||||
{
|
||||
int result = 0;
|
||||
struct class *dev_class;
|
||||
|
||||
result = register_chrdev(0, device_name, &fsicom_driver_fops);
|
||||
if (result < 0) {
|
||||
pr_err("%s> register_chrdev code = %i\n", device_name, result);
|
||||
return result;
|
||||
}
|
||||
device_file_major_number = result;
|
||||
dev_class = class_create(THIS_MODULE, "fsicom_client");
|
||||
if (dev_class == NULL) {
|
||||
pr_err("%s> Could not create class for device\n", device_name);
|
||||
goto class_fail;
|
||||
}
|
||||
|
||||
if ((device_create(dev_class, NULL,
|
||||
MKDEV(device_file_major_number, 0),
|
||||
NULL, "fsicom_client")) == NULL) {
|
||||
pr_err("%s> Could not create device node\n", device_name);
|
||||
goto device_failure;
|
||||
}
|
||||
return 0;
|
||||
|
||||
device_failure:
|
||||
class_destroy(dev_class);
|
||||
class_fail:
|
||||
unregister_chrdev(device_file_major_number, device_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void fsicom_unregister_device(void)
|
||||
{
|
||||
if (device_file_major_number != 0)
|
||||
unregister_chrdev(device_file_major_number, device_name);
|
||||
}
|
||||
|
||||
static const struct of_device_id fsicom_client_dt_match[] = {
|
||||
{ .compatible = "nvidia,tegra234-fsicom-client"},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, fsicom_client_dt_match);
|
||||
|
||||
static int fsicom_client_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
fsicom_register_device();
|
||||
ret = tegra_hsp_mb_init(&pdev->dev);
|
||||
pdev_local = pdev;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsicom_client_remove(struct platform_device *pdev)
|
||||
{
|
||||
fsicom_unregister_device();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver fsicom_client = {
|
||||
.driver = {
|
||||
.name = "fsicom_client",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = of_match_ptr(fsicom_client_dt_match),
|
||||
},
|
||||
.probe = fsicom_client_probe,
|
||||
.remove = fsicom_client_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(fsicom_client);
|
||||
|
||||
MODULE_DESCRIPTION("FSI-CCPLEX-COM driver");
|
||||
MODULE_AUTHOR("Prashant Shaw <pshaw@nvidia.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
26
include/uapi/linux/tegra-fsicom.h
Normal file
26
include/uapi/linux/tegra-fsicom.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note)
|
||||
*
|
||||
* Copyright (c) 2021-2022, NVIDIA CORPORATION, All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_TEGRA_FSICOM_H_
|
||||
#define _UAPI_TEGRA_FSICOM_H_
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
struct rw_data {
|
||||
uint32_t handle;
|
||||
uint64_t pa;
|
||||
uint64_t iova;
|
||||
};
|
||||
|
||||
/* signal value */
|
||||
#define SIG_FSI_DAEMON 44
|
||||
|
||||
/* ioctl call macros */
|
||||
#define NVMAP_SMMU_MAP _IOWR('q', 1, struct rw_data *)
|
||||
#define NVMAP_SMMU_UNMAP _IOWR('q', 2, struct rw_data *)
|
||||
#define TEGRA_HSP_WRITE _IOWR('q', 3, struct rw_data *)
|
||||
#define TEGRA_SIGNAL_REG _IOWR('q', 4, struct rw_data *)
|
||||
|
||||
#endif /* _UAPI_TEGRA_FSICOM_H_ */
|
||||
Reference in New Issue
Block a user