mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts
@ 2026-09-28 16:52 Koichiro Den
  2026-09-28 16:52 ` [PATCH v2 01/15] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
                   ` (15 more replies)
  0 siblings, 16 replies; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

Hi,

This series improves error handling on the R-Car Gen4 PCIe host
controller (tested on R-Car S4 Spider, r8a779f0). It fixes unexpected
link-down handling so the host doesn't hang, and wires up missing Root
Port interrupts (AER, PME, bandwidth notifications) so port services
actually work.

A few hardware quirks made this tricky:

1. PCIEINTSTS0 link-up bits don't track the actual link state. The
   driver never noticed when the link dropped and kept trying config
   accesses on the dead link. Patch 2 combines the APP link-up event
   check from Figure 104.5 with the PORT_DEBUG1 live link check. Startup
   clears the APP latches before enabling LTSSM, and the DWC core polls
   the combined condition on the RC side.

2. On S4, a DBI access immediately after an unexpected link down can
   hang the host. Commit 0056d29f8c1b ("PCI: rcar-gen4: Assure reset
   occurs before DBI access") describes an SError on V4H after reset
   deassertion, but whether the S4 hang shares the same underlying cause
   has not been verified.

   Adding a delay before the DBI access avoided the hang in my tests,
   but that alone would not provide link-down recovery. Also, the reset
   request shares intreq_pcim_sub with iMSI-RX, while AER arrives on
   another IRQ. Delaying AER dispatch alone would therefore leave the
   MSI handler exposed, and both paths would still need coordination
   with the controller reset.

   The driver handles the reset request itself and schedules
   pci_host_handle_link_down(), as the rockchip and qcom drivers do.
   This also provides recovery with older DTs that have no "aer"
   interrupt, without relying on AER to initiate it. The reset callback
   shares the reset sequence used at probe, including the reset-status
   readback and delay added by 0056d29f8c1b.

3. Root Port interrupts only trigger APP status bits on platform IRQs.
   The controller appears to lack SII2MSI, so Root Port MSIs never reach
   the GIC ITS. The Root Port's INTx is routed to intreq_pcim_sub as
   well, which the driver holds, so port services can't request it
   either. This series works around it by hiding Root Port MSI caps
   across the board and emulating INTx using a virtual IRQ domain, fed
   by the "aer" IRQ and intreq_pcim_sub.

Patch 1 is only loosely related: it adds Renesas to the RAS DES VSEC
list so the DWC debugfs error injection works on R-Car. I used it to
test the Root Port AER path (see below) and included it here for that
reason. Happy to send it separately if preferred.

Based on next-20260925. The driver patches build on 7fc9907223d6 ("PCI:
rcar-gen4: Add Application/Local register reset control") in pci/next,
and the DTS patch is for renesas-devel, which already has b43aa6a6ebe8
("arm64: dts: renesas: r8a779f0: Add GICv3 ITS and update PCIe nodes");
configurations b and c below use that DT.

Note: backward compatibility with older DTs is kept. Without the "aer"
      interrupt, only Root Port AER remains unavailable. See the Testing
      section below.

Retesting with v2
-----------------

Setup: R-Car S4 Spider (RC) linked to another S4 Spider running the
       pci-epf-test endpoint. pci_endpoint_test is bound on the RC side.

  1. Link down / recovery. On the EP side, toggle the endpoint controller
     off and on. The short pause keeps the endpoint away long enough for
     the RC to notice, but brings it back within the reset window so
     recovery can succeed. Adopted the test approach from [1]:

     # cd /sys/kernel/config/pci_ep
     # echo 0 > controllers/e65d0000.pcie-ep/start
     # sleep 0.1
     # echo 1 > controllers/e65d0000.pcie-ep/start

     Expected on the RC dmesg:
       pcieport 0000:00:00.0: Recovering Root Port due to Link Down
       pcieport 0000:00:00.0: Root Port has been reset
       pcieport 0000:00:00.0: AER: device recovery successful

     and the "msi" (intreq_pcim_sub) interrupt count going up in
     /proc/interrupts. Without this series nothing shows up here: the
     link comes back on its own once the endpoint returns, but the RC
     never notices the outage and the endpoint is left unconfigured
     (see 4). Config accesses issued while the link is down hang the
     host.

     [1] https://lore.kernel.org/r/abFMa6DCGGLUHddA@fedora/

  2. Bandwidth notification. On the RC, retrain the link:

     # setpci -s 00:00.0 CAP_EXP+0x10.w=0x0c23

     Expected:
     - the virtual Root Port IRQ (rcar-gen4-rp in /proc/interrupts,
       shared by PCIe PME, aerdrv and PCIe bwctrl) fires once
     - bwctrl clears LnkSta.LBMS
       (setpci -s 00:00.0 CAP_EXP+0x12.w reads 0x2024 again).

     Before the series LnkSta read 0xe024 afterwards, LBMS and LABS
     stuck.

  3. Root Port AER. On the RC, inject an LCRC error with the DWC debugfs
     (patch 1) and issue one config read so a TLP actually goes out:

     # cd /sys/kernel/debug/dwc_pcie_e65d0000.pcie/rasdes_err_inj
     # echo 1 > rx_lcrc            # error detected by the Root Port
     # setpci -s 01:00.0 VENDOR_ID.w
     # echo 1 > tx_lcrc            # error detected by the endpoint,
     # setpci -s 01:00.0 VENDOR_ID.w    # reported back with ERR_COR

     Expected on the RC dmesg, respectively:
      pcieport 0000:00:00.0: PCIe Bus Error: severity=Correctable
      pcieport 0000:00:00.0:   [ 6] BadTLP | Receiver | Data Link Layer

      pcieport 0000:00:00.0: AER: Correctable Error message received from 0000:01:00.0
      pci-endpoint-test 0000:01:00.0: PCIe Bus Error: severity=Correctable
      pci-endpoint-test 0000:01:00.0:   [ 6] BadTLP | Receiver | Data Link Layer

     plus the virtual Root Port IRQ count and aer_rootport_total_err_cor
     going up by one each time. The link stays up throughout, the DLL
     retry recovers the TLP. Before the series nothing is reported.

  4. Regression check. Run pci_endpoint_test after step 1. PASS/FAIL/SKIP
     counts match a run without step 1.

Configurations:

  a. Without this series**
  b. GIC ITS, DT with the new "aer" interrupt (this series)
  c. GIC ITS, DT without "aer" (b43aa6a6ebe8 ("arm64: dts: renesas:
     r8a779f0: Add GICv3 ITS and update PCIe nodes") or later)
  d. iMSI-RX, DT before b43aa6a6ebe8 (no msi-parent, no "aer")

Result:
             recovery   bwctrl/PME   RP AER    pcitest
  ---------------------------------------------------------
  a.           none         no         no      all FAIL
  b.            ok          ok         ok      no change
  c.            ok          ok         n/a*    no change
  d.            ok          ok         n/a*    no change

  *  Root Port AER needs the "aer" interrupt; without it the behaviour is
     unchanged from before the series.
  ** Only patch 1 applied on top of the base, so the same debugfs error
     injection could be used for the comparison. pci_endpoint_test fails
     across the board there because nothing restores the endpoint after
     the toggle.

Best regards,
Koichiro
---
Changes in v2:
  - Rebased onto next-20260925, including Marek's R-Car X5H support.
  - Keep .link_up() and require both the APP link-up events and the
    PORT_DEBUG1 live link check. Clear the APP latches before enabling
    LTSSM and reuse the DWC core's polling on the RC side. (Marek)
  - Let the R-Car driver own intreq_pcim_sub in all configurations (new
    patch 10) and check the reset request from its own handler instead
    of hooking into the DWC chained handler. Replace the pre_msi_irq host
    op from v1 with an export of dw_handle_msi_irq() (patch 4). (Marek)
  - Keep the reset_control_status() check before asserting the power
    reset. (Marek)
  - Separate controller reinitialization from .init()/.deinit() and split
    out preparatory changes.
  - Use bool flags instead of a state bitmask. (Marek)
  - Keep Root Port AER notifications masked until .post_init, after
    enumeration. In v1, they could be enabled during port-service probing.
  - Keep APP interrupt sources masked if the Root Port reset callback
    fails, regardless of the reset trigger.
  - Hold a reference on the Root Port in the link-down recovery work.
  - Clear only the MSI-form Root Port latches through PCIEINTSTS0CLR;
    the INTx bits are reserved there.
  - Serialize dispatches to the virtual Root Port IRQ from its two
    parent interrupts.
  - Collected Marek's Reviewed-by on patches 1, 3 and 15.

v1: https://lore.kernel.org/r/20260918032038.2216471-1-den@valinux.co.jp/


Koichiro Den (15):
  PCI: dwc: Add Renesas to the RAS DES VSEC list
  PCI: rcar-gen4: Check live link status in link_up()
  dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
  PCI: dwc: Export dw_handle_msi_irq()
  PCI: rcar-gen4: Move deinitialization helpers before SoC
    initialization
  PCI: rcar-gen4: Assert resets when Gen5 PHY initialization fails
  PCI: rcar-gen4: Separate hardware setup from resource acquisition
  PCI: rcar-gen4: Add a controller reinitialization callback
  PCI: rcar-gen4: Add Root Port reset support
  PCI: rcar-gen4: Take over the iMSI-RX interrupt
  PCI: rcar-gen4: Recover the Root Port on link down
  PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
  PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ
  PCI: rcar-gen4: Route Root Port PME and bandwidth notifications
  arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts

 .../bindings/pci/rcar-gen4-pci-host.yaml      |  10 +-
 arch/arm64/boot/dts/renesas/r8a779f0.dtsi     |  10 +-
 .../pci/controller/dwc/pcie-designware-host.c |   7 +-
 drivers/pci/controller/dwc/pcie-designware.h  |   1 +
 drivers/pci/controller/dwc/pcie-rcar-gen4.c   | 711 ++++++++++++++++--
 include/linux/pcie-dwc.h                      |   2 +
 6 files changed, 670 insertions(+), 71 deletions(-)

base-commit: f5f84daefcd92d7a630066635ecea1433ed5eac7
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 01/15] PCI: dwc: Add Renesas to the RAS DES VSEC list
  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 ` Koichiro Den
  2026-09-28 16:52 ` [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

The Root Port on R-Car Gen4 (DWC 5.20a) carries the RAS DES capability
with the stock DesignWare VSEC ID (0x02, rev 4), but vendor ID 0x1912 is
not in dwc_pcie_rasdes_vsec_ids[]. Add Renesas to the list.

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Add Marek's Reviewed-by tag. No code changes.

 include/linux/pcie-dwc.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h
index 097e806c714c..e5693895dbd5 100644
--- a/include/linux/pcie-dwc.h
+++ b/include/linux/pcie-dwc.h
@@ -38,6 +38,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
 	  .vsec_id = 0x02, .vsec_rev = 0x4 },
 	{ .vendor_id = PCI_VENDOR_ID_LECARC,
 	  .vsec_id = 0x02, .vsec_rev = 0x4 },
+	{ .vendor_id = PCI_VENDOR_ID_RENESAS,
+	  .vsec_id = 0x02, .vsec_rev = 0x4 },
 	{}
 };
 
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up()
  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:52 ` Koichiro Den
  2026-10-03 18:29   ` Marek Vasut
  2026-09-28 16:52 ` [PATCH v2 03/15] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

rcar_gen4_pcie_link_up() checks link state using SMLH_LINK_UP and
RDLH_LINK_UP in PCIEINTSTS0. However, these bits do not reflect the live
link state. On an R-Car S4, after taking down the endpoint, a link-down
interrupt saw PCIEINTSTS0 = 0x20a000c5 with both bits still set. Even
after resetting the controller with the LTSSM back in Polling, they read
0xa000c5, still set.

As a result, dw_pcie_link_up() keeps reporting the link as up after it
has gone down. That defeats the check in dw_pcie_other_conf_map_bus(),
which is supposed to stop config accesses to downstream devices while
the link is down, so such accesses go out on the dead link and stall the
host. It also makes the callback useless for the link-down recovery
added later, which has to wait for the link to actually come back after
resetting the controller.

Keep the APP link-up event check from Figure 104.5 of the R-Car S4
reference manual, but also require PORT_DEBUG1 to report the link up and
not in training. The callback then rejects a downed link even if the APP
link-up events remain latched.

Clear the APP latches before enabling LTSSM to discard events from a
previous start, and only read them in .link_up(). RC startup uses
dw_pcie_wait_for_link() to poll the combined condition.

Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode")
Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Keep the APP link-up event check in .link_up() and combine it with
    the PORT_DEBUG1 check, reusing the DWC core's polling. (Marek)
  - Clear the APP link-up latches before enabling LTSSM and leave them
    latched across .link_up() calls. (Marek)

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

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 21c3dd9b8891..4dabcda051a5 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -53,6 +53,9 @@
 #define PCIEDMAINTSTSEN		0x0314
 #define PCIEDMAINTSTSEN_INIT	GENMASK(15, 0)
 
+/* PCIe Interrupt Status 0 Clear */
+#define PCIEINTSTS0CLR		0x0340
+
 /* Port Logic Registers 89 */
 #define PRTLGC89		0x0b70
 
@@ -120,8 +123,13 @@ static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
 
 	val = readl(rcar->base + PCIEINTSTS0);
 	mask = RDLH_LINK_UP | SMLH_LINK_UP;
+	if ((val & mask) != mask)
+		return false;
 
-	return (val & mask) == mask;
+	/* The APP link-up events remain latched after the link goes down. */
+	val = dw_pcie_readl_dbi(dw, PCIE_PORT_DEBUG1);
+	return (val & PCIE_PORT_DEBUG1_LINK_UP) &&
+	       !(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING);
 }
 
 /*
@@ -208,6 +216,9 @@ static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
 	struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
 	int ret;
 
+	/* Clear link-up events from the previous start. */
+	writel(RDLH_LINK_UP | SMLH_LINK_UP, rcar->base + PCIEINTSTS0CLR);
+
 	if (rcar->drvdata->ltssm_control) {
 		ret = rcar->drvdata->ltssm_control(rcar, true);
 		if (ret)
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 03/15] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
  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:52 ` [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  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
                   ` (12 subsequent siblings)
  15 siblings, 2 replies; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

The intreq_pcim_lperr interrupt carries Root Port AER notifications,
separately from the integrated MSI receiver interrupt. Describe it with
the "aer" name so the controller driver can use it for Root Port error
reporting.

Keep the fifth interrupt optional so existing device trees remain
valid. Extend the example accordingly.

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Add Marek's Reviewed-by tag. No code changes.

 .../devicetree/bindings/pci/rcar-gen4-pci-host.yaml    | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
index 30e244fda56b..aa5876f32121 100644
--- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
@@ -26,14 +26,17 @@ properties:
           - const: renesas,rcar-gen5-pcie4 # R-Car Gen5 PCIe4
 
   interrupts:
-    maxItems: 4
+    minItems: 4
+    maxItems: 5
 
   interrupt-names:
+    minItems: 4
     items:
       - const: msi
       - const: dma
       - const: sft_ce
       - const: app
+      - const: aer
 
   clocks:
     maxItems: 2
@@ -140,8 +143,9 @@ examples:
             interrupts = <GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
                          <GIC_SPI 417 IRQ_TYPE_LEVEL_HIGH>,
                          <GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>,
-                         <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>;
-            interrupt-names = "msi", "dma", "sft_ce", "app";
+                         <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 421 IRQ_TYPE_LEVEL_HIGH>;
+            interrupt-names = "msi", "dma", "sft_ce", "app", "aer";
             clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
             clock-names = "core", "ref";
             power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 04/15] PCI: dwc: Export dw_handle_msi_irq()
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (2 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 03/15] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  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
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

Not every platform brings the iMSI-RX interrupt out as a dedicated
line. On spear13xx and keembay it is a status bit in the SoC wrapper's
interrupt register that the glue driver has to check and clear, and on
dra7xx it shares the line with other sources the glue driver has to
demultiplex. These drivers set msi_irq[0] to -ENODEV so the core does
not install its chained handler, and service the MSIs from a handler of
their own. spear13xx and keembay call dw_handle_msi_irq() for that.
Both are built-in, so the function was never exported. dra7xx can be a
module and has an MSI loop of its own.

Export the function so that modular glue drivers can use it as well.
The R-Car Gen4 driver, where both situations apply, is the first user.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - New patch. Replaces the pre_msi_irq host op of v1, which is no
    longer needed now that the R-Car driver requests the multiplexed
    interrupt itself in all configurations. (Marek)

 drivers/pci/controller/dwc/pcie-designware-host.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index f5a38e6fd8d7..31e15666479b 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -85,6 +85,7 @@ void dw_handle_msi_irq(struct dw_pcie_rp *pp)
 			generic_handle_demux_domain_irq(pp->irq_domain, irq_off + pos);
 	}
 }
+EXPORT_SYMBOL_GPL(dw_handle_msi_irq);
 
 /* Chained MSI interrupt service routine */
 static void dw_chained_msi_isr(struct irq_desc *desc)
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 05/15] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (3 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 04/15] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  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
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

Move common_deinit() next to common_init(), ahead of the Gen5 .init()
that will call it when PHY initialization fails. Place the Gen5 .deinit()
before the Gen5 initialization code as well.

Keep these moves separate from the following initialization refactoring
to make that patch easier to review. The function bodies are unchanged.

No functional changes.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - New patch, separating the deinitialization helper moves from the
    hardware initialization refactoring, for easier review.

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

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 4dabcda051a5..f9241ec0f116 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -303,6 +303,15 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
 	return ret;
 }
 
+static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
+{
+	struct dw_pcie *dw = &rcar->dw;
+
+	reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
+	reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
+	clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
+}
+
 static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
 {
 	struct dw_pcie *dw = &rcar->dw;
@@ -328,6 +337,12 @@ static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
 	return 0;
 }
 
+static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar)
+{
+	phy_exit(rcar->phy);
+	rcar_gen4_pcie_common_deinit(rcar);
+}
+
 static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
 {
 	struct dw_pcie *dw = &rcar->dw;
@@ -371,21 +386,6 @@ static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
 	return ret;
 }
 
-static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
-{
-	struct dw_pcie *dw = &rcar->dw;
-
-	reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
-	reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
-	clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
-}
-
-static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar)
-{
-	phy_exit(rcar->phy);
-	rcar_gen4_pcie_common_deinit(rcar);
-}
-
 static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
 {
 	struct device *dev = rcar->dw.dev;
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 06/15] PCI: rcar-gen4: Assert resets when Gen5 PHY initialization fails
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (4 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 05/15] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  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
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

common_init() enables the clocks and deasserts the power and
Application/Local resets before phy_init(). If phy_init() fails, the
error path only disables the clocks, leaving those resets deasserted.

Assert both resets before disabling the clocks to unwind the completed
common initialization.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - New patch. Place the fix(?) before the initialization refactoring.

Marek, I looked through v1-v5 of your X5H series, but I'm not yet sure,
was there a specific reason to leave resets deasserted when phy_init()
fails? If not, this fixes the unwind before the restructuring in the
following patches.

 drivers/pci/controller/dwc/pcie-rcar-gen4.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index f9241ec0f116..75e74fe3889d 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -381,7 +381,7 @@ static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
 	return 0;
 
 err_unprepare:
-	clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
+	rcar_gen4_pcie_common_deinit(rcar);
 
 	return ret;
 }
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 07/15] PCI: rcar-gen4: Separate hardware setup from resource acquisition
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (5 preceding siblings ...)
  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:52 ` Koichiro Den
  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
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

Factor out the hardware setup needed by Root Port recovery into helpers.
Recovery needs to reset and restore the controller while keeping its
clocks enabled and the Gen5 PHY initialized.

- From rcar_gen4_pcie_common_init(), extract the power reset and mode
  setup. Leave clock enabling and Application/Local reset control in
  common_init(), so recovery can keep the APP registers accessible.

- From the V4H/V4M and Gen5 .init() callbacks, extract the SoC-specific
  register setup. This allows recovery to restore those settings without
  taking another clock or PHY initialization reference.

- From rcar_gen4_pcie_host_init(), extract the Root Port register setup
  and PERST# release. Both paths need this host-specific sequence after
  the per-SoC initialization or reset.

No functional changes.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Separate hardware setup extraction from the .reinit() addition.
  - Keep the reset_control_status() check, so the power reset assertion
    and the 1 ms wait after it remain conditional. (Marek)

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

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 75e74fe3889d..e73ef6ccb145 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -236,20 +236,13 @@ static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)
 		rcar->drvdata->ltssm_control(rcar, false);
 }
 
-static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
+/* Clocks must be enabled and the Application/Local reset deasserted. */
+static int rcar_gen4_pcie_reset(struct rcar_gen4_pcie *rcar)
 {
 	struct dw_pcie *dw = &rcar->dw;
 	u32 val;
 	int ret;
 
-	ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
-	if (ret) {
-		dev_err(dw->dev, "Enabling core clocks failed\n");
-		return ret;
-	}
-
-	reset_control_deassert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
-
 	if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {
 		reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
 		/*
@@ -268,8 +261,7 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
 	} else if (rcar->drvdata->mode == DW_PCIE_EP_TYPE) {
 		val |= DEVICE_TYPE_EP;
 	} else {
-		ret = -EINVAL;
-		goto err_unprepare;
+		return -EINVAL;
 	}
 
 	if (dw->num_lanes < 4)
@@ -279,7 +271,7 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
 
 	ret = reset_control_deassert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
 	if (ret)
-		goto err_unprepare;
+		return ret;
 
 	/*
 	 * Assure the reset is latched and the core is ready for DBI access.
@@ -295,6 +287,26 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
 	fsleep(1000);
 
 	return 0;
+}
+
+static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
+{
+	struct dw_pcie *dw = &rcar->dw;
+	int ret;
+
+	ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
+	if (ret) {
+		dev_err(dw->dev, "Enabling core clocks failed\n");
+		return ret;
+	}
+
+	reset_control_deassert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
+
+	ret = rcar_gen4_pcie_reset(rcar);
+	if (ret)
+		goto err_unprepare;
+
+	return 0;
 
 err_unprepare:
 	reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
@@ -312,16 +324,10 @@ static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
 	clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
 }
 
-static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
+static void rcar_gen4_v4h_v4m_pcie_configure(struct rcar_gen4_pcie *rcar)
 {
 	struct dw_pcie *dw = &rcar->dw;
 	u32 val;
-	int ret;
-
-	/* R-Car Gen4 common initialization. */
-	ret = rcar_gen4_pcie_common_init(rcar);
-	if (ret)
-		return ret;
 
 	/* R-Car V4H and V4M specific additional initialization. */
 	val = dw_pcie_readl_dbi(dw, PCIE_PORT_LANE_SKEW);
@@ -333,6 +339,17 @@ static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
 	val = readl(rcar->base + PCIEPWRMNGCTRL);
 	val |= APP_CLK_REQ_N | APP_CLK_PM_EN;
 	writel(val, rcar->base + PCIEPWRMNGCTRL);
+}
+
+static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
+{
+	int ret;
+
+	ret = rcar_gen4_pcie_common_init(rcar);
+	if (ret)
+		return ret;
+
+	rcar_gen4_v4h_v4m_pcie_configure(rcar);
 
 	return 0;
 }
@@ -343,22 +360,11 @@ static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar)
 	rcar_gen4_pcie_common_deinit(rcar);
 }
 
