1296936Smmel/*-
2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3296936Smmel * All rights reserved.
4296936Smmel *
5296936Smmel * Redistribution and use in source and binary forms, with or without
6296936Smmel * modification, are permitted provided that the following conditions
7296936Smmel * are met:
8296936Smmel * 1. Redistributions of source code must retain the above copyright
9296936Smmel *    notice, this list of conditions and the following disclaimer.
10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296936Smmel *    notice, this list of conditions and the following disclaimer in the
12296936Smmel *    documentation and/or other materials provided with the distribution.
13296936Smmel *
14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296936Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296936Smmel * SUCH DAMAGE.
25296936Smmel */
26296936Smmel
27296936Smmel#include <sys/cdefs.h>
28296936Smmel__FBSDID("$FreeBSD: releng/11.0/sys/arm/nvidia/tegra124/tegra124_cpufreq.c 301288 2016-06-03 21:11:34Z pfg $");
29296936Smmel
30296936Smmel#include <sys/param.h>
31296936Smmel#include <sys/systm.h>
32296936Smmel#include <sys/bus.h>
33296936Smmel#include <sys/cpu.h>
34296936Smmel#include <sys/kernel.h>
35296936Smmel#include <sys/lock.h>
36296936Smmel#include <sys/malloc.h>
37296936Smmel#include <sys/module.h>
38296936Smmel
39296936Smmel#include <machine/bus.h>
40296936Smmel#include <machine/cpu.h>
41296936Smmel
42296936Smmel#include <dev/extres/clk/clk.h>
43296936Smmel#include <dev/extres/regulator/regulator.h>
44296936Smmel#include <dev/ofw/ofw_bus_subr.h>
45296936Smmel
46296936Smmel#include <arm/nvidia/tegra_efuse.h>
47296936Smmel
48296936Smmel#include "cpufreq_if.h"
49296936Smmel
50296936Smmel#define	XXX
51296936Smmel
52296936Smmel/* CPU voltage table entry */
53296936Smmelstruct speedo_entry {
54296936Smmel	uint64_t		freq; 	/* Frequency point */
55296936Smmel	int			c0; 	/* Coeeficient values for */
56296936Smmel	int			c1;	/* quadratic equation: */
57296936Smmel	int 			c2;	/* c2 * speedo^2 + c1 * speedo + c0 */
58296936Smmel};
59296936Smmel
60296936Smmelstruct cpu_volt_def {
61296936Smmel	int			min_uvolt;	/* Min allowed CPU voltage */
62296936Smmel	int			max_uvolt;	/* Max allowed CPU voltage */
63296936Smmel	int 			step_uvolt; 	/* Step of CPU voltage */
64296936Smmel	int			speedo_scale;	/* Scaling factor for cvt */
65296936Smmel	int			speedo_nitems;	/* Size of speedo table */
66296936Smmel	struct speedo_entry	*speedo_tbl;	/* CPU voltage table */
67296936Smmel};
68296936Smmel
69296936Smmelstruct cpu_speed_point {
70296936Smmel	uint64_t		freq;		/* Frequecy */
71296936Smmel	int			uvolt;		/* Requested voltage */
72296936Smmel};
73296936Smmel
74296936Smmelstatic struct speedo_entry tegra124_speedo_dpll_tbl[] =
75296936Smmel{
76296936Smmel	{ 204000000ULL,	1112619, -29295, 402},
77296936Smmel	{ 306000000ULL,	1150460, -30585, 402},
78296936Smmel	{ 408000000ULL,	1190122, -31865, 402},
79296936Smmel	{ 510000000ULL,	1231606, -33155, 402},
80296936Smmel	{ 612000000ULL,	1274912, -34435, 402},
81296936Smmel	{ 714000000ULL,	1320040, -35725, 402},
82296936Smmel	{ 816000000ULL,	1366990, -37005, 402},
83296936Smmel	{ 918000000ULL,	1415762, -38295, 402},
84296936Smmel	{1020000000ULL,	1466355, -39575, 402},
85296936Smmel	{1122000000ULL,	1518771, -40865, 402},
86296936Smmel	{1224000000ULL,	1573009, -42145, 402},
87296936Smmel	{1326000000ULL,	1629068, -43435, 402},
88296936Smmel	{1428000000ULL,	1686950, -44715, 402},
89296936Smmel	{1530000000ULL,	1746653, -46005, 402},
90296936Smmel	{1632000000ULL,	1808179, -47285, 402},
91296936Smmel	{1734000000ULL,	1871526, -48575, 402},
92296936Smmel	{1836000000ULL,	1936696, -49855, 402},
93296936Smmel	{1938000000ULL,	2003687, -51145, 402},
94296936Smmel	{2014500000ULL,	2054787, -52095, 402},
95296936Smmel	{2116500000ULL,	2124957, -53385, 402},
96296936Smmel	{2218500000ULL,	2196950, -54665, 402},
97296936Smmel	{2320500000ULL,	2270765, -55955, 402},
98296936Smmel	{2320500000ULL,	2270765, -55955, 402},
99296936Smmel	{2422500000ULL,	2346401, -57235, 402},
100296936Smmel	{2524500000ULL,	2437299, -58535, 402},
101296936Smmel};
102296936Smmel
103296936Smmelstatic struct cpu_volt_def tegra124_cpu_volt_dpll_def =
104296936Smmel{
105296936Smmel	.min_uvolt =  900000,		/* 0.9 V */
106296936Smmel	.max_uvolt = 1260000,		/* 1.26 */
107296936Smmel	.step_uvolt =  10000,		/* 10 mV */
108296936Smmel	.speedo_scale = 100,
109296936Smmel	.speedo_nitems = nitems(tegra124_speedo_dpll_tbl),
110296936Smmel	.speedo_tbl = tegra124_speedo_dpll_tbl,
111296936Smmel};
112296936Smmel
113296936Smmelstatic struct speedo_entry tegra124_speedo_pllx_tbl[] =
114296936Smmel{
115296936Smmel	{ 204000000ULL,	 800000, 0, 0},
116296936Smmel	{ 306000000ULL,	 800000, 0, 0},
117296936Smmel	{ 408000000ULL,	 800000, 0, 0},
118296936Smmel	{ 510000000ULL,	 800000, 0, 0},
119296936Smmel	{ 612000000ULL,	 800000, 0, 0},
120296936Smmel	{ 714000000ULL,	 800000, 0, 0},
121296936Smmel	{ 816000000ULL,	 820000, 0, 0},
122296936Smmel	{ 918000000ULL,	 840000, 0, 0},
123296936Smmel	{1020000000ULL,	 880000, 0, 0},
124296936Smmel	{1122000000ULL,	 900000, 0, 0},
125296936Smmel	{1224000000ULL,	 930000, 0, 0},
126296936Smmel	{1326000000ULL,	 960000, 0, 0},
127296936Smmel	{1428000000ULL,	 990000, 0, 0},
128296936Smmel	{1530000000ULL,	1020000, 0, 0},
129296936Smmel	{1632000000ULL,	1070000, 0, 0},
130296936Smmel	{1734000000ULL,	1100000, 0, 0},
131296936Smmel	{1836000000ULL,	1140000, 0, 0},
132296936Smmel	{1938000000ULL,	1180000, 0, 0},
133296936Smmel	{2014500000ULL,	1220000, 0, 0},
134296936Smmel	{2116500000ULL,	1260000, 0, 0},
135296936Smmel	{2218500000ULL,	1310000, 0, 0},
136296936Smmel	{2320500000ULL,	1360000, 0, 0},
137296936Smmel	{2397000000ULL,	1400000, 0, 0},
138296936Smmel	{2499000000ULL,	1400000, 0, 0},
139296936Smmel};
140296936Smmel
141296936Smmel
142296936Smmelstatic struct cpu_volt_def tegra124_cpu_volt_pllx_def =
143296936Smmel{
144296936Smmel	.min_uvolt =  900000,		/* 0.9 V */
145296936Smmel	.max_uvolt = 1260000,		/* 1.26 */
146296936Smmel	.step_uvolt =  10000,		/* 10 mV */
147296936Smmel	.speedo_scale = 100,
148296936Smmel	.speedo_nitems = nitems(tegra124_speedo_pllx_tbl),
149296936Smmel	.speedo_tbl = tegra124_speedo_pllx_tbl,
150296936Smmel};
151296936Smmel
152296936Smmelstatic uint64_t cpu_freq_tbl[] = {
153296936Smmel	 204000000ULL,
154296936Smmel	 306000000ULL,
155296936Smmel	 408000000ULL,
156296936Smmel	 510000000ULL,
157296936Smmel	 612000000ULL,
158296936Smmel	 714000000ULL,
159296936Smmel	 816000000ULL,
160296936Smmel	 918000000ULL,
161296936Smmel	1020000000ULL,
162296936Smmel	1122000000ULL,
163296936Smmel	1224000000ULL,
164296936Smmel	1326000000ULL,
165296936Smmel	1428000000ULL,
166296936Smmel	1530000000ULL,
167296936Smmel	1632000000ULL,
168296936Smmel	1734000000ULL,
169296936Smmel	1836000000ULL,
170296936Smmel	1938000000ULL,
171296936Smmel	2014000000ULL,
172296936Smmel	2116000000ULL,
173296936Smmel	2218000000ULL,
174296936Smmel	2320000000ULL,
175296936Smmel	2320000000ULL,
176296936Smmel	2422000000ULL,
177296936Smmel	2524000000ULL,
178296936Smmel};
179296936Smmel
180296936Smmelstatic uint64_t cpu_max_freq[] = {
181296936Smmel	2014500000ULL,
182296936Smmel	2320500000ULL,
183296936Smmel	2116500000ULL,
184296936Smmel	2524500000ULL,
185296936Smmel};
186296936Smmel
187296936Smmelstruct tegra124_cpufreq_softc {
188296936Smmel	device_t		dev;
189296936Smmel	phandle_t		node;
190296936Smmel
191296936Smmel	regulator_t		supply_vdd_cpu;
192296936Smmel	clk_t			clk_cpu_g;
193296936Smmel	clk_t			clk_cpu_lp;
194296936Smmel	clk_t			clk_pll_x;
195296936Smmel	clk_t			clk_pll_p;
196296936Smmel	clk_t			clk_dfll;
197296936Smmel
198296936Smmel	int 			process_id;
199296936Smmel	int 			speedo_id;
200296936Smmel	int 			speedo_value;
201296936Smmel
202296936Smmel	uint64_t		cpu_max_freq;
203296936Smmel	struct cpu_volt_def	*cpu_def;
204296936Smmel	struct cpu_speed_point	*speed_points;
205296936Smmel	int			nspeed_points;
206296936Smmel
207296936Smmel	struct cpu_speed_point	*act_speed_point;
208296936Smmel
209296936Smmel	int			latency;
210296936Smmel};
211296936Smmel
212296936Smmelstatic int cpufreq_lowest_freq = 1;
213296936SmmelTUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq);
214296936Smmel
215296936Smmel#define	DIV_ROUND_CLOSEST(val, div)	(((val) + ((div) / 2)) / (div))
216296936Smmel
217301288Spfg#define	ROUND_UP(val, div)	roundup(val, div)
218301288Spfg#define	ROUND_DOWN(val, div)	rounddown(val, div)
219296936Smmel
220296936Smmel/*
221296936Smmel * Compute requesetd voltage for given frequency and SoC process variations,
222296936Smmel * - compute base voltage from speedo value using speedo table
223296936Smmel * - round up voltage to next regulator step
224296936Smmel * - clamp it to regulator limits
225296936Smmel */
226296936Smmelstatic int
227296936Smmelfreq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq)
228296936Smmel{
229296936Smmel	int uv, scale, min_uvolt, max_uvolt, step_uvolt;
230296936Smmel	struct speedo_entry *ent;
231296936Smmel	int i;
232296936Smmel
233296936Smmel	/* Get speedo entry with higher frequency */
234296936Smmel	ent = NULL;
235296936Smmel	for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {
236296936Smmel		if (sc->cpu_def->speedo_tbl[i].freq >= freq) {
237296936Smmel			ent = &sc->cpu_def->speedo_tbl[i];
238296936Smmel			break;
239296936Smmel		}
240296936Smmel	}
241296936Smmel	if (ent == NULL)
242296936Smmel		ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
243296936Smmel	scale = sc->cpu_def->speedo_scale;
244296936Smmel
245296936Smmel
246296936Smmel	/* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
247296936Smmel	uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);
248296936Smmel	uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +
249296936Smmel	    ent->c0;
250296936Smmel	step_uvolt = sc->cpu_def->step_uvolt;
251296936Smmel	/* Round up it to next regulator step */
252296936Smmel	uv = ROUND_UP(uv, step_uvolt);
253296936Smmel
254296936Smmel	/* Clamp result */
255296936Smmel	min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);
256296936Smmel	max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);
257296936Smmel	if (uv < min_uvolt)
258296936Smmel		uv =  min_uvolt;
259296936Smmel	if (uv > max_uvolt)
260296936Smmel		uv =  max_uvolt;
261296936Smmel	return (uv);
262296936Smmel
263296936Smmel}
264296936Smmel
265296936Smmelstatic void
266296936Smmelbuild_speed_points(struct tegra124_cpufreq_softc *sc) {
267296936Smmel	int i;
268296936Smmel
269296936Smmel	sc->nspeed_points = nitems(cpu_freq_tbl);
270296936Smmel	sc->speed_points = malloc(sizeof(struct cpu_speed_point) *
271296936Smmel	    sc->nspeed_points, M_DEVBUF, M_NOWAIT);
272296936Smmel	for (i = 0; i < sc->nspeed_points; i++) {
273296936Smmel		sc->speed_points[i].freq = cpu_freq_tbl[i];
274296936Smmel		sc->speed_points[i].uvolt = freq_to_voltage(sc,
275296936Smmel		    cpu_freq_tbl[i]);
276296936Smmel	}
277296936Smmel}
278296936Smmel
279296936Smmelstatic struct cpu_speed_point *
280296936Smmelget_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq)
281296936Smmel{
282296936Smmel	int i;
283296936Smmel
284296936Smmel	if (sc->speed_points[0].freq >= freq)
285296936Smmel		return (sc->speed_points + 0);
286296936Smmel
287296936Smmel	for (i = 0; i < sc->nspeed_points - 1; i++) {
288296936Smmel		if (sc->speed_points[i + 1].freq > freq)
289296936Smmel			return (sc->speed_points + i);
290296936Smmel	}
291296936Smmel
292296936Smmel	return (sc->speed_points + sc->nspeed_points - 1);
293296936Smmel}
294296936Smmel
295296936Smmelstatic int
296296936Smmeltegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
297296936Smmel{
298296936Smmel	struct tegra124_cpufreq_softc *sc;
299296936Smmel	int i, j, max_cnt;
300296936Smmel
301296936Smmel	if (sets == NULL || count == NULL)
302296936Smmel		return (EINVAL);
303296936Smmel
304296936Smmel	sc = device_get_softc(dev);
305296936Smmel	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
306296936Smmel
307296936Smmel	max_cnt = min(sc->nspeed_points, *count);
308296936Smmel	for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
309296936Smmel		if (sc->cpu_max_freq < sc->speed_points[j].freq)
310296936Smmel			continue;
311296936Smmel		sets[i].freq = sc->speed_points[j].freq / 1000000;
312296936Smmel		sets[i].volts = sc->speed_points[j].uvolt / 1000;
313296936Smmel		sets[i].lat = sc->latency;
314296936Smmel		sets[i].dev = dev;
315296936Smmel		i++;
316296936Smmel	}
317296936Smmel	*count = i;
318296936Smmel
319296936Smmel	return (0);
320296936Smmel}
321296936Smmel
322296936Smmelstatic int
323296936Smmelset_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq)
324296936Smmel{
325296936Smmel	struct cpu_speed_point *point;
326296936Smmel	int rv;
327296936Smmel
328296936Smmel	point = get_speed_point(sc, freq);
329296936Smmel
330296936Smmel	if (sc->act_speed_point->uvolt < point->uvolt) {
331296936Smmel		/* set cpu voltage */
332296936Smmel		rv = regulator_set_voltage(sc->supply_vdd_cpu,
333296936Smmel		    point->uvolt, point->uvolt);
334296936Smmel		DELAY(10000);
335296936Smmel		if (rv != 0)
336296936Smmel			return (rv);
337296936Smmel	}
338297576Smmel
339297576Smmel	/* Switch supermux to PLLP first */
340297576Smmel	rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p);
341296936Smmel	if (rv != 0) {
342297576Smmel		device_printf(sc->dev, "Can't set parent to PLLP\n");
343297576Smmel		return (rv);
344297576Smmel	}
345297576Smmel
346297576Smmel	/* Set PLLX frequency */
347297576Smmel	rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
348297576Smmel	if (rv != 0) {
349296936Smmel		device_printf(sc->dev, "Can't set CPU clock frequency\n");
350296936Smmel		return (rv);
351296936Smmel	}
352296936Smmel
353297576Smmel	rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x);
354297576Smmel	if (rv != 0) {
355297576Smmel		device_printf(sc->dev, "Can't set parent to PLLX\n");
356297576Smmel		return (rv);
357297576Smmel	}
358297576Smmel
359296936Smmel	if (sc->act_speed_point->uvolt > point->uvolt) {
360296936Smmel		/* set cpu voltage */
361296936Smmel		rv = regulator_set_voltage(sc->supply_vdd_cpu,
362296936Smmel		    point->uvolt, point->uvolt);
363296936Smmel		if (rv != 0)
364296936Smmel			return (rv);
365296936Smmel	}
366296936Smmel
367296936Smmel	sc->act_speed_point = point;
368296936Smmel
369296936Smmel	return (0);
370296936Smmel}
371296936Smmel
372296936Smmelstatic int
373296936Smmeltegra124_cpufreq_set(device_t dev, const struct cf_setting *cf)
374296936Smmel{
375296936Smmel	struct tegra124_cpufreq_softc *sc;
376296936Smmel	uint64_t freq;
377296936Smmel	int rv;
378296936Smmel
379296936Smmel	if (cf == NULL || cf->freq < 0)
380296936Smmel		return (EINVAL);
381296936Smmel
382296936Smmel	sc = device_get_softc(dev);
383296936Smmel
384296936Smmel	freq = cf->freq;
385296936Smmel	if (freq < cpufreq_lowest_freq)
386296936Smmel		freq = cpufreq_lowest_freq;
387296936Smmel	freq *= 1000000;
388296936Smmel	if (freq >= sc->cpu_max_freq)
389296936Smmel		freq = sc->cpu_max_freq;
390296936Smmel	rv = set_cpu_freq(sc, freq);
391296936Smmel
392296936Smmel	return (rv);
393296936Smmel}
394296936Smmel
395296936Smmelstatic int
396296936Smmeltegra124_cpufreq_get(device_t dev, struct cf_setting *cf)
397296936Smmel{
398296936Smmel	struct tegra124_cpufreq_softc *sc;
399296936Smmel
400296936Smmel	if (cf == NULL)
401296936Smmel		return (EINVAL);
402296936Smmel
403296936Smmel	sc = device_get_softc(dev);
404296936Smmel	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
405296936Smmel	cf->dev = NULL;
406296936Smmel	cf->freq = sc->act_speed_point->freq / 1000000;
407296936Smmel	cf->volts = sc->act_speed_point->uvolt / 1000;
408296936Smmel	/* Transition latency in us. */
409296936Smmel	cf->lat = sc->latency;
410296936Smmel	/* Driver providing this setting. */
411296936Smmel	cf->dev = dev;
412296936Smmel
413296936Smmel	return (0);
414296936Smmel}
415296936Smmel
416296936Smmel
417296936Smmelstatic int
418296936Smmeltegra124_cpufreq_type(device_t dev, int *type)
419296936Smmel{
420296936Smmel
421296936Smmel	if (type == NULL)
422296936Smmel		return (EINVAL);
423296936Smmel	*type = CPUFREQ_TYPE_ABSOLUTE;
424296936Smmel
425296936Smmel	return (0);
426296936Smmel}
427296936Smmel
428296936Smmelstatic int
429296936Smmelget_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node)
430296936Smmel{
431296936Smmel	int rv;
432296936Smmel	device_t parent_dev;
433296936Smmel
434296936Smmel	parent_dev =  device_get_parent(sc->dev);
435296936Smmel	rv = regulator_get_by_ofw_property(parent_dev, "vdd-cpu-supply",
436296936Smmel	    &sc->supply_vdd_cpu);
437296936Smmel	if (rv != 0) {
438296936Smmel		device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n");
439296936Smmel		return (rv);
440296936Smmel	}
441296936Smmel
442296936Smmel	rv = clk_get_by_ofw_name(parent_dev, "cpu_g", &sc->clk_cpu_g);
443296936Smmel	if (rv != 0) {
444296936Smmel		device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
445296936Smmel		return (ENXIO);
446296936Smmel	}
447296936Smmel
448296936Smmel	rv = clk_get_by_ofw_name(parent_dev, "cpu_lp", &sc->clk_cpu_lp);
449296936Smmel	if (rv != 0) {
450296936Smmel		device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n");
451296936Smmel		return (ENXIO);
452296936Smmel	}
453296936Smmel
454296936Smmel	rv = clk_get_by_ofw_name(parent_dev, "pll_x", &sc->clk_pll_x);
455296936Smmel	if (rv != 0) {
456296936Smmel		device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
457296936Smmel		return (ENXIO);
458296936Smmel	}
459296936Smmel	rv = clk_get_by_ofw_name(parent_dev, "pll_p", &sc->clk_pll_p);
460296936Smmel	if (rv != 0) {
461296936Smmel		device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
462296936Smmel		return (ENXIO);
463296936Smmel	}
464296936Smmel	rv = clk_get_by_ofw_name(parent_dev, "dfll", &sc->clk_dfll);
465296936Smmel	if (rv != 0) {
466296936Smmel		/* XXX DPLL is not implemented yet */
467296936Smmel/*
468296936Smmel		device_printf(sc->dev, "Cannot get 'dfll' clock\n");
469296936Smmel		return (ENXIO);
470296936Smmel*/
471296936Smmel	}
472296936Smmel	return (0);
473296936Smmel}
474296936Smmel
475296936Smmelstatic void
476296936Smmeltegra124_cpufreq_identify(driver_t *driver, device_t parent)
477296936Smmel{
478296936Smmel
479296936Smmel	if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL)
480296936Smmel		return;
481296936Smmel	if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL)
482296936Smmel		device_printf(parent, "add child failed\n");
483296936Smmel}
484296936Smmel
485296936Smmelstatic int
486296936Smmeltegra124_cpufreq_probe(device_t dev)
487296936Smmel{
488296936Smmel
489296936Smmel	if (device_get_unit(dev) != 0)
490296936Smmel		return (ENXIO);
491296936Smmel	device_set_desc(dev, "CPU Frequency Control");
492296936Smmel
493296936Smmel	return (0);
494296936Smmel}
495296936Smmel
496296936Smmelstatic int
497296936Smmeltegra124_cpufreq_attach(device_t dev)
498296936Smmel{
499296936Smmel	struct tegra124_cpufreq_softc *sc;
500296936Smmel	uint64_t freq;
501296936Smmel	int rv;
502296936Smmel
503296936Smmel	sc = device_get_softc(dev);
504296936Smmel	sc->dev = dev;
505296936Smmel	sc->node = ofw_bus_get_node(device_get_parent(dev));
506296936Smmel
507296936Smmel	sc->process_id = tegra_sku_info.cpu_process_id;
508296936Smmel	sc->speedo_id = tegra_sku_info.cpu_speedo_id;
509296936Smmel	sc->speedo_value = tegra_sku_info.cpu_speedo_value;
510296936Smmel
511296936Smmel	/* Tegra 124 */
512296936Smmel	/* XXX DPLL is not implemented yet */
513296936Smmel	if (1)
514296936Smmel		sc->cpu_def = &tegra124_cpu_volt_pllx_def;
515296936Smmel	else
516296936Smmel		sc->cpu_def = &tegra124_cpu_volt_dpll_def;
517296936Smmel
518296936Smmel
519296936Smmel	rv = get_fdt_resources(sc, sc->node);
520296936Smmel	if (rv !=  0) {
521296936Smmel		return (rv);
522296936Smmel	}
523296936Smmel
524296936Smmel	build_speed_points(sc);
525296936Smmel
526296936Smmel	rv = clk_get_freq(sc->clk_cpu_g, &freq);
527296936Smmel	if (rv != 0) {
528296936Smmel		device_printf(dev, "Can't get CPU clock frequency\n");
529296936Smmel		return (rv);
530296936Smmel	}
531296936Smmel	if (sc->speedo_id < nitems(cpu_max_freq))
532296936Smmel		sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
533296936Smmel	else
534296936Smmel		sc->cpu_max_freq = cpu_max_freq[0];
535296936Smmel	sc->act_speed_point = get_speed_point(sc, freq);
536296936Smmel
537296936Smmel	/* Set safe startup CPU frequency. */
538296936Smmel	rv = set_cpu_freq(sc, 1632000000);
539296936Smmel	if (rv != 0) {
540296936Smmel		device_printf(dev, "Can't set initial CPU clock frequency\n");
541296936Smmel		return (rv);
542296936Smmel	}
543296936Smmel
544296936Smmel	/* This device is controlled by cpufreq(4). */
545296936Smmel	cpufreq_register(dev);
546296936Smmel
547296936Smmel	return (0);
548296936Smmel}
549296936Smmel
550296936Smmelstatic int
551296936Smmeltegra124_cpufreq_detach(device_t dev)
552296936Smmel{
553296936Smmel	struct tegra124_cpufreq_softc *sc;
554296936Smmel
555296936Smmel	sc = device_get_softc(dev);
556296936Smmel	cpufreq_unregister(dev);
557296936Smmel
558296936Smmel	if (sc->supply_vdd_cpu != NULL)
559296936Smmel		regulator_release(sc->supply_vdd_cpu);
560296936Smmel
561296936Smmel	if (sc->clk_cpu_g != NULL)
562296936Smmel		clk_release(sc->clk_cpu_g);
563296936Smmel	if (sc->clk_cpu_lp != NULL)
564296936Smmel		clk_release(sc->clk_cpu_lp);
565296936Smmel	if (sc->clk_pll_x != NULL)
566296936Smmel		clk_release(sc->clk_pll_x);
567296936Smmel	if (sc->clk_pll_p != NULL)
568296936Smmel		clk_release(sc->clk_pll_p);
569296936Smmel	if (sc->clk_dfll != NULL)
570296936Smmel		clk_release(sc->clk_dfll);
571296936Smmel	return (0);
572296936Smmel}
573296936Smmel
574296936Smmelstatic device_method_t tegra124_cpufreq_methods[] = {
575296936Smmel	/* Device interface */
576296936Smmel	DEVMETHOD(device_identify,	tegra124_cpufreq_identify),
577296936Smmel	DEVMETHOD(device_probe,		tegra124_cpufreq_probe),
578296936Smmel	DEVMETHOD(device_attach,	tegra124_cpufreq_attach),
579296936Smmel	DEVMETHOD(device_detach,	tegra124_cpufreq_detach),
580296936Smmel
581296936Smmel	/* cpufreq interface */
582296936Smmel	DEVMETHOD(cpufreq_drv_set,	tegra124_cpufreq_set),
583296936Smmel	DEVMETHOD(cpufreq_drv_get,	tegra124_cpufreq_get),
584296936Smmel	DEVMETHOD(cpufreq_drv_settings,	tegra124_cpufreq_settings),
585296936Smmel	DEVMETHOD(cpufreq_drv_type,	tegra124_cpufreq_type),
586296936Smmel
587296936Smmel	DEVMETHOD_END
588296936Smmel};
589296936Smmel
590296936Smmelstatic devclass_t tegra124_cpufreq_devclass;
591296936Smmelstatic driver_t tegra124_cpufreq_driver = {
592296936Smmel	"tegra124_cpufreq",
593296936Smmel	tegra124_cpufreq_methods,
594296936Smmel	sizeof(struct tegra124_cpufreq_softc),
595296936Smmel};
596296936Smmel
597296936SmmelDRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver,
598296936Smmel    tegra124_cpufreq_devclass, 0, 0);
599