Skip to content

Commit ded4feb

Browse files
committed
Merge tag 'cpufreq-arm-updates-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull CPUFreq updates for 6.19 from Viresh Kumar: "- tegra186: Add OPP / bandwidth support for Tegra186 (Aaron Kling). - Minor improvements to various cpufreq drivers (Christian Marangi, Hal Feng, Jie Zhan, Marco Crivellari, Miaoqian Lin, and Shuhao Fu)." * tag 'cpufreq-arm-updates-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: cpufreq: qcom-nvmem: fix compilation warning for qcom_cpufreq_ipq806x_match_list cpufreq: tegra194: add WQ_PERCPU to alloc_workqueue users cpufreq: qcom-nvmem: add compatible fallback for ipq806x for no SMEM cpufreq: CPPC: Don't warn if FIE init fails to read counters cpufreq: nforce2: fix reference count leak in nforce2 cpufreq: tegra186: add OPP support and set bandwidth cpufreq: dt-platdev: Add JH7110S SOC to the allowlist cpufreq: s5pv210: fix refcount leak
2 parents 1b541e1 + c3852d2 commit ded4feb

7 files changed

Lines changed: 194 additions & 21 deletions

File tree

drivers/cpufreq/cppc_cpufreq.c

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,15 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
142142
init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
143143

144144
ret = cppc_get_perf_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs);
145-
if (ret) {
146-
pr_warn("%s: failed to read perf counters for cpu:%d: %d\n",
147-
__func__, cpu, ret);
148145

149-
/*
150-
* Don't abort if the CPU was offline while the driver
151-
* was getting registered.
152-
*/
153-
if (cpu_online(cpu))
154-
return;
146+
/*
147+
* Don't abort as the CPU was offline while the driver was
148+
* getting registered.
149+
*/
150+
if (ret && cpu_online(cpu)) {
151+
pr_debug("%s: failed to read perf counters for cpu:%d: %d\n",
152+
__func__, cpu, ret);
153+
return;
155154
}
156155
}
157156

drivers/cpufreq/cpufreq-dt-platdev.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ static const struct of_device_id allowlist[] __initconst = {
8787
{ .compatible = "st-ericsson,u9540", },
8888

8989
{ .compatible = "starfive,jh7110", },
90+
{ .compatible = "starfive,jh7110s", },
9091

9192
{ .compatible = "ti,omap2", },
9293
{ .compatible = "ti,omap4", },

drivers/cpufreq/cpufreq-nforce2.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ static unsigned int nforce2_fsb_read(int bootfsb)
145145
pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);
146146
fsb /= 1000000;
147147

148+
pci_dev_put(nforce2_sub5);
149+
148150
/* Check if PLL register is already set */
149151
pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
150152

@@ -426,6 +428,7 @@ static int __init nforce2_init(void)
426428
static void __exit nforce2_exit(void)
427429
{
428430
cpufreq_unregister_driver(&nforce2_driver);
431+
pci_dev_put(nforce2_dev);
429432
}
430433

431434
module_init(nforce2_init);

drivers/cpufreq/qcom-cpufreq-nvmem.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,13 +256,22 @@ static int qcom_cpufreq_krait_name_version(struct device *cpu_dev,
256256
return ret;
257257
}
258258

