diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2026-10-14 15:55:56 -0500 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-10-14 15:55:56 -0500 |
| commit | decd8fdd587075edf7a9cbb59dfeb89cec628dc2 (patch) | |
| tree | ce3f86bdcf2a548908051ce991fe1699d3ff5f13 | |
| parent | a3e00c8a13e42fd1e0af7baceff3e4e9d0a6e008 (diff) | |
| parent | ea3f3a39c3fcea8922d3935af91c96e71df543b9 (diff) | |
| download | linux-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.c | 15 | ||||
| -rw-r--r-- | drivers/pci/quirks.c | 131 |
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); |
