[v3,RESEND,1/9] crypto/ycc: Add YCC (Yitian Cryptography Complex) accelerator driver

Message ID 1667461243-48652-2-git-send-email-guanjun@linux.alibaba.com
State New
Headers
Series Drivers for Alibaba YCC (Yitian Cryptography Complex) cryptographic accelerator |

Commit Message

guanjun Nov. 3, 2022, 7:40 a.m. UTC
  From: Zelin Deng <zelin.deng@linux.alibaba.com>

YCC (Yitian Cryptography Complex) is designed to accelerate the process
of encryption and decryption. Yitian is the name of Alibaba SoCs
which is based on ARMv9 architecture.

This patch aims to add driver with basic pci settings and irq requests.

Signed-off-by: Zelin Deng <zelin.deng@linux.alibaba.com>
Co-developed-by: Jiankang Chen <jkchen@linux.alibaba.com>
Signed-off-by: Jiankang Chen <jkchen@linux.alibaba.com>
Signed-off-by: Guanjun <guanjun@linux.alibaba.com>
---
 drivers/crypto/Kconfig       |   2 +
 drivers/crypto/Makefile      |   1 +
 drivers/crypto/ycc/Kconfig   |   8 +
 drivers/crypto/ycc/Makefile  |   3 +
 drivers/crypto/ycc/ycc_dev.h | 148 +++++++++++++++
 drivers/crypto/ycc/ycc_drv.c | 444 +++++++++++++++++++++++++++++++++++++++++++
 drivers/crypto/ycc/ycc_isr.c | 117 ++++++++++++
 drivers/crypto/ycc/ycc_isr.h |  12 ++
 8 files changed, 735 insertions(+)
 create mode 100644 drivers/crypto/ycc/Kconfig
 create mode 100644 drivers/crypto/ycc/Makefile
 create mode 100644 drivers/crypto/ycc/ycc_dev.h
 create mode 100644 drivers/crypto/ycc/ycc_drv.c
 create mode 100644 drivers/crypto/ycc/ycc_isr.c
 create mode 100644 drivers/crypto/ycc/ycc_isr.h
  

Patch

diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 55e75fb..f0c4aee 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -818,4 +818,6 @@  config CRYPTO_DEV_SA2UL
 source "drivers/crypto/keembay/Kconfig"
 source "drivers/crypto/aspeed/Kconfig"
 
+source "drivers/crypto/ycc/Kconfig"
+
 endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 116de17..a667775 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -52,4 +52,5 @@  obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/
 obj-y += xilinx/
 obj-y += hisilicon/
 obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/
+obj-$(CONFIG_CRYPTO_DEV_YCC) += ycc/
 obj-y += keembay/