259+
static const struct of_device_id qcom_cpufreq_ipq806x_match_list[] __maybe_unused = {
260+
{ .compatible = "qcom,ipq8062", .data = (const void *)QCOM_ID_IPQ8062 },
261+
{ .compatible = "qcom,ipq8064", .data = (const void *)QCOM_ID_IPQ8064 },
262+
{ .compatible = "qcom,ipq8065", .data = (const void *)QCOM_ID_IPQ8065 },
263+
{ .compatible = "qcom,ipq8066", .data = (const void *)QCOM_ID_IPQ8066 },
264+
{ .compatible = "qcom,ipq8068", .data = (const void *)QCOM_ID_IPQ8068 },
265+
{ .compatible = "qcom,ipq8069", .data = (const void *)QCOM_ID_IPQ8069 },
266+
};
267+
259268
static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev,
260269
struct nvmem_cell *speedbin_nvmem,
261270
char **pvs_name,
262271
struct qcom_cpufreq_drv *drv)
263272
{
273+
int msm_id = -1, ret = 0;
264274
int speed = 0, pvs = 0;
265-
int msm_id, ret = 0;
266275
u8 *speedbin;
267276
size_t len;
268277

@@ -279,8 +288,30 @@ static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev,
279288
get_krait_bin_format_a(cpu_dev, &speed, &pvs, speedbin);
280289

281290
ret = qcom_smem_get_soc_id(&msm_id);
282-
if (ret)
291+
if (ret == -ENODEV) {
292+
const struct of_device_id *match;
293+
struct device_node *root;
294+
295+
root = of_find_node_by_path("/");
296+
if (!root) {
297+
ret = -ENODEV;
298+
goto exit;
299+
}
300+
301+
/* Fallback to compatible match with no SMEM initialized */
302+
match = of_match_node(qcom_cpufreq_ipq806x_match_list, root);
303+
of_node_put(root);
304+
if (!match) {
305+
ret = -ENODEV;
306+
goto exit;
307+
}
308+
309+
/* We found a matching device, get the msm_id from the data entry */
310+
msm_id = (int)(uintptr_t)match->data;
311+
ret = 0;
312+
} else if (ret) {
283313
goto exit;
314+
}
284315

285316
switch (msm_id) {
286317
case QCOM_ID_IPQ8062:

drivers/cpufreq/s5pv210-cpufreq.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
518518

519519
if (policy->cpu != 0) {
520520
ret = -EINVAL;
521-
goto out_dmc1;
521+
goto out;
522522
}
523523

524524
/*
@@ -530,7 +530,7 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
530530
if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
531531
pr_err("CPUFreq doesn't support this memory type\n");
532532
ret = -EINVAL;
533-
goto out_dmc1;
533+
goto out;
534534
}
535535

536536
/* Find current refresh counter and frequency each DMC */
@@ -544,6 +544,8 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
544544
cpufreq_generic_init(policy, s5pv210_freq_table, 40000);
545545
return 0;
546546

547+
out:
548+
clk_put(dmc1_clk);
547549
out_dmc1:
548550
clk_put(dmc0_clk);
549551
out_dmc0:

drivers/cpufreq/tegra186-cpufreq.c

Lines changed: 143 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/module.h>
99
#include <linux/of.h>
1010
#include <linux/platform_device.h>
11+
#include <linux/units.h>
1112

1213
#include <soc/tegra/bpmp.h>
1314
#include <soc/tegra/bpmp-abi.h>
@@ -58,24 +59,127 @@ static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {
5859
};
5960

6061
struct tegra186_cpufreq_cluster {
61-
struct cpufreq_frequency_table *table;
62+
struct cpufreq_frequency_table *bpmp_lut;
6263
u32 ref_clk_khz;
6364
u32 div;
6465
};
6566

6667
struct tegra186_cpufreq_data {
6768
void __iomem *regs;
6869
const struct tegra186_cpufreq_cpu *cpus;
70+
bool icc_dram_bw_scaling;
6971
struct tegra186_cpufreq_cluster clusters[];
7072
};
7173

74+
static int tegra_cpufreq_set_bw(struct cpufreq_policy *policy, unsigned long freq_khz)
75+
{
76+
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
77+
struct device *dev;
78+
int ret;
79+
80+
dev = get_cpu_device(policy->cpu);
81+
if (!dev)
82+
return -ENODEV;
83+
84+
struct dev_pm_opp *opp __free(put_opp) =
85+
dev_pm_opp_find_freq_exact(dev, freq_khz * HZ_PER_KHZ, true);
86+
if (IS_ERR(opp))
87+
return PTR_ERR(opp);
88+
89+
ret = dev_pm_opp_set_opp(dev, opp);
90+
if (ret)
91+
data->icc_dram_bw_scaling = false;
92+
93+
return ret;
94+
}
95+
96+
static int tegra_cpufreq_init_cpufreq_table(struct cpufreq_policy *policy,
97+
struct cpufreq_frequency_table *bpmp_lut,
98+
struct cpufreq_frequency_table **opp_table)
99+
{
100+
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
101+
struct cpufreq_frequency_table *freq_table = NULL;
102+
struct cpufreq_frequency_table *pos;
103+
struct device *cpu_dev;
104+
unsigned long rate;
105+
int ret, max_opps;
106+
int j = 0;
107+
108+
cpu_dev = get_cpu_device(policy->cpu);
109+
if (!cpu_dev) {
110+
pr_err("%s: failed to get cpu%d device\n", __func__, policy->cpu);
111+
return -ENODEV;
112+
}
113+
114+
/* Initialize OPP table mentioned in operating-points-v2 property in DT */
115+
ret = dev_pm_opp_of_add_table_indexed(cpu_dev, 0);
116+
if (ret) {
117+
dev_err(cpu_dev, "Invalid or empty opp table in device tree\n");
118+
data->icc_dram_bw_scaling = false;
119+
return ret;
120+
}
121+
122+
max_opps = dev_pm_opp_get_opp_count(cpu_dev);
123+
if (max_opps <= 0) {
124+
dev_err(cpu_dev, "Failed to add OPPs\n");
125+
return max_opps;
126+
}
127+
128+
/* Disable all opps and cross-validate against LUT later */
129+
for (rate = 0; ; rate++) {
130+
struct dev_pm_opp *opp __free(put_opp) =
131+
dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
132+
if (IS_ERR(opp))
133+
break;
134+
135+
dev_pm_opp_disable(cpu_dev, rate);
136+
}
137+
138+
freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_KERNEL);
139+
if (!freq_table)
140+
return -ENOMEM;
141+
142+
/*
143+
* Cross check the frequencies from BPMP-FW LUT against the OPP's present in DT.
144+
* Enable only those DT OPP's which are present in LUT also.
145+
*/
146+
cpufreq_for_each_valid_entry(pos, bpmp_lut) {
147+
struct dev_pm_opp *opp __free(put_opp) =
148+
dev_pm_opp_find_freq_exact(cpu_dev, pos->frequency * HZ_PER_KHZ, false);
149+
if (IS_ERR(opp))
150+
continue;
151+
152+
ret = dev_pm_opp_enable(cpu_dev, pos->frequency * HZ_PER_KHZ);
153+
if (ret < 0)
154+
return ret;
155+
156+
freq_table[j].driver_data = pos->driver_data;
157+
freq_table[j].frequency = pos->frequency;
158+
j++;
159+
}
160+
161+
freq_table[j].driver_data = pos->driver_data;
162+
freq_table[j].frequency = CPUFREQ_TABLE_END;
163+
164+
*opp_table = &freq_table[0];
165+
166+
dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
167+
168+
/* Prime interconnect data */
169+
tegra_cpufreq_set_bw(policy, freq_table[j - 1].frequency);
170+
171+
return ret;
172+
}
173+
72174
static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
73175
{
74176
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
75177
unsigned int cluster = data->cpus[policy->cpu].bpmp_cluster_id;
178+
struct cpufreq_frequency_table *freq_table;
179+
struct cpufreq_frequency_table *bpmp_lut;
76180
u32 cpu;
181+
int ret;
77182

78-
policy->freq_table = data->clusters[cluster].table;
79183
policy->cpuinfo.transition_latency = 300 * 1000;
80184
policy->driver_data = NULL;
81185

@@ -85,6 +189,20 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
85189
cpumask_set_cpu(cpu, policy->cpus);
86190
}
87191

192+
bpmp_lut = data->clusters[cluster].bpmp_lut;
193+
194+
if (data->icc_dram_bw_scaling) {
195+
ret = tegra_cpufreq_init_cpufreq_table(policy, bpmp_lut, &freq_table);
196+
if (!ret) {
197+
policy->freq_table = freq_table;
198+
return 0;
199+
}
200+
}
201+
202+
data->icc_dram_bw_scaling = false;
203+
policy->freq_table = bpmp_lut;
204+
pr_info("OPP tables missing from DT, EMC frequency scaling disabled\n");
205+
88206
return 0;
89207
}
90208

