Skip to content

Commit 51c843c

Browse files
Sibi Sankarvireshk
authored andcommitted
cpufreq: qcom: Update the bandwidth levels on frequency change
Add support to parse optional OPP table attached to the cpu node when the OPP bandwidth values are populated. This allows for scaling of DDR/L3 bandwidth levels with frequency change. Signed-off-by: Sibi Sankar <[email protected]> Reviewed-by: Matthias Kaehlcke <[email protected]> Signed-off-by: Viresh Kumar <[email protected]>
1 parent 3ae1f39 commit 51c843c

File tree

1 file changed

+81
-2
lines changed

1 file changed

+81
-2
lines changed

drivers/cpufreq/qcom-cpufreq-hw.c

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/bitfield.h>
77
#include <linux/cpufreq.h>
88
#include <linux/init.h>
9+
#include <linux/interconnect.h>
910
#include <linux/kernel.h>
1011
#include <linux/module.h>
1112
#include <linux/of_address.h>
@@ -30,6 +31,48 @@
3031

3132
static unsigned long cpu_hw_rate, xo_rate;
3233
static struct platform_device *global_pdev;
34+
static bool icc_scaling_enabled;
35+
36+
static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy,
37+
unsigned long freq_khz)
38+
{
39+
unsigned long freq_hz = freq_khz * 1000;
40+
struct dev_pm_opp *opp;
41+
struct device *dev;
42+
int ret;
43+
44+
dev = get_cpu_device(policy->cpu);
45+
if (!dev)
46+
return -ENODEV;
47+
48+
opp = dev_pm_opp_find_freq_exact(dev, freq_hz, true);
49+
if (IS_ERR(opp))
50+
return PTR_ERR(opp);
51+
52+
ret = dev_pm_opp_set_bw(dev, opp);
53+
dev_pm_opp_put(opp);
54+
return ret;
55+
}
56+
57+
static int qcom_cpufreq_update_opp(struct device *cpu_dev,
58+
unsigned long freq_khz,
59+
unsigned long volt)
60+
{
61+
unsigned long freq_hz = freq_khz * 1000;
62+
int ret;
63+
64+
/* Skip voltage update if the opp table is not available */
65+
if (!icc_scaling_enabled)
66+
return dev_pm_opp_add(cpu_dev, freq_hz, volt);
67+
68+
ret = dev_pm_opp_adjust_voltage(cpu_dev, freq_hz, volt, volt, volt);
69+
if (ret) {
70+
dev_err(cpu_dev, "Voltage update failed freq=%ld\n", freq_khz);
71+
return ret;
72+
}
73+
74+
return dev_pm_opp_enable(cpu_dev, freq_hz);
75+
}
3376

3477
static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
3578
unsigned int index)
@@ -39,6 +82,9 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
3982

4083
writel_relaxed(index, perf_state_reg);
4184

85+
if (icc_scaling_enabled)
86+
qcom_cpufreq_set_bw(policy, freq);
87+
4288
arch_set_freq_scale(policy->related_cpus, freq,
4389
policy->cpuinfo.max_freq);
4490
return 0;
@@ -89,11 +135,33 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
89135
u32 data, src, lval, i, core_count, prev_freq = 0, freq;
90136
u32 volt;
91137
struct cpufreq_frequency_table *table;
138+
struct dev_pm_opp *opp;
139+
unsigned long rate;
140+
int ret;
92141

93142
table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL);
94143
if (!table)
95144
return -ENOMEM;
96145

146+
ret = dev_pm_opp_of_add_table(cpu_dev);
147+
if (!ret) {
148+
/* Disable all opps and cross-validate against LUT later */
149+
icc_scaling_enabled = true;
150+
for (rate = 0; ; rate++) {
151+
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
152+
if (IS_ERR(opp))
153+
break;
154+
155+
dev_pm_opp_put(opp);
156+
dev_pm_opp_disable(cpu_dev, rate);
157+
}
158+
} else if (ret != -ENODEV) {
159+
dev_err(cpu_dev, "Invalid opp table in device tree\n");
160+
return ret;
161+
} else {
162+
icc_scaling_enabled = false;
163+
}
164+
97165
for (i = 0; i < LUT_MAX_ENTRIES; i++) {
98166
data = readl_relaxed(base + REG_FREQ_LUT +
99167
i * LUT_ROW_SIZE);
@@ -112,7 +180,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
112180

113181
if (freq != prev_freq && core_count != LUT_TURBO_IND) {
114182
table[i].frequency = freq;
115-
dev_pm_opp_add(cpu_dev, freq * 1000, volt);
183+
qcom_cpufreq_update_opp(cpu_dev, freq, volt);
116184
dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i,
117185
freq, core_count);
118186
} else if (core_count == LUT_TURBO_IND) {
@@ -133,7 +201,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
133201
if (prev->frequency == CPUFREQ_ENTRY_INVALID) {
134202
prev->frequency = prev_freq;
135203
prev->flags = CPUFREQ_BOOST_FREQ;
136-
dev_pm_opp_add(cpu_dev, prev_freq * 1000, volt);
204+
qcom_cpufreq_update_opp(cpu_dev, prev_freq, volt);
137205
}
138206

139207
break;
@@ -254,6 +322,7 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
254322
void __iomem *base = policy->driver_data - REG_PERF_STATE;
255323

256324
dev_pm_opp_remove_all_dynamic(cpu_dev);
325+
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
257326
kfree(policy->freq_table);
258327
devm_iounmap(&global_pdev->dev, base);
259328

@@ -282,6 +351,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = {
282351

283352
static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
284353
{
354+
struct device *cpu_dev;
285355
struct clk *clk;
286356
int ret;
287357

@@ -301,6 +371,15 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
301371

302372
global_pdev = pdev;
303373

374+
/* Check for optional interconnect paths on CPU0 */
375+
cpu_dev = get_cpu_device(0);
376+
if (!cpu_dev)
377+
return -EPROBE_DEFER;
378+
379+
ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
380+
if (ret)
381+
return ret;
382+
304383
ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver);
305384
if (ret)
306385
dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n");

0 commit comments

Comments
 (0)