diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 3e6aa31..2140a3f 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -819,4 +819,6 @@ config CRYPTO_DEV_SA2UL
 
 source "drivers/crypto/keembay/Kconfig"
 
+source "drivers/crypto/ycc/Kconfig"
+
 endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index f81703a8..0ba29dd 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -51,4 +51,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..7065273
--- /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..e5f76af
--- /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 *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 *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..f056d21
--- /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);
+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
