Devicetree
 help / color / mirror / Atom feed
From: Koichiro Den <den@valinux.co.jp>
To: "Marek Vasut" <marek.vasut+renesas@mailbox.org>,
	"Yoshihiro Shimoda" <yoshihiro.shimoda.uh@renesas.com>,
	"Lorenzo Pieralisi" <lpieralisi@kernel.org>,
	"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
	"Manivannan Sadhasivam" <mani@kernel.org>,
	"Rob Herring" <robh@kernel.org>,
	"Bjorn Helgaas" <bhelgaas@google.com>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Geert Uytterhoeven" <geert+renesas@glider.be>,
	"Magnus Damm" <magnus.damm@gmail.com>,
	"Jingoo Han" <jingoohan1@gmail.com>
Cc: Philipp Zabel <p.zabel@pengutronix.de>,
	Frank Li <Frank.Li@nxp.com>, Niklas Cassel <cassel@kernel.org>,
	Wilfred Mallawa <wilfred.mallawa@wdc.com>,
	Serge Semin <fancer.lancer@gmail.com>,
	linux-pci@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2 11/15] PCI: rcar-gen4: Recover the Root Port on link down
Date: Tue, 29 Sep 2026 01:52:26 +0900	[thread overview]
Message-ID: <20260928165230.3397664-12-den@valinux.co.jp> (raw)
In-Reply-To: <20260928165230.3397664-1-den@valinux.co.jp>

On R-Car, the controller's reset requests (smlh_req_rst_not,
link_req_rst_not) arrive on intreq_pcim_sub, the interrupt the MSIs are
demultiplexed from, so DBI is read as soon as the link goes down. On
R-Car S4 that is a hazard: DBI accesses issued within a few hundred
microseconds of an unexpected link down do not complete and hang the
host. In testing, the first Root Port config read after powering off
the link partner hung unless delayed by ~300 us.

Check the APP reset status in the interrupt handler before DBI is
touched. When a reset request is latched, flag the controller as
needing reinitialization, which masks the sources, ack the request and
schedule recovery work. The work calls pci_host_handle_link_down() on
the Root Port, which runs the AER-style recovery and resets the
controller through reset_root_port(); it holds a reference on the port,
which can be removed through sysfs meanwhile. The check also reports a
recovery already pending, whether started by another interrupt or left
over from a failed reset, so the handler issues no further DBI accesses
until a reset succeeds.

Arm the detection once enumeration is done, as the recovery needs the
Root Port to exist. Only unmasked status bits are handled and pending
latches are cleared when the sources are re-enabled, so requests
recorded during probe or the reset itself do not trigger another
recovery. Teardown only disarms the detection: MSI delivery has to keep
working while devices are removed.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Check the reset status from the driver's own handler now that it
    owns the interrupt, instead of the pre_msi_irq host op. The separate
    request_irq() path for external MSI controllers is gone. (Marek)
  - Use bool flags instead of state bits. (Marek)
  - Hold a reference on the Root Port in the recovery work.

 drivers/pci/controller/dwc/pcie-rcar-gen4.c | 107 ++++++++++++++++++++
 1 file changed, 107 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 256410fb4b1c..aa80232afaba 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -26,6 +26,7 @@
 #include <linux/reset.h>
 
 #include "../../pci.h"
+#include "../pci-host-common.h"
 #include "pcie-designware.h"
 
 /* Renesas-specific */
@@ -41,9 +42,18 @@
 #define MSICAP0_MMESCAP_MASK	GENMASK(19, 17)
 #define MSICAP0_MSIE		BIT(16)
 
+/* PCIe Reset Status */
+#define PCIERSTSTS		0x0020
+#define SMLH_REQ_RST_NOT	BIT(2)
+#define LINK_REQ_RST_NOT	BIT(1)
+#define LINK_DOWN_RESET_MASK	(SMLH_REQ_RST_NOT | LINK_REQ_RST_NOT)
+
 /* PCIe Interrupt Status 0 */
 #define PCIEINTSTS0		0x0084
 
+/* PCIe Reset Status Enable */
+#define PCIERSTSTSEN		0x0300
+
 /* PCIe Interrupt Status 0 Enable */
 #define PCIEINTSTS0EN		0x0310
 #define MSI_CTRL_INT		BIT(26)
@@ -54,6 +64,9 @@
 #define PCIEDMAINTSTSEN		0x0314
 #define PCIEDMAINTSTSEN_INIT	GENMASK(15, 0)
 
+/* PCIe Reset Status Clear */
+#define PCIERSTSTSCLR		0x0330
+
 /* PCIe Interrupt Status 0 Clear */
 #define PCIEINTSTS0CLR		0x0340
 
