tegra124_cpufreq.c revision 308336
1/*-
2 * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_cpufreq.c 308336 2016-11-05 11:00:19Z mmel $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/cpu.h>
34#include <sys/kernel.h>
35#include <sys/lock.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38
39#include <machine/bus.h>
40#include <machine/cpu.h>
41
42#include <dev/extres/clk/clk.h>
43#include <dev/extres/regulator/regulator.h>
44#include <dev/ofw/ofw_bus_subr.h>
45
46#include <arm/nvidia/tegra_efuse.h>
47
48#include "cpufreq_if.h"
49
50#define	XXX
51
52/* CPU voltage table entry */
53struct speedo_entry {
54	uint64_t		freq; 	/* Frequency point */
55	int			c0; 	/* Coeeficient values for */
56	int			c1;	/* quadratic equation: */
57	int 			c2;	/* c2 * speedo^2 + c1 * speedo + c0 */
58};
59
60struct cpu_volt_def {
61	int			min_uvolt;	/* Min allowed CPU voltage */
62	int			max_uvolt;	/* Max allowed CPU voltage */
63	int 			step_uvolt; 	/* Step of CPU voltage */
64	int			speedo_scale;	/* Scaling factor for cvt */
65	int			speedo_nitems;	/* Size of speedo table */
66	struct speedo_entry	*speedo_tbl;	/* CPU voltage table */
67};
68
69struct cpu_speed_point {
70	uint64_t		freq;		/* Frequecy */
71	int			uvolt;		/* Requested voltage */
72};
73
74static struct speedo_entry tegra124_speedo_dpll_tbl[] =
75{
76	{ 204000000ULL,	1112619, -29295, 402},
77	{ 306000000ULL,	1150460, -30585, 402},
78	{ 408000000ULL,	1190122, -31865, 402},
79	{ 510000000ULL,	1231606, -33155, 402},
80	{ 612000000ULL,	1274912, -34435, 402},
81	{ 714000000ULL,	1320040, -35725, 402},
82	{ 816000000ULL,	1366990, -37005, 402},
83	{ 918000000ULL,	1415762, -38295, 402},
84	{1020000000ULL,	1466355, -39575, 402},
85	{1122000000ULL,	1518771, -40865, 402},
86	{1224000000ULL,	1573009, -42145, 402},
87	{1326000000ULL,	1629068, -43435, 402},
88	{1428000000ULL,	1686950, -44715, 402},
89	{1530000000ULL,	1746653, -46005, 402},
90	{1632000000ULL,	1808179, -47285, 402},
91	{1734000000ULL,	1871526, -48575, 402},
92	{1836000000ULL,	1936696, -49855, 402},
93	{1938000000ULL,	2003687, -51145, 402},
94	{2014500000ULL,	2054787, -52095, 402},
95	{2116500000ULL,	2124957, -53385, 402},
96	{2218500000ULL,	2196950, -54665, 402},
97	{2320500000ULL,	2270765, -55955, 402},
98	{2320500000ULL,	2270765, -55955, 402},
99	{2422500000ULL,	2346401, -57235, 402},
100	{2524500000ULL,	2437299, -58535, 402},
101};
102
103static struct cpu_volt_def tegra124_cpu_volt_dpll_def =
104{
105	.min_uvolt =  900000,		/* 0.9 V */
106	.max_uvolt = 1260000,		/* 1.26 */
107	.step_uvolt =  10000,		/* 10 mV */
108	.speedo_scale = 100,
109	.speedo_nitems = nitems(tegra124_speedo_dpll_tbl),
110	.speedo_tbl = tegra124_speedo_dpll_tbl,
111};
112
113static struct speedo_entry tegra124_speedo_pllx_tbl[] =
114{
115	{ 204000000ULL,	 800000, 0, 0},
116	{ 306000000ULL,	 800000, 0, 0},
117	{ 408000000ULL,	 800000, 0, 0},
118	{ 510000000ULL,	 800000, 0, 0},
119	{ 612000000ULL,	 800000, 0, 0},
120	{ 714000000ULL,	 800000, 0, 0},
121	{ 816000000ULL,	 820000, 0, 0},
122	{ 918000000ULL,	 840000, 0, 0},
123	{1020000000ULL,	 880000, 0, 0},
124	{1122000000ULL,	 900000, 0, 0},
125	{1224000000ULL,	 930000, 0, 0},
126	{1326000000ULL,	 960000, 0, 0},
127	{1428000000ULL,	 990000, 0, 0},
128	{1530000000ULL,	1020000, 0, 0},
129	{1632000000ULL,	1070000, 0, 0},
130	{1734000000ULL,	1100000, 0, 0},
131	{1836000000ULL,	1140000, 0, 0},
132	{1938000000ULL,	1180000, 0, 0},
133	{2014500000ULL,	1220000, 0, 0},
134	{2116500000ULL,	1260000, 0, 0},
135	{2218500000ULL,	1310000, 0, 0},
136	{2320500000ULL,	1360000, 0, 0},
137	{2397000000ULL,	1400000, 0, 0},
138	{2499000000ULL,	1400000, 0, 0},
139};
140
141
142static struct cpu_volt_def tegra124_cpu_volt_pllx_def =
143{
144	.min_uvolt = 1000000,		/* XXX 0.9 V doesn't work on all boards */
145	.max_uvolt = 1260000,		/* 1.26 */
146	.step_uvolt =  10000,		/* 10 mV */
147	.speedo_scale = 100,
148	.speedo_nitems = nitems(tegra124_speedo_pllx_tbl),
149	.speedo_tbl = tegra124_speedo_pllx_tbl,
150};
151
152static uint64_t cpu_freq_tbl[] = {
153	 204000000ULL,
154	 306000000ULL,
155	 408000000ULL,
156	 510000000ULL,
157	 612000000ULL,
158	 714000000ULL,
159	 816000000ULL,
160	 918000000ULL,
161	1020000000ULL,
162	1122000000ULL,
163	1224000000ULL,
164	1326000000ULL,
165	1428000000ULL,
166	1530000000ULL,
167	1632000000ULL,
168	1734000000ULL,
169	1836000000ULL,
170	1938000000ULL,
171	2014000000ULL,
172	2116000000ULL,
173	2218000000ULL,
174	2320000000ULL,
175	2422000000ULL,
176	2524000000ULL,
177};
178
179static uint64_t cpu_max_freq[] = {
180	2014500000ULL,
181	2320500000ULL,
182	2116500000ULL,
183	2524500000ULL,
184};
185
186struct tegra124_cpufreq_softc {
187	device_t		dev;
188	phandle_t		node;
189
190	regulator_t		supply_vdd_cpu;
191	clk_t			clk_cpu_g;
192	clk_t			clk_cpu_lp;
193	clk_t			clk_pll_x;
194	clk_t			clk_pll_p;
195	clk_t			clk_dfll;
196
197	int 			process_id;
198	int 			speedo_id;
199	int 			speedo_value;
200
201	uint64_t		cpu_max_freq;
202	struct cpu_volt_def	*cpu_def;
203	struct cpu_speed_point	*speed_points;
204	int			nspeed_points;
205
206	struct cpu_speed_point	*act_speed_point;
207
208	int			latency;
209};
210
211static int cpufreq_lowest_freq = 1;
212TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq);
213
214#define	DIV_ROUND_CLOSEST(val, div)	(((val) + ((div) / 2)) / (div))
215
216#define	ROUND_UP(val, div)	roundup(val, div)
217#define	ROUND_DOWN(val, div)	rounddown(val, div)
218
219/*
220 * Compute requesetd voltage for given frequency and SoC process variations,
221 * - compute base voltage from speedo value using speedo table
222 * - round up voltage to next regulator step
223 * - clamp it to regulator limits
224 */
225static int
226freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq)
227{
228	int uv, scale, min_uvolt, max_uvolt, step_uvolt;
229	struct speedo_entry *ent;
230	int i;
231
232	/* Get speedo entry with higher frequency */
233	ent = NULL;
234	for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {
235		if (sc->cpu_def->speedo_tbl[i].freq >= freq) {
236			ent = &sc->cpu_def->speedo_tbl[i];
237			break;
238		}
239	}
240	if (ent == NULL)
241		ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
242	scale = sc->cpu_def->speedo_scale;
243
244
245	/* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
246	uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);
247	uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +
248	    ent->c0;
249	step_uvolt = sc->cpu_def->step_uvolt;
250	/* Round up it to next regulator step */
251	uv = ROUND_UP(uv, step_uvolt);
252
253	/* Clamp result */
254	min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);
255	max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);
256	if (uv < min_uvolt)
257		uv =  min_uvolt;
258	if (uv > max_uvolt)
259		uv =  max_uvolt;
260	return (uv);
261
262}
263
264static void
265build_speed_points(struct tegra124_cpufreq_softc *sc) {
266	int i;
267
268	sc->nspeed_points = nitems(cpu_freq_tbl);
269	sc->speed_points = malloc(sizeof(struct cpu_speed_point) *
270	    sc->nspeed_points, M_DEVBUF, M_NOWAIT);
271	for (i = 0; i < sc->nspeed_points; i++) {
272		sc->speed_points[i].freq = cpu_freq_tbl[i];
273		sc->speed_points[i].uvolt = freq_to_voltage(sc,
274		    cpu_freq_tbl[i]);
275	}
276}
277
278static struct cpu_speed_point *
279get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq)
280{
281	int i;
282
283	if (sc->speed_points[0].freq >= freq)
284		return (sc->speed_points + 0);
285
286	for (i = 0; i < sc->nspeed_points - 1; i++) {
287		if (sc->speed_points[i + 1].freq > freq)
288			return (sc->speed_points + i);
289	}
290
291	return (sc->speed_points + sc->nspeed_points - 1);
292}
293
294static int
295tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
296{
297	struct tegra124_cpufreq_softc *sc;
298	int i, j, max_cnt;
299
300	if (sets == NULL || count == NULL)
301		return (EINVAL);
302
303	sc = device_get_softc(dev);
304	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
305
306	max_cnt = min(sc->nspeed_points, *count);
307	for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
308		if (sc->cpu_max_freq < sc->speed_points[j].freq)
309			continue;
310		sets[i].freq = sc->speed_points[j].freq / 1000000;
311		sets[i].volts = sc->speed_points[j].uvolt / 1000;
312		sets[i].lat = sc->latency;
313		sets[i].dev = dev;
314		i++;
315	}
316	*count = i;
317
318	return (0);
319}
320
321static int
322set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq)
323{
324	struct cpu_speed_point *point;
325	int rv;
326
327	point = get_speed_point(sc, freq);
328
329	if (sc->act_speed_point->uvolt < point->uvolt) {
330		/* set cpu voltage */
331		rv = regulator_set_voltage(sc->supply_vdd_cpu,
332		    point->uvolt, point->uvolt);
333		DELAY(10000);
334		if (rv != 0)
335			return (rv);
336	}
337
338	/* Switch supermux to PLLP first */
339	rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p);
340	if (rv != 0) {
341		device_printf(sc->dev, "Can't set parent to PLLP\n");
342		return (rv);
343	}
344
345	/* Set PLLX frequency */
346	rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
347	if (rv != 0) {
348		device_printf(sc->dev, "Can't set CPU clock frequency\n");
349		return (rv);
350	}
351
352	rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x);
353	if (rv != 0) {
354		device_printf(sc->dev, "Can't set parent to PLLX\n");
355		return (rv);
356	}
357
358	if (sc->act_speed_point->uvolt > point->uvolt) {
359		/* set cpu voltage */
360		rv = regulator_set_voltage(sc->supply_vdd_cpu,
361		    point->uvolt, point->uvolt);
362		if (rv != 0)
363			return (rv);
364	}
365
366	sc->act_speed_point = point;
367
368	return (0);
369}
370
371static int
372tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf)
373{
374	struct tegra124_cpufreq_softc *sc;
375	uint64_t freq;
376	int rv;
377
378	if (cf == NULL || cf->freq < 0)
379		return (EINVAL);
380
381	sc = device_get_softc(dev);
382
383	freq = cf->freq;
384	if (freq < cpufreq_lowest_freq)
385		freq = cpufreq_lowest_freq;
386	freq *= 1000000;
387	if (freq >= sc->cpu_max_freq)
388		freq = sc->cpu_max_freq;
389	rv = set_cpu_freq(sc, freq);
390
391	return (rv);
392}
393
394static int
395tegra124_cpufreq_get(device_t dev, struct cf_setting *cf)
396{
397	struct tegra124_cpufreq_softc *sc;
398
399	if (cf == NULL)
400		return (EINVAL);
401
402	sc = device_get_softc(dev);
403	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
404	cf->dev = NULL;
405	cf->freq = sc->act_speed_point->freq / 1000000;
406	cf->volts = sc->act_speed_point->uvolt / 1000;
407	/* Transition latency in us. */
408	cf->lat = sc->latency;
409	/* Driver providing this setting. */
410	cf->dev = dev;
411
412	return (0);
413}
414
415
416static int
417tegra124_cpufreq_type(device_t dev, int *type)
418{
419
420	if (type == NULL)
421		return (EINVAL);
422	*type = CPUFREQ_TYPE_ABSOLUTE;
423
424	return (0);
425}
426
427static int
428get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node)
429{
430	int rv;
431	device_t parent_dev;
432
433	parent_dev =  device_get_parent(sc->dev);
434	rv = regulator_get_by_ofw_property(parent_dev, 0, "vdd-cpu-supply",
435	    &sc->supply_vdd_cpu);
436	if (rv != 0) {
437		device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n");
438		return (rv);
439	}
440
441	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);
442	if (rv != 0) {
443		device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
444		return (ENXIO);
445	}
446
447	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_lp", &sc->clk_cpu_lp);
448	if (rv != 0) {
449		device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n");
450		return (ENXIO);
451	}
452
453	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);
454	if (rv != 0) {
455		device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
456		return (ENXIO);
457	}
458	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);
459	if (rv != 0) {
460		device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
461		return (ENXIO);
462	}
463	rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
464	if (rv != 0) {
465		/* XXX DPLL is not implemented yet */
466/*
467		device_printf(sc->dev, "Cannot get 'dfll' clock\n");
468		return (ENXIO);
469*/
470	}
471	return (0);
472}
473
474static void
475tegra124_cpufreq_identify(driver_t *driver, device_t parent)
476{
477
478	if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL)
479		return;
480	if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL)
481		device_printf(parent, "add child failed\n");
482}
483
484static int
485tegra124_cpufreq_probe(device_t dev)
486{
487
488	if (device_get_unit(dev) != 0)
489		return (ENXIO);
490	device_set_desc(dev, "CPU Frequency Control");
491
492	return (0);
493}
494
495static int
496tegra124_cpufreq_attach(device_t dev)
497{
498	struct tegra124_cpufreq_softc *sc;
499	uint64_t freq;
500	int rv;
501
502	sc = device_get_softc(dev);
503	sc->dev = dev;
504	sc->node = ofw_bus_get_node(device_get_parent(dev));
505
506	sc->process_id = tegra_sku_info.cpu_process_id;
507	sc->speedo_id = tegra_sku_info.cpu_speedo_id;
508	sc->speedo_value = tegra_sku_info.cpu_speedo_value;
509
510	/* Tegra 124 */
511	/* XXX DPLL is not implemented yet */
512	if (1)
513		sc->cpu_def = &tegra124_cpu_volt_pllx_def;
514	else
515		sc->cpu_def = &tegra124_cpu_volt_dpll_def;
516
517
518	rv = get_fdt_resources(sc, sc->node);
519	if (rv !=  0) {
520		return (rv);
521	}
522
523	build_speed_points(sc);
524
525	rv = clk_get_freq(sc->clk_cpu_g, &freq);
526	if (rv != 0) {
527		device_printf(dev, "Can't get CPU clock frequency\n");
528		return (rv);
529	}
530	if (sc->speedo_id < nitems(cpu_max_freq))
531		sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
532	else
533		sc->cpu_max_freq = cpu_max_freq[0];
534	sc->act_speed_point = get_speed_point(sc, freq);
535
536	/* Set safe startup CPU frequency. */
537	rv = set_cpu_freq(sc, 1632000000);
538	if (rv != 0) {
539		device_printf(dev, "Can't set initial CPU clock frequency\n");
540		return (rv);
541	}
542
543	/* This device is controlled by cpufreq(4). */
544	cpufreq_register(dev);
545
546	return (0);
547}
548
549static int
550tegra124_cpufreq_detach(device_t dev)
551{
552	struct tegra124_cpufreq_softc *sc;
553
554	sc = device_get_softc(dev);
555	cpufreq_unregister(dev);
556
557	if (sc->supply_vdd_cpu != NULL)
558		regulator_release(sc->supply_vdd_cpu);
559
560	if (sc->clk_cpu_g != NULL)
561		clk_release(sc->clk_cpu_g);
562	if (sc->clk_cpu_lp != NULL)
563		clk_release(sc->clk_cpu_lp);
564	if (sc->clk_pll_x != NULL)
565		clk_release(sc->clk_pll_x);
566	if (sc->clk_pll_p != NULL)
567		clk_release(sc->clk_pll_p);
568	if (sc->clk_dfll != NULL)
569		clk_release(sc->clk_dfll);
570	return (0);
571}
572
573static device_method_t tegra124_cpufreq_methods[] = {
574	/* Device interface */
575	DEVMETHOD(device_identify,	tegra124_cpufreq_identify),
576	DEVMETHOD(device_probe,		tegra124_cpufreq_probe),
577	DEVMETHOD(device_attach,	tegra124_cpufreq_attach),
578	DEVMETHOD(device_detach,	tegra124_cpufreq_detach),
579
580	/* cpufreq interface */
581	DEVMETHOD(cpufreq_drv_set,	tegra124_cpufreq_set),
582	DEVMETHOD(cpufreq_drv_get,	tegra124_cpufreq_get),
583	DEVMETHOD(cpufreq_drv_settings,	tegra124_cpufreq_settings),
584	DEVMETHOD(cpufreq_drv_type,	tegra124_cpufreq_type),
585
586	DEVMETHOD_END
587};
588
589static devclass_t tegra124_cpufreq_devclass;
590static DEFINE_CLASS_0(cpufreq, tegra124_cpufreq_driver,
591    tegra124_cpufreq_methods, sizeof(struct tegra124_cpufreq_softc));
592DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver,
593    tegra124_cpufreq_devclass, NULL, NULL);
594