Re: [PATCH v4 8/8] PCI: rzg3s-host: Add bridge::reset_root_port()
From: Lad, Prabhakar
Date: Thu Sep 24 2026 - 08:35:29 EST
On Tue, Sep 22, 2026 at 3:55 PM Claudiu Beznea
<claudiu.beznea@xxxxxxxxxx> wrote:
>
> From: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx>
>
> 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.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx>
> ---
>
> 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 | 77 +++++++++++++++++++++++-
> 1 file changed, 75 insertions(+), 2 deletions(-)
>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>
Tested-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>
Cheers,
Prabhakar
> diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
> index 1c0353598b59..9cba898a9536 100644
> --- a/drivers/pci/controller/pcie-rzg3s-host.c
> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> @@ -328,6 +328,7 @@ struct rzg3s_pcie_port {
> * @intx_irqs: INTx interrupts
> * @max_link_speed: maximum supported link speed
> * @controller_id: PCIe controller identifier, used for System Controller access
> + * @started: The PCIe controller state (started or not)
> * @num_lanes: The number of lanes
> */
> struct rzg3s_pcie_host {
> @@ -346,6 +347,7 @@ struct rzg3s_pcie_host {
> int intx_irqs[PCI_NUM_INTX];
> int max_link_speed;
> enum rzg3s_pcie_controller_id controller_id;
> + bool started;
> u8 num_lanes;
> };
>
> @@ -1110,6 +1112,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)
> @@ -1855,6 +1862,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 */
> @@ -1876,6 +1886,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 */
> @@ -1891,12 +1903,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;
> @@ -1929,6 +1944,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;
>
> /*
> @@ -1942,6 +1960,58 @@ 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);
> + u32 irqs;
> + int ret;
> +
> + /* Mask link up/down interrupts. */
> + writel(0, host->axi + RZG3S_PCI_PEIE0);
> +
> + /* Mask INTx and MSI interrupts. */
> + irqs = readl_relaxed(host->axi + RZG3S_PCI_PINTRCVIE);
> + writel(0, host->axi + RZG3S_PCI_PINTRCVIE);
> +
> + /*
> + * Make sure the next operations are not disturbed by any pending
> + * IRQs.
> + */
> + 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. */
> + writel_relaxed(irqs, host->axi + RZG3S_PCI_PINTRCVIE);
> +
> + /* Unmask link up/down interrupts. */
> + writel(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;
> @@ -2104,9 +2174,12 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
> if (ret)
> goto power_resets_assert;
>
> + host->started = true;
> +
> 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 host_probe_teardown;
> @@ -2156,7 +2229,7 @@ static int rzg3s_pcie_resume_noirq(struct device *dev)
> struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
> int ret;
>
> - ret = rzg3s_pcie_host_start(host);
> + ret = rzg3s_pcie_host_start(host, true);
> if (ret)
> return ret;
>
> --
> 2.43.0
>
>