Skip to content

Commit 72208eb

Browse files
Mani-Sadhasivammartinkpetersen
authored andcommitted
scsi: ufs: core: Add support for parsing OPP
OPP framework can be used to scale the clocks along with other entities such as regulators, performance state etc... So let's add support for parsing OPP from devicetree. OPP support in devicetree is added through the "operating-points-v2" property which accepts the OPP table defining clock frequency, regulator voltage, power domain performance state etc... Since the UFS controller requires multiple clocks to be controlled for proper working, devm_pm_opp_set_config() has been used which supports scaling multiple clocks through custom ufshcd_opp_config_clks() callback. It should be noted that the OPP support is not compatible with the old "freq-table-hz" property. So only one can be used at a time even though the UFS core supports both. 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 930bd77 commit 72208eb

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

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

+36
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,42 @@ static int ufshcd_set_clk_freq(struct ufs_hba *hba, bool scale_up)
10631063
return ret;
10641064
}
10651065

1066+
int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
1067+
struct dev_pm_opp *opp, void *data,
1068+
bool scaling_down)
1069+
{
1070+
struct ufs_hba *hba = dev_get_drvdata(dev);
1071+
struct list_head *head = &hba->clk_list_head;
1072+
struct ufs_clk_info *clki;
1073+
unsigned long freq;
1074+
u8 idx = 0;
1075+
int ret;
1076+
1077+
list_for_each_entry(clki, head, list) {
1078+
if (!IS_ERR_OR_NULL(clki->clk)) {
1079+
freq = dev_pm_opp_get_freq_indexed(opp, idx++);
1080+
1081+
/* Do not set rate for clocks having frequency as 0 */
1082+
if (!freq)
1083+
continue;
1084+
1085+
ret = clk_set_rate(clki->clk, freq);
1086+
if (ret) {
1087+
dev_err(dev, "%s: %s clk set rate(%ldHz) failed, %d\n",
1088+
__func__, clki->name, freq, ret);
1089+
return ret;
1090+
}
1091+
1092+
trace_ufshcd_clk_scaling(dev_name(dev),
1093+
(scaling_down ? "scaled down" : "scaled up"),
1094+
clki->name, hba->clk_scaling.target_freq, freq);
1095+
}
1096+
}
1097+
1098+
return 0;
1099+
}
1100+
EXPORT_SYMBOL_GPL(ufshcd_opp_config_clks);
1101+
10661102
static int ufshcd_opp_set_rate(struct ufs_hba *hba, unsigned long freq)
10671103
{
10681104
struct dev_pm_opp *opp;

Diff for: drivers/ufs/host/ufshcd-pltfrm.c

+78
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <linux/module.h>
1212
#include <linux/platform_device.h>
13+
#include <linux/pm_opp.h>
1314
#include <linux/pm_runtime.h>
1415
#include <linux/of.h>
1516

@@ -212,6 +213,77 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
212213
}
213214
}
214215

216+
static int ufshcd_parse_operating_points(struct ufs_hba *hba)
217+
{
218+
struct device *dev = hba->dev;
219+
struct device_node *np = dev->of_node;
220+
struct dev_pm_opp_config config = {};
221+
struct ufs_clk_info *clki;
222+
const char **clk_names;
223+
int cnt, i, ret;
224+
225+
if (!of_find_property(np, "operating-points-v2", NULL))
226+
return 0;
227+
228+
if (of_find_property(np, "freq-table-hz", NULL)) {
229+
dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n",
230+
__func__);
231+
return -EINVAL;
232+
}
233+
234+
cnt = of_property_count_strings(np, "clock-names");
235+
if (cnt <= 0) {
236+
dev_err(dev, "%s: Missing clock-names\n", __func__);
237+
return -ENODEV;
238+
}
239+
240+
/* OPP expects clk_names to be NULL terminated */
241+
clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL);
242+
if (!clk_names)
243+
return -ENOMEM;
244+
245+
/*
246+
* We still need to get reference to all clocks as the UFS core uses
247+
* them separately.
248+
*/
249+
for (i = 0; i < cnt; i++) {
250+
ret = of_property_read_string_index(np, "clock-names", i,
251+
&clk_names[i]);
252+
if (ret)
253+
return ret;
254+
255+
clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
256+
if (!clki)
257+
return -ENOMEM;
258+
259+
clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL);
260+
if (!clki->name)
261+
return -ENOMEM;
262+
263+
if (!strcmp(clk_names[i], "ref_clk"))
264+
clki->keep_link_active = true;
265+
266+
list_add_tail(&clki->list, &hba->clk_list_head);
267+
}
268+
269+
config.clk_names = clk_names,
270+
config.config_clks = ufshcd_opp_config_clks;
271+
272+
ret = devm_pm_opp_set_config(dev, &config);
273+
if (ret)
274+
return ret;
275+
276+
ret = devm_pm_opp_of_add_table(dev);
277+
if (ret) {
278+
dev_err(dev, "Failed to add OPP table: %d\n", ret);
279+
return ret;
280+
}
281+
282+
hba->use_pm_opp = true;
283+
284+
return 0;
285+
}
286+
215287
/**
216288
* ufshcd_get_pwr_dev_param - get finally agreed attributes for
217289
* power mode change
@@ -378,6 +450,12 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
378450

379451
ufshcd_init_lanes_per_dir(hba);
380452

453+
err = ufshcd_parse_operating_points(hba);
454+
if (err) {
455+
dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
456+
goto dealloc_host;
457+
}
458+
381459
err = ufshcd_init(hba, mmio_base, irq);
382460
if (err) {
383461
dev_err_probe(dev, err, "Initialization failed with error %d\n",

Diff for: include/ufs/ufshcd.h

+3
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,9 @@ void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba);
12541254
void ufshcd_mcq_enable_esi(struct ufs_hba *hba);
12551255
void ufshcd_mcq_config_esi(struct ufs_hba *hba, struct msi_msg *msg);
12561256

1257+
int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
1258+
struct dev_pm_opp *opp, void *data,
1259+
bool scaling_down);
12571260
/**
12581261
* ufshcd_set_variant - set variant specific data to the hba
12591262
* @hba: per adapter instance

0 commit comments

Comments
 (0)