]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob
8ae098aa16e17fbd551757a6cd20ee73581709d7
[thirdparty/kernel/stable-queue.git] /
1 From 84d49b3d08a1d33690cc159036f381c31c27c17b Mon Sep 17 00:00:00 2001
2 From: Hans de Goede <hdegoede@redhat.com>
3 Date: Mon, 16 Mar 2020 19:47:52 +0100
4 Subject: mmc: sdhci-acpi: Switch signal voltage back to 3.3V on suspend on external microSD on Lenovo Miix 320
5
6 From: Hans de Goede <hdegoede@redhat.com>
7
8 commit 84d49b3d08a1d33690cc159036f381c31c27c17b upstream.
9
10 Based on a sample of 7 DSDTs from Cherry Trail devices using an AXP288
11 PMIC depending on the design one of 2 possible LDOs on the PMIC is used
12 for the MMC signalling voltage, either DLDO3 or GPIO1LDO (GPIO1 pin in
13 low noise LDO mode).
14
15 The Lenovo Miix 320-10ICR uses GPIO1LDO in the SHC1 ACPI device's DSM
16 methods to set 3.3 or 1.8 signalling voltage and this appears to work
17 as advertised, so presumably the device is actually using GPIO1LDO for
18 the external microSD signalling voltage.
19
20 But this device has a bug in the _PS0 method of the SHC1 ACPI device,
21 the DSM remembers the last set signalling voltage and the _PS0 restores
22 this after a (runtime) suspend-resume cycle, but it "restores" the voltage
23 on DLDO3 instead of setting it on GPIO1LDO as the DSM method does. DLDO3
24 is used for the LCD and setting it to 1.8V causes the LCD to go black.
25
26 This commit works around this issue by calling the Intel DSM to reset the
27 signal voltage to 3.3V after the host has been runtime suspended.
28 This will make the _PS0 method reprogram the DLDO3 voltage to 3.3V, which
29 leaves it at its original setting fixing the LCD going black.
30
31 This commit adds and uses a DMI quirk mechanism to only trigger this
32 workaround on the Lenovo Miix 320 while leaving the behavior of the
33 driver unchanged on other devices.
34
35 BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=111294
36 BugLink: https://gitlab.freedesktop.org/drm/intel/issues/355
37 Reported-by: russianneuromancer <russianneuromancer@ya.ru>
38 Signed-off-by: Hans de Goede <hdegoede@redhat.com>
39 Acked-by: Adrian Hunter <adrian.hunter@intel.com>
40 Cc: stable@vger.kernel.org
41 Link: https://lore.kernel.org/r/20200316184753.393458-1-hdegoede@redhat.com
42 Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
43 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
44
45 ---
46 drivers/mmc/host/sdhci-acpi.c | 68 ++++++++++++++++++++++++++++++++++++++++--
47 1 file changed, 66 insertions(+), 2 deletions(-)
48
49 --- a/drivers/mmc/host/sdhci-acpi.c
50 +++ b/drivers/mmc/host/sdhci-acpi.c
51 @@ -23,6 +23,7 @@
52 #include <linux/pm.h>
53 #include <linux/pm_runtime.h>
54 #include <linux/delay.h>
55 +#include <linux/dmi.h>
56
57 #include <linux/mmc/host.h>
58 #include <linux/mmc/pm.h>
59 @@ -72,9 +73,15 @@ struct sdhci_acpi_host {
60 const struct sdhci_acpi_slot *slot;
61 struct platform_device *pdev;
62 bool use_runtime_pm;
63 + bool is_intel;
64 + bool reset_signal_volt_on_suspend;
65 unsigned long private[0] ____cacheline_aligned;
66 };
67
68 +enum {
69 + DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP = BIT(0),
70 +};
71 +
72 static inline void *sdhci_acpi_priv(struct sdhci_acpi_host *c)
73 {
74 return (void *)c->private;
75 @@ -391,6 +398,8 @@ static int intel_probe_slot(struct platf
76 host->mmc_host_ops.start_signal_voltage_switch =
77 intel_start_signal_voltage_switch;
78
79 + c->is_intel = true;
80 +
81 return 0;
82 }
83
84 @@ -647,6 +656,24 @@ static const struct acpi_device_id sdhci
85 };
86 MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
87
88 +static const struct dmi_system_id sdhci_acpi_quirks[] = {
89 + {
90 + /*
91 + * The Lenovo Miix 320-10ICR has a bug in the _PS0 method of
92 + * the SHC1 ACPI device, this bug causes it to reprogram the
93 + * wrong LDO (DLDO3) to 1.8V if 1.8V modes are used and the
94 + * card is (runtime) suspended + resumed. DLDO3 is used for
95 + * the LCD and setting it to 1.8V causes the LCD to go black.
96 + */
97 + .matches = {
98 + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
99 + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
100 + },
101 + .driver_data = (void *)DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP,
102 + },
103 + {} /* Terminating entry */
104 +};
105 +
106 static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(struct acpi_device *adev)
107 {
108 const struct sdhci_acpi_uid_slot *u;
109 @@ -663,17 +690,23 @@ static int sdhci_acpi_probe(struct platf
110 struct device *dev = &pdev->dev;
111 const struct sdhci_acpi_slot *slot;
112 struct acpi_device *device, *child;
113 + const struct dmi_system_id *id;
114 struct sdhci_acpi_host *c;
115 struct sdhci_host *host;
116 struct resource *iomem;
117 resource_size_t len;
118 size_t priv_size;
119 + int quirks = 0;
120 int err;
121
122 device = ACPI_COMPANION(dev);
123 if (!device)
124 return -ENODEV;
125
126 + id = dmi_first_match(sdhci_acpi_quirks);
127 + if (id)
128 + quirks = (long)id->driver_data;
129 +
130 slot = sdhci_acpi_get_slot(device);
131
132 /* Power on the SDHCI controller and its children */
133 @@ -759,6 +792,9 @@ static int sdhci_acpi_probe(struct platf
134 dev_warn(dev, "failed to setup card detect gpio\n");
135 c->use_runtime_pm = false;
136 }
137 +
138 + if (quirks & DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP)
139 + c->reset_signal_volt_on_suspend = true;
140 }
141
142 err = sdhci_setup_host(host);
143 @@ -823,17 +859,39 @@ static int sdhci_acpi_remove(struct plat
144 return 0;
145 }
146
147 +static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed(
148 + struct device *dev)
149 +{
150 + struct sdhci_acpi_host *c = dev_get_drvdata(dev);
151 + struct sdhci_host *host = c->host;
152 +
153 + if (c->is_intel && c->reset_signal_volt_on_suspend &&
154 + host->mmc->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_330) {
155 + struct intel_host *intel_host = sdhci_acpi_priv(c);
156 + unsigned int fn = INTEL_DSM_V33_SWITCH;
157 + u32 result = 0;
158 +
159 + intel_dsm(intel_host, dev, fn, &result);
160 + }
161 +}
162 +
163 #ifdef CONFIG_PM_SLEEP
164
165 static int sdhci_acpi_suspend(struct device *dev)
166 {
167 struct sdhci_acpi_host *c = dev_get_drvdata(dev);
168 struct sdhci_host *host = c->host;
169 + int ret;
170
171 if (host->tuning_mode != SDHCI_TUNING_MODE_3)
172 mmc_retune_needed(host->mmc);
173
174 - return sdhci_suspend_host(host);
175 + ret = sdhci_suspend_host(host);
176 + if (ret)
177 + return ret;
178 +
179 + sdhci_acpi_reset_signal_voltage_if_needed(dev);
180 + return 0;
181 }
182
183 static int sdhci_acpi_resume(struct device *dev)
184 @@ -853,11 +911,17 @@ static int sdhci_acpi_runtime_suspend(st
185 {
186 struct sdhci_acpi_host *c = dev_get_drvdata(dev);
187 struct sdhci_host *host = c->host;
188 + int ret;
189
190 if (host->tuning_mode != SDHCI_TUNING_MODE_3)
191 mmc_retune_needed(host->mmc);
192
193 - return sdhci_runtime_suspend_host(host);
194 + ret = sdhci_runtime_suspend_host(host);
195 + if (ret)
196 + return ret;
197 +
198 + sdhci_acpi_reset_signal_voltage_if_needed(dev);
199 + return 0;
200 }
201
202 static int sdhci_acpi_runtime_resume(struct device *dev)