ad5e497
From 93c501a65e23aba07389ad95b7e31834ae6d4722 Mon Sep 17 00:00:00 2001
88f3771
From: Adrian Hunter <adrian.hunter@intel.com>
88f3771
Date: Wed, 21 Jun 2017 15:08:39 +0300
88f3771
Subject: [PATCH 14/16] mmc: sdhci-acpi: Workaround conflict with PCI wifi on
88f3771
 GPD Win handheld
88f3771
88f3771
GPDwin uses PCI wifi which conflicts with SDIO's use of
88f3771
acpi_device_fix_up_power() on child device nodes. Specifically
88f3771
acpi_device_fix_up_power() causes the wifi module to get turned off.
88f3771
Identifying GPDwin is problematic, but since SDIO is only used for wifi,
88f3771
the presence of the PCI wifi card in the expected slot with an ACPI
88f3771
companion node, is used to indicate that acpi_device_fix_up_power() should
88f3771
be avoided.
88f3771
ad5e497
[labbott@redhat.com: Rebased for Fedora]
88f3771
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
88f3771
Acked-by: Hans de Goede <hdegoede@redhat.com>
88f3771
Tested-by: Hans de Goede <hdegoede@redhat.com>
88f3771
Cc: stable@vger.kernel.org
88f3771
---
88f3771
 drivers/mmc/host/sdhci-acpi.c | 70 +++++++++++++++++++++++++++++++++++++++----
88f3771
 1 file changed, 64 insertions(+), 6 deletions(-)
88f3771
88f3771
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
ad5e497
index cf66a3d..ac678e9 100644
88f3771
--- a/drivers/mmc/host/sdhci-acpi.c
88f3771
+++ b/drivers/mmc/host/sdhci-acpi.c
88f3771
@@ -45,6 +45,7 @@
88f3771
 #include <asm/cpu_device_id.h>
88f3771
 #include <asm/intel-family.h>
88f3771
 #include <asm/iosf_mbi.h>
88f3771
+#include <linux/pci.h>
88f3771
 #endif
88f3771
 
88f3771
 #include "sdhci.h"
88f3771
@@ -134,6 +135,16 @@ static bool sdhci_acpi_byt(void)
88f3771
 	return x86_match_cpu(byt);
88f3771
 }
88f3771
 
88f3771
+static bool sdhci_acpi_cht(void)
88f3771
+{
88f3771
+	static const struct x86_cpu_id cht[] = {
88f3771
+		{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT },
88f3771
+		{}
88f3771
+	};
88f3771
+
88f3771
+	return x86_match_cpu(cht);
88f3771
+}
88f3771
+
88f3771
 #define BYT_IOSF_SCCEP			0x63
88f3771
 #define BYT_IOSF_OCP_NETCTRL0		0x1078
88f3771
 #define BYT_IOSF_OCP_TIMEOUT_BASE	GENMASK(10, 8)
88f3771
@@ -178,6 +189,45 @@ static bool sdhci_acpi_byt_defer(struct device *dev)
88f3771
 	return false;
88f3771
 }
88f3771
 
88f3771
+static bool sdhci_acpi_cht_pci_wifi(unsigned int vendor, unsigned int device,
88f3771
+				    unsigned int slot, unsigned int parent_slot)
88f3771
+{
88f3771
+	struct pci_dev *dev, *parent, *from = NULL;
88f3771
+
88f3771
+	while (1) {
88f3771
+		dev = pci_get_device(vendor, device, from);
88f3771
+		pci_dev_put(from);
88f3771
+		if (!dev)
88f3771
+			break;
88f3771
+		parent = pci_upstream_bridge(dev);
88f3771
+		if (ACPI_COMPANION(&dev->dev) && PCI_SLOT(dev->devfn) == slot &&
88f3771
+		    parent && PCI_SLOT(parent->devfn) == parent_slot &&
88f3771
+		    !pci_upstream_bridge(parent)) {
88f3771
+			pci_dev_put(dev);
88f3771
+			return true;
88f3771
+		}
88f3771
+		from = dev;
88f3771
+	}
88f3771
+
88f3771
+	return false;
88f3771
+}
88f3771
+
88f3771
+/*
88f3771
+ * GPDwin uses PCI wifi which conflicts with SDIO's use of
88f3771
+ * acpi_device_fix_up_power() on child device nodes. Identifying GPDwin is
88f3771
+ * problematic, but since SDIO is only used for wifi, the presence of the PCI
88f3771
+ * wifi card in the expected slot with an ACPI companion node, is used to
88f3771
+ * indicate that acpi_device_fix_up_power() should be avoided.
88f3771
+ */
88f3771
+static inline bool sdhci_acpi_no_fixup_child_power(const char *hid,
88f3771
+						   const char *uid)
88f3771
+{
88f3771
+	return sdhci_acpi_cht() &&
88f3771
+	       !strcmp(hid, "80860F14") &&
88f3771
+	       !strcmp(uid, "2") &&
88f3771
+	       sdhci_acpi_cht_pci_wifi(0x14e4, 0x43ec, 0, 28);
88f3771
+}
88f3771
+
88f3771
 #else
88f3771
 
88f3771
 static inline void sdhci_acpi_byt_setting(struct device *dev)
88f3771
@@ -189,6 +239,12 @@ static inline bool sdhci_acpi_byt_defer(struct device *dev)
88f3771
 	return false;
88f3771
 }
88f3771
 
88f3771
+static inline bool sdhci_acpi_no_fixup_child_power(const char *hid,
88f3771
+						   const char *uid)
88f3771
+{
88f3771
+	return false;
88f3771
+}
88f3771
+
88f3771
 #endif
88f3771
 
88f3771
 static int bxt_get_cd(struct mmc_host *mmc)
ad5e497
@@ -389,18 +445,20 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
88f3771
 	if (acpi_bus_get_device(handle, &device))
88f3771
 		return -ENODEV;
88f3771
 
88f3771
+	hid = acpi_device_hid(device);
88f3771
+	uid = device->pnp.unique_id;
88f3771
+
88f3771
 	/* Power on the SDHCI controller and its children */
88f3771
 	acpi_device_fix_up_power(device);
88f3771
-	list_for_each_entry(child, &device->children, node)
88f3771
-		if (child->status.present && child->status.enabled)
88f3771
-			acpi_device_fix_up_power(child);
88f3771
+	if (!sdhci_acpi_no_fixup_child_power(hid, uid)) {
88f3771
+		list_for_each_entry(child, &device->children, node)
88f3771
+			if (child->status.present && child->status.enabled)
88f3771
+				acpi_device_fix_up_power(child);
88f3771
+	}
88f3771
 
88f3771
 	if (sdhci_acpi_byt_defer(dev))
88f3771
 		return -EPROBE_DEFER;
88f3771
 
88f3771
-	hid = acpi_device_hid(device);
88f3771
-	uid = device->pnp.unique_id;
88f3771
-
88f3771
 	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
88f3771
 	if (!iomem)
88f3771
 		return -ENOMEM;
88f3771
-- 
ad5e497
2.7.5
88f3771