@@ -102,6 +220,10 @@ static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
102220
writel(edvd_val, data->regs + edvd_offset);
103221
}
104222

223+
if (data->icc_dram_bw_scaling)
224+
tegra_cpufreq_set_bw(policy, tbl->frequency);
225+
226+
105227
return 0;
106228
}
107229

@@ -134,7 +256,7 @@ static struct cpufreq_driver tegra186_cpufreq_driver = {
134256
.init = tegra186_cpufreq_init,
135257
};
136258

137-
static struct cpufreq_frequency_table *init_vhint_table(
259+
static struct cpufreq_frequency_table *tegra_cpufreq_bpmp_read_lut(
138260
struct platform_device *pdev, struct tegra_bpmp *bpmp,
139261
struct tegra186_cpufreq_cluster *cluster, unsigned int cluster_id,
140262
int *num_rates)
@@ -229,6 +351,7 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
229351
{
230352
struct tegra186_cpufreq_data *data;
231353
struct tegra_bpmp *bpmp;
354+
struct device *cpu_dev;
232355
unsigned int i = 0, err, edvd_offset;
233356
int num_rates = 0;
234357
u32 edvd_val, cpu;
@@ -254,9 +377,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
254377
for (i = 0; i < TEGRA186_NUM_CLUSTERS; i++) {
255378
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
256379

257-
cluster->table = init_vhint_table(pdev, bpmp, cluster, i, &num_rates);
258-
if (IS_ERR(cluster->table)) {
259-
err = PTR_ERR(cluster->table);
380+
cluster->bpmp_lut = tegra_cpufreq_bpmp_read_lut(pdev, bpmp, cluster, i, &num_rates);
381+
if (IS_ERR(cluster->bpmp_lut)) {
382+
err = PTR_ERR(cluster->bpmp_lut);
260383
goto put_bpmp;
261384
} else if (!num_rates) {
262385
err = -EINVAL;
@@ -265,7 +388,7 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
265388

266389
for (cpu = 0; cpu < ARRAY_SIZE(tegra186_cpus); cpu++) {
267390
if (data->cpus[cpu].bpmp_cluster_id == i) {
268-
edvd_val = cluster->table[num_rates - 1].driver_data;
391+
edvd_val = cluster->bpmp_lut[num_rates - 1].driver_data;
269392
edvd_offset = data->cpus[cpu].edvd_offset;
270393
writel(edvd_val, data->regs + edvd_offset);
271394
}
@@ -274,6 +397,19 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
274397

275398
tegra186_cpufreq_driver.driver_data = data;
276399

400+
/* Check for optional OPPv2 and interconnect paths on CPU0 to enable ICC scaling */
401+
cpu_dev = get_cpu_device(0);
402+
if (!cpu_dev) {
403+
err = -EPROBE_DEFER;
404+
goto put_bpmp;
405+
}
406+
407+
if (dev_pm_opp_of_get_opp_desc_node(cpu_dev)) {
408+
err = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
409+
if (!err)
410+
data->icc_dram_bw_scaling = true;
411+
}
412+
277413
err = cpufreq_register_driver(&tegra186_cpufreq_driver);
278414

279415
put_bpmp:

drivers/cpufreq/tegra194-cpufreq.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,8 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
750750
if (IS_ERR(bpmp))
751751
return PTR_ERR(bpmp);
752752

753-
read_counters_wq = alloc_workqueue("read_counters_wq", __WQ_LEGACY, 1);
753+
read_counters_wq = alloc_workqueue("read_counters_wq",
754+
__WQ_LEGACY | WQ_PERCPU, 1);
754755
if (!read_counters_wq) {
755756
dev_err(&pdev->dev, "fail to create_workqueue\n");
756757
err = -EINVAL;

0 commit comments

Comments
 (0)