summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-10-14 15:55:56 -0500
committerBjorn Helgaas <bhelgaas@google.com>2026-10-14 15:55:56 -0500
commitdecd8fdd587075edf7a9cbb59dfeb89cec628dc2 (patch)
treece3f86bdcf2a548908051ce991fe1699d3ff5f13
parenta3e00c8a13e42fd1e0af7baceff3e4e9d0a6e008 (diff)
parentea3f3a39c3fcea8922d3935af91c96e71df543b9 (diff)
downloadlinux-next-decd8fdd587075edf7a9cbb59dfeb89cec628dc2.tar.gz
linux-next-decd8fdd587075edf7a9cbb59dfeb89cec628dc2.zip
Merge branch 'pci/reset'
- Add device-specific reset for Qualcomm WCN6855/WCN7850 WLAN cards and SDX62/SDX65 modems, which don't support FLR and have broken SBR (Jose Ignacio Tornos Martinez) - Avoid FLR and SBR on Intel DG1 0x4905 graphics adapters, where they sometimes fail and lead to system hangs (Janusz Krzysztofik) - Ignore Immediate Readiness on Samsung 990 PRO NVMe, which falsely advertises it and some register restore after FLR was lost (Alexander Gruhlke) - Don't treat ~0 as 'device is ready' in pci_dev_wait() with RRS SV, because a device that doesn't respond at all may result in Completion Timeout or similar error that returns ~0 data (Alexander Gruhlke) * pci/reset: PCI: Don't treat ~0 as ready in pci_dev_wait() with RRS SV PCI: Ignore Immediate Readiness on Samsung 990 PRO NVMe PCI: Avoid FLR and SBR on Intel DG1 0x4905 graphics adapters PCI: Add device-specific reset for Qualcomm WCN6855/WCN7850 WLAN PCI: Add device-specific reset for Qualcomm SDX62/SDX65 modems
-rw-r--r--drivers/pci/pci.c15
-rw-r--r--drivers/pci/quirks.c131
2 files changed, 142 insertions, 4 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 5b126cd342ca..c5ed5da9cdc8 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1246,9 +1246,11 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
* initialize.
*
* If the device is below a Root Port with Configuration RRS
- * Software Visibility enabled, reading the Vendor ID returns a
+ * Software Visibility enabled, reading the Vendor ID may return a
* special data value if the device responded with RRS. Read the
- * Vendor ID until we get non-RRS status.
+ * Vendor ID until we get non-RRS status and something other than
+ * ~0. The Vendor ID of VFs reads ~0, so check their Command
+ * register instead.
*
* If there's no Root Port or Configuration RRS Software Visibility
* is not enabled, the device may still respond with RRS, but
@@ -1268,8 +1270,13 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
if (root && root->config_rrs_sv) {
pci_read_config_dword(dev, PCI_VENDOR_ID, &id);
- if (!pci_bus_rrs_vendor_id(id))
- break;
+ if (!pci_bus_rrs_vendor_id(id)) {
+ if (PCI_POSSIBLE_ERROR(id) && dev->is_virtfn)
+ pci_read_config_dword(dev, PCI_COMMAND,
+ &id);
+ if (!PCI_POSSIBLE_ERROR(id))
+ break;
+ }
} else {
pci_read_config_dword(dev, PCI_COMMAND, &id);
if (!PCI_POSSIBLE_ERROR(id))
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index a3c9250182a6..73c5f501a4e0 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -22,6 +22,7 @@
#include <linux/isa-dma.h> /* isa_dma_bridge_buggy */
#include <linux/init.h>
#include <linux/iommu.h>
+#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
@@ -3829,6 +3830,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TI, 0xb005, quirk_no_bus_reset);
*/
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1164, quirk_no_bus_reset);
+/* Intel DG1 0x4905 may not recover from bus reset */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4905, quirk_no_bus_reset);
+
static void quirk_no_pm_reset(struct pci_dev *dev)
{
/*
@@ -4248,6 +4252,117 @@ reset_complete:
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)
+{
+ void __iomem *bar;
+ u32 val;
+ u16 cmd;
+ int ret;
+
+ 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);
+ if (PCI_POSSIBLE_ERROR(val)) {
+ ret = -ENODEV;
+ goto out_restore;
+ }
+ 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);
+
+ ret = read_poll_timeout(ioread32, val, !PCI_POSSIBLE_ERROR(val),
+ 20 * USEC_PER_MSEC, 5 * USEC_PER_SEC, false,
+ bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
+ if (ret) {
+ 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 ret;
+}
+
+#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 },
@@ -4263,6 +4378,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 }
};
@@ -5615,6 +5733,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap);
* AMD Neural Processing Unit 0x1502 0x17f0
* Intel 82579LM Gigabit Ethernet Controller 0x1502
* Intel 82579V Gigabit Ethernet Controller 0x1503
+ * Intel DG1 Discrete Graphics Adapter 0x4905
* Mediatek MT7922 802.11ax PCI Express Wireless Network Adapter
* Mediatek MT7925 802.11be PCI Express Wireless Network Adapter
*/
@@ -5630,6 +5749,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1502, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x17f0, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x4905, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x0616, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x7925, quirk_no_flr);
@@ -5641,6 +5761,17 @@ static void quirk_no_flr_snet(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SOLIDRUN, 0x1000, quirk_no_flr_snet);
+/* Samsung 990 PRO advertises Immediate Readiness, but isn't ready after FLR */
+static void quirk_no_imm_ready(struct pci_dev *dev)
+{
+ if (!dev->imm_ready)
+ return;
+
+ pci_info(dev, "Ignoring incorrectly advertised Immediate Readiness support\n");
+ dev->imm_ready = 0;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SAMSUNG, 0xa80c, quirk_no_imm_ready);
+
static void quirk_no_ext_tags(struct pci_dev *pdev)
{
struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);