Skip to content

Commit 930bd77

Browse files
Mani-Sadhasivammartinkpetersen
authored andcommitted
scsi: ufs: core: Add OPP support for scaling clocks and regulators
UFS core is only scaling the clocks during devfreq scaling and initialization. But for an optimum power saving, regulators should also be scaled along with the clocks. So let's use the OPP framework which supports scaling clocks, regulators, and performance state using OPP table defined in devicetree. For accomodating the OPP support, the existing APIs (ufshcd_scale_clks, ufshcd_is_devfreq_scaling_required and ufshcd_devfreq_scale) are modified to accept "freq" as an argument which in turn used by the OPP helpers. The OPP support is added along with the old freq-table based clock scaling so that the existing platforms work as expected. Co-developed-by: Krzysztof Kozlowski <[email protected]> Signed-off-by: Krzysztof Kozlowski <[email protected]> Signed-off-by: Manivannan Sadhasivam <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin K. Petersen <[email protected]>
1 parent e820de1 commit 930bd77

File tree

2 files changed

+115
-33
lines changed

2 files changed

+115
-33
lines changed

Diff for: drivers/ufs/core/ufshcd.c

+111-33
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/delay.h>
2121
#include <linux/interrupt.h>
2222
#include <linux/module.h>
23+
#include <linux/pm_opp.h>
2324
#include <linux/regulator/consumer.h>
2425
#include <linux/sched/clock.h>
2526
#include <linux/iopoll.h>
@@ -274,7 +275,8 @@ static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
274275
static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
275276
static void ufshcd_resume_clkscaling(struct ufs_hba *hba);
276277
static void ufshcd_suspend_clkscaling(struct ufs_hba *hba);
277-
static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up);
278+
static int ufshcd_scale_clks(struct ufs_hba *hba, unsigned long freq,
279+
bool scale_up);
278280
static irqreturn_t ufshcd_intr(int irq, void *__hba);
279281
static int ufshcd_change_power_mode(struct ufs_hba *hba,
280282
struct ufs_pa_layer_attr *pwr_mode);
@@ -1061,14 +1063,32 @@ static int ufshcd_set_clk_freq(struct ufs_hba *hba, bool scale_up)
10611063
return ret;
10621064
}
10631065

