From 8b60a441202cb5c6b5264dbee633fcb24414bab4 Mon Sep 17 00:00:00 2001
From: Sandeep Malik <Sandeep.Malik@nxp.com>
Date: Wed, 15 Nov 2017 17:16:13 +0530
Subject: [PATCH] crypto: caam - add CAAM job ring UIO support

This patch add the support for job ring UIO so
that userspace drivers can have access to the
caam job rings

Signed-off-by: Sandeep Malik <Sandeep.Malik@nxp.com>
Signed-off-by: Gagandeep Singh <g.singh@nxp.com>
---
 drivers/crypto/caam/Kconfig      |  11 ++
 drivers/crypto/caam/Makefile     |   1 +
 drivers/crypto/caam/fsl_jr_uio.c | 256 +++++++++++++++++++++++++++++++++++++++
 drivers/crypto/caam/fsl_jr_uio.h |  25 ++++
 4 files changed, 293 insertions(+)
 create mode 100644 drivers/crypto/caam/fsl_jr_uio.c
 create mode 100644 drivers/crypto/caam/fsl_jr_uio.h

--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -203,6 +203,17 @@ config CRYPTO_DEV_FSL_CAAM_SECVIO
           handler functions which can be specified to act on the consequences
           of a security violation.
 
+config CRYPTO_DEV_FSL_CAAM_JR_UIO
+	tristate "Freescale Job Ring UIO support"
+	depends on UIO
+	default y
+	help
+	  Selecting this will allow job ring UIO support for
+	  Userspace drivers
+
+	  To compile this as a module, choose M here: the module
+	  will be called fsl_jr_uio.
+
 endif # CRYPTO_DEV_FSL_CAAM_JR
 
 endif # CRYPTO_DEV_FSL_CAAM
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caa
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR) += caam_jr.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC) += caamalg_desc.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API_DESC) += caamhash_desc.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR_UIO) += fsl_jr_uio.o
 
 caam-y := ctrl.o
 caam-$(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API) += tag_object.o
