* [PATCH v4 01/18] PCI: dwc: Add Renesas to the RAS DES VSEC list
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check Koichiro Den
` (16 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No 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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-10-15 3:30 ` [PATCH v4 01/18] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:48 ` Shawn Lin
2026-10-15 3:30 ` [PATCH v4 03/18] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
` (15 subsequent siblings)
17 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v4 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check
2026-10-15 3:30 ` [PATCH v4 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check Koichiro Den
@ 2026-10-15 3:48 ` Shawn Lin
0 siblings, 0 replies; 23+ messages in thread
From: Shawn Lin @ 2026-10-15 3:48 UTC (permalink / raw)
To: Koichiro Den, 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: shawn.lin, Philipp Zabel, Frank Li, Niklas Cassel,
Wilfred Mallawa, Serge Semin, linux-pci, linux-renesas-soc,
devicetree, linux-kernel
在 2026/10/15 星期四 11:30, Koichiro Den 写道:
> 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.
>
Reviewed-by: Shawn Lin <Shawn.lin@rock-chips.com>
> Suggested-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes since v3:
> - No changes.
>
> 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);
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v4 03/18] PCI: rcar-gen4: Check live link status in link_up()
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-10-15 3:30 ` [PATCH v4 01/18] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
2026-10-15 3:30 ` [PATCH v4 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 04/18] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt for R-Car Gen4 SoCs Koichiro Den
` (14 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 04/18] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt for R-Car Gen4 SoCs
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (2 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 03/18] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 05/18] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
` (13 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 on R-Car Gen4 SoCs so existing device
trees remain valid. Extend the example accordingly.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v3:
- Rebase on Marek's Gen5 AER binding fix:
https://lore.kernel.org/r/20261003215710.792518-1-marek.vasut+renesas@mailbox.org/
This requires "aer" for the Gen5 SoC PCIe4 controller from the
beginning. Update the subject to clarify that it is optional only
on Gen4 SoCs, for compatibility with existing DTs.
- Drop R-b tags due to the changes.
v3: https://lore.kernel.org/r/20261006084638.3821710-5-den@valinux.co.jp/
.../devicetree/bindings/pci/rcar-gen4-pci-host.yaml | 13 ++++++++++---
1 file changed, 10 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 083b77d4de8f..a86c06701fa9 100644
--- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
@@ -26,7 +26,8 @@ properties:
- const: renesas,rcar-gen5-pcie4 # R-Car Gen5 PCIe4
interrupts:
- maxItems: 4
+ minItems: 4
+ maxItems: 5
clocks:
maxItems: 2
@@ -93,11 +94,13 @@ allOf:
- const: config
interrupt-names:
+ minItems: 4
items:
- const: msi
- const: dma
- const: sft_ce
- const: app
+ - const: aer
- if:
properties:
compatible:
@@ -117,6 +120,9 @@ allOf:
- const: app
- const: config
+ interrupts:
+ maxItems: 4
+
interrupt-names:
items:
- const: msi
@@ -146,8 +152,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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 05/18] PCI: dwc: Export dw_handle_msi_irq()
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (3 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 04/18] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt for R-Car Gen4 SoCs Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:49 ` Shawn Lin
2026-10-15 3:30 ` [PATCH v4 06/18] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization Koichiro Den
` (12 subsequent siblings)
17 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v4 05/18] PCI: dwc: Export dw_handle_msi_irq()
2026-10-15 3:30 ` [PATCH v4 05/18] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
@ 2026-10-15 3:49 ` Shawn Lin
0 siblings, 0 replies; 23+ messages in thread
From: Shawn Lin @ 2026-10-15 3:49 UTC (permalink / raw)
To: Koichiro Den, 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: shawn.lin, Philipp Zabel, Frank Li, Niklas Cassel,
Wilfred Mallawa, Serge Semin, linux-pci, linux-renesas-soc,
devicetree, linux-kernel
在 2026/10/15 星期四 11:30, Koichiro Den 写道:
> 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.
>
Reviewed-by: Shawn Lin <Shawn.lin@rock-chips.com>
> 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 since v3:
> - No changes.
>
> 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)
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v4 06/18] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (4 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 05/18] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 07/18] PCI: rcar-gen4: Assert resets when Gen5 SoC PHY initialization fails Koichiro Den
` (11 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 07/18] PCI: rcar-gen4: Assert resets when Gen5 SoC PHY initialization fails
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (5 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 06/18] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
` (10 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (6 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 07/18] PCI: rcar-gen4: Assert resets when Gen5 SoC PHY initialization fails Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 09/18] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
` (9 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 09/18] PCI: rcar-gen4: Add Root Port reset support
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (7 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback Koichiro Den
` (8 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (8 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 09/18] PCI: rcar-gen4: Add Root Port reset support Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
` (7 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
.../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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (9 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 12/18] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
` (6 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 12/18] PCI: rcar-gen4: Recover the Root Port on link down
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (10 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:30 ` [PATCH v4 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
` (5 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (11 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 12/18] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 4:11 ` Shawn Lin
2026-10-15 3:30 ` [PATCH v4 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
` (4 subsequent siblings)
17 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
drivers/pci/controller/dwc/pcie-designware-host.c | 6 ++++--
drivers/pci/controller/dwc/pcie-designware.h | 1 +
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v4 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
2026-10-15 3:30 ` [PATCH v4 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
@ 2026-10-15 4:11 ` Shawn Lin
2026-10-15 7:17 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Shawn Lin @ 2026-10-15 4:11 UTC (permalink / raw)
To: Koichiro Den, 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: shawn.lin, Philipp Zabel, Frank Li, Niklas Cassel,
Wilfred Mallawa, Serge Semin, linux-pci, linux-renesas-soc,
devicetree, linux-kernel
Hi Koichiro
在 2026/10/15 星期四 11:30, Koichiro Den 写道:
> 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.
>
With it, dw_pcie_rp will carry three flags with overlapping and
priority-dependent semantics:
use_imsi_rx - the MSI controller is iMSI-RX
keep_rp_msi_en - do NOT remove the RP MSI caps (i.MX6 workaround)
no_rp_msi - the platform never gets the RP's own MSIs
and the resulting condition:
if ((pp->use_imsi_rx || pp->no_rp_msi) && !pp->keep_rp_msi_en)
is getting hard to reason about. Adding no_rp_msi for a single platform
whose controller is not even iMSI-RX makes the combination worse.
Since only R-Car Gen4 needs this, and it has its own host init, would it
be fine for the glue driver to call dw_pcie_remove_capability()
directly? I'd like to avoid growing the shared struct if a local
solution is possible.
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes since v3:
> - No changes.
>
> drivers/pci/controller/dwc/pcie-designware-host.c | 6 ++++--
> drivers/pci/controller/dwc/pcie-designware.h | 1 +
> 2 files changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> index 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;
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v4 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities
2026-10-15 4:11 ` Shawn Lin
@ 2026-10-15 7:17 ` Koichiro Den
0 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 7:17 UTC (permalink / raw)
To: Shawn Lin
Cc: 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,
Philipp Zabel, Frank Li, Niklas Cassel, Wilfred Mallawa,
Serge Semin, linux-pci, linux-renesas-soc, devicetree,
linux-kernel
On Thu, Oct 15, 2026 at 12:11:45PM +0800, Shawn Lin wrote:
> Hi Koichiro
>
> 在 2026/10/15 星期四 11:30, Koichiro Den 写道:
> > 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.
> >
>
> With it, dw_pcie_rp will carry three flags with overlapping and
> priority-dependent semantics:
>
> use_imsi_rx - the MSI controller is iMSI-RX
> keep_rp_msi_en - do NOT remove the RP MSI caps (i.MX6 workaround)
> no_rp_msi - the platform never gets the RP's own MSIs
>
> and the resulting condition:
>
> if ((pp->use_imsi_rx || pp->no_rp_msi) && !pp->keep_rp_msi_en)
>
> is getting hard to reason about. Adding no_rp_msi for a single platform
> whose controller is not even iMSI-RX makes the combination worse.
>
> Since only R-Car Gen4 needs this, and it has its own host init, would it
> be fine for the glue driver to call dw_pcie_remove_capability()
> directly? I'd like to avoid growing the shared struct if a local
> solution is possible.
That makes sense. Let me respin and add dw_pcie_remove_capability() calls
to pcie-rcar-gen4.c's host_setup(), with a short comment.
Then I'll drop this patch ([PATCH v4 13/18]).
Thanks for the review!
Best regards,
Koichiro Den
>
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > Changes since v3:
> > - No changes.
> >
> > drivers/pci/controller/dwc/pcie-designware-host.c | 6 ++++--
> > drivers/pci/controller/dwc/pcie-designware.h | 1 +
> > 2 files changed, 5 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> > index 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;
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v4 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (12 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
@ 2026-10-15 3:30 ` Koichiro Den
2026-10-15 3:31 ` [PATCH v4 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
` (3 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:30 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (13 preceding siblings ...)
2026-10-15 3:30 ` [PATCH v4 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
@ 2026-10-15 3:31 ` Koichiro Den
2026-10-15 3:31 ` [PATCH v4 16/18] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
` (2 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:31 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 16/18] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (14 preceding siblings ...)
2026-10-15 3:31 ` [PATCH v4 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
@ 2026-10-15 3:31 ` Koichiro Den
2026-10-15 3:31 ` [PATCH v4 17/18] arm64: dts: renesas: r8a779g0: " Koichiro Den
2026-10-15 3:31 ` [PATCH v4 18/18] arm64: dts: renesas: r8a779h0: Describe the PCIe AER interrupt Koichiro Den
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:31 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 since v3:
- No changes.
arch/arm64/boot/dts/renesas/r8a779f0.dtsi | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a779f0.dtsi b/arch/arm64/boot/dts/renesas/r8a779f0.dtsi
index 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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 17/18] arm64: dts: renesas: r8a779g0: Describe the PCIe AER interrupts
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (15 preceding siblings ...)
2026-10-15 3:31 ` [PATCH v4 16/18] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
@ 2026-10-15 3:31 ` Koichiro Den
2026-10-15 3:31 ` [PATCH v4 18/18] arm64: dts: renesas: r8a779h0: Describe the PCIe AER interrupt Koichiro Den
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:31 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH v4 18/18] arm64: dts: renesas: r8a779h0: Describe the PCIe AER interrupt
2026-10-15 3:30 [PATCH v4 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
` (16 preceding siblings ...)
2026-10-15 3:31 ` [PATCH v4 17/18] arm64: dts: renesas: r8a779g0: " Koichiro Den
@ 2026-10-15 3:31 ` Koichiro Den
17 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-10-15 3:31 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 since v3:
- No changes.
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.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread