Skip to content

Commit 84d49b3

Browse files
jwrdegoedestorulf
authored andcommitted
mmc: sdhci-acpi: Switch signal voltage back to 3.3V on suspend on external microSD on Lenovo Miix 320
Based on a sample of 7 DSDTs from Cherry Trail devices using an AXP288 PMIC depending on the design one of 2 possible LDOs on the PMIC is used for the MMC signalling voltage, either DLDO3 or GPIO1LDO (GPIO1 pin in low noise LDO mode). The Lenovo Miix 320-10ICR uses GPIO1LDO in the SHC1 ACPI device's DSM methods to set 3.3 or 1.8 signalling voltage and this appears to work as advertised, so presumably the device is actually using GPIO1LDO for the external microSD signalling voltage. But this device has a bug in the _PS0 method of the SHC1 ACPI device, the DSM remembers the last set signalling voltage and the _PS0 restores this after a (runtime) suspend-resume cycle, but it "restores" the voltage on DLDO3 instead of setting it on GPIO1LDO as the DSM method does. DLDO3 is used for the LCD and setting it to 1.8V causes the LCD to go black. This commit works around this issue by calling the Intel DSM to reset the signal voltage to 3.3V after the host has been runtime suspended. This will make the _PS0 method reprogram the DLDO3 voltage to 3.3V, which leaves it at its original setting fixing the LCD going black. This commit adds and uses a DMI quirk mechanism to only trigger this workaround on the Lenovo Miix 320 while leaving the behavior of the driver unchanged on other devices. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=111294 BugLink: https://gitlab.freedesktop.org/drm/intel/issues/355 Reported-by: russianneuromancer <[email protected]> Signed-off-by: Hans de Goede <[email protected]> Acked-by: Adrian Hunter <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ulf Hansson <[email protected]>
1 parent fb33c65 commit 84d49b3

File tree

1 file changed

+66
-2
lines changed

1 file changed

+66
-2
lines changed

drivers/mmc/host/sdhci-acpi.c

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/pm.h>
2424
#include <linux/pm_runtime.h>
2525
#include <linux/delay.h>
26+
#include <linux/dmi.h>
2627

2728
#include <linux/mmc/host.h>
2829
#include <linux/mmc/pm.h>
@@ -72,9 +73,15 @@ struct sdhci_acpi_host {
7273
const struct sdhci_acpi_slot *slot;
7374
struct platform_device *pdev;
7475
bool use_runtime_pm;
76+
bool is_intel;
77+
bool reset_signal_volt_on_suspend;
7578
unsigned long private[0] ____cacheline_aligned;
7679
};
7780

81+
enum {
82+
DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP = BIT(0),
83+
};
84+
7885
static inline void *sdhci_acpi_priv(struct sdhci_acpi_host *c)
7986
{
8087
return (void *)c->private;
@@ -391,6 +398,8 @@ static int intel_probe_slot(struct platform_device *pdev, struct acpi_device *ad
391398
host->mmc_host_ops.start_signal_voltage_switch =
392399
intel_start_signal_voltage_switch;
393400

401+
c->is_intel = true;
402+
394403
return 0;
395404
}
396405

@@ -647,6 +656,24 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
647656
};
648657
MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
649658

659+
static const struct dmi_system_id sdhci_acpi_quirks[] = {
660+
{
661+
/*
662+
* The Lenovo Miix 320-10ICR has a bug in the _PS0 method of
663+
* the SHC1 ACPI device, this bug causes it to reprogram the
664+
* wrong LDO (DLDO3) to 1.8V if 1.8V modes are used and the
665+
* card is (runtime) suspended + resumed. DLDO3 is used for
666+
* the LCD and setting it to 1.8V causes the LCD to go black.
667+
*/
668+
.matches = {
669+
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
670+
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
671+
},
672+
.driver_data = (void *)DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP,
673+
},
674+
{} /* Terminating entry */
675+
};
676+
650677
static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(struct acpi_device *adev)
651678
{
652679
const struct sdhci_acpi_uid_slot *u;
@@ -663,17 +690,23 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
663690
struct device *dev = &pdev->dev;
664691
const struct sdhci_acpi_slot *slot;
665692
struct acpi_device *device, *child;
693+
const struct dmi_system_id *id;
666694
struct sdhci_acpi_host *c;
667695
struct sdhci_host *host;
668696
struct resource *iomem;
669697
resource_size_t len;
670698
size_t priv_size;
699+
int quirks = 0;
671700
int err;
672701

673702
device = ACPI_COMPANION(dev);
674703
if (!device)
675704
return -ENODEV;
676705

706+
id = dmi_first_match(sdhci_acpi_quirks);
707+
if (id)
708+
quirks = (long)id->driver_data;
709+
677710
slot = sdhci_acpi_get_slot(device);
678711

679712
/* Power on the SDHCI controller and its children */
@@ -759,6 +792,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
759792
dev_warn(dev, "failed to setup card detect gpio\n");
760793
c->use_runtime_pm = false;
761794
}
795+
796+
if (quirks & DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP)
797+
c->reset_signal_volt_on_suspend = true;
762798
}
763799

764800
err = sdhci_setup_host(host);
@@ -823,17 +859,39 @@ static int sdhci_acpi_remove(struct platform_device *pdev)
823859
return 0;
824860
}
825861

862+
static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed(
863+
struct device *dev)
864+
{
865+
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
866+
struct sdhci_host *host = c->host;
867+
868+
if (c->is_intel && c->reset_signal_volt_on_suspend &&
869+
host->mmc->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_330) {
870+
struct intel_host *intel_host = sdhci_acpi_priv(c);
871+
unsigned int fn = INTEL_DSM_V33_SWITCH;
872+
u32 result = 0;
873+
874+
intel_dsm(intel_host, dev, fn, &result);
875+
}
876+
}
877+
826878
#ifdef CONFIG_PM_SLEEP
827879

828880
static int sdhci_acpi_suspend(struct device *dev)
829881
{
830882
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
831883
struct sdhci_host *host = c->host;
884+
int ret;
832885

833886
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
834887
mmc_retune_needed(host->mmc);
835888

836-
return sdhci_suspend_host(host);
889+
ret = sdhci_suspend_host(host);
890+
if (ret)
891+
return ret;
892+
893+
sdhci_acpi_reset_signal_voltage_if_needed(dev);
894+
return 0;
837895
}
838896

839897
static int sdhci_acpi_resume(struct device *dev)
@@ -853,11 +911,17 @@ static int sdhci_acpi_runtime_suspend(struct device *dev)
853911
{
854912
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
855913
struct sdhci_host *host = c->host;
914+
int ret;
856915

857916
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
858917
mmc_retune_needed(host->mmc);
859918

860-
return sdhci_runtime_suspend_host(host);
919+
ret = sdhci_runtime_suspend_host(host);
920+
if (ret)
921+
return ret;
922+
923+
sdhci_acpi_reset_signal_voltage_if_needed(dev);
924+
return 0;
861925
}
862926

863927
static int sdhci_acpi_runtime_resume(struct device *dev)

0 commit comments

Comments
 (0)