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