// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2020-21 Intel Corporation. */ #include "iosm_ipc_pcie.h" #include "iosm_ipc_protocol.h" static void ipc_write_dbell_reg(struct iosm_pcie *ipc_pcie, int irq_n, u32 data) { void __iomem *write_reg; /* Select the first doorbell register, which is only currently needed * by CP. */ write_reg = (void __iomem *)((u8 __iomem *)ipc_pcie->ipc_regs + ipc_pcie->doorbell_write + (irq_n * ipc_pcie->doorbell_reg_offset)); /* Fire the doorbell irq by writing data on the doorbell write pointer * register. */ iowrite32(data, write_reg); } void ipc_doorbell_fire(struct iosm_pcie *ipc_pcie, int irq_n, u32 data) { ipc_write_dbell_reg(ipc_pcie, irq_n, data); } /* Threaded Interrupt handler for MSI interrupts */ static irqreturn_t ipc_msi_interrupt(int irq, void *dev_id) { struct iosm_pcie *ipc_pcie = dev_id; int instance = irq - ipc_pcie->pci->irq; /* Shift the MSI irq actions to the IPC tasklet. IRQ_NONE means the * irq was not from the IPC device or could not be served. */ if (instance >= ipc_pcie->nvec) return IRQ_NONE; if (!test_bit(0, &ipc_pcie->suspend)) ipc_imem_irq_process(ipc_pcie->imem, instance); return IRQ_HANDLED; } void ipc_release_irq(struct iosm_pcie *ipc_pcie) { struct pci_dev *pdev = ipc_pcie->pci; if (pdev->msi_enabled) { while (--ipc_pcie->nvec >= 0) free_irq(pdev->irq + ipc_pcie->nvec, ipc_pcie); } pci_free_irq_vectors(pdev); } int ipc_acquire_irq(struct iosm_pcie *ipc_pcie) { struct pci_dev *pdev = ipc_pcie->pci; int i, rc = -EINVAL; ipc_pcie->nvec = pci_alloc_irq_vectors(pdev, IPC_MSI_VECTORS, IPC_MSI_VECTORS, PCI_IRQ_MSI); if (ipc_pcie->nvec < 0) { rc = ipc_pcie->nvec; goto error; } if (!pdev->msi_enabled) goto error; for (i = 0; i < ipc_pcie->nvec; ++i) { rc = request_threaded_irq(pdev->irq + i, NULL, ipc_msi_interrupt, IRQF_ONESHOT, KBUILD_MODNAME, ipc_pcie); if (rc) { dev_err(ipc_pcie->dev, "unable to grab IRQ, rc=%d", rc); ipc_pcie->nvec = i; ipc_release_irq(ipc_pcie); goto error; } } error: return rc; }