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