* [PATCH v10] Add device-specific reset for Qualcomm devices
@ 2026-06-23 18:31 Jose Ignacio Tornos Martinez
2026-06-24 7:47 ` Baochen Qiang
2026-06-25 13:46 ` Manivannan Sadhasivam
0 siblings, 2 replies; 6+ messages in thread
From: Jose Ignacio Tornos Martinez @ 2026-06-23 18:31 UTC (permalink / raw)
To: bhelgaas, alex, mani
Cc: jjohnson, linux-pci, linux-wireless, ath11k, ath12k, mhi,
linux-kernel, Jose Ignacio Tornos Martinez
Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
lack working reset methods for VFIO passthrough scenarios. These devices
have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
broken bus reset.
The problem manifests in VFIO passthrough scenarios:
- WCN6855 (17cb:1103) and WCN7850 (17cb:1107) WiFi devices:
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.
- SDX62/SDX65 (17cb:0308) 5G modems: Never successfully initialize even
on first VM assignment without proper reset capability.
Add device-specific reset methods using BAR-space hardware reset registers
that exist in these devices:
- WCN6855/WCN7850 WiFi devices use SoC global reset via BAR0 (sequence from
ath11k/ath12k driver: ath11k_pci_soc_global_reset(), ath11k_pci_sw_reset(),
ath11k_mhi_set_mhictrl_reset()):
- Write/clear reset bit at offset 0x3008
- Wait for PCIe link recovery (up to 5 seconds)
- Clear MHI controller SYSERR status at offset 0x38
- SDX62/SDX65 modem devices use MHI SoC reset via BAR0 (sequence from MHI
driver: mhi_soc_reset(), mhi_pci_reset_prepare()):
- Write reset request to offset 0xb0
- Wait 2 seconds for reset completion
These are true hardware reset mechanisms (not power management or firmware
error recovery), providing proper device reset for VFIO scenarios.
Testing was performed on desktop platforms with M.2 WiFi and modem cards
using M.2-to-PCIe adapters, including extensive force-reset cycling to
verify stability.
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
v10:
- Complete redesign based on maintainer feedback (Manivannan Sadhasivam,
Alex Williamson): use actual hardware reset registers from
device drivers instead of D3hot power cycling
v9: https://lore.kernel.org/all/20260612142638.1243895-1-jtornosm@redhat.com/
drivers/pci/quirks.c | 118 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 118 insertions(+)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 431c021d7414..8ad3f214e520 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -4240,6 +4240,121 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
return 0;
}
+#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET 0x3008
+#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V BIT(0)
+#define QUALCOMM_WIFI_MHISTATUS 0x48
+#define QUALCOMM_WIFI_MHICTRL 0x38
+#define QUALCOMM_WIFI_MHICTRL_RESET_MASK 0x2
+
+/*
+ * Qualcomm WiFi device-specific reset using SoC global reset via BAR0
+ * registers.
+ */
+static int reset_qualcomm_wifi(struct pci_dev *pdev, bool probe)
+{
+ bool link_recovered = false;
+ unsigned long timeout;
+ void __iomem *bar;
+ u32 val;
+ u16 cmd;
+
+ if (probe)
+ return 0;
+
+ if (pdev->current_state != PCI_D0)
+ return -EINVAL;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
+
+ bar = pci_iomap(pdev, 0, 0);
+ if (!bar) {
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ return -ENODEV;
+ }
+
+ val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
+ val |= QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V;
+ iowrite32(val, bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
+ ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
+
+ msleep(10);
+
+ val &= ~QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V;
+ iowrite32(val, bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
+ ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
+
+ msleep(10);
+
+ timeout = jiffies + msecs_to_jiffies(5000);
+ while (time_before(jiffies, timeout)) {
+ val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
+ if (val != 0xffffffff) {
+ link_recovered = true;
+ break;
+ }
+ msleep(20);
+ }
+
+ if (!link_recovered) {
+ pci_err(pdev, "PCIe link failed to recover after reset\n");
+ goto out_restore;
+ }
+
+ /* After SOC_GLOBAL_RESET, MHISTATUS may still have SYSERR bit set
+ * and thus need to set MHICTRL_RESET to clear SYSERR.
+ */
+ iowrite32(QUALCOMM_WIFI_MHICTRL_RESET_MASK, bar + QUALCOMM_WIFI_MHICTRL);
+ ioread32(bar + QUALCOMM_WIFI_MHICTRL);
+
+ msleep(10);
+
+out_restore:
+ pci_iounmap(pdev, bar);
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ return link_recovered ? 0 : -ETIMEDOUT;
+}
+
+#define MHI_SOC_RESET_REQ_OFFSET 0xb0
+#define MHI_SOC_RESET_REQ BIT(0)
+
+/*
+ * Qualcomm modem device-specific reset using MHI SoC reset via BAR0
+ * register.
+ */
+static int reset_qualcomm_modem(struct pci_dev *pdev, bool probe)
+{
+ void __iomem *bar;
+ u16 cmd;
+
+ if (probe)
+ return 0;
+
+ if (pdev->current_state != PCI_D0)
+ return -EINVAL;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
+
+ bar = pci_iomap(pdev, 0, 0);
+ if (!bar) {
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ return -ENODEV;
+ }
+
+ iowrite32(MHI_SOC_RESET_REQ, bar + MHI_SOC_RESET_REQ_OFFSET);
+ ioread32(bar + MHI_SOC_RESET_REQ_OFFSET);
+
+ /* Be sure device reset has been executed */
+ msleep(2000);
+
+ pci_iounmap(pdev, bar);
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ 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 },
@@ -4255,6 +4370,9 @@ 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_qualcomm_wifi }, /* WCN6855 WiFi */
+ { PCI_VENDOR_ID_QCOM, 0x1107, reset_qualcomm_wifi }, /* WCN7850 WiFi */
+ { PCI_VENDOR_ID_QCOM, 0x0308, reset_qualcomm_modem }, /* SDX62/SDX65 modems */
{ 0 }
};
--
2.54.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH v10] Add device-specific reset for Qualcomm devices
2026-06-23 18:31 [PATCH v10] Add device-specific reset for Qualcomm devices Jose Ignacio Tornos Martinez
@ 2026-06-24 7:47 ` Baochen Qiang
2026-06-25 10:35 ` Jose Ignacio Tornos Martinez
2026-06-25 13:03 ` Manivannan Sadhasivam
2026-06-25 13:46 ` Manivannan Sadhasivam
1 sibling, 2 replies; 6+ messages in thread
From: Baochen Qiang @ 2026-06-24 7:47 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez, bhelgaas, alex, mani
Cc: jjohnson, linux-pci, linux-wireless, ath11k, ath12k, mhi,
linux-kernel
On 6/24/2026 2:31 AM, Jose Ignacio Tornos Martinez wrote:
> Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
> lack working reset methods for VFIO passthrough scenarios. These devices
> have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
> broken bus reset.
>
> The problem manifests in VFIO passthrough scenarios:
>
> - WCN6855 (17cb:1103) and WCN7850 (17cb:1107) WiFi devices:
> 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.
>
> - SDX62/SDX65 (17cb:0308) 5G modems: Never successfully initialize even
> on first VM assignment without proper reset capability.
>
> Add device-specific reset methods using BAR-space hardware reset registers
> that exist in these devices:
>
> - WCN6855/WCN7850 WiFi devices use SoC global reset via BAR0 (sequence from
> ath11k/ath12k driver: ath11k_pci_soc_global_reset(), ath11k_pci_sw_reset(),
> ath11k_mhi_set_mhictrl_reset()):
> - Write/clear reset bit at offset 0x3008
> - Wait for PCIe link recovery (up to 5 seconds)
> - Clear MHI controller SYSERR status at offset 0x38
>
> - SDX62/SDX65 modem devices use MHI SoC reset via BAR0 (sequence from MHI
> driver: mhi_soc_reset(), mhi_pci_reset_prepare()):
> - Write reset request to offset 0xb0
> - Wait 2 seconds for reset completion
>
> These are true hardware reset mechanisms (not power management or firmware
> error recovery), providing proper device reset for VFIO scenarios.
>
> Testing was performed on desktop platforms with M.2 WiFi and modem cards
> using M.2-to-PCIe adapters, including extensive force-reset cycling to
> verify stability.
>
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> ---
> v10:
> - Complete redesign based on maintainer feedback (Manivannan Sadhasivam,
> Alex Williamson): use actual hardware reset registers from
> device drivers instead of D3hot power cycling
> v9: https://lore.kernel.org/all/20260612142638.1243895-1-jtornosm@redhat.com/
>
> drivers/pci/quirks.c | 118 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 118 insertions(+)
>
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index 431c021d7414..8ad3f214e520 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -4240,6 +4240,121 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
> return 0;
> }
>
> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET 0x3008
> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V BIT(0)
> +#define QUALCOMM_WIFI_MHISTATUS 0x48
> +#define QUALCOMM_WIFI_MHICTRL 0x38
> +#define QUALCOMM_WIFI_MHICTRL_RESET_MASK 0x2
> +
> +/*
> + * Qualcomm WiFi device-specific reset using SoC global reset via BAR0
> + * registers.
> + */
> +static int reset_qualcomm_wifi(struct pci_dev *pdev, bool probe)
> +{
> + bool link_recovered = false;
> + unsigned long timeout;
> + void __iomem *bar;
> + u32 val;
> + u16 cmd;
> +
> + if (probe)
> + return 0;
> +
> + if (pdev->current_state != PCI_D0)
> + return -EINVAL;
> +
> + pci_read_config_word(pdev, PCI_COMMAND, &cmd);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
> +
> + bar = pci_iomap(pdev, 0, 0);
> + if (!bar) {
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> + return -ENODEV;
> + }
> +
> + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET is beyond the first 4K bar area hence requires MHI
wakeup before accessing, see [1]. the wakeup callback for WCN6855 is
ath11k_pci_bus_wake_up() which calls mhi_device_get_sync(). Not sure how this can be done
here. Maybe Mani can provide some hints?
[1]
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath11k/pcic.c#n216
> + val |= QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V;
> + iowrite32(val, bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> +
> + msleep(10);
> +
> + val &= ~QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V;
> + iowrite32(val, bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> +
> + msleep(10);
> +
> + timeout = jiffies + msecs_to_jiffies(5000);
> + while (time_before(jiffies, timeout)) {
> + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + if (val != 0xffffffff) {
> + link_recovered = true;
> + break;
> + }
> + msleep(20);
> + }
> +
> + if (!link_recovered) {
> + pci_err(pdev, "PCIe link failed to recover after reset\n");
> + goto out_restore;
> + }
> +
> + /* After SOC_GLOBAL_RESET, MHISTATUS may still have SYSERR bit set
> + * and thus need to set MHICTRL_RESET to clear SYSERR.
> + */
> + iowrite32(QUALCOMM_WIFI_MHICTRL_RESET_MASK, bar + QUALCOMM_WIFI_MHICTRL);
> + ioread32(bar + QUALCOMM_WIFI_MHICTRL);
> +
> + msleep(10);
> +
> +out_restore:
> + pci_iounmap(pdev, bar);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> +
> + return link_recovered ? 0 : -ETIMEDOUT;
> +}
> +
> +#define MHI_SOC_RESET_REQ_OFFSET 0xb0
> +#define MHI_SOC_RESET_REQ BIT(0)
> +
> +/*
> + * Qualcomm modem device-specific reset using MHI SoC reset via BAR0
> + * register.
> + */
> +static int reset_qualcomm_modem(struct pci_dev *pdev, bool probe)
> +{
> + void __iomem *bar;
> + u16 cmd;
> +
> + if (probe)
> + return 0;
> +
> + if (pdev->current_state != PCI_D0)
> + return -EINVAL;
> +
> + pci_read_config_word(pdev, PCI_COMMAND, &cmd);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
> +
> + bar = pci_iomap(pdev, 0, 0);
> + if (!bar) {
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> + return -ENODEV;
> + }
> +
> + iowrite32(MHI_SOC_RESET_REQ, bar + MHI_SOC_RESET_REQ_OFFSET);
> + ioread32(bar + MHI_SOC_RESET_REQ_OFFSET);
> +
> + /* Be sure device reset has been executed */
> + msleep(2000);
> +
> + pci_iounmap(pdev, bar);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> +
> + 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 },
> @@ -4255,6 +4370,9 @@ 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_qualcomm_wifi }, /* WCN6855 WiFi */
> + { PCI_VENDOR_ID_QCOM, 0x1107, reset_qualcomm_wifi }, /* WCN7850 WiFi */
> + { PCI_VENDOR_ID_QCOM, 0x0308, reset_qualcomm_modem }, /* SDX62/SDX65 modems */
> { 0 }
> };
>
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v10] Add device-specific reset for Qualcomm devices
2026-06-24 7:47 ` Baochen Qiang
@ 2026-06-25 10:35 ` Jose Ignacio Tornos Martinez
2026-06-25 13:03 ` Manivannan Sadhasivam
1 sibling, 0 replies; 6+ messages in thread
From: Jose Ignacio Tornos Martinez @ 2026-06-25 10:35 UTC (permalink / raw)
To: baochen.qiang
Cc: alex, ath11k, ath12k, bhelgaas, jjohnson, jtornosm, linux-kernel,
linux-pci, linux-wireless, mani, mhi
Hello Baochen and Mani,
> QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET is beyond the first 4K bar area hence requires MHI
> wakeup before accessing, see [1]. the wakeup callback for WCN6855 is
> ath11k_pci_bus_wake_up() which calls mhi_device_get_sync(). Not sure how this can be done
> here. Maybe Mani can provide some hints?
I've analyzed the driver code and see that ath11k_pci_power_down() calls
ath11k_pci_force_wake() before sw_reset().
I can add the same force_wake sequence to the WiFi quirk before accessing
the reset register:
/* Force wake before accessing registers beyond 4K boundary */
iowrite32(1, bar + QUALCOMM_WIFI_PCIE_SOC_WAKE_PCIE_LOCAL_REG); // 0x3004
ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_WAKE_PCIE_LOCAL_REG); // Flush
msleep(5);
With this addition, both WCN6855 (ath11k) and WCN7850 (ath12k) show successful
reset and shutdown cycles in VFIO scenarios, same stability as without it.
Do you consider this addition necessary, or is the current v10 implementation
sufficient given that testing shows direct register access works without
wakeup in VFIO scenarios (where no driver is loaded)?
If you recommend including it, I can send v11 with the force_wake sequence
added.
Thanks
Best regards
Jose Ignacio
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v10] Add device-specific reset for Qualcomm devices
2026-06-24 7:47 ` Baochen Qiang
2026-06-25 10:35 ` Jose Ignacio Tornos Martinez
@ 2026-06-25 13:03 ` Manivannan Sadhasivam
2026-06-29 9:44 ` Baochen Qiang
1 sibling, 1 reply; 6+ messages in thread
From: Manivannan Sadhasivam @ 2026-06-25 13:03 UTC (permalink / raw)
To: Baochen Qiang
Cc: Jose Ignacio Tornos Martinez, bhelgaas, alex, jjohnson, linux-pci,
linux-wireless, ath11k, ath12k, mhi, linux-kernel
On Wed, Jun 24, 2026 at 03:47:12PM +0800, Baochen Qiang wrote:
>
>
> On 6/24/2026 2:31 AM, Jose Ignacio Tornos Martinez wrote:
> > Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
> > lack working reset methods for VFIO passthrough scenarios. These devices
> > have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
> > broken bus reset.
> >
> > The problem manifests in VFIO passthrough scenarios:
> >
> > - WCN6855 (17cb:1103) and WCN7850 (17cb:1107) WiFi devices:
> > 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.
> >
> > - SDX62/SDX65 (17cb:0308) 5G modems: Never successfully initialize even
> > on first VM assignment without proper reset capability.
> >
> > Add device-specific reset methods using BAR-space hardware reset registers
> > that exist in these devices:
> >
> > - WCN6855/WCN7850 WiFi devices use SoC global reset via BAR0 (sequence from
> > ath11k/ath12k driver: ath11k_pci_soc_global_reset(), ath11k_pci_sw_reset(),
> > ath11k_mhi_set_mhictrl_reset()):
> > - Write/clear reset bit at offset 0x3008
> > - Wait for PCIe link recovery (up to 5 seconds)
> > - Clear MHI controller SYSERR status at offset 0x38
> >
> > - SDX62/SDX65 modem devices use MHI SoC reset via BAR0 (sequence from MHI
> > driver: mhi_soc_reset(), mhi_pci_reset_prepare()):
> > - Write reset request to offset 0xb0
> > - Wait 2 seconds for reset completion
> >
> > These are true hardware reset mechanisms (not power management or firmware
> > error recovery), providing proper device reset for VFIO scenarios.
> >
> > Testing was performed on desktop platforms with M.2 WiFi and modem cards
> > using M.2-to-PCIe adapters, including extensive force-reset cycling to
> > verify stability.
> >
> > Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> > ---
> > v10:
> > - Complete redesign based on maintainer feedback (Manivannan Sadhasivam,
> > Alex Williamson): use actual hardware reset registers from
> > device drivers instead of D3hot power cycling
> > v9: https://lore.kernel.org/all/20260612142638.1243895-1-jtornosm@redhat.com/
> >
> > drivers/pci/quirks.c | 118 +++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 118 insertions(+)
> >
> > diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> > index 431c021d7414..8ad3f214e520 100644
> > --- a/drivers/pci/quirks.c
> > +++ b/drivers/pci/quirks.c
> > @@ -4240,6 +4240,121 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
> > return 0;
> > }
> >
> > +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET 0x3008
> > +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V BIT(0)
> > +#define QUALCOMM_WIFI_MHISTATUS 0x48
> > +#define QUALCOMM_WIFI_MHICTRL 0x38
> > +#define QUALCOMM_WIFI_MHICTRL_RESET_MASK 0x2
> > +
> > +/*
> > + * Qualcomm WiFi device-specific reset using SoC global reset via BAR0
> > + * registers.
> > + */
> > +static int reset_qualcomm_wifi(struct pci_dev *pdev, bool probe)
> > +{
> > + bool link_recovered = false;
> > + unsigned long timeout;
> > + void __iomem *bar;
> > + u32 val;
> > + u16 cmd;
> > +
> > + if (probe)
> > + return 0;
> > +
> > + if (pdev->current_state != PCI_D0)
> > + return -EINVAL;
> > +
> > + pci_read_config_word(pdev, PCI_COMMAND, &cmd);
> > + pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
> > +
> > + bar = pci_iomap(pdev, 0, 0);
> > + if (!bar) {
> > + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> > + return -ENODEV;
> > + }
> > +
> > + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
>
> QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET is beyond the first 4K bar area hence requires MHI
> wakeup before accessing, see [1]. the wakeup callback for WCN6855 is
> ath11k_pci_bus_wake_up() which calls mhi_device_get_sync(). Not sure how this can be done
> here. Maybe Mani can provide some hints?
>
I don't think the device needs to be waken up before
QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET. ath11k driver wakes up the device for
accessing the MHI interface I believe. Since this callback is not touching MHI,
there is no need to wakeup the device, AFAIK.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v10] Add device-specific reset for Qualcomm devices
2026-06-25 13:03 ` Manivannan Sadhasivam
@ 2026-06-29 9:44 ` Baochen Qiang
0 siblings, 0 replies; 6+ messages in thread
From: Baochen Qiang @ 2026-06-29 9:44 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Jose Ignacio Tornos Martinez, bhelgaas, alex, jjohnson, linux-pci,
linux-wireless, ath11k, ath12k, mhi, linux-kernel
On 6/25/2026 9:03 PM, Manivannan Sadhasivam wrote:
> On Wed, Jun 24, 2026 at 03:47:12PM +0800, Baochen Qiang wrote:
>>
>>
>> On 6/24/2026 2:31 AM, Jose Ignacio Tornos Martinez wrote:
>>> Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
>>> lack working reset methods for VFIO passthrough scenarios. These devices
>>> have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
>>> broken bus reset.
>>>
>>> The problem manifests in VFIO passthrough scenarios:
>>>
>>> - WCN6855 (17cb:1103) and WCN7850 (17cb:1107) WiFi devices:
>>> 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.
>>>
>>> - SDX62/SDX65 (17cb:0308) 5G modems: Never successfully initialize even
>>> on first VM assignment without proper reset capability.
>>>
>>> Add device-specific reset methods using BAR-space hardware reset registers
>>> that exist in these devices:
>>>
>>> - WCN6855/WCN7850 WiFi devices use SoC global reset via BAR0 (sequence from
>>> ath11k/ath12k driver: ath11k_pci_soc_global_reset(), ath11k_pci_sw_reset(),
>>> ath11k_mhi_set_mhictrl_reset()):
>>> - Write/clear reset bit at offset 0x3008
>>> - Wait for PCIe link recovery (up to 5 seconds)
>>> - Clear MHI controller SYSERR status at offset 0x38
>>>
>>> - SDX62/SDX65 modem devices use MHI SoC reset via BAR0 (sequence from MHI
>>> driver: mhi_soc_reset(), mhi_pci_reset_prepare()):
>>> - Write reset request to offset 0xb0
>>> - Wait 2 seconds for reset completion
>>>
>>> These are true hardware reset mechanisms (not power management or firmware
>>> error recovery), providing proper device reset for VFIO scenarios.
>>>
>>> Testing was performed on desktop platforms with M.2 WiFi and modem cards
>>> using M.2-to-PCIe adapters, including extensive force-reset cycling to
>>> verify stability.
>>>
>>> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
>>> ---
>>> v10:
>>> - Complete redesign based on maintainer feedback (Manivannan Sadhasivam,
>>> Alex Williamson): use actual hardware reset registers from
>>> device drivers instead of D3hot power cycling
>>> v9: https://lore.kernel.org/all/20260612142638.1243895-1-jtornosm@redhat.com/
>>>
>>> drivers/pci/quirks.c | 118 +++++++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 118 insertions(+)
>>>
>>> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
>>> index 431c021d7414..8ad3f214e520 100644
>>> --- a/drivers/pci/quirks.c
>>> +++ b/drivers/pci/quirks.c
>>> @@ -4240,6 +4240,121 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
>>> return 0;
>>> }
>>>
>>> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET 0x3008
>>> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V BIT(0)
>>> +#define QUALCOMM_WIFI_MHISTATUS 0x48
>>> +#define QUALCOMM_WIFI_MHICTRL 0x38
>>> +#define QUALCOMM_WIFI_MHICTRL_RESET_MASK 0x2
>>> +
>>> +/*
>>> + * Qualcomm WiFi device-specific reset using SoC global reset via BAR0
>>> + * registers.
>>> + */
>>> +static int reset_qualcomm_wifi(struct pci_dev *pdev, bool probe)
>>> +{
>>> + bool link_recovered = false;
>>> + unsigned long timeout;
>>> + void __iomem *bar;
>>> + u32 val;
>>> + u16 cmd;
>>> +
>>> + if (probe)
>>> + return 0;
>>> +
>>> + if (pdev->current_state != PCI_D0)
>>> + return -EINVAL;
>>> +
>>> + pci_read_config_word(pdev, PCI_COMMAND, &cmd);
>>> + pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
>>> +
>>> + bar = pci_iomap(pdev, 0, 0);
>>> + if (!bar) {
>>> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
>>> + return -ENODEV;
>>> + }
>>> +
>>> + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
>>
>> QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET is beyond the first 4K bar area hence requires MHI
>> wakeup before accessing, see [1]. the wakeup callback for WCN6855 is
>> ath11k_pci_bus_wake_up() which calls mhi_device_get_sync(). Not sure how this can be done
>> here. Maybe Mani can provide some hints?
>>
>
> I don't think the device needs to be waken up before
> QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET. ath11k driver wakes up the device for
> accessing the MHI interface I believe. Since this callback is not touching MHI,
> there is no need to wakeup the device, AFAIK.
MHI register space is constitute with 3 segments, with mhi offset always being 0, bhi
offset 0x100 and bhie offset 0x224 (the latter two offsets are for WCN7850, but I think
also apply to other devices). That said the whole MHI register space does not go beyond
the first 4K region, hence wakeup is not for accessing MHI interface.
>
> - Mani
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v10] Add device-specific reset for Qualcomm devices
2026-06-23 18:31 [PATCH v10] Add device-specific reset for Qualcomm devices Jose Ignacio Tornos Martinez
2026-06-24 7:47 ` Baochen Qiang
@ 2026-06-25 13:46 ` Manivannan Sadhasivam
1 sibling, 0 replies; 6+ messages in thread
From: Manivannan Sadhasivam @ 2026-06-25 13:46 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez
Cc: bhelgaas, alex, jjohnson, linux-pci, linux-wireless, ath11k,
ath12k, mhi, linux-kernel
On Tue, Jun 23, 2026 at 08:31:15PM +0200, Jose Ignacio Tornos Martinez wrote:
> Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
> lack working reset methods for VFIO passthrough scenarios. These devices
> have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
> broken bus reset.
>
> The problem manifests in VFIO passthrough scenarios:
>
> - WCN6855 (17cb:1103) and WCN7850 (17cb:1107) WiFi devices:
> 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.
>
> - SDX62/SDX65 (17cb:0308) 5G modems: Never successfully initialize even
> on first VM assignment without proper reset capability.
>
> Add device-specific reset methods using BAR-space hardware reset registers
> that exist in these devices:
>
> - WCN6855/WCN7850 WiFi devices use SoC global reset via BAR0 (sequence from
> ath11k/ath12k driver: ath11k_pci_soc_global_reset(), ath11k_pci_sw_reset(),
> ath11k_mhi_set_mhictrl_reset()):
> - Write/clear reset bit at offset 0x3008
> - Wait for PCIe link recovery (up to 5 seconds)
> - Clear MHI controller SYSERR status at offset 0x38
>
> - SDX62/SDX65 modem devices use MHI SoC reset via BAR0 (sequence from MHI
> driver: mhi_soc_reset(), mhi_pci_reset_prepare()):
> - Write reset request to offset 0xb0
> - Wait 2 seconds for reset completion
>
> These are true hardware reset mechanisms (not power management or firmware
> error recovery), providing proper device reset for VFIO scenarios.
>
> Testing was performed on desktop platforms with M.2 WiFi and modem cards
> using M.2-to-PCIe adapters, including extensive force-reset cycling to
> verify stability.
>
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> ---
> v10:
> - Complete redesign based on maintainer feedback (Manivannan Sadhasivam,
> Alex Williamson): use actual hardware reset registers from
> device drivers instead of D3hot power cycling
> v9: https://lore.kernel.org/all/20260612142638.1243895-1-jtornosm@redhat.com/
>
> drivers/pci/quirks.c | 118 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 118 insertions(+)
>
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index 431c021d7414..8ad3f214e520 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -4240,6 +4240,121 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
> return 0;
> }
>
> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET 0x3008
> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V BIT(0)
> +#define QUALCOMM_WIFI_MHISTATUS 0x48
This macro is unused.
> +#define QUALCOMM_WIFI_MHICTRL 0x38
> +#define QUALCOMM_WIFI_MHICTRL_RESET_MASK 0x2
> +
> +/*
> + * Qualcomm WiFi device-specific reset using SoC global reset via BAR0
> + * registers.
> + */
> +static int reset_qualcomm_wifi(struct pci_dev *pdev, bool probe)
> +{
> + bool link_recovered = false;
> + unsigned long timeout;
> + void __iomem *bar;
> + u32 val;
> + u16 cmd;
> +
> + if (probe)
> + return 0;
> +
> + if (pdev->current_state != PCI_D0)
> + return -EINVAL;
> +
> + pci_read_config_word(pdev, PCI_COMMAND, &cmd);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
> +
> + bar = pci_iomap(pdev, 0, 0);
> + if (!bar) {
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> + return -ENODEV;
> + }
> +
> + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + val |= QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V;
> + iowrite32(val, bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> +
> + msleep(10);
> +
> + val &= ~QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V;
> + iowrite32(val, bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> +
> + msleep(10);
> +
> + timeout = jiffies + msecs_to_jiffies(5000);
> + while (time_before(jiffies, timeout)) {
> + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + if (val != 0xffffffff) {
s/0xffffffff/PCI_ERROR_RESPONSE
> + link_recovered = true;
> + break;
> + }
> + msleep(20);
> + }
> +
> + if (!link_recovered) {
> + pci_err(pdev, "PCIe link failed to recover after reset\n");
> + goto out_restore;
> + }
> +
> + /* After SOC_GLOBAL_RESET, MHISTATUS may still have SYSERR bit set
> + * and thus need to set MHICTRL_RESET to clear SYSERR.
> + */
> + iowrite32(QUALCOMM_WIFI_MHICTRL_RESET_MASK, bar + QUALCOMM_WIFI_MHICTRL);
> + ioread32(bar + QUALCOMM_WIFI_MHICTRL);
> +
> + msleep(10);
> +
> +out_restore:
> + pci_iounmap(pdev, bar);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> +
> + return link_recovered ? 0 : -ETIMEDOUT;
> +}
> +
> +#define MHI_SOC_RESET_REQ_OFFSET 0xb0
> +#define MHI_SOC_RESET_REQ BIT(0)
> +
> +/*
> + * Qualcomm modem device-specific reset using MHI SoC reset via BAR0
> + * register.
> + */
> +static int reset_qualcomm_modem(struct pci_dev *pdev, bool probe)
> +{
> + void __iomem *bar;
> + u16 cmd;
> +
> + if (probe)
> + return 0;
> +
> + if (pdev->current_state != PCI_D0)
> + return -EINVAL;
> +
> + pci_read_config_word(pdev, PCI_COMMAND, &cmd);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
> +
> + bar = pci_iomap(pdev, 0, 0);
> + if (!bar) {
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> + return -ENODEV;
> + }
> +
> + iowrite32(MHI_SOC_RESET_REQ, bar + MHI_SOC_RESET_REQ_OFFSET);
> + ioread32(bar + MHI_SOC_RESET_REQ_OFFSET);
> +
> + /* Be sure device reset has been executed */
> + msleep(2000);
> +
> + pci_iounmap(pdev, bar);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> +
> + 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 },
> @@ -4255,6 +4370,9 @@ 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_qualcomm_wifi }, /* WCN6855 WiFi */
> + { PCI_VENDOR_ID_QCOM, 0x1107, reset_qualcomm_wifi }, /* WCN7850 WiFi */
> + { PCI_VENDOR_ID_QCOM, 0x0308, reset_qualcomm_modem }, /* SDX62/SDX65 modems */
Not an issue and the existing entries are not sorted, but can you atleast keep
these IDs sorted in ascending order?
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-29 9:44 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-23 18:31 [PATCH v10] Add device-specific reset for Qualcomm devices Jose Ignacio Tornos Martinez
2026-06-24 7:47 ` Baochen Qiang
2026-06-25 10:35 ` Jose Ignacio Tornos Martinez
2026-06-25 13:03 ` Manivannan Sadhasivam
2026-06-29 9:44 ` Baochen Qiang
2026-06-25 13:46 ` Manivannan Sadhasivam
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox