I would like to get AER working on the am6422 with an upstream kernel.
As far as I can tell the AER port driver gets loaded but AER is not functional. So I tried my luck starring at the TRM and come up with something. It looks like I need to enable the error interrupts first and it then gets signaled via a non MSI/MSIX interrupt.
What I am missing?
+++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -27,6 +27,9 @@ #define STATUS_REG_SYS_2 0x508 #define STATUS_CLR_REG_SYS_2 0x708 #define LINK_DOWN BIT(1) +#define J7200_ERR_CORR BIT(6) +#define J7200_ERR_NONFATAL BIT(7) +#define J7200_ERR_FATAL BIT(8) #define J7200_LINK_DOWN BIT(10) #define J721E_PCIE_USER_CMD_STATUS 0x4 @@ -58,6 +61,7 @@ void __iomem *user_cfg_base; void __iomem *intd_cfg_base; u32 linkdown_irq_regfield; + u32 error_irq_regfield; }; enum j721e_pcie_mode { @@ -70,6 +74,7 @@ unsigned int quirk_retrain_flag:1; unsigned int quirk_detect_quiet_flag:1; u32 linkdown_irq_regfield; + u32 error_irq_regfield; unsigned int byte_access_allowed:1; }; @@ -95,10 +100,17 @@ writel(value, pcie->intd_cfg_base + offset); } +static pci_ers_result_t j721e_reset_link(struct pci_dev *pdev) +{ + dev_info(&pdev->dev, "LINK RESET!\n"); + return PCI_ERS_RESULT_DISCONNECT; +} + static irqreturn_t j721e_pcie_link_irq_handler(int irq, void *priv) { struct j721e_pcie *pcie = priv; struct device *dev = pcie->cdns_pcie->dev; + struct pci_dev *rdev = pcie->cdns_pcie->rdev; u32 reg; reg = j721e_pcie_intd_readl(pcie, STATUS_REG_SYS_2); @@ -107,6 +119,9 @@ dev_err(dev, "LINK DOWN!\n"); + if (rdev) + pcie_do_recovery(rdev, pci_channel_io_frozen, j721e_reset_link); + j721e_pcie_intd_writel(pcie, STATUS_CLR_REG_SYS_2, pcie->linkdown_irq_regfield); return IRQ_HANDLED; } @@ -120,6 +135,36 @@ j721e_pcie_intd_writel(pcie, ENABLE_REG_SYS_2, reg); } +static irqreturn_t j721e_pcie_error_irq_handler(int irq, void *priv) +{ + struct j721e_pcie *pcie = priv; + struct device *dev = pcie->cdns_pcie->dev; + struct pci_dev *rdev = pcie->cdns_pcie->rdev; + u32 reg, pcie_errors; + + reg = j721e_pcie_intd_readl(pcie, STATUS_REG_SYS_2); + pcie_errors = reg & pcie->error_irq_regfield; + if (!pcie_errors) + return IRQ_NONE; + + dev_err(dev, "PCIE ERRORS (%d): 0x%x!\n", irq, pcie_errors); + + if (rdev) + pcie_do_recovery(rdev, pci_channel_io_frozen, j721e_reset_link); + + j721e_pcie_intd_writel(pcie, STATUS_CLR_REG_SYS_2, pcie->error_irq_regfield); + return IRQ_HANDLED; +} + +static void j721e_pcie_config_error_irq(struct j721e_pcie *pcie) +{ + u32 reg; + + reg = j721e_pcie_intd_readl(pcie, ENABLE_REG_SYS_2); + reg |= pcie->error_irq_regfield; + j721e_pcie_intd_writel(pcie, ENABLE_REG_SYS_2, reg); +} + static int j721e_pcie_start_link(struct cdns_pcie *cdns_pcie) { struct j721e_pcie *pcie = dev_get_drvdata(cdns_pcie->dev); @@ -312,6 +357,7 @@ static const struct j721e_pcie_data am64_pcie_rc_data = { .mode = PCI_MODE_RC, .linkdown_irq_regfield = J7200_LINK_DOWN, + .error_irq_regfield = J7200_ERR_CORR | J7200_ERR_NONFATAL | J7200_ERR_FATAL, .byte_access_allowed = true, }; @@ -394,6 +440,7 @@ cdns_pcie = &rc->pcie; cdns_pcie->dev = dev; cdns_pcie->ops = &j721e_pcie_ops; + cdns_pcie->rdev = NULL; pcie->cdns_pcie = cdns_pcie; break; case PCI_MODE_EP: @@ -409,6 +456,7 @@ cdns_pcie = &ep->pcie; cdns_pcie->dev = dev; cdns_pcie->ops = &j721e_pcie_ops; + cdns_pcie->rdev = NULL; pcie->cdns_pcie = cdns_pcie; break; default: @@ -418,6 +466,7 @@ pcie->mode = mode; pcie->linkdown_irq_regfield = data->linkdown_irq_regfield; + pcie->error_irq_regfield = data->error_irq_regfield; base = devm_platform_ioremap_resource_byname(pdev, "intd_cfg"); if (IS_ERR(base)) @@ -464,6 +513,22 @@ j721e_pcie_config_link_irq(pcie); + irq = platform_get_irq_byname(pdev, "pcie_error"); + if (irq >= 0 && pcie->error_irq_regfield) { + dev_info(dev, "configuring pcie error IRQ %d: %d\n", irq, pcie->error_irq_regfield); + + ret = devm_request_irq(dev, irq, j721e_pcie_error_irq_handler, IRQF_NO_THREAD, + "j721e-pcie-error-irq", pcie); + if (ret < 0) { + dev_err(dev, "failed to request pcie error IRQ %d\n", irq); + goto err_get_sync; + } + + j721e_pcie_config_error_irq(pcie); + } else { + dev_warn(dev, "no pcie error IRQ found: %d\n", irq); + } + switch (mode) { case PCI_MODE_RC: gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); @@ -513,6 +578,11 @@ goto err_pcie_setup; } + cdns_pcie->rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0)); + if (!cdns_pcie->rdev) { + dev_warn(dev, "no root complex device found\n"); + } + break; case PCI_MODE_EP: ret = cdns_pcie_init_phy(dev, cdns_pcie); @@ -548,6 +618,7 @@ clk_disable_unprepare(pcie->refclk); cdns_pcie_disable_phy(cdns_pcie); + pci_dev_put(cdns_pcie->rdev); pm_runtime_put(dev); pm_runtime_disable(dev); diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index fe7df39..adde3b9 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -306,6 +306,7 @@ * @reg_base: IO mapped register base * @mem_res: start/end offsets in the physical system memory to map PCI accesses * @dev: PCIe controller + * @rev: PCI device for root complex * @is_rc: tell whether the PCIe controller mode is Root Complex or Endpoint. * @phy_count: number of supported PHY devices * @phy: list of pointers to specific PHY control blocks @@ -317,6 +318,7 @@ void __iomem *reg_base; struct resource *mem_res; struct device *dev; + struct pci_dev *rdev; bool is_rc; int phy_count; struct phy **phy;