2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/param.h>
29 #include <sys/systm.h>
32 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/module.h>
37 #include <machine/bus.h>
38 #include <machine/cpu.h>
40 #include <dev/clk/clk.h>
41 #include <dev/regulator/regulator.h>
42 #include <dev/ofw/ofw_bus_subr.h>
44 #include <arm/nvidia/tegra_efuse.h>
46 #include "cpufreq_if.h"
48 /* CPU voltage table entry */
50 uint64_t freq
; /* Frequency point */
51 int c0
; /* Coeeficient values for */
52 int c1
; /* quadratic equation: */
53 int c2
; /* c2 * speedo^2 + c1 * speedo + c0 */
57 int min_uvolt
; /* Min allowed CPU voltage */
58 int max_uvolt
; /* Max allowed CPU voltage */
59 int step_uvolt
; /* Step of CPU voltage */
60 int speedo_scale
; /* Scaling factor for cvt */
61 int speedo_nitems
; /* Size of speedo table */
62 struct speedo_entry
*speedo_tbl
; /* CPU voltage table */
65 struct cpu_speed_point
{
66 uint64_t freq
; /* Frequecy */
67 int uvolt
; /* Requested voltage */
70 static struct speedo_entry tegra210_speedo_tbl
[] =
72 {204000000UL, 1007452, -23865, 370},
73 {306000000UL, 1052709, -24875, 370},
74 {408000000UL, 1099069, -25895, 370},
75 {510000000UL, 1146534, -26905, 370},
76 {612000000UL, 1195102, -27915, 370},
77 {714000000UL, 1244773, -28925, 370},
78 {816000000UL, 1295549, -29935, 370},
79 {918000000UL, 1347428, -30955, 370},
80 {1020000000UL, 1400411, -31965, 370},
81 {1122000000UL, 1454497, -32975, 370},
82 {1224000000UL, 1509687, -33985, 370},
83 {1326000000UL, 1565981, -35005, 370},
84 {1428000000UL, 1623379, -36015, 370},
85 {1530000000UL, 1681880, -37025, 370},
86 {1632000000UL, 1741485, -38035, 370},
87 {1734000000UL, 1802194, -39055, 370},
88 {1836000000UL, 1864006, -40065, 370},
89 {1912500000UL, 1910780, -40815, 370},
90 {2014500000UL, 1227000, 0, 0},
91 {2218500000UL, 1227000, 0, 0},
94 static struct cpu_volt_def tegra210_cpu_volt_def
=
96 .min_uvolt
= 900000, /* 0.9 V */
97 .max_uvolt
= 1227000, /* 1.227 */
98 .step_uvolt
= 10000, /* 10 mV */
100 .speedo_nitems
= nitems(tegra210_speedo_tbl
),
101 .speedo_tbl
= tegra210_speedo_tbl
,
104 static uint64_t cpu_max_freq
[] = {
118 static uint64_t cpu_freq_tbl
[] = {
141 struct tegra210_cpufreq_softc
{
154 uint64_t cpu_max_freq
;
155 struct cpu_volt_def
*cpu_def
;
156 struct cpu_speed_point
*speed_points
;
159 struct cpu_speed_point
*act_speed_point
;
164 static int cpufreq_lowest_freq
= 1;
165 TUNABLE_INT("hw.tegra210.cpufreq.lowest_freq", &cpufreq_lowest_freq
);
167 #define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div))
169 #define ROUND_UP(val, div) roundup(val, div)
170 #define ROUND_DOWN(val, div) rounddown(val, div)
173 * Compute requesetd voltage for given frequency and SoC process variations,
174 * - compute base voltage from speedo value using speedo table
175 * - round up voltage to next regulator step
176 * - clamp it to regulator limits
179 freq_to_voltage(struct tegra210_cpufreq_softc
*sc
, uint64_t freq
)
181 int uv
, scale
, min_uvolt
, max_uvolt
, step_uvolt
;
182 struct speedo_entry
*ent
;
185 /* Get speedo entry with higher frequency */
187 for (i
= 0; i
< sc
->cpu_def
->speedo_nitems
; i
++) {
188 if (sc
->cpu_def
->speedo_tbl
[i
].freq
>= freq
) {
189 ent
= &sc
->cpu_def
->speedo_tbl
[i
];
194 ent
= &sc
->cpu_def
->speedo_tbl
[sc
->cpu_def
->speedo_nitems
- 1];
195 scale
= sc
->cpu_def
->speedo_scale
;
198 /* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
199 uv
= DIV_ROUND_CLOSEST(ent
->c2
* sc
->speedo_value
, scale
);
200 uv
= DIV_ROUND_CLOSEST((uv
+ ent
->c1
) * sc
->speedo_value
, scale
) +
202 step_uvolt
= sc
->cpu_def
->step_uvolt
;
203 /* Round up it to next regulator step */
204 uv
= ROUND_UP(uv
, step_uvolt
);
207 min_uvolt
= ROUND_UP(sc
->cpu_def
->min_uvolt
, step_uvolt
);
208 max_uvolt
= ROUND_DOWN(sc
->cpu_def
->max_uvolt
, step_uvolt
);
218 build_speed_points(struct tegra210_cpufreq_softc
*sc
) {
221 sc
->nspeed_points
= nitems(cpu_freq_tbl
);
222 sc
->speed_points
= malloc(sizeof(struct cpu_speed_point
) *
223 sc
->nspeed_points
, M_DEVBUF
, M_NOWAIT
);
224 for (i
= 0; i
< sc
->nspeed_points
; i
++) {
225 sc
->speed_points
[i
].freq
= cpu_freq_tbl
[i
];
226 sc
->speed_points
[i
].uvolt
= freq_to_voltage(sc
,
231 static struct cpu_speed_point
*
232 get_speed_point(struct tegra210_cpufreq_softc
*sc
, uint64_t freq
)
236 if (sc
->speed_points
[0].freq
>= freq
)
237 return (sc
->speed_points
+ 0);
239 for (i
= 0; i
< sc
->nspeed_points
- 1; i
++) {
240 if (sc
->speed_points
[i
+ 1].freq
> freq
)
241 return (sc
->speed_points
+ i
);
244 return (sc
->speed_points
+ sc
->nspeed_points
- 1);
248 tegra210_cpufreq_settings(device_t dev
, struct cf_setting
*sets
, int *count
)
250 struct tegra210_cpufreq_softc
*sc
;
253 if (sets
== NULL
|| count
== NULL
)
256 sc
= device_get_softc(dev
);
257 memset(sets
, CPUFREQ_VAL_UNKNOWN
, sizeof(*sets
) * (*count
));
259 for (i
= 0, j
= sc
->nspeed_points
- 1; j
>= 0; j
--) {
260 if (sc
->cpu_max_freq
< sc
->speed_points
[j
].freq
)
262 sets
[i
].freq
= sc
->speed_points
[j
].freq
/ 1000000;
263 sets
[i
].volts
= sc
->speed_points
[j
].uvolt
/ 1000;
264 sets
[i
].lat
= sc
->latency
;
274 set_cpu_freq(struct tegra210_cpufreq_softc
*sc
, uint64_t freq
)
276 struct cpu_speed_point
*point
;
279 point
= get_speed_point(sc
, freq
);
281 /* Set PLLX frequency */
282 rv
= clk_set_freq(sc
->clk_pll_x
, point
->freq
, CLK_SET_ROUND_DOWN
);
284 device_printf(sc
->dev
, "Can't set CPU clock frequency\n");
288 sc
->act_speed_point
= point
;
294 tegra210_cpufreq_set(device_t dev
, const struct cf_setting
*cf
)
296 struct tegra210_cpufreq_softc
*sc
;
300 if (cf
== NULL
|| cf
->freq
< 0)
303 sc
= device_get_softc(dev
);
306 if (freq
< cpufreq_lowest_freq
)
307 freq
= cpufreq_lowest_freq
;
309 if (freq
>= sc
->cpu_max_freq
)
310 freq
= sc
->cpu_max_freq
;
311 rv
= set_cpu_freq(sc
, freq
);
317 tegra210_cpufreq_get(device_t dev
, struct cf_setting
*cf
)
319 struct tegra210_cpufreq_softc
*sc
;
324 sc
= device_get_softc(dev
);
325 memset(cf
, CPUFREQ_VAL_UNKNOWN
, sizeof(*cf
));
327 cf
->freq
= sc
->act_speed_point
->freq
/ 1000000;
328 cf
->volts
= sc
->act_speed_point
->uvolt
/ 1000;
329 /* Transition latency in us. */
330 cf
->lat
= sc
->latency
;
331 /* Driver providing this setting. */
339 tegra210_cpufreq_type(device_t dev
, int *type
)
344 *type
= CPUFREQ_TYPE_ABSOLUTE
;
350 get_fdt_resources(struct tegra210_cpufreq_softc
*sc
, phandle_t node
)
355 parent_dev
= device_get_parent(sc
->dev
);
357 rv
= clk_get_by_ofw_name(parent_dev
, 0, "cpu_g", &sc
->clk_cpu_g
);
359 device_printf(sc
->dev
, "Cannot get 'cpu_g' clock: %d\n", rv
);
363 rv
= clk_get_by_ofw_name(parent_dev
, 0, "pll_x", &sc
->clk_pll_x
);
365 device_printf(sc
->dev
, "Cannot get 'pll_x' clock\n");
368 rv
= clk_get_by_ofw_name(parent_dev
, 0, "pll_p", &sc
->clk_pll_p
);
370 device_printf(parent_dev
, "Cannot get 'pll_p' clock\n");
373 rv
= clk_get_by_ofw_name(parent_dev
, 0, "dfll", &sc
->clk_dfll
);
375 /* XXX DPLL is not implemented yet */
378 device_printf(sc
->dev
, "Cannot get 'dfll' clock\n");
386 tegra210_cpufreq_identify(driver_t
*driver
, device_t parent
)
390 root
= OF_finddevice("/");
391 if (!ofw_bus_node_is_compatible(root
, "nvidia,tegra210"))
394 if (device_get_unit(parent
) != 0)
396 if (device_find_child(parent
, "tegra210_cpufreq", -1) != NULL
)
398 if (BUS_ADD_CHILD(parent
, 0, "tegra210_cpufreq", -1) == NULL
)
399 device_printf(parent
, "add child failed\n");
403 tegra210_cpufreq_probe(device_t dev
)
406 device_set_desc(dev
, "CPU Frequency Control");
412 tegra210_cpufreq_attach(device_t dev
)
414 struct tegra210_cpufreq_softc
*sc
;
418 sc
= device_get_softc(dev
);
420 sc
->node
= ofw_bus_get_node(device_get_parent(dev
));
422 sc
->process_id
= tegra_sku_info
.cpu_process_id
;
423 sc
->speedo_id
= tegra_sku_info
.cpu_speedo_id
;
424 sc
->speedo_value
= tegra_sku_info
.cpu_speedo_value
;
426 sc
->cpu_def
= &tegra210_cpu_volt_def
;
428 rv
= get_fdt_resources(sc
, sc
->node
);
433 build_speed_points(sc
);
435 rv
= clk_get_freq(sc
->clk_cpu_g
, &freq
);
437 device_printf(dev
, "Can't get CPU clock frequency\n");
440 if (sc
->speedo_id
< nitems(cpu_max_freq
))
441 sc
->cpu_max_freq
= cpu_max_freq
[sc
->speedo_id
];
443 sc
->cpu_max_freq
= cpu_max_freq
[0];
444 sc
->act_speed_point
= get_speed_point(sc
, freq
);
446 /* Set safe startup CPU frequency. */
447 rv
= set_cpu_freq(sc
, 1632000000);
449 device_printf(dev
, "Can't set initial CPU clock frequency\n");
453 /* This device is controlled by cpufreq(4). */
454 cpufreq_register(dev
);
460 tegra210_cpufreq_detach(device_t dev
)
462 struct tegra210_cpufreq_softc
*sc
;
464 sc
= device_get_softc(dev
);
465 cpufreq_unregister(dev
);
467 if (sc
->clk_cpu_g
!= NULL
)
468 clk_release(sc
->clk_cpu_g
);
469 if (sc
->clk_pll_x
!= NULL
)
470 clk_release(sc
->clk_pll_x
);
471 if (sc
->clk_pll_p
!= NULL
)
472 clk_release(sc
->clk_pll_p
);
473 if (sc
->clk_dfll
!= NULL
)
474 clk_release(sc
->clk_dfll
);
478 static device_method_t tegra210_cpufreq_methods
[] = {
479 /* Device interface */
480 DEVMETHOD(device_identify
, tegra210_cpufreq_identify
),
481 DEVMETHOD(device_probe
, tegra210_cpufreq_probe
),
482 DEVMETHOD(device_attach
, tegra210_cpufreq_attach
),
483 DEVMETHOD(device_detach
, tegra210_cpufreq_detach
),
485 /* cpufreq interface */
486 DEVMETHOD(cpufreq_drv_set
, tegra210_cpufreq_set
),
487 DEVMETHOD(cpufreq_drv_get
, tegra210_cpufreq_get
),
488 DEVMETHOD(cpufreq_drv_settings
, tegra210_cpufreq_settings
),
489 DEVMETHOD(cpufreq_drv_type
, tegra210_cpufreq_type
),
494 static DEFINE_CLASS_0(tegra210_cpufreq
, tegra210_cpufreq_driver
,
495 tegra210_cpufreq_methods
, sizeof(struct tegra210_cpufreq_softc
));
496 DRIVER_MODULE(tegra210_cpufreq
, cpu
, tegra210_cpufreq_driver
, NULL
, NULL
);