From: Koichiro Den <den@valinux.co.jp>
To: "Marek Vasut" <marek.vasut+renesas@mailbox.org>,
"Geert Uytterhoeven" <geert+renesas@glider.be>,
"Yoshihiro Shimoda" <yoshihiro.shimoda.uh@renesas.com>,
"Lorenzo Pieralisi" <lpieralisi@kernel.org>,
"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
"Manivannan Sadhasivam" <mani@kernel.org>,
"Rob Herring" <robh@kernel.org>,
"Bjorn Helgaas" <bhelgaas@google.com>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Magnus Damm" <magnus.damm@gmail.com>,
"Jingoo Han" <jingoohan1@gmail.com>
Cc: Philipp Zabel <p.zabel@pengutronix.de>,
Frank Li <Frank.Li@nxp.com>, Niklas Cassel <cassel@kernel.org>,
Wilfred Mallawa <wilfred.mallawa@wdc.com>,
Serge Semin <fancer.lancer@gmail.com>,
linux-pci@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v3 09/18] PCI: rcar-gen4: Add Root Port reset support
Date: Tue, 6 Oct 2026 17:46:29 +0900 [thread overview]
Message-ID: <20261006084638.3821710-10-den@valinux.co.jp> (raw)
In-Reply-To: <20261006084638.3821710-1-den@valinux.co.jp>
Implement the host bridge reset_root_port() callback so PCI error
recovery can reset and reinitialize the R-Car controller. This also
provides the reset operation for the link-down handling added later.
Reinitialize without going through .deinit() and .init(), which would
release and re-acquire the clocks and the R-Car Gen5 SoC PHY: split the
SoC-specific register setup that the power reset wipes out into a
.configure callback and repeat only that, then restore the Root Port
registers and restart link training.
Rather than tracking which APP interrupt enables survive the power
reset, derive them from software state through a single helper. A flag
keeps the sources masked from the start of a reset until one succeeds,
so a failed reinitialization does not re-enable them against an
uninitialized controller.
Reject a reset while the controller is suspended, as a failed resume may
have left it powered down.
Track whether the R-Car Gen5 SoC PHY power-on has been requested so a
reset retry, or teardown after a failed reset, does not call
phy_power_off() twice. Continue to ignore the PHY power operation return
values.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v3:
- Replace the per-SoC .reinit() callback with .configure and a common
reinitialization helper, folding the former patch 8 into this one.
(Marek)
- Drop the mutex: the PCI core already serializes the callers through
its device locks, and suspend/resume runs with those callers frozen
or quiesced. See:
https://lore.kernel.org/r/eivzz4zumuu32zrycw6wpt3orzk2dvlzuvj34krmbhjf7a3a6u@ccvu2nkupzid/
- Reject resets while the controller is suspended, including after a
failed resume, now that v3 is based on Marek's PM ops patch.
- Lock explicitly in the rcar_gen4_pcie_host_msi_init() error path
instead of mixing guard() with goto-based cleanup. (Sashiko)
- Spell out the SoC generations in the reset_root_port() comment.
(Marek)
- Revise the commit message.
v2: https://lore.kernel.org/r/20260928165230.3397664-10-den@valinux.co.jp/
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 141 +++++++++++++++++++-
1 file changed, 134 insertions(+), 7 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index cbd22e6bb44b..54260e62293b 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -99,6 +99,8 @@ struct rcar_gen4_pcie;
struct rcar_gen4_pcie_drvdata {
int (*init)(struct rcar_gen4_pcie *rcar);
void (*deinit)(struct rcar_gen4_pcie *rcar);
+ /* SoC-specific register setup, repeated after a power reset */
+ void (*configure)(struct rcar_gen4_pcie *rcar);
int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable);
int (*speed_control)(struct rcar_gen4_pcie *rcar);
enum dw_pcie_device_mode mode;
@@ -109,9 +111,17 @@ struct rcar_gen4_pcie {
void __iomem *base;
void __iomem *phy_base;
struct phy *phy;
+ bool phy_powered;
struct platform_device *pdev;
struct reset_control *perst;
const struct rcar_gen4_pcie_drvdata *drvdata;
+ /*
+ * The controller needs reinitializing; the APP interrupt sources
+ * stay masked until a reset succeeds.
+ */
+ bool reinit_pending;
+ /* Protects APP interrupt enable registers and their software state. */
+ raw_spinlock_t app_lock;
};
#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
@@ -322,6 +332,21 @@ static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
}
+/* Reset and restore registers while clocks and PHY remain initialized. */
+static int rcar_gen4_pcie_common_reinit(struct rcar_gen4_pcie *rcar)
+{
+ int ret;
+
+ ret = rcar_gen4_pcie_reset(rcar);
+ if (ret)
+ return ret;
+
+ if (rcar->drvdata->configure)
+ rcar->drvdata->configure(rcar);
+
+ return 0;
+}
+
static void rcar_gen4_v4h_v4m_pcie_configure(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
@@ -513,10 +538,31 @@ static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
return 0;
}
+static void rcar_gen4_pcie_app_irq_sync_locked(struct rcar_gen4_pcie *rcar)
+{
+ u32 val;
+
+ lockdep_assert_held(&rcar->app_lock);
+
+ val = readl(rcar->base + PCIEINTSTS0EN);
+ val &= ~MSI_CTRL_INT;
+ if (!rcar->reinit_pending && pci_msi_enabled())
+ val |= MSI_CTRL_INT;
+ writel(val, rcar->base + PCIEINTSTS0EN);
+}
+
+static void rcar_gen4_pcie_app_irq_sync(struct rcar_gen4_pcie *rcar)
+{
+ guard(raw_spinlock_irqsave)(&rcar->app_lock);
+
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+}
+
static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+ unsigned long flags;
u32 val;
int ret;
@@ -543,12 +589,7 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
}
/* Configure MSI interrupt signal */
- val = readl(rcar->base + PCIEINTSTS0EN);
- if (pci_msi_enabled())
- val |= MSI_CTRL_INT;
- else
- val &= ~MSI_CTRL_INT;
- writel(val, rcar->base + PCIEINTSTS0EN);
+ rcar_gen4_pcie_app_irq_sync(rcar);
return 0;
@@ -563,9 +604,11 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
writel(0, rcar->base + AXIINTCCONT);
/* Deconfigure MSI interrupt signal */
+ raw_spin_lock_irqsave(&rcar->app_lock, flags);
val = readl(rcar->base + PCIEINTSTS0EN);
val &= ~MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
+ raw_spin_unlock_irqrestore(&rcar->app_lock, flags);
return ret;
}
@@ -661,14 +704,87 @@ static int rcar_gen4_pcie_host_setup(struct dw_pcie_rp *pp)
return 0;
}
+static void rcar_gen4_pcie_quiesce_irqs(struct rcar_gen4_pcie *rcar)
+{
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ rcar->reinit_pending = true;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ }
+}
+
+static void rcar_gen4_pcie_resume_irqs(struct rcar_gen4_pcie *rcar,
+ bool recovered)
+{
+ scoped_guard(raw_spinlock_irqsave, &rcar->app_lock) {
+ rcar->reinit_pending = !recovered;
+ rcar_gen4_pcie_app_irq_sync_locked(rcar);
+ }
+}
+
+/*
+ * R-Car Gen4 SoC PCIe controllers and R-Car Gen5 SoC PCIe4 controller have
+ * a single Root Port per instance, so the 'pci_dev' is ignored and the whole
+ * controller is reset. The PCI core serializes the callers through its
+ * device locks.
+ */
+static int rcar_gen4_pcie_reset_root_port(struct pci_host_bridge *bridge,
+ struct pci_dev *pdev)
+{
+ struct rcar_gen4_pcie *rcar = dev_get_drvdata(bridge->dev.parent);
+ struct dw_pcie *dw = &rcar->dw;
+ struct dw_pcie_rp *pp = &dw->pp;
+ struct device *dev = dw->dev;
+ int ret;
+
+ /* A failed resume may have left the controller powered down. */
+ if (dw->suspended)
+ return -EBUSY;
+
+ rcar_gen4_pcie_quiesce_irqs(rcar);
+
+ dw_pcie_stop_link(dw);
+
+ rcar_gen4_pcie_host_perst_assert(pp, true);
+
+ ret = rcar_gen4_pcie_common_reinit(rcar);
+ if (ret)
+ goto out;
+
+ ret = rcar_gen4_pcie_host_setup(pp);
+ if (ret)
+ goto out;
+
+ ret = dw_pcie_setup_rc(pp);
+ if (ret)
+ goto out;
+
+ ret = dw_pcie_start_link(dw);
+ if (ret)
+ goto out;
+
+ /* Ignore errors, the link may come up later */
+ dw_pcie_wait_for_link(dw);
+ dev_dbg(dev, "Root Port reset completed\n");
+
+out:
+ rcar_gen4_pcie_resume_irqs(rcar, !ret);
+ if (ret)
+ dev_err(dev, "Root Port reset failed (%d); interrupts stay masked until a reset succeeds\n",
+ ret);
+
+ return ret;
+}
+
static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
- if (pp->bridge)
+ if (pp->bridge) {
pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
+ pp->bridge->reset_root_port = rcar_gen4_pcie_reset_root_port;
+ }
rcar_gen4_pcie_host_perst_assert(pp, true);
@@ -708,6 +824,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_HOST))
return -ENODEV;
+ raw_spin_lock_init(&rcar->app_lock);
+
pp->num_vectors = MAX_MSI_IRQS;
/* Reserve an iATU window for the generic PME_Turn_Off implementation. */
pp->use_atu_msg = true;
@@ -944,11 +1062,17 @@ static int rcar_gen5_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
}
writel(val, rcar->base + PCIERSTCTRL1);
+ /* A reset retry may find the PHY already powered off. */
+ if (enable == rcar->phy_powered)
+ return 0;
+
if (enable)
phy_power_on(rcar->phy);
else
phy_power_off(rcar->phy);
+ rcar->phy_powered = enable;
+
return 0;
}
@@ -1141,6 +1265,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = {
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
.init = rcar_gen4_v4h_v4m_pcie_init,
.deinit = rcar_gen4_pcie_common_deinit,
+ .configure = rcar_gen4_v4h_v4m_pcie_configure,
.ltssm_control = rcar_gen4_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_control,
.mode = DW_PCIE_RC_TYPE,
@@ -1149,6 +1274,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
.init = rcar_gen4_v4h_v4m_pcie_init,
.deinit = rcar_gen4_pcie_common_deinit,
+ .configure = rcar_gen4_v4h_v4m_pcie_configure,
.ltssm_control = rcar_gen4_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_control,
.mode = DW_PCIE_EP_TYPE,
@@ -1157,6 +1283,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen5_pcie = {
.init = rcar_gen5_pcie_init,
.deinit = rcar_gen5_pcie_deinit,
+ .configure = rcar_gen5_pcie_configure,
.ltssm_control = rcar_gen5_pcie_ltssm_control,
.speed_control = rcar_gen5_pcie_speed_control,
.mode = DW_PCIE_RC_TYPE,
--
2.51.0
next prev parent reply other threads:[~2026-10-06 8:47 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-06 8:46 [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
2026-10-06 8:46 ` [PATCH v3 01/18] PCI: dwc: Add Renesas to the RAS DES VSEC list Koichiro Den
2026-10-06 8:50 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 02/18] PCI: dwc: Factor out the PORT_LINK_DEBUG1 link-up check Koichiro Den
2026-10-06 8:51 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 03/18] PCI: rcar-gen4: Check live link status in link_up() Koichiro Den
2026-10-06 8:53 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 04/18] dt-bindings: PCI: rcar-gen4: Add optional "aer" interrupt Koichiro Den
2026-10-06 8:52 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 05/18] PCI: dwc: Export dw_handle_msi_irq() Koichiro Den
2026-10-06 8:51 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 06/18] PCI: rcar-gen4: Move deinitialization helpers before SoC initialization Koichiro Den
2026-10-06 8:51 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 07/18] PCI: rcar-gen4: Assert resets when Gen5 SoC PHY initialization fails Koichiro Den
2026-10-06 8:54 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 08/18] PCI: rcar-gen4: Separate hardware setup from resource acquisition Koichiro Den
2026-10-06 8:50 ` sashiko-bot
2026-10-06 8:46 ` Koichiro Den [this message]
2026-10-06 8:55 ` [PATCH v3 09/18] PCI: rcar-gen4: Add Root Port reset support sashiko-bot
2026-10-06 8:46 ` [PATCH v3 10/18] PCI: dwc: Free the MSI domain after the host .deinit() callback Koichiro Den
2026-10-06 9:01 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 11/18] PCI: rcar-gen4: Take over the iMSI-RX interrupt Koichiro Den
2026-10-06 8:56 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 12/18] PCI: rcar-gen4: Recover the Root Port on link down Koichiro Den
2026-10-06 8:58 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 13/18] PCI: dwc: Let glue drivers hide the Root Port MSI capabilities Koichiro Den
2026-10-06 8:51 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 14/18] PCI: rcar-gen4: Route Root Port AER to a virtual Root Port IRQ Koichiro Den
2026-10-06 8:54 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 15/18] PCI: rcar-gen4: Route Root Port PME and bandwidth notifications Koichiro Den
2026-10-06 8:54 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 16/18] arm64: dts: renesas: r8a779f0: Describe the PCIe AER interrupts Koichiro Den
2026-10-06 8:52 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 17/18] arm64: dts: renesas: r8a779g0: " Koichiro Den
2026-10-06 8:52 ` sashiko-bot
2026-10-06 8:46 ` [PATCH v3 18/18] arm64: dts: renesas: r8a779h0: Describe the PCIe AER interrupt Koichiro Den
2026-10-06 8:53 ` sashiko-bot
2026-10-08 5:45 ` [PATCH v3 00/18] PCI: rcar-gen4: Recover from link down and route Root Port interrupts Koichiro Den
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261006084638.3821710-10-den@valinux.co.jp \
--to=den@valinux.co.jp \
--cc=Frank.Li@nxp.com \
--cc=bhelgaas@google.com \
--cc=cassel@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=fancer.lancer@gmail.com \
--cc=geert+renesas@glider.be \
--cc=jingoohan1@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=kwilczynski@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-renesas-soc@vger.kernel.org \
--cc=lpieralisi@kernel.org \
--cc=magnus.damm@gmail.com \
--cc=mani@kernel.org \
--cc=marek.vasut+renesas@mailbox.org \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=wilfred.mallawa@wdc.com \
--cc=yoshihiro.shimoda.uh@renesas.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox