return err;
}
+static int ufshcd_parse_pm_lvl_policy(struct ufs_hba *hba)
+{
+ struct device *dev = hba->dev;
+ struct device_node *np = dev->of_node;
+ u32 lvl_def[] = {UFS_PM_LVL_2, UFS_PM_LVL_5};
+ u32 lvl[2] = {0,}, i;
+
+ for (i = 0; i < ARRAY_SIZE(lvl); i++) {
+ if (of_property_read_u32_index(np, "pm_lvl_states", i, lvl +i)) {
+ dev_info(hba->dev,
+ "UFS power management: set default level%d index %d\n",
+ lvl_def[i], i);
+ lvl[i] = lvl_def[i];
+ }
+
+ if (lvl[i] < UFS_PM_LVL_0 || lvl[i] >= UFS_PM_LVL_MAX) {
+ dev_warn(hba->dev,
+ "UFS power management: out of range level%d index %d\n",
+ lvl[i], i);
+ lvl[i] = lvl_def[i];
+ }
+ }
+
+ hba->rpm_lvl = lvl[0];
+ hba->spm_lvl = lvl[1];
+
+ return 0;
+}
#ifdef CONFIG_PM
/**
* ufshcd_pltfrm_suspend - suspend power management function
goto dealloc_host;
}
+ ufshcd_parse_pm_lvl_policy(hba);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
dev_err(hba->dev,
"%s: power mode change failed %d\n", __func__, ret);
} else {
- ufshcd_vops_pwr_change_notify(hba, POST_CHANGE, NULL,
+ ret = ufshcd_vops_pwr_change_notify(hba, POST_CHANGE, NULL,
pwr_mode);
+ if (ret)
+ goto out;
memcpy(&hba->pwr_info, pwr_mode,
sizeof(struct ufs_pa_layer_attr));
ret = ufshcd_vops_pwr_change_notify(hba, PRE_CHANGE,
desired_pwr_mode, &final_params);
- if (ret)
- memcpy(&final_params, desired_pwr_mode, sizeof(final_params));
+ if (ret) {
+ if (ret == -ENOTSUPP)
+ memcpy(&final_params, desired_pwr_mode, sizeof(final_params));
+ else
+ goto out;
+ }
ret = ufshcd_change_power_mode(hba, &final_params);
if (!ret)
ufshcd_print_pwr_info(hba);
-
+out:
return ret;
}
EXPORT_SYMBOL_GPL(ufshcd_config_pwr_mode);
int ret = 0;
struct ufs_clk_info *clki;
struct list_head *head = &hba->clk_list_head;
+ const char *ref_clk = "ref_clk";
unsigned long flags;
ktime_t start = ktime_get();
bool clk_state_changed = false;
list_for_each_entry(clki, head, list) {
if (!IS_ERR_OR_NULL(clki->clk)) {
- if (skip_ref_clk && !strcmp(clki->name, "ref_clk"))
+ if (skip_ref_clk &&
+ !strncmp(clki->name, ref_clk, strlen(ref_clk)))
continue;
clk_state_changed = on ^ clki->enabled;
if (ret)
goto set_dev_active;
- ufshcd_vreg_set_lpm(hba);
-
disable_clks:
/*
- * Call vendor specific suspend callback. As these callbacks may access
- * vendor specific host controller register space call them before the
- * host clocks are ON.
+ * Disable the host irq as host controller as there won't be any
+ * host controller trasanction expected till resume.
*/
- ret = ufshcd_vops_suspend(hba, pm_op);
- if (ret)
- goto set_link_active;
+ ufshcd_disable_irq(hba);
+
if (!ufshcd_is_link_active(hba))
ufshcd_setup_clocks(hba, false);
hba->clk_gating.state = CLKS_OFF;
trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state);
/*
- * Disable the host irq as host controller as there won't be any
- * host controller transaction expected till resume.
+ * Call vendor specific suspend callback. As these callbacks may access
+ * vendor specific host controller register space call them before the
+ * host clocks are ON.
*/
- ufshcd_disable_irq(hba);
+ ret = ufshcd_vops_suspend(hba, pm_op);
+ if (ret)
+ goto set_link_active;
+
+
/* Put the host controller in low power mode if possible */
ufshcd_hba_vreg_set_lpm(hba);
goto out;