summaryrefslogtreecommitdiffstats
path: root/0014-mmc-sdhci-acpi-Workaround-conflict-with-PCI-wifi-on-.patch
diff options
context:
space:
mode:
Diffstat (limited to '0014-mmc-sdhci-acpi-Workaround-conflict-with-PCI-wifi-on-.patch')
-rw-r--r--0014-mmc-sdhci-acpi-Workaround-conflict-with-PCI-wifi-on-.patch143
1 files changed, 143 insertions, 0 deletions
diff --git a/0014-mmc-sdhci-acpi-Workaround-conflict-with-PCI-wifi-on-.patch b/0014-mmc-sdhci-acpi-Workaround-conflict-with-PCI-wifi-on-.patch
new file mode 100644
index 000000000..b5c717c64
--- /dev/null
+++ b/0014-mmc-sdhci-acpi-Workaround-conflict-with-PCI-wifi-on-.patch
@@ -0,0 +1,143 @@
+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
+