ufs: qcom: Create sysfs node "ufs_pm_mode"
In some HQX targets (where Linux is running as guest OS), when S2R is triggered from host OS, the GVM is sent to Deep Sleep state. Since Deep Sleep is getting triggered from Guest point of view, ufs spm level is set to 5, Power OFF SSU command is sent to the ufs device and all regulators are turned off from ufs. But since it is actually an S2R event from Host PoV, the regulators are not turned off by the PMIC. This is causing power leakage. To fix this issue, create a sysfs node "ufs_pm_mode". The Host OS will write to this node which the event (S2R or Deep Sleep) is getting triggered at the Host. UFS will set the correct spm level accordingly. For S2R case, the default spm level is retained while for Deep Sleep it is set to 5. The default value for this node is 0( which is set during ufs init). This means it will not set anything unless "S2R" or "DEEPSLEEP" is explicitly written to the node. ufs_pm_mode can have three values: "NONE","S2R","DEEPSLEEP": 1. When "NONE" is written to sysfs node: the spm level is set to 5 for deep sleep and existing spm value left unchanged for s2idle case. 2. When "S2R" is written to the node: the default spm level value is retained. 3. When "DEEPSLEEP" is written to the node: the spm level is set to 5. Change-Id: I232449cf93a3b37652897e74a621bf219302b76b Signed-off-by: Ram Kumar Dwivedi <quic_rdwivedi@quicinc.com>
This commit is contained in:
parent
1f9023b537
commit
4ab95acc04
2 changed files with 78 additions and 3 deletions
|
|
@ -87,6 +87,12 @@ enum {
|
|||
UFS_QCOM_CMD_COMPL,
|
||||
};
|
||||
|
||||
enum {
|
||||
UFS_QCOM_SYSFS_NONE,
|
||||
UFS_QCOM_SYSFS_S2R,
|
||||
UFS_QCOM_SYSFS_DEEPSLEEP,
|
||||
};
|
||||
|
||||
static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
|
||||
|
||||
static DEFINE_PER_CPU(struct freq_qos_request, qos_min_req);
|
||||
|
|
@ -5571,6 +5577,59 @@ static ssize_t irq_affinity_support_show(struct device *dev,
|
|||
|
||||
static DEVICE_ATTR_RW(irq_affinity_support);
|
||||
|
||||
static ssize_t ufs_pm_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret = -1;
|
||||
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
|
||||
switch (host->ufs_pm_mode) {
|
||||
case 0:
|
||||
ret = scnprintf(buf, 6, "NONE\n");
|
||||
break;
|
||||
case 1:
|
||||
ret = scnprintf(buf, 5, "S2R\n");
|
||||
break;
|
||||
case 2:
|
||||
ret = scnprintf(buf, 12, "DEEPSLEEP\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ufs_pm_mode_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
char kbuff[12] = {0};
|
||||
|
||||
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
|
||||
strscpy(kbuff, buf, 11);
|
||||
|
||||
if (!strncasecmp(kbuff, "NONE", 4))
|
||||
host->ufs_pm_mode = 0;
|
||||
else if (!strncasecmp(kbuff, "S2R", 3))
|
||||
host->ufs_pm_mode = 1;
|
||||
else if (!strncasecmp(kbuff, "DEEPSLEEP", 9))
|
||||
host->ufs_pm_mode = 2;
|
||||
else
|
||||
dev_err(hba->dev, "Invalid entry for ufs_pm_mode\n");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static DEVICE_ATTR_RW(ufs_pm_mode);
|
||||
|
||||
static struct attribute *ufs_qcom_sysfs_attrs[] = {
|
||||
&dev_attr_err_state.attr,
|
||||
&dev_attr_power_mode.attr,
|
||||
|
|
@ -5582,6 +5641,7 @@ static struct attribute *ufs_qcom_sysfs_attrs[] = {
|
|||
&dev_attr_hibern8_count.attr,
|
||||
&dev_attr_ber_th_exceeded.attr,
|
||||
&dev_attr_irq_affinity_support.attr,
|
||||
&dev_attr_ufs_pm_mode.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -6052,10 +6112,24 @@ static int ufs_qcom_suspend_prepare(struct device *dev)
|
|||
* regulators is turned off in DS. For other senerios
|
||||
* like s2idle, retain the default spm level.
|
||||
*/
|
||||
if (pm_suspend_target_state == PM_SUSPEND_MEM)
|
||||
hba->spm_lvl = UFS_PM_LVL_5;
|
||||
else
|
||||
switch (host->ufs_pm_mode) {
|
||||
case UFS_QCOM_SYSFS_NONE:
|
||||
if (pm_suspend_target_state == PM_SUSPEND_MEM)
|
||||
hba->spm_lvl = UFS_PM_LVL_5;
|
||||
else
|
||||
hba->spm_lvl = host->spm_lvl_default;
|
||||
break;
|
||||
case UFS_QCOM_SYSFS_S2R:
|
||||
hba->spm_lvl = host->spm_lvl_default;
|
||||
break;
|
||||
case UFS_QCOM_SYSFS_DEEPSLEEP:
|
||||
hba->spm_lvl = UFS_PM_LVL_5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info(dev, "spm level is set to %d\n", hba->spm_lvl);
|
||||
|
||||
return ufshcd_suspend_prepare(dev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -592,6 +592,7 @@ struct ufs_qcom_host {
|
|||
bool bypass_g4_cfgready;
|
||||
bool is_dt_pm_level_read;
|
||||
u32 spm_lvl_default;
|
||||
u32 ufs_pm_mode;
|
||||
bool is_phy_pwr_on;
|
||||
/* Protect the usage of is_phy_pwr_on against racing */
|
||||
struct mutex phy_mutex;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue