* [PATCH v12] PCI: Add device-specific reset for Qualcomm devices
@ 2026-06-30 6:58 Jose Ignacio Tornos Martinez
2026-06-30 7:09 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Jose Ignacio Tornos Martinez @ 2026-06-30 6:58 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 WLAN 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) WLAN 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 WLAN 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 WLAN 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>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
---
v12: Address Manivannan Sadhasivam feedback:
- Use WLAN instead of WiFi (Qualcomm's preferred terminology)
- Fix link recovery check: use !PCI_POSSIBLE_ERROR(val) instead of
val != PCI_ERROR_RESPONSE (fixes type promotion bug)
v11: https://lore.kernel.org/all/20260626055023.197470-1-jtornosm@redhat.com/
drivers/pci/quirks.c | 117 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 117 insertions(+)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 431c021d7414..0de606366200 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -4240,6 +4240,120 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
return 0;
}
+#define QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET 0x3008
+#define QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V BIT(0)
+#define QUALCOMM_WLAN_MHICTRL 0x38
+#define QUALCOMM_WLAN_MHICTRL_RESET_MASK 0x2
+
+/*
+ * Qualcomm WLAN device-specific reset using SoC global reset via BAR0
+ * registers.
+ */
+static int reset_qualcomm_wlan(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_WLAN_PCIE_SOC_GLOBAL_RESET);
+ val |= QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V;
+ iowrite32(val, bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
+ ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
+
+ msleep(10);
+
+ val &= ~QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V;
+ iowrite32(val, bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
+ ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
+
+ msleep(10);
+
+ timeout = jiffies + msecs_to_jiffies(5000);
+ while (time_before(jiffies, timeout)) {
+ val = ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
+ if (!PCI_POSSIBLE_ERROR(val)) {
+ 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_WLAN_MHICTRL_RESET_MASK, bar + QUALCOMM_WLAN_MHICTRL);
+ ioread32(bar + QUALCOMM_WLAN_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 +4369,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, 0x0308, reset_qualcomm_modem }, /* SDX62/SDX65 modems */
+ { PCI_VENDOR_ID_QCOM, 0x1103, reset_qualcomm_wlan }, /* WCN6855 WLAN */
+ { PCI_VENDOR_ID_QCOM, 0x1107, reset_qualcomm_wlan }, /* WCN7850 WLAN */
{ 0 }
};
--
2.54.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH v12] PCI: Add device-specific reset for Qualcomm devices
2026-06-30 6:58 [PATCH v12] PCI: Add device-specific reset for Qualcomm devices Jose Ignacio Tornos Martinez
@ 2026-06-30 7:09 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-06-30 7:09 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The polling loop for PCIe link recovery lacks a final condition check, making it vulnerable to spurious timeouts if the executing thread is delayed or preempted.
- [Medium] Read-modify-write operation on the SoC global reset register does not handle `0xFFFFFFFF` (PCI_ERROR_RESPONSE), potentially writing an invalid state to the device.
--
commit 6f0f5be433a0e989c17f01ae046c3574dc1fbf77
Author: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
PCI: Add device-specific reset for Qualcomm devices
This commit adds device-specific hardware reset methods via BAR0 for
specific Qualcomm PCIe devices (WCN6855/WCN7850 WLAN cards and SDX62/SDX65
modems). These resets are intended to fix issues during VFIO passthrough
scenarios where the devices lack a working FLR or standard reset mechanism.
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index b09f27f7846fc..6d465f6d43054 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
[ ... ]
> +static int reset_qualcomm_wlan(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_WLAN_PCIE_SOC_GLOBAL_RESET);
> + val |= QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V;
> + iowrite32(val, bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
[Severity: Medium]
If the device is hung or the link is temporarily unresponsive, could the
initial ioread32() return PCI_ERROR_RESPONSE (0xFFFFFFFF)?
If it does, this read-modify-write operation would write 0xFFFFFFFF (and
later 0xFFFFFFFE) back to the device. Could writing these values trigger
unintended resets across the SoC or leave the hardware in an invalid state?
Would it be safer to verify !PCI_POSSIBLE_ERROR(val) before modifying
and writing it back?
> + ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
> +
> + msleep(10);
> +
> + val &= ~QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V;
> + iowrite32(val, bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
> + ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
> +
> + msleep(10);
> +
> + timeout = jiffies + msecs_to_jiffies(5000);
> + while (time_before(jiffies, timeout)) {
> + val = ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
> + if (!PCI_POSSIBLE_ERROR(val)) {
> + link_recovered = true;
> + break;
> + }
> + msleep(20);
> + }
[Severity: Medium]
Is this polling loop vulnerable to false timeouts due to scheduling delays?
If the thread is preempted during msleep(20) and delayed for more than 5
seconds, time_before(jiffies, timeout) will evaluate to false upon waking
up. The loop will exit without a final check of the register, returning
-ETIMEDOUT even if the link successfully recovered during the delay.
Should there be a final condition check after the loop, or perhaps use
read_poll_timeout() instead to handle preemption robustly?
> +
> + 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.
> + */
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260630065815.199693-1-jtornosm@redhat.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-06-30 7:09 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-30 6:58 [PATCH v12] PCI: Add device-specific reset for Qualcomm devices Jose Ignacio Tornos Martinez
2026-06-30 7:09 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox