* [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts
@ 2026-09-18 3:20 Koichiro Den
2026-09-18 3:20 ` [PATCH 01/11] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
` (10 more replies)
0 siblings, 11 replies; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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 drops this broken callback in
favor of the DWC core check.
2. On link down, the controller's reset request shares the
intreq_pcim_sub line with the integrated MSI receiver. Accessing DBI
right after link down hangs the bus. Recovery has to hook into the
chained MSI handler before DBI is touched and reset the controller
via reset_root_port(). This works both with iMSI-RX and external MSI
controllers (GIC ITS).
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. With iMSI-RX, INTx gets routed to the chained MSI
handler where port services can't request it. 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-20260915. The driver patches build on f0a5c6bd797c ("PCI:
rcar-gen4: Limit Max_Read_Request_Size to 256 Bytes") 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.
Testing
-------
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. Adpoted 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
Koichiro Den (11):
PCI: dwc: Add Renesas to the RAS DES VSEC list
PCI: rcar-gen4: Drop the APP-based link_up check
dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
PCI: dwc: Add a host op to run before iMSI-RX status is read
PCI: rcar-gen4: Split reusable hardware initialization
PCI: rcar-gen4: Add Root Port reset support
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 +-
drivers/pci/controller/dwc/Kconfig | 1 +
.../pci/controller/dwc/pcie-designware-host.c | 9 +-
drivers/pci/controller/dwc/pcie-designware.h | 8 +
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 673 ++++++++++++++++--
include/linux/pcie-dwc.h | 2 +
7 files changed, 652 insertions(+), 61 deletions(-)
base-commit: e6e35979777d646fe3c7c94dca7dd32fb25d45f4
--
2.51.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 01/11] PCI: dwc: Add Renesas to the RAS DES VSEC list
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-22 19:40 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 02/11] PCI: rcar-gen4: Drop the APP-based link_up check Koichiro Den
` (9 subsequent siblings)
10 siblings, 1 reply; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
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] 21+ messages in thread
* [PATCH 02/11] PCI: rcar-gen4: Drop the APP-based link_up check
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-09-18 3:20 ` [PATCH 01/11] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-22 20:56 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 03/11] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
` (8 subsequent siblings)
10 siblings, 1 reply; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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.
Drop the callback and let the DesignWare core use its PORT_DEBUG1 check
instead, which correctly detects the downed link.
Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode")
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 5a076aa3f490..fe1f1940e809 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -44,8 +44,6 @@
/* PCIe Interrupt Status 0 Enable */
#define PCIEINTSTS0EN 0x0310
#define MSI_CTRL_INT BIT(26)
-#define SMLH_LINK_UP BIT(7)
-#define RDLH_LINK_UP BIT(6)
/* PCIe DMA Interrupt Status Enable */
#define PCIEDMAINTSTSEN 0x0314
@@ -102,17 +100,6 @@ struct rcar_gen4_pcie {
#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
/* Common */
-static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
-{
- struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
- u32 val, mask;
-
- val = readl(rcar->base + PCIEINTSTS0);
- mask = RDLH_LINK_UP | SMLH_LINK_UP;
-
- return (val & mask) == mask;
-}
-
/*
* Manually initiate the speed change. Return 0 if change succeeded; otherwise
* -ETIMEDOUT.
@@ -298,7 +285,6 @@ static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar)
static const struct dw_pcie_ops dw_pcie_ops = {
.start_link = rcar_gen4_pcie_start_link,
.stop_link = rcar_gen4_pcie_stop_link,
- .link_up = rcar_gen4_pcie_link_up,
};
static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev)
--
2.51.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 03/11] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-09-18 3:20 ` [PATCH 01/11] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
2026-09-18 3:20 ` [PATCH 02/11] PCI: rcar-gen4: Drop the APP-based link_up check Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-22 20:59 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 04/11] PCI: dwc: Add a host op to run before iMSI-RX status is read Koichiro Den
` (7 subsequent siblings)
10 siblings, 1 reply; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
.../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 64f456c6a75a..b0f4e6e6a662 100644
--- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
@@ -36,14 +36,17 @@ properties:
- const: config
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
@@ -106,8 +109,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] 21+ messages in thread
* [PATCH 04/11] PCI: dwc: Add a host op to run before iMSI-RX status is read
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (2 preceding siblings ...)
2026-09-18 3:20 ` [PATCH 03/11] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-18 3:20 ` [PATCH 05/11] PCI: rcar-gen4: Split reusable hardware initialization Koichiro Den
` (6 subsequent siblings)
10 siblings, 0 replies; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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
Some SoCs multiplex the integrated MSI receiver interrupt and other
notifications on the same interrupt line. On R-Car Gen4, the
intreq_pcim_sub interrupt carries both iMSI-RX MSI status and
controller reset-request notifications.
The generic chained handler reads the MSI status from DBI as soon as the
interrupt fires. That is a problem when the same line also signals a
link down: on R-Car Gen4, DBI accesses issued right after an unexpected
link down stall the host for a while, and the controller then has to be
reset before DBI can be used again. The glue driver needs to look at its
own status first and keep the generic handler away from DBI until the
controller has been recovered.
Add a host callback invoked from the chained handler before the MSI
status is read. Returning true skips MSI handling for that invocation.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pcie-designware-host.c | 3 ++-
drivers/pci/controller/dwc/pcie-designware.h | 7 +++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index f5a38e6fd8d7..ad013dd0a166 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -95,7 +95,8 @@ static void dw_chained_msi_isr(struct irq_desc *desc)
chained_irq_enter(chip, desc);
pp = irq_desc_get_handler_data(desc);
- dw_handle_msi_irq(pp);
+ if (!pp->ops->pre_msi_irq || !pp->ops->pre_msi_irq(pp))
+ dw_handle_msi_irq(pp);
chained_irq_exit(chip, desc);
}
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index a53ac27cd244..947b16086979 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -438,6 +438,13 @@ struct dw_pcie_host_ops {
void (*deinit)(struct dw_pcie_rp *pp);
void (*post_init)(struct dw_pcie_rp *pp);
int (*msi_init)(struct dw_pcie_rp *pp);
+ /*
+ * Called from the chained iMSI-RX interrupt handler before the MSI
+ * status is read from DBI. Return true to skip MSI handling for this
+ * invocation, e.g. when the glue driver has detected a link down
+ * after which DBI must not be accessed.
+ */
+ bool (*pre_msi_irq)(struct dw_pcie_rp *pp);
void (*pme_turn_off)(struct dw_pcie_rp *pp);
};
--
2.51.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 05/11] PCI: rcar-gen4: Split reusable hardware initialization
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (3 preceding siblings ...)
2026-09-18 3:20 ` [PATCH 04/11] PCI: dwc: Add a host op to run before iMSI-RX status is read Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-22 21:15 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 06/11] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
` (5 subsequent siblings)
10 siblings, 1 reply; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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
Prepare for Root Port recovery, which is going to reset and reinitialize
the controller with the same sequence probe uses, minus the steps that
must only run once.
Move clock enabling out of rcar_gen4_pcie_common_init() into
rcar_gen4_pcie_clk_enable(), so the power reset and mode setup in
common_init() can be re-run with the clocks already on. Gating the
clocks as well would add nothing: the "pwr" reset is the controller's
cold reset (power_up_rst_n) and resets all PCIEC registers by itself.
Split rcar_gen4_pcie_host_init() into rcar_gen4_pcie_host_hw_init(),
the part Root Port reset will reuse, and a probe-only wrapper.
The power reset is now asserted unconditionally, since recovery calls
common_init() with the controller running. At probe the block is
usually held in reset already, so this only adds a 1 ms wait there.
Propagate reset-controller errors while at it.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 88 +++++++++++++--------
1 file changed, 57 insertions(+), 31 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index fe1f1940e809..8a85f5f45cda 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -178,23 +178,18 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
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");
+ ret = reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
+ if (ret)
return ret;
- }
- if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {
- reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
- /*
- * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr.
- * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B)
- * indicates that for peripherals in HSC domain, after
- * reset has been asserted by writing a matching reset bit
- * into register SRCR, it is mandatory to wait 1ms.
- */
- fsleep(1000);
- }
+ /*
+ * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr.
+ * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B)
+ * indicates that for peripherals in HSC domain, after
+ * reset has been asserted by writing a matching reset bit
+ * into register SRCR, it is mandatory to wait 1ms.
+ */
+ fsleep(1000);
val = readl(rcar->base + PCIEMSR0);
if (rcar->drvdata->mode == DW_PCIE_RC_TYPE) {
@@ -202,8 +197,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)
@@ -213,7 +207,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.
@@ -225,18 +219,16 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
* synchronous one, and wait a little over 1ms to add additional
* safety margin.
*/
- reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
+ ret = reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
+ if (ret < 0)
+ return ret;
+
fsleep(1000);
if (rcar->drvdata->additional_common_init)
rcar->drvdata->additional_common_init(rcar);
return 0;
-
-err_unprepare:
- clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
-
- return ret;
}
static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
@@ -247,6 +239,18 @@ 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_clk_enable(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;
+}
+
static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
{
struct device *dev = rcar->dw.dev;
@@ -449,15 +453,12 @@ static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
}
/* Host mode */
-static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
+static int rcar_gen4_pcie_host_hw_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;
-
gpiod_set_value_cansleep(dw->pe_rst, 1);
ret = rcar_gen4_pcie_common_init(rcar);
@@ -475,13 +476,32 @@ 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 */
-
gpiod_set_value_cansleep(dw->pe_rst, 0);
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;
+
+ ret = rcar_gen4_pcie_clk_enable(rcar);
+ if (ret)
+ return ret;
+
+ ret = rcar_gen4_pcie_host_hw_init(pp);
+ if (ret)
+ goto err;
+
+ return 0;
err:
rcar_gen4_pcie_common_deinit(rcar);
@@ -529,10 +549,16 @@ static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
writel(0, rcar->base + PCIEDMAINTSTSEN);
- ret = rcar_gen4_pcie_common_init(rcar);
+ ret = rcar_gen4_pcie_clk_enable(rcar);
if (ret)
return ret;
+ ret = rcar_gen4_pcie_common_init(rcar);
+ if (ret) {
+ rcar_gen4_pcie_common_deinit(rcar);
+ return ret;
+ }
+
writel(PCIEDMAINTSTSEN_INIT, rcar->base + PCIEDMAINTSTSEN);
return 0;
--
2.51.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 06/11] PCI: rcar-gen4: Add Root Port reset support
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (4 preceding siblings ...)
2026-09-18 3:20 ` [PATCH 05/11] PCI: rcar-gen4: Split reusable hardware initialization Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-22 21:22 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 07/11] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
` (4 subsequent siblings)
10 siblings, 1 reply; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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
Secondary Bus Reset is a hot reset signalled over the link, so it is of
no use for recovering from a link down, and it does not reset the
controller either. Implement the host bridge reset_root_port() callback
instead: it re-runs the hardware initialization probe uses, redoes the
DesignWare Root Port setup and restarts link training. The PCI core
routes every Secondary Bus Reset of the Root Port through it, including
AER recovery and the link-down handling added later.
Rather than tracking which APP interrupt enables survive the power
reset, derive them from software state through a single helper. A state
bit keeps the sources masked while the controller is being
reinitialized.
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.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
For reviewers:
- scoped_guard() is used deliberately in rcar_gen4_pcie_quiesce_irqs()
and rcar_gen4_pcie_resume_irqs(): the next patch adds code after the
locked section, and keeping that shape here makes the diff easier to
read.
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 118 ++++++++++++++++++--
1 file changed, 111 insertions(+), 7 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 8a85f5f45cda..5b983ef6dc47 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>
@@ -90,12 +91,22 @@ struct rcar_gen4_pcie_drvdata {
enum dw_pcie_device_mode mode;
};
+enum rcar_gen4_pcie_state {
+ /* The controller is being reset and reinitialized */
+ RCAR_PCIE_RESETTING,
+};
+
struct rcar_gen4_pcie {
struct dw_pcie dw;
void __iomem *base;
void __iomem *phy_base;
struct platform_device *pdev;
const struct rcar_gen4_pcie_drvdata *drvdata;
+ unsigned long state;
+ /* 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)
@@ -344,6 +355,33 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
return 0;
}
+/* Whether the APP interrupt sources may currently be enabled. */
+static bool rcar_gen4_pcie_irqs_blocked(struct rcar_gen4_pcie *rcar)
+{
+ return !!rcar->state;
+}
+
+static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
+{
+ bool armed = !rcar_gen4_pcie_irqs_blocked(rcar);
+ u32 val;
+
+ lockdep_assert_held(&rcar->app_lock);
+
+ val = readl(rcar->base + PCIEINTSTS0EN);
+ val &= ~MSI_CTRL_INT;
+ if (armed && 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);
@@ -374,12 +412,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;
@@ -394,6 +427,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);
@@ -484,14 +518,77 @@ static int rcar_gen4_pcie_host_hw_init(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) {
+ set_bit(RCAR_PCIE_RESETTING, &rcar->state);
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ }
+}
+
+static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar)
+{
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ clear_bit(RCAR_PCIE_RESETTING, &rcar->state);
+ 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);
+
+ ret = rcar_gen4_pcie_host_hw_init(pp);
+ if (ret) {
+ dev_err(dev, "Host init failed: %d\n", ret);
+ goto out;
+ }
+
+ ret = dw_pcie_setup_rc(pp);
+ if (ret) {
+ dev_err(dev, "Failed to setup RC: %d\n", 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);
+
+ 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;
+ }
ret = rcar_gen4_pcie_clk_enable(rcar);
if (ret)
@@ -525,10 +622,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;
--
2.51.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 07/11] PCI: rcar-gen4: Recover the Root Port on link down
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (5 preceding siblings ...)
2026-09-18 3:20 ` [PATCH 06/11] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-22 21:44 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 08/11] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
` (3 subsequent siblings)
10 siblings, 1 reply; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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, intreq_pcim_sub carries both the integrated MSI receiver and
the controller's reset requests (smlh_req_rst_not, link_req_rst_not), so
the generic DesignWare chained handler reads the MSI status from DBI 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.
Use the pre-MSI callback to check the APP reset status before DBI is
touched. When a reset request is latched, mask the sources, ack the
request and schedule recovery work. The work calls
pci_host_handle_link_down(), which runs the AER-style recovery and
resets the controller through reset_root_port(). If the reset fails, the
sources stay masked so nothing touches the unrecovered controller.
Only unmasked status bits are handled and pending latches are cleared
when re-arming, so requests recorded during probe or the reset itself do
not trigger another recovery. Teardown only disables link-down
detection: MSI delivery has to keep working while devices are removed.
When iMSI-RX is not used (external MSI controller or pci=nomsi), the
DesignWare core does not request intreq_pcim_sub, so request it in the
driver. The DT routes downstream INTx to the same line, but the driver
has never supported INTx (no INTx domain, INTx enables never set), so
taking the line exclusively takes nothing away. Keep msi_ctrl_int
masked in this mode since nothing would clear it.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/Kconfig | 1 +
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++-
2 files changed, 199 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index dcfbe7e229fd..573fcb2e44d7 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -350,6 +350,7 @@ config PCIE_RCAR_GEN4_HOST
depends on PCI_MSI
select PCIE_DW_HOST
select PCIE_RCAR_GEN4
+ select PCI_HOST_COMMON
help
Say Y here if you want PCIe controller (host mode) on R-Car Gen4 SoCs.
To compile this driver as a module, choose M here: the module will be
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 5b983ef6dc47..238755852045 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -25,6 +25,7 @@
#include <linux/reset.h>
#include "../../pci.h"
+#include "../pci-host-common.h"
#include "pcie-designware.h"
/* Renesas-specific */
@@ -39,9 +40,18 @@
#define MSICAP0 0x0050
#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)
@@ -50,6 +60,9 @@
#define PCIEDMAINTSTSEN 0x0314
#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
+/* PCIe Reset Status Clear */
+#define PCIERSTSTSCLR 0x0330
+
/* Port Logic Registers 89 */
#define PRTLGC89 0x0b70
@@ -92,6 +105,10 @@ struct rcar_gen4_pcie_drvdata {
};
enum rcar_gen4_pcie_state {
+ /* A reset request is pending, or its recovery failed */
+ RCAR_PCIE_LINK_DOWN,
+ /* Link-down detection is being torn down */
+ RCAR_PCIE_IRQ_STOPPED,
/* The controller is being reset and reinitialized */
RCAR_PCIE_RESETTING,
};
@@ -102,6 +119,10 @@ struct rcar_gen4_pcie {
void __iomem *phy_base;
struct platform_device *pdev;
const struct rcar_gen4_pcie_drvdata *drvdata;
+ /* intreq_pcim_sub ("msi"), shared with the iMSI-RX */
+ int sub_irq;
+ bool sub_irq_owned;
+ struct work_struct link_down_work;
unsigned long state;
/* Protects APP interrupt enable registers and their software state. */
raw_spinlock_t app_lock;
@@ -361,8 +382,19 @@ static bool rcar_gen4_pcie_irqs_blocked(struct rcar_gen4_pcie *rcar)
return !!rcar->state;
}
+/*
+ * DBI must not be touched while the link is down or the controller is being
+ * reset.
+ */
+static bool rcar_gen4_pcie_dbi_unsafe(struct rcar_gen4_pcie *rcar)
+{
+ return test_bit(RCAR_PCIE_LINK_DOWN, &rcar->state) ||
+ test_bit(RCAR_PCIE_RESETTING, &rcar->state);
+}
+
static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
{
+ struct dw_pcie_rp *pp = &rcar->dw.pp;
bool armed = !rcar_gen4_pcie_irqs_blocked(rcar);
u32 val;
@@ -370,9 +402,28 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
val = readl(rcar->base + PCIEINTSTS0EN);
val &= ~MSI_CTRL_INT;
- if (armed && pci_msi_enabled())
+ /*
+ * MSI delivery only depends on DBI being usable; teardown must not
+ * stop it while devices are still being removed.
+ */
+ if (!rcar_gen4_pcie_dbi_unsafe(rcar) && pp->use_imsi_rx &&
+ pci_msi_enabled())
val |= MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
+
+ val = readl(rcar->base + PCIERSTSTSEN);
+ if (armed && rcar->sub_irq > 0) {
+ /*
+ * 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)
@@ -435,6 +486,134 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
return ret;
}
+/* Returns true if a latched reset-request status was consumed. */
+static bool rcar_gen4_pcie_handle_link_down(struct rcar_gen4_pcie *rcar)
+{
+ bool schedule = false;
+ u32 status;
+
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ status = readl(rcar->base + PCIERSTSTS) &
+ readl(rcar->base + PCIERSTSTSEN) & LINK_DOWN_RESET_MASK;
+ if (!status)
+ return false;
+
+ if (!test_bit(RCAR_PCIE_IRQ_STOPPED, &rcar->state) &&
+ !test_bit(RCAR_PCIE_RESETTING, &rcar->state))
+ schedule = !test_and_set_bit(RCAR_PCIE_LINK_DOWN,
+ &rcar->state);
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ writel(status, rcar->base + PCIERSTSTSCLR);
+ }
+
+ if (schedule)
+ schedule_work(&rcar->link_down_work);
+
+ return true;
+}
+
+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 dw_pcie_rp *pp = &rcar->dw.pp;
+ struct pci_dev *port;
+
+ for_each_pci_bridge(port, pp->bridge->bus) {
+ if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT)
+ pci_host_handle_link_down(port);
+ }
+
+ if (test_bit(RCAR_PCIE_LINK_DOWN, &rcar->state))
+ dev_err(rcar->dw.dev,
+ "Root Port reset failed; keeping the controller's interrupts masked until a reset succeeds\n");
+}
+
+static bool rcar_gen4_pcie_pre_msi_irq(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);
+
+ rcar_gen4_pcie_handle_link_down(rcar);
+ if (rcar_gen4_pcie_dbi_unsafe(rcar))
+ return true;
+
+ /* Check once more right before the DesignWare DBI access. */
+ rcar_gen4_pcie_handle_link_down(rcar);
+
+ return rcar_gen4_pcie_dbi_unsafe(rcar);
+}
+
+static irqreturn_t rcar_gen4_pcie_sub_irq_handler(int irq, void *data)
+{
+ struct rcar_gen4_pcie *rcar = data;
+
+ return rcar_gen4_pcie_handle_link_down(rcar) ?
+ IRQ_HANDLED : IRQ_NONE;
+}
+
+/*
+ * Arm link-down detection once enumeration is done: the chained iMSI-RX
+ * handler is only installed after ops->init, and the recovery needs the
+ * Root Port to exist.
+ */
+static void rcar_gen4_pcie_link_down_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;
+ int irq, ret;
+
+ if (pp->use_imsi_rx && pp->msi_irq[0] > 0) {
+ /* The DesignWare core owns the chained handler of this interrupt. */
+ rcar->sub_irq = pp->msi_irq[0];
+ } else {
+ irq = platform_get_irq_byname_optional(rcar->pdev, "msi");
+ if (irq < 0) {
+ dev_warn(dev, "Failed to get \"msi\" IRQ; link-down detection unavailable\n");
+ return;
+ }
+
+ /*
+ * Run in hard IRQ context even on PREEMPT_RT, like the chained
+ * handler, so the reset request is masked before anything else
+ * touches DBI.
+ */
+ ret = request_irq(irq, rcar_gen4_pcie_sub_irq_handler,
+ IRQF_NO_THREAD, dev_name(dev), rcar);
+ if (ret) {
+ dev_warn(dev, "Failed to request \"msi\" IRQ; link-down detection unavailable\n");
+ return;
+ }
+
+ rcar->sub_irq = irq;
+ rcar->sub_irq_owned = true;
+ }
+
+ guard(raw_spinlock_irqsave)(&rcar->app_lock);
+ clear_bit(RCAR_PCIE_IRQ_STOPPED, &rcar->state);
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
+static void rcar_gen4_pcie_link_down_irq_deinit(struct rcar_gen4_pcie *rcar)
+{
+ if (test_and_set_bit(RCAR_PCIE_IRQ_STOPPED, &rcar->state))
+ return;
+
+ rcar_gen4_pcie_app_irq_sync(rcar);
+
+ if (rcar->sub_irq > 0)
+ synchronize_irq(rcar->sub_irq);
+
+ cancel_work_sync(&rcar->link_down_work);
+
+ if (rcar->sub_irq_owned) {
+ free_irq(rcar->sub_irq, rcar);
+ rcar->sub_irq_owned = false;
+ }
+ rcar->sub_irq = 0;
+}
+
static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
struct pci_dev *dev)
{
@@ -524,14 +703,23 @@ static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
set_bit(RCAR_PCIE_RESETTING, &rcar->state);
rcar_gen4_pcie_app_irq_sync_locked(rcar);
}
+
+ if (rcar->sub_irq > 0)
+ disable_irq(rcar->sub_irq);
}
-static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar)
+static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
+ bool recovered)
{
scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ if (recovered)
+ clear_bit(RCAR_PCIE_LINK_DOWN, &rcar->state);
clear_bit(RCAR_PCIE_RESETTING, &rcar->state);
rcar_gen4_pcie_app_irq_sync_locked(rcar);
}
+
+ if (rcar->sub_irq > 0)
+ enable_irq(rcar->sub_irq);
}
/*
@@ -574,7 +762,7 @@ static int rcar_gen4_pcie_reset_root_port(struct pci_host_bridge *bridge,
dev_dbg(dev, "Root Port reset completed\n");
out:
- rcar_gen4_pcie_resume_irqs(rcar);
+ rcar_gen4_pcie_resume_irqs(rcar, !ret);
return ret;
}
@@ -610,6 +798,8 @@ 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);
+ rcar_gen4_pcie_link_down_irq_deinit(rcar);
+
gpiod_set_value_cansleep(dw->pe_rst, 1);
rcar_gen4_pcie_common_deinit(rcar);
}
@@ -617,6 +807,8 @@ 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_link_down_irq_init,
+ .pre_msi_irq = rcar_gen4_pcie_pre_msi_irq,
};
static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
@@ -632,6 +824,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;
pp->ops = &rcar_gen4_pcie_host_ops;
@@ -641,6 +834,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_link_down_irq_deinit(rcar);
dw_pcie_host_deinit(&rcar->dw.pp);
}
--
2.51.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 08/11] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (6 preceding siblings ...)
2026-09-18 3:20 ` [PATCH 07/11] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-18 3:20 ` [PATCH 09/11] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
` (2 subsequent siblings)
10 siblings, 0 replies; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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>
---
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 ad013dd0a166..dab6f7c52c12 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 947b16086979..b9c7482223a2 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -451,6 +451,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] 21+ messages in thread
* [PATCH 09/11] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (7 preceding siblings ...)
2026-09-18 3:20 ` [PATCH 08/11] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-18 3:20 ` [PATCH 10/11] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
2026-09-18 3:20 ` [PATCH 11/11] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
10 siblings, 0 replies; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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 Root Port's own interrupts never arrive as MSIs: the
controller (DWC 5.20a) lacks SII2MSI, and the APP block latches the SII
signals into PCIEINTSTS0 and raises platform IRQs instead, AER on
intreq_pcim_lperr, PME and bandwidth events on 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 the chained
iMSI-RX IRQ, which the port services cannot request. In either case,
Root Port interrupts are not delivered.
Set no_rp_msi so the port services fall back to INTx, and stand in for
the Root Port's INTx with a single-entry virtual IRQ domain that the
bridge map_irq callback returns for the Root Port only, leaving the
downstream INTx mappings untouched. 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. host_msi_init()
sets the Root Port's MSI Enable (MSICAP0) whenever MSI is enabled, and
hiding the capability does not clear it, so handle both the MSI and INTx
forms of the status bits. DTs without "aer" keep Root Port AER
unavailable as before.
Mirror the virtual IRQ's mask state in software so the APP enables are
re-derived after a reset and stay masked during recovery or teardown.
Clear the latches in irq_ack, before the service clears the Root Error
Status register, so back-to-back notifications are not lost. Error
command handling and recovery remain with the standard AER driver.
The AER interrupt can also fire on link down, so run the reset-request
handling first and skip the dispatch once recovery has started.
Synchronize and free the parent IRQ before tearing down the domain.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 246 +++++++++++++++++++-
1 file changed, 244 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 238755852045..c24ac82a1ec6 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/platform_device.h>
#include <linux/pm_runtime.h>
@@ -56,6 +58,11 @@
#define PCIEINTSTS0EN 0x0310
#define MSI_CTRL_INT BIT(26)
+/* 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)
@@ -63,6 +70,9 @@
/* PCIe Reset Status Clear */
#define PCIERSTSTSCLR 0x0330
+/* PCIe Interrupt Status 0 Clear */
+#define PCIEINTSTS0CLR 0x0340
+
/* Port Logic Registers 89 */
#define PRTLGC89 0x0b70
@@ -123,6 +133,12 @@ struct rcar_gen4_pcie {
int sub_irq;
bool sub_irq_owned;
struct work_struct link_down_work;
+ /* Virtual Root Port INTx, fed from intreq_pcim_lperr ("aer") */
+ struct irq_domain *rp_irq_domain;
+ struct fwnode_handle *rp_fwnode;
+ unsigned int rp_irq;
+ bool rp_irq_enabled;
+ int aer_irq;
unsigned long state;
/* Protects APP interrupt enable registers and their software state. */
raw_spinlock_t app_lock;
@@ -376,7 +392,7 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
return 0;
}
-/* Whether the APP interrupt sources may currently be enabled. */
+/* True if APP interrupt enables should stay masked */
static bool rcar_gen4_pcie_irqs_blocked(struct rcar_gen4_pcie *rcar)
{
return !!rcar->state;
@@ -401,7 +417,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);
/*
* MSI delivery only depends on DBI being usable; teardown must not
* stop it while devices are still being removed.
@@ -409,6 +425,8 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
if (!rcar_gen4_pcie_dbi_unsafe(rcar) && pp->use_imsi_rx &&
pci_msi_enabled())
val |= MSI_CTRL_INT;
+ if (armed && rcar->rp_irq_enabled && rcar->aer_irq > 0)
+ val |= RP_AER_MASK;
writel(val, rcar->base + PCIEINTSTS0EN);
val = readl(rcar->base + PCIERSTSTSEN);
@@ -504,6 +522,11 @@ static bool rcar_gen4_pcie_handle_link_down(struct rcar_gen4_pcie *rcar)
&rcar->state);
rcar_gen4_pcie_app_irq_sync_locked(rcar);
writel(status, rcar->base + PCIERSTSTSCLR);
+ /*
+ * Drop any Root Port events triggered by link down; the
+ * upcoming reset will reinitialize the controller anyway.
+ */
+ writel(RP_AER_MASK, rcar->base + PCIEINTSTS0CLR);
}
if (schedule)
@@ -512,6 +535,30 @@ static bool rcar_gen4_pcie_handle_link_down(struct rcar_gen4_pcie *rcar)
return true;
}
+/*
+ * Dispatch pending Root Port notifications in @mask to the virtual Root
+ * Port IRQ. Returns true if there was something to handle.
+ */
+static bool rcar_gen4_pcie_rp_irq_dispatch(struct rcar_gen4_pcie *rcar,
+ u32 mask)
+{
+ u32 status;
+
+ status = readl(rcar->base + PCIEINTSTS0) &
+ readl(rcar->base + PCIEINTSTS0EN) & mask;
+ if (!status)
+ return false;
+
+ /* Check once more right before running handlers that access DBI. */
+ if (rcar_gen4_pcie_handle_link_down(rcar) ||
+ rcar_gen4_pcie_irqs_blocked(rcar))
+ return true;
+
+ generic_handle_domain_irq(rcar->rp_irq_domain, 0);
+
+ return true;
+}
+
static void rcar_gen4_pcie_link_down_work(struct work_struct *work)
{
struct rcar_gen4_pcie *rcar =
@@ -604,6 +651,8 @@ static void rcar_gen4_pcie_link_down_irq_deinit(struct rcar_gen4_pcie *rcar)
if (rcar->sub_irq > 0)
synchronize_irq(rcar->sub_irq);
+ if (rcar->aer_irq > 0)
+ synchronize_irq(rcar->aer_irq);
cancel_work_sync(&rcar->link_down_work);
@@ -614,6 +663,183 @@ static void rcar_gen4_pcie_link_down_irq_deinit(struct rcar_gen4_pcie *rcar)
rcar->sub_irq = 0;
}
+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_AER_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;
+
+ if (rcar_gen4_pcie_handle_link_down(rcar))
+ return IRQ_HANDLED;
+
+ return rcar_gen4_pcie_rp_irq_dispatch(rcar, RP_AER_MASK) ?
+ IRQ_HANDLED : IRQ_NONE;
+}
+
+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;
+ 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");
+
+ /* Keep the notifications masked until a port service requests the IRQ. */
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ rcar->rp_irq_enabled = false;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ }
+ writel(RP_AER_MASK, rcar->base + PCIEINTSTS0CLR);
+
+ /*
+ * Note: suspend/resume support must not tear down and recreate this
+ * domain, as the port driver keeps the mapped IRQ.
+ */
+ rcar->rp_fwnode = irq_domain_alloc_named_fwnode(dev_name(dev));
+ if (!rcar->rp_fwnode)
+ return -ENOMEM;
+
+ rcar->rp_irq_domain = irq_domain_create_linear(rcar->rp_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. */
+ ret = devm_request_irq(dev, 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);
+ rcar->rp_irq = 0;
+err_remove_domain:
+ irq_domain_remove(rcar->rp_irq_domain);
+ rcar->rp_irq_domain = NULL;
+err_free_fwnode:
+ irq_domain_free_fwnode(rcar->rp_fwnode);
+ rcar->rp_fwnode = NULL;
+
+ return ret;
+}
+
+static void rcar_gen4_pcie_rp_irq_deinit(struct rcar_gen4_pcie *rcar)
+{
+ if (!rcar->rp_irq_domain)
+ return;
+
+ /*
+ * The port services are gone; stop the parent IRQ before tearing down
+ * the domain.
+ */
+ if (rcar->aer_irq > 0)
+ devm_free_irq(rcar->dw.dev, rcar->aer_irq, rcar);
+
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ rcar->rp_irq_enabled = false;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ }
+
+ irq_dispose_mapping(rcar->rp_irq);
+ irq_domain_remove(rcar->rp_irq_domain);
+ irq_domain_free_fwnode(rcar->rp_fwnode);
+ rcar->rp_irq_domain = NULL;
+ rcar->rp_irq = 0;
+ rcar->aer_irq = 0;
+ rcar->rp_fwnode = NULL;
+}
+
static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
struct pci_dev *dev)
{
@@ -706,6 +932,8 @@ static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
if (rcar->sub_irq > 0)
disable_irq(rcar->sub_irq);
+ if (rcar->aer_irq > 0)
+ disable_irq(rcar->aer_irq);
}
static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
@@ -718,6 +946,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);
if (rcar->sub_irq > 0)
enable_irq(rcar->sub_irq);
}
@@ -786,9 +1016,14 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
goto err;
+ ret = rcar_gen4_pcie_rp_irq_init(pp);
+ if (ret)
+ goto err;
+
return 0;
err:
+ gpiod_set_value_cansleep(dw->pe_rst, 1);
rcar_gen4_pcie_common_deinit(rcar);
return ret;
}
@@ -799,6 +1034,7 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
rcar_gen4_pcie_link_down_irq_deinit(rcar);
+ rcar_gen4_pcie_rp_irq_deinit(rcar);
gpiod_set_value_cansleep(dw->pe_rst, 1);
rcar_gen4_pcie_common_deinit(rcar);
@@ -827,6 +1063,12 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
INIT_WORK(&rcar->link_down_work, rcar_gen4_pcie_link_down_work);
pp->num_vectors = MAX_MSI_IRQS;
+ /*
+ * 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] 21+ messages in thread
* [PATCH 10/11] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (8 preceding siblings ...)
2026-09-18 3:20 ` [PATCH 09/11] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-18 3:20 ` [PATCH 11/11] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
10 siblings, 0 replies; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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 latched in
PCIEINTSTS0 and routed to intreq_pcim_sub, the interrupt shared with the
integrated MSI receiver and the reset requests. Dispatch them to the
virtual Root Port IRQ from both interrupt paths: the DesignWare chained
handler through the pre-MSI callback, and the directly requested handler
used with an external MSI controller or pci=nomsi.
Enable the sources only while the interrupt is owned and the Root Port
IRQ is unmasked, and clear the latches in the ack together with the AER
ones. On an R-Car S4 with the GIC ITS as MSI parent, a Link Retrain now
reaches the bandwidth controller service, which clears LnkSta.LBMS.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 28 +++++++++++++++++----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index c24ac82a1ec6..20f221cb7a40 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -63,6 +63,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)
@@ -417,7 +427,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);
/*
* MSI delivery only depends on DBI being usable; teardown must not
* stop it while devices are still being removed.
@@ -427,6 +437,8 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
val |= MSI_CTRL_INT;
if (armed && rcar->rp_irq_enabled && rcar->aer_irq > 0)
val |= RP_AER_MASK;
+ if (armed && rcar->rp_irq_enabled && rcar->sub_irq > 0)
+ val |= RP_SUB_MASK;
writel(val, rcar->base + PCIEINTSTS0EN);
val = readl(rcar->base + PCIERSTSTSEN);
@@ -526,7 +538,8 @@ static bool rcar_gen4_pcie_handle_link_down(struct rcar_gen4_pcie *rcar)
* Drop any Root Port events triggered by link down; the
* upcoming reset will reinitialize the controller anyway.
*/
- writel(RP_AER_MASK, rcar->base + PCIEINTSTS0CLR);
+ writel(RP_AER_MASK | RP_SUB_MASK,
+ rcar->base + PCIEINTSTS0CLR);
}
if (schedule)
@@ -585,6 +598,8 @@ static bool rcar_gen4_pcie_pre_msi_irq(struct dw_pcie_rp *pp)
if (rcar_gen4_pcie_dbi_unsafe(rcar))
return true;
+ rcar_gen4_pcie_rp_irq_dispatch(rcar, RP_SUB_MASK);
+
/* Check once more right before the DesignWare DBI access. */
rcar_gen4_pcie_handle_link_down(rcar);
@@ -595,7 +610,10 @@ static irqreturn_t rcar_gen4_pcie_sub_irq_handler(int irq, void *data)
{
struct rcar_gen4_pcie *rcar = data;
- return rcar_gen4_pcie_handle_link_down(rcar) ?
+ if (rcar_gen4_pcie_handle_link_down(rcar))
+ return IRQ_HANDLED;
+
+ return rcar_gen4_pcie_rp_irq_dispatch(rcar, RP_SUB_MASK) ?
IRQ_HANDLED : IRQ_NONE;
}
@@ -689,7 +707,7 @@ static void rcar_gen4_pcie_rp_irq_ack(struct irq_data *d)
* Clear APP latches before the service clears Root Error Status so
* we don't drop back-to-back notifications.
*/
- writel(RP_AER_MASK, rcar->base + PCIEINTSTS0CLR);
+ writel(RP_AER_MASK | RP_SUB_MASK, rcar->base + PCIEINTSTS0CLR);
}
static const struct irq_chip rcar_gen4_pcie_rp_irq_chip = {
@@ -756,7 +774,7 @@ static int rcar_gen4_pcie_rp_irq_init(struct dw_pcie_rp *pp)
rcar->rp_irq_enabled = false;
rcar_gen4_pcie_app_irq_sync_locked(rcar);
}
- writel(RP_AER_MASK, rcar->base + PCIEINTSTS0CLR);
+ writel(RP_AER_MASK | RP_SUB_MASK, rcar->base + PCIEINTSTS0CLR);
/*
* Note: suspend/resume support must not tear down and recreate this
--
2.51.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 11/11] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (9 preceding siblings ...)
2026-09-18 3:20 ` [PATCH 10/11] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
@ 2026-09-18 3:20 ` Koichiro Den
2026-09-22 21:31 ` Marek Vasut
10 siblings, 1 reply; 21+ messages in thread
From: Koichiro Den @ 2026-09-18 3:20 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.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
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] 21+ messages in thread
* Re: [PATCH 01/11] PCI: dwc: Add Renesas to the RAS DES VSEC list
2026-09-18 3:20 ` [PATCH 01/11] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
@ 2026-09-22 19:40 ` Marek Vasut
0 siblings, 0 replies; 21+ messages in thread
From: Marek Vasut @ 2026-09-22 19: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/18/26 5:20 AM, Koichiro Den wrote:
> 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.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 02/11] PCI: rcar-gen4: Drop the APP-based link_up check
2026-09-18 3:20 ` [PATCH 02/11] PCI: rcar-gen4: Drop the APP-based link_up check Koichiro Den
@ 2026-09-22 20:56 ` Marek Vasut
2026-09-23 14:56 ` Koichiro Den
0 siblings, 1 reply; 21+ messages in thread
From: Marek Vasut @ 2026-09-22 20:56 UTC (permalink / raw)
To: Koichiro Den, 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
Hello Den-san,
I apologize for my late reply.
On 9/18/26 5:20 AM, 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.
>
> Drop the callback and let the DesignWare core use its PORT_DEBUG1 check
> instead, which correctly detects the downed link.
>
> Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode")
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> drivers/pci/controller/dwc/pcie-rcar-gen4.c | 14 --------------
> 1 file changed, 14 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> index 5a076aa3f490..fe1f1940e809 100644
> --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> @@ -44,8 +44,6 @@
> /* PCIe Interrupt Status 0 Enable */
> #define PCIEINTSTS0EN 0x0310
> #define MSI_CTRL_INT BIT(26)
> -#define SMLH_LINK_UP BIT(7)
> -#define RDLH_LINK_UP BIT(6)
>
> /* PCIe DMA Interrupt Status Enable */
> #define PCIEDMAINTSTSEN 0x0314
> @@ -102,17 +100,6 @@ struct rcar_gen4_pcie {
> #define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
>
> /* Common */
> -static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
> -{
> - struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
> - u32 val, mask;
> -
> - val = readl(rcar->base + PCIEINTSTS0);
> - mask = RDLH_LINK_UP | SMLH_LINK_UP;
> -
> - return (val & mask) == mask;
> -}
> -
> /*
> * Manually initiate the speed change. Return 0 if change succeeded; otherwise
> * -ETIMEDOUT.
> @@ -298,7 +285,6 @@ static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar)
> static const struct dw_pcie_ops dw_pcie_ops = {
> .start_link = rcar_gen4_pcie_start_link,
> .stop_link = rcar_gen4_pcie_stop_link,
> - .link_up = rcar_gen4_pcie_link_up,
> };
>
> static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev)
Can we include some form of the draft patch below, so the S4 Reference
Manual rev.1.40 , page 1564 , Figure 104.5 Initial Setting of PCIEC ,
bottommost diamond in the figure (smlh_link_up and rdlh_link_up = 1
test), would still be fulfilled, and the initialization code in the
driver would not diverge from the initialization sequence listed in the
reference manual ? What do you think ?
The PCIEINTSTS0CLR should clear the link state bits before the link gets
started, so the initialization code should be able to sample those bits
after the link came up and confirm they were set during the link up.
The PCIEINTSTS0CLR usage however won't solve the case where the DWC PCIe
core code has to sample PCIe link state at arbitrary time, this is what
this patch does solve correctly.
"
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index e5833f36625d2..9f29055f0bed7 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -203,17 +203,28 @@ static int rcar_gen5_pcie_speed_control(struct
rcar_gen4_pcie *rcar)
* Enable LTSSM of this controller and manually initiate the speed change.
* Always return 0.
*/
+#define PCIEINTSTS0CLR 0x0340
static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
{
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+ u32 val, mask;
int ret;
+ /* Clear RDLH/SMLH link state */
+ writel(RDLH_LINK_UP | SMLH_LINK_UP, rcar->base + PCIEINTSTS0CLR);
+
if (rcar->drvdata->ltssm_control) {
ret = rcar->drvdata->ltssm_control(rcar, true);
if (ret)
return ret;
}
+ ret = rcar->drvdata->speed_control(rcar);
+ if (ret)
+ return ret;
+
+ val = readl(rcar->base + PCIEINTSTS0);
+ mask = RDLH_LINK_UP | SMLH_LINK_UP;
+ return ((val & mask) == mask)
}
@@ -223,6 +234,9 @@ static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)
if (rcar->drvdata->ltssm_control)
rcar->drvdata->ltssm_control(rcar, false);
+
+ /* Clear RDLH/SMLH link state */
+ writel(RDLH_LINK_UP | SMLH_LINK_UP, rcar->base + PCIEINTSTS0CLR);
}
static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
"
--
Best regards,
Marek Vasut
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 03/11] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
2026-09-18 3:20 ` [PATCH 03/11] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
@ 2026-09-22 20:59 ` Marek Vasut
0 siblings, 0 replies; 21+ messages in thread
From: Marek Vasut @ 2026-09-22 20:59 UTC (permalink / raw)
To: Koichiro Den, 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/18/26 5:20 AM, 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.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 05/11] PCI: rcar-gen4: Split reusable hardware initialization
2026-09-18 3:20 ` [PATCH 05/11] PCI: rcar-gen4: Split reusable hardware initialization Koichiro Den
@ 2026-09-22 21:15 ` Marek Vasut
2026-09-23 15:24 ` Koichiro Den
0 siblings, 1 reply; 21+ messages in thread
From: Marek Vasut @ 2026-09-22 21:15 UTC (permalink / raw)
To: Koichiro Den, 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/18/26 5:20 AM, Koichiro Den wrote:
[...]
> +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> @@ -178,23 +178,18 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
> 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");
> + ret = reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
> + if (ret)
> return ret;
> - }
>
> - if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {
> - reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
> - /*
> - * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr.
> - * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B)
> - * indicates that for peripherals in HSC domain, after
> - * reset has been asserted by writing a matching reset bit
> - * into register SRCR, it is mandatory to wait 1ms.
> - */
> - fsleep(1000);
> - }
> + /*
> + * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr.
> + * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B)
> + * indicates that for peripherals in HSC domain, after
> + * reset has been asserted by writing a matching reset bit
> + * into register SRCR, it is mandatory to wait 1ms.
> + */
> + fsleep(1000);
This fsleep here should only happen if the reset wasn't asserted before.
Is removal of reset_control_status() correct ?
> val = readl(rcar->base + PCIEMSR0);
> if (rcar->drvdata->mode == DW_PCIE_RC_TYPE) {
[...]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 06/11] PCI: rcar-gen4: Add Root Port reset support
2026-09-18 3:20 ` [PATCH 06/11] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
@ 2026-09-22 21:22 ` Marek Vasut
0 siblings, 0 replies; 21+ messages in thread
From: Marek Vasut @ 2026-09-22 21:22 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/18/26 5:20 AM, Koichiro Den wrote:
[...]
> +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
[...]
> @@ -90,12 +91,22 @@ struct rcar_gen4_pcie_drvdata {
> enum dw_pcie_device_mode mode;
> };
>
> +enum rcar_gen4_pcie_state {
> + /* The controller is being reset and reinitialized */
> + RCAR_PCIE_RESETTING,
> +};
> +
> struct rcar_gen4_pcie {
> struct dw_pcie dw;
> void __iomem *base;
> void __iomem *phy_base;
> struct platform_device *pdev;
> const struct rcar_gen4_pcie_drvdata *drvdata;
> + unsigned long state;
Can we simply use boolean flags here in struct rcar_gen4_pcie, instead
of the enum rcar_gen4_pcie_state ?
> + /* 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)
>
> @@ -344,6 +355,33 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
> return 0;
> }
>
> +/* Whether the APP interrupt sources may currently be enabled. */
> +static bool rcar_gen4_pcie_irqs_blocked(struct rcar_gen4_pcie *rcar)
> +{
> + return !!rcar->state;
> +}
> +
> +static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
> +{
> + bool armed = !rcar_gen4_pcie_irqs_blocked(rcar);
> + u32 val;
> +
> + lockdep_assert_held(&rcar->app_lock);
> +
> + val = readl(rcar->base + PCIEINTSTS0EN);
> + val &= ~MSI_CTRL_INT;
> + if (armed && pci_msi_enabled())
> + val |= MSI_CTRL_INT;
Should this function cache the state of pci_msi_enabled() in struct
rcar_gen4_pcie , so that in case rcar_gen4_pcie_irqs_blocked() reports
MSIs as blocked at this point ...
> + 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);
... this function can enable MSIs once it is called and MSIs are no
longer blocked ?
> +}
> +
> static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
> {
> struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
[...]
--
Best regards,
Marek Vasut
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 11/11] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts
2026-09-18 3:20 ` [PATCH 11/11] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
@ 2026-09-22 21:31 ` Marek Vasut
0 siblings, 0 replies; 21+ messages in thread
From: Marek Vasut @ 2026-09-22 21:31 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/18/26 5:20 AM, 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.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 07/11] PCI: rcar-gen4: Recover the Root Port on link down
2026-09-18 3:20 ` [PATCH 07/11] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
@ 2026-09-22 21:44 ` Marek Vasut
0 siblings, 0 replies; 21+ messages in thread
From: Marek Vasut @ 2026-09-22 21:44 UTC (permalink / raw)
To: Koichiro Den, 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/18/26 5:20 AM, Koichiro Den wrote:
> On R-Car, intreq_pcim_sub carries both the integrated MSI receiver and
> the controller's reset requests (smlh_req_rst_not, link_req_rst_not), so
> the generic DesignWare chained handler reads the MSI status from DBI 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.
Out of curiosity, do they trigger SError, and does the firmware (TFA)
trap/fix those up in EL3?
> Use the pre-MSI callback to check the APP reset status before DBI is
> touched. When a reset request is latched, mask the sources, ack the
> request and schedule recovery work. The work calls
> pci_host_handle_link_down(), which runs the AER-style recovery and
> resets the controller through reset_root_port(). If the reset fails, the
> sources stay masked so nothing touches the unrecovered controller.
>
> Only unmasked status bits are handled and pending latches are cleared
> when re-arming, so requests recorded during probe or the reset itself do
> not trigger another recovery. Teardown only disables link-down
> detection: MSI delivery has to keep working while devices are removed.
>
> When iMSI-RX is not used (external MSI controller or pci=nomsi), the
> DesignWare core does not request intreq_pcim_sub, so request it in the
> driver.
Would it make sense to request the line unconditionally, to simplify the
driver(s) ?
I also have to wonder, is this specific to R-Car or could it be this is
a generic property of the DWC controller and this should go into the DWC
core ?
Thank you for your help !
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 02/11] PCI: rcar-gen4: Drop the APP-based link_up check
2026-09-22 20:56 ` Marek Vasut
@ 2026-09-23 14:56 ` Koichiro Den
0 siblings, 0 replies; 21+ messages in thread
From: Koichiro Den @ 2026-09-23 14:56 UTC (permalink / raw)
To: 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
On Tue, Sep 22, 2026 at 10:56:02PM +0200, Marek Vasut wrote:
> Hello Den-san,
>
> I apologize for my late reply.
>
> On 9/18/26 5:20 AM, 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.
> >
> > Drop the callback and let the DesignWare core use its PORT_DEBUG1 check
> > instead, which correctly detects the downed link.
> >
> > Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode")
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 14 --------------
> > 1 file changed, 14 deletions(-)
> >
> > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > index 5a076aa3f490..fe1f1940e809 100644
> > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > @@ -44,8 +44,6 @@
> > /* PCIe Interrupt Status 0 Enable */
> > #define PCIEINTSTS0EN 0x0310
> > #define MSI_CTRL_INT BIT(26)
> > -#define SMLH_LINK_UP BIT(7)
> > -#define RDLH_LINK_UP BIT(6)
> > /* PCIe DMA Interrupt Status Enable */
> > #define PCIEDMAINTSTSEN 0x0314
> > @@ -102,17 +100,6 @@ struct rcar_gen4_pcie {
> > #define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
> > /* Common */
> > -static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
> > -{
> > - struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
> > - u32 val, mask;
> > -
> > - val = readl(rcar->base + PCIEINTSTS0);
> > - mask = RDLH_LINK_UP | SMLH_LINK_UP;
> > -
> > - return (val & mask) == mask;
> > -}
> > -
> > /*
> > * Manually initiate the speed change. Return 0 if change succeeded; otherwise
> > * -ETIMEDOUT.
> > @@ -298,7 +285,6 @@ static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar)
> > static const struct dw_pcie_ops dw_pcie_ops = {
> > .start_link = rcar_gen4_pcie_start_link,
> > .stop_link = rcar_gen4_pcie_stop_link,
> > - .link_up = rcar_gen4_pcie_link_up,
> > };
> > static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev)
>
> Can we include some form of the draft patch below, so the S4 Reference
> Manual rev.1.40 , page 1564 , Figure 104.5 Initial Setting of PCIEC ,
> bottommost diamond in the figure (smlh_link_up and rdlh_link_up = 1 test),
> would still be fulfilled, and the initialization code in the driver would
> not diverge from the initialization sequence listed in the reference manual
> ? What do you think ?
If always relying on the PORT_DEBUG1 check instead of the SMLH/RDLH check does
not introduce any regressions, I'd personally prefer to keep patch 2 as-is
because it keeps the code simpler. We could just add a comment noting that this
link-up check diverges from Figure 104.5.
That said, I agree that in general we should follow the R-Car reference manual
where possible, and your draft makes sense for that purpose. If we go that way,
I have one question: would we need a polling loop with a timeout
(PCIE_LINK_WAIT_MAX_RETRIES * PCIE_LINK_WAIT_SLEEP_MS) in
rcar_gen4_pcie_start_link(), similar to dw_pcie_wait_for_link()?
P.S. I'll rebase v2 onto the latest pci/controller/dwc-rcar-gen4.
Thanks for the review.
Best regards,
Koichiro
>
> The PCIEINTSTS0CLR should clear the link state bits before the link gets
> started, so the initialization code should be able to sample those bits
> after the link came up and confirm they were set during the link up.
>
> The PCIEINTSTS0CLR usage however won't solve the case where the DWC PCIe
> core code has to sample PCIe link state at arbitrary time, this is what this
> patch does solve correctly.
>
> "
> diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> index e5833f36625d2..9f29055f0bed7 100644
> --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> @@ -203,17 +203,28 @@ static int rcar_gen5_pcie_speed_control(struct
> rcar_gen4_pcie *rcar)
> * Enable LTSSM of this controller and manually initiate the speed change.
> * Always return 0.
> */
> +#define PCIEINTSTS0CLR 0x0340
> static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
> {
> struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
> + u32 val, mask;
> int ret;
>
> + /* Clear RDLH/SMLH link state */
> + writel(RDLH_LINK_UP | SMLH_LINK_UP, rcar->base + PCIEINTSTS0CLR);
> +
> if (rcar->drvdata->ltssm_control) {
> ret = rcar->drvdata->ltssm_control(rcar, true);
> if (ret)
> return ret;
> }
>
> + ret = rcar->drvdata->speed_control(rcar);
> + if (ret)
> + return ret;
> +
> + val = readl(rcar->base + PCIEINTSTS0);
> + mask = RDLH_LINK_UP | SMLH_LINK_UP;
> + return ((val & mask) == mask)
> }
>
> @@ -223,6 +234,9 @@ static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)
>
> if (rcar->drvdata->ltssm_control)
> rcar->drvdata->ltssm_control(rcar, false);
> +
> + /* Clear RDLH/SMLH link state */
> + writel(RDLH_LINK_UP | SMLH_LINK_UP, rcar->base + PCIEINTSTS0CLR);
> }
>
> static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
> "
>
> --
> Best regards,
> Marek Vasut
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 05/11] PCI: rcar-gen4: Split reusable hardware initialization
2026-09-22 21:15 ` Marek Vasut
@ 2026-09-23 15:24 ` Koichiro Den
0 siblings, 0 replies; 21+ messages in thread
From: Koichiro Den @ 2026-09-23 15:24 UTC (permalink / raw)
To: 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
On Tue, Sep 22, 2026 at 11:15:48PM +0200, Marek Vasut wrote:
> On 9/18/26 5:20 AM, Koichiro Den wrote:
>
> [...]
>
> > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > @@ -178,23 +178,18 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
> > 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");
> > + ret = reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
> > + if (ret)
> > return ret;
> > - }
> > - if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {
> > - reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
> > - /*
> > - * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr.
> > - * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B)
> > - * indicates that for peripherals in HSC domain, after
> > - * reset has been asserted by writing a matching reset bit
> > - * into register SRCR, it is mandatory to wait 1ms.
> > - */
> > - fsleep(1000);
> > - }
> > + /*
> > + * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr.
> > + * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B)
> > + * indicates that for peripherals in HSC domain, after
> > + * reset has been asserted by writing a matching reset bit
> > + * into register SRCR, it is mandatory to wait 1ms.
> > + */
> > + fsleep(1000);
>
> This fsleep here should only happen if the reset wasn't asserted before.
> Is removal of reset_control_status() correct ?
You're right. Looking at the code again, adding the recovery path doesn't at all
justify removing the reset_control_status() check. I'll fix it and restore that
condition.
Thanks for the review!
Best regards,
Koichiro
>
> > val = readl(rcar->base + PCIEMSR0);
> > if (rcar->drvdata->mode == DW_PCIE_RC_TYPE) {
> [...]
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-09-23 15:24 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-18 3:20 [PATCH 00/11] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-09-18 3:20 ` [PATCH 01/11] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
2026-09-22 19:40 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 02/11] PCI: rcar-gen4: Drop the APP-based link_up check Koichiro Den
2026-09-22 20:56 ` Marek Vasut
2026-09-23 14:56 ` Koichiro Den
2026-09-18 3:20 ` [PATCH 03/11] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
2026-09-22 20:59 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 04/11] PCI: dwc: Add a host op to run before iMSI-RX status is read Koichiro Den
2026-09-18 3:20 ` [PATCH 05/11] PCI: rcar-gen4: Split reusable hardware initialization Koichiro Den
2026-09-22 21:15 ` Marek Vasut
2026-09-23 15:24 ` Koichiro Den
2026-09-18 3:20 ` [PATCH 06/11] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
2026-09-22 21:22 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 07/11] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
2026-09-22 21:44 ` Marek Vasut
2026-09-18 3:20 ` [PATCH 08/11] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
2026-09-18 3:20 ` [PATCH 09/11] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
2026-09-18 3:20 ` [PATCH 10/11] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
2026-09-18 3:20 ` [PATCH 11/11] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
2026-09-22 21:31 ` 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®