summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-06-23 17:32:01 -0500
committerBjorn Helgaas <bhelgaas@google.com>2026-06-23 17:32:01 -0500
commit3a81c660546352577a35280898d9aa20d8a91e3d (patch)
tree371ceaf017cedc7749dcc0ee78e7a32580f63311 /drivers/pci
parent5ea9159447890425c37455e14f47eb243fcb3dd5 (diff)
parent748a927e081bffc9a9be17b4d96088515e1c5f01 (diff)
downloadlinux-next-3a81c660546352577a35280898d9aa20d8a91e3d.tar.gz
linux-next-3a81c660546352577a35280898d9aa20d8a91e3d.zip
Merge branch 'pci/pm'
- Set power state to 'unknown' for all devices, not just those with drivers, during suspend (Lukas Wunner) - Skip restoring Resizable BARs and VF Resizable BARs if device doesn't respond to config reads, to avoid invalid array accesses (Marco Nenciarini) - Add pci_suspend_retains_context() so drivers can tell whether devices may be reset while resuming from suspend due to platform issues; use this in nvme to avoid issues on Qcom RCs (Manivannan Sadhasivam) * pci/pm: nvme-pci: Use pci_suspend_retains_context() during suspend PCI: qcom: Indicate broken L1SS exit during resume from system suspend PCI: Indicate context lost if L1SS exit is broken during resume from system suspend PCI: Add pci_suspend_retains_context() to check if device state is preserved during suspend PCI/IOV: Skip VF Resizable BAR restore on read error PCI: Skip Resizable BAR restore on read error PCI: Stop setting cached power state to 'unknown' on unbind
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c13
-rw-r--r--drivers/pci/iov.c6
-rw-r--r--drivers/pci/pci-driver.c10
-rw-r--r--drivers/pci/pci.c35
-rw-r--r--drivers/pci/rebar.c6
5 files changed, 62 insertions, 8 deletions
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index af6bf5cce65b..a0b59b4ef1d2 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -1368,6 +1368,19 @@ static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp)
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct qcom_pcie *pcie = to_qcom_pcie(pci);
+ /*
+ * During system suspend, the Qcom RC driver may turn off the
+ * analog circuitry of PHY and remove controller votes to save
+ * power. If the link is in L1SS and the endpoint asserts CLKREQ#
+ * to exit L1SS, the time required to wake the system and restore
+ * the PHY/REFCLK may exceed the L1SS exit timing (L10_REFCLK_ON +
+ * T_COMMONMODE), resulting in Link Down (LDn) and a reset of the
+ * endpoint. Set this flag to indicate this limitation to client
+ * drivers so that they can avoid relying on device state being
+ * preserved during system suspend.
+ */
+ pp->bridge->broken_l1ss_resume = true;
+
if (pcie->cfg->ops->host_post_init)
pcie->cfg->ops->host_post_init(pcie);
}
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 91ac4e37ecb9..08df9bace13d 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -938,12 +938,18 @@ static void sriov_restore_vf_rebar_state(struct pci_dev *dev)
return;
pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl);
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return;
+
nbars = FIELD_GET(PCI_VF_REBAR_CTRL_NBAR_MASK, ctrl);
for (i = 0; i < nbars; i++, pos += 8) {
int bar_idx, size;
pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl);
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return;
+
bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl);
size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]);
ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d10ece0889f0..2bfefd8db526 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -513,13 +513,6 @@ static void pci_device_remove(struct device *dev)
pm_runtime_put_sync(dev);
/*
- * If the device is still on, set the power state as "unknown",
- * since it might change by the next time we load the driver.
- */
- if (pci_dev->current_state == PCI_D0)
- pci_dev->current_state = PCI_UNKNOWN;
-
- /*
* We would love to complain here if pci_dev->is_enabled is set, that
* the driver should have called pci_disable_device(), but the
* unfortunate fact is there are too many odd BIOS and bridge setups
@@ -893,7 +886,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
if (!pm) {
pci_save_state(pci_dev);
- goto Fixup;
+ goto set_unknown;
}
if (pm->suspend_noirq) {
@@ -945,6 +938,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
goto Fixup;
}
+set_unknown:
pci_pm_set_unknown_state(pci_dev);
/*
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ebbb5ec0a9df..8ec24562c94c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -33,6 +33,7 @@
#include <asm/dma.h>
#include <linux/aer.h>
#include <linux/bitfield.h>
+#include <linux/suspend.h>
#include "pci.h"
DEFINE_MUTEX(pci_slot_mutex);
@@ -2886,6 +2887,40 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
pm_runtime_put_sync(parent);
}
+/**
+ * pci_suspend_retains_context - Check if the platform can retain the device
+ * context during system suspend
+ * @pdev: PCI device to check
+ *
+ * Return: true if the platform can guarantee to retain the device context,
+ * false otherwise.
+ */
+bool pci_suspend_retains_context(struct pci_dev *pdev)
+{
+ struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);
+
+ /*
+ * If the platform firmware (like ACPI) is involved at the end of
+ * system suspend, device context may not be retained.
+ */
+ if (pm_suspend_via_firmware())
+ return false;
+
+ /*
+ * Some host bridges power off the PHY to enter deep low-power
+ * modes during system suspend. Exiting L1SS from this condition
+ * may violate timing requirements and result in Link Down (LDn),
+ * which causes a reset of the device. On such platforms, the
+ * endpoint must be prepared for context loss.
+ */
+ if (bridge && bridge->broken_l1ss_resume)
+ return false;
+
+ /* Assume that the context is retained by default */
+ return true;
+}
+EXPORT_SYMBOL_GPL(pci_suspend_retains_context);
+
static const struct dmi_system_id bridge_d3_blacklist[] = {
#ifdef CONFIG_X86
{
diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c
index 39f8cf3b70d5..11965947c4cb 100644
--- a/drivers/pci/rebar.c
+++ b/drivers/pci/rebar.c
@@ -231,6 +231,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev)
return;
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return;
+
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
for (i = 0; i < nbars; i++, pos += 8) {
@@ -238,6 +241,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev)
int bar_idx, size;
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return;
+
bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
res = pci_resource_n(pdev, bar_idx);
size = pci_rebar_bytes_to_size(resource_size(res));