Linux PCI subsystem development
 help / color / mirror / Atom feed
* [PATCH v3 1/2] PCI: Add D3cold as general reset method
@ 2026-05-13 12:23 Jose Ignacio Tornos Martinez
  2026-05-13 12:23 ` [PATCH v3 2/2] PCI: Disable broken bus reset on Qualcomm devices Jose Ignacio Tornos Martinez
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Jose Ignacio Tornos Martinez @ 2026-05-13 12:23 UTC (permalink / raw)
  To: bhelgaas, alex; +Cc: linux-pci, linux-kernel, Jose Ignacio Tornos Martinez

Add D3cold power cycle as a general PCI reset method, available for
single-function devices. This provides a more robust reset mechanism
than D3hot for devices where a full power cycle is beneficial.

The implementation uses pci_set_power_state(dev, PCI_D3cold), which
automatically handles platform differences:
- Platforms WITH _PR3 ACPI power resources: true D3cold (power cycle)
- Platforms WITHOUT _PR3: automatic fallback to D3hot transition

D3cold reset is placed at the end of the reset hierarchy as a last
resort before giving up, since it provides a strong reset when other
methods are unavailable or broken.

Reset hierarchy with this change:
1. device_specific
2. acpi
3. flr
4. af_flr
5. pm (D3hot via config space)
6. bus (SBR)
7. cxl_bus
8. d3cold (NEW - power cycle with D3hot fallback)

This benefits devices that:
- Have broken or missing FLR
- Advertise NoSoftRst+ incorrectly (blocking D3hot PM reset)
- Have broken bus reset implementations
- Need power cycle for reliable reset
- Are used in VFIO passthrough scenarios

Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
v3: Implement d3cold as a general PCI core reset method instead of
  device-specific quirk approach from v2 (Alex Williamson suggestion)
v2: https://lore.kernel.org/all/20260508145153.717641-2-jtornosm@redhat.com/

 drivers/pci/pci.c   | 41 +++++++++++++++++++++++++++++++++++++++++
 include/linux/pci.h |  2 +-
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 8f7cfcc00090..6da8feff7ccc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4491,6 +4491,46 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe)
 	return ret;
 }
 
+/**
+ * pci_d3cold_reset - Put device into D3cold (or D3hot) and back to D0 for reset
+ * @dev: PCI device to reset
+ * @probe: if true, check if D3cold reset is supported; if false, perform reset
+ *
+ * Attempt to reset the device by transitioning through D3cold and back to D0.
+ * On platforms with ACPI _PR3 power resources, this performs a true D3cold
+ * power cycle (actual power removal). On platforms without _PR3 support,
+ * pci_set_power_state() automatically falls back to D3hot, providing a
+ * D3hot->D0 reset transition.
+ *
+ * Only available for single-function devices to avoid affecting other
+ * functions in multi-function devices.
+ *
+ * Returns 0 if device can be/was reset this way, -ENOTTY if not supported,
+ * or other negative error code on failure.
+ */
+static int pci_d3cold_reset(struct pci_dev *dev, bool probe)
+{
+	int ret;
+
+	if (dev->multifunction)
+		return -ENOTTY;
+
+	if (probe) {
+		if (!pci_pr3_present(dev))
+			pci_dbg(dev, "d3cold reset: no _PR3 support, will use D3hot fallback\n");
+		return 0;
+	}
+
+	if (dev->current_state != PCI_D0)
+		return -EINVAL;
+
+	ret = pci_set_power_state(dev, PCI_D3cold);
+	if (ret)
+		return ret;
+
+	return pci_set_power_state(dev, PCI_D0);
+}
+
 /**
  * pcie_wait_for_link_status - Wait for link status change
  * @pdev: Device whose link to wait for.
@@ -5065,6 +5105,7 @@ const struct pci_reset_fn_method pci_reset_fn_methods[] = {
 	{ pci_pm_reset, .name = "pm" },
 	{ pci_reset_bus_function, .name = "bus" },
 	{ cxl_reset_bus_function, .name = "cxl_bus" },
+	{ pci_d3cold_reset, .name = "d3cold" },
 };
 
 /**
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2c4454583c11..1ca7b880ead7 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -51,7 +51,7 @@
 			       PCI_STATUS_PARITY)
 
 /* Number of reset methods used in pci_reset_fn_methods array in pci.c */
-#define PCI_NUM_RESET_METHODS 8
+#define PCI_NUM_RESET_METHODS 9
 
 #define PCI_RESET_PROBE		true
 #define PCI_RESET_DO_RESET	false
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v3 2/2] PCI: Disable broken bus reset on Qualcomm devices
  2026-05-13 12:23 [PATCH v3 1/2] PCI: Add D3cold as general reset method Jose Ignacio Tornos Martinez
@ 2026-05-13 12:23 ` Jose Ignacio Tornos Martinez
  2026-05-13 13:31 ` [PATCH v3 1/2] PCI: Add D3cold as general reset method Alex Williamson
  2026-05-13 14:39 ` Lukas Wunner
  2 siblings, 0 replies; 4+ messages in thread
