Because of the Thanksgiving holiday in the U.S., TI E2E™ design support forum responses may be delayed from November 25 through December 2. Thank you for your patience.

This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM6422: Linux: pcie: AER does not work

Part Number: AM6422

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;

  • Hi Christian,

    CONFIG_PCIEAER is not enabled in Processor SDK kernel defconfig, so as you mentioned, AER is not supported on AM6422 kernel.

    I will ask our kernel dev team to comment on the guidelines of enabling AER, and get back to you. Please expect the response could either be a guideline or just simply no comments because this feature has not been looked it.

  • The kernel config is one thing .. but even when I enable CONFIG_PCIEAER (what I have done) the AER stuff is not working. That is why I looked into the TRM and came up with the patch.

    Is it needed to enable the error interrupts in STATUS_REG_SYS_2 register?