1*6e54367aSthorpej /* $NetBSD: tegra124_cpu.c,v 1.6 2021/01/27 03:10:19 thorpej Exp $ */
2c2239868Sjmcneill
3c2239868Sjmcneill /*-
4c2239868Sjmcneill * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5c2239868Sjmcneill * All rights reserved.
6c2239868Sjmcneill *
7c2239868Sjmcneill * Redistribution and use in source and binary forms, with or without
8c2239868Sjmcneill * modification, are permitted provided that the following conditions
9c2239868Sjmcneill * are met:
10c2239868Sjmcneill * 1. Redistributions of source code must retain the above copyright
11c2239868Sjmcneill * notice, this list of conditions and the following disclaimer.
12c2239868Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13c2239868Sjmcneill * notice, this list of conditions and the following disclaimer in the
14c2239868Sjmcneill * documentation and/or other materials provided with the distribution.
15c2239868Sjmcneill *
16c2239868Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17c2239868Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18c2239868Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19c2239868Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20c2239868Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21c2239868Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22c2239868Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23c2239868Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24c2239868Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c2239868Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c2239868Sjmcneill * SUCH DAMAGE.
27c2239868Sjmcneill */
28c2239868Sjmcneill
29c2239868Sjmcneill #include "opt_tegra.h"
30c2239868Sjmcneill #include "opt_multiprocessor.h"
31c2239868Sjmcneill
32c2239868Sjmcneill #include <sys/cdefs.h>
33*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: tegra124_cpu.c,v 1.6 2021/01/27 03:10:19 thorpej Exp $");
34c2239868Sjmcneill
35c2239868Sjmcneill #include <sys/param.h>
36c2239868Sjmcneill #include <sys/bus.h>
37c2239868Sjmcneill #include <sys/cpu.h>
38c2239868Sjmcneill #include <sys/device.h>
39c2239868Sjmcneill
40c2239868Sjmcneill #include <uvm/uvm_extern.h>
41c2239868Sjmcneill
42c2239868Sjmcneill #include <dev/fdt/fdtvar.h>
43c2239868Sjmcneill
44c2239868Sjmcneill #include <arm/cpufunc.h>
45c2239868Sjmcneill
46c2239868Sjmcneill #include <arm/nvidia/tegra_reg.h>
47c2239868Sjmcneill #include <arm/nvidia/tegra_pmcreg.h>
48c2239868Sjmcneill #include <arm/nvidia/tegra_var.h>
49c2239868Sjmcneill
50c2239868Sjmcneill
51c2239868Sjmcneill #define FUSE_SKU_INFO_REG 0x010
52c2239868Sjmcneill #define FUSE_CPU_SPEEDO_0_REG 0x014
53c2239868Sjmcneill #define FUSE_CPU_IDDQ_REG 0x018
54c2239868Sjmcneill #define FUSE_FT_REV_REG 0x028
55c2239868Sjmcneill #define FUSE_CPU_SPEEDO_1_REG 0x02c
56c2239868Sjmcneill #define FUSE_CPU_SPEEDO_2_REG 0x030
57c2239868Sjmcneill #define FUSE_SOC_SPEEDO_0_REG 0x034
58c2239868Sjmcneill #define FUSE_SOC_SPEEDO_1_REG 0x038
59c2239868Sjmcneill #define FUSE_SOC_SPEEDO_2_REG 0x03c
60c2239868Sjmcneill #define FUSE_SOC_IDDQ_REG 0x040
61c2239868Sjmcneill #define FUSE_GPU_IDDQ_REG 0x128
62c2239868Sjmcneill
63c2239868Sjmcneill static void tegra124_speedo_init(void);
64c2239868Sjmcneill static int tegra124_speedo_init_ids(uint32_t);
65c2239868Sjmcneill static bool tegra124_speedo_rate_ok(u_int);
66c2239868Sjmcneill
67c2239868Sjmcneill static u_int tegra124_cpufreq_set_rate(u_int);
68c2239868Sjmcneill static u_int tegra124_cpufreq_get_rate(void);
69c2239868Sjmcneill static size_t tegra124_cpufreq_get_available(u_int *, size_t);
70c2239868Sjmcneill
71c2239868Sjmcneill static int tegra124_cpu_match(device_t, cfdata_t, void *);
72c2239868Sjmcneill static void tegra124_cpu_attach(device_t, device_t, void *);
73b7a940d9Sjmcneill static int tegra124_cpu_init_cpufreq(device_t);
74c2239868Sjmcneill
75c2239868Sjmcneill CFATTACH_DECL_NEW(tegra124_cpu, 0, tegra124_cpu_match, tegra124_cpu_attach,
76c2239868Sjmcneill NULL, NULL);
77c2239868Sjmcneill
78c2239868Sjmcneill static const struct tegra_cpufreq_func tegra124_cpufreq_func = {
79c2239868Sjmcneill .set_rate = tegra124_cpufreq_set_rate,
80c2239868Sjmcneill .get_rate = tegra124_cpufreq_get_rate,
81c2239868Sjmcneill .get_available = tegra124_cpufreq_get_available,
82c2239868Sjmcneill };
83c2239868Sjmcneill
84c2239868Sjmcneill static struct tegra124_cpufreq_rate {
85c2239868Sjmcneill u_int rate;
86c2239868Sjmcneill u_int divm;
87c2239868Sjmcneill u_int divn;
88c2239868Sjmcneill u_int divp;
89c2239868Sjmcneill u_int uvol;
90c2239868Sjmcneill } tegra124_cpufreq_rates[] = {
910474988eSjmcneill { 2316, 1, 193, 0, 1360000 },
920474988eSjmcneill { 2100, 1, 175, 0, 1260000 },
930474988eSjmcneill { 1896, 1, 158, 0, 1180000 },
940474988eSjmcneill { 1692, 1, 141, 0, 1100000 },
950474988eSjmcneill { 1500, 1, 125, 0, 1020000 },
960474988eSjmcneill { 1296, 1, 108, 0, 960000 },
970474988eSjmcneill { 1092, 1, 91, 0, 900000 },
980474988eSjmcneill { 900, 1, 75, 0, 840000 },
990474988eSjmcneill { 696, 1, 58, 0, 800000 }
100c2239868Sjmcneill };
101c2239868Sjmcneill
102c2239868Sjmcneill static const u_int tegra124_cpufreq_max[] = {
103c2239868Sjmcneill 2014,
104c2239868Sjmcneill 2320,
105c2239868Sjmcneill 2116,
106c2239868Sjmcneill 2524
107c2239868Sjmcneill };
108c2239868Sjmcneill
109c2239868Sjmcneill static struct tegra124_speedo {
110c2239868Sjmcneill u_int cpu_speedo_id;
111c2239868Sjmcneill u_int soc_speedo_id;
112c2239868Sjmcneill u_int gpu_speedo_id;
113c2239868Sjmcneill } tegra124_speedo = {
114c2239868Sjmcneill .cpu_speedo_id = 0,
115c2239868Sjmcneill .soc_speedo_id = 0,
116c2239868Sjmcneill .gpu_speedo_id = 0
117c2239868Sjmcneill };
118c2239868Sjmcneill
119c2239868Sjmcneill static struct clk *tegra124_clk_pllx = NULL;
120c2239868Sjmcneill static struct fdtbus_regulator *tegra124_reg_vddcpu = NULL;
121c2239868Sjmcneill
122*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
123*6e54367aSthorpej { .compat = "nvidia,tegra124" },
124*6e54367aSthorpej DEVICE_COMPAT_EOL
125*6e54367aSthorpej };
126*6e54367aSthorpej
127c2239868Sjmcneill static int
tegra124_cpu_match(device_t parent,cfdata_t cf,void * aux)128c2239868Sjmcneill tegra124_cpu_match(device_t parent, cfdata_t cf, void *aux)
129c2239868Sjmcneill {
130c2239868Sjmcneill struct fdt_attach_args *faa = aux;
131c2239868Sjmcneill
132b7a940d9Sjmcneill if (OF_finddevice("/cpus/cpu@0") != faa->faa_phandle)
133c2239868Sjmcneill return 0;
134c2239868Sjmcneill
135*6e54367aSthorpej return of_compatible_match(OF_finddevice("/"), compat_data);
136c2239868Sjmcneill }
137c2239868Sjmcneill
138c2239868Sjmcneill static void
tegra124_cpu_attach(device_t parent,device_t self,void * aux)139c2239868Sjmcneill tegra124_cpu_attach(device_t parent, device_t self, void *aux)
140c2239868Sjmcneill {
141c2239868Sjmcneill aprint_naive("\n");
142b7a940d9Sjmcneill aprint_normal(": DVFS\n");
143c2239868Sjmcneill
144b7a940d9Sjmcneill config_finalize_register(self, tegra124_cpu_init_cpufreq);
145c2239868Sjmcneill }
146c2239868Sjmcneill
1478efa41a8Sskrll static bool tegra124_cpu_init_done = false;
1488efa41a8Sskrll
149b7a940d9Sjmcneill static int
tegra124_cpu_init_cpufreq(device_t dev)150c2239868Sjmcneill tegra124_cpu_init_cpufreq(device_t dev)
151c2239868Sjmcneill {
1528efa41a8Sskrll if (tegra124_cpu_init_done)
1538efa41a8Sskrll return 0;
1548efa41a8Sskrll
155c2239868Sjmcneill tegra124_speedo_init();
156c2239868Sjmcneill
157c2239868Sjmcneill int cpu_node = OF_finddevice("/cpus/cpu@0");
158c2239868Sjmcneill if (cpu_node != -1) {
159c2239868Sjmcneill tegra124_clk_pllx = fdtbus_clock_get(cpu_node, "pll_x");
160c2239868Sjmcneill tegra124_reg_vddcpu = fdtbus_regulator_acquire(cpu_node,
161c2239868Sjmcneill "vdd-cpu-supply");
162c2239868Sjmcneill }
163c2239868Sjmcneill if (tegra124_clk_pllx == NULL) {
164c2239868Sjmcneill aprint_error_dev(dev, "couldn't find clock pll_x\n");
165b7a940d9Sjmcneill return 0;
166c2239868Sjmcneill }
167c2239868Sjmcneill if (tegra124_reg_vddcpu == NULL) {
168c2239868Sjmcneill aprint_error_dev(dev, "couldn't find voltage regulator\n");
169b7a940d9Sjmcneill return 0;
170c2239868Sjmcneill }
171c2239868Sjmcneill
172c2239868Sjmcneill tegra_cpufreq_register(&tegra124_cpufreq_func);
173b7a940d9Sjmcneill
1748efa41a8Sskrll tegra124_cpu_init_done = true;
1758efa41a8Sskrll
176b7a940d9Sjmcneill return 0;
177c2239868Sjmcneill }
178c2239868Sjmcneill
179c2239868Sjmcneill static void
tegra124_speedo_init(void)180c2239868Sjmcneill tegra124_speedo_init(void)
181c2239868Sjmcneill {
182c2239868Sjmcneill uint32_t sku_id;
183c2239868Sjmcneill
184c2239868Sjmcneill sku_id = tegra_fuse_read(FUSE_SKU_INFO_REG);
185c2239868Sjmcneill tegra124_speedo_init_ids(sku_id);
186c2239868Sjmcneill }
187c2239868Sjmcneill
188c2239868Sjmcneill static int
tegra124_speedo_init_ids(uint32_t sku_id)189c2239868Sjmcneill tegra124_speedo_init_ids(uint32_t sku_id)
190c2239868Sjmcneill {
191c2239868Sjmcneill int threshold = 0;
192c2239868Sjmcneill
193c2239868Sjmcneill switch (sku_id) {
194c2239868Sjmcneill case 0x00:
195c2239868Sjmcneill case 0x0f:
196c2239868Sjmcneill case 0x23:
197c2239868Sjmcneill break; /* use default */
198c2239868Sjmcneill case 0x83:
199c2239868Sjmcneill tegra124_speedo.cpu_speedo_id = 2;
200c2239868Sjmcneill break;
201c2239868Sjmcneill case 0x1f:
202c2239868Sjmcneill case 0x87:
203c2239868Sjmcneill case 0x27:
204c2239868Sjmcneill tegra124_speedo.cpu_speedo_id = 2;
205c2239868Sjmcneill tegra124_speedo.soc_speedo_id = 0;
206c2239868Sjmcneill tegra124_speedo.gpu_speedo_id = 1;
207c2239868Sjmcneill break;
208c2239868Sjmcneill case 0x81:
209c2239868Sjmcneill case 0x21:
210c2239868Sjmcneill case 0x07:
211c2239868Sjmcneill tegra124_speedo.cpu_speedo_id = 1;
212c2239868Sjmcneill tegra124_speedo.soc_speedo_id = 1;
213c2239868Sjmcneill tegra124_speedo.gpu_speedo_id = 1;
214c2239868Sjmcneill threshold = 1;
215c2239868Sjmcneill break;
216c2239868Sjmcneill case 0x49:
217c2239868Sjmcneill case 0x4a:
218c2239868Sjmcneill case 0x48:
219c2239868Sjmcneill tegra124_speedo.cpu_speedo_id = 4;
220c2239868Sjmcneill tegra124_speedo.soc_speedo_id = 2;
221c2239868Sjmcneill tegra124_speedo.gpu_speedo_id = 3;
222c2239868Sjmcneill threshold = 1;
223c2239868Sjmcneill break;
224c2239868Sjmcneill default:
225c2239868Sjmcneill aprint_error("tegra124: unknown SKU ID %#x\n", sku_id);
226c2239868Sjmcneill break; /* use default */
227c2239868Sjmcneill }
228c2239868Sjmcneill
229c2239868Sjmcneill return threshold;
230c2239868Sjmcneill }
231c2239868Sjmcneill
232c2239868Sjmcneill static bool
tegra124_speedo_rate_ok(u_int rate)233c2239868Sjmcneill tegra124_speedo_rate_ok(u_int rate)
234c2239868Sjmcneill {
235c2239868Sjmcneill u_int tbl = 0;
236c2239868Sjmcneill
237c2239868Sjmcneill if (tegra124_speedo.cpu_speedo_id < __arraycount(tegra124_cpufreq_max))
238c2239868Sjmcneill tbl = tegra124_speedo.cpu_speedo_id;
239c2239868Sjmcneill
240c2239868Sjmcneill return rate <= tegra124_cpufreq_max[tbl];
241c2239868Sjmcneill }
242c2239868Sjmcneill
243c2239868Sjmcneill
244c2239868Sjmcneill static u_int
tegra124_cpufreq_set_rate(u_int rate)245c2239868Sjmcneill tegra124_cpufreq_set_rate(u_int rate)
246c2239868Sjmcneill {
247c2239868Sjmcneill const u_int nrates = __arraycount(tegra124_cpufreq_rates);
248c2239868Sjmcneill const struct tegra124_cpufreq_rate *r = NULL;
249c2239868Sjmcneill CPU_INFO_ITERATOR cii;
250c2239868Sjmcneill struct cpu_info *ci;
251c2239868Sjmcneill u_int cur_uvol;
252c2239868Sjmcneill int error;
253c2239868Sjmcneill
254c2239868Sjmcneill if (tegra124_speedo_rate_ok(rate) == false)
255c2239868Sjmcneill return EINVAL;
256c2239868Sjmcneill
257c2239868Sjmcneill for (int i = 0; i < nrates; i++) {
258c2239868Sjmcneill if (tegra124_cpufreq_rates[i].rate == rate) {
259c2239868Sjmcneill r = &tegra124_cpufreq_rates[i];
260c2239868Sjmcneill break;
261c2239868Sjmcneill }
262c2239868Sjmcneill }
263c2239868Sjmcneill if (r == NULL)
264c2239868Sjmcneill return EINVAL;
265c2239868Sjmcneill
266c2239868Sjmcneill error = fdtbus_regulator_get_voltage(tegra124_reg_vddcpu, &cur_uvol);
267c2239868Sjmcneill if (error != 0)
268c2239868Sjmcneill return error;
269c2239868Sjmcneill
270c2239868Sjmcneill if (cur_uvol < r->uvol) {
271c2239868Sjmcneill error = fdtbus_regulator_set_voltage(tegra124_reg_vddcpu,
272c2239868Sjmcneill r->uvol, r->uvol);
273c2239868Sjmcneill if (error != 0)
274c2239868Sjmcneill return error;
275c2239868Sjmcneill }
276c2239868Sjmcneill
277c2239868Sjmcneill error = clk_set_rate(tegra124_clk_pllx, r->rate * 1000000);
278c2239868Sjmcneill if (error == 0) {
279c2239868Sjmcneill rate = tegra124_cpufreq_get_rate();
280c2239868Sjmcneill for (CPU_INFO_FOREACH(cii, ci)) {
281c2239868Sjmcneill ci->ci_data.cpu_cc_freq = rate * 1000000;
282c2239868Sjmcneill }
283c2239868Sjmcneill }
284c2239868Sjmcneill
285c2239868Sjmcneill if (cur_uvol > r->uvol) {
286c2239868Sjmcneill (void)fdtbus_regulator_set_voltage(tegra124_reg_vddcpu,
287c2239868Sjmcneill r->uvol, r->uvol);
288c2239868Sjmcneill }
289c2239868Sjmcneill
290c2239868Sjmcneill return error;
291c2239868Sjmcneill }
292c2239868Sjmcneill
293c2239868Sjmcneill static u_int
tegra124_cpufreq_get_rate(void)294c2239868Sjmcneill tegra124_cpufreq_get_rate(void)
295c2239868Sjmcneill {
296c2239868Sjmcneill return clk_get_rate(tegra124_clk_pllx) / 1000000;
297c2239868Sjmcneill }
298c2239868Sjmcneill
299c2239868Sjmcneill static size_t
tegra124_cpufreq_get_available(u_int * pavail,size_t maxavail)300c2239868Sjmcneill tegra124_cpufreq_get_available(u_int *pavail, size_t maxavail)
301c2239868Sjmcneill {
302c2239868Sjmcneill const u_int nrates = __arraycount(tegra124_cpufreq_rates);
303c2239868Sjmcneill u_int n, cnt;
304c2239868Sjmcneill
305c2239868Sjmcneill KASSERT(nrates <= maxavail);
306c2239868Sjmcneill
307c2239868Sjmcneill for (n = 0, cnt = 0; n < nrates; n++) {
308c2239868Sjmcneill if (tegra124_speedo_rate_ok(tegra124_cpufreq_rates[n].rate)) {
309c2239868Sjmcneill pavail[cnt++] = tegra124_cpufreq_rates[n].rate;
310c2239868Sjmcneill }
311c2239868Sjmcneill }
312c2239868Sjmcneill
313c2239868Sjmcneill return cnt;
314c2239868Sjmcneill }
315