162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020-21 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "iosm_ipc_pcie.h"
762306a36Sopenharmony_ci#include "iosm_ipc_protocol.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistatic void ipc_write_dbell_reg(struct iosm_pcie *ipc_pcie, int irq_n, u32 data)
1062306a36Sopenharmony_ci{
1162306a36Sopenharmony_ci	void __iomem *write_reg;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci	/* Select the first doorbell register, which is only currently needed
1462306a36Sopenharmony_ci	 * by CP.
1562306a36Sopenharmony_ci	 */
1662306a36Sopenharmony_ci	write_reg = (void __iomem *)((u8 __iomem *)ipc_pcie->ipc_regs +
1762306a36Sopenharmony_ci				     ipc_pcie->doorbell_write +
1862306a36Sopenharmony_ci				     (irq_n * ipc_pcie->doorbell_reg_offset));
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	/* Fire the doorbell irq by writing data on the doorbell write pointer
2162306a36Sopenharmony_ci	 * register.
2262306a36Sopenharmony_ci	 */
2362306a36Sopenharmony_ci	iowrite32(data, write_reg);
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_civoid ipc_doorbell_fire(struct iosm_pcie *ipc_pcie, int irq_n, u32 data)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	ipc_write_dbell_reg(ipc_pcie, irq_n, data);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Threaded Interrupt handler for MSI interrupts */
3262306a36Sopenharmony_cistatic irqreturn_t ipc_msi_interrupt(int irq, void *dev_id)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct iosm_pcie *ipc_pcie = dev_id;
3562306a36Sopenharmony_ci	int instance = irq - ipc_pcie->pci->irq;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* Shift the MSI irq actions to the IPC tasklet. IRQ_NONE means the
3862306a36Sopenharmony_ci	 * irq was not from the IPC device or could not be served.
3962306a36Sopenharmony_ci	 */
4062306a36Sopenharmony_ci	if (instance >= ipc_pcie->nvec)
4162306a36Sopenharmony_ci		return IRQ_NONE;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!test_bit(0, &ipc_pcie->suspend))
4462306a36Sopenharmony_ci		ipc_imem_irq_process(ipc_pcie->imem, instance);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return IRQ_HANDLED;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_civoid ipc_release_irq(struct iosm_pcie *ipc_pcie)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct pci_dev *pdev = ipc_pcie->pci;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (pdev->msi_enabled) {
5462306a36Sopenharmony_ci		while (--ipc_pcie->nvec >= 0)
5562306a36Sopenharmony_ci			free_irq(pdev->irq + ipc_pcie->nvec, ipc_pcie);
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciint ipc_acquire_irq(struct iosm_pcie *ipc_pcie)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct pci_dev *pdev = ipc_pcie->pci;
6362306a36Sopenharmony_ci	int i, rc = -EINVAL;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	ipc_pcie->nvec = pci_alloc_irq_vectors(pdev, IPC_MSI_VECTORS,
6662306a36Sopenharmony_ci					       IPC_MSI_VECTORS, PCI_IRQ_MSI);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (ipc_pcie->nvec < 0) {
6962306a36Sopenharmony_ci		rc = ipc_pcie->nvec;
7062306a36Sopenharmony_ci		goto error;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (!pdev->msi_enabled)
7462306a36Sopenharmony_ci		goto error;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	for (i = 0; i < ipc_pcie->nvec; ++i) {
7762306a36Sopenharmony_ci		rc = request_threaded_irq(pdev->irq + i, NULL,
7862306a36Sopenharmony_ci					  ipc_msi_interrupt, IRQF_ONESHOT,
7962306a36Sopenharmony_ci					  KBUILD_MODNAME, ipc_pcie);
8062306a36Sopenharmony_ci		if (rc) {
8162306a36Sopenharmony_ci			dev_err(ipc_pcie->dev, "unable to grab IRQ, rc=%d", rc);
8262306a36Sopenharmony_ci			ipc_pcie->nvec = i;
8362306a36Sopenharmony_ci			ipc_release_irq(ipc_pcie);
8462306a36Sopenharmony_ci			goto error;
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cierror:
8962306a36Sopenharmony_ci	return rc;
9062306a36Sopenharmony_ci}
91