-static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
+static void rcar_gen5_pcie_configure(struct rcar_gen4_pcie *rcar)
 {
 	struct dw_pcie *dw = &rcar->dw;
-	int ret;
 	u32 val;
 
-	/* R-Car Gen4 and Gen5 common initialization. */
-	ret = rcar_gen4_pcie_common_init(rcar);
-	if (ret)
-		return ret;
-
-	/* R-Car Gen5 specific additional initialization. */
-	ret = phy_init(rcar->phy);
-	if (ret)
-		goto err_unprepare;
-
 	dw_pcie_dbi_ro_wr_en(dw);
 
 	val = dw_pcie_readl_dbi(dw, PCIE_PORT_LANE_SKEW);
@@ -377,13 +383,25 @@ static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
 	val |= APP_CLK_REQ_N | APP_CLK_PM_EN |
 	       APP_READY_ENTR_L23 | APP_REQ_ENTR_L1;
 	writel(val, rcar->base + PCIEPWRMNGCTRL);
+}
 
-	return 0;
+static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
+{
+	int ret;
 
-err_unprepare:
-	rcar_gen4_pcie_common_deinit(rcar);
+	ret = rcar_gen4_pcie_common_init(rcar);
+	if (ret)
+		return ret;
 
-	return ret;
+	ret = phy_init(rcar->phy);
+	if (ret) {
+		rcar_gen4_pcie_common_deinit(rcar);
+		return ret;
+	}
+
+	rcar_gen5_pcie_configure(rcar);
+
+	return 0;
 }
 
 static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
@@ -621,21 +639,11 @@ static void rcar_gen4_pcie_host_perst_assert(struct dw_pcie_rp *pp, bool assert)
 }
 
 /* Host mode */
-static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
+static int rcar_gen4_pcie_host_setup(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);
 	int ret;
 
-	if (pp->bridge)
-		pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
-
-	rcar_gen4_pcie_host_perst_assert(pp, true);
-
-	ret = rcar->drvdata->init(rcar);
-	if (ret)
-		return ret;
-
 	/*
 	 * According to the section 3.5.7.2 "RC Mode" in DWC PCIe Dual Mode
 	 * Rev.5.20a and 3.5.6.1 "RC mode" in DWC PCIe RC databook v5.20a, we
@@ -647,13 +655,34 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
 
 	ret = rcar_gen4_pcie_host_msi_init(pp);
 	if (ret)
-		goto err;
+		return ret;
 
 	msleep(PCIE_T_PVPERL_MS);	/* pe_rst requires 100msec delay */
-
 	rcar_gen4_pcie_host_perst_assert(pp, false);
 
 	return 0;
+}
+
+static int rcar_gen4_pcie_host_init(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);
+	int ret;
+
+	if (pp->bridge)
+		pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
+
+	rcar_gen4_pcie_host_perst_assert(pp, true);
+
+	ret = rcar->drvdata->init(rcar);
+	if (ret)
+		return ret;
+
+	ret = rcar_gen4_pcie_host_setup(pp);
+	if (ret)
+		goto err;
+
+	return 0;
 
 err:
 	rcar->drvdata->deinit(rcar);
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 08/15] PCI: rcar-gen4: Add a controller reinitialization callback
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (6 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 07/15] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  2026-10-03 23:54   ` Marek Vasut
  2026-09-28 16:52 ` [PATCH v2 09/15] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

Root Port recovery needs to reset and restore the controller while
keeping the clocks enabled and the Gen5 PHY initialized. Reusing .init()
would acquire these resources again, while calling .deinit() first would
make the APP registers inaccessible to interrupt handling.

Add a per-SoC .reinit() callback that repeats the power reset and register
setup without acquiring or releasing resources. Use the same reset and
register setup helpers as .init().

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Adapt to the per-SoC .init()/.deinit() callbacks in next-20260925.
    Add .reinit() for recovery without changing their resource ownership
    or taking another PHY initialization reference.
  - Move hardware setup extraction and Gen5 initialization cleanup into
    preceding patches. This patch only adds the callback and its
    implementations.

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

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index e73ef6ccb145..d1ea94272711 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -99,6 +99,8 @@ struct rcar_gen4_pcie;
 struct rcar_gen4_pcie_drvdata {
 	int (*init)(struct rcar_gen4_pcie *rcar);
 	void (*deinit)(struct rcar_gen4_pcie *rcar);
+	/* Reset and restore registers while clocks and PHY remain initialized. */
+	int (*reinit)(struct rcar_gen4_pcie *rcar);
 	int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable);
 	int (*speed_control)(struct rcar_gen4_pcie *rcar);
 	enum dw_pcie_device_mode mode;
@@ -341,6 +343,19 @@ static void rcar_gen4_v4h_v4m_pcie_configure(struct rcar_gen4_pcie *rcar)
 	writel(val, rcar->base + PCIEPWRMNGCTRL);
 }
 
+static int rcar_gen4_v4h_v4m_pcie_reinit(struct rcar_gen4_pcie *rcar)
+{
+	int ret;
+
+	ret = rcar_gen4_pcie_reset(rcar);
+	if (ret)
+		return ret;
+
+	rcar_gen4_v4h_v4m_pcie_configure(rcar);
+
+	return 0;
+}
+
 static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
 {
 	int ret;
@@ -385,6 +400,19 @@ static void rcar_gen5_pcie_configure(struct rcar_gen4_pcie *rcar)
 	writel(val, rcar->base + PCIEPWRMNGCTRL);
 }
 
+static int rcar_gen5_pcie_reinit(struct rcar_gen4_pcie *rcar)
+{
+	int ret;
+
+	ret = rcar_gen4_pcie_reset(rcar);
+	if (ret)
+		return ret;
+
+	rcar_gen5_pcie_configure(rcar);
+
+	return 0;
+}
+
 static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
 {
 	int ret;
@@ -1105,6 +1133,7 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
 static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = {
 	.init = rcar_gen4_pcie_common_init,
 	.deinit = rcar_gen4_pcie_common_deinit,
+	.reinit = rcar_gen4_pcie_reset,
 	.ltssm_control = r8a779f0_pcie_ltssm_control,
 	.speed_control = rcar_gen4_pcie_speed_control,
 	.mode = DW_PCIE_RC_TYPE,
@@ -1113,6 +1142,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = {
 static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = {
 	.init = rcar_gen4_pcie_common_init,
 	.deinit = rcar_gen4_pcie_common_deinit,
+	.reinit = rcar_gen4_pcie_reset,
 	.ltssm_control = r8a779f0_pcie_ltssm_control,
 	.speed_control = rcar_gen4_pcie_speed_control,
 	.mode = DW_PCIE_EP_TYPE,
@@ -1121,6 +1151,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = {
 static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
 	.init = rcar_gen4_v4h_v4m_pcie_init,
 	.deinit = rcar_gen4_pcie_common_deinit,
+	.reinit = rcar_gen4_v4h_v4m_pcie_reinit,
 	.ltssm_control = rcar_gen4_pcie_ltssm_control,
 	.speed_control = rcar_gen4_pcie_speed_control,
 	.mode = DW_PCIE_RC_TYPE,
@@ -1129,6 +1160,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
 static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
 	.init = rcar_gen4_v4h_v4m_pcie_init,
 	.deinit = rcar_gen4_pcie_common_deinit,
+	.reinit = rcar_gen4_v4h_v4m_pcie_reinit,
 	.ltssm_control = rcar_gen4_pcie_ltssm_control,
 	.speed_control = rcar_gen4_pcie_speed_control,
 	.mode = DW_PCIE_EP_TYPE,
@@ -1137,6 +1169,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
 static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen5_pcie = {
 	.init = rcar_gen5_pcie_init,
 	.deinit = rcar_gen5_pcie_deinit,
+	.reinit = rcar_gen5_pcie_reinit,
 	.ltssm_control = rcar_gen5_pcie_ltssm_control,
 	.speed_control = rcar_gen5_pcie_speed_control,
 	.mode = DW_PCIE_RC_TYPE,
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 09/15] PCI: rcar-gen4: Add Root Port reset support
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (7 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 08/15] PCI: rcar-gen4: Add a controller reinitialization callback Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  2026-10-04  0:11   ` Marek Vasut
  2026-09-28 16:52 ` [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

Implement the host bridge reset_root_port() callback so PCI error
recovery can reset and reinitialize the R-Car controller. This also
provides the reset operation for the link-down handling added later.

Call .reinit() with clocks and PHY initialization retained, restore
the Root Port registers and restart link training.

Rather than tracking which APP interrupt enables survive the power
reset, derive them from software state through a single helper. A flag
keeps the sources masked from the start of a reset until one succeeds,
so a failed reinitialization does not re-enable them against an
uninitialized controller.

Serialize the reset with a mutex, as not all callers hold the Root
Port's device lock: pci_try_reset_function() on a downstream device only
locks that device before falling back to a parent bus reset.

Track whether Gen5 PHY power-on has been requested so a reset retry, or
teardown after a failed reset, does not call phy_power_off() twice.
Continue to ignore the PHY power operation return values.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Use a bool flag instead of a state bitmask. (Marek)
  - Keep the sources masked after a failed reset.
  - Use .reinit() to retain clocks and PHY initialization across resets.
  - Keep Gen5 PHY power calls balanced across failed reset retries.
  - Refine the commit message.

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

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index d1ea94272711..d3f04358bbe3 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -15,6 +15,7 @@
 #include <linux/iopoll.h>
 #include <linux/irqchip/arm-gic-v3.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
@@ -111,9 +112,19 @@ struct rcar_gen4_pcie {
 	void __iomem *base;
 	void __iomem *phy_base;
 	struct phy *phy;
+	bool phy_powered;
 	struct platform_device *pdev;
 	struct reset_control *perst;
 	const struct rcar_gen4_pcie_drvdata *drvdata;
+	/*
+	 * The controller needs reinitializing; the APP interrupt sources
+	 * stay masked until a reset succeeds.
+	 */
+	bool reinit_pending;
+	/* Protects APP interrupt enable registers and their software state. */
+	raw_spinlock_t app_lock;
+	/* Serializes Root Port hardware reinitialization. */
+	struct mutex reset_lock;
 };
 #define to_rcar_gen4_pcie(_dw)	container_of(_dw, struct rcar_gen4_pcie, dw)
 
@@ -543,6 +554,26 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
 	return 0;
 }
 
+static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
+{
+	u32 val;
+
+	lockdep_assert_held(&rcar->app_lock);
+
+	val = readl(rcar->base + PCIEINTSTS0EN);
+	val &= ~MSI_CTRL_INT;
+	if (!rcar->reinit_pending && pci_msi_enabled())
+		val |= MSI_CTRL_INT;
+	writel(val, rcar->base + PCIEINTSTS0EN);
+}
+
+static void rcar_gen4_pcie_app_irq_sync(struct rcar_gen4_pcie *rcar)
+{
+	guard(raw_spinlock_irqsave)(&rcar->app_lock);
+
+	rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
 static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
 {
 	struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
@@ -573,12 +604,7 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
 	}
 
 	/* Configure MSI interrupt signal */
-	val = readl(rcar->base + PCIEINTSTS0EN);
-	if (pci_msi_enabled())
-		val |= MSI_CTRL_INT;
-	else
-		val &= ~MSI_CTRL_INT;
-	writel(val, rcar->base + PCIEINTSTS0EN);
+	rcar_gen4_pcie_app_irq_sync(rcar);
 
 	return 0;
 
@@ -593,6 +619,7 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
 	writel(0, rcar->base + AXIINTCCONT);
 
 	/* Deconfigure MSI interrupt signal */
+	guard(raw_spinlock_irqsave)(&rcar->app_lock);
 	val = readl(rcar->base + PCIEINTSTS0EN);
 	val &= ~MSI_CTRL_INT;
 	writel(val, rcar->base + PCIEINTSTS0EN);
@@ -691,14 +718,83 @@ static int rcar_gen4_pcie_host_setup(struct dw_pcie_rp *pp)
 	return 0;
 }
 
+static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
+{
+	scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+		rcar->reinit_pending = true;
+		rcar_gen4_pcie_app_irq_sync_locked(rcar);
+	}
+}
+
+static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
+				       bool recovered)
+{
+	scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+		rcar->reinit_pending = !recovered;
+		rcar_gen4_pcie_app_irq_sync_locked(rcar);
+	}
+}
+
+/*
+ * R-Car Gen4 controllers have a single Root Port per instance, so the
+ * 'pci_dev' is ignored and the whole controller is reset.
+ */
+static int rcar_gen4_pcie_reset_root_port(struct pci_host_bridge *bridge,
+					  struct pci_dev *pdev)
+{
+	struct rcar_gen4_pcie *rcar = dev_get_drvdata(bridge->dev.parent);
+	struct dw_pcie *dw = &rcar->dw;
+	struct dw_pcie_rp *pp = &dw->pp;
+	struct device *dev = dw->dev;
+	int ret;
+
+	guard(mutex)(&rcar->reset_lock);
+
+	rcar_gen4_pcie_quiesce_irqs(rcar);
+
+	dw_pcie_stop_link(dw);
+
+	rcar_gen4_pcie_host_perst_assert(pp, true);
+
+	ret = rcar->drvdata->reinit(rcar);
+	if (ret)
+		goto out;
+
+	ret = rcar_gen4_pcie_host_setup(pp);
+	if (ret)
+		goto out;
+
+	ret = dw_pcie_setup_rc(pp);
+	if (ret)
+		goto out;
+
+	ret = dw_pcie_start_link(dw);
+	if (ret)
+		goto out;
+
+	/* Ignore errors, the link may come up later */
+	dw_pcie_wait_for_link(dw);
+	dev_dbg(dev, "Root Port reset completed\n");
+
+out:
+	rcar_gen4_pcie_resume_irqs(rcar, !ret);
+	if (ret)
+		dev_err(dev, "Root Port reset failed (%d); interrupts stay masked until a reset succeeds\n",
+			ret);
+
+	return ret;
+}
+
 static int rcar_gen4_pcie_host_init(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);
 	int ret;
 
-	if (pp->bridge)
+	if (pp->bridge) {
 		pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
+		pp->bridge->reset_root_port = rcar_gen4_pcie_reset_root_port;
+	}
 
 	rcar_gen4_pcie_host_perst_assert(pp, true);
 
@@ -734,10 +830,17 @@ static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = {
 static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
 {
 	struct dw_pcie_rp *pp = &rcar->dw.pp;
+	int ret;
 
 	if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_HOST))
 		return -ENODEV;
 
+	ret = devm_mutex_init(rcar->dw.dev, &rcar->reset_lock);
+	if (ret)
+		return ret;
+
+	raw_spin_lock_init(&rcar->app_lock);
+
 	pp->num_vectors = MAX_MSI_IRQS;
 	pp->ops = &rcar_gen4_pcie_host_ops;
 
@@ -972,11 +1075,17 @@ static int rcar_gen5_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
 	}
 	writel(val, rcar->base + PCIERSTCTRL1);
 
+	/* A reset retry may find the PHY already powered off. */
+	if (enable == rcar->phy_powered)
+		return 0;
+
 	if (enable)
 		phy_power_on(rcar->phy);
 	else
 		phy_power_off(rcar->phy);
 
+	rcar->phy_powered = enable;
+
 	return 0;
 }
 
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (8 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 09/15] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  2026-09-29 17:43   ` Geert Uytterhoeven
  2026-10-04  0:53   ` Marek Vasut
  2026-09-28 16:52 ` [PATCH v2 11/15] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
                   ` (5 subsequent siblings)
  15 siblings, 2 replies; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On R-Car Gen4, intreq_pcim_sub ("msi") carries more than the integrated