@@ -118,6 +131,9 @@ struct rcar_gen4_pcie {
 	const struct rcar_gen4_pcie_drvdata *drvdata;
 	/* intreq_pcim_sub ("msi"): iMSI-RX and other controller notifications */
 	int msi_irq;
+	struct work_struct link_down_work;
+	/* Allow Root Port event handling after enumeration and until teardown. */
+	bool rp_events_armed;
 	/*
 	 * The controller needs reinitializing; the APP interrupt sources
 	 * stay masked until a reset succeeds.
@@ -559,15 +575,35 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
 static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
 {
 	struct dw_pcie_rp *pp = &rcar->dw.pp;
+	bool rp_events = rcar->rp_events_armed && !rcar->reinit_pending;
 	u32 val;
 
 	lockdep_assert_held(&rcar->app_lock);
 
 	val = readl(rcar->base + PCIEINTSTS0EN);
 	val &= ~MSI_CTRL_INT;
+	/*
+	 * Note that rcar->rp_events_armed is not checked here because device
+	 * removal may still need iMSI-RX interrupts after Root Port events have
+	 * been disarmed.
+	 */
 	if (!rcar->reinit_pending && pp->use_imsi_rx && pci_msi_enabled())
 		val |= MSI_CTRL_INT;
 	writel(val, rcar->base + PCIEINTSTS0EN);
+
+	val = readl(rcar->base + PCIERSTSTSEN);
+	if (rp_events) {
+		/*
+		 * Clear latches recorded while the sources were masked, so
+		 * stale requests do not fire as soon as they are re-enabled.
+		 */
+		if (!(val & LINK_DOWN_RESET_MASK))
+			writel(LINK_DOWN_RESET_MASK, rcar->base + PCIERSTSTSCLR);
+		val |= LINK_DOWN_RESET_MASK;
+	} else {
+		val &= ~LINK_DOWN_RESET_MASK;
+	}
+	writel(val, rcar->base + PCIERSTSTSEN);
 }
 
 static void rcar_gen4_pcie_app_irq_sync(struct rcar_gen4_pcie *rcar)
@@ -630,6 +666,44 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
 	return ret;
 }
 
+/*
+ * Consume a latched reset request and start recovery. Returns true if DBI
+ * must be left alone: a request was just consumed or recovery is pending.
+ */
+static bool rcar_gen4_pcie_handle_link_down(struct rcar_gen4_pcie *rcar)
+{
+	u32 status;
+
+	guard(raw_spinlock_irqsave)(&rcar->app_lock);
+
+	status = readl(rcar->base + PCIERSTSTS) &
+		 readl(rcar->base + PCIERSTSTSEN) & LINK_DOWN_RESET_MASK;
+	if (status) {
+		/* The sources are only enabled while armed with no reinit pending. */
+		rcar->reinit_pending = true;
+		rcar_gen4_pcie_app_irq_sync_locked(rcar);
+		writel(status, rcar->base + PCIERSTSTSCLR);
+		schedule_work(&rcar->link_down_work);
+	}
+
+	return rcar->reinit_pending;
+}
+
+static void rcar_gen4_pcie_link_down_work(struct work_struct *work)
+{
+	struct rcar_gen4_pcie *rcar =
+		container_of(work, struct rcar_gen4_pcie, link_down_work);
+	struct pci_dev *port;
+
+	/* Hold a reference: the Root Port can be removed through sysfs. */
+	port = pci_get_slot(rcar->dw.pp.bridge->bus, PCI_DEVFN(0, 0));
+	if (!port)
+		return;
+
+	pci_host_handle_link_down(port);
+	pci_dev_put(port);
+}
+
 /*
  * intreq_pcim_sub carries the iMSI-RX interrupt along with other controller
  * notifications, so the driver owns it instead of the DesignWare core (see
@@ -640,6 +714,10 @@ static irqreturn_t rcar_gen4_pcie_msi_irq_handler(int irq, void *data)
 	struct rcar_gen4_pcie *rcar = data;
 	u32 status;
 
+	/* Check reset requests before MSI handling, which accesses DBI. */
+	if (rcar_gen4_pcie_handle_link_down(rcar))
+		return IRQ_HANDLED;
+
 	status = readl(rcar->base + PCIEINTSTS0) &
 		 readl(rcar->base + PCIEINTSTS0EN);
 	if (!(status & MSI_CTRL_INT))
@@ -676,6 +754,31 @@ static int rcar_gen4_pcie_msi_irq_init(struct rcar_gen4_pcie *rcar)
 	return 0;
 }
 
