91 lines
2.1 KiB
C
91 lines
2.1 KiB
C
// 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;
|
|
}
|