[PATCH 1/2] PCI: qcom: Implement .reset_root_port() for firmware-managed controllers
From: Mrinmay Sarkar
Date: Fri Oct 16 2026 - 09:03:24 EST
During PCIe recovery events, such as a fatal AER event or other
platform-specific PCIe failures, the controller may enter a state that
requires a full reset before normal link operation can be restored.
On firmware-managed PCIe controllers, Linux does not control the PHY,
clocks, resets, or LTSSM state directly. Instead, these resources are
managed by firmware through the power-domain framework.
Implement the reset_root_port() callback by performing a Runtime PM
power cycle of the controller. This allows firmware to reset and
reinitialize the hardware before bringing the controller back online.
Since controller state is lost across the power cycle, reinitialize the
DWC MSI configuration after resume.
This enables Root Port reset and recovery support for firmware-managed
controllers.
Signed-off-by: Mrinmay Sarkar <mrinmay.sarkar@xxxxxxxxxxxxxxxx>
---
drivers/pci/controller/dwc/pcie-qcom.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index d903f10211c2..744455ae90d4 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -1855,6 +1855,35 @@ static int qcom_pcie_set_max_opp(struct device *dev)
return ret;
}
+/*
+ * Reset for firmware-managed controllers: power-cycle via runtime PM and
+ * reprogram DWC MSI registers on resume.
+ */
+static int qcom_pcie_fw_reset_root_port(struct pci_host_bridge *bridge,
+ struct pci_dev *pdev)
+{
+ struct pci_config_window *cfg = bridge->sysdata;
+ struct device *dev = bridge->dev.parent;
+ struct dw_pcie_rp *pp = cfg->priv;
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ dev_err(dev, "Failed to force runtime suspend for reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "Failed to force runtime resume after reset: %d\n", ret);
+ return ret;
+ }
+
+ dw_pcie_msi_init(pp);
+
+ return 0;
+}
+
/*
* Qcom PCIe controllers only support one Root Port per controller instance. So
* this function ignores the 'pci_dev' associated with the Root Port and just
@@ -2031,6 +2060,8 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg)
dw_pcie_msi_init(pp);
+ cfg->priv = pp;
+
return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp);
}
@@ -2292,6 +2323,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
bridge->sysdata = cfg;
bridge->ops = (struct pci_ops *)&pci_qcom_ecam_ops.pci_ops;
bridge->msi_domain = true;
+ bridge->reset_root_port = qcom_pcie_fw_reset_root_port;
ret = pci_host_probe(bridge);
if (ret)
--
2.43.0