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;
 | |
| }
 |