MSI receiver: the controller's reset requests and the Root Port's PME
and bandwidth notifications are signalled on the same line, and the
following patches need to handle them. With the DesignWare core owning
the line through its chained handler, the driver would have to hook into
that handler when iMSI-RX is used and request the line itself otherwise.

Instead, request the interrupt in the driver in all configurations and
set pp->msi_irq[0] to -ENODEV so the core does not install its chained
handler, as spear13xx, keembay and dra7xx do. The handler demultiplexes
the MSIs through dw_handle_msi_irq() when the APP block reports
msi_ctrl_int. With an external MSI controller or pci=nomsi the iMSI-RX
is not set up, so keep msi_ctrl_int masked rather than enabled, and the
handler has nothing to do there yet. Request the interrupt before
enumeration, as endpoint drivers may use MSIs from their probe, with
IRQF_NO_THREAD so the MSIs are demultiplexed in hard IRQ context like
the chained handler did. The interrupt is required by the binding.

Release the interrupt in .deinit, before asserting the controller reset
and disabling its clocks. Disable it around a Root Port reset because
the handler accesses the MSI status registers through DBI.

The DT routes downstream INTx to the same line, but the driver has never
supported INTx (no INTx domain, INTx enables never set), so requesting
the line exclusively takes nothing away.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - New patch. Owning the interrupt in both MSI configurations, as
    suggested by Marek, replaces the pre_msi_irq host op and the
    two-path handling of v1. (Marek)
  - Rename sub_irq to msi_irq to avoid confusion with a child IRQ. In the
    complete series, pcie-rcar-gen4.c uses these names:
      * DT "msi" IRQ: msi_irq (was named sub_irq in v1, after
	                       intreq_pcim_sub)
      * DT "aer" IRQ: aer_irq
      * Virtual Root Port IRQ: rp_irq

Known gap spotted while preparing v2:
  - dw_pcie_host_deinit() frees the iMSI-RX domain before .deinit, so a
    parent handler already past the APP status check can still reach it.
    Not specific to this driver: spear13xx, keembay and dra7xx own their
    parent IRQ the same way (no .remove, so probe unwind only), and
    dw_pcie_free_msi() does not synchronize the core's own chained
    handler either. I believe this needs a common DWC core fix letting
    glue drivers stop the parent IRQ before the domain goes. That would
    require each driver review and testing, so I left it out of scope.

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

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index d3f04358bbe3..256410fb4b1c 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -116,6 +116,8 @@ struct rcar_gen4_pcie {
 	struct platform_device *pdev;
 	struct reset_control *perst;
 	const struct rcar_gen4_pcie_drvdata *drvdata;
+	/* intreq_pcim_sub ("msi"): iMSI-RX and other controller notifications */
+	int msi_irq;
 	/*
 	 * The controller needs reinitializing; the APP interrupt sources
 	 * stay masked until a reset succeeds.
@@ -556,13 +558,14 @@ 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;
 	u32 val;
 
 	lockdep_assert_held(&rcar->app_lock);
 
 	val = readl(rcar->base + PCIEINTSTS0EN);
 	val &= ~MSI_CTRL_INT;
-	if (!rcar->reinit_pending && pci_msi_enabled())
+	if (!rcar->reinit_pending && pp->use_imsi_rx && pci_msi_enabled())
 		val |= MSI_CTRL_INT;
 	writel(val, rcar->base + PCIEINTSTS0EN);
 }
@@ -627,6 +630,52 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
 	return ret;
 }
 
+/*
+ * intreq_pcim_sub carries the iMSI-RX interrupt along with other controller
+ * notifications, so the driver owns it instead of the DesignWare core (see
+ * pp->msi_irq[0]) and demultiplexes the MSIs itself.
+ */
+static irqreturn_t rcar_gen4_pcie_msi_irq_handler(int irq, void *data)
+{
+	struct rcar_gen4_pcie *rcar = data;
+	u32 status;
+
+	status = readl(rcar->base + PCIEINTSTS0) &
+		 readl(rcar->base + PCIEINTSTS0EN);
+	if (!(status & MSI_CTRL_INT))
+		return IRQ_NONE;
+
+	dw_handle_msi_irq(&rcar->dw.pp);
+
+	return IRQ_HANDLED;
+}
+
+static int rcar_gen4_pcie_msi_irq_init(struct rcar_gen4_pcie *rcar)
+{
+	struct device *dev = rcar->dw.dev;
+	int irq, ret;
+
+	irq = platform_get_irq_byname(rcar->pdev, "msi");
+	if (irq < 0)
+		return irq;
+
+	/*
+	 * Child IRQ flow handlers require local IRQs to be disabled. Keep this
+	 * handler in hard IRQ context on PREEMPT_RT.
+	 *
+	 * Release the IRQ in .deinit before asserting reset and disabling the
+	 * clocks. Automatic devres cleanup would be too late.
+	 */
+	ret = request_irq(irq, rcar_gen4_pcie_msi_irq_handler, IRQF_NO_THREAD,
+			  dev_name(dev), rcar);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to request \"msi\" IRQ\n");
+
+	rcar->msi_irq = irq;
+
+	return 0;
+}
+
 static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
 					struct pci_dev *dev)
 {
@@ -724,6 +773,9 @@ static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
 		rcar->reinit_pending = true;
 		rcar_gen4_pcie_app_irq_sync_locked(rcar);
 	}
+
+	/* The MSI status lives in DBI; keep the handler away during the reset. */
+	disable_irq(rcar->msi_irq);
 }
 
 static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
@@ -733,6 +785,8 @@ static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
 		rcar->reinit_pending = !recovered;
 		rcar_gen4_pcie_app_irq_sync_locked(rcar);
 	}
+
+	enable_irq(rcar->msi_irq);
 }
 
 /*
@@ -804,11 +858,17 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
 
 	ret = rcar_gen4_pcie_host_setup(pp);
 	if (ret)
-		goto err;
+		goto err_deinit;
+
+	ret = rcar_gen4_pcie_msi_irq_init(rcar);
+	if (ret)
+		goto err_assert_perst;
 
 	return 0;
 
-err:
+err_assert_perst:
+	rcar_gen4_pcie_host_perst_assert(pp, true);
+err_deinit:
 	rcar->drvdata->deinit(rcar);
 	return ret;
 }
@@ -818,6 +878,9 @@ static void rcar_gen4_pcie_host_deinit(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);
 
+	/* Stop the handler before asserting reset and disabling the clocks. */
+	free_irq(rcar->msi_irq, rcar);
+
 	rcar_gen4_pcie_host_perst_assert(pp, true);
 	rcar->drvdata->deinit(rcar);
 }
@@ -842,6 +905,12 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
 	raw_spin_lock_init(&rcar->app_lock);
 
 	pp->num_vectors = MAX_MSI_IRQS;
