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