Skip to content

Commit 85976d3

Browse files
webgeek1234vireshk
authored andcommitted
cpufreq: tegra186: add OPP support and set bandwidth
Add support to use OPP table from DT in Tegra186 cpufreq driver. Tegra SoC's receive the frequency lookup table (LUT) from BPMP-FW. Cross check the OPP's present in DT against the LUT from BPMP-FW and enable only those DT OPP's which are present in LUT also. The OPP table in DT has CPU Frequency to bandwidth mapping where the bandwidth value is per MC channel. DRAM bandwidth depends on the number of MC channels which can vary as per the boot configuration. This per channel bandwidth from OPP table will be later converted by MC driver to final bandwidth value by multiplying with number of channels before being handled in the EMC driver. If OPP table is not present in DT, then use the LUT from BPMP-FW directly as the CPU frequency table and not do the DRAM frequency scaling which is same as the current behavior. Signed-off-by: Aaron Kling <webgeek1234@gmail.com> [ Viresh: Fix _free() definitions ] Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
1 parent 6e7970c commit 85976d3

1 file changed

Lines changed: 143 additions & 7 deletions

File tree

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:

0 commit comments

Comments
 (0)