GNU Linux-libre 5.10.153-gnu1
[releases.git] / drivers / cpufreq / tegra186-cpufreq.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved
4  */
5
6 #include <linux/cpufreq.h>
7 #include <linux/dma-mapping.h>
8 #include <linux/module.h>
9 #include <linux/of.h>
10 #include <linux/platform_device.h>
11
12 #include <soc/tegra/bpmp.h>
13 #include <soc/tegra/bpmp-abi.h>
14
15 #define EDVD_CORE_VOLT_FREQ(core)               (0x20 + (core) * 0x4)
16 #define EDVD_CORE_VOLT_FREQ_F_SHIFT             0
17 #define EDVD_CORE_VOLT_FREQ_F_MASK              0xffff
18 #define EDVD_CORE_VOLT_FREQ_V_SHIFT             16
19
20 struct tegra186_cpufreq_cluster_info {
21         unsigned long offset;
22         int cpus[4];
23         unsigned int bpmp_cluster_id;
24 };
25
26 #define NO_CPU -1
27 static const struct tegra186_cpufreq_cluster_info tegra186_clusters[] = {
28         /* Denver cluster */
29         {
30                 .offset = SZ_64K * 7,
31                 .cpus = { 1, 2, NO_CPU, NO_CPU },
32                 .bpmp_cluster_id = 0,
33         },
34         /* A57 cluster */
35         {
36                 .offset = SZ_64K * 6,
37                 .cpus = { 0, 3, 4, 5 },
38                 .bpmp_cluster_id = 1,
39         },
40 };
41
42 struct tegra186_cpufreq_cluster {
43         const struct tegra186_cpufreq_cluster_info *info;
44         struct cpufreq_frequency_table *table;
45         u32 ref_clk_khz;
46         u32 div;
47 };
48
49 struct tegra186_cpufreq_data {
50         void __iomem *regs;
51
52         size_t num_clusters;
53         struct tegra186_cpufreq_cluster *clusters;
54 };
55
56 static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
57 {
58         struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
59         unsigned int i;
60
61         for (i = 0; i < data->num_clusters; i++) {
62                 struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
63                 const struct tegra186_cpufreq_cluster_info *info =
64                         cluster->info;
65                 int core;
66
67                 for (core = 0; core < ARRAY_SIZE(info->cpus); core++) {
68                         if (info->cpus[core] == policy->cpu)
69                                 break;
70                 }
71                 if (core == ARRAY_SIZE(info->cpus))
72                         continue;
73
74                 policy->driver_data =
75                         data->regs + info->offset + EDVD_CORE_VOLT_FREQ(core);
76                 policy->freq_table = cluster->table;
77                 break;
78         }
79
80         policy->cpuinfo.transition_latency = 300 * 1000;
81
82         return 0;
83 }
84
85 static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
86                                        unsigned int index)
87 {
88         struct cpufreq_frequency_table *tbl = policy->freq_table + index;
89         void __iomem *edvd_reg = policy->driver_data;
90         u32 edvd_val = tbl->driver_data;
91
92         writel(edvd_val, edvd_reg);
93
94         return 0;
95 }
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
131 static struct cpufreq_driver tegra186_cpufreq_driver = {
132         .name = "tegra186",
133         .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
134                         CPUFREQ_NEED_INITIAL_FREQ_CHECK,
135         .get = tegra186_cpufreq_get,
136         .verify = cpufreq_generic_frequency_table_verify,
137         .target_index = tegra186_cpufreq_set_target,
138         .init = tegra186_cpufreq_init,
139         .attr = cpufreq_generic_attr,
140 };
141
142 static struct cpufreq_frequency_table *init_vhint_table(
143         struct platform_device *pdev, struct tegra_bpmp *bpmp,
144         struct tegra186_cpufreq_cluster *cluster)
145 {
146         struct cpufreq_frequency_table *table;
147         struct mrq_cpu_vhint_request req;
148         struct tegra_bpmp_message msg;
149         struct cpu_vhint_data *data;
150         int err, i, j, num_rates = 0;
151         dma_addr_t phys;
152         void *virt;
153
154         virt = dma_alloc_coherent(bpmp->dev, sizeof(*data), &phys,
155                                   GFP_KERNEL);
156         if (!virt)
157                 return ERR_PTR(-ENOMEM);
158
159         data = (struct cpu_vhint_data *)virt;
160
161         memset(&req, 0, sizeof(req));
162         req.addr = phys;
163         req.cluster_id = cluster->info->bpmp_cluster_id;
164
165         memset(&msg, 0, sizeof(msg));
166         msg.mrq = MRQ_CPU_VHINT;
167         msg.tx.data = &req;
168         msg.tx.size = sizeof(req);
169
170         err = tegra_bpmp_transfer(bpmp, &msg);
171         if (err) {
172                 table = ERR_PTR(err);
173                 goto free;
174         }
175
176         for (i = data->vfloor; i <= data->vceil; i++) {
177                 u16 ndiv = data->ndiv[i];
178
179                 if (ndiv < data->ndiv_min || ndiv > data->ndiv_max)
180                         continue;
181
182                 /* Only store lowest voltage index for each rate */
183                 if (i > 0 && ndiv == data->ndiv[i - 1])
184                         continue;
185
186                 num_rates++;
187         }
188
189         table = devm_kcalloc(&pdev->dev, num_rates + 1, sizeof(*table),
190                              GFP_KERNEL);
191         if (!table) {
192                 table = ERR_PTR(-ENOMEM);
193                 goto free;
194         }
195
196         cluster->ref_clk_khz = data->ref_clk_hz / 1000;
197         cluster->div = data->pdiv * data->mdiv;
198
199         for (i = data->vfloor, j = 0; i <= data->vceil; i++) {
200                 struct cpufreq_frequency_table *point;
201                 u16 ndiv = data->ndiv[i];
202                 u32 edvd_val = 0;
203
204                 if (ndiv < data->ndiv_min || ndiv > data->ndiv_max)
205                         continue;
206
207                 /* Only store lowest voltage index for each rate */
208                 if (i > 0 && ndiv == data->ndiv[i - 1])
209                         continue;
210
211                 edvd_val |= i << EDVD_CORE_VOLT_FREQ_V_SHIFT;
212                 edvd_val |= ndiv << EDVD_CORE_VOLT_FREQ_F_SHIFT;
213
214                 point = &table[j++];
215                 point->driver_data = edvd_val;
216                 point->frequency = (cluster->ref_clk_khz * ndiv) / cluster->div;
217         }
218
219         table[j].frequency = CPUFREQ_TABLE_END;
220
221 free:
222         dma_free_coherent(bpmp->dev, sizeof(*data), virt, phys);
223
224         return table;
225 }
226
227 static int tegra186_cpufreq_probe(struct platform_device *pdev)
228 {
229         struct tegra186_cpufreq_data *data;
230         struct tegra_bpmp *bpmp;
231         unsigned int i = 0, err;
232
233         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
234         if (!data)
235                 return -ENOMEM;
236
237         data->clusters = devm_kcalloc(&pdev->dev, ARRAY_SIZE(tegra186_clusters),
238                                       sizeof(*data->clusters), GFP_KERNEL);
239         if (!data->clusters)
240                 return -ENOMEM;
241
242         data->num_clusters = ARRAY_SIZE(tegra186_clusters);
243
244         bpmp = tegra_bpmp_get(&pdev->dev);
245         if (IS_ERR(bpmp))
246                 return PTR_ERR(bpmp);
247
248         data->regs = devm_platform_ioremap_resource(pdev, 0);
249         if (IS_ERR(data->regs)) {
250                 err = PTR_ERR(data->regs);
251                 goto put_bpmp;
252         }
253
254         for (i = 0; i < data->num_clusters; i++) {
255                 struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
256
257                 cluster->info = &tegra186_clusters[i];
258                 cluster->table = init_vhint_table(pdev, bpmp, cluster);
259                 if (IS_ERR(cluster->table)) {
260                         err = PTR_ERR(cluster->table);
261                         goto put_bpmp;
262                 }
263         }
264
265         tegra186_cpufreq_driver.driver_data = data;
266
267         err = cpufreq_register_driver(&tegra186_cpufreq_driver);
268
269 put_bpmp:
270         tegra_bpmp_put(bpmp);
271
272         return err;
273 }
274
275 static int tegra186_cpufreq_remove(struct platform_device *pdev)
276 {
277         cpufreq_unregister_driver(&tegra186_cpufreq_driver);
278
279         return 0;
280 }
281
282 static const struct of_device_id tegra186_cpufreq_of_match[] = {
283         { .compatible = "nvidia,tegra186-ccplex-cluster", },
284         { }
285 };
286 MODULE_DEVICE_TABLE(of, tegra186_cpufreq_of_match);
287
288 static struct platform_driver tegra186_cpufreq_platform_driver = {
289         .driver = {
290                 .name = "tegra186-cpufreq",
291                 .of_match_table = tegra186_cpufreq_of_match,
292         },
293         .probe = tegra186_cpufreq_probe,
294         .remove = tegra186_cpufreq_remove,
295 };
296 module_platform_driver(tegra186_cpufreq_platform_driver);
297
298 MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
299 MODULE_DESCRIPTION("NVIDIA Tegra186 cpufreq driver");
300 MODULE_LICENSE("GPL v2");