@@ -360,6 +360,12 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
goto dealloc_host;
}
+ if (devm_pm_opp_of_add_table(dev))
+ dev_dbg(dev, "no OPP table (%d), no performance state control\n",
+ err);
+ else
+ hba->use_pm_opp = true;
+
ufshcd_init_lanes_per_dir(hba);
err = ufshcd_init(hba, mmio_base, irq);
@@ -1164,11 +1164,16 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
{
int ret = 0;
+ struct ufs_clk_info *clki;
+ unsigned long pm_opp_target_rate;
struct ufs_pa_layer_attr new_pwr_info;
+ clki = list_first_entry(&hba->clk_list_head, struct ufs_clk_info, list);
+
if (scale_up) {
memcpy(&new_pwr_info, &hba->clk_scaling.saved_pwr_info.info,
sizeof(struct ufs_pa_layer_attr));
+ pm_opp_target_rate = clki->max_freq;
} else {
memcpy(&new_pwr_info, &hba->pwr_info,
sizeof(struct ufs_pa_layer_attr));
@@ -1184,6 +1189,13 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
new_pwr_info.gear_tx = hba->clk_scaling.min_gear;
new_pwr_info.gear_rx = hba->clk_scaling.min_gear;
}
+ pm_opp_target_rate = clki->min_freq;
+ }
+
+ if (hba->use_pm_opp && scale_up) {
+ ret = dev_pm_opp_set_rate(hba->dev, pm_opp_target_rate);
+ if (ret)
+ return ret;
}
/* check if the power mode needs to be changed or not? */
@@ -1194,6 +1206,11 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
hba->pwr_info.gear_tx, hba->pwr_info.gear_rx,
new_pwr_info.gear_tx, new_pwr_info.gear_rx);
+ if (ret && hba->use_pm_opp && scale_up)
+ dev_pm_opp_set_rate(hba->dev, hba->devfreq->previous_freq);
+ else if (hba->use_pm_opp && !scale_up)
+ ret = dev_pm_opp_set_rate(hba->dev, pm_opp_target_rate);
+
return ret;
}
@@ -1435,9 +1452,11 @@ static int ufshcd_devfreq_init(struct ufs_hba *hba)
if (list_empty(clk_list))
return 0;
- clki = list_first_entry(clk_list, struct ufs_clk_info, list);
- dev_pm_opp_add(hba->dev, clki->min_freq, 0);
- dev_pm_opp_add(hba->dev, clki->max_freq, 0);
+ if (!hba->use_pm_opp) {
+ clki = list_first_entry(clk_list, struct ufs_clk_info, list);
+ dev_pm_opp_add(hba->dev, clki->min_freq, 0);
+ dev_pm_opp_add(hba->dev, clki->max_freq, 0);
+ }
ufshcd_vops_config_scaling_param(hba, &hba->vps->devfreq_profile,
&hba->vps->ondemand_data);
@@ -1449,8 +1468,10 @@ static int ufshcd_devfreq_init(struct ufs_hba *hba)
ret = PTR_ERR(devfreq);
dev_err(hba->dev, "Unable to register with devfreq %d\n", ret);
- dev_pm_opp_remove(hba->dev, clki->min_freq);
- dev_pm_opp_remove(hba->dev, clki->max_freq);
+ if (!hba->use_pm_opp) {
+ dev_pm_opp_remove(hba->dev, clki->min_freq);
+ dev_pm_opp_remove(hba->dev, clki->max_freq);
+ }
return ret;
}
@@ -1462,7 +1483,6 @@ static int ufshcd_devfreq_init(struct ufs_hba *hba)
static void ufshcd_devfreq_remove(struct ufs_hba *hba)
{
struct list_head *clk_list = &hba->clk_list_head;
- struct ufs_clk_info *clki;
if (!hba->devfreq)
return;
@@ -1470,9 +1490,13 @@ static void ufshcd_devfreq_remove(struct ufs_hba *hba)
devfreq_remove_device(hba->devfreq);
hba->devfreq = NULL;
- clki = list_first_entry(clk_list, struct ufs_clk_info, list);
- dev_pm_opp_remove(hba->dev, clki->min_freq);
- dev_pm_opp_remove(hba->dev, clki->max_freq);
+ if (!hba->use_pm_opp) {
+ struct ufs_clk_info *clki;
+
+ clki = list_first_entry(clk_list, struct ufs_clk_info, list);
+ dev_pm_opp_remove(hba->dev, clki->min_freq);
+ dev_pm_opp_remove(hba->dev, clki->max_freq);
+ }
}
static void __ufshcd_suspend_clkscaling(struct ufs_hba *hba)
@@ -776,6 +776,8 @@ struct ufs_hba_monitor {
* @auto_bkops_enabled: to track whether bkops is enabled in device
* @vreg_info: UFS device voltage regulator information
* @clk_list_head: UFS host controller clocks list node head
+ * @use_pm_opp: whether OPP table is provided and scaling gears should trigger
+ * setting OPP
* @pwr_info: holds current power mode
* @max_pwr_info: keeps the device max valid pwm
* @clk_scaling_lock: used to serialize device commands and clock scaling
@@ -894,6 +896,7 @@ struct ufs_hba {
bool auto_bkops_enabled;
struct ufs_vreg_info vreg_info;
struct list_head clk_list_head;
+ bool use_pm_opp;
/* Number of requests aborts */
int req_abort_count;
Scaling gears requires not only scaling clocks, but also voltage levels, e.g. via performance states. USe the provided OPP table, to set proper OPP frequency which through required-opps will trigger performance state change. Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> --- drivers/scsi/ufs/ufshcd-pltfrm.c | 6 +++++ drivers/scsi/ufs/ufshcd.c | 42 +++++++++++++++++++++++++------- drivers/scsi/ufs/ufshcd.h | 3 +++ 3 files changed, 42 insertions(+), 9 deletions(-)