From: Jose Ignacio Tornos Martinez @ 2026-05-13 12:23 UTC (permalink / raw)
  To: bhelgaas, alex; +Cc: linux-pci, linux-kernel, Jose Ignacio Tornos Martinez

Some Qualcomm PCIe devices do not properly support Secondary Bus Reset
(SBR). These devices have no FLR capability and advertise NoSoftRst+
(blocking PM reset), leaving bus reset as the only available method.
However, bus reset does not work reliably for these devices.

The problem manifests in VFIO passthrough scenarios with these affected
devices:

- ath11k WiFi (17cb:1103): Normal VM operation works fine, including
  clean shutdown/reboot. However, when the VM terminates uncleanly
  (crash, force-off), VFIO attempts to reset the device before it can
  be assigned to another VM. Without a working reset method, the device
  remains in an undefined state, preventing reuse.

- ath12k WiFi (17cb:1107): Same behavior as ath11k.

- SDX62/SDX65 5G modems (17cb:0308): Never successfully initialize even
  on first VM assignment without proper reset capability.

Disable bus reset for these devices (following the pattern of other
Atheros/Qualcomm devices) so the d3cold reset method (added in previous
patch) is used instead, which provides working reset via D3cold power
cycle or D3hot fallback.

Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
v3: Try to address Alex Williamson comments:
    - Change from PCI_DEV_FLAGS_FORCE_PM_RESET quirk (v2) to quirk_no_bus_reset
    - Rely on d3cold general reset method (patch 1) instead of forcing PM reset
