xref: /freebsd/sys/dev/cpufreq/cpufreq_dt.c (revision b2f0caf160993c30a0d0e3d52e8a2d03616908cd)
1 /*-
2  * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.Org>
3  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
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 ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * 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 /*
28  * Generic DT based cpufreq driver
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/rman.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/cpu.h>
38 #include <sys/cpuset.h>
39 #include <sys/smp.h>
40 
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43 
44 #include <dev/clk/clk.h>
45 #include <dev/regulator/regulator.h>
46 
47 #include "cpufreq_if.h"
48 
49 #if 0
50 #define DPRINTF(dev, msg...) device_printf(dev, "cpufreq_dt: " msg);
51 #else
52 #define DPRINTF(dev, msg...)
53 #endif
54 
55 enum opp_version {
56 	OPP_V1 = 1,
57 	OPP_V2,
58 };
59 
60 struct cpufreq_dt_opp {
61 	uint64_t	freq;
62 	uint32_t	uvolt_target;
63 	uint32_t	uvolt_min;
64 	uint32_t	uvolt_max;
65 	uint32_t	uamps;
66 	uint32_t	clk_latency;
67 	bool		turbo_mode;
68 	bool		opp_suspend;
69 };
70 
71 #define	CPUFREQ_DT_HAVE_REGULATOR(sc)	((sc)->reg != NULL)
72 
73 struct cpufreq_dt_softc {
74 	device_t dev;
75 	clk_t clk;
76 	regulator_t reg;
77 
78 	struct cpufreq_dt_opp *opp;
79 	ssize_t nopp;
80 
81 	int cpu;
82 	cpuset_t cpus;
83 };
84 
85 static void
cpufreq_dt_notify(device_t dev,uint64_t freq)86 cpufreq_dt_notify(device_t dev, uint64_t freq)
87 {
88 	struct cpufreq_dt_softc *sc;
89 	struct pcpu *pc;
90 	int cpu;
91 
92 	sc = device_get_softc(dev);
93 
94 	CPU_FOREACH(cpu) {
95 		if (CPU_ISSET(cpu, &sc->cpus)) {
96 			pc = pcpu_find(cpu);
97 			pc->pc_clock = freq;
98 		}
99 	}
100 }
101 
102 static const struct cpufreq_dt_opp *
cpufreq_dt_find_opp(device_t dev,uint64_t freq)103 cpufreq_dt_find_opp(device_t dev, uint64_t freq)
104 {
105 	struct cpufreq_dt_softc *sc;
106 	uint64_t diff, best_diff;
107 	ssize_t n, best_n;
108 
109 	sc = device_get_softc(dev);
110 
111 	diff = 0;
112 	best_diff = ~0;
113 	DPRINTF(dev, "Looking for freq %ju\n", freq);
114 	for (n = 0; n < sc->nopp; n++) {
115 		diff = abs64((int64_t)sc->opp[n].freq - (int64_t)freq);
116 		DPRINTF(dev, "Testing %ju, diff is %ju\n", sc->opp[n].freq, diff);
117 		if (diff < best_diff) {
118 			best_diff = diff;
119 			best_n = n;
120 			DPRINTF(dev, "%ju is best for now\n", sc->opp[n].freq);
121 		}
122 	}
123 
124 	DPRINTF(dev, "Will use %ju\n", sc->opp[best_n].freq);
125 	return (&sc->opp[best_n]);
126 }
127 
128 static void
cpufreq_dt_opp_to_setting(device_t dev,const struct cpufreq_dt_opp * opp,struct cf_setting * set)129 cpufreq_dt_opp_to_setting(device_t dev, const struct cpufreq_dt_opp *opp,
130     struct cf_setting *set)
131 {
132 
133 	memset(set, 0, sizeof(*set));
134 	set->freq = opp->freq / 1000000;
135 	set->volts = opp->uvolt_target / 1000;
136 	set->power = CPUFREQ_VAL_UNKNOWN;
137 	set->lat = opp->clk_latency;
138 	set->dev = dev;
139 }
140 
141 static int
cpufreq_dt_get(device_t dev,struct cf_setting * set)142 cpufreq_dt_get(device_t dev, struct cf_setting *set)
143 {
144 	struct cpufreq_dt_softc *sc;
145 	const struct cpufreq_dt_opp *opp;
146 	uint64_t freq;
147 
148 	sc = device_get_softc(dev);
149 
150 	DPRINTF(dev, "cpufreq_dt_get\n");
151 	if (clk_get_freq(sc->clk, &freq) != 0)
152 		return (ENXIO);
153 
154 	opp = cpufreq_dt_find_opp(dev, freq);
155 	if (opp == NULL) {
156 		device_printf(dev, "Can't find the current freq in opp\n");
157 		return (ENOENT);
158 	}
159 
160 	cpufreq_dt_opp_to_setting(dev, opp, set);
161 
162 	DPRINTF(dev, "Current freq %dMhz\n", set->freq);
163 	return (0);
164 }
165 
166 static int
cpufreq_dt_set(device_t dev,const struct cf_setting * set)167 cpufreq_dt_set(device_t dev, const struct cf_setting *set)
168 {
169 	struct cpufreq_dt_softc *sc;
170 	const struct cpufreq_dt_opp *opp, *copp;
171 	uint64_t freq;
172 	int uvolt, error;
173 
174 	sc = device_get_softc(dev);
175 
176 	DPRINTF(dev, "Working on cpu %d\n", sc->cpu);
177 	DPRINTF(dev, "We have %d cpu on this dev\n", CPU_COUNT(&sc->cpus));
178 	if (!CPU_ISSET(sc->cpu, &sc->cpus)) {
179 		DPRINTF(dev, "Not for this CPU\n");
180 		return (0);
181 	}
182 
183 	if (clk_get_freq(sc->clk, &freq) != 0) {
184 		device_printf(dev, "Can't get current clk freq\n");
185 		return (ENXIO);
186 	}
187 
188 	/*
189 	 * Only do the regulator work if it's required.
190 	 */
191 	if (CPUFREQ_DT_HAVE_REGULATOR(sc)) {
192 		/* Try to get current valtage by using regulator first. */
193 		error = regulator_get_voltage(sc->reg, &uvolt);
194 		if (error != 0) {
195 			/*
196 			 * Try oppoints table as backup way. However,
197 			 * this is insufficient because the actual processor
198 			 * frequency may not be in the table. PLL frequency
199 			 * granularity can be different that granularity of
200 			 * oppoint table.
201 			 */
202 			copp = cpufreq_dt_find_opp(sc->dev, freq);
203 			if (copp == NULL) {
204 				device_printf(dev,
205 				    "Can't find the current freq in opp\n");
206 				return (ENOENT);
207 			}
208 			uvolt = copp->uvolt_target;
209 		}
210 	} else
211 		uvolt = 0;
212 
213 	opp = cpufreq_dt_find_opp(sc->dev, set->freq * 1000000);
214 	if (opp == NULL) {
215 		device_printf(dev, "Couldn't find an opp for this freq\n");
216 		return (EINVAL);
217 	}
218 	DPRINTF(sc->dev, "Current freq %ju, uvolt: %d\n", freq, uvolt);
219 	DPRINTF(sc->dev, "Target freq %ju, , uvolt: %d\n",
220 	    opp->freq, opp->uvolt_target);
221 
222 	if (CPUFREQ_DT_HAVE_REGULATOR(sc) && (uvolt < opp->uvolt_target)) {
223 		DPRINTF(dev, "Changing regulator from %u to %u\n",
224 		    uvolt, opp->uvolt_target);
225 		error = regulator_set_voltage(sc->reg,
226 		    opp->uvolt_min,
227 		    opp->uvolt_max);
228 		if (error != 0) {
229 			DPRINTF(dev, "Failed, backout\n");
230 			return (ENXIO);
231 		}
232 	}
233 
234 	DPRINTF(dev, "Setting clk to %ju\n", opp->freq);
235 	error = clk_set_freq(sc->clk, opp->freq, CLK_SET_ROUND_DOWN);
236 	if (error != 0) {
237 		DPRINTF(dev, "Failed, backout\n");
238 		/* Restore previous voltage (best effort) */
239 		if (CPUFREQ_DT_HAVE_REGULATOR(sc))
240 			error = regulator_set_voltage(sc->reg,
241 			    copp->uvolt_min,
242 			    copp->uvolt_max);
243 		return (ENXIO);
244 	}
245 
246 	if (CPUFREQ_DT_HAVE_REGULATOR(sc) && (uvolt > opp->uvolt_target)) {
247 		DPRINTF(dev, "Changing regulator from %u to %u\n",
248 		    uvolt, opp->uvolt_target);
249 		error = regulator_set_voltage(sc->reg,
250 		    opp->uvolt_min,
251 		    opp->uvolt_max);
252 		if (error != 0) {
253 			DPRINTF(dev, "Failed to switch regulator to %d\n",
254 			    opp->uvolt_target);
255 			/* Restore previous CPU frequency (best effort) */
256 			(void)clk_set_freq(sc->clk, copp->freq, 0);
257 			return (ENXIO);
258 		}
259 	}
260 
261 	if (clk_get_freq(sc->clk, &freq) == 0)
262 		cpufreq_dt_notify(dev, freq);
263 
264 	return (0);
265 }
266 
267 static int
cpufreq_dt_type(device_t dev,int * type)268 cpufreq_dt_type(device_t dev, int *type)
269 {
270 	if (type == NULL)
271 		return (EINVAL);
272 
273 	*type = CPUFREQ_TYPE_ABSOLUTE;
274 	return (0);
275 }
276 
277 static int
cpufreq_dt_settings(device_t dev,struct cf_setting * sets,int * count)278 cpufreq_dt_settings(device_t dev, struct cf_setting *sets, int *count)
279 {
280 	struct cpufreq_dt_softc *sc;
281 	ssize_t n;
282 
283 	DPRINTF(dev, "cpufreq_dt_settings\n");
284 	if (sets == NULL || count == NULL)
285 		return (EINVAL);
286 
287 	sc = device_get_softc(dev);
288 
289 	if (*count < sc->nopp) {
290 		*count = (int)sc->nopp;
291 		return (E2BIG);
292 	}
293 
294 	for (n = 0; n < sc->nopp; n++)
295 		cpufreq_dt_opp_to_setting(dev, &sc->opp[n], &sets[n]);
296 
297 	*count = (int)sc->nopp;
298 
299 	return (0);
300 }
301 
302 static void
cpufreq_dt_identify(driver_t * driver,device_t parent)303 cpufreq_dt_identify(driver_t *driver, device_t parent)
304 {
305 	phandle_t node;
306 
307 	/* Properties must be listed under node /cpus/cpu@0 */
308 	node = ofw_bus_get_node(parent);
309 
310 	/* The cpu@0 node must have the following properties */
311 	if (!OF_hasprop(node, "clocks"))
312 		return;
313 
314 	if (!OF_hasprop(node, "operating-points") &&
315 	    !OF_hasprop(node, "operating-points-v2"))
316 		return;
317 
318 	if (device_find_child(parent, "cpufreq_dt", -1) != NULL)
319 		return;
320 
321 	if (BUS_ADD_CHILD(parent, 0, "cpufreq_dt", device_get_unit(parent))
322 	    == NULL)
323 		device_printf(parent, "add cpufreq_dt child failed\n");
324 }
325 
326 static int
cpufreq_dt_probe(device_t dev)327 cpufreq_dt_probe(device_t dev)
328 {
329 	phandle_t node;
330 
331 	node = ofw_bus_get_node(device_get_parent(dev));
332 
333 	/*
334 	 * Note - supply isn't required here for probe; we'll check
335 	 * it out in more detail during attach.
336 	 */
337 	if (!OF_hasprop(node, "clocks"))
338 		return (ENXIO);
339 
340 	if (!OF_hasprop(node, "operating-points") &&
341 	  !OF_hasprop(node, "operating-points-v2"))
342 		return (ENXIO);
343 
344 	device_set_desc(dev, "Generic cpufreq driver");
345 	return (BUS_PROBE_GENERIC);
346 }
347 
348 static int
cpufreq_dt_oppv1_parse(struct cpufreq_dt_softc * sc,phandle_t node)349 cpufreq_dt_oppv1_parse(struct cpufreq_dt_softc *sc, phandle_t node)
350 {
351 	uint32_t *opp, lat;
352 	ssize_t n;
353 
354 	sc->nopp = OF_getencprop_alloc_multi(node, "operating-points",
355 	    sizeof(uint32_t) * 2, (void **)&opp);
356 	if (sc->nopp == -1)
357 		return (ENXIO);
358 
359 	if (OF_getencprop(node, "clock-latency", &lat, sizeof(lat)) == -1)
360 		lat = CPUFREQ_VAL_UNKNOWN;
361 
362 	sc->opp = malloc(sizeof(*sc->opp) * sc->nopp, M_DEVBUF, M_WAITOK);
363 
364 	for (n = 0; n < sc->nopp; n++) {
365 		sc->opp[n].freq = opp[n * 2 + 0] * 1000;
366 		sc->opp[n].uvolt_min = opp[n * 2 + 1];
367 		sc->opp[n].uvolt_max = sc->opp[n].uvolt_min;
368 		sc->opp[n].uvolt_target = sc->opp[n].uvolt_min;
369 		sc->opp[n].clk_latency = lat;
370 
371 		if (bootverbose)
372 			device_printf(sc->dev, "%ju.%03ju MHz, %u uV\n",
373 			    sc->opp[n].freq / 1000000,
374 			    sc->opp[n].freq % 1000000,
375 			    sc->opp[n].uvolt_target);
376 	}
377 	free(opp, M_OFWPROP);
378 
379 	return (0);
380 }
381 
382 static int
cpufreq_dt_oppv2_parse(struct cpufreq_dt_softc * sc,phandle_t node)383 cpufreq_dt_oppv2_parse(struct cpufreq_dt_softc *sc, phandle_t node)
384 {
385 	phandle_t opp, opp_table, opp_xref;
386 	pcell_t cell[2];
387 	uint32_t *volts, lat;
388 	int nvolt, i;
389 
390 	/*
391 	 * operating-points-v2 does not require the voltage entries
392 	 * and a regulator.  So, it's OK if they're not there.
393 	 */
394 	if (OF_getencprop(node, "operating-points-v2", &opp_xref,
395 	    sizeof(opp_xref)) == -1) {
396 		device_printf(sc->dev, "Cannot get xref to oppv2 table\n");
397 		return (ENXIO);
398 	}
399 
400 	opp_table = OF_node_from_xref(opp_xref);
401 	if (opp_table == opp_xref)
402 		return (ENXIO);
403 
404 	if (!OF_hasprop(opp_table, "opp-shared")) {
405 		device_printf(sc->dev, "Only opp-shared is supported\n");
406 		return (ENXIO);
407 	}
408 
409 	for (opp = OF_child(opp_table); opp > 0; opp = OF_peer(opp))
410 		sc->nopp += 1;
411 
412 	sc->opp = malloc(sizeof(*sc->opp) * sc->nopp, M_DEVBUF, M_WAITOK);
413 
414 	for (i = 0, opp_table = OF_child(opp_table); opp_table > 0;
415 	     opp_table = OF_peer(opp_table), i++) {
416 		/* opp-hz is a required property */
417 		if (OF_getencprop(opp_table, "opp-hz", cell,
418 		    sizeof(cell)) == -1)
419 			continue;
420 
421 		sc->opp[i].freq = cell[0];
422 		sc->opp[i].freq <<= 32;
423 		sc->opp[i].freq |= cell[1];
424 
425 		if (OF_getencprop(opp_table, "clock-latency", &lat,
426 		    sizeof(lat)) == -1)
427 			sc->opp[i].clk_latency = CPUFREQ_VAL_UNKNOWN;
428 		else
429 			sc->opp[i].clk_latency = (int)lat;
430 
431 		if (OF_hasprop(opp_table, "turbo-mode"))
432 			sc->opp[i].turbo_mode = true;
433 		if (OF_hasprop(opp_table, "opp-suspend"))
434 			sc->opp[i].opp_suspend = true;
435 
436 		if (CPUFREQ_DT_HAVE_REGULATOR(sc)) {
437 			nvolt = OF_getencprop_alloc_multi(opp_table,
438 			    "opp-microvolt", sizeof(*volts), (void **)&volts);
439 			if (nvolt == 1) {
440 				sc->opp[i].uvolt_target = volts[0];
441 				sc->opp[i].uvolt_min = volts[0];
442 				sc->opp[i].uvolt_max = volts[0];
443 			} else if (nvolt == 3) {
444 				sc->opp[i].uvolt_target = volts[0];
445 				sc->opp[i].uvolt_min = volts[1];
446 				sc->opp[i].uvolt_max = volts[2];
447 			} else {
448 				device_printf(sc->dev,
449 				    "Wrong count of opp-microvolt property\n");
450 				OF_prop_free(volts);
451 				free(sc->opp, M_DEVBUF);
452 				return (ENXIO);
453 			}
454 			OF_prop_free(volts);
455 		} else {
456 			/* No regulator required; don't add anything */
457 			sc->opp[i].uvolt_target = 0;
458 			sc->opp[i].uvolt_min = 0;
459 			sc->opp[i].uvolt_max = 0;
460 		}
461 
462 		if (bootverbose)
463 			device_printf(sc->dev, "%ju.%03ju Mhz (%u uV)\n",
464 			    sc->opp[i].freq / 1000000,
465 			    sc->opp[i].freq % 1000000,
466 			    sc->opp[i].uvolt_target);
467 	}
468 	return (0);
469 }
470 
471 static int
cpufreq_dt_attach(device_t dev)472 cpufreq_dt_attach(device_t dev)
473 {
474 	struct cpufreq_dt_softc *sc;
475 	phandle_t node;
476 	phandle_t cnode, opp, copp;
477 	int cpu;
478 	uint64_t freq;
479 	int rv = 0;
480 	char device_type[16];
481 	enum opp_version version;
482 
483 	sc = device_get_softc(dev);
484 	sc->dev = dev;
485 	node = ofw_bus_get_node(device_get_parent(dev));
486 	sc->cpu = device_get_unit(device_get_parent(dev));
487 	sc->reg = NULL;
488 
489 	DPRINTF(dev, "cpu=%d\n", sc->cpu);
490 	if (sc->cpu >= mp_ncpus) {
491 		device_printf(dev, "Not attaching as cpu is not present\n");
492 		rv = ENXIO;
493 		goto error;
494 	}
495 
496 	/*
497 	 * Cache if we have the regulator supply but don't error out
498 	 * quite yet.  If it's operating-points-v2 then regulator
499 	 * and voltage entries are optional.
500 	 */
501 	if (regulator_get_by_ofw_property(dev, node, "cpu-supply",
502 	    &sc->reg) == 0)
503 		device_printf(dev, "Found cpu-supply\n");
504 	else if (regulator_get_by_ofw_property(dev, node, "cpu0-supply",
505 	    &sc->reg) == 0)
506 		device_printf(dev, "Found cpu0-supply\n");
507 
508 	/*
509 	 * Determine which operating mode we're in.  Error out if we expect
510 	 * a regulator but we're not getting it.
511 	 */
512 	if (OF_hasprop(node, "operating-points"))
513 		version = OPP_V1;
514 	else if (OF_hasprop(node, "operating-points-v2"))
515 		version = OPP_V2;
516 	else {
517 		device_printf(dev,
518 		    "didn't find a valid operating-points or v2 node\n");
519 		rv = ENXIO;
520 		goto error;
521 	}
522 
523 	/*
524 	 * Now, we only enforce needing a regulator for v1.
525 	 */
526 	if ((version == OPP_V1) && !CPUFREQ_DT_HAVE_REGULATOR(sc)) {
527 		device_printf(dev, "no regulator for %s\n",
528 		    ofw_bus_get_name(device_get_parent(dev)));
529 		rv = ENXIO;
530 		goto error;
531 	}
532 
533 	if (clk_get_by_ofw_index(dev, node, 0, &sc->clk) != 0) {
534 		device_printf(dev, "no clock for %s\n",
535 		    ofw_bus_get_name(device_get_parent(dev)));
536 		rv = ENXIO;
537 		goto error;
538 	}
539 
540 	if (version == OPP_V1) {
541 		rv = cpufreq_dt_oppv1_parse(sc, node);
542 		if (rv != 0) {
543 			device_printf(dev, "Failed to parse opp-v1 table\n");
544 			goto error;
545 		}
546 		OF_getencprop(node, "operating-points", &opp,
547 		    sizeof(opp));
548 	} else if (version == OPP_V2) {
549 		rv = cpufreq_dt_oppv2_parse(sc, node);
550 		if (rv != 0) {
551 			device_printf(dev, "Failed to parse opp-v2 table\n");
552 			goto error;
553 		}
554 		OF_getencprop(node, "operating-points-v2", &opp,
555 		    sizeof(opp));
556 	} else {
557 		device_printf(dev, "operating points version is incorrect\n");
558 		goto error;
559 	}
560 
561 	/*
562 	 * Find all CPUs that share the same opp table
563 	 */
564 	CPU_ZERO(&sc->cpus);
565 	cnode = OF_parent(node);
566 	for (cpu = 0, cnode = OF_child(cnode); cnode > 0; cnode = OF_peer(cnode)) {
567 		if (OF_getprop(cnode, "device_type", device_type, sizeof(device_type)) <= 0)
568 			continue;
569 		if (strcmp(device_type, "cpu") != 0)
570 			continue;
571 		if (cpu == sc->cpu) {
572 			DPRINTF(dev, "Skipping our cpu\n");
573 			CPU_SET(cpu, &sc->cpus);
574 			cpu++;
575 			continue;
576 		}
577 		DPRINTF(dev, "Testing CPU %d\n", cpu);
578 		copp = -1;
579 		if (version == OPP_V1)
580 			OF_getencprop(cnode, "operating-points", &copp,
581 			    sizeof(copp));
582 		else if (version == OPP_V2)
583 			OF_getencprop(cnode, "operating-points-v2",
584 			    &copp, sizeof(copp));
585 		if (opp == copp) {
586 			DPRINTF(dev, "CPU %d is using the same opp as this one (%d)\n",
587 			    cpu, sc->cpu);
588 			CPU_SET(cpu, &sc->cpus);
589 		}
590 		cpu++;
591 	}
592 
593 	if (clk_get_freq(sc->clk, &freq) == 0)
594 		cpufreq_dt_notify(dev, freq);
595 
596 	cpufreq_register(dev);
597 
598 	return (0);
599 error:
600 	if (CPUFREQ_DT_HAVE_REGULATOR(sc))
601 		regulator_release(sc->reg);
602 	return (rv);
603 }
604 
605 static device_method_t cpufreq_dt_methods[] = {
606 	/* Device interface */
607 	DEVMETHOD(device_identify,	cpufreq_dt_identify),
608 	DEVMETHOD(device_probe,		cpufreq_dt_probe),
609 	DEVMETHOD(device_attach,	cpufreq_dt_attach),
610 
611 	/* cpufreq interface */
612 	DEVMETHOD(cpufreq_drv_get,	cpufreq_dt_get),
613 	DEVMETHOD(cpufreq_drv_set,	cpufreq_dt_set),
614 	DEVMETHOD(cpufreq_drv_type,	cpufreq_dt_type),
615 	DEVMETHOD(cpufreq_drv_settings,	cpufreq_dt_settings),
616 
617 	DEVMETHOD_END
618 };
619 
620 static driver_t cpufreq_dt_driver = {
621 	"cpufreq_dt",
622 	cpufreq_dt_methods,
623 	sizeof(struct cpufreq_dt_softc),
624 };
625 
626 DRIVER_MODULE(cpufreq_dt, cpu, cpufreq_dt_driver, 0, 0);
627 MODULE_VERSION(cpufreq_dt, 1);
628