From b41ef0cf5ebdf863d8b1ac960a6bf71a08349aa0 Mon Sep 17 00:00:00 2001 From: Rahul Bedarkar Date: Mon, 2 May 2022 11:09:05 +0000 Subject: [PATCH] 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 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2707054 Reviewed-by: Laxman Dewangan GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/Makefile | 1 + drivers/platform/tegra/tegra-fsicom.c | 259 ++++++++++++++++++++++++++ include/uapi/linux/tegra-fsicom.h | 26 +++ 3 files changed, 286 insertions(+) create mode 100644 drivers/platform/tegra/tegra-fsicom.c create mode 100644 include/uapi/linux/tegra-fsicom.h diff --git a/drivers/platform/tegra/Makefile b/drivers/platform/tegra/Makefile index 5fa88170..3d1380d1 100644 --- a/drivers/platform/tegra/Makefile +++ b/drivers/platform/tegra/Makefile @@ -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 diff --git a/drivers/platform/tegra/tegra-fsicom.c b/drivers/platform/tegra/tegra-fsicom.c new file mode 100644 index 00000000..67a5ca3b --- /dev/null +++ b/drivers/platform/tegra/tegra-fsicom.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* 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 "); +MODULE_LICENSE("GPL v2"); diff --git a/include/uapi/linux/tegra-fsicom.h b/include/uapi/linux/tegra-fsicom.h new file mode 100644 index 00000000..da0cc698 --- /dev/null +++ b/include/uapi/linux/tegra-fsicom.h @@ -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 + +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_ */