--- /dev/null
+++ b/drivers/crypto/caam/fsl_jr_uio.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/uio_driver.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include "regs.h"
+#include "fsl_jr_uio.h"
+
+static const char jr_uio_version[] = "fsl JR UIO driver v1.0";
+
+#define NAME_LENGTH 30
+#define JR_INDEX_OFFSET 12
+
+static const char uio_device_name[] = "fsl-jr";
+static LIST_HEAD(jr_list);
+
+struct jr_uio_info {
+	atomic_t ref; /* exclusive, only one open() at a time */
+	struct uio_info uio;
+	char name[NAME_LENGTH];
+};
+
+struct jr_dev {
+	u32 revision;
+	u32 index;
+	u32 irq;
+	struct caam_job_ring __iomem *global_regs;
+	struct device *dev;
+	struct resource *res;
+	struct jr_uio_info info;
+	struct list_head node;
+	struct list_head jr_list;
+};
+
+static int jr_uio_open(struct uio_info *info, struct inode *inode)
+{
+	struct jr_uio_info *uio_info = container_of(info,
+					struct jr_uio_info, uio);
+
+	if (!atomic_dec_and_test(&uio_info->ref)) {
+		pr_err("%s: failing non-exclusive open()\n", uio_info->name);
+		atomic_inc(&uio_info->ref);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int jr_uio_release(struct uio_info *info, struct inode *inode)
+{
+	struct jr_uio_info *uio_info = container_of(info,
+					struct jr_uio_info, uio);
+	atomic_inc(&uio_info->ref);
+
+	return 0;
+}
+
+static irqreturn_t jr_uio_irq_handler(int irq, struct uio_info *dev_info)
+{
+	struct jr_dev *jrdev = dev_info->priv;
+	u32 irqstate;
+
+	irqstate = rd_reg32(&jrdev->global_regs->jrintstatus);
+
+	if (!irqstate)
+		return IRQ_NONE;
+
+	if (irqstate & JRINT_JR_ERROR)
+		dev_info(jrdev->dev, "uio job ring error - irqstate: %08x\n",
+			 irqstate);
+
+	/*mask valid interrupts */
+	clrsetbits_32(&jrdev->global_regs->rconfig_lo, 0, JRCFG_IMSK);
+
+	/* Have valid interrupt at this point, just ACK and trigger */
+	wr_reg32(&jrdev->global_regs->jrintstatus, irqstate);
+
+	return IRQ_HANDLED;
+}
+
+static int jr_uio_irqcontrol(struct uio_info *dev_info, int irqon)
+{
+	struct jr_dev *jrdev = dev_info->priv;
+
+	switch (irqon) {
+	case SEC_UIO_SIMULATE_IRQ_CMD:
+		uio_event_notify(dev_info);
+		break;
+	case SEC_UIO_ENABLE_IRQ_CMD:
+		/* Enable Job Ring interrupt */
+		clrsetbits_32(&jrdev->global_regs->rconfig_lo, JRCFG_IMSK, 0);
+		break;
+	case SEC_UIO_DISABLE_IRQ_CMD:
+		/* Disable Job Ring interrupt */
+		clrsetbits_32(&jrdev->global_regs->rconfig_lo, 0, JRCFG_IMSK);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int __init jr_uio_init(struct jr_dev *uio_dev)
+{
+	int ret;
+	struct jr_uio_info *info;
+
+	info = &uio_dev->info;
+	atomic_set(&info->ref, 1);
+	info->uio.version = jr_uio_version;
+	info->uio.name = uio_dev->info.name;
+	info->uio.mem[0].name = "JR config space";
+	info->uio.mem[0].addr = uio_dev->res->start;
+	info->uio.mem[0].size = uio_dev->res->end - uio_dev->res->start + 1;
+	info->uio.mem[0].internal_addr = uio_dev->global_regs;
+	info->uio.mem[0].memtype = UIO_MEM_PHYS;
+	info->uio.irq = uio_dev->irq;
+	info->uio.irq_flags = IRQF_SHARED;
+	info->uio.handler = jr_uio_irq_handler;
+	info->uio.irqcontrol = jr_uio_irqcontrol;
+	info->uio.open = jr_uio_open;
+	info->uio.release = jr_uio_release;
+	info->uio.priv = uio_dev;
+
+	ret = uio_register_device(uio_dev->dev, &info->uio);
+	if (ret) {
+		dev_err(uio_dev->dev, "jr_uio: UIO registration failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id jr_ids[] = {
+	{ .compatible = "fsl,sec-v4.0-job-ring", },
+	{ .compatible = "fsl,sec-v4.4-job-ring", },
+	{ .compatible = "fsl,sec-v5.0-job-ring", },
+	{ .compatible = "fsl,sec-v6.0-job-ring", },
+	{},
+};
+
+static int fsl_jr_probe(struct platform_device *dev)
+{
+	struct resource regs;
+	struct jr_dev *jr_dev;
+	struct device_node *jr_node;
+	int ret, count = 0;
+	struct list_head *p;
+
+	jr_node = dev->dev.of_node;
+	if (!jr_node) {
+		dev_err(&dev->dev, "Device OF-Node is NULL\n");
+		return -EFAULT;
+	}
+
+	jr_dev = devm_kzalloc(&dev->dev, sizeof(*jr_dev), GFP_KERNEL);
+	if (!jr_dev)
+		return -ENOMEM;
+
+	/* Creat name and index */
+	list_for_each(p, &jr_list) {
+		count++;
+	}
+	jr_dev->index = count;
+
+	snprintf(jr_dev->info.name, sizeof(jr_dev->info.name) - 1,
+		 "%s%d", uio_device_name, jr_dev->index);
+
+	jr_dev->dev = &dev->dev;
+	platform_set_drvdata(dev, jr_dev);
+
+	/* Get the resource from dtb node */
+	ret = of_address_to_resource(jr_node, 0, &regs);
+	if (unlikely(ret < 0)) {
+		dev_err(&dev->dev, "OF-Address-to-resource Failed\n");
+		ret = -EFAULT;
+		goto abort;
+	}
+
+	jr_dev->res = devm_request_mem_region(&dev->dev, regs.start,
+					      regs.end - regs.start + 1,
+					      jr_dev->info.name);
+	if (unlikely(!jr_dev->res)) {
+		dev_err(jr_dev->dev, "devm_request_mem_region failed\n");
+		ret = -ENOMEM;
+		goto abort;
+	}
+
+	jr_dev->global_regs =
+		devm_ioremap(&dev->dev, jr_dev->res->start,
+			     jr_dev->res->end - jr_dev->res->start + 1);
+	if (unlikely(jr_dev->global_regs == 0)) {
+		dev_err(jr_dev->dev, "devm_ioremap failed\n");
+		ret = -EIO;
+		goto abort;
+	}
+	jr_dev->irq = irq_of_parse_and_map(jr_node, 0);
+	dev_dbg(jr_dev->dev, "errirq: %d\n", jr_dev->irq);
+
+	/* Register UIO */
+	ret = jr_uio_init(jr_dev);
+	if (ret) {
+		dev_err(&dev->dev, "UIO init Failed\n");
+		goto abort;
+	}
+
+	list_add_tail(&jr_dev->node, &jr_list);
+
+	dev_info(jr_dev->dev, "UIO device full name %s initialized\n",
+		 jr_dev->info.name);
+
+	return 0;
+
+abort:
+	return ret;
+}
+
+static int fsl_jr_remove(struct platform_device *dev)
+{
+	struct jr_dev *jr_dev = platform_get_drvdata(dev);
+
+	if (!jr_dev)
+		return 0;
+
+	list_del(&jr_dev->node);
+	uio_unregister_device(&jr_dev->info.uio);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(of, jr_ids);
+
+static struct platform_driver fsl_jr_driver = {
+	.driver = {
+		.name = "fsl-jr-uio",
+		.of_match_table = jr_ids,
+	},
+	.probe = fsl_jr_probe,
+	.remove = fsl_jr_remove,
+};
+
+module_platform_driver(fsl_jr_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("NXP");
+MODULE_DESCRIPTION("FSL SEC UIO Driver");
--- /dev/null
+++ b/drivers/crypto/caam/fsl_jr_uio.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CAAM Job RING UIO support header file
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc
+ * Copyright 2018 NXP
+ */
+
+#ifndef FSL_JR_UIO_H
+#define FSL_JR_UIO_H
+
+/** UIO command used by user-space driver to request
+ *  disabling IRQs on a certain job ring
+ */
+#define SEC_UIO_DISABLE_IRQ_CMD         0
+/** UIO command used by user-space driver to request
+ *  enabling IRQs on a certain job ring
+ */
+#define SEC_UIO_ENABLE_IRQ_CMD          1
+/** UIO command used by user-space driver to request SEC kernel driver
+ *  to simulate that an IRQ is generated on a certain job ring
+ */
+#define SEC_UIO_SIMULATE_IRQ_CMD        2
+
+#endif