public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
From: Mahesh Vaidya <mahesh.vaidya@altera.com>
To: joyce.ooi@intel.com, lpieralisi@kernel.org,
	kwilczynski@kernel.org, mani@kernel.org, robh@kernel.org,
	bhelgaas@google.com, krzk+dt@kernel.org, conor+dt@kernel.org
Cc: linux-pci@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, subhransu.sekhar.prusty@altera.com,
	dinguyen@kernel.org, Mahesh Vaidya <mahesh.vaidya@altera.com>
Subject: [PATCH 3/3] PCI: altera: add Agilex 5 support
Date: Fri, 24 Apr 2026 02:49:13 -0700	[thread overview]
Message-ID: <20260424094913.522123-4-mahesh.vaidya@altera.com> (raw)
In-Reply-To: <20260424094913.522123-1-mahesh.vaidya@altera.com>

Add PCIe root port controller support for the Agilex 5 (V4) family
of SoC FPGAs.

The Agilex 5 PCIe Hard IP reuses the same config-space access path
as Agilex 7 (V3). Root port and endpoint configuration reads/writes
use direct MMIO to the HIP and CRA regions.

The difference is in the HIP port-level registers (IRQ status and IRQ
enable). On V3 these are directly mapped through the HIP MMIO window.
On V4 these registers are only reachable through an indirect access
mailbox (CFG REG IA CTRL) in the PCIe Subsystem AXI-Lite interface,
documented in the GTS AXI Streaming IP for PCIe User Guide.

This adds:
- ALTERA_PCIE_V4 version and platform data
- Indirect register read/write helpers using readl_poll_timeout_atomic
- Chained IRQ handler (aglx5_isr) for the V4 interrupt path
- OF match for "altr,pcie-root-port-4.0"

Co-developed-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
Co-developed-by: Peter Colberg <peter.colberg@intel.com>
Signed-off-by: Peter Colberg <peter.colberg@intel.com>
Signed-off-by: Mahesh Vaidya <mahesh.vaidya@altera.com>
---
 drivers/pci/controller/pcie-altera.c | 156 ++++++++++++++++++++++++++-
 1 file changed, 155 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c
index 025ba74d1ee2..db8149d84c96 100644
--- a/drivers/pci/controller/pcie-altera.c
+++ b/drivers/pci/controller/pcie-altera.c
@@ -12,6 +12,8 @@
 #include <linux/irqchip/chained_irq.h>
 #include <linux/irqdomain.h>
 #include <linux/init.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_pci.h>
@@ -93,16 +95,36 @@
 #define AGLX_CFG_TARGET_LOCAL_2000	2
 #define AGLX_CFG_TARGET_LOCAL_3000	3
 
+/* PCIe subsystem indirect register access */
+#define PCIE_SS_IA_CTL			0xc8 /* control register */
+#define PCIE_SS_IA_FN_NUM		0xcc /* function number */
+#define PCIE_SS_IA_FN_WRDATA		0xd0 /* write data */
+#define PCIE_SS_IA_FN_RDDATA		0xd4 /* read data */
+
+/* PCIE_SS_IA_CTL bitfields */
+#define PCIE_SS_IA_CTL_INITIATE		BIT(0)
+#define PCIE_SS_IA_CTL_WRITE		BIT(1)
+#define PCIE_SS_IA_CTL_BYTE_EN		GENMASK(5, 2)
+#define PCIE_SS_IA_CTL_ADDR		GENMASK(31, 6)
+
+/* PCIE_SS_IA_FN_NUM function types */
+#define PCIE_SS_IA_FN_TYPE_HIP		2
+
+#define AGLX5_INDIRECT_SLEEP_US		1
+#define AGLX5_INDIRECT_TIMEOUT_US	1000
+
 enum altera_pcie_version {
 	ALTERA_PCIE_V1 = 0,
 	ALTERA_PCIE_V2,
 	ALTERA_PCIE_V3,
+	ALTERA_PCIE_V4,
 };
 
 struct altera_pcie {
 	struct platform_device	*pdev;
 	void __iomem		*cra_base;
 	void __iomem		*hip_base;
+	void __iomem		*controller_base;
 	int			irq;
 	u8			root_bus_nr;
 	struct irq_domain	*irq_domain;
@@ -849,6 +871,98 @@ static void aglx_isr(struct irq_desc *desc)
 	chained_irq_exit(chip, desc);
 }
 
