* [PATCH v3 01/18] PCI: dwc: Add Renesas to the RAS DES VSEC list
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:50 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check Koichiro Den
` (17 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, 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 SoCs (DWC 5.20a) carries the RAS DES capability
with the stock DesignWare VSEC ID (0x02, rev 4), but vendor ID 0x1912 is
not in dwc_pcie_rasdes_vsec_ids[]. Add Renesas to the list.
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Clarify the SoC generation in the commit message. No code changes.
include/linux/pcie-dwc.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h
index 097e806c714c..e5693895dbd5 100644
--- a/include/linux/pcie-dwc.h
+++ b/include/linux/pcie-dwc.h
@@ -38,6 +38,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
.vsec_id = 0x02, .vsec_rev = 0x4 },
{ .vendor_id = PCI_VENDOR_ID_LECARC,
.vsec_id = 0x02, .vsec_rev = 0x4 },
+ { .vendor_id = PCI_VENDOR_ID_RENESAS,
+ .vsec_id = 0x02, .vsec_rev = 0x4 },
{}
};
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [PATCH v3 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-10-06 8:46 ` [PATCH v3 01/18] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:51 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 03/18] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
` (16 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
dw_pcie_link_up() reads PORT_LINK_DEBUG1 when the glue driver provides
no .link_up() callback. A callback that needs a platform-specific check
on top of that register would have to duplicate the register read.
Factor the check out into dw_pcie_link_up_debug_check() and export it
for modular glue drivers. pcie-rcar-gen4 will be the first user: its APP
link-up events remain latched after the link goes down, so it has to
combine them with the live PORT_LINK_DEBUG1 state.
No functional change.
Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- New patch, suggested by Marek in the v2 review of patch 2.
https://lore.kernel.org/r/c038dbd9-7675-43a9-834c-915b6795dd50@mailbox.org/
drivers/pci/controller/dwc/pcie-designware.c | 15 +++++++++++----
drivers/pci/controller/dwc/pcie-designware.h | 1 +
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index c726aa71c830..49a9ba910ba8 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -817,17 +817,24 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
}
EXPORT_SYMBOL_GPL(dw_pcie_wait_for_link);
-bool dw_pcie_link_up(struct dw_pcie *pci)
+/* Link state from PORT_LINK_DEBUG1: up and not in training */
+bool dw_pcie_link_up_debug_check(struct dw_pcie *pci)
{
u32 val;
- if (pci->ops && pci->ops->link_up)
- return pci->ops->link_up(pci);
-
val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG1);
return ((val & PORT_LINK_DEBUG1_LINK_UP) &&
(!(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING)));
}
+EXPORT_SYMBOL_GPL(dw_pcie_link_up_debug_check);
+
+bool dw_pcie_link_up(struct dw_pcie *pci)
+{
+ if (pci->ops && pci->ops->link_up)
+ return pci->ops->link_up(pci);
+
+ return dw_pcie_link_up_debug_check(pci);
+}
EXPORT_SYMBOL_GPL(dw_pcie_link_up);
void dw_pcie_upconfig_setup(struct dw_pcie *pci)
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 419932488280..2ce61709b58c 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -618,6 +618,7 @@ int dw_pcie_write(void __iomem *addr, int size, u32 val);
u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size);
void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val);
void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val);
+bool dw_pcie_link_up_debug_check(struct dw_pcie *pci);
bool dw_pcie_link_up(struct dw_pcie *pci);
void dw_pcie_upconfig_setup(struct dw_pcie *pci);
int dw_pcie_wait_for_link(struct dw_pcie *pci);
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [PATCH v3 03/18] PCI: rcar-gen4: Check live link status in link_up()
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-10-06 8:46 ` [PATCH v3 01/18] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
2026-10-06 8:46 ` [PATCH v3 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:53 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 04/18] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
` (15 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
rcar_gen4_pcie_link_up() checks link state using SMLH_LINK_UP and
RDLH_LINK_UP in PCIEINTSTS0. However, these bits do not reflect the live
link state. On an R-Car S4, after taking down the endpoint, a link-down
interrupt saw PCIEINTSTS0 = 0x20a000c5 with both bits still set. Even
after resetting the controller with the LTSSM back in Polling, they read
0xa000c5, still set.
As a result, dw_pcie_link_up() keeps reporting the link as up after it
has gone down. That defeats the check in dw_pcie_other_conf_map_bus(),
which is supposed to stop config accesses to downstream devices while
the link is down, so such accesses go out on the dead link and stall the
host. It also makes the callback useless for the link-down recovery
added later, which has to wait for the link to actually come back after
resetting the controller.
Keep the APP link-up event check from Figure 104.5 of the R-Car S4
reference manual, but also require PORT_LINK_DEBUG1 to report the link
up and not in training, through dw_pcie_link_up_debug_check(). The
callback then rejects a downed link even if the APP link-up events
remain latched.
Clear the APP latches before enabling LTSSM to discard events from a
previous start, and only read them in .link_up(). RC startup uses
dw_pcie_wait_for_link() to poll the combined condition.
Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode")
Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Tested-by: Marek Vasut <marek.vasut+renesas@mailbox.org> # R-Car V4H
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Use dw_pcie_link_up_debug_check() from the preceding patch instead
of open-coding the PORT_LINK_DEBUG1 check. (Marek)
- Carry Marek's R-b and T-b tags, as the only change is using the new
helper.
v2: https://lore.kernel.org/r/20260928165230.3397664-3-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index fceecbd16355..b101ad7dbc31 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -53,6 +53,9 @@
#define PCIEDMAINTSTSEN 0x0314
#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
+/* PCIe Interrupt Status 0 Clear */
+#define PCIEINTSTS0CLR 0x0340
+
/* Port Logic Registers 89 */
#define PRTLGC89 0x0b70
@@ -120,8 +123,11 @@ static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
val = readl(rcar->base + PCIEINTSTS0);
mask = RDLH_LINK_UP | SMLH_LINK_UP;
+ if ((val & mask) != mask)
+ return false;
- return (val & mask) == mask;
+ /* The APP link-up events remain latched after the link goes down. */
+ return dw_pcie_link_up_debug_check(dw);
}
/*
@@ -208,6 +214,9 @@ static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
+ /* Clear link-up events from the previous start. */
+ writel(RDLH_LINK_UP | SMLH_LINK_UP, rcar->base + PCIEINTSTS0CLR);
+
if (rcar->drvdata->ltssm_control) {
ret = rcar->drvdata->ltssm_control(rcar, true);
if (ret)
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 03/18] PCI: rcar-gen4: Check live link status in link_up()
2026-10-06 8:46 ` [PATCH v3 03/18] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
@ 2026-10-06 8:53 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:53 UTC (permalink / raw)
To: Koichiro Den; +Cc: devicetree, wsa+renesas, robh, conor+dt, linux-pci
> rcar_gen4_pcie_link_up() checks link state using SMLH_LINK_UP and
> RDLH_LINK_UP in PCIEINTSTS0. However, these bits do not reflect the live
> link state. On an R-Car S4, after taking down the endpoint, a link-down
> interrupt saw PCIEINTSTS0 = 0x20a000c5 with both bits still set. Even
> after resetting the controller with the LTSSM back in Polling, they read
> 0xa000c5, still set.
>
> As a result, dw_pcie_link_up() keeps reporting the link as up after it
> has gone down. That defeats the check in dw_pcie_other_conf_map_bus(),
> which is supposed to stop config accesses to downstream devices while
> the link is down, so such accesses go out on the dead link and stall the
> host. It also makes the callback useless for the link-down recovery
> added later, which has to wait for the link to actually come back after
> resetting the controller.
>
> Keep the APP link-up event check from Figure 104.5 of the R-Car S4
> reference manual, but also require PORT_LINK_DEBUG1 to report the link
> up and not in training, through dw_pcie_link_up_debug_check(). The
> callback then rejects a downed link even if the APP link-up events
> remain latched.
>
> Clear the APP latches before enabling LTSSM to discard events from a
> previous start, and only read them in .link_up(). RC startup uses
> dw_pcie_wait_for_link() to poll the combined condition.
>
> Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode")
> Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Tested-by: Marek Vasut <marek.vasut+renesas@mailbox.org> # R-Car V4H
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=3
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 04/18] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (2 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 03/18] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:52 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 05/18] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
` (14 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
The intreq_pcim_lperr interrupt carries Root Port AER notifications,
separately from the integrated MSI receiver interrupt. Describe it with
the "aer" name so the controller driver can use it for Root Port error
reporting.
Keep the fifth interrupt optional so existing device trees remain
valid. Extend the example accordingly.
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Add Krzysztof's R-b tag. No code changes.
v2: https://lore.kernel.org/r/20260928165230.3397664-4-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 362c43a1a15d..b24d0fdb12cb 100644
--- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
@@ -26,14 +26,17 @@ properties:
- const: renesas,rcar-gen5-pcie4 # R-Car Gen5 PCIe4
interrupts:
- maxItems: 4
+ minItems: 4
+ maxItems: 5
interrupt-names:
+ minItems: 4
items:
- const: msi
- const: dma
- const: sft_ce
- const: app
+ - const: aer
clocks:
maxItems: 2
@@ -139,8 +142,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 related [flat|nested] 38+ messages in thread* [PATCH v3 05/18] PCI: dwc: Export dw_handle_msi_irq()
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (3 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 04/18] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:51 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 06/18] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization Koichiro Den
` (13 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
Not every platform brings the iMSI-RX interrupt out as a dedicated
line. On spear13xx and keembay it is a status bit in the SoC wrapper's
interrupt register that the glue driver has to check and clear, and on
dra7xx it shares the line with other sources the glue driver has to
demultiplex. These drivers set msi_irq[0] to -ENODEV so the core does
not install its chained handler, and service the MSIs from a handler of
their own. spear13xx and keembay call dw_handle_msi_irq() for that.
Both are built-in, so the function was never exported. dra7xx can be a
module and has an MSI loop of its own.
Export the function so that modular glue drivers can use it as well.
The pcie-rcar-gen4 driver, where both situations apply, is the first user.
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Add Marek's R-b tag.
- Refer to the driver by name in the commit message. No code changes.
v2: https://lore.kernel.org/r/20260928165230.3397664-5-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-designware-host.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 77aa79ef0ccf..63b38532d069 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -85,6 +85,7 @@ void dw_handle_msi_irq(struct dw_pcie_rp *pp)
generic_handle_demux_domain_irq(pp->irq_domain, irq_off + pos);
}
}
+EXPORT_SYMBOL_GPL(dw_handle_msi_irq);
/* Chained MSI interrupt service routine */
static void dw_chained_msi_isr(struct irq_desc *desc)
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 05/18] PCI: dwc: Export dw_handle_msi_irq()
2026-10-06 8:46 ` [PATCH v3 05/18] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
@ 2026-10-06 8:51 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:51 UTC (permalink / raw)
To: Koichiro Den; +Cc: conor+dt, robh, wsa+renesas, linux-pci, devicetree
> Not every platform brings the iMSI-RX interrupt out as a dedicated
> line. On spear13xx and keembay it is a status bit in the SoC wrapper's
> interrupt register that the glue driver has to check and clear, and on
> dra7xx it shares the line with other sources the glue driver has to
> demultiplex. These drivers set msi_irq[0] to -ENODEV so the core does
> not install its chained handler, and service the MSIs from a handler of
> their own. spear13xx and keembay call dw_handle_msi_irq() for that.
> Both are built-in, so the function was never exported. dra7xx can be a
> module and has an MSI loop of its own.
>
> Export the function so that modular glue drivers can use it as well.
> The pcie-rcar-gen4 driver, where both situations apply, is the first user.
>
> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=5
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 06/18] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (4 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 05/18] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:51 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 07/18] PCI: rcar-gen4: Assert resets when Gen5 SoC PHY initialization fails Koichiro Den
` (12 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
Move common_deinit() next to common_init(), ahead of
rcar_gen5_pcie_init(), which will call it when PHY initialization fails.
Place rcar_gen5_pcie_deinit() before rcar_gen5_pcie_init() as well.
Keep these moves separate from the following initialization refactoring
to make that patch easier to review. The function bodies are unchanged.
No functional changes.
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Add Marek's R-b tag.
- Use function names in the commit message to avoid ambiguous Gen5
references. No code changes.
v2: https://lore.kernel.org/r/20260928165230.3397664-6-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 30 ++++++++++-----------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index b101ad7dbc31..0a845b67f6c1 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -301,6 +301,15 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
return ret;
}
+static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
+{
+ struct dw_pcie *dw = &rcar->dw;
+
+ reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
+ reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
+ clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
+}
+
static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
@@ -326,6 +335,12 @@ static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
return 0;
}
+static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar)
+{
+ phy_exit(rcar->phy);
+ rcar_gen4_pcie_common_deinit(rcar);
+}
+
static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
@@ -369,21 +384,6 @@ static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
return ret;
}
-static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
-{
- struct dw_pcie *dw = &rcar->dw;
-
- reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
- reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
- clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
-}
-
-static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar)
-{
- phy_exit(rcar->phy);
- rcar_gen4_pcie_common_deinit(rcar);
-}
-
static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
{
struct device *dev = rcar->dw.dev;
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [PATCH v3 07/18] PCI: rcar-gen4: Assert resets when Gen5 SoC PHY initialization fails
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (5 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 06/18] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:54 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
` (11 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
common_init() enables the clocks and deasserts the power and
Application/Local resets before phy_init(). If phy_init() fails, the
error path only disables the clocks, leaving those resets deasserted.
Assert both resets before disabling the clocks to unwind the completed
common initialization.
Fixes: 3283c4db6009 ("PCI: rcar-gen4: Add support for R-Car X5H PCIe4")
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Add the Fixes tag and Marek's R-b tag.
- Clarify the SoC generation in the subject. No code changes.
v2: https://lore.kernel.org/r/20260928165230.3397664-7-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 0a845b67f6c1..e1b748a9d58b 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -379,7 +379,7 @@ static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
return 0;
err_unprepare:
- clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
+ rcar_gen4_pcie_common_deinit(rcar);
return ret;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [PATCH v3 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (6 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 07/18] PCI: rcar-gen4: Assert resets when Gen5 SoC PHY initialization fails Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:50 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 09/18] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
` (10 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
Factor out the hardware setup needed by Root Port recovery into helpers.
Recovery needs to reset and restore the controller while keeping its
clocks enabled and the R-Car Gen5 SoC PHY initialized.
- From rcar_gen4_pcie_common_init(), extract the power reset and mode
setup. Leave clock enabling and Application/Local reset control in
common_init(), so recovery can keep the APP registers accessible.
- From rcar_gen4_v4h_v4m_pcie_init() and rcar_gen5_pcie_init(), extract
the SoC-specific register setup. This allows recovery to restore those
settings without taking another clock or PHY initialization reference.
- From rcar_gen4_pcie_host_init(), extract the Root Port register setup
and PERST# release. Both paths need this host-specific sequence after
the per-SoC initialization or reset.
No functional changes.
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Add Marek's R-b tag.
- Clarify the SoC generation and name the .init() functions in the
commit message. No code changes.
v2: https://lore.kernel.org/r/20260928165230.3397664-8-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 125 ++++++++++++--------
1 file changed, 77 insertions(+), 48 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index e1b748a9d58b..cbd22e6bb44b 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -234,20 +234,13 @@ static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)
rcar->drvdata->ltssm_control(rcar, false);
}
-static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
+/* Clocks must be enabled and the Application/Local reset deasserted. */
+static int rcar_gen4_pcie_reset(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
u32 val;
int ret;
- ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
- if (ret) {
- dev_err(dw->dev, "Enabling core clocks failed\n");
- return ret;
- }
-
- reset_control_deassert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
-
if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
/*
@@ -266,8 +259,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)
@@ -277,7 +269,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.
@@ -293,6 +285,26 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
fsleep(1000);
return 0;
+}
+
+static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
+{
+ struct dw_pcie *dw = &rcar->dw;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
+ if (ret) {
+ dev_err(dw->dev, "Enabling core clocks failed\n");
+ return ret;
+ }
+
+ reset_control_deassert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
+
+ ret = rcar_gen4_pcie_reset(rcar);
+ if (ret)
+ goto err_unprepare;
+
+ return 0;
err_unprepare:
reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
@@ -310,16 +322,10 @@ static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
}
-static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
+static void rcar_gen4_v4h_v4m_pcie_configure(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
u32 val;
- int ret;
-
- /* R-Car Gen4 common initialization. */
- ret = rcar_gen4_pcie_common_init(rcar);
- if (ret)
- return ret;
/* R-Car V4H and V4M specific additional initialization. */
val = dw_pcie_readl_dbi(dw, PORT_LANE_SKEW);
@@ -331,6 +337,17 @@ static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
val = readl(rcar->base + PCIEPWRMNGCTRL);
val |= APP_CLK_REQ_N | APP_CLK_PM_EN;
writel(val, rcar->base + PCIEPWRMNGCTRL);
+}
+
+static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
+{
+ int ret;
+
+ ret = rcar_gen4_pcie_common_init(rcar);
+ if (ret)
+ return ret;
+
+ rcar_gen4_v4h_v4m_pcie_configure(rcar);
return 0;
}
@@ -341,22 +358,11 @@ static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar)
rcar_gen4_pcie_common_deinit(rcar);
}
-static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
+static void rcar_gen5_pcie_configure(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
- int ret;
u32 val;
- /* R-Car Gen4 and Gen5 common initialization. */
- ret = rcar_gen4_pcie_common_init(rcar);
- if (ret)
- return ret;
-
- /* R-Car Gen5 specific additional initialization. */
- ret = phy_init(rcar->phy);
- if (ret)
- goto err_unprepare;
-
dw_pcie_dbi_ro_wr_en(dw);
val = dw_pcie_readl_dbi(dw, PORT_LANE_SKEW);
@@ -375,13 +381,25 @@ static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
val |= APP_CLK_REQ_N | APP_CLK_PM_EN |
APP_READY_ENTR_L23 | APP_REQ_ENTR_L1;
writel(val, rcar->base + PCIEPWRMNGCTRL);
+}
- return 0;
+static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
+{
+ int ret;
-err_unprepare:
- rcar_gen4_pcie_common_deinit(rcar);
+ ret = rcar_gen4_pcie_common_init(rcar);
+ if (ret)
+ return ret;
- return ret;
+ ret = phy_init(rcar->phy);
+ if (ret) {
+ rcar_gen4_pcie_common_deinit(rcar);
+ return ret;
+ }
+
+ rcar_gen5_pcie_configure(rcar);
+
+ return 0;
}
static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
@@ -619,21 +637,11 @@ static void rcar_gen4_pcie_host_perst_assert(struct dw_pcie_rp *pp, bool assert)
}
/* Host mode */
-static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
+static int rcar_gen4_pcie_host_setup(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
- struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
- if (pp->bridge)
- pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
-
- rcar_gen4_pcie_host_perst_assert(pp, true);
-
- ret = rcar->drvdata->init(rcar);
- if (ret)
- return ret;
-
/*
* According to the section 3.5.7.2 "RC Mode" in DWC PCIe Dual Mode
* Rev.5.20a and 3.5.6.1 "RC mode" in DWC PCIe RC databook v5.20a, we
@@ -645,13 +653,34 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
ret = rcar_gen4_pcie_host_msi_init(pp);
if (ret)
- goto err;
+ return ret;
msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
-
rcar_gen4_pcie_host_perst_assert(pp, false);
return 0;
+}
+
+static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
+ struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+ int ret;
+
+ if (pp->bridge)
+ pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
+
+ rcar_gen4_pcie_host_perst_assert(pp, true);
+
+ ret = rcar->drvdata->init(rcar);
+ if (ret)
+ return ret;
+
+ ret = rcar_gen4_pcie_host_setup(pp);
+ if (ret)
+ goto err;
+
+ return 0;
err:
rcar->drvdata->deinit(rcar);
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition
2026-10-06 8:46 ` [PATCH v3 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
@ 2026-10-06 8:50 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:50 UTC (permalink / raw)
To: Koichiro Den; +Cc: devicetree, wsa+renesas, robh, conor+dt, linux-pci
> Factor out the hardware setup needed by Root Port recovery into helpers.
> Recovery needs to reset and restore the controller while keeping its
> clocks enabled and the R-Car Gen5 SoC PHY initialized.
>
> - From rcar_gen4_pcie_common_init(), extract the power reset and mode
> setup. Leave clock enabling and Application/Local reset control in
> common_init(), so recovery can keep the APP registers accessible.
>
> - From rcar_gen4_v4h_v4m_pcie_init() and rcar_gen5_pcie_init(), extract
> the SoC-specific register setup. This allows recovery to restore those
> settings without taking another clock or PHY initialization reference.
>
> - From rcar_gen4_pcie_host_init(), extract the Root Port register setup
> and PERST# release. Both paths need this host-specific sequence after
> the per-SoC initialization or reset.
>
> No functional changes.
>
> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=8
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 09/18] PCI: rcar-gen4: Add Root Port reset support
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (7 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:55 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback Koichiro Den
` (9 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
Implement the host bridge reset_root_port() callback so PCI error
recovery can reset and reinitialize the R-Car controller. This also
provides the reset operation for the link-down handling added later.
Reinitialize without going through .deinit() and .init(), which would
release and re-acquire the clocks and the R-Car Gen5 SoC PHY: split the
SoC-specific register setup that the power reset wipes out into a
.configure callback and repeat only that, then restore the Root Port
registers and restart link training.
Rather than tracking which APP interrupt enables survive the power
reset, derive them from software state through a single helper. A flag
keeps the sources masked from the start of a reset until one succeeds,
so a failed reinitialization does not re-enable them against an
uninitialized controller.
Reject a reset while the controller is suspended, as a failed resume may
have left it powered down.
Track whether the R-Car Gen5 SoC PHY power-on has been requested so a
reset retry, or teardown after a failed reset, does not call
phy_power_off() twice. Continue to ignore the PHY power operation return
values.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Replace the per-SoC .reinit() callback with .configure and a common
reinitialization helper, folding the former patch 8 into this one.
(Marek)
- Drop the mutex: the PCI core already serializes the callers through
its device locks, and suspend/resume runs with those callers frozen
or quiesced. See:
https://lore.kernel.org/r/eivzz4zumuu32zrycw6wpt3orzk2dvlzuvj34krmbhjf7a3a6u@ccvu2nkupzid/
- Reject resets while the controller is suspended, including after a
failed resume, now that v3 is based on Marek's PM ops patch.
- Lock explicitly in the rcar_gen4_pcie_host_msi_init() error path
instead of mixing guard() with goto-based cleanup. (Sashiko)
- Spell out the SoC generations in the reset_root_port() comment.
(Marek)
- Revise the commit message.
v2: https://lore.kernel.org/r/20260928165230.3397664-10-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 141 +++++++++++++++++++-
1 file changed, 134 insertions(+), 7 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index cbd22e6bb44b..54260e62293b 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -99,6 +99,8 @@ struct rcar_gen4_pcie;
struct rcar_gen4_pcie_drvdata {
int (*init)(struct rcar_gen4_pcie *rcar);
void (*deinit)(struct rcar_gen4_pcie *rcar);
+ /* SoC-specific register setup, repeated after a power reset */
+ void (*configure)(struct rcar_gen4_pcie *rcar);
int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable);
int (*speed_control)(struct rcar_gen4_pcie *rcar);
enum dw_pcie_device_mode mode;
@@ -109,9 +111,17 @@ struct rcar_gen4_pcie {
void __iomem *base;
void __iomem *phy_base;
struct phy *phy;
+ bool phy_powered;
struct platform_device *pdev;
struct reset_control *perst;
const struct rcar_gen4_pcie_drvdata *drvdata;
+ /*
+ * The controller needs reinitializing; the APP interrupt sources
+ * stay masked until a reset succeeds.
+ */
+ bool reinit_pending;
+ /* Protects APP interrupt enable registers and their software state. */
+ raw_spinlock_t app_lock;
};
#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
@@ -322,6 +332,21 @@ static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
}
+/* Reset and restore registers while clocks and PHY remain initialized. */
+static int rcar_gen4_pcie_common_reinit(struct rcar_gen4_pcie *rcar)
+{
+ int ret;
+
+ ret = rcar_gen4_pcie_reset(rcar);
+ if (ret)
+ return ret;
+
+ if (rcar->drvdata->configure)
+ rcar->drvdata->configure(rcar);
+
+ return 0;
+}
+
static void rcar_gen4_v4h_v4m_pcie_configure(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
@@ -513,10 +538,31 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
return 0;
}
+static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
+{
+ u32 val;
+
+ lockdep_assert_held(&rcar->app_lock);
+
+ val = readl(rcar->base + PCIEINTSTS0EN);
+ val &= ~MSI_CTRL_INT;
+ if (!rcar->reinit_pending && pci_msi_enabled())
+ val |= MSI_CTRL_INT;
+ writel(val, rcar->base + PCIEINTSTS0EN);
+}
+
+static void rcar_gen4_pcie_app_irq_sync(struct rcar_gen4_pcie *rcar)
+{
+ guard(raw_spinlock_irqsave)(&rcar->app_lock);
+
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+ unsigned long flags;
u32 val;
int ret;
@@ -543,12 +589,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;
@@ -563,9 +604,11 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
writel(0, rcar->base + AXIINTCCONT);
/* Deconfigure MSI interrupt signal */
+ raw_spin_lock_irqsave(&rcar->app_lock, flags);
val = readl(rcar->base + PCIEINTSTS0EN);
val &= ~MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
+ raw_spin_unlock_irqrestore(&rcar->app_lock, flags);
return ret;
}
@@ -661,14 +704,87 @@ static int rcar_gen4_pcie_host_setup(struct dw_pcie_rp *pp)
return 0;
}
+static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
+{
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ rcar->reinit_pending = true;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ }
+}
+
+static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
+ bool recovered)
+{
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ rcar->reinit_pending = !recovered;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ }
+}
+
+/*
+ * R-Car Gen4 SoC PCIe controllers and R-Car Gen5 SoC PCIe4 controller have
+ * a single Root Port per instance, so the 'pci_dev' is ignored and the whole
+ * controller is reset. The PCI core serializes the callers through its
+ * device locks.
+ */
+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;
+
+ /* A failed resume may have left the controller powered down. */
+ if (dw->suspended)
+ return -EBUSY;
+
+ rcar_gen4_pcie_quiesce_irqs(rcar);
+
+ dw_pcie_stop_link(dw);
+
+ rcar_gen4_pcie_host_perst_assert(pp, true);
+
+ ret = rcar_gen4_pcie_common_reinit(rcar);
+ if (ret)
+ goto out;
+
+ ret = rcar_gen4_pcie_host_setup(pp);
+ if (ret)
+ goto out;
+
+ ret = dw_pcie_setup_rc(pp);
+ if (ret)
+ goto out;
+
+ ret = dw_pcie_start_link(dw);
+ if (ret)
+ goto out;
+
+ /* Ignore errors, the link may come up later */
+ dw_pcie_wait_for_link(dw);
+ dev_dbg(dev, "Root Port reset completed\n");
+
+out:
+ rcar_gen4_pcie_resume_irqs(rcar, !ret);
+ if (ret)
+ dev_err(dev, "Root Port reset failed (%d); interrupts stay masked until a reset succeeds\n",
+ ret);
+
+ return ret;
+}
+
static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
- if (pp->bridge)
+ if (pp->bridge) {
pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
+ pp->bridge->reset_root_port = rcar_gen4_pcie_reset_root_port;
+ }
rcar_gen4_pcie_host_perst_assert(pp, true);
@@ -708,6 +824,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_HOST))
return -ENODEV;
+ raw_spin_lock_init(&rcar->app_lock);
+
pp->num_vectors = MAX_MSI_IRQS;
/* Reserve an iATU window for the generic PME_Turn_Off implementation. */
pp->use_atu_msg = true;
@@ -944,11 +1062,17 @@ static int rcar_gen5_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
}
writel(val, rcar->base + PCIERSTCTRL1);
+ /* A reset retry may find the PHY already powered off. */
+ if (enable == rcar->phy_powered)
+ return 0;
+
if (enable)
phy_power_on(rcar->phy);
else
phy_power_off(rcar->phy);
+ rcar->phy_powered = enable;
+
return 0;
}
@@ -1141,6 +1265,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = {
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
.init = rcar_gen4_v4h_v4m_pcie_init,
.deinit = rcar_gen4_pcie_common_deinit,
+ .configure = rcar_gen4_v4h_v4m_pcie_configure,
.ltssm_control = rcar_gen4_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_control,
.mode = DW_PCIE_RC_TYPE,
@@ -1149,6 +1274,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
.init = rcar_gen4_v4h_v4m_pcie_init,
.deinit = rcar_gen4_pcie_common_deinit,
+ .configure = rcar_gen4_v4h_v4m_pcie_configure,
.ltssm_control = rcar_gen4_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_control,
.mode = DW_PCIE_EP_TYPE,
@@ -1157,6 +1283,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen5_pcie = {
.init = rcar_gen5_pcie_init,
.deinit = rcar_gen5_pcie_deinit,
+ .configure = rcar_gen5_pcie_configure,
.ltssm_control = rcar_gen5_pcie_ltssm_control,
.speed_control = rcar_gen5_pcie_speed_control,
.mode = DW_PCIE_RC_TYPE,
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 09/18] PCI: rcar-gen4: Add Root Port reset support
2026-10-06 8:46 ` [PATCH v3 09/18] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
@ 2026-10-06 8:55 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:55 UTC (permalink / raw)
To: Koichiro Den; +Cc: linux-pci, wsa+renesas, robh, conor+dt, devicetree
> Implement the host bridge reset_root_port() callback so PCI error
> recovery can reset and reinitialize the R-Car controller. This also
> provides the reset operation for the link-down handling added later.
>
> Reinitialize without going through .deinit() and .init(), which would
> release and re-acquire the clocks and the R-Car Gen5 SoC PHY: split the
> SoC-specific register setup that the power reset wipes out into a
> .configure callback and repeat only that, then restore the Root Port
> registers and restart link training.
>
> Rather than tracking which APP interrupt enables survive the power
> reset, derive them from software state through a single helper. A flag
> keeps the sources masked from the start of a reset until one succeeds,
> so a failed reinitialization does not re-enable them against an
> uninitialized controller.
>
> Reject a reset while the controller is suspended, as a failed resume may
> have left it powered down.
>
> Track whether the R-Car Gen5 SoC PHY power-on has been requested so a
> reset retry, or teardown after a failed reset, does not call
> phy_power_off() twice. Continue to ignore the PHY power operation return
> values.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=9
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (8 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 09/18] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 9:01 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
` (8 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
dw_pcie_host_deinit() frees the iMSI-RX domain before calling the host
.deinit() callback. For a core-owned parent interrupt,
dw_pcie_free_msi() removes the chained handler right before the domain.
A glue driver that owns the parent interrupt and demultiplexes the MSIs
itself through dw_handle_msi_irq() can only stop its handler in
.deinit(), after the domain is gone, so a late interrupt can still
dispatch into the freed domain.
Remove the domain after .deinit(), in dw_pcie_host_deinit() and in the
dw_pcie_host_init() unwind. Keep removing the chained handlers of
core-owned interrupts before .deinit(), which powers the controller
down. Clear pp->irq_domain when the domain is removed so the unwind can
tell whether it exists.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- New patch. In v2 the R-Car driver freed its parent IRQ in .deinit(),
after the core had already removed the iMSI-RX domain. This closes
the known gap noted under v2 patch 10:
https://lore.kernel.org/r/20260928165230.3397664-11-den@valinux.co.jp/
.../pci/controller/dwc/pcie-designware-host.c | 31 ++++++++++++++-----
1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 63b38532d069..774e1936d999 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -228,7 +228,7 @@ int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
}
EXPORT_SYMBOL_GPL(dw_pcie_allocate_domains);
-void dw_pcie_free_msi(struct dw_pcie_rp *pp)
+static void dw_pcie_remove_msi_handlers(struct dw_pcie_rp *pp)
{
u32 ctrl;
@@ -236,8 +236,18 @@ void dw_pcie_free_msi(struct dw_pcie_rp *pp)
if (pp->msi_irq[ctrl] > 0)
irq_set_chained_handler_and_data(pp->msi_irq[ctrl], NULL, NULL);
}
+}
+static void dw_pcie_free_msi_domain(struct dw_pcie_rp *pp)
+{
irq_domain_remove(pp->irq_domain);
+ pp->irq_domain = NULL;
+}
+
+void dw_pcie_free_msi(struct dw_pcie_rp *pp)
+{
+ dw_pcie_remove_msi_handlers(pp);
+ dw_pcie_free_msi_domain(pp);
}
EXPORT_SYMBOL_GPL(dw_pcie_free_msi);
@@ -633,7 +643,7 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
ret = of_pci_get_equalization_presets(dev, &pp->presets, pci->num_lanes);
if (ret)
- goto err_free_msi;
+ goto err_deinit_host;
/*
* Allocate the resource for MSG TLP before programming the iATU
@@ -649,7 +659,7 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
ret = dw_pcie_edma_detect(pci);
if (ret)
- goto err_free_msi;
+ goto err_deinit_host;
ret = dw_pcie_setup_rc(pp);
if (ret)
@@ -686,14 +696,16 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
err_remove_edma:
dw_pcie_edma_remove(pci);
-err_free_msi:
- if (pp->use_imsi_rx)
- dw_pcie_free_msi(pp);
-
err_deinit_host:
+ if (pp->use_imsi_rx && pp->irq_domain)
+ dw_pcie_remove_msi_handlers(pp);
+
if (pp->ops->deinit)
pp->ops->deinit(pp);
+ if (pp->use_imsi_rx && pp->irq_domain)
+ dw_pcie_free_msi_domain(pp);
+
err_free_ecam:
if (pp->cfg)
pci_ecam_free(pp->cfg);
@@ -718,11 +730,14 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)
dw_pcie_edma_remove(pci);
if (pp->use_imsi_rx)
- dw_pcie_free_msi(pp);
+ dw_pcie_remove_msi_handlers(pp);
if (pp->ops->deinit)
pp->ops->deinit(pp);
+ if (pp->use_imsi_rx)
+ dw_pcie_free_msi_domain(pp);
+
if (pp->cfg)
pci_ecam_free(pp->cfg);
}
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback
2026-10-06 8:46 ` [PATCH v3 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback Koichiro Den
@ 2026-10-06 9:01 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 9:01 UTC (permalink / raw)
To: Koichiro Den; +Cc: conor+dt, devicetree, robh, linux-pci, wsa+renesas
> dw_pcie_host_deinit() frees the iMSI-RX domain before calling the host
> .deinit() callback. For a core-owned parent interrupt,
> dw_pcie_free_msi() removes the chained handler right before the domain.
> A glue driver that owns the parent interrupt and demultiplexes the MSIs
> itself through dw_handle_msi_irq() can only stop its handler in
> .deinit(), after the domain is gone, so a late interrupt can still
> dispatch into the freed domain.
>
> Remove the domain after .deinit(), in dw_pcie_host_deinit() and in the
> dw_pcie_host_init() unwind. Keep removing the chained handlers of
> core-owned interrupts before .deinit(), which powers the controller
> down. Clear pp->irq_domain when the domain is removed so the unwind can
> tell whether it exists.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=10
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (9 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:56 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 12/18] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
` (7 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, 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 SoCs, intreq_pcim_sub ("msi") carries more than the
integrated MSI receiver: the controller's reset requests and the Root
Port's PME and bandwidth notifications are signalled on the same line,
and the following patches need to handle them. With the DesignWare core
owning the line through its chained handler, the driver would have to
hook into that handler when iMSI-RX is used and request the line itself
otherwise.
Instead, request the interrupt in the driver in all configurations and
set pp->msi_irq[0] to -ENODEV so the core does not install its chained
handler, as spear13xx, keembay and dra7xx do. The handler demultiplexes
the MSIs through dw_handle_msi_irq() when the APP block reports
msi_ctrl_int, in hard IRQ context like the chained handler did. With an
external MSI controller or pci=nomsi the iMSI-RX is not set up, so keep
msi_ctrl_int masked, and the handler has nothing to do there yet.
Request the interrupt in probe with IRQF_NO_AUTOEN and enable it at the
end of .init(), before endpoint drivers can use MSIs from their probe.
Disable it in .deinit(), before the controller is powered down, and
around a Root Port reset, as the handler reads the controller registers.
Suspend and resume go through the same two callbacks.
The DT routes downstream INTx to the same line, but the driver has never
supported INTx (no INTx domain, INTx enables never set), so requesting
the line exclusively takes nothing away.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Request the IRQ in probe with IRQF_NO_AUTOEN, so a failure does not
toggle PERST#, and enable it in .init().
- Keep the IRQ registered across suspend/resume, disabling it in
.deinit() and re-enabling it in .init(). This fixes the unbalanced
enable Geert reported when combining v2 with the PM ops patch:
https://lore.kernel.org/CAMuHMdXoKFCroL1d6-fLwYR0UkLBL7suKy=rOFavioLd7rC4OA@mail.gmail.com/
- Stop the handler in .deinit() while the DWC MSI domain is still
alive, which the previous patch makes possible. This closes the
known gap noted in v2.
- Request the IRQ with devm. The handler is stopped in .deinit(), so
releasing the IRQ at driver detach is no longer too late.
- Revise the commit message.
v2: https://lore.kernel.org/r/20260928165230.3397664-11-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 79 ++++++++++++++++++++-
1 file changed, 78 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 54260e62293b..d51c0fc5ff36 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -115,6 +115,8 @@ struct rcar_gen4_pcie {
struct platform_device *pdev;
struct reset_control *perst;
const struct rcar_gen4_pcie_drvdata *drvdata;
+ /* intreq_pcim_sub ("msi"): iMSI-RX and other controller notifications */
+ int msi_irq;
/*
* The controller needs reinitializing; the APP interrupt sources
* stay masked until a reset succeeds.
@@ -540,13 +542,14 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
{
+ struct dw_pcie_rp *pp = &rcar->dw.pp;
u32 val;
lockdep_assert_held(&rcar->app_lock);
val = readl(rcar->base + PCIEINTSTS0EN);
val &= ~MSI_CTRL_INT;
- if (!rcar->reinit_pending && pci_msi_enabled())
+ if (!rcar->reinit_pending && pp->use_imsi_rx && pci_msi_enabled())
val |= MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
}
@@ -613,6 +616,54 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
return ret;
}
+/*
+ * intreq_pcim_sub carries the iMSI-RX interrupt along with other controller
+ * notifications, so the driver owns it instead of the DesignWare core (see
+ * pp->msi_irq[0]) and demultiplexes the MSIs itself.
+ */
+static irqreturn_t rcar_gen4_pcie_msi_irq_handler(int irq, void *data)
+{
+ struct rcar_gen4_pcie *rcar = data;
+ u32 status;
+
+ status = readl(rcar->base + PCIEINTSTS0) &
+ readl(rcar->base + PCIEINTSTS0EN);
+ if (!(status & MSI_CTRL_INT))
+ return IRQ_NONE;
+
+ dw_handle_msi_irq(&rcar->dw.pp);
+
+ return IRQ_HANDLED;
+}
+
+static int rcar_gen4_pcie_msi_irq_init(struct rcar_gen4_pcie *rcar)
+{
+ struct device *dev = rcar->dw.dev;
+ int irq, ret;
+
+ irq = platform_get_irq_byname(rcar->pdev, "msi");
+ if (irq < 0)
+ return irq;
+
+ /*
+ * Child IRQ flow handlers require local IRQs to be disabled. Keep this
+ * handler in hard IRQ context on PREEMPT_RT.
+ *
+ * Keep the IRQ registered for the lifetime of the driver. .init()
+ * enables it once the controller is set up, and .deinit() disables it
+ * before the controller is powered down.
+ */
+ ret = devm_request_irq(dev, irq, rcar_gen4_pcie_msi_irq_handler,
+ IRQF_NO_THREAD | IRQF_NO_AUTOEN, dev_name(dev),
+ rcar);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request \"msi\" IRQ\n");
+
+ rcar->msi_irq = irq;
+
+ return 0;
+}
+
static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
struct pci_dev *dev)
{
@@ -710,6 +761,9 @@ static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
rcar->reinit_pending = true;
rcar_gen4_pcie_app_irq_sync_locked(rcar);
}
+
+ /* The MSI status lives in DBI; keep the handler away during the reset. */
+ disable_irq(rcar->msi_irq);
}
static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
@@ -719,6 +773,8 @@ static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
rcar->reinit_pending = !recovered;
rcar_gen4_pcie_app_irq_sync_locked(rcar);
}
+
+ enable_irq(rcar->msi_irq);
}
/*
@@ -796,6 +852,13 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
goto err;
+ /*
+ * Undo IRQF_NO_AUTOEN at probe, or the disable in .deinit() on resume.
+ * The iMSI-RX interrupts stay disabled until dw_pcie_setup_rc(), so the
+ * handler cannot run before the MSI domain exists.
+ */
+ rcar_gen4_pcie_resume_irqs(rcar, true);
+
return 0;
err:
@@ -808,6 +871,9 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+ /* Stop the handler before asserting reset and disabling the clocks. */
+ rcar_gen4_pcie_quiesce_irqs(rcar);
+
rcar_gen4_pcie_host_perst_assert(pp, true);
rcar->drvdata->deinit(rcar);
}
@@ -820,15 +886,26 @@ 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;
raw_spin_lock_init(&rcar->app_lock);
+ ret = rcar_gen4_pcie_msi_irq_init(rcar);
+ if (ret)
+ return ret;
+
pp->num_vectors = MAX_MSI_IRQS;
/* Reserve an iATU window for the generic PME_Turn_Off implementation. */
pp->use_atu_msg = true;
+ /*
+ * intreq_pcim_sub ("msi") is shared with other controller
+ * notifications: the driver requests it itself and demultiplexes the
+ * iMSI-RX MSIs from its own handler.
+ */
+ pp->msi_irq[0] = -ENODEV;
pp->ops = &rcar_gen4_pcie_host_ops;
return dw_pcie_host_init(pp);
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt
2026-10-06 8:46 ` [PATCH v3 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
@ 2026-10-06 8:56 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:56 UTC (permalink / raw)
To: Koichiro Den; +Cc: devicetree, wsa+renesas, conor+dt, linux-pci, robh
> On R-Car Gen4 SoCs, intreq_pcim_sub ("msi") carries more than the
> integrated MSI receiver: the controller's reset requests and the Root
> Port's PME and bandwidth notifications are signalled on the same line,
> and the following patches need to handle them. With the DesignWare core
> owning the line through its chained handler, the driver would have to
> hook into that handler when iMSI-RX is used and request the line itself
> otherwise.
>
> Instead, request the interrupt in the driver in all configurations and
> set pp->msi_irq[0] to -ENODEV so the core does not install its chained
> handler, as spear13xx, keembay and dra7xx do. The handler demultiplexes
> the MSIs through dw_handle_msi_irq() when the APP block reports
> msi_ctrl_int, in hard IRQ context like the chained handler did. With an
> external MSI controller or pci=nomsi the iMSI-RX is not set up, so keep
> msi_ctrl_int masked, and the handler has nothing to do there yet.
>
> Request the interrupt in probe with IRQF_NO_AUTOEN and enable it at the
> end of .init(), before endpoint drivers can use MSIs from their probe.
> Disable it in .deinit(), before the controller is powered down, and
> around a Root Port reset, as the handler reads the controller registers.
> Suspend and resume go through the same two callbacks.
>
> The DT routes downstream INTx to the same line, but the driver has never
> supported INTx (no INTx domain, INTx enables never set), so requesting
> the line exclusively takes nothing away.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=11
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 12/18] PCI: rcar-gen4: Recover the Root Port on link down
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (10 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:58 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
` (6 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
On R-Car, the controller's reset requests (smlh_req_rst_not,
link_req_rst_not) arrive on intreq_pcim_sub, the interrupt the MSIs are
demultiplexed from, so DBI is read as soon as the link goes down. On
R-Car S4 that is a hazard: DBI accesses issued within a few hundred
microseconds of an unexpected link down do not complete and hang the
host. In testing, the first Root Port config read after powering off
the link partner hung unless delayed by ~300 us.
Check the APP reset status in the interrupt handler before DBI is
touched. When a reset request is latched, flag the controller as
needing reinitialization, which masks the sources, ack the request and
schedule recovery work. The work calls pci_host_handle_link_down() on
the Root Port, which runs the AER-style recovery and resets the
controller through reset_root_port(), holding a reference on the port.
If the Root Port is absent when the work runs, reset the controller
directly so that successful reinitialization can restore interrupt
delivery. Hold the rescan lock across the absence check and reset.
The APP reset-status check also reports a recovery already pending,
whether started by another interrupt or left over from a failed reset,
so the handler issues no further DBI accesses until a reset succeeds.
Arm detection after initial PCI enumeration, so recovery does not
interrupt device discovery or resource assignment. Only unmasked status
bits are handled and pending latches are cleared when the sources are
re-enabled, so requests recorded during probe or the reset itself do
not trigger another recovery. Teardown only disarms the detection: MSI
delivery has to keep working while devices are removed.
Run recovery on a freezable workqueue to keep it outside device
suspend/resume. Reject suspend while a reset request is pending,
including one latched after the parent IRQs were suspended.
dw_pcie_suspend_noirq() would otherwise access DBI, and the queued
recovery can run once the system thaws. Disarm Root Port events during
suspend and re-arm them in .post_init() after resume has set up the
link, so taking the link down for suspend does not start recovery.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Keep recovery outside device suspend/resume with a freezable workqueue,
and reject suspend while recovery is pending.
- Disarm Root Port events during suspend until .post_init() re-arms them
after resume.
- Reset the controller directly if the Root Port is absent when the
work runs, so interrupt delivery can resume after a successful reset.
Serialize the absence check and reset against rescans. (Sashiko)
- Warn when suspend is refused because the controller needs
reinitializing.
v2: https://lore.kernel.org/r/20260928165230.3397664-12-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 127 ++++++++++++++++++++
1 file changed, 127 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index d51c0fc5ff36..b8a5bfaf0bbc 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 */
@@ -40,9 +41,18 @@
#define MSICAP0_MMESCAP_MASK GENMASK(19, 17)
#define MSICAP0_MSIE BIT(16)
+/* PCIe Reset Status */
+#define PCIERSTSTS 0x0020
+#define SMLH_REQ_RST_NOT BIT(2)
+#define LINK_REQ_RST_NOT BIT(1)
+#define LINK_DOWN_RESET_MASK (SMLH_REQ_RST_NOT | LINK_REQ_RST_NOT)
+
/* PCIe Interrupt Status 0 */
#define PCIEINTSTS0 0x0084
+/* PCIe Reset Status Enable */
+#define PCIERSTSTSEN 0x0300
+
/* PCIe Interrupt Status 0 Enable */
#define PCIEINTSTS0EN 0x0310
#define MSI_CTRL_INT BIT(26)
@@ -53,6 +63,9 @@
#define PCIEDMAINTSTSEN 0x0314
#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
+/* PCIe Reset Status Clear */
+#define PCIERSTSTSCLR 0x0330
+
/* PCIe Interrupt Status 0 Clear */
#define PCIEINTSTS0CLR 0x0340
@@ -117,6 +130,9 @@ struct rcar_gen4_pcie {
const struct rcar_gen4_pcie_drvdata *drvdata;
/* intreq_pcim_sub ("msi"): iMSI-RX and other controller notifications */
int msi_irq;
+ struct work_struct link_down_work;
+ /* Allow Root Port event handling after enumeration and until teardown. */
+ bool rp_events_armed;
/*
* The controller needs reinitializing; the APP interrupt sources
* stay masked until a reset succeeds.
@@ -543,15 +559,35 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie_rp *pp = &rcar->dw.pp;
+ bool rp_events = rcar->rp_events_armed && !rcar->reinit_pending;
u32 val;
lockdep_assert_held(&rcar->app_lock);
val = readl(rcar->base + PCIEINTSTS0EN);
val &= ~MSI_CTRL_INT;
+ /*
+ * Note that rcar->rp_events_armed is not checked here because device
+ * removal may still need iMSI-RX interrupts after Root Port events have
+ * been disarmed.
+ */
if (!rcar->reinit_pending && pp->use_imsi_rx && pci_msi_enabled())
val |= MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
+
+ val = readl(rcar->base + PCIERSTSTSEN);
+ if (rp_events) {
+ /*
+ * Clear latches recorded while the sources were masked, so
+ * stale requests do not fire as soon as they are re-enabled.
+ */
+ if (!(val & LINK_DOWN_RESET_MASK))
+ writel(LINK_DOWN_RESET_MASK, rcar->base + PCIERSTSTSCLR);
+ val |= LINK_DOWN_RESET_MASK;
+ } else {
+ val &= ~LINK_DOWN_RESET_MASK;
+ }
+ writel(val, rcar->base + PCIERSTSTSEN);
}
static void rcar_gen4_pcie_app_irq_sync(struct rcar_gen4_pcie *rcar)
@@ -616,6 +652,50 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
return ret;
}
+/*
+ * Consume a latched reset request and start recovery. Returns true if DBI
+ * must be left alone: a request was just consumed or recovery is pending.
+ */
+static bool rcar_gen4_pcie_handle_link_down(struct rcar_gen4_pcie *rcar)
+{
+ u32 status;
+
+ guard(raw_spinlock_irqsave)(&rcar->app_lock);
+
+ status = readl(rcar->base + PCIERSTSTS) &
+ readl(rcar->base + PCIERSTSTSEN) & LINK_DOWN_RESET_MASK;
+ if (status) {
+ /* The sources are only enabled while armed with no reinit pending. */
+ rcar->reinit_pending = true;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ writel(status, rcar->base + PCIERSTSTSCLR);
+ /* Keep recovery outside device suspend/resume. */
+ queue_work(system_freezable_wq, &rcar->link_down_work);
+ }
+
+ return rcar->reinit_pending;
+}
+
+static void rcar_gen4_pcie_link_down_work(struct work_struct *work)
+{
+ struct rcar_gen4_pcie *rcar =
+ container_of(work, struct rcar_gen4_pcie, link_down_work);
+ struct pci_host_bridge *bridge = rcar->dw.pp.bridge;
+ struct pci_dev *port;
+
+ /* Serialize the absence check and controller reset against rescans. */
+ pci_lock_rescan_remove();
+ port = pci_get_slot(bridge->bus, PCI_DEVFN(0, 0));
+ if (!port)
+ bridge->reset_root_port(bridge, NULL);
+ pci_unlock_rescan_remove();
+
+ if (port) {
+ pci_host_handle_link_down(port);
+ pci_dev_put(port);
+ }
+}
+
/*
* intreq_pcim_sub carries the iMSI-RX interrupt along with other controller
* notifications, so the driver owns it instead of the DesignWare core (see
@@ -626,6 +706,10 @@ static irqreturn_t rcar_gen4_pcie_msi_irq_handler(int irq, void *data)
struct rcar_gen4_pcie *rcar = data;
u32 status;
+ /* Check reset requests before MSI handling, which accesses DBI. */
+ if (rcar_gen4_pcie_handle_link_down(rcar))
+ return IRQ_HANDLED;
+
status = readl(rcar->base + PCIEINTSTS0) &
readl(rcar->base + PCIEINTSTS0EN);
if (!(status & MSI_CTRL_INT))
@@ -664,6 +748,28 @@ static int rcar_gen4_pcie_msi_irq_init(struct rcar_gen4_pcie *rcar)
return 0;
}
+/* Keep link-down recovery out of initial PCI enumeration. */
+static void rcar_gen4_pcie_rp_events_arm(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
+ struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+
+ guard(raw_spinlock_irqsave)(&rcar->app_lock);
+ rcar->rp_events_armed = true;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
+static void rcar_gen4_pcie_rp_events_disarm(struct rcar_gen4_pcie *rcar)
+{
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ rcar->rp_events_armed = false;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ }
+
+ /* Handlers queue the work under app_lock, so none can follow this. */
+ cancel_work_sync(&rcar->link_down_work);
+}
+
static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
struct pci_dev *dev)
{
@@ -871,6 +977,9 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+ /* Re-arm Root Port events in .post_init, after link setup. */
+ rcar_gen4_pcie_rp_events_disarm(rcar);
+
/* Stop the handler before asserting reset and disabling the clocks. */
rcar_gen4_pcie_quiesce_irqs(rcar);
@@ -881,6 +990,7 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = {
.init = rcar_gen4_pcie_host_init,
.deinit = rcar_gen4_pcie_host_deinit,
+ .post_init = rcar_gen4_pcie_rp_events_arm,
};
static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
@@ -892,6 +1002,7 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
return -ENODEV;
raw_spin_lock_init(&rcar->app_lock);
+ INIT_WORK(&rcar->link_down_work, rcar_gen4_pcie_link_down_work);
ret = rcar_gen4_pcie_msi_irq_init(rcar);
if (ret)
@@ -913,6 +1024,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
{
+ /* Cancel recovery work before removing the PCI bus */
+ rcar_gen4_pcie_rp_events_disarm(rcar);
dw_pcie_host_deinit(&rcar->dw.pp);
}
@@ -1310,6 +1423,20 @@ static int rcar_gen4_pcie_suspend_noirq(struct device *dev)
if (rcar->drvdata->mode != DW_PCIE_RC_TYPE)
return 0;
+ if (rcar->dw.suspended)
+ return 0;
+
+ /*
+ * Recovery work queued after the workqueues were frozen cannot run
+ * until thaw. Abort suspend so it runs then, instead of touching DBI
+ * here. This also catches requests latched after the parent IRQs were
+ * suspended.
+ */
+ if (rcar_gen4_pcie_handle_link_down(rcar)) {
+ dev_warn(dev, "Controller needs reinitializing, aborting suspend\n");
+ return -EBUSY;
+ }
+
return dw_pcie_suspend_noirq(&rcar->dw);
}
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 12/18] PCI: rcar-gen4: Recover the Root Port on link down
2026-10-06 8:46 ` [PATCH v3 12/18] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
@ 2026-10-06 8:58 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:58 UTC (permalink / raw)
To: Koichiro Den; +Cc: conor+dt, linux-pci, devicetree, robh, wsa+renesas
> On R-Car, the controller's reset requests (smlh_req_rst_not,
> link_req_rst_not) arrive on intreq_pcim_sub, the interrupt the MSIs are
> demultiplexed from, so DBI is read as soon as the link goes down. On
> R-Car S4 that is a hazard: DBI accesses issued within a few hundred
> microseconds of an unexpected link down do not complete and hang the
> host. In testing, the first Root Port config read after powering off
> the link partner hung unless delayed by ~300 us.
>
> Check the APP reset status in the interrupt handler before DBI is
> touched. When a reset request is latched, flag the controller as
> needing reinitialization, which masks the sources, ack the request and
> schedule recovery work. The work calls pci_host_handle_link_down() on
> the Root Port, which runs the AER-style recovery and resets the
> controller through reset_root_port(), holding a reference on the port.
> If the Root Port is absent when the work runs, reset the controller
> [ ... ]
> recovery can run once the system thaws. Disarm Root Port events during
> suspend and re-arm them in .post_init() after resume has set up the
> link, so taking the link down for suspend does not start recovery.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=12
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (11 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 12/18] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:51 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
` (5 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, 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. The controller on R-Car Gen4 SoCs
(DWC 5.20a) appears to lack SII2MSI, and its APP block merely latches
the signals into status registers wired to platform IRQs, so an MSI
targeted at the GIC ITS is never sent.
Add a no_rp_msi flag so glue drivers can request the same INTx fallback
regardless of the MSI controller in use. It is the counterpart of
'keep_rp_msi_en' from commit 3a4e8302e72f ("PCI: imx6: Keep Root Port
MSI capability with iMSI-RX to work around hardware bug"), which keeps
precedence: that one is needed for endpoint MSIs to work at all, whereas
hiding the capability only moves the Root Port services to INTx. The
removal stays in dw_pcie_setup_rc(), which also runs after a Root Port
reset.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Clarify the SoC generation in the commit message. No code changes.
drivers/pci/controller/dwc/pcie-designware-host.c | 6 ++++--
drivers/pci/controller/dwc/pcie-designware.h | 1 +
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 774e1936d999..32e179603cf4 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -1191,9 +1191,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 2ce61709b58c..d94116402b74 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -445,6 +445,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 related [flat|nested] 38+ messages in thread* Re: [PATCH v3 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
2026-10-06 8:46 ` [PATCH v3 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
@ 2026-10-06 8:51 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:51 UTC (permalink / raw)
To: Koichiro Den; +Cc: wsa+renesas, linux-pci, robh, conor+dt, devicetree
> 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. The controller on R-Car Gen4 SoCs
> (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>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=13
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (12 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:54 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
` (4 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, 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 SoCs, the APP block exposes the Root Port interrupt
signals in PCIEINTSTS0 and routes them to platform IRQs: AER to
intreq_pcim_lperr, and PME and bandwidth notifications to
intreq_pcim_sub. On an R-Car S4 with GIC ITS, a link retrain set
LnkSta.LBMS and latched PCIEINTSTS0[23] (cfg_bw_mgt_msi), but the Root
Port's MSI never fired while endpoint MSIs worked fine. With iMSI-RX the
Root Port MSI capabilities are already hidden, but its INTx maps to
intreq_pcim_sub. Note that even before this driver took over the IRQ
with rcar_gen4_pcie_msi_irq_handler, the port service was unable to
request it, as the DWC core used that IRQ for its chained MSI handler.
In either case, iMSI-RX or an external MSI controller, Root Port
interrupts are not delivered to the port services.
Set no_rp_msi so the Root Port services use INTx regardless of the MSI
controller. Use a virtual IRQ to deliver notifications from separate
platform IRQs to the standard handlers. Route AER from the optional
"aer" interrupt. DTs without it remain supported, but Root Port AER
stays unavailable.
The APP block may still receive MSI-form notifications because hiding
the Root Port's MSI capabilities does not clear MSICAP0.MSIE. Handle
these alongside the INTx-form level signals. Clear the MSI-form latch
before the AER handler clears Root Error Status, so a late APP clear
does not erase a new notification.
Check for reset requests before dispatch: on S4, DBI accesses from
the port services can hang the host immediately after link down.
The next patch also feeds PME and bandwidth notifications from "msi"
into this IRQ, so both parents may dispatch concurrently.
handle_level_irq() calls irq_ack() before checking whether it can run
the handlers. Keep the APP latches set while the IRQ is in progress or
disabled, so the level parents can retry through the reset checks.
This avoids losing notifications without a raw lock around port
services that take regular spinlocks.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Create the virtual Root Port IRQ and request the AER parent in probe,
keep them for the lifetime of the driver, and enable and disable the
parent in .init() and .deinit() like the "msi" one.
- Drop the dispatch lock of v2, which nested the port services' locks
inside a raw spinlock. Keep the MSI-form latches set in irq_ack()
while the virtual IRQ is in progress or disabled instead. (Sashiko)
- Mark the virtual IRQ as level-triggered, so a pending interrupt is
re-delivered by the parent lines rather than resent in software.
- Revise the commit message.
v2: https://lore.kernel.org/r/20260928165230.3397664-14-den@valinux.co.jp/
@Marek, I did not carry your R-b tag over, as the patch changed quite a
bit. I'd appreciate another look.
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 216 +++++++++++++++++++-
1 file changed, 213 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index b8a5bfaf0bbc..db0d5362eaed 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -14,10 +14,12 @@
#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/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
@@ -59,6 +61,11 @@
#define SMLH_LINK_UP BIT(7)
#define RDLH_LINK_UP BIT(6)
+/* Root Port notifications routed to intreq_pcim_lperr ("aer") */
+#define AER_RC_ERR_MSI BIT(29)
+#define AER_RC_ERR_INT BIT(27)
+#define RP_AER_MASK (AER_RC_ERR_MSI | AER_RC_ERR_INT)
+
/* PCIe DMA Interrupt Status Enable */
#define PCIEDMAINTSTSEN 0x0314
#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
@@ -68,6 +75,7 @@
/* PCIe Interrupt Status 0 Clear */
#define PCIEINTSTS0CLR 0x0340
+#define RP_LATCHED_MASK AER_RC_ERR_MSI
/* Port Logic Registers 89 */
#define PRTLGC89 0x0b70
@@ -133,6 +141,12 @@ struct rcar_gen4_pcie {
struct work_struct link_down_work;
/* Allow Root Port event handling after enumeration and until teardown. */
bool rp_events_armed;
+ /* Virtual Root Port INTx shared by the port services */
+ struct irq_domain *rp_irq_domain;
+ unsigned int rp_irq;
+ bool rp_irq_enabled;
+ /* intreq_pcim_lperr ("aer"): Root Port AER notifications, optional */
+ int aer_irq;
/*
* The controller needs reinitializing; the APP interrupt sources
* stay masked until a reset succeeds.
@@ -565,7 +579,7 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
lockdep_assert_held(&rcar->app_lock);
val = readl(rcar->base + PCIEINTSTS0EN);
- val &= ~MSI_CTRL_INT;
+ val &= ~(MSI_CTRL_INT | RP_AER_MASK);
/*
* Note that rcar->rp_events_armed is not checked here because device
* removal may still need iMSI-RX interrupts after Root Port events have
@@ -573,6 +587,8 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
*/
if (!rcar->reinit_pending && pp->use_imsi_rx && pci_msi_enabled())
val |= MSI_CTRL_INT;
+ if (rp_events && rcar->rp_irq_enabled && rcar->aer_irq > 0)
+ val |= RP_AER_MASK;
writel(val, rcar->base + PCIEINTSTS0EN);
val = readl(rcar->base + PCIERSTSTSEN);
@@ -676,6 +692,15 @@ static bool rcar_gen4_pcie_handle_link_down(struct rcar_gen4_pcie *rcar)
return rcar->reinit_pending;
}
+static void rcar_gen4_pcie_rp_irq_dispatch(struct rcar_gen4_pcie *rcar)
+{
+ /* Recheck reset requests after the parent's status read. */
+ if (rcar_gen4_pcie_handle_link_down(rcar))
+ return;
+
+ generic_handle_domain_irq(rcar->rp_irq_domain, 0);
+}
+
static void rcar_gen4_pcie_link_down_work(struct work_struct *work)
{
struct rcar_gen4_pcie *rcar =
@@ -770,6 +795,162 @@ static void rcar_gen4_pcie_rp_events_disarm(struct rcar_gen4_pcie *rcar)
cancel_work_sync(&rcar->link_down_work);
}
+static void rcar_gen4_pcie_rp_irq_mask(struct irq_data *d)
+{
+ struct rcar_gen4_pcie *rcar = irq_data_get_irq_chip_data(d);
+
+ guard(raw_spinlock_irqsave)(&rcar->app_lock);
+ rcar->rp_irq_enabled = false;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
+static void rcar_gen4_pcie_rp_irq_unmask(struct irq_data *d)
+{
+ struct rcar_gen4_pcie *rcar = irq_data_get_irq_chip_data(d);
+
+ guard(raw_spinlock_irqsave)(&rcar->app_lock);
+ rcar->rp_irq_enabled = true;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
+static void rcar_gen4_pcie_rp_irq_ack(struct irq_data *d)
+{
+ struct rcar_gen4_pcie *rcar = irq_data_get_irq_chip_data(d);
+
+ /*
+ * handle_level_irq() may skip the handlers after calling irq_ack().
+ * Keep notifications latched if the IRQ is disabled or in progress,
+ * so they raise a parent IRQ again after unmasking. The descriptor
+ * lock serializes this check with IRQD_IRQ_INPROGRESS.
+ */
+ if (irqd_irq_disabled(d) || irqd_irq_inprogress(d))
+ return;
+
+ /* Clear APP latches before the service clears Root Error Status. */
+ writel(RP_LATCHED_MASK, rcar->base + PCIEINTSTS0CLR);
+}
+
+static const struct irq_chip rcar_gen4_pcie_rp_irq_chip = {
+ .name = "rcar-gen4-rp",
+ .irq_mask = rcar_gen4_pcie_rp_irq_mask,
+ .irq_unmask = rcar_gen4_pcie_rp_irq_unmask,
+ .irq_ack = rcar_gen4_pcie_rp_irq_ack,
+};
+
+static int rcar_gen4_pcie_rp_irq_map(struct irq_domain *domain,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &rcar_gen4_pcie_rp_irq_chip,
+ handle_level_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ irq_set_status_flags(irq, IRQ_LEVEL);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops rcar_gen4_pcie_rp_irq_domain_ops = {
+ .map = rcar_gen4_pcie_rp_irq_map,
+};
+
+static irqreturn_t rcar_gen4_pcie_aer_irq_handler(int irq, void *data)
+{
+ struct rcar_gen4_pcie *rcar = data;
+ u32 status;
+
+ /* The line also fires on link down; reset requests come first. */
+ if (rcar_gen4_pcie_handle_link_down(rcar))
+ return IRQ_HANDLED;
+
+ status = readl(rcar->base + PCIEINTSTS0) &
+ readl(rcar->base + PCIEINTSTS0EN);
+ if (!(status & RP_AER_MASK))
+ return IRQ_NONE;
+
+ rcar_gen4_pcie_rp_irq_dispatch(rcar);
+
+ return IRQ_HANDLED;
+}
+
+static int rcar_gen4_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
+ struct rcar_gen4_pcie *rcar = dev_get_drvdata(bridge->dev.parent);
+
+ /* Downstream devices use standard OF INTx mapping */
+ if (!pci_is_root_bus(dev->bus))
+ return of_irq_parse_and_map_pci(dev, slot, pin);
+
+ /* Root Port uses the virtual IRQ (always INTA) */
+ return rcar->rp_irq;
+}
+
+static int rcar_gen4_pcie_rp_irq_init(struct rcar_gen4_pcie *rcar)
+{
+ struct device *dev = rcar->dw.dev;
+ struct fwnode_handle *fwnode;
+ int irq, ret;
+
+ irq = platform_get_irq_byname_optional(rcar->pdev, "aer");
+ if (irq == -ENXIO)
+ irq = 0; /* old DT: Root Port AER stays unavailable */
+ else if (irq < 0)
+ return dev_err_probe(dev, irq, "Failed to get AER IRQ\n");
+
+ fwnode = irq_domain_alloc_named_fwnode(dev_name(dev));
+ if (!fwnode)
+ return -ENOMEM;
+
+ rcar->rp_irq_domain = irq_domain_create_linear(fwnode, 1,
+ &rcar_gen4_pcie_rp_irq_domain_ops,
+ rcar);
+ if (!rcar->rp_irq_domain) {
+ ret = -ENOMEM;
+ goto err_free_fwnode;
+ }
+
+ rcar->rp_irq = irq_create_mapping(rcar->rp_irq_domain, 0);
+ if (!rcar->rp_irq) {
+ ret = -ENOMEM;
+ goto err_remove_domain;
+ }
+
+ if (irq) {
+ /*
+ * Dispatch the child IRQ in hard IRQ context, including on
+ * PREEMPT_RT. Enable it together with the "msi" parent IRQ.
+ */
+ ret = devm_request_irq(dev, irq, rcar_gen4_pcie_aer_irq_handler,
+ IRQF_NO_THREAD | IRQF_NO_AUTOEN,
+ "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;
+ }
+
+ return 0;
+
+err_dispose_mapping:
+ irq_dispose_mapping(rcar->rp_irq);
+err_remove_domain:
+ irq_domain_remove(rcar->rp_irq_domain);
+err_free_fwnode:
+ irq_domain_free_fwnode(fwnode);
+
+ return ret;
+}
+
+static void rcar_gen4_pcie_rp_irq_deinit(struct rcar_gen4_pcie *rcar)
+{
+ struct fwnode_handle *fwnode = rcar->rp_irq_domain->fwnode;
+
+ irq_dispose_mapping(rcar->rp_irq);
+ irq_domain_remove(rcar->rp_irq_domain);
+ irq_domain_free_fwnode(fwnode);
+}
+
static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
struct pci_dev *dev)
{
@@ -870,6 +1051,8 @@ static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
/* The MSI status lives in DBI; keep the handler away during the reset. */
disable_irq(rcar->msi_irq);
+ if (rcar->aer_irq > 0)
+ disable_irq(rcar->aer_irq);
}
static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
@@ -880,6 +1063,8 @@ static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
rcar_gen4_pcie_app_irq_sync_locked(rcar);
}
+ if (rcar->aer_irq > 0)
+ enable_irq(rcar->aer_irq);
enable_irq(rcar->msi_irq);
}
@@ -946,6 +1131,12 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
if (pp->bridge) {
pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
pp->bridge->reset_root_port = rcar_gen4_pcie_reset_root_port;
+ /*
+ * 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;
}
rcar_gen4_pcie_host_perst_assert(pp, true);
@@ -1004,10 +1195,14 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
raw_spin_lock_init(&rcar->app_lock);
INIT_WORK(&rcar->link_down_work, rcar_gen4_pcie_link_down_work);
- ret = rcar_gen4_pcie_msi_irq_init(rcar);
+ ret = rcar_gen4_pcie_rp_irq_init(rcar);
if (ret)
return ret;
+ ret = rcar_gen4_pcie_msi_irq_init(rcar);
+ if (ret)
+ goto err_rp_irq_deinit;
+
pp->num_vectors = MAX_MSI_IRQS;
/* Reserve an iATU window for the generic PME_Turn_Off implementation. */
pp->use_atu_msg = true;
@@ -1017,9 +1212,23 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
* iMSI-RX MSIs from its own handler.
*/
pp->msi_irq[0] = -ENODEV;
+ /*
+ * The APP block only latches the Root Port's own interrupts into
+ * PCIEINTSTS0; they are never delivered as MSI. Fall back to INTx,
+ * which rcar_gen4_pcie_map_irq() routes to the virtual Root Port IRQ.
+ */
+ pp->no_rp_msi = true;
pp->ops = &rcar_gen4_pcie_host_ops;
- return dw_pcie_host_init(pp);
+ ret = dw_pcie_host_init(pp);
+ if (ret)
+ goto err_rp_irq_deinit;
+
+ return 0;
+
+err_rp_irq_deinit:
+ rcar_gen4_pcie_rp_irq_deinit(rcar);
+ return ret;
}
static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
@@ -1027,6 +1236,7 @@ static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
/* Cancel recovery work before removing the PCI bus */
rcar_gen4_pcie_rp_events_disarm(rcar);
dw_pcie_host_deinit(&rcar->dw.pp);
+ rcar_gen4_pcie_rp_irq_deinit(rcar);
}
/* Endpoint mode */
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ
2026-10-06 8:46 ` [PATCH v3 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
@ 2026-10-06 8:54 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:54 UTC (permalink / raw)
To: Koichiro Den; +Cc: robh, devicetree, wsa+renesas, linux-pci, conor+dt
> On R-Car Gen4 SoCs, the APP block exposes the Root Port interrupt
> signals in PCIEINTSTS0 and routes them to platform IRQs: AER to
> intreq_pcim_lperr, and PME and bandwidth notifications to
> intreq_pcim_sub. On an R-Car S4 with GIC ITS, a link retrain set
> LnkSta.LBMS and latched PCIEINTSTS0[23] (cfg_bw_mgt_msi), but the Root
> Port's MSI never fired while endpoint MSIs worked fine. With iMSI-RX the
> Root Port MSI capabilities are already hidden, but its INTx maps to
> intreq_pcim_sub. Note that even before this driver took over the IRQ
> with rcar_gen4_pcie_msi_irq_handler, the port service was unable to
> request it, as the DWC core used that IRQ for its chained MSI handler.
> In either case, iMSI-RX or an external MSI controller, Root Port
> interrupts are not delivered to the port services.
>
> Set no_rp_msi so the Root Port services use INTx regardless of the MSI
> controller. Use a virtual IRQ to deliver notifications from separate
> [ ... ]
> disabled, so the level parents can retry through the reset checks.
> This avoids losing notifications without a raw lock around port
> services that take regular spinlocks.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=14
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (13 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:54 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 16/18] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
` (3 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
The Root Port's PME and link bandwidth notifications are reported in
PCIEINTSTS0 and routed to intreq_pcim_sub, the interrupt shared with the
integrated MSI receiver and the reset requests. Dispatch these
notifications to the virtual Root Port IRQ from the "msi" IRQ handler
after handling any pending iMSI-RX interrupts. This keeps Root Port
service handling out of the path between the reset-request check and
the iMSI-RX status reads. The existing dispatcher rechecks reset requests
before invoking the port services.
Enable the sources only while the Root Port IRQ is unmasked, and clear
the MSI-form latches in irq_ack together with the AER one. On an R-Car
S4 with the GIC ITS as MSI parent, a notification from a link retrain
now reaches the bandwidth controller service, which clears LnkSta.LBMS.
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Return IRQ_HANDLED or IRQ_NONE without a ternary operator. (Marek)
- Add Marek's R-b tag, as the change from v2 is trivial.
v2: https://lore.kernel.org/r/20260928165230.3397664-15-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 30 ++++++++++++++++-----
1 file changed, 23 insertions(+), 7 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index db0d5362eaed..040a51f7acb8 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -66,6 +66,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)
@@ -75,7 +85,8 @@
/* PCIe Interrupt Status 0 Clear */
#define PCIEINTSTS0CLR 0x0340
-#define RP_LATCHED_MASK AER_RC_ERR_MSI
+#define RP_LATCHED_MASK (AER_RC_ERR_MSI | BW_MGT_MSI | LINK_AUTO_BW_MSI | \
+ PME_MSI)
/* Port Logic Registers 89 */
#define PRTLGC89 0x0b70
@@ -136,7 +147,7 @@ struct rcar_gen4_pcie {
struct platform_device *pdev;
struct reset_control *perst;
const struct rcar_gen4_pcie_drvdata *drvdata;
- /* intreq_pcim_sub ("msi"): iMSI-RX and other controller notifications */
+ /* intreq_pcim_sub ("msi"): iMSI-RX, reset requests, Root Port PME/bandwidth */
int msi_irq;
struct work_struct link_down_work;
/* Allow Root Port event handling after enumeration and until teardown. */
@@ -579,7 +590,7 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
lockdep_assert_held(&rcar->app_lock);
val = readl(rcar->base + PCIEINTSTS0EN);
- val &= ~(MSI_CTRL_INT | RP_AER_MASK);
+ val &= ~(MSI_CTRL_INT | RP_AER_MASK | RP_SUB_MASK);
/*
* Note that rcar->rp_events_armed is not checked here because device
* removal may still need iMSI-RX interrupts after Root Port events have
@@ -589,6 +600,8 @@ static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
val |= MSI_CTRL_INT;
if (rp_events && rcar->rp_irq_enabled && rcar->aer_irq > 0)
val |= RP_AER_MASK;
+ if (rp_events && rcar->rp_irq_enabled)
+ val |= RP_SUB_MASK;
writel(val, rcar->base + PCIEINTSTS0EN);
val = readl(rcar->base + PCIERSTSTSEN);
@@ -737,12 +750,15 @@ static irqreturn_t rcar_gen4_pcie_msi_irq_handler(int irq, void *data)
status = readl(rcar->base + PCIEINTSTS0) &
readl(rcar->base + PCIEINTSTS0EN);
- if (!(status & MSI_CTRL_INT))
- return IRQ_NONE;
+ if (status & MSI_CTRL_INT)
+ dw_handle_msi_irq(&rcar->dw.pp);
+ if (status & RP_SUB_MASK)
+ rcar_gen4_pcie_rp_irq_dispatch(rcar);
- dw_handle_msi_irq(&rcar->dw.pp);
+ if (status & (MSI_CTRL_INT | RP_SUB_MASK))
+ return IRQ_HANDLED;
- return IRQ_HANDLED;
+ return IRQ_NONE;
}
static int rcar_gen4_pcie_msi_irq_init(struct rcar_gen4_pcie *rcar)
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications
2026-10-06 8:46 ` [PATCH v3 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
@ 2026-10-06 8:54 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-10-06 8:54 UTC (permalink / raw)
To: Koichiro Den; +Cc: robh, linux-pci, conor+dt, devicetree, wsa+renesas
> The Root Port's PME and link bandwidth notifications are reported in
> PCIEINTSTS0 and routed to intreq_pcim_sub, the interrupt shared with the
> integrated MSI receiver and the reset requests. Dispatch these
> notifications to the virtual Root Port IRQ from the "msi" IRQ handler
> after handling any pending iMSI-RX interrupts. This keeps Root Port
> service handling out of the path between the reset-request check and
> the iMSI-RX status reads. The existing dispatcher rechecks reset requests
> before invoking the port services.
>
> Enable the sources only while the Root Port IRQ is unmasked, and clear
> the MSI-form latches in irq_ack together with the AER one. On an R-Car
> S4 with the GIC ITS as MSI parent, a notification from a link retrain
> now reaches the bandwidth controller service, which clears LnkSta.LBMS.
>
> Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261006084638.3821710-1-den@valinux.co.jp?part=15
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 16/18] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (14 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:52 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 17/18] arm64: dts: renesas: r8a779g0: " Koichiro Den
` (2 subsequent siblings)
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
The PCIe controllers report Root Port AER notifications through
intreq_pci0_lperr (SPI 421) and intreq_pci1_lperr (SPI 428),
respectively, separately from the integrated MSI receiver interrupts.
Describe these interrupts with the "aer" name so the controller driver
can use them for Root Port error reporting.
Append the new interrupts to preserve the existing interrupt indices.
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v2:
- Add Marek's R-b tag. No changes other than that.
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 17a632770e65..8c818eb93119 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 related [flat|nested] 38+ messages in thread* [PATCH v3 17/18] arm64: dts: renesas: r8a779g0: Describe the PCIe AER interrupts
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (15 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 16/18] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:52 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 18/18] arm64: dts: renesas: r8a779h0: Describe the PCIe AER interrupt Koichiro Den
2026-10-08 5:45 ` [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, 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 SPI 454
and SPI 461, 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.
Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- New patch. Marek provided this change and ran the bandwidth
notification and Root Port AER tests from the cover letter on a
Retronix Sparrow Hawk (R-Car V4H).
https://lore.kernel.org/r/d9ffb9f3-ad89-4912-b863-637c838196cb@mailbox.org/
arch/arm64/boot/dts/renesas/r8a779g0.dtsi | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
index 5284923b6cef..8342d3c6f988 100644
--- a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
@@ -801,8 +801,9 @@ pciec0: pcie@e65d0000 {
interrupts = <GIC_SPI 449 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 450 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 451 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "msi", "dma", "sft_ce", "app";
+ <GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 454 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi", "dma", "sft_ce", "app", "aer";
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
clock-names = "core", "ref";
power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
@@ -850,8 +851,9 @@ pciec1: pcie@e65d8000 {
interrupts = <GIC_SPI 456 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 457 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 462 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "msi", "dma", "sft_ce", "app";
+ <GIC_SPI 462 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 461 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi", "dma", "sft_ce", "app", "aer";
clocks = <&cpg CPG_MOD 625>, <&pcie1_clkref>;
clock-names = "core", "ref";
power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [PATCH v3 18/18] arm64: dts: renesas: r8a779h0: Describe the PCIe AER interrupt
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (16 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 17/18] arm64: dts: renesas: r8a779g0: " Koichiro Den
@ 2026-10-06 8:46 ` Koichiro Den
2026-10-06 8:53 ` sashiko-bot
2026-10-08 5:45 ` [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
18 siblings, 1 reply; 38+ messages in thread
From: Koichiro Den @ 2026-10-06 8:46 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, 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 controller reports Root Port AER notifications through SPI 454,
separately from the integrated MSI receiver interrupt. Describe this
interrupt with the "aer" name so the controller driver can use it for
Root Port error reporting.
Append the new interrupt to preserve the existing interrupt indices.
Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- New patch. Marek provided this change and boot-tested it on an R-Car
V4M board without a PCIe device attached.
https://lore.kernel.org/r/d9ffb9f3-ad89-4912-b863-637c838196cb@mailbox.org/
arch/arm64/boot/dts/renesas/r8a779h0.dtsi | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a779h0.dtsi b/arch/arm64/boot/dts/renesas/r8a779h0.dtsi
index 5f9223a0f446..a377aab3c8be 100644
--- a/arch/arm64/boot/dts/renesas/r8a779h0.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a779h0.dtsi
@@ -683,8 +683,9 @@ pciec0: pcie@e65d0000 {
interrupts = <GIC_SPI 449 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 450 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 451 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "msi", "dma", "sft_ce", "app";
+ <GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 454 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi", "dma", "sft_ce", "app", "aer";
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
clock-names = "core", "ref";
power-domains = <&sysc R8A779H0_PD_A2PCIPHY>;
--
2.51.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (17 preceding siblings ...)
2026-10-06 8:46 ` [PATCH v3 18/18] arm64: dts: renesas: r8a779h0: Describe the PCIe AER interrupt Koichiro Den
@ 2026-10-08 5:45 ` Koichiro Den
18 siblings, 0 replies; 38+ messages in thread
From: Koichiro Den @ 2026-10-08 5:45 UTC (permalink / raw)
To: Marek Vasut, Geert Uytterhoeven, Yoshihiro Shimoda,
Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jingoo Han
Cc: Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
On Tue, Oct 06, 2026 at 05:46:20PM +0900, Koichiro Den wrote:
> Hi,
>
> This series improves error handling in the pcie-rcar-gen4 driver, which
> supports PCIe controllers in R-Car Gen4 SoCs and the PCIe4 controller
> in R-Car Gen5 SoCs. 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. I tested on
> an R-Car S4 Spider (r8a779f0); Marek tested the link state fix and the
> Root Port AER and bandwidth paths on an R-Car V4H Sparrow Hawk.
>
> 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 3 combines the APP link-up event
> check from Figure 104.5 with the PORT_LINK_DEBUG1 live link check,
> which patch 2 factors out of the DWC core. Startup clears the APP
> latches before enabling LTSSM, and the DWC core polls the combined
> condition on the RC side.
>
> 2. On S4, a DBI access immediately after an unexpected link down can
> hang the host. Commit 0056d29f8c1b ("PCI: rcar-gen4: Assure reset
> occurs before DBI access") describes an SError on V4H after reset
> deassertion, but whether the S4 hang shares the same underlying cause
> has not been verified.
>
> Adding a delay before the DBI access avoided the hang in my tests,
> but that alone would not provide link-down recovery. Also, the reset
> request shares intreq_pcim_sub with iMSI-RX, while AER arrives on
> another IRQ. Delaying AER dispatch alone would therefore leave the
> MSI handler exposed, and both paths would still need coordination
> with the controller reset.
>
> The driver handles the reset request itself and schedules
> pci_host_handle_link_down(), as the rockchip and qcom drivers do.
> This also provides recovery with older DTs that have no "aer"
> interrupt, without relying on AER to initiate it. The reset callback
> shares the reset sequence used at probe, including the reset-status
> readback and delay added by 0056d29f8c1b.
>
> This covers the interrupt paths that fire on link down. Port service
> threads or work already queued or running at that point are not
> stopped.
>
> 3. Root Port interrupts only trigger APP status bits on platform IRQs.
> The controller appears to lack SII2MSI, so Root Port MSIs never reach
> the GIC ITS. The Root Port's INTx is routed to intreq_pcim_sub as
> well, which the driver holds, so port services can't request it
> either. This series works around it by hiding Root Port MSI caps
> across the board and emulating INTx using a virtual IRQ domain, fed
> by the "aer" IRQ and intreq_pcim_sub.
>
> Patch 1 is only loosely related: it adds Renesas to the RAS DES VSEC
> list so the DWC debugfs error injection works on R-Car. I used it to
> test the Root Port AER path (see below) and included it here for that
> reason. Happy to send it separately if preferred.
>
> Based on next-20261002, with Marek's PM ops v5 patch applied first:
> https://lore.kernel.org/r/20261004011851.866833-1-marek.vasut+renesas@mailbox.org/
Turns out patch 4 ("dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt")
also needs to be rebased on top of Marek's patch [2].
(Thanks for the off-list heads-up, Marek!)
I think I'll do that when another respin is needed, or once the series is
otherwise acked. At least Sashiko hasn't complained about v3.
[2] https://lore.kernel.org/r/20261003215710.792518-1-marek.vasut+renesas@mailbox.org/
Best regards,
Koichiro Den
>
> Note: backward compatibility with older DTs is kept. Without the "aer"
> interrupt, only Root Port AER remains unavailable. See the Testing
> section below.
>
> Retesting with v3
> -----------------
>
> Setup: R-Car S4 Spider (RC) linked to another S4 Spider running the
> pci-epf-test endpoint. pci_endpoint_test is bound on the RC side.
>
> 1. Link down / recovery. On the EP side, toggle the endpoint controller
> off and on. The short pause keeps the endpoint away long enough for
> the RC to notice, but brings it back within the reset window so
> recovery can succeed. Adopted the test approach from [1]. Make sure
> that the unused function 0000:01:00.1 was removed on the RC before
> testing.
>
> # 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.
>
> This test does not generate a PME. The PME service shares the
> virtual IRQ, but its path was not exercised separately.
>
> 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 RP AER pcitest
> ---------------------------------------------------------
> a. none no no all FAIL
> b. ok ok ok no change
> c. ok ok n/a* no change
> d. ok ok n/a* no change
>
> * Root Port AER needs the "aer" interrupt; without it the behaviour is
> unchanged from before the series.
> ** Only patch 1 applied on top of the base, so the same debugfs error
> injection could be used for the comparison. pci_endpoint_test fails
> across the board there because nothing restores the endpoint after
> the toggle.
>
> Best regards,
> Koichiro
> ---
> Changes in v3:
> - Rebase onto next-20261002 with Marek's PM ops v5 applied first.
> - Keep parent IRQs registered across suspend/resume to fix the
> unbalanced enable Geert reported. Request them in probe with
> IRQF_NO_AUTOEN and enable/disable them in .init()/.deinit().
> - Use a freezable workqueue for recovery and reject suspend while
> reinitialization is pending. Re-arm Root Port events only after
> resume has set up the link.
> - Factor out the PORT_LINK_DEBUG1 check (patch 2) and reuse it in
> patch 3. (Marek)
> - Replace .reinit() with .configure, folding v2 patch 8 into patch 9.
> Drop the reset mutex and rely on PCI core serialization. (Marek)
> - Free the MSI domain after host .deinit(), so the driver can stop
> its parent IRQ first (patch 10). This closes the known gap in v2.
> - Reset the controller if recovery finds no Root Port, so a successful
> reset can restore interrupt delivery.
> - Drop the raw lock around virtual IRQ dispatch. Preserve MSI-form
> latches in irq_ack() while the IRQ is in progress or disabled,
> avoiding lost notifications without nesting port service locks.
> - Add patch 7's Fixes tag, clarify SoC generations, separate guard()
> from goto-based cleanup, and simplify IRQ return statements.
> (Marek, Sashiko)
> - Add the V4H and V4M DTS patches 17 and 18. (Marek)
> - Collect Reviewed-by (Marek, Krzysztof) and Tested-by (Marek) tags.
> Drop Marek's Reviewed-by from the reworked patches 12 and 14.
>
> Changes in v2:
> - Rebased onto next-20260925, including Marek's R-Car X5H support.
> - Keep .link_up() and require both the APP link-up events and the
> PORT_DEBUG1 live link check. Clear the APP latches before enabling
> LTSSM and reuse the DWC core's polling on the RC side. (Marek)
> - Let the R-Car driver own intreq_pcim_sub in all configurations (new
> patch 10) and check the reset request from its own handler instead
> of hooking into the DWC chained handler. Replace the pre_msi_irq host
> op from v1 with an export of dw_handle_msi_irq() (patch 4). (Marek)
> - Keep the reset_control_status() check before asserting the power
> reset. (Marek)
> - Separate controller reinitialization from .init()/.deinit() and split
> out preparatory changes.
> - Use bool flags instead of a state bitmask. (Marek)
> - Keep Root Port AER notifications masked until .post_init, after
> enumeration. In v1, they could be enabled during port-service probing.
> - Keep APP interrupt sources masked if the Root Port reset callback
> fails, regardless of the reset trigger.
> - Hold a reference on the Root Port in the link-down recovery work.
> - Clear only the MSI-form Root Port latches through PCIEINTSTS0CLR;
> the INTx bits are reserved there.
> - Serialize dispatches to the virtual Root Port IRQ from its two
> parent interrupts.
> - Collected Marek's Reviewed-by on patches 1, 3 and 15.
>
> v2: https://lore.kernel.org/r/20260928165230.3397664-1-den@valinux.co.jp/
> v1: https://lore.kernel.org/r/20260918032038.2216471-1-den@valinux.co.jp/
>
>
> Koichiro Den (18):
> PCI: dwc: Add Renesas to the RAS DES VSEC list
> PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check
> PCI: rcar-gen4: Check live link status in link_up()
> dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt
> PCI: dwc: Export dw_handle_msi_irq()
> PCI: rcar-gen4: Move deinitialization helpers before SoC
> initialization
> PCI: rcar-gen4: Assert resets when Gen5 SoC PHY initialization fails
> PCI: rcar-gen4: Separate hardware setup from resource acquisition
> PCI: rcar-gen4: Add Root Port reset support
> PCI: dwc: Free the MSI domain after the host .deinit() callback
> PCI: rcar-gen4: Take over the iMSI-RX interrupt
> PCI: rcar-gen4: Recover the Root Port on link down
> PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
> PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ
> PCI: rcar-gen4: Route Root Port PME and bandwidth notifications
> arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts
> arm64: dts: renesas: r8a779g0: Describe the PCIe AER interrupts
> arm64: dts: renesas: r8a779h0: Describe the PCIe AER interrupt
>
> .../bindings/pci/rcar-gen4-pci-host.yaml | 10 +-
> arch/arm64/boot/dts/renesas/r8a779f0.dtsi | 10 +-
> arch/arm64/boot/dts/renesas/r8a779g0.dtsi | 10 +-
> arch/arm64/boot/dts/renesas/r8a779h0.dtsi | 5 +-
> .../pci/controller/dwc/pcie-designware-host.c | 38 +-
> drivers/pci/controller/dwc/pcie-designware.c | 15 +-
> drivers/pci/controller/dwc/pcie-designware.h | 2 +
> drivers/pci/controller/dwc/pcie-rcar-gen4.c | 723 ++++++++++++++++--
> include/linux/pcie-dwc.h | 2 +
> 9 files changed, 724 insertions(+), 91 deletions(-)
>
>
> base-commit: f0406245cb9855e6318335a8a223551354291a46
> prerequisite-patch-id: 5b4c9da1333342baa90cf6a86dc879e8382d3f65
> --
> 2.51.0
>
>
^ permalink raw reply [flat|nested] 38+ messages in thread