1 /* 2 * NXP Wireless LAN device driver: PCIE and platform specific quirks 3 * 4 * This software file (the "File") is distributed by NXP 5 * under the terms of the GNU General Public License Version 2, June 1991 6 * (the "License"). You may use, redistribute and/or modify this File in 7 * accordance with the terms and conditions of the License, a copy of which 8 * is available by writing to the Free Software Foundation, Inc., 9 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 10 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 11 * 12 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 13 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 14 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 15 * this warranty disclaimer. 16 */ 17 18 #include <linux/dmi.h> 19 20 #include "pcie_quirks.h" 21 22 /* quirk table based on DMI matching */ 23 static const struct dmi_system_id mwifiex_quirk_table[] = { 24 { 25 .ident = "Surface Pro 4", 26 .matches = { 27 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 28 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), 29 }, 30 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 31 }, 32 { 33 .ident = "Surface Pro 5", 34 .matches = { 35 /* match for SKU here due to generic product name "Surface Pro" */ 36 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 37 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), 38 }, 39 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 40 }, 41 { 42 .ident = "Surface Pro 5 (LTE)", 43 .matches = { 44 /* match for SKU here due to generic product name "Surface Pro" */ 45 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 46 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), 47 }, 48 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 49 }, 50 { 51 .ident = "Surface Pro 6", 52 .matches = { 53 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 54 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), 55 }, 56 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 57 }, 58 { 59 .ident = "Surface Book 1", 60 .matches = { 61 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 62 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), 63 }, 64 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 65 }, 66 { 67 .ident = "Surface Book 2", 68 .matches = { 69 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 70 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), 71 }, 72 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 73 }, 74 { 75 .ident = "Surface Laptop 1", 76 .matches = { 77 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 78 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), 79 }, 80 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 81 }, 82 { 83 .ident = "Surface Laptop 2", 84 .matches = { 85 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 86 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), 87 }, 88 .driver_data = (void *)QUIRK_FW_RST_D3COLD, 89 }, 90 {} 91 }; 92 93 void mwifiex_initialize_quirks(struct pcie_service_card *card) 94 { 95 struct pci_dev *pdev = card->dev; 96 const struct dmi_system_id *dmi_id; 97 98 dmi_id = dmi_first_match(mwifiex_quirk_table); 99 if (dmi_id) 100 card->quirks = (uintptr_t)dmi_id->driver_data; 101 102 if (!card->quirks) 103 dev_info(&pdev->dev, "no quirks enabled\n"); 104 if (card->quirks & QUIRK_FW_RST_D3COLD) 105 dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); 106 } 107 108 static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) 109 { 110 dev_info(&pdev->dev, "putting into D3cold...\n"); 111 112 pci_save_state(pdev); 113 if (pci_is_enabled(pdev)) 114 pci_disable_device(pdev); 115 pci_set_power_state(pdev, PCI_D3cold); 116 } 117 118 static int mwifiex_pcie_set_power_d0(struct pci_dev *pdev) 119 { 120 int ret; 121 122 dev_info(&pdev->dev, "putting into D0...\n"); 123 124 pci_set_power_state(pdev, PCI_D0); 125 ret = pci_enable_device(pdev); 126 if (ret) { 127 dev_err(&pdev->dev, "pci_enable_device failed\n"); 128 return ret; 129 } 130 pci_restore_state(pdev); 131 132 return 0; 133 } 134 135 int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev) 136 { 137 struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); 138 int ret; 139 140 /* Power-cycle (put into D3cold then D0) */ 141 dev_info(&pdev->dev, "Using reset_d3cold quirk to perform FW reset\n"); 142 143 /* We need to perform power-cycle also for bridge of wifi because 144 * on some devices (e.g. Surface Book 1), the OS for some reasons 145 * can't know the real power state of the bridge. 146 * When tried to power-cycle only wifi, the reset failed with the 147 * following dmesg log: 148 * "Cannot transition to power state D0 for parent in D3hot". 149 */ 150 mwifiex_pcie_set_power_d3cold(pdev); 151 mwifiex_pcie_set_power_d3cold(parent_pdev); 152 153 ret = mwifiex_pcie_set_power_d0(parent_pdev); 154 if (ret) 155 return ret; 156 ret = mwifiex_pcie_set_power_d0(pdev); 157 if (ret) 158 return ret; 159 160 return 0; 161 } 162