xref: /openbmc/linux/drivers/net/wwan/iosm/iosm_ipc_irq.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1*7f41ce08SM Chetan Kumar // SPDX-License-Identifier: GPL-2.0-only
2*7f41ce08SM Chetan Kumar /*
3*7f41ce08SM Chetan Kumar  * Copyright (C) 2020-21 Intel Corporation.
4*7f41ce08SM Chetan Kumar  */
5*7f41ce08SM Chetan Kumar 
6*7f41ce08SM Chetan Kumar #include "iosm_ipc_pcie.h"
7*7f41ce08SM Chetan Kumar #include "iosm_ipc_protocol.h"
8*7f41ce08SM Chetan Kumar 
ipc_write_dbell_reg(struct iosm_pcie * ipc_pcie,int irq_n,u32 data)9*7f41ce08SM Chetan Kumar static void ipc_write_dbell_reg(struct iosm_pcie *ipc_pcie, int irq_n, u32 data)
10*7f41ce08SM Chetan Kumar {
11*7f41ce08SM Chetan Kumar 	void __iomem *write_reg;
12*7f41ce08SM Chetan Kumar 
13*7f41ce08SM Chetan Kumar 	/* Select the first doorbell register, which is only currently needed
14*7f41ce08SM Chetan Kumar 	 * by CP.
15*7f41ce08SM Chetan Kumar 	 */
16*7f41ce08SM Chetan Kumar 	write_reg = (void __iomem *)((u8 __iomem *)ipc_pcie->ipc_regs +
17*7f41ce08SM Chetan Kumar 				     ipc_pcie->doorbell_write +
18*7f41ce08SM Chetan Kumar 				     (irq_n * ipc_pcie->doorbell_reg_offset));
19*7f41ce08SM Chetan Kumar 
20*7f41ce08SM Chetan Kumar 	/* Fire the doorbell irq by writing data on the doorbell write pointer
21*7f41ce08SM Chetan Kumar 	 * register.
22*7f41ce08SM Chetan Kumar 	 */
23*7f41ce08SM Chetan Kumar 	iowrite32(data, write_reg);
24*7f41ce08SM Chetan Kumar }
25*7f41ce08SM Chetan Kumar 
ipc_doorbell_fire(struct iosm_pcie * ipc_pcie,int irq_n,u32 data)26*7f41ce08SM Chetan Kumar void ipc_doorbell_fire(struct iosm_pcie *ipc_pcie, int irq_n, u32 data)
27*7f41ce08SM Chetan Kumar {
28*7f41ce08SM Chetan Kumar 	ipc_write_dbell_reg(ipc_pcie, irq_n, data);
29*7f41ce08SM Chetan Kumar }
30*7f41ce08SM Chetan Kumar 
31*7f41ce08SM Chetan Kumar /* Threaded Interrupt handler for MSI interrupts */
ipc_msi_interrupt(int irq,void * dev_id)32*7f41ce08SM Chetan Kumar static irqreturn_t ipc_msi_interrupt(int irq, void *dev_id)
33*7f41ce08SM Chetan Kumar {
34*7f41ce08SM Chetan Kumar 	struct iosm_pcie *ipc_pcie = dev_id;
35*7f41ce08SM Chetan Kumar 	int instance = irq - ipc_pcie->pci->irq;
36*7f41ce08SM Chetan Kumar 
37*7f41ce08SM Chetan Kumar 	/* Shift the MSI irq actions to the IPC tasklet. IRQ_NONE means the
38*7f41ce08SM Chetan Kumar 	 * irq was not from the IPC device or could not be served.
39*7f41ce08SM Chetan Kumar 	 */
40*7f41ce08SM Chetan Kumar 	if (instance >= ipc_pcie->nvec)
41*7f41ce08SM Chetan Kumar 		return IRQ_NONE;
42*7f41ce08SM Chetan Kumar 
43*7f41ce08SM Chetan Kumar 	if (!test_bit(0, &ipc_pcie->suspend))
44*7f41ce08SM Chetan Kumar 		ipc_imem_irq_process(ipc_pcie->imem, instance);
45*7f41ce08SM Chetan Kumar 
46*7f41ce08SM Chetan Kumar 	return IRQ_HANDLED;
47*7f41ce08SM Chetan Kumar }
48*7f41ce08SM Chetan Kumar 
ipc_release_irq(struct iosm_pcie * ipc_pcie)49*7f41ce08SM Chetan Kumar void ipc_release_irq(struct iosm_pcie *ipc_pcie)
50*7f41ce08SM Chetan Kumar {
51*7f41ce08SM Chetan Kumar 	struct pci_dev *pdev = ipc_pcie->pci;
52*7f41ce08SM Chetan Kumar 
53*7f41ce08SM Chetan Kumar 	if (pdev->msi_enabled) {
54*7f41ce08SM Chetan Kumar 		while (--ipc_pcie->nvec >= 0)
55*7f41ce08SM Chetan Kumar 			free_irq(pdev->irq + ipc_pcie->nvec, ipc_pcie);
56*7f41ce08SM Chetan Kumar 	}
57*7f41ce08SM Chetan Kumar 	pci_free_irq_vectors(pdev);
58*7f41ce08SM Chetan Kumar }
59*7f41ce08SM Chetan Kumar 
ipc_acquire_irq(struct iosm_pcie * ipc_pcie)60*7f41ce08SM Chetan Kumar int ipc_acquire_irq(struct iosm_pcie *ipc_pcie)
61*7f41ce08SM Chetan Kumar {
62*7f41ce08SM Chetan Kumar 	struct pci_dev *pdev = ipc_pcie->pci;
63*7f41ce08SM Chetan Kumar 	int i, rc = -EINVAL;
64*7f41ce08SM Chetan Kumar 
65*7f41ce08SM Chetan Kumar 	ipc_pcie->nvec = pci_alloc_irq_vectors(pdev, IPC_MSI_VECTORS,
66*7f41ce08SM Chetan Kumar 					       IPC_MSI_VECTORS, PCI_IRQ_MSI);
67*7f41ce08SM Chetan Kumar 
68*7f41ce08SM Chetan Kumar 	if (ipc_pcie->nvec < 0) {
69*7f41ce08SM Chetan Kumar 		rc = ipc_pcie->nvec;
70*7f41ce08SM Chetan Kumar 		goto error;
71*7f41ce08SM Chetan Kumar 	}
72*7f41ce08SM Chetan Kumar 
73*7f41ce08SM Chetan Kumar 	if (!pdev->msi_enabled)
74*7f41ce08SM Chetan Kumar 		goto error;
75*7f41ce08SM Chetan Kumar 
76*7f41ce08SM Chetan Kumar 	for (i = 0; i < ipc_pcie->nvec; ++i) {
77*7f41ce08SM Chetan Kumar 		rc = request_threaded_irq(pdev->irq + i, NULL,
78*7f41ce08SM Chetan Kumar 					  ipc_msi_interrupt, IRQF_ONESHOT,
79*7f41ce08SM Chetan Kumar 					  KBUILD_MODNAME, ipc_pcie);
80*7f41ce08SM Chetan Kumar 		if (rc) {
81*7f41ce08SM Chetan Kumar 			dev_err(ipc_pcie->dev, "unable to grab IRQ, rc=%d", rc);
82*7f41ce08SM Chetan Kumar 			ipc_pcie->nvec = i;
83*7f41ce08SM Chetan Kumar 			ipc_release_irq(ipc_pcie);
84*7f41ce08SM Chetan Kumar 			goto error;
85*7f41ce08SM Chetan Kumar 		}
86*7f41ce08SM Chetan Kumar 	}
87*7f41ce08SM Chetan Kumar 
88*7f41ce08SM Chetan Kumar error:
89*7f41ce08SM Chetan Kumar 	return rc;
90*7f41ce08SM Chetan Kumar }
91