v2: https://lore.kernel.org/all/20260508145153.717641-2-jtornosm@redhat.com/

 drivers/pci/quirks.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 000000000000..111111111111 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3789,6 +3789,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0032, quirk_no_bus_reset);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0034, quirk_no_bus_reset);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003e, quirk_no_bus_reset);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x1103, quirk_no_bus_reset); /* ath11k */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x1107, quirk_no_bus_reset); /* ath12k */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x0308, quirk_no_bus_reset); /* SDX62/SDX65 */

 /*
  * Root port on some Cavium CN8xxx chips do not successfully complete a bus
--
2.53.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 1/2] PCI: Add D3cold as general reset method
  2026-05-13 12:23 [PATCH v3 1/2] PCI: Add D3cold as general reset method Jose Ignacio Tornos Martinez
  2026-05-13 12:23 ` [PATCH v3 2/2] PCI: Disable broken bus reset on Qualcomm devices Jose Ignacio Tornos Martinez
@ 2026-05-13 13:31 ` Alex Williamson
  2026-05-13 14:39 ` Lukas Wunner
  2 siblings, 0 replies; 4+ messages in thread
From: Alex Williamson @ 2026-05-13 13:31 UTC (permalink / raw)
  To: Jose Ignacio Tornos Martinez, Bjorn Helgaas; +Cc: linux-pci, linux-kernel

On Wed, May 13, 2026, at 6:23 AM, Jose Ignacio Tornos Martinez wrote:
> Add D3cold power cycle as a general PCI reset method, available for
> single-function devices. This provides a more robust reset mechanism
> than D3hot for devices where a full power cycle is beneficial.
>
> The implementation uses pci_set_power_state(dev, PCI_D3cold), which
> automatically handles platform differences:
> - Platforms WITH _PR3 ACPI power resources: true D3cold (power cycle)
> - Platforms WITHOUT _PR3: automatic fallback to D3hot transition
>
> D3cold reset is placed at the end of the reset hierarchy as a last
> resort before giving up, since it provides a strong reset when other
> methods are unavailable or broken.
>
> Reset hierarchy with this change:
> 1. device_specific
> 2. acpi
> 3. flr
> 4. af_flr
> 5. pm (D3hot via config space)
> 6. bus (SBR)
> 7. cxl_bus
> 8. d3cold (NEW - power cycle with D3hot fallback)
>
> This benefits devices that:
> - Have broken or missing FLR
> - Advertise NoSoftRst+ incorrectly (blocking D3hot PM reset)
> - Have broken bus reset implementations
> - Need power cycle for reliable reset
> - Are used in VFIO passthrough scenarios
>
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> ---
> v3: Implement d3cold as a general PCI core reset method instead of
>   device-specific quirk approach from v2 (Alex Williamson suggestion)
> v2: https://lore.kernel.org/all/20260508145153.717641-2-jtornosm@redhat.com/
>
>  drivers/pci/pci.c   | 41 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/pci.h |  2 +-
>  2 files changed, 42 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 8f7cfcc00090..6da8feff7ccc 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -4491,6 +4491,46 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe)
>  	return ret;
>  }
> 
> +/**
> + * pci_d3cold_reset - Put device into D3cold (or D3hot) and back to D0 
> for reset
> + * @dev: PCI device to reset
> + * @probe: if true, check if D3cold reset is supported; if false, 
> perform reset
> + *
> + * Attempt to reset the device by transitioning through D3cold and 
> back to D0.
> + * On platforms with ACPI _PR3 power resources, this performs a true 
> D3cold
> + * power cycle (actual power removal). On platforms without _PR3 
> support,
> + * pci_set_power_state() automatically falls back to D3hot, providing a
> + * D3hot->D0 reset transition.
> + *
> + * Only available for single-function devices to avoid affecting other
> + * functions in multi-function devices.
> + *
> + * Returns 0 if device can be/was reset this way, -ENOTTY if not 
> supported,
> + * or other negative error code on failure.
> + */
> +static int pci_d3cold_reset(struct pci_dev *dev, bool probe)
> +{
> +	int ret;
> +
> +	if (dev->multifunction)
> +		return -ENOTTY;
> +
> +	if (probe) {
> +		if (!pci_pr3_present(dev))
> +			pci_dbg(dev, "d3cold reset: no _PR3 support, will use D3hot 
> fallback\n");

This fall through is invalid, if D3cold can't be reached the reset should be handled by pci_pm_reset() where NoSoftRst is honored.  pci_pr3_preset() should be tested in all cases, not just probe.  I think we also need to test device flags that would prevent a D3cold transition, like pci_pm_reset().  Thanks,

Alex

> +		return 0;
> +	}
> +
> +	if (dev->current_state != PCI_D0)
> +		return -EINVAL;
> +
> +	ret = pci_set_power_state(dev, PCI_D3cold);
> +	if (ret)
> +		return ret;
> +
> +	return pci_set_power_state(dev, PCI_D0);
> +}
> +
>  /**
>   * pcie_wait_for_link_status - Wait for link status change
>   * @pdev: Device whose link to wait for.
> @@ -5065,6 +5105,7 @@ const struct pci_reset_fn_method 
> pci_reset_fn_methods[] = {
>  	{ pci_pm_reset, .name = "pm" },
>  	{ pci_reset_bus_function, .name = "bus" },
>  	{ cxl_reset_bus_function, .name = "cxl_bus" },
> +	{ pci_d3cold_reset, .name = "d3cold" },
>  };
> 
>  /**
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 2c4454583c11..1ca7b880ead7 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -51,7 +51,7 @@
>  			       PCI_STATUS_PARITY)
> 
>  /* Number of reset methods used in pci_reset_fn_methods array in pci.c */
> -#define PCI_NUM_RESET_METHODS 8
> +#define PCI_NUM_RESET_METHODS 9
> 
>  #define PCI_RESET_PROBE		true
>  #define PCI_RESET_DO_RESET	false
> -- 
> 2.53.0

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 1/2] PCI: Add D3cold as general reset method
  2026-05-13 12:23 [PATCH v3 1/2] PCI: Add D3cold as general reset method Jose Ignacio Tornos Martinez
  2026-05-13 12:23 ` [PATCH v3 2/2] PCI: Disable broken bus reset on Qualcomm devices Jose Ignacio Tornos Martinez
  2026-05-13 13:31 ` [PATCH v3 1/2] PCI: Add D3cold as general reset method Alex Williamson
