@@ -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
@@ -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/
new file mode 100644
@@ -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.
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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");
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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