mmc: sdhci-pci-gli: fix LPM negotiation so x86/S0ix SoCs can suspend
authorSven van Ashbrook <svenva@chromium.org>
Thu, 31 Aug 2023 16:00:56 +0000 (16:00 +0000)
committerUlf Hansson <ulf.hansson@linaro.org>
Tue, 26 Sep 2023 13:08:05 +0000 (15:08 +0200)
To improve the r/w performance of GL9763E, the current driver inhibits LPM
negotiation while the device is active.

This prevents a large number of SoCs from suspending, notably x86 systems
which commonly use S0ix as the suspend mechanism - for example, Intel
Alder Lake and Raptor Lake processors.

Failure description:
1. Userspace initiates s2idle suspend (e.g. via writing to
   /sys/power/state)
2. This switches the runtime_pm device state to active, which disables
   LPM negotiation, then calls the "regular" suspend callback
3. With LPM negotiation disabled, the bus cannot enter low-power state
4. On a large number of SoCs, if the bus not in a low-power state, S0ix
   cannot be entered, which in turn prevents the SoC from entering
   suspend.

Fix by re-enabling LPM negotiation in the device's suspend callback.

Suggested-by: Stanislaw Kardach <skardach@google.com>
Fixes: f9e5b33934ce ("mmc: host: Improve I/O read/write performance for GL9763E")
Cc: stable@vger.kernel.org
Signed-off-by: Sven van Ashbrook <svenva@chromium.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Link: https://lore.kernel.org/r/20230831160055.v3.1.I7ed1ca09797be2dd76ca914c57d88b32d24dac88@changeid
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci-pci-gli.c

index ae8c307..109d4b0 100644 (file)
@@ -1144,42 +1144,6 @@ static u32 sdhci_gl9750_readl(struct sdhci_host *host, int reg)
        return value;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip)
-{
-       struct sdhci_pci_slot *slot = chip->slots[0];
-
-       pci_free_irq_vectors(slot->chip->pdev);
-       gli_pcie_enable_msi(slot);
-
-       return sdhci_pci_resume_host(chip);
-}
-
-static int sdhci_cqhci_gli_resume(struct sdhci_pci_chip *chip)
-{
-       struct sdhci_pci_slot *slot = chip->slots[0];
-       int ret;
-
-       ret = sdhci_pci_gli_resume(chip);
-       if (ret)
-               return ret;
-
-       return cqhci_resume(slot->host->mmc);
-}
-
-static int sdhci_cqhci_gli_suspend(struct sdhci_pci_chip *chip)
-{
-       struct sdhci_pci_slot *slot = chip->slots[0];
-       int ret;
-
-       ret = cqhci_suspend(slot->host->mmc);
-       if (ret)
-               return ret;
-
-       return sdhci_suspend_host(slot->host);
-}
-#endif
-
 static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
                                          struct mmc_ios *ios)
 {
@@ -1420,6 +1384,70 @@ static int gl9763e_runtime_resume(struct sdhci_pci_chip *chip)
 }
 #endif
 
+#ifdef CONFIG_PM_SLEEP
+static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip)
+{
+       struct sdhci_pci_slot *slot = chip->slots[0];
+
+       pci_free_irq_vectors(slot->chip->pdev);
+       gli_pcie_enable_msi(slot);
+
+       return sdhci_pci_resume_host(chip);
+}
+
+static int gl9763e_resume(struct sdhci_pci_chip *chip)
+{
+       struct sdhci_pci_slot *slot = chip->slots[0];
+       int ret;
+
+       ret = sdhci_pci_gli_resume(chip);
+       if (ret)
+               return ret;
+
+       ret = cqhci_resume(slot->host->mmc);
+       if (ret)
+               return ret;
+
+       /*
+        * Disable LPM negotiation to bring device back in sync
+        * with its runtime_pm state.
+        */
+       gl9763e_set_low_power_negotiation(slot, false);
+
+       return 0;
+}
+
+static int gl9763e_suspend(struct sdhci_pci_chip *chip)
+{
+       struct sdhci_pci_slot *slot = chip->slots[0];
+       int ret;
+
+       /*
+        * Certain SoCs can suspend only with the bus in low-
+        * power state, notably x86 SoCs when using S0ix.
+        * Re-enable LPM negotiation to allow entering L1 state
+        * and entering system suspend.
+        */
+       gl9763e_set_low_power_negotiation(slot, true);
+
+       ret = cqhci_suspend(slot->host->mmc);
+       if (ret)
+               goto err_suspend;
+
+       ret = sdhci_suspend_host(slot->host);
+       if (ret)
+               goto err_suspend_host;
+
+       return 0;
+
+err_suspend_host:
+       cqhci_resume(slot->host->mmc);
+err_suspend:
+       gl9763e_set_low_power_negotiation(slot, false);
+       return ret;
+}
+#endif
+
 static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
 {
        struct pci_dev *pdev = slot->chip->pdev;
@@ -1527,8 +1555,8 @@ const struct sdhci_pci_fixes sdhci_gl9763e = {
        .probe_slot     = gli_probe_slot_gl9763e,
        .ops            = &sdhci_gl9763e_ops,
 #ifdef CONFIG_PM_SLEEP
-       .resume         = sdhci_cqhci_gli_resume,
-       .suspend        = sdhci_cqhci_gli_suspend,
+       .resume         = gl9763e_resume,
+       .suspend        = gl9763e_suspend,
 #endif
 #ifdef CONFIG_PM
        .runtime_suspend = gl9763e_runtime_suspend,