1066+
static int ufshcd_opp_set_rate(struct ufs_hba *hba, unsigned long freq)
1067+
{
1068+
struct dev_pm_opp *opp;
1069+
int ret;
1070+
1071+
opp = dev_pm_opp_find_freq_floor_indexed(hba->dev,
1072+
&freq, 0);
1073+
if (IS_ERR(opp))
1074+
return PTR_ERR(opp);
1075+
1076+
ret = dev_pm_opp_set_opp(hba->dev, opp);
1077+
dev_pm_opp_put(opp);
1078+
1079+
return ret;
1080+
}
1081+
10641082
/**
10651083
* ufshcd_scale_clks - scale up or scale down UFS controller clocks
10661084
* @hba: per adapter instance
1085+
* @freq: frequency to scale
10671086
* @scale_up: True if scaling up and false if scaling down
10681087
*
10691088
* Return: 0 if successful; < 0 upon failure.
10701089
*/
1071-
static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
1090+
static int ufshcd_scale_clks(struct ufs_hba *hba, unsigned long freq,
1091+
bool scale_up)
10721092
{
10731093
int ret = 0;
10741094
ktime_t start = ktime_get();
@@ -1077,13 +1097,21 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
10771097
if (ret)
10781098
goto out;
10791099

1080-
ret = ufshcd_set_clk_freq(hba, scale_up);
1100+
if (hba->use_pm_opp)
1101+
ret = ufshcd_opp_set_rate(hba, freq);
1102+
else
1103+
ret = ufshcd_set_clk_freq(hba, scale_up);
10811104
if (ret)
10821105
goto out;
10831106

10841107
ret = ufshcd_vops_clk_scale_notify(hba, scale_up, POST_CHANGE);
1085-
if (ret)
1086-
ufshcd_set_clk_freq(hba, !scale_up);
1108+
if (ret) {
1109+
if (hba->use_pm_opp)
1110+
ufshcd_opp_set_rate(hba,
1111+
hba->devfreq->previous_freq);
1112+
else
1113+
ufshcd_set_clk_freq(hba, !scale_up);
1114+
}
10871115

10881116
out:
10891117
trace_ufshcd_profile_clk_scaling(dev_name(hba->dev),
@@ -1095,19 +1123,23 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
10951123
/**
10961124
* ufshcd_is_devfreq_scaling_required - check if scaling is required or not
10971125
* @hba: per adapter instance
1126+
* @freq: frequency to scale
10981127
* @scale_up: True if scaling up and false if scaling down
10991128
*
11001129
* Return: true if scaling is required, false otherwise.
11011130
*/
11021131
static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba,
1103-
bool scale_up)
1132+
unsigned long freq, bool scale_up)
11041133
{
11051134
struct ufs_clk_info *clki;
11061135
struct list_head *head = &hba->clk_list_head;
11071136

11081137
if (list_empty(head))
11091138
return false;
11101139

1140+
if (hba->use_pm_opp)
1141+
return freq != hba->clk_scaling.target_freq;
1142+
11111143
list_for_each_entry(clki, head, list) {
11121144
if (!IS_ERR_OR_NULL(clki->clk)) {
11131145
if (scale_up && clki->max_freq) {
@@ -1303,12 +1335,14 @@ static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err, bool sc
13031335
/**
13041336
* ufshcd_devfreq_scale - scale up/down UFS clocks and gear
13051337
* @hba: per adapter instance
1338+
* @freq: frequency to scale
13061339
* @scale_up: True for scaling up and false for scalin down
13071340
*
13081341
* Return: 0 for success; -EBUSY if scaling can't happen at this time; non-zero
13091342
* for any other errors.
13101343
*/
1311-
static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
1344+
static int ufshcd_devfreq_scale(struct ufs_hba *hba, unsigned long freq,
1345+
bool scale_up)
13121346
{
13131347
int ret = 0;
13141348

@@ -1323,7 +1357,7 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
13231357
goto out_unprepare;
13241358
}
13251359

1326-
ret = ufshcd_scale_clks(hba, scale_up);
1360+
ret = ufshcd_scale_clks(hba, freq, scale_up);
13271361
if (ret) {
13281362
if (!scale_up)
13291363
ufshcd_scale_gear(hba, true);
@@ -1334,7 +1368,8 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
13341368
if (scale_up) {
13351369
ret = ufshcd_scale_gear(hba, true);
13361370
if (ret) {
1337-
ufshcd_scale_clks(hba, false);
1371+
ufshcd_scale_clks(hba, hba->devfreq->previous_freq,
1372+
false);
13381373
goto out_unprepare;
13391374
}
13401375
}
@@ -1393,9 +1428,22 @@ static int ufshcd_devfreq_target(struct device *dev,
13931428
if (!ufshcd_is_clkscaling_supported(hba))
13941429
return -EINVAL;
13951430

1396-
clki = list_first_entry(&hba->clk_list_head, struct ufs_clk_info, list);
1397-
/* Override with the closest supported frequency */
1398-
*freq = (unsigned long) clk_round_rate(clki->clk, *freq);
1431+
if (hba->use_pm_opp) {
1432+
struct dev_pm_opp *opp;
1433+
1434+
/* Get the recommended frequency from OPP framework */
1435+
opp = devfreq_recommended_opp(dev, freq, flags);
1436+
if (IS_ERR(opp))
1437+
return PTR_ERR(opp);
1438+
1439+
dev_pm_opp_put(opp);
1440+
} else {
1441+
/* Override with the closest supported frequency */
1442+
clki = list_first_entry(&hba->clk_list_head, struct ufs_clk_info,
1443+
list);
1444+
*freq = (unsigned long) clk_round_rate(clki->clk, *freq);
1445+
}
1446+
13991447
spin_lock_irqsave(hba->host->host_lock, irq_flags);
14001448
if (ufshcd_eh_in_progress(hba)) {
14011449
spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
@@ -1417,20 +1465,27 @@ static int ufshcd_devfreq_target(struct device *dev,
14171465
goto out;
14181466
}
14191467

1420-
/* Decide based on the rounded-off frequency and update */
1421-
scale_up = *freq == clki->max_freq;
1422-
if (!scale_up)
1468+
/* Decide based on the target or rounded-off frequency and update */
1469+
if (hba->use_pm_opp)
1470+
scale_up = *freq > hba->clk_scaling.target_freq;
1471+
else
1472+
scale_up = *freq == clki->max_freq;
1473+
1474+
if (!hba->use_pm_opp && !scale_up)
14231475
*freq = clki->min_freq;
1476+
14241477
/* Update the frequency */
1425-
if (!ufshcd_is_devfreq_scaling_required(hba, scale_up)) {
1478+
if (!ufshcd_is_devfreq_scaling_required(hba, *freq, scale_up)) {
14261479
spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
14271480
ret = 0;
14281481
goto out; /* no state change required */
14291482
}
14301483
spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
14311484

14321485
start = ktime_get();
1433-
ret = ufshcd_devfreq_scale(hba, scale_up);
1486+
ret = ufshcd_devfreq_scale(hba, *freq, scale_up);
1487+
if (!ret)
1488+
hba->clk_scaling.target_freq = *freq;
14341489

14351490
trace_ufshcd_profile_clk_scaling(dev_name(hba->dev),
14361491
(scale_up ? "up" : "down"),
@@ -1450,8 +1505,6 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev,
14501505
struct ufs_hba *hba = dev_get_drvdata(dev);
14511506
struct ufs_clk_scaling *scaling = &hba->clk_scaling;
14521507
unsigned long flags;
1453-
struct list_head *clk_list = &hba->clk_list_head;
1454-
struct ufs_clk_info *clki;
14551508
ktime_t curr_t;
14561509

14571510
if (!ufshcd_is_clkscaling_supported(hba))
@@ -1464,17 +1517,24 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev,
14641517
if (!scaling->window_start_t)
14651518
goto start_window;
14661519

1467-
clki = list_first_entry(clk_list, struct ufs_clk_info, list);
14681520
/*
14691521
* If current frequency is 0, then the ondemand governor considers
14701522
* there's no initial frequency set. And it always requests to set
14711523
* to max. frequency.
14721524
*/
1473-
stat->current_frequency = clki->curr_freq;
1525+
if (hba->use_pm_opp) {
1526+
stat->current_frequency = hba->clk_scaling.target_freq;
1527+
} else {
1528+
struct list_head *clk_list = &hba->clk_list_head;
1529+
struct ufs_clk_info *clki;
1530+
1531+
clki = list_first_entry(clk_list, struct ufs_clk_info, list);
1532+
stat->current_frequency = clki->curr_freq;
1533+
}
1534+
14741535
if (scaling->is_busy_started)
14751536
scaling->tot_busy_t += ktime_us_delta(curr_t,
14761537
scaling->busy_start_t);
1477-
14781538
stat->total_time = ktime_us_delta(curr_t, scaling->window_start_t);
14791539
stat->busy_time = scaling->tot_busy_t;
14801540
start_window:
@@ -1503,9 +1563,11 @@ static int ufshcd_devfreq_init(struct ufs_hba *hba)
15031563
if (list_empty(clk_list))
15041564
return 0;
15051565

1506-
clki = list_first_entry(clk_list, struct ufs_clk_info, list);
1507-
dev_pm_opp_add(hba->dev, clki->min_freq, 0);
1508-
dev_pm_opp_add(hba->dev, clki->max_freq, 0);
1566+
if (!hba->use_pm_opp) {
1567+
clki = list_first_entry(clk_list, struct ufs_clk_info, list);
1568+
dev_pm_opp_add(hba->dev, clki->min_freq, 0);
1569+
dev_pm_opp_add(hba->dev, clki->max_freq, 0);
1570+
}
15091571

15101572
ufshcd_vops_config_scaling_param(hba, &hba->vps->devfreq_profile,
15111573
&hba->vps->ondemand_data);
@@ -1517,8 +1579,10 @@ static int ufshcd_devfreq_init(struct ufs_hba *hba)
15171579
ret = PTR_ERR(devfreq);
15181580
dev_err(hba->dev, "Unable to register with devfreq %d\n", ret);
15191581

1520-
dev_pm_opp_remove(hba->dev, clki->min_freq);
1521-
dev_pm_opp_remove(hba->dev, clki->max_freq);
1582+
if (!hba->use_pm_opp) {
1583+
dev_pm_opp_remove(hba->dev, clki->min_freq);
1584+
dev_pm_opp_remove(hba->dev, clki->max_freq);
1585+
}
15221586
return ret;
15231587
}
15241588

@@ -1530,17 +1594,20 @@ static int ufshcd_devfreq_init(struct ufs_hba *hba)
15301594
static void ufshcd_devfreq_remove(struct ufs_hba *hba)
15311595
{
15321596
struct list_head *clk_list = &hba->clk_list_head;
1533-
struct ufs_clk_info *clki;
15341597

15351598
if (!hba->devfreq)
15361599
return;
15371600

15381601
devfreq_remove_device(hba->devfreq);
15391602
hba->devfreq = NULL;
15401603

1541-
clki = list_first_entry(clk_list, struct ufs_clk_info, list);
1542-
dev_pm_opp_remove(hba->dev, clki->min_freq);
1543-
dev_pm_opp_remove(hba->dev, clki->max_freq);
1604+
if (!hba->use_pm_opp) {
1605+
struct ufs_clk_info *clki;
1606+
1607+
clki = list_first_entry(clk_list, struct ufs_clk_info, list);
1608+
dev_pm_opp_remove(hba->dev, clki->min_freq);
1609+
dev_pm_opp_remove(hba->dev, clki->max_freq);
1610+
}
15441611
}
15451612

15461613
static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
@@ -1616,7 +1683,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev,
16161683
ufshcd_resume_clkscaling(hba);
16171684
} else {
16181685
ufshcd_suspend_clkscaling(hba);
1619-
err = ufshcd_devfreq_scale(hba, true);
1686+
err = ufshcd_devfreq_scale(hba, ULONG_MAX, true);
16201687
if (err)
16211688
dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
16221689
__func__, err);
@@ -7617,7 +7684,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba)
76177684
hba->silence_err_logs = false;
76187685

76197686
/* scale up clocks to max frequency before full reinitialization */
7620-
ufshcd_scale_clks(hba, true);
7687+
ufshcd_scale_clks(hba, ULONG_MAX, true);
76217688

76227689
err = ufshcd_hba_enable(hba);
76237690

@@ -9163,6 +9230,17 @@ static int ufshcd_init_clocks(struct ufs_hba *hba)
91639230
dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__,
91649231
clki->name, clk_get_rate(clki->clk));
91659232
}
9233+
9234+
/* Set Max. frequency for all clocks */
9235+
if (hba->use_pm_opp) {
9236+
ret = ufshcd_opp_set_rate(hba, ULONG_MAX);
9237+
if (ret) {
9238+
dev_err(hba->dev, "%s: failed to set OPP: %d", __func__,
9239+
ret);
9240+
goto out;
9241+
}
9242+
}
9243+
91669244
out:
91679245
return ret;
91689246
}

Diff for: include/ufs/ufshcd.h

+4
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ struct ufs_clk_gating {
429429
* @workq: workqueue to schedule devfreq suspend/resume work
430430
* @suspend_work: worker to suspend devfreq
431431
* @resume_work: worker to resume devfreq
432+
* @target_freq: frequency requested by devfreq framework
432433
* @min_gear: lowest HS gear to scale down to
433434
* @is_enabled: tracks if scaling is currently enabled or not, controlled by
434435
* clkscale_enable sysfs node
@@ -448,6 +449,7 @@ struct ufs_clk_scaling {
448449
struct workqueue_struct *workq;
449450
struct work_struct suspend_work;
450451
struct work_struct resume_work;
452+
unsigned long target_freq;
451453
u32 min_gear;
452454
bool is_enabled;
453455
bool is_allowed;
@@ -862,6 +864,7 @@ enum ufshcd_mcq_opr {
862864
* @auto_bkops_enabled: to track whether bkops is enabled in device
863865
* @vreg_info: UFS device voltage regulator information
864866
* @clk_list_head: UFS host controller clocks list node head
867+
* @use_pm_opp: Indicates whether OPP based scaling is used or not
865868
* @req_abort_count: number of times ufshcd_abort() has been called
866869
* @lanes_per_direction: number of lanes per data direction between the UFS
867870
* controller and the UFS device.
@@ -1012,6 +1015,7 @@ struct ufs_hba {
10121015
bool auto_bkops_enabled;
10131016
struct ufs_vreg_info vreg_info;
10141017
struct list_head clk_list_head;
1018+
bool use_pm_opp;
10151019

10161020
/* Number of requests aborts */
10171021
int req_abort_count;

0 commit comments

Comments
 (0)