xref: /netbsd-src/sys/arch/arm/nvidia/tegra124_cpu.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
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