@ 2026-05-13 14:39 ` Lukas Wunner
  2 siblings, 0 replies; 4+ messages in thread
From: Lukas Wunner @ 2026-05-13 14:39 UTC (permalink / raw)
  To: Jose Ignacio Tornos Martinez; +Cc: bhelgaas, alex, linux-pci, linux-kernel

On Wed, May 13, 2026 at 02:23:48PM +0200, Jose Ignacio Tornos Martinez wrote:
> Add D3cold power cycle as a general PCI reset method, available for
> single-function devices. This provides a more robust reset mechanism
> than D3hot for devices where a full power cycle is beneficial.
> 
> The implementation uses pci_set_power_state(dev, PCI_D3cold), which
> automatically handles platform differences:
> - Platforms WITH _PR3 ACPI power resources: true D3cold (power cycle)
> - Platforms WITHOUT _PR3: automatic fallback to D3hot transition

Why fall back to D3hot, even though the name of the reset method
suggests it uses D3cold?  If D3cold isn't supported, I'd just not
expose the new reset method.

> Reset hierarchy with this change:
> 1. device_specific
> 2. acpi
> 3. flr
> 4. af_flr
> 5. pm (D3hot via config space)
> 6. bus (SBR)
> 7. cxl_bus
> 8. d3cold (NEW - power cycle with D3hot fallback)

I'd call it "powercycle" for clarity but that's just my preferred
bikeshed color.

> +static int pci_d3cold_reset(struct pci_dev *dev, bool probe)
> +{
> +	int ret;
> +
> +	if (dev->multifunction)
> +		return -ENOTTY;
> +
> +	if (probe) {
> +		if (!pci_pr3_present(dev))
> +			pci_dbg(dev, "d3cold reset: no _PR3 support, will use D3hot fallback\n");
> +		return 0;
> +	}

So I'd just return -ENOTTY here in the !pci_pr3_present() case
and I'd do that for probe == 0 as well.

Thanks,

Lukas

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-05-13 14:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-13 12:23 [PATCH v3 1/2] PCI: Add D3cold as general reset method Jose Ignacio Tornos Martinez
2026-05-13 12:23 ` [PATCH v3 2/2] PCI: Disable broken bus reset on Qualcomm devices Jose Ignacio Tornos Martinez
2026-05-13 13:31 ` [PATCH v3 1/2] PCI: Add D3cold as general reset method Alex Williamson
2026-05-13 14:39 ` Lukas Wunner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox