1 // SPDX-License-Identifier: GPL-2.0-only 2 // NXP Wireless LAN device driver: PCIE and platform specific quirks 3 4 #include <linux/dmi.h> 5 6 #include "pcie_quirks.h" 7 8 /* quirk table based on DMI matching */ 9 static const struct dmi_system_id mwifiex_quirk_table[] = { 10 { 11 .ident = "Surface Pro 4", 12 .matches = { 13 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 14 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), 15 }, 16 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 17 }, 18 { 19 .ident = "Surface Pro 5", 20 .matches = { 21 /* match for SKU here due to generic product name "Surface Pro" */ 22 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 23 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), 24 }, 25 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 26 }, 27 { 28 .ident = "Surface Pro 5 (LTE)", 29 .matches = { 30 /* match for SKU here due to generic product name "Surface Pro" */ 31 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 32 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), 33 }, 34 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 35 }, 36 { 37 .ident = "Surface Pro 6", 38 .matches = { 39 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 40 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), 41 }, 42 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 43 }, 44 { 45 .ident = "Surface Book 1", 46 .matches = { 47 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 48 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), 49 }, 50 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 51 }, 52 { 53 .ident = "Surface Book 2", 54 .matches = { 55 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 56 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), 57 }, 58 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 59 }, 60 { 61 .ident = "Surface Laptop 1", 62 .matches = { 63 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 64 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), 65 }, 66 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 67 }, 68 { 69 .ident = "Surface Laptop 2", 70 .matches = { 71 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 72 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), 73 }, 74 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 75 }, 76 {} 77 }; 78 79 void mwifiex_initialize_quirks(struct pcie_service_card *card) 80 { 81 struct pci_dev *pdev = card->dev; 82 const struct dmi_system_id *dmi_id; 83 84 dmi_id = dmi_first_match(mwifiex_quirk_table); 85 if (dmi_id) 86 card->quirks = (uintptr_t)dmi_id->driver_data; 87 88 if (!card->quirks) 89 dev_info(&pdev->dev, "no quirks enabled\n"); 90 if (card->quirks & QUIRK_FW_RST_D3COLD) 91 dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); 92 } 93 94 static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) 95 { 96 dev_info(&pdev->dev, "putting into D3cold...\n"); 97 98 pci_save_state(pdev); 99 if (pci_is_enabled(pdev)) 100 pci_disable_device(pdev); 101 pci_set_power_state(pdev, PCI_D3cold); 102 } 103 104 static int mwifiex_pcie_set_power_d0(struct pci_dev *pdev) 105 { 106 int ret; 107 108 dev_info(&pdev->dev, "putting into D0...\n"); 109 110 pci_set_power_state(pdev, PCI_D0); 111 ret = pci_enable_device(pdev); 112 if (ret) { 113 dev_err(&pdev->dev, "pci_enable_device failed\n"); 114 return ret; 115 } 116 pci_restore_state(pdev); 117 118 return 0; 119 } 120 121 int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev) 122 { 123 struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); 124 int ret; 125 126 /* Power-cycle (put into D3cold then D0) */ 127 dev_info(&pdev->dev, "Using reset_d3cold quirk to perform FW reset\n"); 128 129 /* We need to perform power-cycle also for bridge of wifi because 130 * on some devices (e.g. Surface Book 1), the OS for some reasons 131 * can't know the real power state of the bridge. 132 * When tried to power-cycle only wifi, the reset failed with the 133 * following dmesg log: 134 * "Cannot transition to power state D0 for parent in D3hot". 135 */ 136 mwifiex_pcie_set_power_d3cold(pdev); 137 mwifiex_pcie_set_power_d3cold(parent_pdev); 138 139 ret = mwifiex_pcie_set_power_d0(parent_pdev); 140 if (ret) 141 return ret; 142 ret = mwifiex_pcie_set_power_d0(pdev); 143 if (ret) 144 return ret; 145 146 return 0; 147 } 148