| .. | .. |
|---|
| 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); |
|---|