[PATCH 24/42] PCI: aardvark: Fix support for PME on emulated bridge

From: Pali Rohár
Date: Thu May 06 2021 - 11:36:17 EST


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