]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - drivers/pci/controller/pci-aardvark.c
PCI: aardvark: Fix support for PME requester on emulated bridge
[mirror_ubuntu-jammy-kernel.git] / drivers / pci / controller / pci-aardvark.c
index fcce4e391d0a69195cf27a3afc88a1c740e9cfcc..6e8d1dc9bf2f28df445fc950609ce2fdb31d4688 100644 (file)
@@ -589,6 +589,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
        reg &= ~PCIE_ISR0_MSI_INT_PENDING;
        advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
 
+       /* Unmask PME interrupt for processing of PME requester */
+       reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+       reg &= ~PCIE_MSG_PM_PME_MASK;
+       advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
+
        /* Enable summary interrupt for GIC SPI source */
        reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
        advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG);
@@ -855,22 +860,11 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
                *value = PCI_EXP_SLTSTA_PDS << 16;
                return PCI_BRIDGE_EMUL_HANDLED;
 
-       case PCI_EXP_RTCTL: {
-               u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
-               *value = (val & PCIE_MSG_PM_PME_MASK) ? 0 : PCI_EXP_RTCTL_PMEIE;
-               *value |= le16_to_cpu(bridge->pcie_conf.rootctl) & PCI_EXP_RTCTL_CRSSVE;
-               *value |= PCI_EXP_RTCAP_CRSVIS << 16;
-               return PCI_BRIDGE_EMUL_HANDLED;
-       }
-
-       case PCI_EXP_RTSTA: {
-               u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG);
-               u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG);
-               *value = msglog >> 16;
-               if (isr0 & PCIE_MSG_PM_PME_MASK)
-                       *value |= PCI_EXP_RTSTA_PME;
-               return PCI_BRIDGE_EMUL_HANDLED;
-       }
+       /*
+        * PCI_EXP_RTCTL and PCI_EXP_RTSTA are also supported, but do not need
+        * to be handled here, because their values are stored in emulated
+        * config space buffer, and we read them from there when needed.
+        */
 
        case PCI_EXP_LNKCAP: {
                u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
@@ -924,22 +918,19 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
                        advk_pcie_wait_for_retrain(pcie);
                break;
 
-       case PCI_EXP_RTCTL:
-               /* Only mask/unmask PME interrupt */
-               if (mask & PCI_EXP_RTCTL_PMEIE) {
-                       u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
-                       if (new & PCI_EXP_RTCTL_PMEIE)
-                               val &= ~PCIE_MSG_PM_PME_MASK;
-                       else
-                               val |= PCIE_MSG_PM_PME_MASK;
-                       advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
-               }
+       case PCI_EXP_RTCTL: {
+               u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl);
+               /* Only emulation of PMEIE and CRSSVE bits is provided */
+               rootctl &= PCI_EXP_RTCTL_PMEIE | PCI_EXP_RTCTL_CRSSVE;
+               bridge->pcie_conf.rootctl = cpu_to_le16(rootctl);
                break;
+       }
 
-       case PCI_EXP_RTSTA:
-               if (new & PCI_EXP_RTSTA_PME)
-                       advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
-               break;
+       /*
+        * PCI_EXP_RTSTA is also supported, but does not need to be handled
+        * here, because its value is stored in emulated config space buffer,
+        * and we write it there when needed.
+        */
 
        case PCI_EXP_DEVCTL:
        case PCI_EXP_DEVCTL2:
@@ -1444,6 +1435,32 @@ static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie)
        irq_domain_remove(pcie->irq_domain);
 }
 
+static void advk_pcie_handle_pme(struct advk_pcie *pcie)
+{
+       u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16;
+
+       advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
+
+       /*
+        * PCIE_MSG_LOG_REG contains the last inbound message, so store
+        * the requester ID only when PME was not asserted yet.
+        * Also do not trigger PME interrupt when PME is still asserted.
+        */
+       if (!(le32_to_cpu(pcie->bridge.pcie_conf.rootsta) & PCI_EXP_RTSTA_PME)) {
+               pcie->bridge.pcie_conf.rootsta = cpu_to_le32(requester | PCI_EXP_RTSTA_PME);
+
+               /*
+                * Trigger PME interrupt only if PMEIE bit in Root Control is set.
+                * Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe interrupt 0.
+                */
+               if (!(le16_to_cpu(pcie->bridge.pcie_conf.rootctl) & PCI_EXP_RTCTL_PMEIE))
+                       return;
+
+               if (generic_handle_domain_irq(pcie->irq_domain, 0) == -EINVAL)
+                       dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
+       }
+}
+
 static void advk_pcie_handle_msi(struct advk_pcie *pcie)
 {
        u32 msi_val, msi_mask, msi_status, msi_idx;
@@ -1479,17 +1496,9 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie)
        isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
        isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
 
-       /* Process PME interrupt */
-       if (isr0_status & PCIE_MSG_PM_PME_MASK) {
-               /*
-                * Do not clear PME interrupt bit in ISR0, it is cleared by IRQ
-                * receiver by writing to the PCI_EXP_RTSTA register of emulated
-                * root bridge. Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ,
-                * so use PCIe interrupt 0.
-                */
-               if (generic_handle_domain_irq(pcie->irq_domain, 0) == -EINVAL)
-                       dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
-       }
+       /* Process PME interrupt as the first one to do not miss PME requester id */
+       if (isr0_status & PCIE_MSG_PM_PME_MASK)
+               advk_pcie_handle_pme(pcie);
 
        /* Process ERR interrupt */
        if (isr0_status & PCIE_ISR0_ERR_MASK) {