+/*
+ * Arm Root Port events once enumeration is done, as recovery needs the
+ * Root Port to exist.
+ */
+static void rcar_gen4_pcie_rp_events_arm(struct dw_pcie_rp *pp)
+{
+	struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
+	struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+
+	guard(raw_spinlock_irqsave)(&rcar->app_lock);
+	rcar->rp_events_armed = true;
+	rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
+static void rcar_gen4_pcie_rp_events_disarm(struct rcar_gen4_pcie *rcar)
+{
+	scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+		rcar->rp_events_armed = false;
+		rcar_gen4_pcie_app_irq_sync_locked(rcar);
+	}
+
+	/* Handlers queue the work under app_lock, so none can follow this. */
+	cancel_work_sync(&rcar->link_down_work);
+}
+
 static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
 					struct pci_dev *dev)
 {
@@ -888,6 +991,7 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
 static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = {
 	.init = rcar_gen4_pcie_host_init,
 	.deinit = rcar_gen4_pcie_host_deinit,
+	.post_init = rcar_gen4_pcie_rp_events_arm,
 };
 
 static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
@@ -903,6 +1007,7 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
 		return ret;
 
 	raw_spin_lock_init(&rcar->app_lock);
+	INIT_WORK(&rcar->link_down_work, rcar_gen4_pcie_link_down_work);
 
 	pp->num_vectors = MAX_MSI_IRQS;
 	/*
@@ -918,6 +1023,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
 
 static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
 {
+	/* Cancel recovery work before removing the PCI bus */
+	rcar_gen4_pcie_rp_events_disarm(rcar);
 	dw_pcie_host_deinit(&rcar->dw.pp);
 }
 
-- 
2.51.0


  parent reply	other threads:[~2026-09-28 16:53 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-09-28 16:52 ` [PATCH v2 01/15] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
2026-09-28 16:56   ` sashiko-bot
2026-09-28 16:52 ` [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
2026-09-28 16:57   ` sashiko-bot
2026-10-03 18:29   ` Marek Vasut
2026-10-03 18:51     ` Marek Vasut
2026-10-05  4:22       ` Koichiro Den
2026-10-05  5:59         ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 03/15] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
2026-09-28 16:58   ` sashiko-bot
2026-09-30 10:31   ` Krzysztof Kozlowski
2026-10-03 20:00   ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 04/15] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
2026-09-28 16:58   ` sashiko-bot
2026-10-03 20:02   ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 05/15] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization Koichiro Den
2026-09-28 16:57   ` sashiko-bot
2026-10-03 20:23   ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 06/15] PCI: rcar-gen4: Assert resets when Gen5 PHY initialization fails Koichiro Den
2026-09-28 16:59   ` sashiko-bot
2026-10-03 21:34   ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 07/15] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
2026-09-28 16:56   ` sashiko-bot
2026-10-03 23:41   ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 08/15] PCI: rcar-gen4: Add a controller reinitialization callback Koichiro Den
2026-09-28 17:02   ` sashiko-bot
2026-10-03 23:54   ` Marek Vasut
2026-10-05  4:31     ` Koichiro Den
2026-09-28 16:52 ` [PATCH v2 09/15] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
2026-09-28 17:03   ` sashiko-bot
2026-10-04  0:11   ` Marek Vasut
2026-10-05  6:14     ` Koichiro Den
2026-09-28 16:52 ` [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
2026-09-28 17:04   ` sashiko-bot
2026-09-29 17:43   ` Geert Uytterhoeven
2026-09-30  6:38     ` Koichiro Den
2026-10-04  1:17       ` Marek Vasut
2026-10-04  0:53   ` Marek Vasut
2026-10-05 17:05     ` Koichiro Den
2026-09-28 16:52 ` Koichiro Den [this message]
2026-09-28 17:07   ` [PATCH v2 11/15] PCI: rcar-gen4: Recover the Root Port on link down sashiko-bot
2026-10-04  1:38   ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 12/15] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
2026-09-28 16:59   ` sashiko-bot
2026-09-28 16:52 ` [PATCH v2 13/15] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
2026-09-28 17:06   ` sashiko-bot
2026-10-04  2:52   ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 14/15] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
2026-09-28 17:08   ` sashiko-bot
2026-10-04  4:40   ` Marek Vasut
2026-10-05 17:09     ` Koichiro Den
2026-09-28 16:52 ` [PATCH v2 15/15] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
2026-09-28 16:59   ` sashiko-bot
2026-10-03 20:00   ` Marek Vasut
2026-10-05 17:11     ` Koichiro Den
2026-10-06  5:20       ` Marek Vasut
2026-10-03 17:46 ` [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Marek Vasut

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=20260928165230.3397664-12-den@valinux.co.jp \
    --to=den@valinux.co.jp \
    --cc=Frank.Li@nxp.com \
    --cc=bhelgaas@google.com \
    --cc=cassel@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=fancer.lancer@gmail.com \
    --cc=geert+renesas@glider.be \
    --cc=jingoohan1@gmail.com \
    --cc=krzk+dt@kernel.org \
    --cc=kwilczynski@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    --cc=lpieralisi@kernel.org \
    --cc=magnus.damm@gmail.com \
    --cc=mani@kernel.org \
    --cc=marek.vasut+renesas@mailbox.org \
    --cc=p.zabel@pengutronix.de \
    --cc=robh@kernel.org \
    --cc=wilfred.mallawa@wdc.com \
    --cc=yoshihiro.shimoda.uh@renesas.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