]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
scsi: ufs: ufs-pci: Fix S0ix/S3 for Intel controllers
authorAdrian Hunter <adrian.hunter@intel.com>
Fri, 24 Oct 2025 08:59:15 +0000 (11:59 +0300)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 30 Oct 2025 03:20:19 +0000 (23:20 -0400)
Intel platforms with UFS, can support Suspend-to-Idle (S0ix) and
Suspend-to-RAM (S3).  For S0ix the link state should be HIBERNATE.  For
S3, state is lost, so the link state must be OFF.  Driver policy,
expressed by spm_lvl, can be 3 (link HIBERNATE, device SLEEP) for S0ix
but must be changed to 5 (link OFF, device POWEROFF) for S3.

Fix support for S0ix/S3 by switching spm_lvl as needed.  During suspend
->prepare(), if the suspend target state is not Suspend-to-Idle, ensure
the spm_lvl is at least 5 to ensure that resume will be possible from
deep sleep states.  During suspend ->complete(), restore the spm_lvl to
its original value that is suitable for S0ix.

This fix is first needed in Intel Alder Lake based controllers.

Fixes: 7dc9fb47bc9a ("scsi: ufs: ufs-pci: Add support for Intel ADL")
Cc: stable@vger.kernel.org
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Link: https://patch.msgid.link/20251024085918.31825-2-adrian.hunter@intel.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/ufs/host/ufshcd-pci.c

index b87e037773955fea50a054bfb678a637b5a0c56b..89f88b69385020f6665e41ade9fa13a972ddefae 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/pci.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_qos.h>
+#include <linux/suspend.h>
 #include <linux/debugfs.h>
 #include <linux/uuid.h>
 #include <linux/acpi.h>
@@ -31,6 +32,7 @@ struct intel_host {
        u32             dsm_fns;
        u32             active_ltr;
        u32             idle_ltr;
+       int             saved_spm_lvl;
        struct dentry   *debugfs_root;
        struct gpio_desc *reset_gpio;
 };
@@ -347,6 +349,7 @@ static int ufs_intel_common_init(struct ufs_hba *hba)
        host = devm_kzalloc(hba->dev, sizeof(*host), GFP_KERNEL);
        if (!host)
                return -ENOMEM;
+       host->saved_spm_lvl = -1;
        ufshcd_set_variant(hba, host);
        intel_dsm_init(host, hba->dev);
        if (INTEL_DSM_SUPPORTED(host, RESET)) {
@@ -538,6 +541,66 @@ static int ufshcd_pci_restore(struct device *dev)
 
        return ufshcd_system_resume(dev);
 }
+
+static int ufs_intel_suspend_prepare(struct device *dev)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       struct intel_host *host = ufshcd_get_variant(hba);
+       int err;
+
+       /*
+        * Only s2idle (S0ix) retains link state.  Force power-off
+        * (UFS_PM_LVL_5) for any other case.
+        */
+       if (pm_suspend_target_state != PM_SUSPEND_TO_IDLE && hba->spm_lvl < UFS_PM_LVL_5) {
+               host->saved_spm_lvl = hba->spm_lvl;
+               hba->spm_lvl = UFS_PM_LVL_5;
+       }
+
+       err = ufshcd_suspend_prepare(dev);
+
+       if (err < 0 && host->saved_spm_lvl != -1) {
+               hba->spm_lvl = host->saved_spm_lvl;
+               host->saved_spm_lvl = -1;
+       }
+
+       return err;
+}
+
+static void ufs_intel_resume_complete(struct device *dev)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       struct intel_host *host = ufshcd_get_variant(hba);
+
+       ufshcd_resume_complete(dev);
+
+       if (host->saved_spm_lvl != -1) {
+               hba->spm_lvl = host->saved_spm_lvl;
+               host->saved_spm_lvl = -1;
+       }
+}
+
+static int ufshcd_pci_suspend_prepare(struct device *dev)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+
+       if (!strcmp(hba->vops->name, "intel-pci"))
+               return ufs_intel_suspend_prepare(dev);
+
+       return ufshcd_suspend_prepare(dev);
+}
+
+static void ufshcd_pci_resume_complete(struct device *dev)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+
+       if (!strcmp(hba->vops->name, "intel-pci")) {
+               ufs_intel_resume_complete(dev);
+               return;
+       }
+
+       ufshcd_resume_complete(dev);
+}
 #endif
 
 /**
@@ -611,8 +674,8 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = {
        .thaw           = ufshcd_system_resume,
        .poweroff       = ufshcd_system_suspend,
        .restore        = ufshcd_pci_restore,
-       .prepare        = ufshcd_suspend_prepare,
-       .complete       = ufshcd_resume_complete,
+       .prepare        = ufshcd_pci_suspend_prepare,
+       .complete       = ufshcd_pci_resume_complete,
 #endif
 };