Re: [PATCH] PCI: Add D3cold reset quirk for devices with broken/missing FLR
From: Alex Williamson
Date: Thu May 07 2026 - 11:32:29 EST
On Thu, 7 May 2026 16:29:16 +0200
Jose Ignacio Tornos Martinez <jtornosm@xxxxxxxxxx> wrote:
> Some PCIe devices require D3cold power state transitions for proper
> firmware reset when used in VFIO passthrough scenarios, but lack
> Function Level Reset (FLR) capability or have incomplete FLR
> implementations that don't fully reset firmware state.
>
> Known devices affected:
> - Qualcomm ath11k WiFi (17cb:1103) - FLReset-, reset unreliable
> - Qualcomm ath12k WiFi (17cb:1107) - FLReset-, reset always fails
> - Qualcomm SDX62/SDX65 5G modems (17cb:0308) - FLReset-, never
> initialize in VMs
> - MediaTek mt7925e WiFi (14c3:7925) - FLReset+ but broken, reset always
> fails
>
> The problem manifests in two scenarios:
>
> 1. WiFi devices (ath11k, ath12k, mt7925e): 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. Because FLR is missing or broken, the
> reset fails and the device remains in an undefined state, preventing
> reuse.
>
> 2. Modem devices (SDX62/SDX65): Never successfully initialize even on first
> VM assignment due to lack of proper reset capability.
>
> Add reset_device_d3cold() quirk that performs a simple D3cold->D0
> power cycle when the device is bound to vfio-pci. This provides
> firmware reset capability for VM reset operations where standard PCI
> reset methods are insufficient.
>
> The quirk only applies during VFIO passthrough - native drivers use
> their own reset mechanisms and will fall back to standard PCI reset
> methods by returning -ENOTTY.
>
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@xxxxxxxxxx>
> ---
> drivers/pci/quirks.c | 38 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
>
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index caaed1a01dc0..11d9a8b562e4 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -4237,6 +4237,40 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
> return 0;
> }
>
> +/*
> + * Some devices need D3cold->D0 power cycle for proper firmware reset
> + * when used in VFIO passthrough. Some claim FLReset+ but it's incomplete,
> + * others lack FLR entirely, and standard reset methods don't fully reset
> + * firmware state. On bare metal with native drivers, we skip this and let
> + * the driver handle reset via standard methods.
> + */
> +static int reset_device_d3cold(struct pci_dev *dev, bool probe)
> +{
> + int ret;
> +
> + if (probe)
> + return 0;
> +
> + if (!dev->driver || strcmp(dev->driver->name, "vfio-pci") != 0)
We should not have driver dependent reset behavior. If FLR is broken,
add these devices to the list of devices using quirk_no_flr() and we'll
fall back to another reset method. We also shouldn't be implementing a
variant of pci_pm_reset(). PM reset can also be prioritized over FLR
via the reset_methods sysfs attribute if the reset method really is
tied to the usage. Thanks,
Alex
> + return -ENOTTY;
> +
> + /*
> + * D3cold->D0 power cycle for firmware reset.
> + * VFIO has already disabled interrupts and will handle state
> + * save/restore, so we just do the power transition.
> + */
> + ret = pci_set_power_state(dev, PCI_D3cold);
> + if (ret && ret != -EIO)
> + pci_warn(dev, "D3cold transition failed: %d\n", ret);
> +
> + ret = pci_set_power_state(dev, PCI_D0);
> + if (ret && ret != -EIO)
> + pci_warn(dev, "D0 transition failed: %d\n", ret);
> +
> + pci_info(dev, "D3cold reset completed\n");
> + return 0;
> +}
> +
> static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
> { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82599_SFP_VF,
> reset_intel_82599_sfp_virtfn },
> @@ -4252,6 +4286,10 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
> reset_chelsio_generic_dev },
> { PCI_VENDOR_ID_HUAWEI, PCI_DEVICE_ID_HINIC_VF,
> reset_hinic_vf_dev },
> + { PCI_VENDOR_ID_QCOM, 0x1103, reset_device_d3cold }, /* Qualcomm ath11k WiFi */
> + { PCI_VENDOR_ID_QCOM, 0x1107, reset_device_d3cold }, /* Qualcomm ath12k WiFi */
> + { PCI_VENDOR_ID_QCOM, 0x0308, reset_device_d3cold }, /* Qualcomm SDX62/SDX65 5G modem */
> + { PCI_VENDOR_ID_MEDIATEK, 0x7925, reset_device_d3cold }, /* MediaTek mt7925e WiFi */
> { 0 }
> };
>