162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// NXP Wireless LAN device driver: PCIE and platform specific quirks 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/dmi.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "pcie_quirks.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* quirk table based on DMI matching */ 962306a36Sopenharmony_cistatic const struct dmi_system_id mwifiex_quirk_table[] = { 1062306a36Sopenharmony_ci { 1162306a36Sopenharmony_ci .ident = "Surface Pro 4", 1262306a36Sopenharmony_ci .matches = { 1362306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 1462306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), 1562306a36Sopenharmony_ci }, 1662306a36Sopenharmony_ci .driver_data = (void *)QUIRK_FW_RST_D3COLD, 1762306a36Sopenharmony_ci }, 1862306a36Sopenharmony_ci { 1962306a36Sopenharmony_ci .ident = "Surface Pro 5", 2062306a36Sopenharmony_ci .matches = { 2162306a36Sopenharmony_ci /* match for SKU here due to generic product name "Surface Pro" */ 2262306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 2362306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), 2462306a36Sopenharmony_ci }, 2562306a36Sopenharmony_ci .driver_data = (void *)QUIRK_FW_RST_D3COLD, 2662306a36Sopenharmony_ci }, 2762306a36Sopenharmony_ci { 2862306a36Sopenharmony_ci .ident = "Surface Pro 5 (LTE)", 2962306a36Sopenharmony_ci .matches = { 3062306a36Sopenharmony_ci /* match for SKU here due to generic product name "Surface Pro" */ 3162306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 3262306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), 3362306a36Sopenharmony_ci }, 3462306a36Sopenharmony_ci .driver_data = (void *)QUIRK_FW_RST_D3COLD, 3562306a36Sopenharmony_ci }, 3662306a36Sopenharmony_ci { 3762306a36Sopenharmony_ci .ident = "Surface Pro 6", 3862306a36Sopenharmony_ci .matches = { 3962306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 4062306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), 4162306a36Sopenharmony_ci }, 4262306a36Sopenharmony_ci .driver_data = (void *)QUIRK_FW_RST_D3COLD, 4362306a36Sopenharmony_ci }, 4462306a36Sopenharmony_ci { 4562306a36Sopenharmony_ci .ident = "Surface Book 1", 4662306a36Sopenharmony_ci .matches = { 4762306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 4862306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), 4962306a36Sopenharmony_ci }, 5062306a36Sopenharmony_ci .driver_data = (void *)QUIRK_FW_RST_D3COLD, 5162306a36Sopenharmony_ci }, 5262306a36Sopenharmony_ci { 5362306a36Sopenharmony_ci .ident = "Surface Book 2", 5462306a36Sopenharmony_ci .matches = { 5562306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 5662306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), 5762306a36Sopenharmony_ci }, 5862306a36Sopenharmony_ci .driver_data = (void *)QUIRK_FW_RST_D3COLD, 5962306a36Sopenharmony_ci }, 6062306a36Sopenharmony_ci { 6162306a36Sopenharmony_ci .ident = "Surface Laptop 1", 6262306a36Sopenharmony_ci .matches = { 6362306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 6462306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), 6562306a36Sopenharmony_ci }, 6662306a36Sopenharmony_ci .driver_data = (void *)QUIRK_FW_RST_D3COLD, 6762306a36Sopenharmony_ci }, 6862306a36Sopenharmony_ci { 6962306a36Sopenharmony_ci .ident = "Surface Laptop 2", 7062306a36Sopenharmony_ci .matches = { 7162306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 7262306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), 7362306a36Sopenharmony_ci }, 7462306a36Sopenharmony_ci .driver_data = (void *)QUIRK_FW_RST_D3COLD, 7562306a36Sopenharmony_ci }, 7662306a36Sopenharmony_ci {} 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid mwifiex_initialize_quirks(struct pcie_service_card *card) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct pci_dev *pdev = card->dev; 8262306a36Sopenharmony_ci const struct dmi_system_id *dmi_id; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci dmi_id = dmi_first_match(mwifiex_quirk_table); 8562306a36Sopenharmony_ci if (dmi_id) 8662306a36Sopenharmony_ci card->quirks = (uintptr_t)dmi_id->driver_data; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (!card->quirks) 8962306a36Sopenharmony_ci dev_info(&pdev->dev, "no quirks enabled\n"); 9062306a36Sopenharmony_ci if (card->quirks & QUIRK_FW_RST_D3COLD) 9162306a36Sopenharmony_ci dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci dev_info(&pdev->dev, "putting into D3cold...\n"); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci pci_save_state(pdev); 9962306a36Sopenharmony_ci if (pci_is_enabled(pdev)) 10062306a36Sopenharmony_ci pci_disable_device(pdev); 10162306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D3cold); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int mwifiex_pcie_set_power_d0(struct pci_dev *pdev) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci int ret; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci dev_info(&pdev->dev, "putting into D0...\n"); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 11162306a36Sopenharmony_ci ret = pci_enable_device(pdev); 11262306a36Sopenharmony_ci if (ret) { 11362306a36Sopenharmony_ci dev_err(&pdev->dev, "pci_enable_device failed\n"); 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci pci_restore_state(pdev); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ciint mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); 12462306a36Sopenharmony_ci int ret; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Power-cycle (put into D3cold then D0) */ 12762306a36Sopenharmony_ci dev_info(&pdev->dev, "Using reset_d3cold quirk to perform FW reset\n"); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* We need to perform power-cycle also for bridge of wifi because 13062306a36Sopenharmony_ci * on some devices (e.g. Surface Book 1), the OS for some reasons 13162306a36Sopenharmony_ci * can't know the real power state of the bridge. 13262306a36Sopenharmony_ci * When tried to power-cycle only wifi, the reset failed with the 13362306a36Sopenharmony_ci * following dmesg log: 13462306a36Sopenharmony_ci * "Cannot transition to power state D0 for parent in D3hot". 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci mwifiex_pcie_set_power_d3cold(pdev); 13762306a36Sopenharmony_ci mwifiex_pcie_set_power_d3cold(parent_pdev); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = mwifiex_pcie_set_power_d0(parent_pdev); 14062306a36Sopenharmony_ci if (ret) 14162306a36Sopenharmony_ci return ret; 14262306a36Sopenharmony_ci ret = mwifiex_pcie_set_power_d0(pdev); 14362306a36Sopenharmony_ci if (ret) 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 148