linux/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
<<
>>
Prefs
   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 */
  23static 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
  93void 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
 108static 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
 118static 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
 135int 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