Re: [PATCH v10] Add device-specific reset for Qualcomm devices
Manivannan Sadhasivam <[email protected]> Thu, 25 Jun 2026 15:46:55 +0200
| Newsgroups | dev.linux.lists.mhi,org.infradead.lists.ath11k,org.infradead.lists.ath12k,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.linux-wireless |
|---|---|
| Message-ID | <mxarrodtocadpq3yu4nkbs6g7u5tzpk3vg5ot2tmlw442eg2hg@nlcdcinamlah> |
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 <[email protected]> > --- > 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/[email protected]/ > > 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 -- மணிவண்ணன் சதாசிவம்