1) Request interrupt vector, frees allocated resource.
2) Registers IRQ handler.

Signed-off-by: M Chetan Kumar <m.chetan.ku...@intel.com>
---
v2: Streamline multiple returns using goto.
---
 drivers/net/wwan/iosm/iosm_ipc_irq.c | 91 ++++++++++++++++++++++++++++
 drivers/net/wwan/iosm/iosm_ipc_irq.h | 33 ++++++++++
 2 files changed, 124 insertions(+)
 create mode 100644 drivers/net/wwan/iosm/iosm_ipc_irq.c
 create mode 100644 drivers/net/wwan/iosm/iosm_ipc_irq.h

diff --git a/drivers/net/wwan/iosm/iosm_ipc_irq.c 
b/drivers/net/wwan/iosm/iosm_ipc_irq.c
new file mode 100644
index 000000000000..a3e017604fa4
--- /dev/null
+++ b/drivers/net/wwan/iosm/iosm_ipc_irq.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-21 Intel Corporation.
+ */
+
+#include "iosm_ipc_pcie.h"
+#include "iosm_ipc_protocol.h"
+
+static inline void 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)
+{
+       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;
+}
diff --git a/drivers/net/wwan/iosm/iosm_ipc_irq.h 
b/drivers/net/wwan/iosm/iosm_ipc_irq.h
new file mode 100644
index 000000000000..a8ed596cb6a5
--- /dev/null
+++ b/drivers/net/wwan/iosm/iosm_ipc_irq.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020-21 Intel Corporation.
+ */
+
+#ifndef IOSM_IPC_IRQ_H
+#define IOSM_IPC_IRQ_H
+
+struct iosm_pcie;
+
+/**
+ * ipc_doorbell_fire - fire doorbell to CP
+ * @ipc_pcie:  Pointer to iosm_pcie
+ * @irq_n:     Doorbell type
+ * @data:      ipc state
+ */
+void ipc_doorbell_fire(struct iosm_pcie *ipc_pcie, int irq_n, u32 data);
+
+/**
+ * ipc_release_irq - Release the IRQ handler.
+ * @ipc_pcie:  Pointer to iosm_pcie struct
+ */
+void ipc_release_irq(struct iosm_pcie *ipc_pcie);
+
+/**
+ * ipc_acquire_irq - acquire IRQ & register IRQ handler.
+ * @ipc_pcie:  Pointer to iosm_pcie struct
+ *
+ * Return: 0 on success and failure value on error
+ */
+int ipc_acquire_irq(struct iosm_pcie *ipc_pcie);
+
+#endif
-- 
2.25.1

Reply via email to