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