xref: /freebsd/sys/arm64/nvidia/tegra210/tegra210_cpufreq.c (revision 4b9b6a501a76d93c085223a8bc86073d202cc1bb)
1e9034789SMichal Meloun /*-
2e9034789SMichal Meloun  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3e9034789SMichal Meloun  *
4e9034789SMichal Meloun  * Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
5e9034789SMichal Meloun  *
6e9034789SMichal Meloun  * Redistribution and use in source and binary forms, with or without
7e9034789SMichal Meloun  * modification, are permitted provided that the following conditions
8e9034789SMichal Meloun  * are met:
9e9034789SMichal Meloun  * 1. Redistributions of source code must retain the above copyright
10e9034789SMichal Meloun  *    notice, this list of conditions and the following disclaimer.
11e9034789SMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
12e9034789SMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
13e9034789SMichal Meloun  *    documentation and/or other materials provided with the distribution.
14e9034789SMichal Meloun  *
15e9034789SMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16e9034789SMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e9034789SMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e9034789SMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19e9034789SMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e9034789SMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e9034789SMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e9034789SMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e9034789SMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e9034789SMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e9034789SMichal Meloun  * SUCH DAMAGE.
26e9034789SMichal Meloun  */
27e9034789SMichal Meloun 
28e9034789SMichal Meloun #include <sys/cdefs.h>
29e9034789SMichal Meloun __FBSDID("$FreeBSD$");
30e9034789SMichal Meloun 
31e9034789SMichal Meloun #include <sys/param.h>
32e9034789SMichal Meloun #include <sys/systm.h>
33e9034789SMichal Meloun #include <sys/bus.h>
34e9034789SMichal Meloun #include <sys/cpu.h>
35e9034789SMichal Meloun #include <sys/kernel.h>
36e9034789SMichal Meloun #include <sys/lock.h>
37e9034789SMichal Meloun #include <sys/malloc.h>
38e9034789SMichal Meloun #include <sys/module.h>
39e9034789SMichal Meloun 
40e9034789SMichal Meloun #include <machine/bus.h>
41e9034789SMichal Meloun #include <machine/cpu.h>
42e9034789SMichal Meloun 
43e9034789SMichal Meloun #include <dev/extres/clk/clk.h>
44e9034789SMichal Meloun #include <dev/extres/regulator/regulator.h>
45e9034789SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
46e9034789SMichal Meloun 
47e9034789SMichal Meloun #include <arm/nvidia/tegra_efuse.h>
48e9034789SMichal Meloun 
49e9034789SMichal Meloun #include "cpufreq_if.h"
50e9034789SMichal Meloun 
51e9034789SMichal Meloun /* CPU voltage table entry */
52e9034789SMichal Meloun struct speedo_entry {
53e9034789SMichal Meloun 	uint64_t		freq; 	/* Frequency point */
54e9034789SMichal Meloun 	int			c0; 	/* Coeeficient values for */
55e9034789SMichal Meloun 	int			c1;	/* quadratic equation: */
56e9034789SMichal Meloun 	int 			c2;	/* c2 * speedo^2 + c1 * speedo + c0 */
57e9034789SMichal Meloun };
58e9034789SMichal Meloun 
59e9034789SMichal Meloun struct cpu_volt_def {
60e9034789SMichal Meloun 	int			min_uvolt;	/* Min allowed CPU voltage */
61e9034789SMichal Meloun 	int			max_uvolt;	/* Max allowed CPU voltage */
62e9034789SMichal Meloun 	int 			step_uvolt; 	/* Step of CPU voltage */
63e9034789SMichal Meloun 	int			speedo_scale;	/* Scaling factor for cvt */
64e9034789SMichal Meloun 	int			speedo_nitems;	/* Size of speedo table */
65e9034789SMichal Meloun 	struct speedo_entry	*speedo_tbl;	/* CPU voltage table */
66e9034789SMichal Meloun };
67e9034789SMichal Meloun 
68e9034789SMichal Meloun struct cpu_speed_point {
69e9034789SMichal Meloun 	uint64_t		freq;		/* Frequecy */
70e9034789SMichal Meloun 	int			uvolt;		/* Requested voltage */
71e9034789SMichal Meloun };
72e9034789SMichal Meloun 
73e9034789SMichal Meloun static struct speedo_entry tegra210_speedo_tbl[] =
74e9034789SMichal Meloun {
75e9034789SMichal Meloun 	{204000000UL,	1007452, -23865, 370},
76e9034789SMichal Meloun 	{306000000UL,	1052709, -24875, 370},
77e9034789SMichal Meloun 	{408000000UL,	1099069, -25895, 370},
78e9034789SMichal Meloun 	{510000000UL,	1146534, -26905, 370},
79e9034789SMichal Meloun 	{612000000UL,	1195102, -27915, 370},
80e9034789SMichal Meloun 	{714000000UL,	1244773, -28925, 370},
81e9034789SMichal Meloun 	{816000000UL,	1295549, -29935, 370},
82e9034789SMichal Meloun 	{918000000UL,	1347428, -30955, 370},
83e9034789SMichal Meloun 	{1020000000UL,	1400411, -31965, 370},
84e9034789SMichal Meloun 	{1122000000UL,	1454497, -32975, 370},
85e9034789SMichal Meloun 	{1224000000UL,	1509687, -33985, 370},
86e9034789SMichal Meloun 	{1326000000UL,	1565981, -35005, 370},
87e9034789SMichal Meloun 	{1428000000UL,	1623379, -36015, 370},
88e9034789SMichal Meloun 	{1530000000UL,	1681880, -37025, 370},
89e9034789SMichal Meloun 	{1632000000UL,	1741485, -38035, 370},
90e9034789SMichal Meloun 	{1734000000UL,	1802194, -39055, 370},
91e9034789SMichal Meloun 	{1836000000UL,	1864006, -40065, 370},
92e9034789SMichal Meloun 	{1912500000UL,	1910780, -40815, 370},
93e9034789SMichal Meloun 	{2014500000UL,	1227000,      0,   0},
94e9034789SMichal Meloun 	{2218500000UL,	1227000,      0,   0},
95e9034789SMichal Meloun };
96e9034789SMichal Meloun 
97e9034789SMichal Meloun static struct cpu_volt_def tegra210_cpu_volt_def =
98e9034789SMichal Meloun {
99e9034789SMichal Meloun 	.min_uvolt = 900000,		/* 0.9 V */
100e9034789SMichal Meloun 	.max_uvolt = 1227000,		/* 1.227 */
101e9034789SMichal Meloun 	.step_uvolt =  10000,		/* 10 mV */
102e9034789SMichal Meloun 	.speedo_scale = 100,
103e9034789SMichal Meloun 	.speedo_nitems = nitems(tegra210_speedo_tbl),
104e9034789SMichal Meloun 	.speedo_tbl = tegra210_speedo_tbl,
105e9034789SMichal Meloun };
106e9034789SMichal Meloun 
107e9034789SMichal Meloun static uint64_t cpu_max_freq[] = {
108e9034789SMichal Meloun 	1912500000UL,
109e9034789SMichal Meloun 	1912500000UL,
110e9034789SMichal Meloun 	2218500000UL,
111e9034789SMichal Meloun 	1785000000UL,
112e9034789SMichal Meloun 	1632000000UL,
113e9034789SMichal Meloun 	1912500000UL,
114e9034789SMichal Meloun 	2014500000UL,
115e9034789SMichal Meloun 	1734000000UL,
116e9034789SMichal Meloun 	1683000000UL,
117e9034789SMichal Meloun 	1555500000UL,
118e9034789SMichal Meloun 	1504500000UL,
119e9034789SMichal Meloun };
120e9034789SMichal Meloun 
121e9034789SMichal Meloun static uint64_t cpu_freq_tbl[] = {
122e9034789SMichal Meloun 	 204000000UL,
123e9034789SMichal Meloun 	 306000000UL,
124e9034789SMichal Meloun 	 408000000UL,
125e9034789SMichal Meloun 	 510000000UL,
126e9034789SMichal Meloun 	 612000000UL,
127e9034789SMichal Meloun 	 714000000UL,
128e9034789SMichal Meloun 	 816000000UL,
129e9034789SMichal Meloun 	 918000000UL,
130e9034789SMichal Meloun 	1020000000UL,
131e9034789SMichal Meloun 	1122000000UL,
132e9034789SMichal Meloun 	1224000000UL,
133e9034789SMichal Meloun 	1326000000UL,
134e9034789SMichal Meloun 	1428000000UL,
135e9034789SMichal Meloun 	1530000000UL,
136e9034789SMichal Meloun 	1632000000UL,
137e9034789SMichal Meloun 	1734000000UL,
138e9034789SMichal Meloun 	1836000000UL,
139e9034789SMichal Meloun 	1912500000UL,
140e9034789SMichal Meloun 	2014500000UL,
141e9034789SMichal Meloun 	2218500000UL,
142e9034789SMichal Meloun };
143e9034789SMichal Meloun 
144e9034789SMichal Meloun struct tegra210_cpufreq_softc {
145e9034789SMichal Meloun 	device_t		dev;
146e9034789SMichal Meloun 	phandle_t		node;
147e9034789SMichal Meloun 
148e9034789SMichal Meloun 	clk_t			clk_cpu_g;
149e9034789SMichal Meloun 	clk_t			clk_pll_x;
150e9034789SMichal Meloun 	clk_t			clk_pll_p;
151e9034789SMichal Meloun 	clk_t			clk_dfll;
152e9034789SMichal Meloun 
153e9034789SMichal Meloun 	int 			process_id;
154e9034789SMichal Meloun 	int 			speedo_id;
155e9034789SMichal Meloun 	int 			speedo_value;
156e9034789SMichal Meloun 
157e9034789SMichal Meloun 	uint64_t		cpu_max_freq;
158e9034789SMichal Meloun 	struct cpu_volt_def	*cpu_def;
159e9034789SMichal Meloun 	struct cpu_speed_point	*speed_points;
160e9034789SMichal Meloun 	int			nspeed_points;
161e9034789SMichal Meloun 
162e9034789SMichal Meloun 	struct cpu_speed_point	*act_speed_point;
163e9034789SMichal Meloun 
164e9034789SMichal Meloun 	int			latency;
165e9034789SMichal Meloun };
166e9034789SMichal Meloun 
167e9034789SMichal Meloun static int cpufreq_lowest_freq = 1;
168e9034789SMichal Meloun TUNABLE_INT("hw.tegra210.cpufreq.lowest_freq", &cpufreq_lowest_freq);
169e9034789SMichal Meloun 
170e9034789SMichal Meloun #define	DIV_ROUND_CLOSEST(val, div)	(((val) + ((div) / 2)) / (div))
171e9034789SMichal Meloun 
172e9034789SMichal Meloun #define	ROUND_UP(val, div)	roundup(val, div)
173e9034789SMichal Meloun #define	ROUND_DOWN(val, div)	rounddown(val, div)
174e9034789SMichal Meloun 
175e9034789SMichal Meloun /*
176e9034789SMichal Meloun  * Compute requesetd voltage for given frequency and SoC process variations,
177e9034789SMichal Meloun  * - compute base voltage from speedo value using speedo table
178e9034789SMichal Meloun  * - round up voltage to next regulator step
179e9034789SMichal Meloun  * - clamp it to regulator limits
180e9034789SMichal Meloun  */
181e9034789SMichal Meloun static int
182e9034789SMichal Meloun freq_to_voltage(struct tegra210_cpufreq_softc *sc, uint64_t freq)
183e9034789SMichal Meloun {
184e9034789SMichal Meloun 	int uv, scale, min_uvolt, max_uvolt, step_uvolt;
185e9034789SMichal Meloun 	struct speedo_entry *ent;
186e9034789SMichal Meloun 	int i;
187e9034789SMichal Meloun 
188e9034789SMichal Meloun 	/* Get speedo entry with higher frequency */
189e9034789SMichal Meloun 	ent = NULL;
190e9034789SMichal Meloun 	for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {
191e9034789SMichal Meloun 		if (sc->cpu_def->speedo_tbl[i].freq >= freq) {
192e9034789SMichal Meloun 			ent = &sc->cpu_def->speedo_tbl[i];
193e9034789SMichal Meloun 			break;
194e9034789SMichal Meloun 		}
195e9034789SMichal Meloun 	}
196e9034789SMichal Meloun 	if (ent == NULL)
197e9034789SMichal Meloun 		ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
198e9034789SMichal Meloun 	scale = sc->cpu_def->speedo_scale;
199e9034789SMichal Meloun 
200e9034789SMichal Meloun 
201e9034789SMichal Meloun 	/* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
202e9034789SMichal Meloun 	uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);
203e9034789SMichal Meloun 	uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +
204e9034789SMichal Meloun 	    ent->c0;
205e9034789SMichal Meloun 	step_uvolt = sc->cpu_def->step_uvolt;
206e9034789SMichal Meloun 	/* Round up it to next regulator step */
207e9034789SMichal Meloun 	uv = ROUND_UP(uv, step_uvolt);
208e9034789SMichal Meloun 
209e9034789SMichal Meloun 	/* Clamp result */
210e9034789SMichal Meloun 	min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);
211e9034789SMichal Meloun 	max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);
212e9034789SMichal Meloun 	if (uv < min_uvolt)
213e9034789SMichal Meloun 		uv =  min_uvolt;
214e9034789SMichal Meloun 	if (uv > max_uvolt)
215e9034789SMichal Meloun 		uv =  max_uvolt;
216e9034789SMichal Meloun 	return (uv);
217e9034789SMichal Meloun 
218e9034789SMichal Meloun }
219e9034789SMichal Meloun 
220e9034789SMichal Meloun static void
221e9034789SMichal Meloun build_speed_points(struct tegra210_cpufreq_softc *sc) {
222e9034789SMichal Meloun 	int i;
223e9034789SMichal Meloun 
224e9034789SMichal Meloun 	sc->nspeed_points = nitems(cpu_freq_tbl);
225e9034789SMichal Meloun 	sc->speed_points = malloc(sizeof(struct cpu_speed_point) *
226e9034789SMichal Meloun 	    sc->nspeed_points, M_DEVBUF, M_NOWAIT);
227e9034789SMichal Meloun 	for (i = 0; i < sc->nspeed_points; i++) {
228e9034789SMichal Meloun 		sc->speed_points[i].freq = cpu_freq_tbl[i];
229e9034789SMichal Meloun 		sc->speed_points[i].uvolt = freq_to_voltage(sc,
230e9034789SMichal Meloun 		    cpu_freq_tbl[i]);
231e9034789SMichal Meloun 	}
232e9034789SMichal Meloun }
233e9034789SMichal Meloun 
234e9034789SMichal Meloun static struct cpu_speed_point *
235e9034789SMichal Meloun get_speed_point(struct tegra210_cpufreq_softc *sc, uint64_t freq)
236e9034789SMichal Meloun {
237e9034789SMichal Meloun 	int i;
238e9034789SMichal Meloun 
239e9034789SMichal Meloun 	if (sc->speed_points[0].freq >= freq)
240e9034789SMichal Meloun 		return (sc->speed_points + 0);
241e9034789SMichal Meloun 
242e9034789SMichal Meloun 	for (i = 0; i < sc->nspeed_points - 1; i++) {
243e9034789SMichal Meloun 		if (sc->speed_points[i + 1].freq > freq)
244e9034789SMichal Meloun 			return (sc->speed_points + i);
245e9034789SMichal Meloun 	}
246e9034789SMichal Meloun 
247e9034789SMichal Meloun 	return (sc->speed_points + sc->nspeed_points - 1);
248e9034789SMichal Meloun }
249e9034789SMichal Meloun 
250e9034789SMichal Meloun static int
251e9034789SMichal Meloun tegra210_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
252e9034789SMichal Meloun {
253e9034789SMichal Meloun 	struct tegra210_cpufreq_softc *sc;
254*4b9b6a50SJohn Baldwin 	int i, j;
255e9034789SMichal Meloun 
256e9034789SMichal Meloun 	if (sets == NULL || count == NULL)
257e9034789SMichal Meloun 		return (EINVAL);
258e9034789SMichal Meloun 
259e9034789SMichal Meloun 	sc = device_get_softc(dev);
260e9034789SMichal Meloun 	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
261e9034789SMichal Meloun 
262e9034789SMichal Meloun 	for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
263e9034789SMichal Meloun 		if (sc->cpu_max_freq < sc->speed_points[j].freq)
264e9034789SMichal Meloun 			continue;
265e9034789SMichal Meloun 		sets[i].freq = sc->speed_points[j].freq / 1000000;
266e9034789SMichal Meloun 		sets[i].volts = sc->speed_points[j].uvolt / 1000;
267e9034789SMichal Meloun 		sets[i].lat = sc->latency;
268e9034789SMichal Meloun 		sets[i].dev = dev;
269e9034789SMichal Meloun 		i++;
270e9034789SMichal Meloun 	}
271e9034789SMichal Meloun 	*count = i;
272e9034789SMichal Meloun 
273e9034789SMichal Meloun 	return (0);
274e9034789SMichal Meloun }
275e9034789SMichal Meloun 
276e9034789SMichal Meloun static int
277e9034789SMichal Meloun set_cpu_freq(struct tegra210_cpufreq_softc *sc, uint64_t freq)
278e9034789SMichal Meloun {
279e9034789SMichal Meloun 	struct cpu_speed_point *point;
280e9034789SMichal Meloun 	int rv;
281e9034789SMichal Meloun 
282e9034789SMichal Meloun 	point = get_speed_point(sc, freq);
283e9034789SMichal Meloun 
284e9034789SMichal Meloun 	/* Set PLLX frequency */
285e9034789SMichal Meloun 	rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
286e9034789SMichal Meloun 	if (rv != 0) {
287e9034789SMichal Meloun 		device_printf(sc->dev, "Can't set CPU clock frequency\n");
288e9034789SMichal Meloun 		return (rv);
289e9034789SMichal Meloun 	}
290e9034789SMichal Meloun 
291e9034789SMichal Meloun 	sc->act_speed_point = point;
292e9034789SMichal Meloun 
293e9034789SMichal Meloun 	return (0);
294e9034789SMichal Meloun }
295e9034789SMichal Meloun 
296e9034789SMichal Meloun static int
297e9034789SMichal Meloun tegra210_cpufreq_set(device_t dev, const struct cf_setting *cf)
298e9034789SMichal Meloun {
299e9034789SMichal Meloun 	struct tegra210_cpufreq_softc *sc;
300e9034789SMichal Meloun 	uint64_t freq;
301e9034789SMichal Meloun 	int rv;
302e9034789SMichal Meloun 
303e9034789SMichal Meloun 	if (cf == NULL || cf->freq < 0)
304e9034789SMichal Meloun 		return (EINVAL);
305e9034789SMichal Meloun 
306e9034789SMichal Meloun 	sc = device_get_softc(dev);
307e9034789SMichal Meloun 
308e9034789SMichal Meloun 	freq = cf->freq;
309e9034789SMichal Meloun 	if (freq < cpufreq_lowest_freq)
310e9034789SMichal Meloun 		freq = cpufreq_lowest_freq;
311e9034789SMichal Meloun 	freq *= 1000000;
312e9034789SMichal Meloun 	if (freq >= sc->cpu_max_freq)
313e9034789SMichal Meloun 		freq = sc->cpu_max_freq;
314e9034789SMichal Meloun 	rv = set_cpu_freq(sc, freq);
315e9034789SMichal Meloun 
316e9034789SMichal Meloun 	return (rv);
317e9034789SMichal Meloun }
318e9034789SMichal Meloun 
319e9034789SMichal Meloun static int
320e9034789SMichal Meloun tegra210_cpufreq_get(device_t dev, struct cf_setting *cf)
321e9034789SMichal Meloun {
322e9034789SMichal Meloun 	struct tegra210_cpufreq_softc *sc;
323e9034789SMichal Meloun 
324e9034789SMichal Meloun 	if (cf == NULL)
325e9034789SMichal Meloun 		return (EINVAL);
326e9034789SMichal Meloun 
327e9034789SMichal Meloun 	sc = device_get_softc(dev);
328e9034789SMichal Meloun 	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
329e9034789SMichal Meloun 	cf->dev = NULL;
330e9034789SMichal Meloun 	cf->freq = sc->act_speed_point->freq / 1000000;
331e9034789SMichal Meloun 	cf->volts = sc->act_speed_point->uvolt / 1000;
332e9034789SMichal Meloun 	/* Transition latency in us. */
333e9034789SMichal Meloun 	cf->lat = sc->latency;
334e9034789SMichal Meloun 	/* Driver providing this setting. */
335e9034789SMichal Meloun 	cf->dev = dev;
336e9034789SMichal Meloun 
337e9034789SMichal Meloun 	return (0);
338e9034789SMichal Meloun }
339e9034789SMichal Meloun 
340e9034789SMichal Meloun 
341e9034789SMichal Meloun static int
342e9034789SMichal Meloun tegra210_cpufreq_type(device_t dev, int *type)
343e9034789SMichal Meloun {
344e9034789SMichal Meloun 
345e9034789SMichal Meloun 	if (type == NULL)
346e9034789SMichal Meloun 		return (EINVAL);
347e9034789SMichal Meloun 	*type = CPUFREQ_TYPE_ABSOLUTE;
348e9034789SMichal Meloun 
349e9034789SMichal Meloun 	return (0);
350e9034789SMichal Meloun }
351e9034789SMichal Meloun 
352e9034789SMichal Meloun static int
353e9034789SMichal Meloun get_fdt_resources(struct tegra210_cpufreq_softc *sc, phandle_t node)
354e9034789SMichal Meloun {
355e9034789SMichal Meloun 	int rv;
356e9034789SMichal Meloun 	device_t parent_dev;
357e9034789SMichal Meloun 
358e9034789SMichal Meloun 	parent_dev =  device_get_parent(sc->dev);
359e9034789SMichal Meloun 
360e9034789SMichal Meloun 	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);
361e9034789SMichal Meloun 	if (rv != 0) {
362e9034789SMichal Meloun 		device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
363e9034789SMichal Meloun 		return (ENXIO);
364e9034789SMichal Meloun 	}
365e9034789SMichal Meloun 
366e9034789SMichal Meloun 	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);
367e9034789SMichal Meloun 	if (rv != 0) {
368e9034789SMichal Meloun 		device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
369e9034789SMichal Meloun 		return (ENXIO);
370e9034789SMichal Meloun 	}
371e9034789SMichal Meloun 	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);
372e9034789SMichal Meloun 	if (rv != 0) {
373e9034789SMichal Meloun 		device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
374e9034789SMichal Meloun 		return (ENXIO);
375e9034789SMichal Meloun 	}
376e9034789SMichal Meloun 	rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
377e9034789SMichal Meloun 
378e9034789SMichal Meloun 	/* XXX DPLL is not implemented yet */
379e9034789SMichal Meloun #if 0
380e9034789SMichal Meloun 	if (rv != 0) {
381e9034789SMichal Meloun 		device_printf(sc->dev, "Cannot get 'dfll' clock\n");
382e9034789SMichal Meloun 		return (ENXIO);
383e9034789SMichal Meloun 	}
384e9034789SMichal Meloun #endif
385e9034789SMichal Meloun 	return (0);
386e9034789SMichal Meloun }
387e9034789SMichal Meloun 
388e9034789SMichal Meloun static void
389e9034789SMichal Meloun tegra210_cpufreq_identify(driver_t *driver, device_t parent)
390e9034789SMichal Meloun {
391e9034789SMichal Meloun 	phandle_t root;
392e9034789SMichal Meloun 
393e9034789SMichal Meloun 	root = OF_finddevice("/");
394e9034789SMichal Meloun 	if (!ofw_bus_node_is_compatible(root, "nvidia,tegra210"))
395e9034789SMichal Meloun 		return;
396e9034789SMichal Meloun 
397e9034789SMichal Meloun 	if (device_get_unit(parent) != 0)
398e9034789SMichal Meloun 		return;
399e9034789SMichal Meloun 	if (device_find_child(parent, "tegra210_cpufreq", -1) != NULL)
400e9034789SMichal Meloun 		return;
401e9034789SMichal Meloun 	if (BUS_ADD_CHILD(parent, 0, "tegra210_cpufreq", -1) == NULL)
402e9034789SMichal Meloun 		device_printf(parent, "add child failed\n");
403e9034789SMichal Meloun }
404e9034789SMichal Meloun 
405e9034789SMichal Meloun static int
406e9034789SMichal Meloun tegra210_cpufreq_probe(device_t dev)
407e9034789SMichal Meloun {
408e9034789SMichal Meloun 
409e9034789SMichal Meloun 	device_set_desc(dev, "CPU Frequency Control");
410e9034789SMichal Meloun 
411e9034789SMichal Meloun 	return (0);
412e9034789SMichal Meloun }
413e9034789SMichal Meloun 
414e9034789SMichal Meloun static int
415e9034789SMichal Meloun tegra210_cpufreq_attach(device_t dev)
416e9034789SMichal Meloun {
417e9034789SMichal Meloun 	struct tegra210_cpufreq_softc *sc;
418e9034789SMichal Meloun 	uint64_t freq;
419e9034789SMichal Meloun 	int rv;
420e9034789SMichal Meloun 
421e9034789SMichal Meloun 	sc = device_get_softc(dev);
422e9034789SMichal Meloun 	sc->dev = dev;
423e9034789SMichal Meloun 	sc->node = ofw_bus_get_node(device_get_parent(dev));
424e9034789SMichal Meloun 
425e9034789SMichal Meloun 	sc->process_id = tegra_sku_info.cpu_process_id;
426e9034789SMichal Meloun 	sc->speedo_id = tegra_sku_info.cpu_speedo_id;
427e9034789SMichal Meloun 	sc->speedo_value = tegra_sku_info.cpu_speedo_value;
428e9034789SMichal Meloun 
429e9034789SMichal Meloun 	sc->cpu_def = &tegra210_cpu_volt_def;
430e9034789SMichal Meloun 
431e9034789SMichal Meloun 	rv = get_fdt_resources(sc, sc->node);
432e9034789SMichal Meloun 	if (rv !=  0) {
433e9034789SMichal Meloun 		return (rv);
434e9034789SMichal Meloun 	}
435e9034789SMichal Meloun 
436e9034789SMichal Meloun 	build_speed_points(sc);
437e9034789SMichal Meloun 
438e9034789SMichal Meloun 	rv = clk_get_freq(sc->clk_cpu_g, &freq);
439e9034789SMichal Meloun 	if (rv != 0) {
440e9034789SMichal Meloun 		device_printf(dev, "Can't get CPU clock frequency\n");
441e9034789SMichal Meloun 		return (rv);
442e9034789SMichal Meloun 	}
443e9034789SMichal Meloun 	if (sc->speedo_id < nitems(cpu_max_freq))
444e9034789SMichal Meloun 		sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
445e9034789SMichal Meloun 	else
446e9034789SMichal Meloun 		sc->cpu_max_freq = cpu_max_freq[0];
447e9034789SMichal Meloun 	sc->act_speed_point = get_speed_point(sc, freq);
448e9034789SMichal Meloun 
449e9034789SMichal Meloun 	/* Set safe startup CPU frequency. */
450e9034789SMichal Meloun 	rv = set_cpu_freq(sc, 1632000000);
451e9034789SMichal Meloun 	if (rv != 0) {
452e9034789SMichal Meloun 		device_printf(dev, "Can't set initial CPU clock frequency\n");
453e9034789SMichal Meloun 		return (rv);
454e9034789SMichal Meloun 	}
455e9034789SMichal Meloun 
456e9034789SMichal Meloun 	/* This device is controlled by cpufreq(4). */
457e9034789SMichal Meloun 	cpufreq_register(dev);
458e9034789SMichal Meloun 
459e9034789SMichal Meloun 	return (0);
460e9034789SMichal Meloun }
461e9034789SMichal Meloun 
462e9034789SMichal Meloun static int
463e9034789SMichal Meloun tegra210_cpufreq_detach(device_t dev)
464e9034789SMichal Meloun {
465e9034789SMichal Meloun 	struct tegra210_cpufreq_softc *sc;
466e9034789SMichal Meloun 
467e9034789SMichal Meloun 	sc = device_get_softc(dev);
468e9034789SMichal Meloun 	cpufreq_unregister(dev);
469e9034789SMichal Meloun 
470e9034789SMichal Meloun 	if (sc->clk_cpu_g != NULL)
471e9034789SMichal Meloun 		clk_release(sc->clk_cpu_g);
472e9034789SMichal Meloun 	if (sc->clk_pll_x != NULL)
473e9034789SMichal Meloun 		clk_release(sc->clk_pll_x);
474e9034789SMichal Meloun 	if (sc->clk_pll_p != NULL)
475e9034789SMichal Meloun 		clk_release(sc->clk_pll_p);
476e9034789SMichal Meloun 	if (sc->clk_dfll != NULL)
477e9034789SMichal Meloun 		clk_release(sc->clk_dfll);
478e9034789SMichal Meloun 	return (0);
479e9034789SMichal Meloun }
480e9034789SMichal Meloun 
481e9034789SMichal Meloun static device_method_t tegra210_cpufreq_methods[] = {
482e9034789SMichal Meloun 	/* Device interface */
483e9034789SMichal Meloun 	DEVMETHOD(device_identify,	tegra210_cpufreq_identify),
484e9034789SMichal Meloun 	DEVMETHOD(device_probe,		tegra210_cpufreq_probe),
485e9034789SMichal Meloun 	DEVMETHOD(device_attach,	tegra210_cpufreq_attach),
486e9034789SMichal Meloun 	DEVMETHOD(device_detach,	tegra210_cpufreq_detach),
487e9034789SMichal Meloun 
488e9034789SMichal Meloun 	/* cpufreq interface */
489e9034789SMichal Meloun 	DEVMETHOD(cpufreq_drv_set,	tegra210_cpufreq_set),
490e9034789SMichal Meloun 	DEVMETHOD(cpufreq_drv_get,	tegra210_cpufreq_get),
491e9034789SMichal Meloun 	DEVMETHOD(cpufreq_drv_settings,	tegra210_cpufreq_settings),
492e9034789SMichal Meloun 	DEVMETHOD(cpufreq_drv_type,	tegra210_cpufreq_type),
493e9034789SMichal Meloun 
494e9034789SMichal Meloun 	DEVMETHOD_END
495e9034789SMichal Meloun };
496e9034789SMichal Meloun 
497e9034789SMichal Meloun static devclass_t tegra210_cpufreq_devclass;
498e9034789SMichal Meloun static DEFINE_CLASS_0(tegra210_cpufreq, tegra210_cpufreq_driver,
499e9034789SMichal Meloun     tegra210_cpufreq_methods, sizeof(struct tegra210_cpufreq_softc));
500e9034789SMichal Meloun DRIVER_MODULE(tegra210_cpufreq, cpu, tegra210_cpufreq_driver,
501e9034789SMichal Meloun     tegra210_cpufreq_devclass, NULL, NULL);
502