@@ -38,6 +38,14 @@ Optional properties:
defined or a value in the array is "0" then it is assumed
that the frequency is set by the parent clock or a
fixed rate clock source.
+- rpm-level : UFS Runtime power management level. Following PM levels are supported:
+ 0 - Both UFS device and Link in active state (Highest power consumption)
+ 1 - UFS device in active state but Link in Hibern8 state
+ 2 - UFS device in Sleep state but Link in active state
+ 3 - UFS device in Sleep state and Link in hibern8 state (default PM level)
+ 4 - UFS device in Power-down state and Link in Hibern8 state
+ 5 - UFS device in Power-down state and Link in OFF state (Lowest power consumption)
+- spm-level : UFS System power management level. Allowed PM levels are same as rpm-level.
- lanes-per-direction: number of lanes available per direction - either 1 or 2.
Note that it is assume same number of lanes is used both directions at once.
If not specified, default is 2 lanes per direction.
@@ -226,7 +226,20 @@ out:
return err;
}
-#ifdef CONFIG_PM
+static void ufshcd_parse_pm_levels(struct ufs_hba *hba)
+{
+ struct device *dev = hba->dev;
+ struct device_node *np = dev->of_node;
+
+ if (np) {
+ if (of_property_read_u32(np, "rpm-level", &hba->rpm_lvl))
+ hba->rpm_lvl = -1;
+ if (of_property_read_u32(np, "spm-level", &hba->spm_lvl))
+ hba->spm_lvl = -1;
+ }
+}
+
+#ifdef CONFIG_SMP
/**
* ufshcd_pltfrm_suspend - suspend power management function
* @dev: pointer to device handle
@@ -345,6 +358,8 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
goto dealloc_host;
}
+ ufshcd_parse_pm_levels(hba);
+
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
@@ -187,6 +187,30 @@ ufs_get_pm_lvl_to_link_pwr_state(enum ufs_pm_level lvl)
return ufs_pm_lvl_states[lvl].link_state;
}
+static inline enum ufs_pm_level
+ufs_get_desired_pm_lvl_for_dev_link_state(enum ufs_dev_pwr_mode dev_state,
+ enum uic_link_state link_state)
+{
+ enum ufs_pm_level lvl;
+
+ for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++) {
+ if ((ufs_pm_lvl_states[lvl].dev_state == dev_state) &&
+ (ufs_pm_lvl_states[lvl].link_state == link_state))
+ return lvl;
+ }
+
+ /* if no match found, return the level 0 */
+ return UFS_PM_LVL_0;
+}
+
+static inline bool ufshcd_is_valid_pm_lvl(int lvl)
+{
+ if (lvl >= 0 && lvl < ARRAY_SIZE(ufs_pm_lvl_states))
+ return true;
+ else
+ return false;
+}
+
static void ufshcd_tmc_handler(struct ufs_hba *hba);
static void ufshcd_async_scan(void *data, async_cookie_t cookie);
static int ufshcd_reset_and_restore(struct ufs_hba *hba);
@@ -5885,6 +5909,21 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
hba->clk_scaling.window_start_t = 0;
}
+ /*
+ * If rpm_lvl and and spm_lvl are not already set to valid levels,
+ * set the default power management level for UFS runtime and system
+ * suspend. Default power saving mode selected is keeping UFS link in
+ * Hibern8 state and UFS device in sleep.
+ */
+ if (!ufshcd_is_valid_pm_lvl(hba->rpm_lvl))
+ hba->rpm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
+ UFS_SLEEP_PWR_MODE,
+ UIC_LINK_HIBERN8_STATE);
+ if (!ufshcd_is_valid_pm_lvl(hba->spm_lvl))
+ hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
+ UFS_SLEEP_PWR_MODE,
+ UIC_LINK_HIBERN8_STATE);
+
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync(dev);
@@ -408,9 +408,9 @@ struct ufs_hba {
enum ufs_dev_pwr_mode curr_dev_pwr_mode;
enum uic_link_state uic_link_state;
/* Desired UFS power management level during runtime PM */
- enum ufs_pm_level rpm_lvl;
+ int rpm_lvl;
/* Desired UFS power management level during system PM */
- enum ufs_pm_level spm_lvl;
+ int spm_lvl;
int pm_op_in_progress;
struct ufshcd_lrb *lrb;