* [PATCH v8 1/4] PCI: host-common: Fix early bus-walk exit in d3cold_possible()
2026-11-07 1:45 [PATCH v8 0/4] PCI: qcom: Implement shutdown() to avoid SMMU/NoC errors on reboot Krishna Chaitanya Chundru
@ 2026-11-07 1:45 ` Krishna Chaitanya Chundru
2026-11-07 1:45 ` [PATCH v8 2/4] PCI: dwc: Force L2 link entry on shutdown/reboot without D3cold check Krishna Chaitanya Chundru
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-11-07 1:45 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas
Cc: konrad.dybcio, linux-pci, linux-kernel, linux-arm-msm,
Krishna Chaitanya Chundru
__pci_host_common_d3cold_possible() returns -EOPNOTSUPP for the first
downstream device it finds outside PCI_D3hot, and pci_walk_bus() aborts
the walk as soon as its callback returns nonzero. Any device enumerated
after the disqualifying one -- including a wakeup-enabled, PME-from-D3cold
capable endpoint -- is then never visited, so pme_capable can come back
false even though such a device exists on the bus.
Since pci_host_common_d3cold_possible() already returns false whenever
any device disqualifies D3cold, aborting the walk buys nothing for the
plain suspend path: the overall bool result is unaffected. But it
silently drops pme_capable detection for any device ordered after the
disqualifying one.
This matters for the upcoming shutdown path in particular: unlike plain
suspend, shutdown forces the link into L2/D3cold regardless of whether
pci_host_common_d3cold_possible() itself allows it (see the following
"force_d3cold" changes), so at shutdown time it's common for an
endpoint to still be in D0 and disqualify D3cold while a later,
PME-capable device is never visited. dw_pcie_suspend_noirq() still
uses "pme_capable" to set pci->pp.skip_pwrctrl_off, so an inaccurate
result here can cause Vaux/wakeup support to be dropped for a device
that actually supports PME from D3cold.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/pci/controller/pci-host-common.c | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index a23907a875e5..c2c852c64aa8 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -275,21 +275,16 @@ static int __pci_host_common_d3cold_possible(struct pci_dev *pdev,
return 0;
if (pdev->current_state != PCI_D3hot)
- goto exit;
+ *flags &= ~PCI_HOST_D3COLD_ALLOWED;
if (device_may_wakeup(&pdev->dev)) {
- if (!pci_pme_capable(pdev, PCI_D3cold))
- goto exit;
- else
+ if (pci_pme_capable(pdev, PCI_D3cold))
*flags |= PCI_HOST_PME_D3COLD_CAPABLE;
+ else
+ *flags &= ~PCI_HOST_D3COLD_ALLOWED;
}
return 0;
-
-exit:
- *flags &= ~PCI_HOST_D3COLD_ALLOWED;
-
- return -EOPNOTSUPP;
}
/**
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v8 2/4] PCI: dwc: Force L2 link entry on shutdown/reboot without D3cold check
2026-11-07 1:45 [PATCH v8 0/4] PCI: qcom: Implement shutdown() to avoid SMMU/NoC errors on reboot Krishna Chaitanya Chundru
2026-11-07 1:45 ` [PATCH v8 1/4] PCI: host-common: Fix early bus-walk exit in d3cold_possible() Krishna Chaitanya Chundru
@ 2026-11-07 1:45 ` Krishna Chaitanya Chundru
2026-11-07 1:45 ` [PATCH v8 3/4] PCI: dwc: Add dw_pcie_mask_msi() to mask chained MSI without freeing domain Krishna Chaitanya Chundru
2026-11-07 1:45 ` [PATCH v8 4/4] PCI: qcom: Implement shutdown() callback Krishna Chaitanya Chundru
3 siblings, 0 replies; 5+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-11-07 1:45 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas
Cc: konrad.dybcio, linux-pci, linux-kernel, linux-arm-msm,
Krishna Chaitanya Chundru
dw_pcie_suspend_noirq() normally calls pci_host_common_d3cold_possible()
to check whether every downstream endpoint can be put into D3cold before
bothering to move the link to L2. If no endpoint supports it, the
function returns early and leaves the link up.
Querying D3cold support during shutdown is actively harmful, not just
slow: pci_host_common_d3cold_possible() requires every active endpoint
to already be in PCI_D3hot, and returns false otherwise. If any endpoint
is still in D0 -- which is common, since endpoint drivers aren't
guaranteed to have suspended by the time the host's shutdown path runs
the check fails and dw_pcie_suspend_noirq() returns early without ever
moving the link to L2, leaving it up right up to the point where the
system cuts power/clocks to the controller.
Add a force parameter to dw_pcie_suspend_noirq() that callers set
explicitly to force this behavior, still call
pci_host_common_d3cold_possible() unconditionally, since it's also how
pme_capable gets set, but ignore its return value and force L2 entry
regardless when force is set.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/pci/controller/dwc/pci-imx6.c | 2 +-
drivers/pci/controller/dwc/pci-layerscape.c | 2 +-
drivers/pci/controller/dwc/pcie-designware-host.c | 9 ++++-----
drivers/pci/controller/dwc/pcie-designware.h | 4 ++--
drivers/pci/controller/dwc/pcie-eswin.c | 2 +-
drivers/pci/controller/dwc/pcie-nxp-s32g.c | 2 +-
drivers/pci/controller/dwc/pcie-qcom.c | 2 +-
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 2 +-
drivers/pci/controller/dwc/pcie-stm32.c | 2 +-
drivers/pci/controller/dwc/pcie-ultrarisc.c | 2 +-
10 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 83fbd998551d..1f2b942e8423 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -1788,7 +1788,7 @@ static int imx_pcie_suspend_noirq(struct device *dev)
imx_pcie_assert_perst(imx_pcie, true);
imx_pcie->drvdata->enable_ref_clk(imx_pcie, false);
} else {
- return dw_pcie_suspend_noirq(imx_pcie->pci);
+ return dw_pcie_suspend_noirq(imx_pcie->pci, false);
}
return 0;
diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
index 7c9fb3bef682..8481fe5a0bf0 100644
--- a/drivers/pci/controller/dwc/pci-layerscape.c
+++ b/drivers/pci/controller/dwc/pci-layerscape.c
@@ -370,7 +370,7 @@ static int ls_pcie_suspend_noirq(struct device *dev)
if (!pcie->drvdata->pm_support)
return 0;
- return dw_pcie_suspend_noirq(pcie->pci);
+ return dw_pcie_suspend_noirq(pcie->pci, false);
}
static int ls_pcie_resume_noirq(struct device *dev)
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index ab343ed53ba5..0837bb9e67e0 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -1229,23 +1229,22 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
return 0;
}
-int dw_pcie_suspend_noirq(struct dw_pcie *pci)
+int dw_pcie_suspend_noirq(struct dw_pcie *pci, bool force)
{
- bool d3cold, pme_capable = false;
+ bool d3cold_possible, pme_capable = false;
int ret = 0;
u32 val;
if (!dw_pcie_link_up(pci))
goto stop_link;
- d3cold = pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable);
-
/*
* Enter L2 if D3cold is possible, or if the platform loses RC power
* during suspend (in which case L2 entry is required to notify
* endpoints before power loss, regardless of D3cold support).
*/
- if (!d3cold && !pci->pp.suspend_poweroff)
+ d3cold_possible = pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable);
+ if (!d3cold_possible && !force && !pci->pp.suspend_poweroff)
return 0;
if (pci->pp.ops->pme_turn_off) {
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 1b298639165d..90e6e25fde73 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -860,7 +860,7 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm);
#ifdef CONFIG_PCIE_DW_HOST
-int dw_pcie_suspend_noirq(struct dw_pcie *pci);
+int dw_pcie_suspend_noirq(struct dw_pcie *pci, bool force);
int dw_pcie_resume_noirq(struct dw_pcie *pci);
void dw_handle_msi_irq(struct dw_pcie_rp *pp);
void dw_pcie_msi_init(struct dw_pcie_rp *pp);
@@ -873,7 +873,7 @@ int dw_pcie_allocate_msi_domains(struct dw_pcie_rp *pp);
void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn,
int where);
#else
-static inline int dw_pcie_suspend_noirq(struct dw_pcie *pci)
+static inline int dw_pcie_suspend_noirq(struct dw_pcie *pci, bool force)
{
return 0;
}
diff --git a/drivers/pci/controller/dwc/pcie-eswin.c b/drivers/pci/controller/dwc/pcie-eswin.c
index 1bb44696057e..21d077172724 100644
--- a/drivers/pci/controller/dwc/pcie-eswin.c
+++ b/drivers/pci/controller/dwc/pcie-eswin.c
@@ -367,7 +367,7 @@ static int eswin_pcie_suspend_noirq(struct device *dev)
{
struct eswin_pcie *pcie = dev_get_drvdata(dev);
- return dw_pcie_suspend_noirq(&pcie->pci);
+ return dw_pcie_suspend_noirq(&pcie->pci, false);
}
static int eswin_pcie_resume_noirq(struct device *dev)
diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c
index 386e44dd0477..cb9cc9327d85 100644
--- a/drivers/pci/controller/dwc/pcie-nxp-s32g.c
+++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c
@@ -365,7 +365,7 @@ static int s32g_pcie_suspend_noirq(struct device *dev)
struct s32g_pcie *s32g_pp = dev_get_drvdata(dev);
struct dw_pcie *pci = &s32g_pp->pci;
- return dw_pcie_suspend_noirq(pci);
+ return dw_pcie_suspend_noirq(pci, false);
}
static int s32g_pcie_resume_noirq(struct device *dev)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index c62abfc7a446..d39753f0e724 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -2479,7 +2479,7 @@ static int qcom_pcie_suspend_noirq(struct device *dev)
if (!pcie)
return 0;
- ret = dw_pcie_suspend_noirq(pcie->pci);
+ ret = dw_pcie_suspend_noirq(pcie->pci, false);
if (ret)
return ret;
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index ba8e6b297674..e3807da316c8 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -1071,7 +1071,7 @@ static int rcar_gen4_pcie_suspend_noirq(struct device *dev)
if (rcar->drvdata->mode != DW_PCIE_RC_TYPE)
return 0;
- return dw_pcie_suspend_noirq(&rcar->dw);
+ return dw_pcie_suspend_noirq(&rcar->dw, false);
}
static int rcar_gen4_pcie_resume_noirq(struct device *dev)
diff --git a/drivers/pci/controller/dwc/pcie-stm32.c b/drivers/pci/controller/dwc/pcie-stm32.c
index 349618ea5b9c..7b3a8c1b04a4 100644
--- a/drivers/pci/controller/dwc/pcie-stm32.c
+++ b/drivers/pci/controller/dwc/pcie-stm32.c
@@ -78,7 +78,7 @@ static int stm32_pcie_suspend_noirq(struct device *dev)
struct stm32_pcie *stm32_pcie = dev_get_drvdata(dev);
int ret;
- ret = dw_pcie_suspend_noirq(&stm32_pcie->pci);
+ ret = dw_pcie_suspend_noirq(&stm32_pcie->pci, false);
if (ret)
return ret;
diff --git a/drivers/pci/controller/dwc/pcie-ultrarisc.c b/drivers/pci/controller/dwc/pcie-ultrarisc.c
index a0d328c0c09b..3d7af1ff327e 100644
--- a/drivers/pci/controller/dwc/pcie-ultrarisc.c
+++ b/drivers/pci/controller/dwc/pcie-ultrarisc.c
@@ -187,7 +187,7 @@ static int ultrarisc_pcie_suspend_noirq(struct device *dev)
if (pci->suspended)
return 0;
- ret = dw_pcie_suspend_noirq(pci);
+ ret = dw_pcie_suspend_noirq(pci, false);
if (ret)
return ret;
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v8 3/4] PCI: dwc: Add dw_pcie_mask_msi() to mask chained MSI without freeing domain
2026-11-07 1:45 [PATCH v8 0/4] PCI: qcom: Implement shutdown() to avoid SMMU/NoC errors on reboot Krishna Chaitanya Chundru
2026-11-07 1:45 ` [PATCH v8 1/4] PCI: host-common: Fix early bus-walk exit in d3cold_possible() Krishna Chaitanya Chundru
2026-11-07 1:45 ` [PATCH v8 2/4] PCI: dwc: Force L2 link entry on shutdown/reboot without D3cold check Krishna Chaitanya Chundru
@ 2026-11-07 1:45 ` Krishna Chaitanya Chundru
2026-11-07 1:45 ` [PATCH v8 4/4] PCI: qcom: Implement shutdown() callback Krishna Chaitanya Chundru
3 siblings, 0 replies; 5+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-11-07 1:45 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas
Cc: konrad.dybcio, linux-pci, linux-kernel, linux-arm-msm,
Krishna Chaitanya Chundru
dw_pcie_free_msi() detaches the chained MSI handler(s) and then removes
the MSI IRQ domain via irq_domain_remove(). That is correct for the
dw_pcie_host_deinit() teardown path, where PCI devices have already been
removed and no MSI mappings remain.
It is not safe to call from the shutdown()/reboot path. device_shutdown()
does not remove PCI devices or tear down their per-device child MSI
domains, so endpoint drivers that do not free their MSI vectors in a
shutdown callback leave live MSI descriptors parented to the domain.
Removing the domain there orphans those descriptors and leaves dangling
references to freed memory: during a subsequent kexec CPU teardown, IRQ
migration walks irq_data->parent_data and can dereference the freed
msi_irq_domain.
Add dw_pcie_mask_msi(), which only disables the controller's chained MSI
parent interrupt line(s) to stop further MSI delivery, leaving the IRQ
domain and the downstream MSI mappings intact. This gives the shutdown
path a way to silence MSIs before clocks/power are cut without the
teardown side effects of dw_pcie_free_msi().
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/pci/controller/dwc/pcie-designware-host.c | 22 ++++++++++++++++++++++
drivers/pci/controller/dwc/pcie-designware.h | 4 ++++
2 files changed, 26 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 0837bb9e67e0..7f0126c5336b 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -240,6 +240,28 @@ void dw_pcie_free_msi(struct dw_pcie_rp *pp)
}
EXPORT_SYMBOL_GPL(dw_pcie_free_msi);
+/**
+ * dw_pcie_mask_msi - Mask the chained MSI parent interrupt(s)
+ * @pp: DWC PCIe RP private data
+ *
+ * Disable the controller's chained MSI interrupt line(s) to stop further
+ * MSI delivery, while leaving the MSI IRQ domain and the MSI mappings held
+ * by downstream devices intact. Unlike dw_pcie_free_msi(), this does not
+ * remove the IRQ domain, so it is safe to call from the shutdown()/reboot
+ * path where PCI devices are not removed and their MSI descriptors must not
+ * be orphaned.
+ */
+void dw_pcie_mask_msi(struct dw_pcie_rp *pp)
+{
+ u32 ctrl;
+
+ for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) {
+ if (pp->msi_irq[ctrl] > 0)
+ disable_irq(pp->msi_irq[ctrl]);
+ }
+}
+EXPORT_SYMBOL_GPL(dw_pcie_mask_msi);
+
void dw_pcie_msi_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 90e6e25fde73..16b180275aca 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -866,6 +866,7 @@ void dw_handle_msi_irq(struct dw_pcie_rp *pp);
void dw_pcie_msi_init(struct dw_pcie_rp *pp);
int dw_pcie_msi_host_init(struct dw_pcie_rp *pp);
void dw_pcie_free_msi(struct dw_pcie_rp *pp);
+void dw_pcie_mask_msi(struct dw_pcie_rp *pp);
int dw_pcie_setup_rc(struct dw_pcie_rp *pp);
int dw_pcie_host_init(struct dw_pcie_rp *pp);
void dw_pcie_host_deinit(struct dw_pcie_rp *pp);
@@ -896,6 +897,9 @@ static inline int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
static inline void dw_pcie_free_msi(struct dw_pcie_rp *pp)
{ }
+static inline void dw_pcie_mask_msi(struct dw_pcie_rp *pp)
+{ }
+
static inline int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
{
return 0;
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v8 4/4] PCI: qcom: Implement shutdown() callback
2026-11-07 1:45 [PATCH v8 0/4] PCI: qcom: Implement shutdown() to avoid SMMU/NoC errors on reboot Krishna Chaitanya Chundru
` (2 preceding siblings ...)
2026-11-07 1:45 ` [PATCH v8 3/4] PCI: dwc: Add dw_pcie_mask_msi() to mask chained MSI without freeing domain Krishna Chaitanya Chundru
@ 2026-11-07 1:45 ` Krishna Chaitanya Chundru
3 siblings, 0 replies; 5+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-11-07 1:45 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas
Cc: konrad.dybcio, linux-pci, linux-kernel, linux-arm-msm,
Krishna Chaitanya Chundru, Manivannan Sadhasivam
From: Manivannan Sadhasivam <mani@kernel.org>
PCIe host controllers should bring the link down cleanly before system
shutdown/reboot proceeds to remove power/clocks from the controller.
Without this, the link may still be up and endpoints still have
transactions in flight when power/clocks are cut, which can trip SMMU
translation faults or NoC protocol errors.
Reuse dw_pcie_suspend_noirq() in the shutdown path to force the link
into L2, putting it into D3cold.
device_shutdown() runs with interrupts enabled, unlike suspend_noirq().
Mask the chained MSI IRQ(s) and the Global IRQ before tearing down the
link and clocks/PHY, since a late/spurious interrupt could otherwise
reach a handler that touches now-unclocked PARF/DBI registers. Use
dw_pcie_mask_msi() rather than dw_pcie_free_msi() so the MSI IRQ domain
and the MSI mappings held by downstream devices (which device_shutdown()
does not remove) are left intact.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/pci/controller/dwc/pcie-qcom.c | 41 ++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index d39753f0e724..c5b06db3b92a 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -2046,6 +2046,9 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg)
dw_pcie_msi_init(pp);
+ /* Stash pci so qcom_pcie_shutdown() can mask the MSI IRQ(s) later */
+ platform_set_drvdata(to_platform_device(dev), pci);
+
return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp);
}
@@ -2466,6 +2469,43 @@ static int qcom_pcie_probe(struct platform_device *pdev)
return ret;
}
+static void qcom_pcie_shutdown(struct platform_device *pdev)
+{
+ const struct qcom_pcie_cfg *pcie_cfg = of_device_get_match_data(&pdev->dev);
+ struct qcom_pcie *pcie = NULL;
+ struct dw_pcie *pci;
+
+ if (pcie_cfg && pcie_cfg->firmware_managed) {
+ /*
+ * Firmware owns the link teardown and clock/PHY shutdown in
+ * this mode; Linux only owns the chained MSI IRQ(s), which
+ * still need to be masked off before shutdown proceeds.
+ */
+ pci = platform_get_drvdata(pdev);
+ } else {
+ pcie = platform_get_drvdata(pdev);
+ pci = pcie->pci;
+
+ if (pcie->global_irq)
+ disable_irq(pcie->global_irq);
+ }
+
+ if (pci->pp.use_imsi_rx)
+ dw_pcie_mask_msi(&pci->pp);
+
+ /*
+ * In firmware-managed mode 'pcie' stays NULL and firmware owns the
+ * link/clock teardown. Otherwise, skip if the controller is already
+ * suspended (e.g. a prior resume failed), since dw_pcie_suspend_noirq()
+ * would touch DBI registers with clocks/interconnects already off.
+ */
+ if (pcie && !pcie->pci->suspended)
+ dw_pcie_suspend_noirq(pcie->pci, true);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
static int qcom_pcie_suspend_noirq(struct device *dev)
{
struct qcom_pcie *pcie;
@@ -2657,5 +2697,6 @@ static struct platform_driver qcom_pcie_driver = {
.pm = &qcom_pcie_pm_ops,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
+ .shutdown = qcom_pcie_shutdown,
};
builtin_platform_driver(qcom_pcie_driver);
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread