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