@@ -424,6 +424,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
reg &= ~(PCIE_ISR0_FAT_ERR | PCIE_ISR0_NFAT_ERR | PCIE_ISR0_CORR_ERR);
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);
@@ -557,30 +562,17 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
{
struct advk_pcie *pcie = bridge->data;
+ /*
+ * PCI_EXP_RTCTL and PCI_EXP_RTSTA registers are fully supported
+ * but their values are stored only in emulated config space buffer.
+ * So there is no need to handle them in read_pcie callback.
+ */
switch (reg) {
case PCI_EXP_SLTCTL:
*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 |= 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);
- u32 val = msglog >> 16;
- if (isr0 & PCIE_MSG_PM_PME_MASK)
- val |= PCI_EXP_RTSTA_PME;
- *value = val;
- return PCI_BRIDGE_EMUL_HANDLED;
- }
-
case PCI_EXP_LNKCTL: {
/* u32 contains both PCI_EXP_LNKCTL and PCI_EXP_LNKSTA */
u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg) &
@@ -609,6 +601,12 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
{
struct advk_pcie *pcie = bridge->data;
+ /*
+ * PCI_EXP_RTCTL and PCI_EXP_RTSTA registers are fully supported
+ * but their values are stored only in emulated config space buffer.
+ * So there is no need to handle them in write_pcie callback.
+ */
+
switch (reg) {
case PCI_EXP_DEVCTL:
advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
@@ -620,23 +618,6 @@ 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) == 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:
- if (new & PCI_EXP_RTSTA_PME)
- advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
- break;
-
default:
break;
}
@@ -1224,19 +1205,29 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie)
if (!isr0_status && !isr1_status)
return;
- /* Process PME interrupt */
+ /* Process PME interrupt as the first one to do not miss PME requester id */
if (isr0_status & PCIE_MSG_PM_PME_MASK) {
+ advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
/*
- * 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.
+ * PCIE_MSG_LOG_REG contains the last inbound message,
+ * so store requester id only when PME was not asserted yet.
+ * Also do not trigger PME interrupt when PME is still asserted.
*/
- virq = irq_find_mapping(pcie->irq_domain, 0);
- if (virq)
- generic_handle_irq(virq);
- else
- dev_err(&pcie->pdev->dev, "unexpected PME IRQ\n");
+ if (!(le32_to_cpu(pcie->bridge.pcie_conf.rootsta) & PCI_EXP_RTSTA_PME)) {
+ u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16;
+ pcie->bridge.pcie_conf.rootsta = cpu_to_le32(requester | PCI_EXP_RTSTA_PME);
+ /*
+ * Trigger PME interrupt only in case when 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) {
+ 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 */