.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or modify it |
---|
5 | | - * under the terms and conditions of the GNU General Public License, |
---|
6 | | - * version 2, as published by the Free Software Foundation. |
---|
7 | | - * |
---|
8 | | - * This program is distributed in the hope it will be useful, but WITHOUT |
---|
9 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
---|
10 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
---|
11 | | - * more details. |
---|
12 | 4 | */ |
---|
13 | 5 | |
---|
14 | 6 | #include <linux/cpufreq.h> |
---|
.. | .. |
---|
22 | 14 | |
---|
23 | 15 | #define EDVD_CORE_VOLT_FREQ(core) (0x20 + (core) * 0x4) |
---|
24 | 16 | #define EDVD_CORE_VOLT_FREQ_F_SHIFT 0 |
---|
| 17 | +#define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff |
---|
25 | 18 | #define EDVD_CORE_VOLT_FREQ_V_SHIFT 16 |
---|
26 | 19 | |
---|
27 | 20 | struct tegra186_cpufreq_cluster_info { |
---|
.. | .. |
---|
49 | 42 | struct tegra186_cpufreq_cluster { |
---|
50 | 43 | const struct tegra186_cpufreq_cluster_info *info; |
---|
51 | 44 | struct cpufreq_frequency_table *table; |
---|
| 45 | + u32 ref_clk_khz; |
---|
| 46 | + u32 div; |
---|
52 | 47 | }; |
---|
53 | 48 | |
---|
54 | 49 | struct tegra186_cpufreq_data { |
---|
.. | .. |
---|
99 | 94 | return 0; |
---|
100 | 95 | } |
---|
101 | 96 | |
---|
| 97 | +static unsigned int tegra186_cpufreq_get(unsigned int cpu) |
---|
| 98 | +{ |
---|
| 99 | + struct tegra186_cpufreq_data *data = cpufreq_get_driver_data(); |
---|
| 100 | + struct cpufreq_policy *policy; |
---|
| 101 | + void __iomem *edvd_reg; |
---|
| 102 | + unsigned int i, freq = 0; |
---|
| 103 | + u32 ndiv; |
---|
| 104 | + |
---|
| 105 | + policy = cpufreq_cpu_get(cpu); |
---|
| 106 | + if (!policy) |
---|
| 107 | + return 0; |
---|
| 108 | + |
---|
| 109 | + edvd_reg = policy->driver_data; |
---|
| 110 | + ndiv = readl(edvd_reg) & EDVD_CORE_VOLT_FREQ_F_MASK; |
---|
| 111 | + |
---|
| 112 | + for (i = 0; i < data->num_clusters; i++) { |
---|
| 113 | + struct tegra186_cpufreq_cluster *cluster = &data->clusters[i]; |
---|
| 114 | + int core; |
---|
| 115 | + |
---|
| 116 | + for (core = 0; core < ARRAY_SIZE(cluster->info->cpus); core++) { |
---|
| 117 | + if (cluster->info->cpus[core] != policy->cpu) |
---|
| 118 | + continue; |
---|
| 119 | + |
---|
| 120 | + freq = (cluster->ref_clk_khz * ndiv) / cluster->div; |
---|
| 121 | + goto out; |
---|
| 122 | + } |
---|
| 123 | + } |
---|
| 124 | + |
---|
| 125 | +out: |
---|
| 126 | + cpufreq_cpu_put(policy); |
---|
| 127 | + |
---|
| 128 | + return freq; |
---|
| 129 | +} |
---|
| 130 | + |
---|
102 | 131 | static struct cpufreq_driver tegra186_cpufreq_driver = { |
---|
103 | 132 | .name = "tegra186", |
---|
104 | | - .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY, |
---|
| 133 | + .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | |
---|
| 134 | + CPUFREQ_NEED_INITIAL_FREQ_CHECK, |
---|
| 135 | + .get = tegra186_cpufreq_get, |
---|
105 | 136 | .verify = cpufreq_generic_frequency_table_verify, |
---|
106 | 137 | .target_index = tegra186_cpufreq_set_target, |
---|
107 | 138 | .init = tegra186_cpufreq_init, |
---|
.. | .. |
---|
110 | 141 | |
---|
111 | 142 | static struct cpufreq_frequency_table *init_vhint_table( |
---|
112 | 143 | struct platform_device *pdev, struct tegra_bpmp *bpmp, |
---|
113 | | - unsigned int cluster_id) |
---|
| 144 | + struct tegra186_cpufreq_cluster *cluster) |
---|
114 | 145 | { |
---|
115 | 146 | struct cpufreq_frequency_table *table; |
---|
116 | 147 | struct mrq_cpu_vhint_request req; |
---|
.. | .. |
---|
121 | 152 | void *virt; |
---|
122 | 153 | |
---|
123 | 154 | virt = dma_alloc_coherent(bpmp->dev, sizeof(*data), &phys, |
---|
124 | | - GFP_KERNEL | GFP_DMA32); |
---|
| 155 | + GFP_KERNEL); |
---|
125 | 156 | if (!virt) |
---|
126 | 157 | return ERR_PTR(-ENOMEM); |
---|
127 | 158 | |
---|
.. | .. |
---|
129 | 160 | |
---|
130 | 161 | memset(&req, 0, sizeof(req)); |
---|
131 | 162 | req.addr = phys; |
---|
132 | | - req.cluster_id = cluster_id; |
---|
| 163 | + req.cluster_id = cluster->info->bpmp_cluster_id; |
---|
133 | 164 | |
---|
134 | 165 | memset(&msg, 0, sizeof(msg)); |
---|
135 | 166 | msg.mrq = MRQ_CPU_VHINT; |
---|
.. | .. |
---|
162 | 193 | goto free; |
---|
163 | 194 | } |
---|
164 | 195 | |
---|
| 196 | + cluster->ref_clk_khz = data->ref_clk_hz / 1000; |
---|
| 197 | + cluster->div = data->pdiv * data->mdiv; |
---|
| 198 | + |
---|
165 | 199 | for (i = data->vfloor, j = 0; i <= data->vceil; i++) { |
---|
166 | 200 | struct cpufreq_frequency_table *point; |
---|
167 | 201 | u16 ndiv = data->ndiv[i]; |
---|
.. | .. |
---|
179 | 213 | |
---|
180 | 214 | point = &table[j++]; |
---|
181 | 215 | point->driver_data = edvd_val; |
---|
182 | | - point->frequency = data->ref_clk_hz * ndiv / data->pdiv / |
---|
183 | | - data->mdiv / 1000; |
---|
| 216 | + point->frequency = (cluster->ref_clk_khz * ndiv) / cluster->div; |
---|
184 | 217 | } |
---|
185 | 218 | |
---|
186 | 219 | table[j].frequency = CPUFREQ_TABLE_END; |
---|
.. | .. |
---|
195 | 228 | { |
---|
196 | 229 | struct tegra186_cpufreq_data *data; |
---|
197 | 230 | struct tegra_bpmp *bpmp; |
---|
198 | | - struct resource *res; |
---|
199 | 231 | unsigned int i = 0, err; |
---|
200 | 232 | |
---|
201 | 233 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
---|
.. | .. |
---|
213 | 245 | if (IS_ERR(bpmp)) |
---|
214 | 246 | return PTR_ERR(bpmp); |
---|
215 | 247 | |
---|
216 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
217 | | - data->regs = devm_ioremap_resource(&pdev->dev, res); |
---|
| 248 | + data->regs = devm_platform_ioremap_resource(pdev, 0); |
---|
218 | 249 | if (IS_ERR(data->regs)) { |
---|
219 | 250 | err = PTR_ERR(data->regs); |
---|
220 | 251 | goto put_bpmp; |
---|
.. | .. |
---|
224 | 255 | struct tegra186_cpufreq_cluster *cluster = &data->clusters[i]; |
---|
225 | 256 | |
---|
226 | 257 | cluster->info = &tegra186_clusters[i]; |
---|
227 | | - cluster->table = init_vhint_table( |
---|
228 | | - pdev, bpmp, cluster->info->bpmp_cluster_id); |
---|
| 258 | + cluster->table = init_vhint_table(pdev, bpmp, cluster); |
---|
229 | 259 | if (IS_ERR(cluster->table)) { |
---|
230 | 260 | err = PTR_ERR(cluster->table); |
---|
231 | 261 | goto put_bpmp; |
---|
232 | 262 | } |
---|
233 | 263 | } |
---|
234 | 264 | |
---|
235 | | - tegra_bpmp_put(bpmp); |
---|
236 | | - |
---|
237 | 265 | tegra186_cpufreq_driver.driver_data = data; |
---|
238 | 266 | |
---|
239 | 267 | err = cpufreq_register_driver(&tegra186_cpufreq_driver); |
---|
240 | | - if (err) |
---|
241 | | - return err; |
---|
242 | | - |
---|
243 | | - return 0; |
---|
244 | 268 | |
---|
245 | 269 | put_bpmp: |
---|
246 | 270 | tegra_bpmp_put(bpmp); |
---|