+/*
+ * Indirect register access to HIP registers via the PCIe Subsystem
+ * AXI-Lite mailbox, documented in the GTS AXI Streaming IP for PCIe
+ * User Guide. Called from chained IRQ handler (hardirq) and probe
+ * (before handler is installed), so no locking is required.
+ */
+static int aglx5_indirect_readl(const struct altera_pcie *pcie,
+				unsigned int addr, unsigned int *val)
+{
+	unsigned int ctl;
+	int ret;
+
+	writel(PCIE_SS_IA_FN_TYPE_HIP,
+	       pcie->controller_base + PCIE_SS_IA_FN_NUM);
+
+	ctl = FIELD_PREP(PCIE_SS_IA_CTL_ADDR, addr >> 2) |
+	      PCIE_SS_IA_CTL_BYTE_EN | PCIE_SS_IA_CTL_INITIATE;
+	writel(ctl, (pcie->controller_base + PCIE_SS_IA_CTL));
+
+	ret = readl_poll_timeout_atomic(pcie->controller_base + PCIE_SS_IA_CTL,
+					ctl, !(ctl & PCIE_SS_IA_CTL_INITIATE),
+					AGLX5_INDIRECT_SLEEP_US,
+					AGLX5_INDIRECT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	*val = readl(pcie->controller_base + PCIE_SS_IA_FN_RDDATA);
+
+	return 0;
+}
+
+static int aglx5_indirect_writel(const struct altera_pcie *pcie,
+				 unsigned int addr, unsigned int val)
+{
+	unsigned int ctl;
+	int ret;
+
+	writel(PCIE_SS_IA_FN_TYPE_HIP,
+	       pcie->controller_base + PCIE_SS_IA_FN_NUM);
+	writel(val, pcie->controller_base + PCIE_SS_IA_FN_WRDATA);
+
+	ctl = FIELD_PREP(PCIE_SS_IA_CTL_ADDR, addr >> 2) |
+	      PCIE_SS_IA_CTL_BYTE_EN | PCIE_SS_IA_CTL_WRITE |
+	      PCIE_SS_IA_CTL_INITIATE;
+	writel(ctl, pcie->controller_base + PCIE_SS_IA_CTL);
+
+	ret = readl_poll_timeout_atomic(pcie->controller_base + PCIE_SS_IA_CTL,
+					ctl, !(ctl & PCIE_SS_IA_CTL_INITIATE),
+					AGLX5_INDIRECT_SLEEP_US,
+					AGLX5_INDIRECT_TIMEOUT_US);
+
+	return ret;
+}
+
+static void aglx5_isr(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct altera_pcie *pcie;
+	struct device *dev;
+	u32 status = 0;
+	int ret;
+
+	chained_irq_enter(chip, desc);
+	pcie = irq_desc_get_handler_data(desc);
+	dev = &pcie->pdev->dev;
+
+	ret = aglx5_indirect_readl(pcie, pcie->pcie_data->port_irq_status_offset, &status);
+	if (ret) {
+		dev_err(dev, "timeout reading IRQ status, masking IRQ\n");
+		disable_irq_nosync(pcie->irq);
+		goto out;
+	}
+
+	if (status & CFG_AER) {
+		ret = generic_handle_domain_irq(pcie->irq_domain, 0);
+		if (ret)
+			dev_err_ratelimited(dev, "unexpected IRQ\n");
+
+		/* W1C: clear the handled bit */
+		ret = aglx5_indirect_writel(pcie,
+					    pcie->pcie_data->port_irq_status_offset,
+					    CFG_AER);
+		if (ret) {
+			dev_err(dev, "timeout clearing IRQ status, masking IRQ\n");
+			disable_irq_nosync(pcie->irq);
+		}
+	}
+
+out:
+	chained_irq_exit(chip, desc);
+}
+
 static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
 {
 	struct device *dev = &pcie->pdev->dev;
@@ -880,12 +994,19 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie)
 		return PTR_ERR(pcie->cra_base);
 
 	if (pcie->pcie_data->version == ALTERA_PCIE_V2 ||
-	    pcie->pcie_data->version == ALTERA_PCIE_V3) {
+	    pcie->pcie_data->version == ALTERA_PCIE_V3 ||
+	    pcie->pcie_data->version == ALTERA_PCIE_V4) {
 		pcie->hip_base = devm_platform_ioremap_resource_byname(pdev, "Hip");
 		if (IS_ERR(pcie->hip_base))
 			return PTR_ERR(pcie->hip_base);
 	}
 
+	if (pcie->pcie_data->version == ALTERA_PCIE_V4) {
+		pcie->controller_base = devm_platform_ioremap_resource_byname(pdev, "Txs");
+		if (IS_ERR(pcie->controller_base))
+			return PTR_ERR(pcie->controller_base);
+	}
+
 	/* setup IRQ */
 	pcie->irq = platform_get_irq(pdev, 0);
 	if (pcie->irq < 0)
@@ -924,6 +1045,15 @@ static const struct altera_pcie_ops altera_pcie_ops_3_0 = {
 	.rp_isr = aglx_isr,
 };
 
+static const struct altera_pcie_ops altera_pcie_ops_4_0 = {
+	.rp_read_cfg = aglx_rp_read_cfg,
+	.rp_write_cfg = aglx_rp_write_cfg,
+	.get_link_status = aglx_altera_pcie_link_up,
+	.ep_read_cfg = aglx_ep_read_cfg,
+	.ep_write_cfg = aglx_ep_write_cfg,
+	.rp_isr = aglx5_isr,
+};
+
 static const struct altera_pcie_data altera_pcie_1_0_data = {
 	.ops = &altera_pcie_ops_1_0,
 	.cap_offset = 0x80,
@@ -971,6 +1101,20 @@ static const struct altera_pcie_data altera_pcie_3_0_r_tile_data = {
 	.port_irq_enable_offset = 0x4,
 };
 
+static const struct altera_pcie_data altera_pcie_4_0_data = {
+	.ops = &altera_pcie_ops_4_0,
+	.version = ALTERA_PCIE_V4,
+	.cap_offset = 0x70,
+	.port_conf_offset = 0x14000,
+	/*
+	 * Unlike V3 where IRQ offsets are relative to port_conf_offset,
+	 * V4 IRQ offsets are absolute addresses in the HIP indirect access
+	 * space documented in the GTS AXI Streaming IP for PCIe User Guide.
+	 */
+	.port_irq_status_offset = 0x1414c,
+	.port_irq_enable_offset = 0x14150,
+};
+
 static const struct of_device_id altera_pcie_of_match[] = {
 	{.compatible = "altr,pcie-root-port-1.0",
 	 .data = &altera_pcie_1_0_data },
@@ -982,6 +1126,8 @@ static const struct of_device_id altera_pcie_of_match[] = {
 	 .data = &altera_pcie_3_0_p_tile_data },
 	{.compatible = "altr,pcie-root-port-3.0-r-tile",
 	 .data = &altera_pcie_3_0_r_tile_data },
+	{.compatible = "altr,pcie-root-port-4.0",
+	 .data = &altera_pcie_4_0_data },
 	{},
 };
 
@@ -1035,6 +1181,14 @@ static int altera_pcie_probe(struct platform_device *pdev)
 		writel(CFG_AER,
 		       pcie->hip_base + pcie->pcie_data->port_conf_offset +
 		       pcie->pcie_data->port_irq_enable_offset);
+	} else if (pcie->pcie_data->version == ALTERA_PCIE_V4) {
+		ret = aglx5_indirect_writel(pcie,
+					    pcie->pcie_data->port_irq_enable_offset,
+					    CFG_AER);
+		if (ret) {
+			dev_err(dev, "Failed to enable AER IRQ\n");
+			goto err_teardown_irq;
+		}
 	}
 
 	bridge->sysdata = pcie;
-- 
2.34.1


  parent reply	other threads:[~2026-04-24  9:49 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-24  9:49 [PATCH 0/3] PCI: altera: Add Agilex 5 PCIe Root Port support Mahesh Vaidya
2026-04-24  9:49 ` [PATCH 1/3] dt-bindings: PCI: altera: add binding for Agilex 5 Mahesh Vaidya
2026-04-25 10:22   ` Krzysztof Kozlowski
2026-04-27 12:43     ` Mahesh Vaidya
2026-04-24  9:49 ` [PATCH 2/3] PCI: altera: fix resource leaks on probe failure Mahesh Vaidya
2026-04-24 12:42   ` Dinh Nguyen
2026-04-27 12:36     ` Mahesh Vaidya
2026-04-24  9:49 ` Mahesh Vaidya [this message]
2026-04-24 15:48   ` [PATCH 3/3] PCI: altera: add Agilex 5 support Bjorn Helgaas
2026-04-27 12:49     ` Mahesh Vaidya

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260424094913.522123-4-mahesh.vaidya@altera.com \
    --to=mahesh.vaidya@altera.com \
    --cc=bhelgaas@google.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dinguyen@kernel.org \
    --cc=joyce.ooi@intel.com \
    --cc=krzk+dt@kernel.org \
    --cc=kwilczynski@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lpieralisi@kernel.org \
    --cc=mani@kernel.org \
    --cc=robh@kernel.org \
    --cc=subhransu.sekhar.prusty@altera.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox