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