The emulated bridge returns incorrect value for PCI_EXP_RTSTA register during readout in advk_pci_bridge_emul_pcie_conf_read() function. Fix it by setting correct bit PCI_EXP_RTSTA_PME based on PCIE_MSG_PM_PME_MASK. Currently enabling PCI_EXP_RTSTA_PME bit in PCI_EXP_RTCTL register does nothing. This is because PCIe PME driver expects to receive PCIe interrupt defined in PCI_EXP_FLAGS_IRQ register. But aardvark hardware does not trigger PCIe INTx/MSI interrupt for PME event, rather it triggers custom aardvark interrupt which this driver is not processing yet. Fix this issue by handling PME interrupt in advk_pcie_handle_int() and chaining it to PCIe interrupt 0 with generic_handle_irq() (since aardvark hardware sets PCI_EXP_FLAGS_IRQ to zero). With this change PCIe PME driver finally starts receiving PME interrupt. To optimize advk_pci_bridge_emul_pcie_conf_write() code, touch PCIE_ISR0_REG and PCIE_ISR0_MASK_REG registers only when it is really needed, when processing PCI_EXP_RTCTL_PMEIE and PCI_EXP_RTSTA_PME bits. Signed-off-by: Pali Rohár <pali@xxxxxxxxxx> Reviewed-by: Marek Behún <kabel@xxxxxxxxxx> Fixes: 8a3ebd8de328 ("PCI: aardvark: Implement emulated root PCI bridge config space") Cc: stable@xxxxxxxxxxxxxxx # c0f05a6ab525 ("PCI: aardvark: Fix PCI_EXP_RTCTL register configuration") --- drivers/pci/controller/pci-aardvark.c | 40 ++++++++++++++++++++------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 2ea58ba10a97..fac48797d922 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -574,7 +574,10 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, case PCI_EXP_RTSTA: { u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG); u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG); - *value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16 | (msglog >> 16); + u32 val = msglog >> 16; + if (isr0 & PCIE_MSG_PM_PME_MASK) + val |= PCI_EXP_RTSTA_PME; + *value = val; return PCI_BRIDGE_EMUL_HANDLED; } @@ -617,19 +620,21 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, advk_pcie_wait_for_retrain(pcie); break; - case PCI_EXP_RTCTL: { + case PCI_EXP_RTCTL: /* Only mask/unmask PME interrupt */ - u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG) & - ~PCIE_MSG_PM_PME_MASK; - if ((new & PCI_EXP_RTCTL_PMEIE) == 0) - val |= PCIE_MSG_PM_PME_MASK; - advk_writel(pcie, val, PCIE_ISR0_MASK_REG); + if (mask & PCI_EXP_RTCTL_PMEIE) { + u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG); + if ((new & PCI_EXP_RTCTL_PMEIE) == 0) + val |= PCIE_MSG_PM_PME_MASK; + else + val &= ~PCIE_MSG_PM_PME_MASK; + advk_writel(pcie, val, PCIE_ISR0_MASK_REG); + } break; - } case PCI_EXP_RTSTA: - new = (new & PCI_EXP_RTSTA_PME) >> 9; - advk_writel(pcie, new, PCIE_ISR0_REG); + if (new & PCI_EXP_RTSTA_PME) + advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG); break; default: @@ -1219,6 +1224,21 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) if (!isr0_status && !isr1_status) return; + /* 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. + */ + virq = irq_find_mapping(pcie->irq_domain, 0); + if (virq) + generic_handle_irq(virq); + else + dev_err(&pcie->pdev->dev, "unexpected PME IRQ\n"); + } + /* Process ERR interrupt */ if (err_bits) { advk_writel(pcie, err_bits, PCIE_ISR0_REG); -- 2.20.1