+	/*
+	 * intreq_pcim_sub ("msi") is shared with other controller
+	 * notifications: the driver requests it itself and demultiplexes the
+	 * iMSI-RX MSIs from its own handler.
+	 */
+	pp->msi_irq[0] = -ENODEV;
 	pp->ops = &rcar_gen4_pcie_host_ops;
 
 	return dw_pcie_host_init(pp);
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 11/15] PCI: rcar-gen4: Recover the Root Port on link down
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (9 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  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
                   ` (4 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

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


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 12/15] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (10 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 11/15] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  2026-09-28 16:52 ` [PATCH v2 13/15] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

Commit f5cd8a929c82 ("PCI: dwc: Remove MSI/MSIX capability for Root Port
if iMSI-RX is used as MSI controller") hides the Root Port MSI/MSI-X
capabilities when iMSI-RX is the MSI controller, as it cannot receive
the Root Port's own MSIs.

Some platforms cannot deliver them with an external MSI controller
either. The DesignWare core signals Root Port interrupts (AER, PME,
bandwidth, hotplug) as sideband signals on its SII interface and only
turns them into MSI writes with the optional SII2MSI feature. Otherwise
it is up to the vendor wrapper. R-Car Gen4 (DWC 5.20a) appears to lack
SII2MSI, and its APP block merely latches the signals into status
registers wired to platform IRQs, so an MSI targeted at the GIC ITS is
never sent.

Add a no_rp_msi flag so glue drivers can request the same INTx fallback
regardless of the MSI controller in use. It is the counterpart of
'keep_rp_msi_en' from commit 3a4e8302e72f ("PCI: imx6: Keep Root Port
MSI capability with iMSI-RX to work around hardware bug"), which keeps
precedence: that one is needed for endpoint MSIs to work at all, whereas
hiding the capability only moves the Root Port services to INTx. The
removal stays in dw_pcie_setup_rc(), which also runs after a Root Port
reset.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - No changes.

 drivers/pci/controller/dwc/pcie-designware-host.c | 6 ++++--
 drivers/pci/controller/dwc/pcie-designware.h      | 1 +
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 31e15666479b..5b2f249490a7 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -1176,9 +1176,11 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
 	 * The iMSI-RX module does not support receiving MSI or MSI-X generated
 	 * by the Root Port. If iMSI-RX is used as the MSI controller, remove
 	 * the MSI and MSI-X capabilities of the Root Port to allow the drivers
-	 * to fall back to INTx instead.
+	 * to fall back to INTx instead. Glue drivers set no_rp_msi when the
+	 * platform never delivers the Root Port's own MSIs, whatever the MSI
+	 * controller in use.
 	 */
-	if (pp->use_imsi_rx && !pp->keep_rp_msi_en) {
+	if ((pp->use_imsi_rx || pp->no_rp_msi) && !pp->keep_rp_msi_en) {
 		dw_pcie_remove_capability(pci, PCI_CAP_ID_MSI);
 		dw_pcie_remove_capability(pci, PCI_CAP_ID_MSIX);
 	}
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index a53ac27cd244..cf1b912bb396 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -444,6 +444,7 @@ struct dw_pcie_host_ops {
 struct dw_pcie_rp {
 	bool			use_imsi_rx:1;
 	bool			keep_rp_msi_en:1;
+	bool			no_rp_msi:1;
 	bool			cfg0_io_shared:1;
 	u64			cfg0_base;
 	void __iomem		*va_cfg0_base;
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 13/15] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (11 preceding siblings ...)
  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:52 ` Koichiro Den
  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
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On R-Car Gen4, the APP block exposes the Root Port interrupt signals in
PCIEINTSTS0 and routes them to platform IRQs: AER to intreq_pcim_lperr,
and PME and bandwidth notifications to intreq_pcim_sub. On an R-Car S4
with GIC ITS, a link retrain set LnkSta.LBMS and latched PCIEINTSTS0[23]
(cfg_bw_mgt_msi), but the Root Port's MSI never fired while endpoint
MSIs worked fine. With iMSI-RX the Root Port MSI capabilities are
already hidden, but its INTx maps to intreq_pcim_sub. Note that even
before this driver took over the IRQ with
rcar_gen4_pcie_msi_irq_handler, the port service was unable to request
it, as the DWC core used that IRQ for its chained MSI handler. In either
case (ie. iMSI-RX or an external MSI controller), Root Port interrupts
are not delivered to the port services.

Set no_rp_msi so the port services always fall back to INTx. Allocate an
IRQ in a single-entry domain and return its number from the bridge's
map_irq callback for the Root Port only, leaving downstream INTx
mappings unchanged. Notifications from different platform IRQs can then
feed the one IRQ the port services share. Route the AER notifications
from the "aer" interrupt to it. rcar_gen4_pcie_host_msi_init() sets
MSICAP0.MSIE when pci_msi_enabled() is true, and hiding the capability
does not clear it, so handle both the MSI and INTx forms of the status
bits. DTs without "aer" remain supported, but the APP AER notification
sources stay masked.

Preserve the virtual IRQ's enable state across controller resets, but
keep its APP sources masked while reinitialization is pending or Root
Port events are disarmed. Clear only the MSI-form APP latch, before the
AER handler clears Root Error Status, to avoid clearing a newly latched
notification.

Handle pending reset requests before dispatching AER to avoid unsafe DBI
access. Serialize dispatches from the parent IRQs, including the "msi"
path added by the next patch, since handle_level_irq() could otherwise
acknowledge a concurrent interrupt without running its handlers. Recheck
reset requests after taking the lock in case the other parent has marked
reinitialization as pending.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Adapt to the driver taking over the iMSI-RX interrupt, and keep
    AER notifications masked until .post_init, after enumeration.
  - Clear only the MSI-form latch through PCIEINTSTS0CLR; the INTx bit
    is reserved there.
  - Serialize dispatches to the virtual Root Port IRQ.
  - Revise the commit message.

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

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index aa80232afaba..c01f157a7198 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -14,11 +14,13 @@
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/irqchip/arm-gic-v3.h>
+#include <linux/irqdomain.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/of_pci.h>
 #include <linux/pci.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
@@ -60,6 +62,11 @@
 #define SMLH_LINK_UP		BIT(7)
 #define RDLH_LINK_UP		BIT(6)
 
+/* Root Port notifications routed to intreq_pcim_lperr ("aer") */
+#define AER_RC_ERR_MSI		BIT(29)
+#define AER_RC_ERR_INT		BIT(27)
+#define RP_AER_MASK		(AER_RC_ERR_MSI | AER_RC_ERR_INT)
+
 /* PCIe DMA Interrupt Status Enable */
 #define PCIEDMAINTSTSEN		0x0314
 #define PCIEDMAINTSTSEN_INIT	GENMASK(15, 0)
@@ -69,6 +76,7 @@
 
 /* PCIe Interrupt Status 0 Clear */
 #define PCIEINTSTS0CLR		0x0340
+#define RP_LATCHED_MASK		AER_RC_ERR_MSI
 
 /* Port Logic Registers 89 */
 #define PRTLGC89		0x0b70
@@ -134,6 +142,14 @@ struct rcar_gen4_pcie {
 	struct work_struct link_down_work;
 	/* Allow Root Port event handling after enumeration and until teardown. */
 	bool rp_events_armed;
+	/* Virtual Root Port INTx shared by the port services */
+	struct irq_domain *rp_irq_domain;
+	unsigned int rp_irq;
+	bool rp_irq_enabled;
+	/* Serializes dispatch to the virtual Root Port IRQ from its parent IRQs */
+	raw_spinlock_t rp_irq_lock;
+	/* intreq_pcim_lperr ("aer"): Root Port AER notifications, optional */
+	int aer_irq;
 	/*
 	 * The controller needs reinitializing; the APP interrupt sources
 	 * stay masked until a reset succeeds.
@@ -581,7 +597,7 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
 	lockdep_assert_held(&rcar->app_lock);
 
 	val = readl(rcar->base + PCIEINTSTS0EN);
-	val &= ~MSI_CTRL_INT;
+	val &= ~(MSI_CTRL_INT | RP_AER_MASK);
 	/*
 	 * Note that rcar->rp_events_armed is not checked here because device
 	 * removal may still need iMSI-RX interrupts after Root Port events have
@@ -589,6 +605,8 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
 	 */
 	if (!rcar->reinit_pending && pp->use_imsi_rx && pci_msi_enabled())
 		val |= MSI_CTRL_INT;
+	if (rp_events && rcar->rp_irq_enabled && rcar->aer_irq > 0)
+		val |= RP_AER_MASK;
 	writel(val, rcar->base + PCIEINTSTS0EN);
 
 	val = readl(rcar->base + PCIERSTSTSEN);
@@ -689,6 +707,21 @@ static bool rcar_gen4_pcie_handle_link_down(struct rcar_gen4_pcie *rcar)
 	return rcar->reinit_pending;
 }
 
+static void rcar_gen4_pcie_rp_irq_dispatch(struct rcar_gen4_pcie *rcar)
+{
+	/*
+	 * Serialize dispatches from the parent IRQs. handle_level_irq() would
+	 * ack an IRQ already in progress without running its handlers again.
+	 * Recheck after taking the lock since the parent's check may be stale.
+	 */
+	guard(raw_spinlock_irqsave)(&rcar->rp_irq_lock);
+
+	if (rcar_gen4_pcie_handle_link_down(rcar))
+		return;
+
+	generic_handle_domain_irq(rcar->rp_irq_domain, 0);
+}
+
 static void rcar_gen4_pcie_link_down_work(struct work_struct *work)
 {
 	struct rcar_gen4_pcie *rcar =
@@ -779,6 +812,170 @@ static void rcar_gen4_pcie_rp_events_disarm(struct rcar_gen4_pcie *rcar)
 	cancel_work_sync(&rcar->link_down_work);
 }
 
+static void rcar_gen4_pcie_rp_irq_mask(struct irq_data *d)
+{
+	struct rcar_gen4_pcie *rcar = irq_data_get_irq_chip_data(d);
+
+	guard(raw_spinlock_irqsave)(&rcar->app_lock);
+	rcar->rp_irq_enabled = false;
+	rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
+static void rcar_gen4_pcie_rp_irq_unmask(struct irq_data *d)
+{
+	struct rcar_gen4_pcie *rcar = irq_data_get_irq_chip_data(d);
+
+	guard(raw_spinlock_irqsave)(&rcar->app_lock);
+	rcar->rp_irq_enabled = true;
+	rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
+static void rcar_gen4_pcie_rp_irq_ack(struct irq_data *d)
+{
+	struct rcar_gen4_pcie *rcar = irq_data_get_irq_chip_data(d);
+
+	/*
+	 * Clear APP latches before the service clears Root Error Status so
+	 * we don't drop back-to-back notifications.
+	 */
+	writel(RP_LATCHED_MASK, rcar->base + PCIEINTSTS0CLR);
+}
+
+static const struct irq_chip rcar_gen4_pcie_rp_irq_chip = {
+	.name = "rcar-gen4-rp",
+	.irq_mask = rcar_gen4_pcie_rp_irq_mask,
+	.irq_unmask = rcar_gen4_pcie_rp_irq_unmask,
+	.irq_ack = rcar_gen4_pcie_rp_irq_ack,
+};
+
+static int rcar_gen4_pcie_rp_irq_map(struct irq_domain *domain,
+				     unsigned int irq, irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &rcar_gen4_pcie_rp_irq_chip,
+				 handle_level_irq);
+	irq_set_chip_data(irq, domain->host_data);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops rcar_gen4_pcie_rp_irq_domain_ops = {
+	.map = rcar_gen4_pcie_rp_irq_map,
+};
+
+static irqreturn_t rcar_gen4_pcie_aer_irq_handler(int irq, void *data)
+{
+	struct rcar_gen4_pcie *rcar = data;
+	u32 status;
+
+	/* The line also fires on link down; reset requests come first. */
+	if (rcar_gen4_pcie_handle_link_down(rcar))
+		return IRQ_HANDLED;
+
+	status = readl(rcar->base + PCIEINTSTS0) &
+		 readl(rcar->base + PCIEINTSTS0EN);
+	if (!(status & RP_AER_MASK))
+		return IRQ_NONE;
+
+	rcar_gen4_pcie_rp_irq_dispatch(rcar);
+
+	return IRQ_HANDLED;
+}
+
+static int rcar_gen4_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
+	struct rcar_gen4_pcie *rcar = dev_get_drvdata(bridge->dev.parent);
+
+	/* Downstream devices use standard OF INTx mapping */
+	if (!pci_is_root_bus(dev->bus))
+		return of_irq_parse_and_map_pci(dev, slot, pin);
+
+	/* Root Port uses the virtual IRQ (always INTA) */
+	return rcar->rp_irq;
+}
+
+static int rcar_gen4_pcie_rp_irq_init(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);
+	struct device *dev = dw->dev;
+	struct fwnode_handle *fwnode;
+	int irq, ret;
+
+	irq = platform_get_irq_byname_optional(rcar->pdev, "aer");
+	if (irq == -ENXIO)
+		irq = 0;	/* old DT: Root Port AER stays unavailable */
+	else if (irq < 0)
+		return dev_err_probe(dev, irq, "Failed to get AER IRQ\n");
+
+	/*
+	 * Note: suspend/resume support must not tear down and recreate this
+	 * domain, as the port driver keeps the mapped IRQ.
+	 */
+	fwnode = irq_domain_alloc_named_fwnode(dev_name(dev));
+	if (!fwnode)
+		return -ENOMEM;
+
+	rcar->rp_irq_domain = irq_domain_create_linear(fwnode, 1,
+						       &rcar_gen4_pcie_rp_irq_domain_ops,
+						       rcar);
+	if (!rcar->rp_irq_domain) {
+		ret = -ENOMEM;
+		goto err_free_fwnode;
+	}
+
+	rcar->rp_irq = irq_create_mapping(rcar->rp_irq_domain, 0);
+	if (!rcar->rp_irq) {
+		ret = -ENOMEM;
+		goto err_remove_domain;
+	}
+
+	if (irq) {
+		/*
+		 * Dispatch the child IRQ in hard IRQ context, including on
+		 * PREEMPT_RT. Released in .deinit, like the "msi" IRQ.
+		 */
+		ret = request_irq(irq, rcar_gen4_pcie_aer_irq_handler,
+				  IRQF_NO_THREAD, "rcar-gen4-pcie-aer", rcar);
+		if (ret) {
+			dev_err_probe(dev, ret, "Failed to request AER IRQ\n");
+			goto err_dispose_mapping;
+		}
+		rcar->aer_irq = irq;
+	}
+
+	/*
+	 * The Root Port MSI capabilities are hidden (no_rp_msi), so the port
+	 * services fall back to INTx; route it to the virtual IRQ.
+	 */
+	pp->bridge->map_irq = rcar_gen4_pcie_map_irq;
+
+	return 0;
+
+err_dispose_mapping:
+	irq_dispose_mapping(rcar->rp_irq);
+err_remove_domain:
+	irq_domain_remove(rcar->rp_irq_domain);
+err_free_fwnode:
+	irq_domain_free_fwnode(fwnode);
+
+	return ret;
+}
+
+static void rcar_gen4_pcie_rp_irq_deinit(struct rcar_gen4_pcie *rcar)
+{
+	struct fwnode_handle *fwnode = rcar->rp_irq_domain->fwnode;
+
+	/* The port services are gone; stop the parent IRQ before the domain. */
+	if (rcar->aer_irq > 0)
+		free_irq(rcar->aer_irq, rcar);
+
+	irq_dispose_mapping(rcar->rp_irq);
+	irq_domain_remove(rcar->rp_irq_domain);
+	irq_domain_free_fwnode(fwnode);
+}
+
 static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
 					struct pci_dev *dev)
 {
@@ -879,6 +1076,8 @@ static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
 
 	/* The MSI status lives in DBI; keep the handler away during the reset. */
 	disable_irq(rcar->msi_irq);
+	if (rcar->aer_irq > 0)
+		disable_irq(rcar->aer_irq);
 }
 
 static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
@@ -889,6 +1088,8 @@ static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
 		rcar_gen4_pcie_app_irq_sync_locked(rcar);
 	}
 
+	if (rcar->aer_irq > 0)
+		enable_irq(rcar->aer_irq);
 	enable_irq(rcar->msi_irq);
 }
 
@@ -963,12 +1164,18 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
 	if (ret)
 		goto err_deinit;
 
-	ret = rcar_gen4_pcie_msi_irq_init(rcar);
+	ret = rcar_gen4_pcie_rp_irq_init(pp);
 	if (ret)
 		goto err_assert_perst;
 
+	ret = rcar_gen4_pcie_msi_irq_init(rcar);
+	if (ret)
+		goto err_rp_irq_deinit;
+
 	return 0;
 
+err_rp_irq_deinit:
+	rcar_gen4_pcie_rp_irq_deinit(rcar);
 err_assert_perst:
 	rcar_gen4_pcie_host_perst_assert(pp, true);
 err_deinit:
@@ -984,6 +1191,8 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
 	/* Stop the handler before asserting reset and disabling the clocks. */
 	free_irq(rcar->msi_irq, rcar);
 
+	rcar_gen4_pcie_rp_irq_deinit(rcar);
+
 	rcar_gen4_pcie_host_perst_assert(pp, true);
 	rcar->drvdata->deinit(rcar);
 }
@@ -1007,6 +1216,7 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
 		return ret;
 
 	raw_spin_lock_init(&rcar->app_lock);
+	raw_spin_lock_init(&rcar->rp_irq_lock);
 	INIT_WORK(&rcar->link_down_work, rcar_gen4_pcie_link_down_work);
 
 	pp->num_vectors = MAX_MSI_IRQS;
@@ -1016,6 +1226,12 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
 	 * iMSI-RX MSIs from its own handler.
 	 */
 	pp->msi_irq[0] = -ENODEV;
+	/*
+	 * The APP block only latches the Root Port's own interrupts into
+	 * PCIEINTSTS0; they are never delivered as MSI. Fall back to INTx,
+	 * which rcar_gen4_pcie_map_irq() routes to the virtual Root Port IRQ.
+	 */
+	pp->no_rp_msi = true;
 	pp->ops = &rcar_gen4_pcie_host_ops;
 
 	return dw_pcie_host_init(pp);
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 14/15] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (12 preceding siblings ...)
  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 16:52 ` Koichiro Den
  2026-10-04  4:40   ` Marek Vasut
  2026-09-28 16:52 ` [PATCH v2 15/15] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
  2026-10-03 17:46 ` [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Marek Vasut
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

The Root Port's PME and link bandwidth notifications are reported in
PCIEINTSTS0 and routed to intreq_pcim_sub, the interrupt shared with the
integrated MSI receiver and the reset requests. Dispatch these
notifications to the virtual Root Port IRQ from the "msi" IRQ handler
after handling any pending iMSI-RX interrupts. This keeps Root Port
service handling out of the path between the reset-request check and
the iMSI-RX status reads. The existing dispatcher rechecks reset requests
under rp_irq_lock before invoking the port services.

Enable the sources only while the Root Port IRQ is unmasked, and clear
the MSI-form latches in irq_ack together with the AER one. On an R-Car
S4 with the GIC ITS as MSI parent, a notification from a link retrain
now reaches the bandwidth controller service, which clears LnkSta.LBMS.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Dispatch from the driver's "msi" IRQ handler instead of the
    pre_msi_irq callback and the separate handler used in v1. (Marek)
  - Clear only the MSI-form latches. The corresponding INTx bit
    positions are reserved in PCIEINTSTS0CLR.
  - Revise the commit message.

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

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index c01f157a7198..319f917f9027 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -67,6 +67,16 @@
 #define AER_RC_ERR_INT		BIT(27)
 #define RP_AER_MASK		(AER_RC_ERR_MSI | AER_RC_ERR_INT)
 
+/* Root Port notifications routed to intreq_pcim_sub ("msi") */
+#define BW_MGT_MSI		BIT(23)
+#define BW_MGT_INT		BIT(22)
+#define LINK_AUTO_BW_MSI	BIT(21)
+#define LINK_AUTO_BW_INT	BIT(20)
+#define PME_MSI			BIT(12)
+#define PME_INT			BIT(10)
+#define RP_SUB_MASK		(BW_MGT_MSI | BW_MGT_INT | LINK_AUTO_BW_MSI | \
+				 LINK_AUTO_BW_INT | PME_MSI | PME_INT)
+
 /* PCIe DMA Interrupt Status Enable */
 #define PCIEDMAINTSTSEN		0x0314
 #define PCIEDMAINTSTSEN_INIT	GENMASK(15, 0)
@@ -76,7 +86,8 @@
 
 /* PCIe Interrupt Status 0 Clear */
 #define PCIEINTSTS0CLR		0x0340
-#define RP_LATCHED_MASK		AER_RC_ERR_MSI
+#define RP_LATCHED_MASK		(AER_RC_ERR_MSI | BW_MGT_MSI | LINK_AUTO_BW_MSI | \
+				 PME_MSI)
 
 /* Port Logic Registers 89 */
 #define PRTLGC89		0x0b70
@@ -137,7 +148,7 @@ struct rcar_gen4_pcie {
 	struct platform_device *pdev;
 	struct reset_control *perst;
 	const struct rcar_gen4_pcie_drvdata *drvdata;
-	/* intreq_pcim_sub ("msi"): iMSI-RX and other controller notifications */
+	/* intreq_pcim_sub ("msi"): iMSI-RX, reset requests, Root Port PME/bandwidth */
 	int msi_irq;
 	struct work_struct link_down_work;
 	/* Allow Root Port event handling after enumeration and until teardown. */
@@ -597,7 +608,7 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
 	lockdep_assert_held(&rcar->app_lock);
 
 	val = readl(rcar->base + PCIEINTSTS0EN);
-	val &= ~(MSI_CTRL_INT | RP_AER_MASK);
+	val &= ~(MSI_CTRL_INT | RP_AER_MASK | RP_SUB_MASK);
 	/*
 	 * Note that rcar->rp_events_armed is not checked here because device
 	 * removal may still need iMSI-RX interrupts after Root Port events have
@@ -607,6 +618,8 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
 		val |= MSI_CTRL_INT;
 	if (rp_events && rcar->rp_irq_enabled && rcar->aer_irq > 0)
 		val |= RP_AER_MASK;
+	if (rp_events && rcar->rp_irq_enabled)
+		val |= RP_SUB_MASK;
 	writel(val, rcar->base + PCIEINTSTS0EN);
 
 	val = readl(rcar->base + PCIERSTSTSEN);
@@ -753,12 +766,12 @@ static irqreturn_t rcar_gen4_pcie_msi_irq_handler(int irq, void *data)
 
 	status = readl(rcar->base + PCIEINTSTS0) &
 		 readl(rcar->base + PCIEINTSTS0EN);
-	if (!(status & MSI_CTRL_INT))
-		return IRQ_NONE;
+	if (status & MSI_CTRL_INT)
+		dw_handle_msi_irq(&rcar->dw.pp);
+	if (status & RP_SUB_MASK)
+		rcar_gen4_pcie_rp_irq_dispatch(rcar);
 
-	dw_handle_msi_irq(&rcar->dw.pp);
-
-	return IRQ_HANDLED;
+	return status & (MSI_CTRL_INT | RP_SUB_MASK) ? IRQ_HANDLED : IRQ_NONE;
 }
 
 static int rcar_gen4_pcie_msi_irq_init(struct rcar_gen4_pcie *rcar)
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 15/15] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (13 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 14/15] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
@ 2026-09-28 16:52 ` Koichiro Den
  2026-10-03 20:00   ` 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
  15 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-28 16:52 UTC (permalink / raw)
  To: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

The PCIe controllers report Root Port AER notifications through
intreq_pci0_lperr (SPI 421) and intreq_pci1_lperr (SPI 428),
respectively, separately from the integrated MSI receiver interrupts.
Describe these interrupts with the "aer" name so the controller driver
can use them for Root Port error reporting.

Append the new interrupts to preserve the existing interrupt indices.

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
  - Add Marek's Reviewed-by tag. No code changes.

 arch/arm64/boot/dts/renesas/r8a779f0.dtsi | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/boot/dts/renesas/r8a779f0.dtsi b/arch/arm64/boot/dts/renesas/r8a779f0.dtsi
index 16a83b1749b4..b9de7c795e7c 100644
--- a/arch/arm64/boot/dts/renesas/r8a779f0.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a779f0.dtsi
@@ -785,8 +785,9 @@ pciec0: pcie@e65d0000 {
 			interrupts = <GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 417 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>;
-			interrupt-names = "msi", "dma", "sft_ce", "app";
+				     <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 421 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "msi", "dma", "sft_ce", "app", "aer";
 			clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
 			clock-names = "core", "ref";
 			power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
@@ -824,8 +825,9 @@ pciec1: pcie@e65d8000 {
 			interrupts = <GIC_SPI 423 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 425 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 429 IRQ_TYPE_LEVEL_HIGH>;
-			interrupt-names = "msi", "dma", "sft_ce", "app";
+				     <GIC_SPI 429 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 428 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "msi", "dma", "sft_ce", "app", "aer";
 			clocks = <&cpg CPG_MOD 625>, <&pcie1_clkref>;
 			clock-names = "core", "ref";
 			power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
-- 
2.51.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt
  2026-09-28 16:52 ` [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
@ 2026-09-29 17:43   ` Geert Uytterhoeven
  2026-09-30  6:38     ` Koichiro Den
  2026-10-04  0:53   ` Marek Vasut
  1 sibling, 1 reply; 39+ messages in thread
From: Geert Uytterhoeven @ 2026-09-29 17:43 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut
  Cc: Yoshihiro Shimoda, Lorenzo Pieralisi, Krzysztof Wilczyński,
	Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
	Krzysztof Kozlowski, Conor Dooley, Geert Uytterhoeven,
	Magnus Damm, Jingoo Han, Philipp Zabel, Frank Li, Niklas Cassel,
	Wilfred Mallawa, Serge Semin, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel

Hi Den-san, Marek,

On Mon, 28 Sept 2026 at 18:53, Koichiro Den <den@valinux.co.jp> wrote:
> On R-Car Gen4, intreq_pcim_sub ("msi") carries more than the integrated
> MSI receiver: the controller's reset requests and the Root Port's PME
> and bandwidth notifications are signalled on the same line, and the
> following patches need to handle them. With the DesignWare core owning
> the line through its chained handler, the driver would have to hook into
> that handler when iMSI-RX is used and request the line itself otherwise.
>
> Instead, request the interrupt in the driver in all configurations and
> set pp->msi_irq[0] to -ENODEV so the core does not install its chained
> handler, as spear13xx, keembay and dra7xx do. The handler demultiplexes
> the MSIs through dw_handle_msi_irq() when the APP block reports
> msi_ctrl_int. With an external MSI controller or pci=nomsi the iMSI-RX
> is not set up, so keep msi_ctrl_int masked rather than enabled, and the
> handler has nothing to do there yet. Request the interrupt before
> enumeration, as endpoint drivers may use MSIs from their probe, with
> IRQF_NO_THREAD so the MSIs are demultiplexed in hard IRQ context like
> the chained handler did. The interrupt is required by the binding.
>
> Release the interrupt in .deinit, before asserting the controller reset
> and disabling its clocks. Disable it around a Root Port reset because
> the handler accesses the MSI status registers through DBI.
>
> The DT routes downstream INTx to the same line, but the driver has never
> supported INTx (no INTx domain, INTx enables never set), so requesting
> the line exclusively takes nothing away.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>

Thanks for your patch!

FTR, this interacts badly with "[PATCH v2] PCI: rcar-gen4: Add missing
PM ops"[1] during resume from s2idle:

    Unbalanced enable for IRQ 57
    WARNING: kernel/irq/manage.c:775 at __enable_irq+0x38/0x64, CPU#3:
s2idle/686
    Modules linked in:
    CPU: 3 UID: 0 PID: 686 Comm: s2idle Not tainted
7.3.0-rc5-rcar3-09216-ga295839bfe55 #696 PREEMPT
    Hardware name: Renesas Gray Hawk Single board based on r8a779h0 (DT)
    pstate: 604000c5 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
    pc : __enable_irq+0x38/0x64
    lr : __enable_irq+0x38/0x64
    sp : ffffffc08904bab0
    x29: ffffffc08904bab0 x28: ffffff8446a4a880 x27: ffffffc084e64828
    x26: ffffffc084e64838 x25: ffffffc08069e630 x24: ffffff8443ad7cb0
    x23: 0000000000000000 x22: 0000000000000000 x21: 0000000000000070
    x20: 0000000000000039 x19: ffffff8443ad7c00 x18: 00000000e630bb27
    x17: 0000000000000000 x16: 0000000000000000 x15: 0720072007200720
    x14: 0720072007200720 x13: 0720072007200720 x12: 0000000000000566
    x11: 0000000000000000 x10: ffffffc08418a038 x9 : ffffffc08158a090
    x8 : ffffffc08904b798 x7 : ffffffc08904b7a0 x6 : 3ffffffffff7ffff
    x5 : fffffffffff7ffff x4 : 0000000000000000 x3 : 0000000000000000
    x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffffff8446a4a880
    Call trace:
     __enable_irq+0x38/0x64 (P)
     resume_irqs+0xf0/0x130
     resume_device_irqs+0x10/0x18
     dpm_resume_noirq+0xe8/0x190
     suspend_devices_and_enter+0x524/0x594
     pm_suspend+0x22c/0x270
     state_store+0xa8/0xe8
     kobj_attr_store+0x14/0x24
     sysfs_kf_write+0x4c/0x64
     kernfs_fop_write_iter+0x13c/0x184
     vfs_write+0x148/0x1b4
     ksys_write+0x78/0xe0
     __arm64_sys_write+0x14/0x1c
     invoke_syscall+0xa0/0x100
     el0_svc_common.constprop.0+0xb0/0xcc
     do_el0_svc+0x18/0x20
     el0_svc+0x3c/0x114
     el0t_64_sync_handler+0x58/0x134
     el0t_64_sync+0x158/0x15c
    irq event stamp: 0
    hardirqs last  enabled at (0): [<0000000000000000>] 0x0
    hardirqs last disabled at (0): [<ffffffc080092a2c>]
copy_process+0x9e0/0x17b8
    softirqs last  enabled at (0): [<ffffffc080092a34>]
copy_process+0x9e8/0x17b8
    softirqs last disabled at (0): [<0000000000000000>] 0x0
    ---[ end trace 0000000000000000 ]---

[1] https://lore.kernel.org/20260907153711.653861-1-marek.vasut+renesas@mailbox.org/

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt
  2026-09-29 17:43   ` Geert Uytterhoeven
@ 2026-09-30  6:38     ` Koichiro Den
  2026-10-04  1:17       ` Marek Vasut
  0 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-09-30  6:38 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han, Philipp Zabel,
	Frank Li, Niklas Cassel, Wilfred Mallawa, Serge Semin, linux-pci,
	linux-renesas-soc, devicetree, linux-kernel

On Tue, Sep 29, 2026 at 07:43:28PM +0200, Geert Uytterhoeven wrote:
> Hi Den-san, Marek,
> 
> On Mon, 28 Sept 2026 at 18:53, Koichiro Den <den@valinux.co.jp> wrote:
> > On R-Car Gen4, intreq_pcim_sub ("msi") carries more than the integrated
> > MSI receiver: the controller's reset requests and the Root Port's PME
> > and bandwidth notifications are signalled on the same line, and the
> > following patches need to handle them. With the DesignWare core owning
> > the line through its chained handler, the driver would have to hook into
> > that handler when iMSI-RX is used and request the line itself otherwise.
> >
> > Instead, request the interrupt in the driver in all configurations and
> > set pp->msi_irq[0] to -ENODEV so the core does not install its chained
> > handler, as spear13xx, keembay and dra7xx do. The handler demultiplexes
> > the MSIs through dw_handle_msi_irq() when the APP block reports
> > msi_ctrl_int. With an external MSI controller or pci=nomsi the iMSI-RX
> > is not set up, so keep msi_ctrl_int masked rather than enabled, and the
> > handler has nothing to do there yet. Request the interrupt before
> > enumeration, as endpoint drivers may use MSIs from their probe, with
> > IRQF_NO_THREAD so the MSIs are demultiplexed in hard IRQ context like
> > the chained handler did. The interrupt is required by the binding.
> >
> > Release the interrupt in .deinit, before asserting the controller reset
> > and disabling its clocks. Disable it around a Root Port reset because
> > the handler accesses the MSI status registers through DBI.
> >
> > The DT routes downstream INTx to the same line, but the driver has never
> > supported INTx (no INTx domain, INTx enables never set), so requesting
> > the line exclusively takes nothing away.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> 
> Thanks for your patch!
> 
> FTR, this interacts badly with "[PATCH v2] PCI: rcar-gen4: Add missing
> PM ops"[1] during resume from s2idle:

Hi Geert, thanks for the heads-up.

I'll fix this in v3. I'm planning to address this together with the "Known gap"
noted below the commit message, by moving IRQ setup/teardown out of
.init()/.deinit(). On hindsight, I should have done so in v2. That should also
avoid freeing and re-requesting IRQs during suspend/resume, without needing
IRQF_NO_SUSPEND.

By the way, AFAIK Marek has posted v4 of the PM ops patch:
https://lore.kernel.org/r/20260922175525.288106-1-marek.vasut+renesas@mailbox.org/

I had only (re-)tested v3 on the EP side (not RC side):
https://lore.kernel.org/r/ain27ypszl767h3fygurivurojxctyjy2yprada6lgjef5c623@23nn7tsrecb6/
but while looking at your report and reproducing, I noticed that
rcar_gen4_pcie_host_ops lacks .pme_turn_off implementation,
so I wonder if it should also set 'pp->use_atu_msg = true'.

I mean, something like this:

------8<-------8<------
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 1f8821e3a424..b8237962de18 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -671,6 +671,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
                return -ENODEV;

        pp->num_vectors = MAX_MSI_IRQS;
+       /* Reserve an iATU window for the generic PME_Turn_Off implementation. */
+       pp->use_atu_msg = true;
        pp->ops = &rcar_gen4_pcie_host_ops;

        return dw_pcie_host_init(pp);

------8<-------8<------

Marek, I'd appreciate your thoughts on this too.
Perhaps this small change could be included in v5, if you agree.

Best regards,
Koichiro

> 
>     Unbalanced enable for IRQ 57
>     WARNING: kernel/irq/manage.c:775 at __enable_irq+0x38/0x64, CPU#3:
> s2idle/686
>     Modules linked in:
>     CPU: 3 UID: 0 PID: 686 Comm: s2idle Not tainted
> 7.3.0-rc5-rcar3-09216-ga295839bfe55 #696 PREEMPT
>     Hardware name: Renesas Gray Hawk Single board based on r8a779h0 (DT)
>     pstate: 604000c5 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
>     pc : __enable_irq+0x38/0x64
>     lr : __enable_irq+0x38/0x64
>     sp : ffffffc08904bab0
>     x29: ffffffc08904bab0 x28: ffffff8446a4a880 x27: ffffffc084e64828
>     x26: ffffffc084e64838 x25: ffffffc08069e630 x24: ffffff8443ad7cb0
>     x23: 0000000000000000 x22: 0000000000000000 x21: 0000000000000070
>     x20: 0000000000000039 x19: ffffff8443ad7c00 x18: 00000000e630bb27
>     x17: 0000000000000000 x16: 0000000000000000 x15: 0720072007200720
>     x14: 0720072007200720 x13: 0720072007200720 x12: 0000000000000566
>     x11: 0000000000000000 x10: ffffffc08418a038 x9 : ffffffc08158a090
>     x8 : ffffffc08904b798 x7 : ffffffc08904b7a0 x6 : 3ffffffffff7ffff
>     x5 : fffffffffff7ffff x4 : 0000000000000000 x3 : 0000000000000000
>     x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffffff8446a4a880
>     Call trace:
>      __enable_irq+0x38/0x64 (P)
>      resume_irqs+0xf0/0x130
>      resume_device_irqs+0x10/0x18
>      dpm_resume_noirq+0xe8/0x190
>      suspend_devices_and_enter+0x524/0x594
>      pm_suspend+0x22c/0x270
>      state_store+0xa8/0xe8
>      kobj_attr_store+0x14/0x24
>      sysfs_kf_write+0x4c/0x64
>      kernfs_fop_write_iter+0x13c/0x184
>      vfs_write+0x148/0x1b4
>      ksys_write+0x78/0xe0
>      __arm64_sys_write+0x14/0x1c
>      invoke_syscall+0xa0/0x100
>      el0_svc_common.constprop.0+0xb0/0xcc
>      do_el0_svc+0x18/0x20
>      el0_svc+0x3c/0x114
>      el0t_64_sync_handler+0x58/0x134
>      el0t_64_sync+0x158/0x15c
>     irq event stamp: 0
>     hardirqs last  enabled at (0): [<0000000000000000>] 0x0
>     hardirqs last disabled at (0): [<ffffffc080092a2c>]
> copy_process+0x9e0/0x17b8
>     softirqs last  enabled at (0): [<ffffffc080092a34>]
> copy_process+0x9e8/0x17b8
>     softirqs last disabled at (0): [<0000000000000000>] 0x0
>     ---[ end trace 0000000000000000 ]---
> 
> [1] https://lore.kernel.org/20260907153711.653861-1-marek.vasut+renesas@mailbox.org/
> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 
> -- 
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> 
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 03/15] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
  2026-09-28 16:52 ` [PATCH v2 03/15] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
@ 2026-09-30 10:31   ` Krzysztof Kozlowski
  2026-10-03 20:00   ` Marek Vasut
  1 sibling, 0 replies; 39+ messages in thread
From: Krzysztof Kozlowski @ 2026-09-30 10:31 UTC (permalink / raw)
  To: Koichiro Den
  Cc: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han, Philipp Zabel,
	Frank Li, Niklas Cassel, Wilfred Mallawa, Serge Semin, linux-pci,
	linux-renesas-soc, devicetree, linux-kernel

On Tue, Sep 29, 2026 at 01:52:18AM +0900, Koichiro Den wrote:
> The intreq_pcim_lperr interrupt carries Root Port AER notifications,
> separately from the integrated MSI receiver interrupt. Describe it with
> the "aer" name so the controller driver can use it for Root Port error
> reporting.
> 
> Keep the fifth interrupt optional so existing device trees remain
> valid. Extend the example accordingly.
> 
> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts
  2026-09-28 16:52 [PATCH v2 00/15] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
                   ` (14 preceding siblings ...)
  2026-09-28 16:52 ` [PATCH v2 15/15] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
@ 2026-10-03 17:46 ` Marek Vasut
  15 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 17:46 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> Hi,
> 
> This series improves error handling on the R-Car Gen4 PCIe host
> controller (tested on R-Car S4 Spider, r8a779f0). It fixes unexpected
> link-down handling so the host doesn't hang, and wires up missing Root
> Port interrupts (AER, PME, bandwidth notifications) so port services
> actually work.
> 
> A few hardware quirks made this tricky:
> 
> 1. PCIEINTSTS0 link-up bits don't track the actual link state. The
>     driver never noticed when the link dropped and kept trying config
>     accesses on the dead link. Patch 2 combines the APP link-up event
>     check from Figure 104.5 with the PORT_DEBUG1 live link check. Startup
>     clears the APP latches before enabling LTSSM, and the DWC core polls
>     the combined condition on the RC side.
> 
> 2. On S4, a DBI access immediately after an unexpected link down can
>     hang the host. Commit 0056d29f8c1b ("PCI: rcar-gen4: Assure reset
>     occurs before DBI access") describes an SError on V4H after reset
>     deassertion, but whether the S4 hang shares the same underlying cause
>     has not been verified.
> 
>     Adding a delay before the DBI access avoided the hang in my tests,
>     but that alone would not provide link-down recovery. Also, the reset
>     request shares intreq_pcim_sub with iMSI-RX, while AER arrives on
>     another IRQ. Delaying AER dispatch alone would therefore leave the
>     MSI handler exposed, and both paths would still need coordination
>     with the controller reset.
> 
>     The driver handles the reset request itself and schedules
>     pci_host_handle_link_down(), as the rockchip and qcom drivers do.
>     This also provides recovery with older DTs that have no "aer"
>     interrupt, without relying on AER to initiate it. The reset callback
>     shares the reset sequence used at probe, including the reset-status
>     readback and delay added by 0056d29f8c1b.
> 
> 3. Root Port interrupts only trigger APP status bits on platform IRQs.
>     The controller appears to lack SII2MSI, so Root Port MSIs never reach
>     the GIC ITS. The Root Port's INTx is routed to intreq_pcim_sub as
>     well, which the driver holds, so port services can't request it
>     either. This series works around it by hiding Root Port MSI caps
>     across the board and emulating INTx using a virtual IRQ domain, fed
>     by the "aer" IRQ and intreq_pcim_sub.
> 
> Patch 1 is only loosely related: it adds Renesas to the RAS DES VSEC
> list so the DWC debugfs error injection works on R-Car. I used it to
> test the Root Port AER path (see below) and included it here for that
> reason. Happy to send it separately if preferred.
> 
> Based on next-20260925. The driver patches build on 7fc9907223d6 ("PCI:
> rcar-gen4: Add Application/Local register reset control") in pci/next,
> and the DTS patch is for renesas-devel, which already has b43aa6a6ebe8
> ("arm64: dts: renesas: r8a779f0: Add GICv3 ITS and update PCIe nodes");
> configurations b and c below use that DT.
> 
> Note: backward compatibility with older DTs is kept. Without the "aer"
>        interrupt, only Root Port AER remains unavailable. See the Testing
>        section below.
> 
> Retesting with v2
> -----------------
> 
> Setup: R-Car S4 Spider (RC) linked to another S4 Spider running the
>         pci-epf-test endpoint. pci_endpoint_test is bound on the RC side.
> 
>    1. Link down / recovery. On the EP side, toggle the endpoint controller
>       off and on. The short pause keeps the endpoint away long enough for
>       the RC to notice, but brings it back within the reset window so
>       recovery can succeed. Adopted the test approach from [1]:
> 
>       # cd /sys/kernel/config/pci_ep
>       # echo 0 > controllers/e65d0000.pcie-ep/start
>       # sleep 0.1
>       # echo 1 > controllers/e65d0000.pcie-ep/start
> 
>       Expected on the RC dmesg:
>         pcieport 0000:00:00.0: Recovering Root Port due to Link Down
>         pcieport 0000:00:00.0: Root Port has been reset
>         pcieport 0000:00:00.0: AER: device recovery successful
> 
>       and the "msi" (intreq_pcim_sub) interrupt count going up in
>       /proc/interrupts. Without this series nothing shows up here: the
>       link comes back on its own once the endpoint returns, but the RC
>       never notices the outage and the endpoint is left unconfigured
>       (see 4). Config accesses issued while the link is down hang the
>       host.
> 
>       [1] https://lore.kernel.org/r/abFMa6DCGGLUHddA@fedora/
> 
>    2. Bandwidth notification. On the RC, retrain the link:
> 
>       # setpci -s 00:00.0 CAP_EXP+0x10.w=0x0c23
> 
>       Expected:
>       - the virtual Root Port IRQ (rcar-gen4-rp in /proc/interrupts,
>         shared by PCIe PME, aerdrv and PCIe bwctrl) fires once
>       - bwctrl clears LnkSta.LBMS
>         (setpci -s 00:00.0 CAP_EXP+0x12.w reads 0x2024 again).
> 
>       Before the series LnkSta read 0xe024 afterwards, LBMS and LABS
>       stuck.
> 
>    3. Root Port AER. On the RC, inject an LCRC error with the DWC debugfs
>       (patch 1) and issue one config read so a TLP actually goes out:
> 
>       # cd /sys/kernel/debug/dwc_pcie_e65d0000.pcie/rasdes_err_inj
>       # echo 1 > rx_lcrc            # error detected by the Root Port
>       # setpci -s 01:00.0 VENDOR_ID.w
>       # echo 1 > tx_lcrc            # error detected by the endpoint,
>       # setpci -s 01:00.0 VENDOR_ID.w    # reported back with ERR_COR
> 
>       Expected on the RC dmesg, respectively:
>        pcieport 0000:00:00.0: PCIe Bus Error: severity=Correctable
>        pcieport 0000:00:00.0:   [ 6] BadTLP | Receiver | Data Link Layer
> 
>        pcieport 0000:00:00.0: AER: Correctable Error message received from 0000:01:00.0
>        pci-endpoint-test 0000:01:00.0: PCIe Bus Error: severity=Correctable
>        pci-endpoint-test 0000:01:00.0:   [ 6] BadTLP | Receiver | Data Link Layer
> 
>       plus the virtual Root Port IRQ count and aer_rootport_total_err_cor
>       going up by one each time. The link stays up throughout, the DLL
>       retry recovers the TLP. Before the series nothing is reported.
> 
>    4. Regression check. Run pci_endpoint_test after step 1. PASS/FAIL/SKIP
>       counts match a run without step 1.
> 
> Configurations:
> 
>    a. Without this series**
>    b. GIC ITS, DT with the new "aer" interrupt (this series)
>    c. GIC ITS, DT without "aer" (b43aa6a6ebe8 ("arm64: dts: renesas:
>       r8a779f0: Add GICv3 ITS and update PCIe nodes") or later)
>    d. iMSI-RX, DT before b43aa6a6ebe8 (no msi-parent, no "aer")
> 
> Result:
>               recovery   bwctrl/PME   RP AER    pcitest
>    ---------------------------------------------------------
>    a.           none         no         no      all FAIL
>    b.            ok          ok         ok      no change
>    c.            ok          ok         n/a*    no change
>    d.            ok          ok         n/a*    no change
> 
>    *  Root Port AER needs the "aer" interrupt; without it the behaviour is
>       unchanged from before the series.
>    ** Only patch 1 applied on top of the base, so the same debugfs error
>       injection could be used for the comparison. pci_endpoint_test fails
>       across the board there because nothing restores the endpoint after
>       the toggle.
This is one awesome cover letter.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up()
  2026-09-28 16:52 ` [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
@ 2026-10-03 18:29   ` Marek Vasut
  2026-10-03 18:51     ` Marek Vasut
  0 siblings, 1 reply; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 18:29 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> rcar_gen4_pcie_link_up() checks link state using SMLH_LINK_UP and
> RDLH_LINK_UP in PCIEINTSTS0. However, these bits do not reflect the live
> link state. On an R-Car S4, after taking down the endpoint, a link-down
> interrupt saw PCIEINTSTS0 = 0x20a000c5 with both bits still set. Even
> after resetting the controller with the LTSSM back in Polling, they read
> 0xa000c5, still set.
> 
> As a result, dw_pcie_link_up() keeps reporting the link as up after it
> has gone down. That defeats the check in dw_pcie_other_conf_map_bus(),
> which is supposed to stop config accesses to downstream devices while
> the link is down, so such accesses go out on the dead link and stall the
> host. It also makes the callback useless for the link-down recovery
> added later, which has to wait for the link to actually come back after
> resetting the controller.
> 
> Keep the APP link-up event check from Figure 104.5 of the R-Car S4
> reference manual, but also require PORT_DEBUG1 to report the link up and
> not in training. The callback then rejects a downed link even if the APP
> link-up events remain latched.
> 
> Clear the APP latches before enabling LTSSM to discard events from a
> previous start, and only read them in .link_up(). RC startup uses
> dw_pcie_wait_for_link() to poll the combined condition.
> 
> Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode")
> Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>

I apologize for the late reply.

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Tested-by: Marek Vasut <marek.vasut+renesas@mailbox.org> # R-Car V4H

Since next 20261002 now contains commit

f29719b5064c ("PCI: dwc: Align register macros with Synopsys documentation")

This will need the following slight adjustment:

"s@PCIE_PORT_DEBUG@PORT_LINK_DEBUG@g"

"
-       val = dw_pcie_readl_dbi(dw, PCIE_PORT_DEBUG1);
-       return (val & PCIE_PORT_DEBUG1_LINK_UP) &&
-              !(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING);
+       val = dw_pcie_readl_dbi(dw, PORT_LINK_DEBUG1);
+       return (val & PORT_LINK_DEBUG1_LINK_UP) &&
+              !(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING);
"

Thank you for your help !

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up()
  2026-10-03 18:29   ` Marek Vasut
@ 2026-10-03 18:51     ` Marek Vasut
  2026-10-05  4:22       ` Koichiro Den
  0 siblings, 1 reply; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 18:51 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 10/3/26 8:29 PM, Marek Vasut wrote:
> On 9/28/26 6:52 PM, Koichiro Den wrote:
>> rcar_gen4_pcie_link_up() checks link state using SMLH_LINK_UP and
>> RDLH_LINK_UP in PCIEINTSTS0. However, these bits do not reflect the live
>> link state. On an R-Car S4, after taking down the endpoint, a link-down
>> interrupt saw PCIEINTSTS0 = 0x20a000c5 with both bits still set. Even
>> after resetting the controller with the LTSSM back in Polling, they read
>> 0xa000c5, still set.
>>
>> As a result, dw_pcie_link_up() keeps reporting the link as up after it
>> has gone down. That defeats the check in dw_pcie_other_conf_map_bus(),
>> which is supposed to stop config accesses to downstream devices while
>> the link is down, so such accesses go out on the dead link and stall the
>> host. It also makes the callback useless for the link-down recovery
>> added later, which has to wait for the link to actually come back after
>> resetting the controller.
>>
>> Keep the APP link-up event check from Figure 104.5 of the R-Car S4
>> reference manual, but also require PORT_DEBUG1 to report the link up and
>> not in training. The callback then rejects a downed link even if the APP
>> link-up events remain latched.
>>
>> Clear the APP latches before enabling LTSSM to discard events from a
>> previous start, and only read them in .link_up(). RC startup uses
>> dw_pcie_wait_for_link() to poll the combined condition.
>>
>> Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller 
>> support for host mode")
>> Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
>> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> 
> I apologize for the late reply.
> 
> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Tested-by: Marek Vasut <marek.vasut+renesas@mailbox.org> # R-Car V4H
> 
> Since next 20261002 now contains commit
> 
> f29719b5064c ("PCI: dwc: Align register macros with Synopsys 
> documentation")
> 
> This will need the following slight adjustment:
> 
> "s@PCIE_PORT_DEBUG@PORT_LINK_DEBUG@g"
> 
> "
> -       val = dw_pcie_readl_dbi(dw, PCIE_PORT_DEBUG1);
> -       return (val & PCIE_PORT_DEBUG1_LINK_UP) &&
> -              !(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING);
> +       val = dw_pcie_readl_dbi(dw, PORT_LINK_DEBUG1);
> +       return (val & PORT_LINK_DEBUG1_LINK_UP) &&
> +              !(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING);
> "

A small nitpick, would the following change make sense to reduce 
duplication a bit ?

diff --git a/drivers/pci/controller/dwc/pcie-designware.c 
b/drivers/pci/controller/dwc/pcie-designware.c
index c726aa71c830c..52bdb1fcc2db2 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -817,17 +817,23 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
  }
  EXPORT_SYMBOL_GPL(dw_pcie_wait_for_link);

-bool dw_pcie_link_up(struct dw_pcie *pci)
+bool dw_pcie_link_up_debug_check(struct dw_pcie *pci)
  {
         u32 val;

-       if (pci->ops && pci->ops->link_up)
-               return pci->ops->link_up(pci);
-
         val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG1);
         return ((val & PORT_LINK_DEBUG1_LINK_UP) &&
                 (!(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING)));
  }
+EXPORT_SYMBOL_GPL(dw_pcie_link_up_debug_check);
+
+bool dw_pcie_link_up(struct dw_pcie *pci)
+{
+       if (pci->ops && pci->ops->link_up)
+               return pci->ops->link_up(pci);
+
+       return dw_pcie_link_up_debug_check(pci);
+}
  EXPORT_SYMBOL_GPL(dw_pcie_link_up);

  void dw_pcie_upconfig_setup(struct dw_pcie *pci)
diff --git a/drivers/pci/controller/dwc/pcie-designware.h 
b/drivers/pci/controller/dwc/pcie-designware.h
index 4199324882800..2ce61709b58c2 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -618,6 +618,7 @@ int dw_pcie_write(void __iomem *addr, int size, u32 
val);
  u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size);
  void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 
val);
  void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 
val);
+bool dw_pcie_link_up_debug_check(struct dw_pcie *pci);
  bool dw_pcie_link_up(struct dw_pcie *pci);
  void dw_pcie_upconfig_setup(struct dw_pcie *pci);
  int dw_pcie_wait_for_link(struct dw_pcie *pci);
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c 
b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index dbc0115885afc..b2ed1a329c418 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -127,9 +127,7 @@ static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
                 return false;

         /* The APP link-up events remain latched after the link goes 
down. */
-       val = dw_pcie_readl_dbi(dw, PORT_LINK_DEBUG1);
-       return (val & PORT_LINK_DEBUG1_LINK_UP) &&
-              !(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING);
+       return dw_pcie_link_up_debug_check(dw);
  }

  /*

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 15/15] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts
  2026-09-28 16:52 ` [PATCH v2 15/15] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
@ 2026-10-03 20:00   ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 20:00 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> The PCIe controllers report Root Port AER notifications through
> intreq_pci0_lperr (SPI 421) and intreq_pci1_lperr (SPI 428),
> respectively, separately from the integrated MSI receiver interrupts.
> Describe these interrupts with the "aer" name so the controller driver
> can use them for Root Port error reporting.
> 
> Append the new interrupts to preserve the existing interrupt indices.
> 
> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Please also feel free to add this patch for R-Car V4H (performed cover 
letter tests 2 and 3 on Retronix Sparrow Hawk)

diff --git a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi 
b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
index 5284923b6cef7..8342d3c6f9881 100644
--- a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
@@ -801,8 +801,9 @@ pciec0: pcie@e65d0000 {
                         interrupts = <GIC_SPI 449 IRQ_TYPE_LEVEL_HIGH>,
                                      <GIC_SPI 450 IRQ_TYPE_LEVEL_HIGH>,
                                      <GIC_SPI 451 IRQ_TYPE_LEVEL_HIGH>,
-                                    <GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>;
-                       interrupt-names = "msi", "dma", "sft_ce", "app";
+                                    <GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 454 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "msi", "dma", "sft_ce", "app", 
"aer";
                         clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
                         clock-names = "core", "ref";
                         power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
@@ -850,8 +851,9 @@ pciec1: pcie@e65d8000 {
                         interrupts = <GIC_SPI 456 IRQ_TYPE_LEVEL_HIGH>,
                                      <GIC_SPI 457 IRQ_TYPE_LEVEL_HIGH>,
                                      <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH>,
-                                    <GIC_SPI 462 IRQ_TYPE_LEVEL_HIGH>;
-                       interrupt-names = "msi", "dma", "sft_ce", "app";
+                                    <GIC_SPI 462 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 461 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "msi", "dma", "sft_ce", "app", 
"aer";
                         clocks = <&cpg CPG_MOD 625>, <&pcie1_clkref>;
                         clock-names = "core", "ref";
                         power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;

And a matching patch for R-Car V4M (boot tested, but the board has no 
PCIe device plugged into the slot)

diff --git a/arch/arm64/boot/dts/renesas/r8a779h0.dtsi 
b/arch/arm64/boot/dts/renesas/r8a779h0.dtsi
index 5f9223a0f4468..a377aab3c8be0 100644
--- a/arch/arm64/boot/dts/renesas/r8a779h0.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a779h0.dtsi
@@ -683,8 +683,9 @@ pciec0: pcie@e65d0000 {
                         interrupts = <GIC_SPI 449 IRQ_TYPE_LEVEL_HIGH>,
                                      <GIC_SPI 450 IRQ_TYPE_LEVEL_HIGH>,
                                      <GIC_SPI 451 IRQ_TYPE_LEVEL_HIGH>,
-                                    <GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>;
-                       interrupt-names = "msi", "dma", "sft_ce", "app";
+                                    <GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 454 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "msi", "dma", "sft_ce", "app", 
"aer";
                         clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
                         clock-names = "core", "ref";
                         power-domains = <&sysc R8A779H0_PD_A2PCIPHY>;

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 03/15] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
  2026-09-28 16:52 ` [PATCH v2 03/15] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
  2026-09-30 10:31   ` Krzysztof Kozlowski
@ 2026-10-03 20:00   ` Marek Vasut
  1 sibling, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 20:00 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> The intreq_pcim_lperr interrupt carries Root Port AER notifications,
> separately from the integrated MSI receiver interrupt. Describe it with
> the "aer" name so the controller driver can use it for Root Port error
> reporting.
> 
> Keep the fifth interrupt optional so existing device trees remain
> valid. Extend the example accordingly.
> 
> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 04/15] PCI: dwc: Export dw_handle_msi_irq()
  2026-09-28 16:52 ` [PATCH v2 04/15] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
@ 2026-10-03 20:02   ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 20:02 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> Not every platform brings the iMSI-RX interrupt out as a dedicated
> line. On spear13xx and keembay it is a status bit in the SoC wrapper's
> interrupt register that the glue driver has to check and clear, and on
> dra7xx it shares the line with other sources the glue driver has to
> demultiplex. These drivers set msi_irq[0] to -ENODEV so the core does
> not install its chained handler, and service the MSIs from a handler of
> their own. spear13xx and keembay call dw_handle_msi_irq() for that.
> Both are built-in, so the function was never exported. dra7xx can be a
> module and has an MSI loop of its own.
> 
> Export the function so that modular glue drivers can use it as well.
> The R-Car Gen4 driver, where both situations apply, is the first user.
> 
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 05/15] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization
  2026-09-28 16:52 ` [PATCH v2 05/15] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization Koichiro Den
@ 2026-10-03 20:23   ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 20:23 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> Move common_deinit() next to common_init(), ahead of the Gen5 .init()
> that will call it when PHY initialization fails. Place the Gen5 .deinit()
> before the Gen5 initialization code as well.
> 
> Keep these moves separate from the following initialization refactoring
> to make that patch easier to review. The function bodies are unchanged.
> 
> No functional changes.
> 
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 06/15] PCI: rcar-gen4: Assert resets when Gen5 PHY initialization fails
  2026-09-28 16:52 ` [PATCH v2 06/15] PCI: rcar-gen4: Assert resets when Gen5 PHY initialization fails Koichiro Den
@ 2026-10-03 21:34   ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 21:34 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> common_init() enables the clocks and deasserts the power and
> Application/Local resets before phy_init(). If phy_init() fails, the
> error path only disables the clocks, leaving those resets deasserted.
> 
> Assert both resets before disabling the clocks to unwind the completed
> common initialization.
> 
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes in v2:
>    - New patch. Place the fix(?) before the initialization refactoring.
> 
> Marek, I looked through v1-v5 of your X5H series, but I'm not yet sure,
> was there a specific reason to leave resets deasserted when phy_init()
> fails? If not, this fixes the unwind before the restructuring in the
> following patches.
I agree, this change is correct, and should have a Fixes: tag .

Fixes: 3283c4db6009 ("PCI: rcar-gen4: Add support for R-Car X5H PCIe4")

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

Thank you for spotting this !

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 07/15] PCI: rcar-gen4: Separate hardware setup from resource acquisition
  2026-09-28 16:52 ` [PATCH v2 07/15] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
@ 2026-10-03 23:41   ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 23:41 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> Factor out the hardware setup needed by Root Port recovery into helpers.
> Recovery needs to reset and restore the controller while keeping its
> clocks enabled and the Gen5 PHY initialized.
> 
> - From rcar_gen4_pcie_common_init(), extract the power reset and mode
>    setup. Leave clock enabling and Application/Local reset control in
>    common_init(), so recovery can keep the APP registers accessible.
> 
> - From the V4H/V4M and Gen5 .init() callbacks, extract the SoC-specific
>    register setup. This allows recovery to restore those settings without
>    taking another clock or PHY initialization reference.
> 
> - From rcar_gen4_pcie_host_init(), extract the Root Port register setup
>    and PERST# release. Both paths need this host-specific sequence after
>    the per-SoC initialization or reset.
> 
> No functional changes.
> 
> Signed-off-by: Koichiro Den <den@valinux.co.jp>

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 08/15] PCI: rcar-gen4: Add a controller reinitialization callback
  2026-09-28 16:52 ` [PATCH v2 08/15] PCI: rcar-gen4: Add a controller reinitialization callback Koichiro Den
@ 2026-10-03 23:54   ` Marek Vasut
  2026-10-05  4:31     ` Koichiro Den
  0 siblings, 1 reply; 39+ messages in thread
From: Marek Vasut @ 2026-10-03 23:54 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> Root Port recovery needs to reset and restore the controller while
> keeping the clocks enabled and the Gen5 PHY initialized. Reusing .init()
> would acquire these resources again, while calling .deinit() first would
> make the APP registers inaccessible to interrupt handling.
> 
> Add a per-SoC .reinit() callback that repeats the power reset and register
> setup without acquiring or releasing resources. Use the same reset and
> register setup helpers as .init().
> 
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes in v2:
>    - Adapt to the per-SoC .init()/.deinit() callbacks in next-20260925.
>      Add .reinit() for recovery without changing their resource ownership
>      or taking another PHY initialization reference.
>    - Move hardware setup extraction and Gen5 initialization cleanup into
>      preceding patches. This patch only adds the callback and its
>      implementations.
Could we introduce .configure callback instead, and deduplicate the two 
rcar_{gen4_v4h_v4m,gen5}_pcie_reinit() functions ? What do you think ?

This kind of a change (this would likely require folding this patch into 
09/15):

diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c 
b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 3d667f25fb32e..3691c2ddbf6bc 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -100,7 +100,7 @@ struct rcar_gen4_pcie_drvdata {
         int (*init)(struct rcar_gen4_pcie *rcar);
         void (*deinit)(struct rcar_gen4_pcie *rcar);
         /* Reset and restore registers while clocks and PHY remain 
initialized. */
-       int (*reinit)(struct rcar_gen4_pcie *rcar);
+       void (*configure)(struct rcar_gen4_pcie *rcar);
         int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable);
         int (*speed_control)(struct rcar_gen4_pcie *rcar);
         enum dw_pcie_device_mode mode;
@@ -326,6 +326,20 @@ static void rcar_gen4_pcie_common_deinit(struct 
rcar_gen4_pcie *rcar)
         clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
  }

+static int rcar_gen4_pcie_common_reinit(struct rcar_gen4_pcie *rcar)
+{
+       int ret;
+
+       ret = rcar_gen4_pcie_reset(rcar);
+       if (ret)
+               return ret;
+
+       if (rcar->drvdata->configure)
+               rcar->drvdata->configure(rcar);
+
+       return 0;
+}
+
  static void rcar_gen4_v4h_v4m_pcie_configure(struct rcar_gen4_pcie *rcar)
  {
         struct dw_pcie *dw = &rcar->dw;
@@ -343,19 +357,6 @@ static void rcar_gen4_v4h_v4m_pcie_configure(struct 
rcar_gen4_pcie *rcar)
         writel(val, rcar->base + PCIEPWRMNGCTRL);
  }

-static int rcar_gen4_v4h_v4m_pcie_reinit(struct rcar_gen4_pcie *rcar)
-{
-       int ret;
-
-       ret = rcar_gen4_pcie_reset(rcar);
-       if (ret)
-               return ret;
-
-       rcar_gen4_v4h_v4m_pcie_configure(rcar);
-
-       return 0;
-}
-
  static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
  {
         int ret;
@@ -400,19 +401,6 @@ static void rcar_gen5_pcie_configure(struct 
rcar_gen4_pcie *rcar)
         writel(val, rcar->base + PCIEPWRMNGCTRL);
  }

-static int rcar_gen5_pcie_reinit(struct rcar_gen4_pcie *rcar)
-{
-       int ret;
-
-       ret = rcar_gen4_pcie_reset(rcar);
-       if (ret)
-               return ret;
-
-       rcar_gen5_pcie_configure(rcar);
-
-       return 0;
-}
-
  static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
  {
         int ret;
@@ -1133,7 +1121,6 @@ static int rcar_gen4_pcie_ltssm_control(struct 
rcar_gen4_pcie *rcar, bool enable
  static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = {
         .init = rcar_gen4_pcie_common_init,
         .deinit = rcar_gen4_pcie_common_deinit,
-       .reinit = rcar_gen4_pcie_reset,
         .ltssm_control = r8a779f0_pcie_ltssm_control,
         .speed_control = rcar_gen4_pcie_speed_control,
         .mode = DW_PCIE_RC_TYPE,
@@ -1142,7 +1129,6 @@ static struct rcar_gen4_pcie_drvdata 
drvdata_r8a779f0_pcie = {
  static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = {
         .init = rcar_gen4_pcie_common_init,
         .deinit = rcar_gen4_pcie_common_deinit,
-       .reinit = rcar_gen4_pcie_reset,
         .ltssm_control = r8a779f0_pcie_ltssm_control,
         .speed_control = rcar_gen4_pcie_speed_control,
         .mode = DW_PCIE_EP_TYPE,
@@ -1151,7 +1137,7 @@ static struct rcar_gen4_pcie_drvdata 
drvdata_r8a779f0_pcie_ep = {
  static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
         .init = rcar_gen4_v4h_v4m_pcie_init,
         .deinit = rcar_gen4_pcie_common_deinit,
-       .reinit = rcar_gen4_v4h_v4m_pcie_reinit,
+       .configure = rcar_gen4_v4h_v4m_pcie_configure,
         .ltssm_control = rcar_gen4_pcie_ltssm_control,
         .speed_control = rcar_gen4_pcie_speed_control,
         .mode = DW_PCIE_RC_TYPE,
@@ -1160,7 +1146,7 @@ static struct rcar_gen4_pcie_drvdata 
drvdata_rcar_gen4_pcie = {
  static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
         .init = rcar_gen4_v4h_v4m_pcie_init,
         .deinit = rcar_gen4_pcie_common_deinit,
-       .reinit = rcar_gen4_v4h_v4m_pcie_reinit,
+       .configure = rcar_gen4_v4h_v4m_pcie_configure,
         .ltssm_control = rcar_gen4_pcie_ltssm_control,
         .speed_control = rcar_gen4_pcie_speed_control,
         .mode = DW_PCIE_EP_TYPE,
@@ -1169,7 +1155,7 @@ static struct rcar_gen4_pcie_drvdata 
drvdata_rcar_gen4_pcie_ep = {
  static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen5_pcie = {
         .init = rcar_gen5_pcie_init,
         .deinit = rcar_gen5_pcie_deinit,
-       .reinit = rcar_gen5_pcie_reinit,
+       .configure = rcar_gen5_pcie_configure,
         .ltssm_control = rcar_gen5_pcie_ltssm_control,
         .speed_control = rcar_gen5_pcie_speed_control,
         .mode = DW_PCIE_RC_TYPE,


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 09/15] PCI: rcar-gen4: Add Root Port reset support
  2026-09-28 16:52 ` [PATCH v2 09/15] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
@ 2026-10-04  0:11   ` Marek Vasut
  2026-10-05  6:14     ` Koichiro Den
  0 siblings, 1 reply; 39+ messages in thread
From: Marek Vasut @ 2026-10-04  0:11 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> Implement the host bridge reset_root_port() callback so PCI error
> recovery can reset and reinitialize the R-Car controller. This also
> provides the reset operation for the link-down handling added later.
> 
> Call .reinit() with clocks and PHY initialization retained, restore
> the Root Port registers and restart link training.
> 
> Rather than tracking which APP interrupt enables survive the power
> reset, derive them from software state through a single helper. A flag
> keeps the sources masked from the start of a reset until one succeeds,
> so a failed reinitialization does not re-enable them against an
> uninitialized controller.
> 
> Serialize the reset with a mutex, as not all callers hold the Root
> Port's device lock: pci_try_reset_function() on a downstream device only
> locks that device before falling back to a parent bus reset.

Please pardon my ignorance, but is this maybe something that could be 
fixed in the core code ?

[...]

> +/*
> + * R-Car Gen4 controllers have a single Root Port per instance, so the

I have two nitpicks here.

First, this is also applicable to R-Car Gen5 SoC PCIe4 controller, so 
please rephrase as:

-R-Car Gen4 controllers ...
+R-Car Gen4 SoC PCIe controllers and R-Car Gen5 SoC PCIe4 controller ...

Second, in another review thread, Bjorn mentioned it would be good to be 
more explicit about what is SoC generation and what is PCIe generation:

https://lore.kernel.org/all/20260928222442.GA2266778@bhelgaas/

That is also why I used such a lengthy sentence above, that is

                               Controllers are here
                                         |
                        _________________^__________________
                       |                                    |
                vvvvvvvvvvvvvvvv                    vvvvvvvvvvvvvvvv
R-Car Gen4 SoC PCIe controllers and R-Car Gen5 SoC PCIe4 controller ...
       ^^^^^^^^                            ^^^^^^^^
           |                                   |
           '----------------- -----------------'
                             V
                             |
                    SoC generation is here

> + * 'pci_dev' is ignored and the whole controller is reset.
> + */
> +static int rcar_gen4_pcie_reset_root_port(struct pci_host_bridge *bridge,
> +					  struct pci_dev *pdev)
> +{
> +	struct rcar_gen4_pcie *rcar = dev_get_drvdata(bridge->dev.parent);
> +	struct dw_pcie *dw = &rcar->dw;
> +	struct dw_pcie_rp *pp = &dw->pp;
> +	struct device *dev = dw->dev;
> +	int ret;
The rest looks good, thank you !

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt
  2026-09-28 16:52 ` [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
  2026-09-29 17:43   ` Geert Uytterhoeven
@ 2026-10-04  0:53   ` Marek Vasut
  1 sibling, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-04  0:53 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:

[...]

> @@ -724,6 +773,9 @@ static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
>   		rcar->reinit_pending = true;
>   		rcar_gen4_pcie_app_irq_sync_locked(rcar);
>   	}
> +
> +	/* The MSI status lives in DBI; keep the handler away during the reset. */

Would it make sense to add lockdep_assert_held(&rcar->reset_lock) here, 
to make it clear that this IRQ disable is protected by the reset lock ?

> +	disable_irq(rcar->msi_irq);
>   }
>   
>   static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
> @@ -733,6 +785,8 @@ static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
>   		rcar->reinit_pending = !recovered;
>   		rcar_gen4_pcie_app_irq_sync_locked(rcar);
>   	}
> +

Would it make sense to add lockdep_assert_held(&rcar->reset_lock) here, 
to make it clear that this IRQ enable is protected by the reset lock ?

> +	enable_irq(rcar->msi_irq);
>   }
>   
>   /*
> @@ -804,11 +858,17 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
>   
>   	ret = rcar_gen4_pcie_host_setup(pp);
>   	if (ret)
> -		goto err;
> +		goto err_deinit;
> +
> +	ret = rcar_gen4_pcie_msi_irq_init(rcar);

Would it make sense to acquire the IRQ a bit earlier, so you could avoid 
rcar_gen4_pcie_host_perst_assert(pp, true); in the fail path ?

I think if the MSI acquisition fails, perst signal would pulse (rapid 
sequence of deassert and assert), and that could be avoided.

> +	if (ret)
> +		goto err_assert_perst;
>   
>   	return 0;
>   
> -err:
> +err_assert_perst:
> +	rcar_gen4_pcie_host_perst_assert(pp, true);
> +err_deinit:
>   	rcar->drvdata->deinit(rcar);
>   	return ret;
>   }
> @@ -818,6 +878,9 @@ static void rcar_gen4_pcie_host_deinit(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);
>   
> +	/* Stop the handler before asserting reset and disabling the clocks. */
> +	free_irq(rcar->msi_irq, rcar);
> +
>   	rcar_gen4_pcie_host_perst_assert(pp, true);
>   	rcar->drvdata->deinit(rcar);
Shouldn't the IRQ be released only after reset is asserted and clock are 
stopped , otherwise it might accidentally fire and cause unhandled IRQ 
event ?

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 10/15] PCI: rcar-gen4: Take over the iMSI-RX interrupt
  2026-09-30  6:38     ` Koichiro Den
@ 2026-10-04  1:17       ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-04  1:17 UTC (permalink / raw)
  To: Koichiro Den, Geert Uytterhoeven
  Cc: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han, Philipp Zabel,
	Frank Li, Niklas Cassel, Wilfred Mallawa, Serge Semin, linux-pci,
	linux-renesas-soc, devicetree, linux-kernel

On 9/30/26 8:38 AM, Koichiro Den wrote:
> On Tue, Sep 29, 2026 at 07:43:28PM +0200, Geert Uytterhoeven wrote:
>> Hi Den-san, Marek,
>>
>> On Mon, 28 Sept 2026 at 18:53, Koichiro Den <den@valinux.co.jp> wrote:
>>> On R-Car Gen4, intreq_pcim_sub ("msi") carries more than the integrated
>>> MSI receiver: the controller's reset requests and the Root Port's PME
>>> and bandwidth notifications are signalled on the same line, and the
>>> following patches need to handle them. With the DesignWare core owning
>>> the line through its chained handler, the driver would have to hook into
>>> that handler when iMSI-RX is used and request the line itself otherwise.
>>>
>>> Instead, request the interrupt in the driver in all configurations and
>>> set pp->msi_irq[0] to -ENODEV so the core does not install its chained
>>> handler, as spear13xx, keembay and dra7xx do. The handler demultiplexes
>>> the MSIs through dw_handle_msi_irq() when the APP block reports
>>> msi_ctrl_int. With an external MSI controller or pci=nomsi the iMSI-RX
>>> is not set up, so keep msi_ctrl_int masked rather than enabled, and the
>>> handler has nothing to do there yet. Request the interrupt before
>>> enumeration, as endpoint drivers may use MSIs from their probe, with
>>> IRQF_NO_THREAD so the MSIs are demultiplexed in hard IRQ context like
>>> the chained handler did. The interrupt is required by the binding.
>>>
>>> Release the interrupt in .deinit, before asserting the controller reset
>>> and disabling its clocks. Disable it around a Root Port reset because
>>> the handler accesses the MSI status registers through DBI.
>>>
>>> The DT routes downstream INTx to the same line, but the driver has never
>>> supported INTx (no INTx domain, INTx enables never set), so requesting
>>> the line exclusively takes nothing away.
>>>
>>> Signed-off-by: Koichiro Den <den@valinux.co.jp>
>>
>> Thanks for your patch!
>>
>> FTR, this interacts badly with "[PATCH v2] PCI: rcar-gen4: Add missing
>> PM ops"[1] during resume from s2idle:
> 
> Hi Geert, thanks for the heads-up.
> 
> I'll fix this in v3. I'm planning to address this together with the "Known gap"
> noted below the commit message, by moving IRQ setup/teardown out of
> .init()/.deinit(). On hindsight, I should have done so in v2. That should also
> avoid freeing and re-requesting IRQs during suspend/resume, without needing
> IRQF_NO_SUSPEND.
> 
> By the way, AFAIK Marek has posted v4 of the PM ops patch:
> https://lore.kernel.org/r/20260922175525.288106-1-marek.vasut+renesas@mailbox.org/
> 
> I had only (re-)tested v3 on the EP side (not RC side):
> https://lore.kernel.org/r/ain27ypszl767h3fygurivurojxctyjy2yprada6lgjef5c623@23nn7tsrecb6/
> but while looking at your report and reproducing, I noticed that
> rcar_gen4_pcie_host_ops lacks .pme_turn_off implementation,
> so I wonder if it should also set 'pp->use_atu_msg = true'.
> 
> I mean, something like this:
> 
> ------8<-------8<------
> diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> index 1f8821e3a424..b8237962de18 100644
> --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> @@ -671,6 +671,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
>                  return -ENODEV;
> 
>          pp->num_vectors = MAX_MSI_IRQS;
> +       /* Reserve an iATU window for the generic PME_Turn_Off implementation. */
> +       pp->use_atu_msg = true;
>          pp->ops = &rcar_gen4_pcie_host_ops;
> 
>          return dw_pcie_host_init(pp);
> 
> ------8<-------8<------
> 
> Marek, I'd appreciate your thoughts on this too.
> Perhaps this small change could be included in v5, if you agree.
This does make sense, yes, thank you for spotting this. I will be 
sending a V5 of the PM ops patch now.

[...]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 11/15] PCI: rcar-gen4: Recover the Root Port on link down
  2026-09-28 16:52 ` [PATCH v2 11/15] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
@ 2026-10-04  1:38   ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-04  1:38 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> 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>

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 13/15] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ
  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-10-04  2:52   ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-04  2:52 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:
> On R-Car Gen4, the APP block exposes the Root Port interrupt signals in
> PCIEINTSTS0 and routes them to platform IRQs: AER to intreq_pcim_lperr,
> and PME and bandwidth notifications to intreq_pcim_sub. On an R-Car S4
> with GIC ITS, a link retrain set LnkSta.LBMS and latched PCIEINTSTS0[23]
> (cfg_bw_mgt_msi), but the Root Port's MSI never fired while endpoint
> MSIs worked fine. With iMSI-RX the Root Port MSI capabilities are
> already hidden, but its INTx maps to intreq_pcim_sub. Note that even
> before this driver took over the IRQ with
> rcar_gen4_pcie_msi_irq_handler, the port service was unable to request
> it, as the DWC core used that IRQ for its chained MSI handler. In either
> case (ie. iMSI-RX or an external MSI controller), Root Port interrupts
> are not delivered to the port services.
> 
> Set no_rp_msi so the port services always fall back to INTx. Allocate an
> IRQ in a single-entry domain and return its number from the bridge's
> map_irq callback for the Root Port only, leaving downstream INTx
> mappings unchanged. Notifications from different platform IRQs can then
> feed the one IRQ the port services share. Route the AER notifications
> from the "aer" interrupt to it. rcar_gen4_pcie_host_msi_init() sets
> MSICAP0.MSIE when pci_msi_enabled() is true, and hiding the capability
> does not clear it, so handle both the MSI and INTx forms of the status
> bits. DTs without "aer" remain supported, but the APP AER notification
> sources stay masked.
> 
> Preserve the virtual IRQ's enable state across controller resets, but
> keep its APP sources masked while reinitialization is pending or Root
> Port events are disarmed. Clear only the MSI-form APP latch, before the
> AER handler clears Root Error Status, to avoid clearing a newly latched
> notification.
> 
> Handle pending reset requests before dispatching AER to avoid unsafe DBI
> access. Serialize dispatches from the parent IRQs, including the "msi"
> path added by the next patch, since handle_level_irq() could otherwise
> acknowledge a concurrent interrupt without running its handlers. Recheck
> reset requests after taking the lock in case the other parent has marked
> reinitialization as pending.
> 
> Signed-off-by: Koichiro Den <den@valinux.co.jp>

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 14/15] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications
  2026-09-28 16:52 ` [PATCH v2 14/15] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
@ 2026-10-04  4:40   ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-04  4:40 UTC (permalink / raw)
  To: Koichiro Den, Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han
  Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
	Serge Semin, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel

On 9/28/26 6:52 PM, Koichiro Den wrote:

[...]

> @@ -753,12 +766,12 @@ static irqreturn_t rcar_gen4_pcie_msi_irq_handler(int irq, void *data)
>   
>   	status = readl(rcar->base + PCIEINTSTS0) &
>   		 readl(rcar->base + PCIEINTSTS0EN);
> -	if (!(status & MSI_CTRL_INT))
> -		return IRQ_NONE;
> +	if (status & MSI_CTRL_INT)
> +		dw_handle_msi_irq(&rcar->dw.pp);
> +	if (status & RP_SUB_MASK)
> +		rcar_gen4_pcie_rp_irq_dispatch(rcar);
>   
> -	dw_handle_msi_irq(&rcar->dw.pp);
> -
> -	return IRQ_HANDLED;
> +	return status & (MSI_CTRL_INT | RP_SUB_MASK) ? IRQ_HANDLED : IRQ_NONE;

Only a small nitpick, please don't use the ternary operator, readability 
suffers, please do:

"
if (status & (MSI_CTRL_INT | RP_SUB_MASK))
   return IRQ_HANDLED;

return IRQ_NONE;
"

with that fixed,

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

Thank you !

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up()
  2026-10-03 18:51     ` Marek Vasut
@ 2026-10-05  4:22       ` Koichiro Den
  2026-10-05  5:59         ` Marek Vasut
  0 siblings, 1 reply; 39+ messages in thread
From: Koichiro Den @ 2026-10-05  4:22 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han, Philipp Zabel,
	Frank Li, Niklas Cassel, Wilfred Mallawa, Serge Semin, linux-pci,
	linux-renesas-soc, devicetree, linux-kernel

On Sat, Oct 03, 2026 at 08:51:58PM +0200, Marek Vasut wrote:
> On 10/3/26 8:29 PM, Marek Vasut wrote:
> > On 9/28/26 6:52 PM, Koichiro Den wrote:
> > > rcar_gen4_pcie_link_up() checks link state using SMLH_LINK_UP and
> > > RDLH_LINK_UP in PCIEINTSTS0. However, these bits do not reflect the live
> > > link state. On an R-Car S4, after taking down the endpoint, a link-down
> > > interrupt saw PCIEINTSTS0 = 0x20a000c5 with both bits still set. Even
> > > after resetting the controller with the LTSSM back in Polling, they read
> > > 0xa000c5, still set.
> > > 
> > > As a result, dw_pcie_link_up() keeps reporting the link as up after it
> > > has gone down. That defeats the check in dw_pcie_other_conf_map_bus(),
> > > which is supposed to stop config accesses to downstream devices while
> > > the link is down, so such accesses go out on the dead link and stall the
> > > host. It also makes the callback useless for the link-down recovery
> > > added later, which has to wait for the link to actually come back after
> > > resetting the controller.
> > > 
> > > Keep the APP link-up event check from Figure 104.5 of the R-Car S4
> > > reference manual, but also require PORT_DEBUG1 to report the link up and
> > > not in training. The callback then rejects a downed link even if the APP
> > > link-up events remain latched.
> > > 
> > > Clear the APP latches before enabling LTSSM to discard events from a
> > > previous start, and only read them in .link_up(). RC startup uses
> > > dw_pcie_wait_for_link() to poll the combined condition.
> > > 
> > > Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller
> > > support for host mode")
> > > Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > 

Hi Marek,

> > I apologize for the late reply.
> > 
> > Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> > Tested-by: Marek Vasut <marek.vasut+renesas@mailbox.org> # R-Car V4H

Thanks for the review and for testing on V4H.

> > 
> > Since next 20261002 now contains commit
> > 
> > f29719b5064c ("PCI: dwc: Align register macros with Synopsys
> > documentation")
> > 
> > This will need the following slight adjustment:
> > 
> > "s@PCIE_PORT_DEBUG@PORT_LINK_DEBUG@g"
> > 
> > "
> > -       val = dw_pcie_readl_dbi(dw, PCIE_PORT_DEBUG1);
> > -       return (val & PCIE_PORT_DEBUG1_LINK_UP) &&
> > -              !(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING);
> > +       val = dw_pcie_readl_dbi(dw, PORT_LINK_DEBUG1);
> > +       return (val & PORT_LINK_DEBUG1_LINK_UP) &&
> > +              !(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING);
> > "

Thanks for the heads-up. v3 will be rebased onto the latest -next and use the
new names.

> 
> A small nitpick, would the following change make sense to reduce duplication
> a bit ?

Yes. In v3 I'm planning to add it as a separate patch right before this one,
with your Suggested-by.

Best regards,
Koichiro

> 
> diff --git a/drivers/pci/controller/dwc/pcie-designware.c
> b/drivers/pci/controller/dwc/pcie-designware.c
> index c726aa71c830c..52bdb1fcc2db2 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.c
> +++ b/drivers/pci/controller/dwc/pcie-designware.c
> @@ -817,17 +817,23 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
>  }
>  EXPORT_SYMBOL_GPL(dw_pcie_wait_for_link);
> 
> -bool dw_pcie_link_up(struct dw_pcie *pci)
> +bool dw_pcie_link_up_debug_check(struct dw_pcie *pci)
>  {
>         u32 val;
> 
> -       if (pci->ops && pci->ops->link_up)
> -               return pci->ops->link_up(pci);
> -
>         val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG1);
>         return ((val & PORT_LINK_DEBUG1_LINK_UP) &&
>                 (!(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING)));
>  }
> +EXPORT_SYMBOL_GPL(dw_pcie_link_up_debug_check);
> +
> +bool dw_pcie_link_up(struct dw_pcie *pci)
> +{
> +       if (pci->ops && pci->ops->link_up)
> +               return pci->ops->link_up(pci);
> +
> +       return dw_pcie_link_up_debug_check(pci);
> +}
>  EXPORT_SYMBOL_GPL(dw_pcie_link_up);
> 
>  void dw_pcie_upconfig_setup(struct dw_pcie *pci)
> diff --git a/drivers/pci/controller/dwc/pcie-designware.h
> b/drivers/pci/controller/dwc/pcie-designware.h
> index 4199324882800..2ce61709b58c2 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.h
> +++ b/drivers/pci/controller/dwc/pcie-designware.h
> @@ -618,6 +618,7 @@ int dw_pcie_write(void __iomem *addr, int size, u32
> val);
>  u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size);
>  void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val);
>  void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32
> val);
> +bool dw_pcie_link_up_debug_check(struct dw_pcie *pci);
>  bool dw_pcie_link_up(struct dw_pcie *pci);
>  void dw_pcie_upconfig_setup(struct dw_pcie *pci);
>  int dw_pcie_wait_for_link(struct dw_pcie *pci);
> diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> index dbc0115885afc..b2ed1a329c418 100644
> --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> @@ -127,9 +127,7 @@ static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
>                 return false;
> 
>         /* The APP link-up events remain latched after the link goes down.
> */
> -       val = dw_pcie_readl_dbi(dw, PORT_LINK_DEBUG1);
> -       return (val & PORT_LINK_DEBUG1_LINK_UP) &&
> -              !(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING);
> +       return dw_pcie_link_up_debug_check(dw);
>  }
> 
>  /*

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 08/15] PCI: rcar-gen4: Add a controller reinitialization callback
  2026-10-03 23:54   ` Marek Vasut
@ 2026-10-05  4:31     ` Koichiro Den
  0 siblings, 0 replies; 39+ messages in thread
From: Koichiro Den @ 2026-10-05  4:31 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han, Philipp Zabel,
	Frank Li, Niklas Cassel, Wilfred Mallawa, Serge Semin, linux-pci,
	linux-renesas-soc, devicetree, linux-kernel

On Sun, Oct 04, 2026 at 01:54:59AM +0200, Marek Vasut wrote:
> On 9/28/26 6:52 PM, Koichiro Den wrote:
> > Root Port recovery needs to reset and restore the controller while
> > keeping the clocks enabled and the Gen5 PHY initialized. Reusing .init()
> > would acquire these resources again, while calling .deinit() first would
> > make the APP registers inaccessible to interrupt handling.
> > 
> > Add a per-SoC .reinit() callback that repeats the power reset and register
> > setup without acquiring or releasing resources. Use the same reset and
> > register setup helpers as .init().
> > 
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > Changes in v2:
> >    - Adapt to the per-SoC .init()/.deinit() callbacks in next-20260925.
> >      Add .reinit() for recovery without changing their resource ownership
> >      or taking another PHY initialization reference.
> >    - Move hardware setup extraction and Gen5 initialization cleanup into
> >      preceding patches. This patch only adds the callback and its
> >      implementations.
> Could we introduce .configure callback instead, and deduplicate the two
> rcar_{gen4_v4h_v4m,gen5}_pcie_reinit() functions ? What do you think ?

Agreed, that is cleaner. Thanks for the suggestion.

> 
> This kind of a change (this would likely require folding this patch into
> 09/15):

Yes. With that change rcar_gen4_pcie_common_reinit() would have no caller in
this patch and compiler would complain, so yes, I'll fold this patch into 09/15.

Thanks for the review!

Best regards,
Koichiro Den

> 
> diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> index 3d667f25fb32e..3691c2ddbf6bc 100644
> --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> @@ -100,7 +100,7 @@ struct rcar_gen4_pcie_drvdata {
>         int (*init)(struct rcar_gen4_pcie *rcar);
>         void (*deinit)(struct rcar_gen4_pcie *rcar);
>         /* Reset and restore registers while clocks and PHY remain
> initialized. */
> -       int (*reinit)(struct rcar_gen4_pcie *rcar);
> +       void (*configure)(struct rcar_gen4_pcie *rcar);
>         int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable);
>         int (*speed_control)(struct rcar_gen4_pcie *rcar);
>         enum dw_pcie_device_mode mode;
> @@ -326,6 +326,20 @@ static void rcar_gen4_pcie_common_deinit(struct
> rcar_gen4_pcie *rcar)
>         clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
>  }
> 
> +static int rcar_gen4_pcie_common_reinit(struct rcar_gen4_pcie *rcar)
> +{
> +       int ret;
> +
> +       ret = rcar_gen4_pcie_reset(rcar);
> +       if (ret)
> +               return ret;
> +
> +       if (rcar->drvdata->configure)
> +               rcar->drvdata->configure(rcar);
> +
> +       return 0;
> +}
> +
>  static void rcar_gen4_v4h_v4m_pcie_configure(struct rcar_gen4_pcie *rcar)
>  {
>         struct dw_pcie *dw = &rcar->dw;
> @@ -343,19 +357,6 @@ static void rcar_gen4_v4h_v4m_pcie_configure(struct
> rcar_gen4_pcie *rcar)
>         writel(val, rcar->base + PCIEPWRMNGCTRL);
>  }
> 
> -static int rcar_gen4_v4h_v4m_pcie_reinit(struct rcar_gen4_pcie *rcar)
> -{
> -       int ret;
> -
> -       ret = rcar_gen4_pcie_reset(rcar);
> -       if (ret)
> -               return ret;
> -
> -       rcar_gen4_v4h_v4m_pcie_configure(rcar);
> -
> -       return 0;
> -}
> -
>  static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
>  {
>         int ret;
> @@ -400,19 +401,6 @@ static void rcar_gen5_pcie_configure(struct
> rcar_gen4_pcie *rcar)
>         writel(val, rcar->base + PCIEPWRMNGCTRL);
>  }
> 
> -static int rcar_gen5_pcie_reinit(struct rcar_gen4_pcie *rcar)
> -{
> -       int ret;
> -
> -       ret = rcar_gen4_pcie_reset(rcar);
> -       if (ret)
> -               return ret;
> -
> -       rcar_gen5_pcie_configure(rcar);
> -
> -       return 0;
> -}
> -
>  static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
>  {
>         int ret;
> @@ -1133,7 +1121,6 @@ static int rcar_gen4_pcie_ltssm_control(struct
> rcar_gen4_pcie *rcar, bool enable
>  static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = {
>         .init = rcar_gen4_pcie_common_init,
>         .deinit = rcar_gen4_pcie_common_deinit,
> -       .reinit = rcar_gen4_pcie_reset,
>         .ltssm_control = r8a779f0_pcie_ltssm_control,
>         .speed_control = rcar_gen4_pcie_speed_control,
>         .mode = DW_PCIE_RC_TYPE,
> @@ -1142,7 +1129,6 @@ static struct rcar_gen4_pcie_drvdata
> drvdata_r8a779f0_pcie = {
>  static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = {
>         .init = rcar_gen4_pcie_common_init,
>         .deinit = rcar_gen4_pcie_common_deinit,
> -       .reinit = rcar_gen4_pcie_reset,
>         .ltssm_control = r8a779f0_pcie_ltssm_control,
>         .speed_control = rcar_gen4_pcie_speed_control,
>         .mode = DW_PCIE_EP_TYPE,
> @@ -1151,7 +1137,7 @@ static struct rcar_gen4_pcie_drvdata
> drvdata_r8a779f0_pcie_ep = {
>  static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
>         .init = rcar_gen4_v4h_v4m_pcie_init,
>         .deinit = rcar_gen4_pcie_common_deinit,
> -       .reinit = rcar_gen4_v4h_v4m_pcie_reinit,
> +       .configure = rcar_gen4_v4h_v4m_pcie_configure,
>         .ltssm_control = rcar_gen4_pcie_ltssm_control,
>         .speed_control = rcar_gen4_pcie_speed_control,
>         .mode = DW_PCIE_RC_TYPE,
> @@ -1160,7 +1146,7 @@ static struct rcar_gen4_pcie_drvdata
> drvdata_rcar_gen4_pcie = {
>  static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
>         .init = rcar_gen4_v4h_v4m_pcie_init,
>         .deinit = rcar_gen4_pcie_common_deinit,
> -       .reinit = rcar_gen4_v4h_v4m_pcie_reinit,
> +       .configure = rcar_gen4_v4h_v4m_pcie_configure,
>         .ltssm_control = rcar_gen4_pcie_ltssm_control,
>         .speed_control = rcar_gen4_pcie_speed_control,
>         .mode = DW_PCIE_EP_TYPE,
> @@ -1169,7 +1155,7 @@ static struct rcar_gen4_pcie_drvdata
> drvdata_rcar_gen4_pcie_ep = {
>  static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen5_pcie = {
>         .init = rcar_gen5_pcie_init,
>         .deinit = rcar_gen5_pcie_deinit,
> -       .reinit = rcar_gen5_pcie_reinit,
> +       .configure = rcar_gen5_pcie_configure,
>         .ltssm_control = rcar_gen5_pcie_ltssm_control,
>         .speed_control = rcar_gen5_pcie_speed_control,
>         .mode = DW_PCIE_RC_TYPE,
> 

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up()
  2026-10-05  4:22       ` Koichiro Den
@ 2026-10-05  5:59         ` Marek Vasut
  0 siblings, 0 replies; 39+ messages in thread
From: Marek Vasut @ 2026-10-05  5:59 UTC (permalink / raw)
  To: Koichiro Den
  Cc: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han, Philipp Zabel,
	Frank Li, Niklas Cassel, Wilfred Mallawa, Serge Semin, linux-pci,
	linux-renesas-soc, devicetree, linux-kernel

On 10/5/26 6:22 AM, Koichiro Den wrote:

Hello Den-san,

>>> I apologize for the late reply.
>>>
>>> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
>>> Tested-by: Marek Vasut <marek.vasut+renesas@mailbox.org> # R-Car V4H
> 
> Thanks for the review and for testing on V4H.
> 
>>>
>>> Since next 20261002 now contains commit
>>>
>>> f29719b5064c ("PCI: dwc: Align register macros with Synopsys
>>> documentation")
>>>
>>> This will need the following slight adjustment:
>>>
>>> "s@PCIE_PORT_DEBUG@PORT_LINK_DEBUG@g"
>>>
>>> "
>>> -       val = dw_pcie_readl_dbi(dw, PCIE_PORT_DEBUG1);
>>> -       return (val & PCIE_PORT_DEBUG1_LINK_UP) &&
>>> -              !(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING);
>>> +       val = dw_pcie_readl_dbi(dw, PORT_LINK_DEBUG1);
>>> +       return (val & PORT_LINK_DEBUG1_LINK_UP) &&
>>> +              !(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING);
>>> "
> 
> Thanks for the heads-up. v3 will be rebased onto the latest -next and use the
> new names.
> 
>>
>> A small nitpick, would the following change make sense to reduce duplication
>> a bit ?
> 
> Yes. In v3 I'm planning to add it as a separate patch right before this one,
> with your Suggested-by.
Thank you !

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 09/15] PCI: rcar-gen4: Add Root Port reset support
  2026-10-04  0:11   ` Marek Vasut
@ 2026-10-05  6:14     ` Koichiro Den
  0 siblings, 0 replies; 39+ messages in thread
From: Koichiro Den @ 2026-10-05  6:14 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Marek Vasut, Yoshihiro Shimoda, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Jingoo Han, Philipp Zabel,
	Frank Li, Niklas Cassel, Wilfred Mallawa, Serge Semin, linux-pci,
	linux-renesas-soc, devicetree, linux-kernel

On Sun, Oct 04, 2026 at 02:11:37AM +0200, Marek Vasut wrote:
> On 9/28/26 6:52 PM, Koichiro Den wrote:
> > Implement the host bridge reset_root_port() callback so PCI error
> > recovery can reset and reinitialize the R-Car controller. This also
> > provides the reset operation for the link-down handling added later.
> > 
> > Call .reinit() with clocks and PHY initialization retained, restore
> > the Root Port registers and restart link training.
> > 
> > Rather than tracking which APP interrupt enables survive the power
> > reset, derive them from software state through a single helper. A flag
> > keeps the sources masked from the start of a reset until one succeeds,
> > so a failed reinitialization does not re-enable them against an
> > uninitialized controller.
> > 
> > Serialize the reset with a mutex, as not all callers hold the Root
> > Port's device lock: pci_try_reset_function() on a downstream device only
> > locks that device before falling back to a parent bus reset.
> 
> Please pardon my ignorance, but is this maybe something that could be fixed
> in the core code ?

Yes. pci_reset_function() has locked the upstream bridge since 7e89efc6e9e4
("PCI: Lock upstream bridge for pci_reset_function()"), but
pci_try_reset_function() and pci_reset_function_locked() still do not. I am not
sure whether this is intentional, but in either case, that is a core matter and
separate from this series.

However: more to the point, your question made me look at this again, and I now
believe the mutex (rcar->reset_lock) was not needed in the first place. Every
path to reset_root_port() already holds a device lock that excludes the others:
- error recovery and bus resets go through pci_bus_lock(), which locks the Root
  Port and every device below it, and
- pci_try_reset_function() (even without the upstream bridge locked, as I noted
  above) holds the lock of the downstream device, which pci_parent_bus_reset()
  only accepts when it is the only device on the bus.

So I will drop the mutex in v3. Thanks for making me look at this again, it
really helps!

> 
> [...]
> 
> > +/*
> > + * R-Car Gen4 controllers have a single Root Port per instance, so the
> 
> I have two nitpicks here.
> 
> First, this is also applicable to R-Car Gen5 SoC PCIe4 controller, so please
> rephrase as:
> 
> -R-Car Gen4 controllers ...
> +R-Car Gen4 SoC PCIe controllers and R-Car Gen5 SoC PCIe4 controller ...
> 
> Second, in another review thread, Bjorn mentioned it would be good to be
> more explicit about what is SoC generation and what is PCIe generation:
> 
> https://lore.kernel.org/all/20260928222442.GA2266778@bhelgaas/
> 
> That is also why I used such a lengthy sentence above, that is
> 
>                               Controllers are here
>                                         |
>                        _________________^__________________
>                       |                                    |
>                vvvvvvvvvvvvvvvv                    vvvvvvvvvvvvvvvv
> R-Car Gen4 SoC PCIe controllers and R-Car Gen5 SoC PCIe4 controller ...
>       ^^^^^^^^                            ^^^^^^^^
>           |                                   |
>           '----------------- -----------------'
>                             V
>                             |
>                    SoC generation is here

That all makes sense. Thanks for the advice and for pointing me to the context.
I'll use your wording in the comment and all relevant commit messages in v3.

Best regards,
Koichiro Den

> 
> > + * 'pci_dev' is ignored and the whole controller is reset.
> > + */
> > +static int rcar_gen4_pcie_reset_root_port(struct pci_host_bridge *bridge,
> > +					  struct pci_dev *pdev)
> > +{
> > +	struct rcar_gen4_pcie *rcar = dev_get_drvdata(bridge->dev.parent);
> > +	struct dw_pcie *dw = &rcar->dw;
> > +	struct dw_pcie_rp *pp = &dw->pp;
> > +	struct device *dev = dw->dev;
> > +	int ret;
> The rest looks good, thank you !

^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2026-10-05  6:14 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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:52 ` [PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
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-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-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-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-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-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-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-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-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-09-28 16:52 ` [PATCH v2 11/15] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
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:52 ` [PATCH v2 13/15] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
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-10-04  4:40   ` Marek Vasut
2026-09-28 16:52 ` [PATCH v2 15/15] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
2026-10-03 20:00   ` 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®