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