diff --git a/drivers/crypto/ycc/Kconfig b/drivers/crypto/ycc/Kconfig
new file mode 100644
index 00000000..6e88ecb
--- /dev/null
+++ b/drivers/crypto/ycc/Kconfig
@@ -0,0 +1,8 @@ 
+config CRYPTO_DEV_YCC
+	tristate "Support for Alibaba YCC cryptographic accelerator"
+	depends on CRYPTO && CRYPTO_HW && PCI
+	default n
+	help
+	  Enables the driver for the on-chip cryptographic accelerator of
+	  Alibaba Yitian SoCs which is based on ARMv9 architecture.
+	  If unsure say N.
diff --git a/drivers/crypto/ycc/Makefile b/drivers/crypto/ycc/Makefile
new file mode 100644
index 00000000..ef28b7c
--- /dev/null
+++ b/drivers/crypto/ycc/Makefile
@@ -0,0 +1,3 @@ 
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CRYPTO_DEV_YCC) += ycc.o
+ycc-objs := ycc_drv.o ycc_isr.o
diff --git a/drivers/crypto/ycc/ycc_dev.h b/drivers/crypto/ycc/ycc_dev.h
new file mode 100644
index 00000000..427046e
--- /dev/null
+++ b/drivers/crypto/ycc/ycc_dev.h
@@ -0,0 +1,148 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __YCC_DEV_H
+#define __YCC_DEV_H
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+
+#define YCC_MAX_DEBUGFS_NAME		20
+
+#define PCI_VENDOR_ID_YCC		0x1DED
+#define PCI_DEVICE_ID_RCEC		0x8003
+#define PCI_DEVICE_ID_RCIEP		0x8001
+
+#define YCC_RINGPAIR_NUM		48
+#define YCC_IRQS			(YCC_RINGPAIR_NUM + 1)
+
+#define RING_STOP_BIT			BIT(15)
+#define RING_CFG_RING_SZ		GENMASK(2, 0)
+#define RING_CFG_INT_TH			GENMASK(15, 8)
+#define RING_ERR_AXI			BIT(0)
+#define RING_PENDING_CNT		GENMASK(9, 0)
+
+#define YCC_SEC_CFG_BAR			0
+#define YCC_NSEC_CFG_BAR		1
+#define YCC_SEC_Q_BAR			2
+#define YCC_NSEC_Q_BAR			3
+
+/* YCC secure configuration register offset */
+#define REG_YCC_CTL			0x18
+#define REG_YCC_GO			0x50
+#define REG_YCC_HCLK_INT_STATUS		0x54
+#define REG_YCC_XCLK_INT_STATUS		0x58
+#define REG_YCC_XCLK_MEM_ECC_EN_0	0x5c
+#define REG_YCC_XCLK_MEM_ECC_EN_1	0x60
+#define REG_YCC_XCLK_MEM_ECC_COR_0	0x74
+#define REG_YCC_XCLK_MEM_ECC_COR_1	0x78
+#define REG_YCC_XCLK_MEM_ECC_UNCOR_0	0x80
+#define REG_YCC_XCLK_MEM_ECC_UNCOR_1	0x84
+#define REG_YCC_HCLK_MEM_ECC_EN		0x88
+#define REG_YCC_HCLK_MEM_ECC_COR	0x94
+#define REG_YCC_HCLK_MEM_ECC_UNCOR	0x98
+
+#define REG_YCC_DEV_INT_MASK		0xA4
+#define REG_YCC_HCLK_INT_MASK		0xE4
+#define REG_YCC_XCLK_INT_MASK		0xE8
+
+/* ring register offset */
+#define REG_RING_CMD_BASE_ADDR_LO	0x00
+#define REG_RING_CMD_BASE_ADDR_HI	0x04
+#define REG_RING_CMD_WR_PTR		0x08
+#define REG_RING_CMD_RD_PTR		0x0C
+#define REG_RING_RSP_BASE_ADDR_LO	0x10
+#define REG_RING_RSP_BASE_ADDR_HI	0x14
+#define REG_RING_RSP_WR_PTR		0x18
+#define REG_RING_RSP_RD_PTR		0x1C
+#define REG_RING_CFG			0x20
+#define REG_RING_TO_TH			0x24
+#define REG_RING_STATUS			0x28
+#define REG_RING_PENDING_CMD		0x2C
+#define REG_RING_RSP_WR_SHADOWN_PTR	0x30
+#define REG_RING_RSP_AFULL_TH		0x34
+
+#define YCC_HCLK_AHB_ERR		BIT(0)
+#define YCC_HCLK_SHIELD_ERR		BIT(1)
+#define YCC_HCLK_TRNG_ERR		BIT(2)
+#define YCC_HCLK_EFUSE_ERR		BIT(3)
+#define YCC_HCLK_INIT_ERR		GENMASK(30, 16)
+#define YCC_HCLK_CB_TRNG_ERR		BIT(31)
+
+#define YCC_CTRL_IRAM_EN		BIT(1)
+#define YCC_CTRL_SEC_EN			BIT(3)
+
+#define YCC_GO_PWRON			BIT(0)
+#define YCC_GO_ENABLED			BIT(1)
+
+#define PCI_EXR_DEVCTL_TRP		BIT(21)
+#define PCI_EXP_DEVCTL_FLREN		BIT(15)
+
+#define YDEV_STATUS_BIND		0
+#define YDEV_STATUS_INIT		1
+#define YDEV_STATUS_RESET		2
+#define YDEV_STATUS_READY		3
+#define YDEV_STATUS_ERR			4
+#define YDEV_STATUS_SRIOV		5
+
+struct ycc_bar {
+	void __iomem *vaddr;
+	resource_size_t paddr;
+	resource_size_t size;
+};
+
+enum ycc_dev_type {
+	YCC_RCIEP,
+	YCC_RCEC,
+};
+
+struct ycc_dev {
+	struct list_head list;
+	struct pci_dev *pdev;
+
+	u32 type;
+	int id;
+	int node;
+	const char *dev_name;
+	struct ycc_bar ycc_bars[4];
+	struct ycc_dev *assoc_dev;
+
+	bool is_polling;
+	unsigned long status;
+	struct workqueue_struct *dev_err_q;
+	char err_irq_name[32];
+	struct work_struct work;
+	char *msi_name[48];
+	struct dentry *debug_dir;
+	atomic_t refcnt;
+
+	bool sec;
+	bool is_vf;
+	bool enable_vf;
+};
+
+#define YCC_CSR_WR(csr_base, csr_offset, val)		\
+	__raw_writel(val, csr_base + csr_offset)
+#define YCC_CSR_RD(csr_base, csr_offset)		\
+	__raw_readl(csr_base + csr_offset)
+
+static inline void ycc_dev_get(struct ycc_dev *ydev)
+{
+	atomic_inc(&ydev->refcnt);
+}
+
+static inline void ycc_dev_put(struct ycc_dev *ydev)
+{
+	atomic_dec(&ydev->refcnt);
+}
+
+static inline void ycc_g_err_mask(void __iomem *vaddr)
+{
+	/* This will mask all error interrupt */
+	YCC_CSR_WR(vaddr, REG_YCC_DEV_INT_MASK, (u32)~0);
+}
+
+static inline void ycc_g_err_unmask(void __iomem *vaddr)
+{
+	/* This will unmask all error interrupt */
+	YCC_CSR_WR(vaddr, REG_YCC_DEV_INT_MASK, 0);
+}
+
+#endif
diff --git a/drivers/crypto/ycc/ycc_drv.c b/drivers/crypto/ycc/ycc_drv.c
new file mode 100644
index 00000000..4467dcd
--- /dev/null
+++ b/drivers/crypto/ycc/ycc_drv.c
@@ -0,0 +1,444 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * YCC: Drivers for Alibaba YCC (Yitian Cryptography Complex) cryptographic
+ *   accelerator. Enables the on-chip cryptographic accelerator of Alibaba
+ *   Yitian SoCs which is based on ARMv9 architecture.
+ *
+ * Copyright (C) 2020-2022 Alibaba Corporation. All rights reserved.
+ * Author: Zelin Deng <zelin.deng@linux.alibaba.com>
+ * Author: Guanjun <guanjun@linux.alibaba.com>
+ */
+
+#define pr_fmt(fmt) "YCC: " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/iommu.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+
+#include "ycc_isr.h"
+
+static const char ycc_name[] = "ycc";
+
+static bool is_polling = true;
+module_param(is_polling, bool, 0644);
+
+LIST_HEAD(ycc_table);
+static DEFINE_MUTEX(ycc_mutex);
+
+/*
+ * Each ycc device (RCIEP or RCEC) supports upto 48 VFs
+ * when enables SR-IOV. So each socket has 98 devices,
+ * includes 2 PFs and 96 VFs.
+ */
+#define YCC_MAX_DEVICES		(98 * 4) /* Assume 4 sockets */
+static DEFINE_IDR(ycc_idr);
+
+static int ycc_device_flr(struct pci_dev *pdev, struct pci_dev *rcec_pdev)
+{
+	int ret;
+
+	/*
+	 * NOTE: When rciep gets FLR, its associated rcec gets reset as well.
+	 * It does not make sense that individual pcie device should impact
+	 * others. Before it has been fixed on silicon side, add a workaround to
+	 * do FLR properly -- save both pci states and restore them latter.
+	 */
+	ret = pci_save_state(pdev);
+	if (ret) {
+		pr_err("Failed to save pci state\n");
+		return ret;
+	}
+
+	ret = pci_save_state(rcec_pdev);
+	if (ret) {
+		pr_err("Failed to save RCEC pci state\n");
+		return ret;
+	}
+
+	pcie_reset_flr(pdev, 0);
+	pcie_reset_flr(rcec_pdev, 0);
+
+	pci_restore_state(pdev);
+	pci_restore_state(rcec_pdev);
+
+	return 0;
+}
+
+static int ycc_resource_setup(struct ycc_dev *ydev)
+{
+	struct pci_dev *rcec_pdev = ydev->assoc_dev->pdev;
+	struct pci_dev *pdev = ydev->pdev;
+	struct ycc_bar *abar, *cfg_bar;
+	u32 hclk_status;
+	int ret;
+
+	ret = ycc_device_flr(pdev, rcec_pdev);
+	if (ret)
+		return ret;
+
+	ret = pci_request_regions(pdev, ydev->dev_name);
+	if (ret) {
+		pr_err("Failed to request RCIEP mem regions\n");
+		return ret;
+	}
+
+	ret = -EIO;
+	cfg_bar = &ydev->ycc_bars[YCC_SEC_CFG_BAR];
+	cfg_bar->paddr = pci_resource_start(pdev, YCC_SEC_CFG_BAR);
+	cfg_bar->size = pci_resource_len(pdev, YCC_SEC_CFG_BAR);
+	cfg_bar->vaddr = ioremap(cfg_bar->paddr, cfg_bar->size);
+	if (!cfg_bar->vaddr) {
+		pr_err("Failed to ioremap RCIEP cfg bar\n");
+		goto release_mem_regions;
+	}
+
+	ycc_g_err_mask(cfg_bar->vaddr);
+
+	YCC_CSR_WR(cfg_bar->vaddr, REG_YCC_CTL, 0|YCC_CTRL_IRAM_EN);
+	YCC_CSR_WR(cfg_bar->vaddr, REG_YCC_GO, 0|YCC_GO_PWRON);
+
+	/* Waiting for ycc firmware ready, 1000ms is recommended by the HW designers */
+	mdelay(1000);
+	if (!(YCC_CSR_RD(cfg_bar->vaddr, REG_YCC_GO) & YCC_GO_ENABLED)) {
+		pr_err("Failed to set ycc enabled\n");
+		goto iounmap_cfg_bar;
+	}
+
+	/* Check HCLK status register, some error may happen at PWRON stage */
+	hclk_status = YCC_CSR_RD(cfg_bar->vaddr, REG_YCC_HCLK_INT_STATUS);
+	if (hclk_status & YCC_HCLK_INIT_ERR) {
+		pr_err("Error happened when ycc was initializing\n");
+		goto iounmap_cfg_bar;
+	}
+
+	abar = &ydev->ycc_bars[YCC_NSEC_Q_BAR];
+	abar->paddr = pci_resource_start(pdev, YCC_NSEC_Q_BAR);
+	abar->size = pci_resource_len(pdev, YCC_NSEC_Q_BAR);
+	abar->vaddr = pci_iomap(pdev, YCC_NSEC_Q_BAR, abar->size);
+	if (!abar->vaddr) {
+		pr_err("Failed to ioremap RCIEP queue bar\n");
+		goto iounmap_cfg_bar;
+	}
+
+	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+	if (ret < 0) {
+		pr_info("Failed to set DMA bit mask 64, try 32\n");
+		ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+		if (ret < 0)
+			goto iounmap_queue_bar;
+	}
+
+	ret = ycc_enable_msix(ydev);
+	if (ret <= 0) {
+		pr_err("Failed to enable msix, ret: %d\n", ret);
+		ret = (ret == 0) ? -EINVAL : ret;
+		goto iounmap_queue_bar;
+	}
+
+	ret = ycc_init_global_err(ydev);
+	if (ret) {
+		pr_err("Failed to enable global err\n");
+		goto disable_msix;
+	}
+
+	ret = ycc_alloc_irqs(ydev);
+	if (ret) {
+		pr_err("Failed to alloc irqs\n");
+		goto deinit_g_err;
+	}
+
+	YCC_CSR_WR(cfg_bar->vaddr, REG_YCC_HCLK_INT_STATUS, ~0);
+	ycc_g_err_unmask(cfg_bar->vaddr);
+	return 0;
+
+deinit_g_err:
+	ycc_deinit_global_err(ydev);
+disable_msix:
+	ycc_disable_msix(ydev);
+iounmap_queue_bar:
+	iounmap(abar->vaddr);
+iounmap_cfg_bar:
+	iounmap(cfg_bar->vaddr);
+release_mem_regions:
+	pci_release_regions(pdev);
+	return ret;
+}
+
+static void ycc_resource_free(struct ycc_dev *ydev)
+{
+	ycc_deinit_global_err(ydev);
+	ycc_free_irqs(ydev);
+	ycc_disable_msix(ydev);
+	iounmap(ydev->ycc_bars[YCC_SEC_CFG_BAR].vaddr);
+	iounmap(ydev->ycc_bars[YCC_NSEC_Q_BAR].vaddr);
+	pci_release_regions(ydev->pdev);
+}
+
+static inline bool ycc_rcec_match(struct pci_dev *pdev0, struct pci_dev *pdev1)
+{
+	return pdev0->bus->number == pdev1->bus->number;
+}
+
+static int ycc_rcec_bind(struct ycc_dev *ydev)
+{
+	struct ycc_dev *assoc_dev, *rciep, *rcec;
+	struct list_head *itr;
+	int ret = 0;
+
+	if (list_empty(&ycc_table))
+		return ret;
+
+	list_for_each(itr, &ycc_table) {
+		assoc_dev = list_entry(itr, struct ycc_dev, list);
+		/* not in the same pci bus */
+		if (!ycc_rcec_match(ydev->pdev, assoc_dev->pdev))
+			continue;
+
+		/* if sriov is enabled, it could be the same */
+		if (ydev == assoc_dev)
+			continue;
+
+		/* if sriov is enabled, found other VFs */
+		if (ydev->type == assoc_dev->type)
+			continue;
+
+		/* have been bound */
+		if (test_bit(YDEV_STATUS_BIND, &assoc_dev->status))
+			continue;
+
+		/* assocated device has been enabled sriov */
+		if (test_bit(YDEV_STATUS_SRIOV, &assoc_dev->status))
+			break;
+
+		ydev->assoc_dev = assoc_dev;
+		assoc_dev->assoc_dev = ydev;
+		rciep = (ydev->type == YCC_RCIEP) ? ydev : ydev->assoc_dev;
+		rcec = rciep->assoc_dev;
+
+		ret = sysfs_create_link(&rcec->pdev->dev.kobj,
+					&rciep->pdev->dev.kobj, "ycc_rciep");
+		if (ret)
+			goto out;
+
+		ret = sysfs_create_link(&rciep->pdev->dev.kobj,
+					&rcec->pdev->dev.kobj, "ycc_rcec");
+		if (ret)
+			goto remove_rciep_link;
+
+		ret = ycc_resource_setup(rciep);
+		if (ret)
+			goto remove_rcec_link;
+
+		set_bit(YDEV_STATUS_READY, &rciep->status);
+		set_bit(YDEV_STATUS_BIND, &rciep->status);
+		set_bit(YDEV_STATUS_READY, &rcec->status);
+		set_bit(YDEV_STATUS_BIND, &rcec->status);
+		break;
+	}
+
+	return ret;
+
+remove_rcec_link:
+	sysfs_remove_link(&rciep->pdev->dev.kobj, "ycc_rcec");
+remove_rciep_link:
+	sysfs_remove_link(&rcec->pdev->dev.kobj, "ycc_rciep");
+out:
+	return ret;
+}
+
+static void ycc_rcec_unbind(struct ycc_dev *ydev)
+{
+	struct ycc_dev *rciep, *rcec;
+
+	if (!test_bit(YDEV_STATUS_BIND, &ydev->status))
+		return;
+
+	rciep = (ydev->type == YCC_RCIEP) ? ydev : ydev->assoc_dev;
+	rcec = rciep->assoc_dev;
+
+	clear_bit(YDEV_STATUS_READY, &rciep->status);
+	clear_bit(YDEV_STATUS_READY, &rcec->status);
+	clear_bit(YDEV_STATUS_BIND, &rciep->status);
+	clear_bit(YDEV_STATUS_BIND, &rcec->status);
+	sysfs_remove_link(&rcec->pdev->dev.kobj, "ycc_rciep");
+	sysfs_remove_link(&rciep->pdev->dev.kobj, "ycc_rcec");
+	ycc_resource_free(rciep);
+	rciep->assoc_dev = NULL;
+	rcec->assoc_dev = NULL;
+}
+
+static int ycc_dev_add(struct ycc_dev *ydev)
+{
+	int ret;
+
+	mutex_lock(&ycc_mutex);
+	ret = ycc_rcec_bind(ydev);
+	if (ret)
+		goto out;
+	list_add_tail(&ydev->list, &ycc_table);
+
+out:
+	mutex_unlock(&ycc_mutex);
+	return ret;
+}
+
+static void ycc_dev_del(struct ycc_dev *ydev)
+{
+	mutex_lock(&ycc_mutex);
+	ycc_rcec_unbind(ydev);
+	list_del(&ydev->list);
+	mutex_unlock(&ycc_mutex);
+}
+
+static inline int ycc_rciep_init(struct ycc_dev *ydev)
+{
+	struct pci_dev *pdev = ydev->pdev;
+	char name[YCC_MAX_DEBUGFS_NAME + 1];
+	int idr;
+
+	ydev->sec = false;
+	ydev->dev_name = ycc_name;
+	ydev->is_polling = is_polling;
+
+	idr = idr_alloc(&ycc_idr, ydev, 0, YCC_MAX_DEVICES, GFP_KERNEL);
+	if (idr < 0) {
+		pr_err("Failed to allocate idr for RCIEP device\n");
+		return idr;
+	}
+
+	ydev->id = idr;
+
+	snprintf(name, YCC_MAX_DEBUGFS_NAME, "ycc_%02x:%02d.%02d",
+		 pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+	ydev->debug_dir = debugfs_create_dir(name, NULL);
+	/* If failed to create debugfs, driver can still work */
+	if (IS_ERR_OR_NULL(ydev->debug_dir)) {
+		pr_warn("Failed to create debugfs for RCIEP device\n");
+		ydev->debug_dir = NULL;
+	}
+
+	return 0;
+}
+
+static int ycc_drv_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct ycc_dev *ydev;
+	struct device *dev = &pdev->dev;
+	int node = dev_to_node(dev);
+	int ret = -ENOMEM;
+
+	ydev = kzalloc_node(sizeof(struct ycc_dev), GFP_KERNEL, node);
+	if (!ydev)
+		return ret;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		pr_err("Failed to enable pci device\n");
+		goto free_ydev;
+	}
+	pci_set_master(pdev);
+	pci_set_drvdata(pdev, ydev);
+
+	ydev->pdev = pdev;
+	ydev->is_vf = false;
+	ydev->enable_vf = false;
+	ydev->node = node;
+	if (id->device == PCI_DEVICE_ID_RCIEP) {
+		ydev->type = YCC_RCIEP;
+		ret = ycc_rciep_init(ydev);
+		if (ret)
+			goto disable_ydev;
+	} else {
+		ydev->type = YCC_RCEC;
+	}
+
+	ret = ycc_dev_add(ydev);
+	if (ret)
+		goto remove_debugfs;
+
+	return ret;
+
+remove_debugfs:
+	if (ydev->type == YCC_RCIEP) {
+		debugfs_remove_recursive(ydev->debug_dir);
+		idr_remove(&ycc_idr, ydev->id);
+	}
+disable_ydev:
+	pci_disable_device(pdev);
+free_ydev:
+	pr_err("Failed to probe %s\n", ydev->type == YCC_RCIEP ? "RCIEP" : "RCEC");
+	kfree(ydev);
+	return ret;
+}
+
+static void ycc_drv_remove(struct pci_dev *pdev)
+{
+	struct ycc_dev *ydev = pci_get_drvdata(pdev);
+
+	ycc_dev_del(ydev);
+	if (ydev->type == YCC_RCIEP) {
+		debugfs_remove_recursive(ydev->debug_dir);
+		idr_remove(&ycc_idr, ydev->id);
+	}
+
+	pci_disable_sriov(pdev);
+	pci_disable_device(pdev);
+	kfree(ydev);
+}
+
+/*
+ * SRIOV is not supported now.
+ */
+static int ycc_drv_sriov_configure(struct pci_dev *pdev, int numvfs)
+{
+	return -EFAULT;
+}
+
+static const struct pci_device_id ycc_id_table[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_YCC, PCI_DEVICE_ID_RCIEP) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_YCC, PCI_DEVICE_ID_RCEC) },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, ycc_id_table);
+
+static struct pci_driver ycc_driver = {
+	.name		= "ycc",
+	.id_table	= ycc_id_table,
+	.probe		= ycc_drv_probe,
+	.remove		= ycc_drv_remove,
+	.sriov_configure = ycc_drv_sriov_configure,
+};
+
+static int __init ycc_drv_init(void)
+{
+	int ret;
+
+	ret = pci_register_driver(&ycc_driver);
+	if (ret)
+		goto out;
+
+	return 0;
+
+out:
+	return ret;
+}
+
+static void __exit ycc_drv_exit(void)
+{
+	pci_unregister_driver(&ycc_driver);
+}
+
+module_init(ycc_drv_init);
+module_exit(ycc_drv_exit);
+MODULE_AUTHOR("Zelin Deng <zelin.deng@linux.alibaba.com>");
+MODULE_AUTHOR("Guanjun <guanjun@linux.alibaba.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Driver for Alibaba YCC cryptographic accelerator");
diff --git a/drivers/crypto/ycc/ycc_isr.c b/drivers/crypto/ycc/ycc_isr.c
new file mode 100644
index 00000000..f2f751c
--- /dev/null
+++ b/drivers/crypto/ycc/ycc_isr.c
@@ -0,0 +1,117 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) "YCC: " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "ycc_isr.h"
+
+/*
+ * TODO: will implement when ycc ring actually work.
+ */
+static void ycc_process_global_err(struct work_struct *work)
+{
+}
+
+static irqreturn_t ycc_g_err_isr(int irq, void *data)
+{
+	struct ycc_dev *ydev = (struct ycc_dev *)data;
+	struct ycc_bar *cfg_bar;
+
+	if (test_and_set_bit(YDEV_STATUS_ERR, &ydev->status))
+		return IRQ_HANDLED;
+
+	/* Mask global errors until it has been processed */
+	cfg_bar = &ydev->ycc_bars[YCC_SEC_CFG_BAR];
+	ycc_g_err_mask(cfg_bar->vaddr);
+
+	clear_bit(YDEV_STATUS_READY, &ydev->status);
+
+	schedule_work(&ydev->work);
+	return IRQ_HANDLED;
+}
+
+int ycc_enable_msix(struct ycc_dev *ydev)
+{
+	struct pci_dev *rcec_pdev = ydev->assoc_dev->pdev;
+
+	/* Disable intx explicitly */
+	return pci_alloc_irq_vectors(rcec_pdev, YCC_IRQS, YCC_IRQS, PCI_IRQ_MSIX);
+}
+
+void ycc_disable_msix(struct ycc_dev *ydev)
+{
+	struct pci_dev *rcec_pdev = ydev->assoc_dev->pdev;
+
+	pci_free_irq_vectors(rcec_pdev);
+}
+
+static int ycc_setup_global_err_workqueue(struct ycc_dev *ydev)
+{
+	char name[32] = {0};
+
+	sprintf(name, "ycc_dev_%d_g_errd", ydev->id);
+	INIT_WORK(&ydev->work, ycc_process_global_err);
+
+	/* Allocated, but not used temporarily */
+	ydev->dev_err_q = alloc_workqueue(name, WQ_UNBOUND, 0);
+	if (!ydev->dev_err_q) {
+		pr_err("Failed to alloc workqueue for dev: %d\n", ydev->id);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void ycc_cleanup_global_err_workqueue(struct ycc_dev *ydev)
+{
+	if (ydev->dev_err_q)
+		destroy_workqueue(ydev->dev_err_q);
+}
+
+/*
+ * TODO: Just request irq for global err. Irq for each ring
+ * will be requested when ring actually work.
+ */
+int ycc_alloc_irqs(struct ycc_dev *ydev)
+{
+	struct pci_dev *rcec_pdev = ydev->assoc_dev->pdev;
+	int num = ydev->is_vf ? 1 : YCC_RINGPAIR_NUM;
+	int ret;
+
+	sprintf(ydev->err_irq_name, "ycc_dev_%d_global_err", ydev->id);
+	ret = request_irq(pci_irq_vector(rcec_pdev, num),
+			  ycc_g_err_isr, 0, ydev->err_irq_name, ydev);
+	if (ret)
+		pr_err("Failed to alloc global irq interrupt for dev: %d\n", ydev->id);
+
+	return ret;
+}
+
+/*
+ * TODO: Same as the allocate action.
+ */
+void ycc_free_irqs(struct ycc_dev *ydev)
+{
+	struct pci_dev *rcec_pdev = ydev->assoc_dev->pdev;
+	int num = ydev->is_vf ? 1 : YCC_RINGPAIR_NUM;
+
+	free_irq(pci_irq_vector(rcec_pdev, num), ydev);
+}
+
+int ycc_init_global_err(struct ycc_dev *ydev)
+{
+	return ycc_setup_global_err_workqueue(ydev);
+}
+
+void ycc_deinit_global_err(struct ycc_dev *ydev)
+{
+	ycc_cleanup_global_err_workqueue(ydev);
+}
diff --git a/drivers/crypto/ycc/ycc_isr.h b/drivers/crypto/ycc/ycc_isr.h
new file mode 100644
index 00000000..8318a6f
--- /dev/null
+++ b/drivers/crypto/ycc/ycc_isr.h
@@ -0,0 +1,12 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __YCC_ISR_H
+
+#include "ycc_dev.h"
+
+int ycc_enable_msix(struct ycc_dev *ydev);
+void ycc_disable_msix(struct ycc_dev *ydev);
+int ycc_alloc_irqs(struct ycc_dev *ydev);
+void ycc_free_irqs(struct ycc_dev *ydev);
+int ycc_init_global_err(struct ycc_dev *ydev);
+void ycc_deinit_global_err(struct ycc_dev *ydev);
+#endif