Commit bb44826c authored by Adrian Hunter's avatar Adrian Hunter Committed by Martin K. Petersen
Browse files

scsi: ufs: ufs-pci: Fix S0ix/S3 for Intel controllers



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: 7dc9fb47 ("scsi: ufs: ufs-pci: Add support for Intel ADL")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Reviewed-by: default avatarBart Van Assche <bvanassche@acm.org>
Link: https://patch.msgid.link/20251024085918.31825-2-adrian.hunter@intel.com


Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent f838d624
Loading
Loading
Loading
Loading
+65 −2
Original line number Diff line number Diff line
@@ -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
};