From: Claudiu Beznea <claudiu.beznea@kernel.org>
To: lpieralisi@kernel.org, kwilczynski@kernel.org, mani@kernel.org,
robh@kernel.org, bhelgaas@google.com, p.zabel@pengutronix.de,
18255117159@163.com
Cc: claudiu.beznea@tuxon.dev, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>,
Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Subject: [PATCH v5 9/9] PCI: rzg3s-host: Add bridge::reset_root_port()
Date: Fri, 2 Oct 2026 14:09:35 +0300 [thread overview]
Message-ID: <20261002110935.2004349-10-claudiu.beznea@kernel.org> (raw)
In-Reply-To: <20261002110935.2004349-1-claudiu.beznea@kernel.org>
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Add bridge::reset_root_port(). This allows, on some devices, performing
reads and writes to an NVMe endpoint after a link down/link up cycle
without failures.
The implementation of struct pci_host_bridge::reset_root_port() masks all
enabled interrupts and synchronizes them before resetting the controller
to prevent asynchronous events from interfering with the reset operation.
struct rzg3s_pcie_host::started boolean was added to keep track of the
controller state, since rzg3s_pcie_host_start() and rzg3s_pcie_host_stop()
are called from multiple paths: .reset_root_port(), .suspend_noirq() and
.resume_noirq(). The flag prevents double start/stop and breaking
reference counters for clocks and resets.
rzg3s_pcie_pm_notifier() disables the link event IRQ, and disable_irq()
waits for a running IRQ thread to complete. If that thread is handling a
link down event, it calls .reset_root_port(), which unmasks the link
interrupt before returning, undoing the mask. Therefore, mask the link
interrupt again after disable_irq() has returned.
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Tested-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v5:
- lock around reads/writes on RZG3S_PCI_PINTRCVIE
- in rzg3s_pcie_host_reset_root_port() kept non-relaxed reads/writes
on interrupt disable registers (even though the spin lock/unlock are around
them) to emphasize the need for updates before the IRQs are synchronized and
relaxed variants on interrupt enable path
- collected tags
Changes in v4:
- this patch is new and is a split from patch 7 of v3
- use a boolean to keep track of the controller start state; with this
the state management code from patch 7 of v3 was dropped
drivers/pci/controller/pcie-rzg3s-host.c | 83 +++++++++++++++++++++++-
1 file changed, 81 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
index 78e783928b9d..eea215818633 100644
--- a/drivers/pci/controller/pcie-rzg3s-host.c
+++ b/drivers/pci/controller/pcie-rzg3s-host.c
@@ -331,6 +331,7 @@ struct rzg3s_pcie_port {
* @max_link_speed: maximum supported link speed
* @controller_id: PCIe controller identifier, used for System Controller access
* @link_rescan: The PCIe link rescan state
+ * @started: The PCIe controller state (started or not)
* @num_lanes: The number of lanes
*/
struct rzg3s_pcie_host {
@@ -351,6 +352,7 @@ struct rzg3s_pcie_host {
int max_link_speed;
enum rzg3s_pcie_controller_id controller_id;
bool link_rescan;
+ bool started;
u8 num_lanes;
};
@@ -1115,6 +1117,11 @@ static void rzg3s_pcie_link_event(struct rzg3s_pcie_host *host)
dev_info(host->dev, "PCIe link down, removing devices\n");
+ for_each_pci_bridge(dev, bridge->bus) {
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+ pci_host_handle_link_down(dev);
+ }
+
pci_lock_rescan_remove();
list_for_each_entry_safe_reverse(dev, tmp, &bus->devices,
bus_list)
@@ -1869,6 +1876,9 @@ static int rzg3s_pcie_host_stop(struct rzg3s_pcie_host *host)
struct rzg3s_sysc *sysc = host->sysc;
int ret;
+ if (!host->started)
+ return 0;
+
clk_disable_unprepare(port->refclk);
/* SoC-specific de-initialization */
@@ -1890,6 +1900,8 @@ static int rzg3s_pcie_host_stop(struct rzg3s_pcie_host *host)
if (ret)
goto power_resets_restore;
+ host->started = false;
+
return 0;
/* Restore the previous state if any error happens */
@@ -1905,12 +1917,15 @@ static int rzg3s_pcie_host_stop(struct rzg3s_pcie_host *host)
return ret;
}
-static int rzg3s_pcie_host_start(struct rzg3s_pcie_host *host)
+static int rzg3s_pcie_host_start(struct rzg3s_pcie_host *host, bool set_started)
{
const struct rzg3s_pcie_soc_data *data = host->data;
struct rzg3s_sysc *sysc = host->sysc;
int ret;
+ if (host->started)
+ return 0;
+
ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_MODE, 1);
if (ret)
return ret;
@@ -1943,6 +1958,9 @@ static int rzg3s_pcie_host_start(struct rzg3s_pcie_host *host)
if (ret)
goto assert_power_resets;
+ if (set_started)
+ host->started = true;
+
return 0;
/*
@@ -1956,6 +1974,64 @@ static int rzg3s_pcie_host_start(struct rzg3s_pcie_host *host)
return ret;
}
+static int rzg3s_pcie_host_reset_root_port(struct pci_host_bridge *bridge,
+ struct pci_dev *pdev)
+{
+ struct rzg3s_pcie_host *host = pci_host_bridge_priv(bridge);
+ unsigned long flags;
+ u32 irqs;
+ int ret;
+
+ /* Mask link up/down interrupts. */
+ writel(0, host->axi + RZG3S_PCI_PEIE0);
+
+ /* Mask INTx and MSI interrupts. */
+ raw_spin_lock_irqsave(&host->hw_lock, flags);
+ irqs = readl(host->axi + RZG3S_PCI_PINTRCVIE);
+ writel(0, host->axi + RZG3S_PCI_PINTRCVIE);
+ raw_spin_unlock_irqrestore(&host->hw_lock, flags);
+
+ /*
+ * Make sure the next operations are not disturbed by any pending
+ * IRQs.
+ */
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ synchronize_irq(host->msi.irq);
+ for (unsigned int i = 0; i < PCI_NUM_INTX; i++)
+ synchronize_irq(host->intx_irqs[i]);
+
+ ret = rzg3s_pcie_host_stop(host);
+ if (ret) {
+ dev_err(host->dev, "Failed to stop the host!\n");
+ goto unmask_irqs;
+ }
+
+ ret = rzg3s_pcie_host_start(host, false);
+ if (ret) {
+ dev_err(host->dev, "Failed to start the host!\n");
+
+ /*
+ * Don't unmask IRQs. We are in a bad state here and we
+ * can recover only through a suspend/resume cycle. Just
+ * return and preserve the stop state.
+ */
+ return ret;
+ }
+
+unmask_irqs:
+ /* Unmask INTx and MSI interrupts. */
+ raw_spin_lock_irqsave(&host->hw_lock, flags);
+ writel_relaxed(irqs, host->axi + RZG3S_PCI_PINTRCVIE);
+ raw_spin_unlock_irqrestore(&host->hw_lock, flags);
+
+ /* Unmask link up/down interrupts. */
+ writel_relaxed(RZG3S_PCI_PEIE0_DL_UPDOWN, host->axi + RZG3S_PCI_PEIE0);
+
+ host->started = true;
+
+ return ret;
+}
+
static int rzg3s_pcie_get_controller_id(struct rzg3s_pcie_host *host)
{
struct device_node *np = host->dev->of_node;
@@ -2036,6 +2112,7 @@ static int rzg3s_pcie_pm_notifier(struct notifier_block *nb,
case PM_SUSPEND_PREPARE:
/* Disable link up/down interrupts. */
disable_irq(host->event_irq);
+ writel_relaxed(0, host->axi + RZG3S_PCI_PEIE0);
break;
case PM_POST_SUSPEND:
@@ -2143,6 +2220,7 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
if (ret)
goto power_resets_assert;
+ host->started = true;
host->pm_nb.notifier_call = rzg3s_pcie_pm_notifier;
ret = register_pm_notifier(&host->pm_nb);
if (ret)
@@ -2151,6 +2229,7 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
bridge->sysdata = host;
bridge->ops = &rzg3s_pcie_root_ops;
bridge->child_ops = &rzg3s_pcie_child_ops;
+ bridge->reset_root_port = rzg3s_pcie_host_reset_root_port;
ret = pci_host_probe(bridge);
if (ret)
goto pm_notifier_unregister;
@@ -2201,7 +2280,7 @@ static int rzg3s_pcie_resume_noirq(struct device *dev)
{
struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
- return rzg3s_pcie_host_start(host);
+ return rzg3s_pcie_host_start(host, true);
}
static const struct dev_pm_ops rzg3s_pcie_pm_ops = {
--
2.43.0
next prev parent reply other threads:[~2026-10-02 11:10 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-02 11:09 [PATCH v5 0/9] PCI: rzg3s-host: Add PCIe hotplug support Claudiu Beznea
2026-10-02 11:09 ` [PATCH v5 1/9] PCI: rzg3s-host: Follow hardware manual clock/reset initialization order Claudiu Beznea
2026-10-02 11:23 ` sashiko-bot
2026-10-02 11:09 ` [PATCH v5 2/9] PCI: rzg3s-host: Fix runtime PM handling in the NOIRQ suspend/resume phase Claudiu Beznea
2026-10-02 11:18 ` sashiko-bot
2026-10-02 11:09 ` [PATCH v5 3/9] PCI: rzg3s-host: Select PCI_HOST_COMMON Claudiu Beznea
2026-10-02 11:22 ` sashiko-bot
2026-10-05 13:50 ` Claudiu Beznea
2026-10-02 11:09 ` [PATCH v5 4/9] PCI: rzg3s-host: Drop nop instructions Claudiu Beznea
2026-10-02 11:17 ` sashiko-bot
2026-10-02 11:09 ` [PATCH v5 5/9] PCI: rzg3s-host: Move host configuration code together Claudiu Beznea
2026-10-02 11:20 ` sashiko-bot
2026-10-02 11:09 ` [PATCH v5 6/9] PCI: rzg3s-host: Move suspend/resume code into dedicated functions Claudiu Beznea
2026-10-02 11:17 ` sashiko-bot
2026-10-02 11:09 ` [PATCH v5 7/9] PCI: rzg3s-host: Move IRQ domain setup code Claudiu Beznea
2026-10-02 11:19 ` sashiko-bot
2026-10-02 11:09 ` [PATCH v5 8/9] PCI: rzg3s-host: Re-enumerate the bus on PCIe link-state changes Claudiu Beznea
2026-10-02 11:25 ` sashiko-bot
2026-10-05 13:50 ` Claudiu Beznea
2026-10-02 11:09 ` Claudiu Beznea [this message]
2026-10-02 11:25 ` [PATCH v5 9/9] PCI: rzg3s-host: Add bridge::reset_root_port() sashiko-bot
2026-10-05 14:55 ` Claudiu Beznea
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=20261002110935.2004349-10-claudiu.beznea@kernel.org \
--to=claudiu.beznea@kernel.org \
--cc=18255117159@163.com \
--cc=bhelgaas@google.com \
--cc=claudiu.beznea.uj@bp.renesas.com \
--cc=claudiu.beznea@tuxon.dev \
--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=mani@kernel.org \
--cc=p.zabel@pengutronix.de \
--cc=prabhakar.mahadev-lad.rj@bp.renesas.com \
--cc=robh@kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.