xref: /freebsd/sys/kern/kern_cpu.c (revision 6b806d21d144c25f4fad714e1c0cf780f5e27d7e)
1 /*-
2  * Copyright (c) 2004-2005 Nate Lawson (SDG)
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$");
29 
30 #include <sys/param.h>
31 #include <sys/bus.h>
32 #include <sys/cpu.h>
33 #include <sys/eventhandler.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/module.h>
37 #include <sys/proc.h>
38 #include <sys/queue.h>
39 #include <sys/sched.h>
40 #include <sys/sysctl.h>
41 #include <sys/systm.h>
42 #include <sys/sbuf.h>
43 
44 #include "cpufreq_if.h"
45 
46 /*
47  * Common CPU frequency glue code.  Drivers for specific hardware can
48  * attach this interface to allow users to get/set the CPU frequency.
49  */
50 
51 /*
52  * Number of levels we can handle.  Levels are synthesized from settings
53  * so for N settings there may be N^2 levels.
54  */
55 #define CF_MAX_LEVELS	32
56 
57 struct cpufreq_softc {
58 	struct cf_level			curr_level;
59 	int				priority;
60 	int				all_count;
61 	struct cf_level_lst		all_levels;
62 	device_t			dev;
63 	struct sysctl_ctx_list		sysctl_ctx;
64 };
65 
66 struct cf_setting_array {
67 	struct cf_setting		sets[MAX_SETTINGS];
68 	int				count;
69 	TAILQ_ENTRY(cf_setting_array)	link;
70 };
71 
72 TAILQ_HEAD(cf_setting_lst, cf_setting_array);
73 
74 static int	cpufreq_attach(device_t dev);
75 static int	cpufreq_detach(device_t dev);
76 static void	cpufreq_evaluate(void *arg);
77 static int	cf_set_method(device_t dev, const struct cf_level *level,
78 		    int priority);
79 static int	cf_get_method(device_t dev, struct cf_level *level);
80 static int	cf_levels_method(device_t dev, struct cf_level *levels,
81 		    int *count);
82 static int	cpufreq_insert_abs(struct cpufreq_softc *sc,
83 		    struct cf_setting *sets, int count);
84 static int	cpufreq_expand_set(struct cpufreq_softc *sc,
85 		    struct cf_setting_array *set_arr);
86 static struct cf_level *cpufreq_dup_set(struct cpufreq_softc *sc,
87 		    struct cf_level *dup, struct cf_setting *set);
88 static int	cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS);
89 static int	cpufreq_levels_sysctl(SYSCTL_HANDLER_ARGS);
90 
91 static device_method_t cpufreq_methods[] = {
92 	DEVMETHOD(device_probe,		bus_generic_probe),
93 	DEVMETHOD(device_attach,	cpufreq_attach),
94 	DEVMETHOD(device_detach,	cpufreq_detach),
95 
96         DEVMETHOD(cpufreq_set,		cf_set_method),
97         DEVMETHOD(cpufreq_get,		cf_get_method),
98         DEVMETHOD(cpufreq_levels,	cf_levels_method),
99 	{0, 0}
100 };
101 static driver_t cpufreq_driver = {
102 	"cpufreq", cpufreq_methods, sizeof(struct cpufreq_softc)
103 };
104 static devclass_t cpufreq_dc;
105 DRIVER_MODULE(cpufreq, cpu, cpufreq_driver, cpufreq_dc, 0, 0);
106 
107 static eventhandler_tag cf_ev_tag;
108 
109 static int
110 cpufreq_attach(device_t dev)
111 {
112 	struct cpufreq_softc *sc;
113 	device_t parent;
114 	int numdevs;
115 
116 	sc = device_get_softc(dev);
117 	parent = device_get_parent(dev);
118 	sc->dev = dev;
119 	sysctl_ctx_init(&sc->sysctl_ctx);
120 	TAILQ_INIT(&sc->all_levels);
121 	sc->curr_level.total_set.freq = CPUFREQ_VAL_UNKNOWN;
122 
123 	/*
124 	 * Only initialize one set of sysctls for all CPUs.  In the future,
125 	 * if multiple CPUs can have different settings, we can move these
126 	 * sysctls to be under every CPU instead of just the first one.
127 	 */
128 	numdevs = devclass_get_count(cpufreq_dc);
129 	if (numdevs > 1)
130 		return (0);
131 
132 	SYSCTL_ADD_PROC(&sc->sysctl_ctx,
133 	    SYSCTL_CHILDREN(device_get_sysctl_tree(parent)),
134 	    OID_AUTO, "freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
135 	    cpufreq_curr_sysctl, "I", "Current CPU frequency");
136 	SYSCTL_ADD_PROC(&sc->sysctl_ctx,
137 	    SYSCTL_CHILDREN(device_get_sysctl_tree(parent)),
138 	    OID_AUTO, "freq_levels", CTLTYPE_STRING | CTLFLAG_RD, sc, 0,
139 	    cpufreq_levels_sysctl, "A", "CPU frequency levels");
140 	cf_ev_tag = EVENTHANDLER_REGISTER(cpufreq_changed, cpufreq_evaluate,
141 	    NULL, EVENTHANDLER_PRI_ANY);
142 
143 	return (0);
144 }
145 
146 static int
147 cpufreq_detach(device_t dev)
148 {
149 	struct cpufreq_softc *sc;
150 	int numdevs;
151 
152 	sc = device_get_softc(dev);
153 	sysctl_ctx_free(&sc->sysctl_ctx);
154 
155 	/* Only clean up these resources when the last device is detaching. */
156 	numdevs = devclass_get_count(cpufreq_dc);
157 	if (numdevs == 1)
158 		EVENTHANDLER_DEREGISTER(cpufreq_changed, cf_ev_tag);
159 
160 	return (0);
161 }
162 
163 static void
164 cpufreq_evaluate(void *arg)
165 {
166 	/* TODO: Re-evaluate when notified of changes to drivers. */
167 }
168 
169 static int
170 cf_set_method(device_t dev, const struct cf_level *level, int priority)
171 {
172 	struct cpufreq_softc *sc;
173 	const struct cf_setting *set;
174 	int error, i;
175 
176 	sc = device_get_softc(dev);
177 
178 	/* If already at this level, just return. */
179 	if (CPUFREQ_CMP(sc->curr_level.total_set.freq, level->total_set.freq))
180 		return (0);
181 
182 	/* First, set the absolute frequency via its driver. */
183 	set = &level->abs_set;
184 	if (set->dev) {
185 		if (!device_is_attached(set->dev)) {
186 			error = ENXIO;
187 			goto out;
188 		}
189 		error = CPUFREQ_DRV_SET(set->dev, set);
190 		if (error) {
191 			goto out;
192 		}
193 	}
194 
195 	/* Next, set any/all relative frequencies via their drivers. */
196 	for (i = 0; i < level->rel_count; i++) {
197 		set = &level->rel_set[i];
198 		if (!device_is_attached(set->dev)) {
199 			error = ENXIO;
200 			goto out;
201 		}
202 		error = CPUFREQ_DRV_SET(set->dev, set);
203 		if (error) {
204 			/* XXX Back out any successful setting? */
205 			goto out;
206 		}
207 	}
208 
209 	/* Record the current level. */
210 	sc->curr_level = *level;
211 	sc->priority = priority;
212 	error = 0;
213 
214 out:
215 	if (error)
216 		device_printf(set->dev, "set freq failed, err %d\n", error);
217 	return (error);
218 }
219 
220 static int
221 cf_get_method(device_t dev, struct cf_level *level)
222 {
223 	struct cpufreq_softc *sc;
224 	struct cf_level *levels;
225 	struct cf_setting *curr_set, set;
226 	struct pcpu *pc;
227 	device_t *devs;
228 	int count, error, i, numdevs;
229 	uint64_t rate;
230 
231 	sc = device_get_softc(dev);
232 	curr_set = &sc->curr_level.total_set;
233 	levels = NULL;
234 
235 	/* If we already know the current frequency, we're done. */
236 	if (curr_set->freq != CPUFREQ_VAL_UNKNOWN)
237 		goto out;
238 
239 	/*
240 	 * We need to figure out the current level.  Loop through every
241 	 * driver, getting the current setting.  Then, attempt to get a best
242 	 * match of settings against each level.
243 	 */
244 	count = CF_MAX_LEVELS;
245 	levels = malloc(count * sizeof(*levels), M_TEMP, M_NOWAIT);
246 	if (levels == NULL)
247 		return (ENOMEM);
248 	error = CPUFREQ_LEVELS(sc->dev, levels, &count);
249 	if (error)
250 		goto out;
251 	error = device_get_children(device_get_parent(dev), &devs, &numdevs);
252 	if (error)
253 		goto out;
254 	for (i = 0; i < numdevs && curr_set->freq == CPUFREQ_VAL_UNKNOWN; i++) {
255 		if (!device_is_attached(devs[i]))
256 			continue;
257 		error = CPUFREQ_DRV_GET(devs[i], &set);
258 		if (error)
259 			continue;
260 		for (i = 0; i < count; i++) {
261 			if (CPUFREQ_CMP(set.freq, levels[i].total_set.freq)) {
262 				sc->curr_level = levels[i];
263 				break;
264 			}
265 		}
266 	}
267 	free(devs, M_TEMP);
268 	if (curr_set->freq != CPUFREQ_VAL_UNKNOWN)
269 		goto out;
270 
271 	/*
272 	 * We couldn't find an exact match, so attempt to estimate and then
273 	 * match against a level.
274 	 */
275 	pc = cpu_get_pcpu(dev);
276 	if (pc == NULL) {
277 		error = ENXIO;
278 		goto out;
279 	}
280 	cpu_est_clockrate(pc->pc_cpuid, &rate);
281 	rate /= 1000000;
282 	for (i = 0; i < count; i++) {
283 		if (CPUFREQ_CMP(rate, levels[i].total_set.freq)) {
284 			sc->curr_level = levels[i];
285 			break;
286 		}
287 	}
288 
289 out:
290 	if (levels)
291 		free(levels, M_TEMP);
292 	*level = sc->curr_level;
293 	return (0);
294 }
295 
296 static int
297 cf_levels_method(device_t dev, struct cf_level *levels, int *count)
298 {
299 	struct cf_setting_array *set_arr;
300 	struct cf_setting_lst rel_sets;
301 	struct cpufreq_softc *sc;
302 	struct cf_level *lev;
303 	struct cf_setting *sets;
304 	struct pcpu *pc;
305 	device_t *devs;
306 	int error, i, numdevs, set_count, type;
307 	uint64_t rate;
308 
309 	if (levels == NULL || count == NULL)
310 		return (EINVAL);
311 
312 	TAILQ_INIT(&rel_sets);
313 	sc = device_get_softc(dev);
314 	error = device_get_children(device_get_parent(dev), &devs, &numdevs);
315 	if (error)
316 		return (error);
317 	sets = malloc(MAX_SETTINGS * sizeof(*sets), M_TEMP, M_NOWAIT);
318 	if (sets == NULL) {
319 		free(devs, M_TEMP);
320 		return (ENOMEM);
321 	}
322 
323 	/* Get settings from all cpufreq drivers. */
324 	for (i = 0; i < numdevs; i++) {
325 		if (!device_is_attached(devs[i]))
326 			continue;
327 		set_count = MAX_SETTINGS;
328 		error = CPUFREQ_DRV_SETTINGS(devs[i], sets, &set_count, &type);
329 		if (error || set_count == 0)
330 			continue;
331 
332 		switch (type) {
333 		case CPUFREQ_TYPE_ABSOLUTE:
334 			error = cpufreq_insert_abs(sc, sets, set_count);
335 			break;
336 		case CPUFREQ_TYPE_RELATIVE:
337 			set_arr = malloc(sizeof(*set_arr), M_TEMP, M_NOWAIT);
338 			if (set_arr == NULL) {
339 				error = ENOMEM;
340 				goto out;
341 			}
342 			bcopy(sets, set_arr->sets, set_count * sizeof(*sets));
343 			set_arr->count = set_count;
344 			TAILQ_INSERT_TAIL(&rel_sets, set_arr, link);
345 			break;
346 		default:
347 			error = EINVAL;
348 			break;
349 		}
350 		if (error)
351 			goto out;
352 	}
353 
354 	/* If there are no absolute levels, create a fake one at 100%. */
355 	if (TAILQ_EMPTY(&sc->all_levels)) {
356 		bzero(&sets[0], sizeof(*sets));
357 		pc = cpu_get_pcpu(dev);
358 		if (pc == NULL) {
359 			error = ENXIO;
360 			goto out;
361 		}
362 		cpu_est_clockrate(pc->pc_cpuid, &rate);
363 		sets[0].freq = rate / 1000000;
364 		error = cpufreq_insert_abs(sc, sets, 1);
365 		if (error)
366 			goto out;
367 	}
368 
369 	/* Create a combined list of absolute + relative levels. */
370 	TAILQ_FOREACH(set_arr, &rel_sets, link)
371 		cpufreq_expand_set(sc, set_arr);
372 
373 	/* If the caller doesn't have enough space, return the actual count. */
374 	if (sc->all_count > *count) {
375 		*count = sc->all_count;
376 		error = E2BIG;
377 		goto out;
378 	}
379 
380 	/* Finally, output the list of levels. */
381 	i = 0;
382 	TAILQ_FOREACH(lev, &sc->all_levels, link) {
383 		levels[i] = *lev;
384 		i++;
385 	}
386 	*count = sc->all_count;
387 	error = 0;
388 
389 out:
390 	/* Clear all levels since we regenerate them each time. */
391 	while ((lev = TAILQ_FIRST(&sc->all_levels)) != NULL) {
392 		TAILQ_REMOVE(&sc->all_levels, lev, link);
393 		free(lev, M_TEMP);
394 	}
395 	while ((set_arr = TAILQ_FIRST(&rel_sets)) != NULL) {
396 		TAILQ_REMOVE(&rel_sets, set_arr, link);
397 		free(set_arr, M_TEMP);
398 	}
399 	sc->all_count = 0;
400 	free(devs, M_TEMP);
401 	free(sets, M_TEMP);
402 	return (error);
403 }
404 
405 /*
406  * Create levels for an array of absolute settings and insert them in
407  * sorted order in the specified list.
408  */
409 static int
410 cpufreq_insert_abs(struct cpufreq_softc *sc, struct cf_setting *sets,
411     int count)
412 {
413 	struct cf_level_lst *list;
414 	struct cf_level *level, *search;
415 	int i;
416 
417 	list = &sc->all_levels;
418 	for (i = 0; i < count; i++) {
419 		level = malloc(sizeof(*level), M_TEMP, M_NOWAIT | M_ZERO);
420 		if (level == NULL)
421 			return (ENOMEM);
422 		level->abs_set = sets[i];
423 		level->total_set = sets[i];
424 		level->total_set.dev = NULL;
425 		sc->all_count++;
426 
427 		if (TAILQ_EMPTY(list)) {
428 			TAILQ_INSERT_HEAD(list, level, link);
429 			continue;
430 		}
431 
432 		TAILQ_FOREACH_REVERSE(search, list, cf_level_lst, link) {
433 			if (sets[i].freq <= search->total_set.freq) {
434 				TAILQ_INSERT_AFTER(list, search, level, link);
435 				break;
436 			}
437 		}
438 	}
439 	return (0);
440 }
441 
442 /*
443  * Expand a group of relative settings, creating derived levels from them.
444  */
445 static int
446 cpufreq_expand_set(struct cpufreq_softc *sc, struct cf_setting_array *set_arr)
447 {
448 	struct cf_level *fill, *search;
449 	struct cf_setting *set;
450 	int i;
451 
452 	TAILQ_FOREACH(search, &sc->all_levels, link) {
453 		/* Skip this level if we've already modified it. */
454 		for (i = 0; i < search->rel_count; i++) {
455 			if (search->rel_set[i].dev == set_arr->sets[0].dev)
456 				break;
457 		}
458 		if (i != search->rel_count)
459 			continue;
460 
461 		/* Add each setting to the level, duplicating if necessary. */
462 		for (i = 0; i < set_arr->count; i++) {
463 			set = &set_arr->sets[i];
464 
465 			/*
466 			 * If this setting is less than 100%, split the level
467 			 * into two and add this setting to the new level.
468 			 */
469 			fill = search;
470 			if (set->freq < 10000)
471 				fill = cpufreq_dup_set(sc, search, set);
472 
473 			/*
474 			 * The new level was a duplicate of an existing level
475 			 * so we freed it.  Go to the next setting.
476 			 */
477 			if (fill == NULL)
478 				continue;
479 
480 			/* Add this setting to the existing or new level. */
481 			KASSERT(fill->rel_count < MAX_SETTINGS,
482 			    ("cpufreq: too many relative drivers (%d)",
483 			    MAX_SETTINGS));
484 			fill->rel_set[fill->rel_count] = *set;
485 			fill->rel_count++;
486 		}
487 	}
488 
489 	return (0);
490 }
491 
492 static struct cf_level *
493 cpufreq_dup_set(struct cpufreq_softc *sc, struct cf_level *dup,
494     struct cf_setting *set)
495 {
496 	struct cf_level_lst *list;
497 	struct cf_level *fill, *itr;
498 	struct cf_setting *fill_set, *itr_set;
499 	int i;
500 
501 	/*
502 	 * Create a new level, copy it from the old one, and update the
503 	 * total frequency and power by the percentage specified in the
504 	 * relative setting.
505 	 */
506 	fill = malloc(sizeof(*fill), M_TEMP, M_NOWAIT);
507 	if (fill == NULL)
508 		return (NULL);
509 	*fill = *dup;
510 	fill_set = &fill->total_set;
511 	fill_set->freq =
512 	    ((uint64_t)fill_set->freq * set->freq) / 10000;
513 	if (fill_set->power != CPUFREQ_VAL_UNKNOWN) {
514 		fill_set->power = ((uint64_t)fill_set->power * set->freq)
515 		    / 10000;
516 	}
517 	if (set->lat != CPUFREQ_VAL_UNKNOWN) {
518 		if (fill_set->lat != CPUFREQ_VAL_UNKNOWN)
519 			fill_set->lat += set->lat;
520 		else
521 			fill_set->lat = set->lat;
522 	}
523 
524 	/*
525 	 * If we copied an old level that we already modified (say, at 100%),
526 	 * we need to remove that setting before adding this one.  Since we
527 	 * process each setting array in order, we know any settings for this
528 	 * driver will be found at the end.
529 	 */
530 	for (i = fill->rel_count; i != 0; i--) {
531 		if (fill->rel_set[i - 1].dev != set->dev)
532 			break;
533 		fill->rel_count--;
534 	}
535 
536 	/*
537 	 * Insert the new level in sorted order.  If we find a duplicate,
538 	 * free the new level.  We can do this since any existing level will
539 	 * be guaranteed to have the same or less settings and thus consume
540 	 * less power.  For example, a level with one absolute setting of
541 	 * 800 Mhz uses less power than one composed of an absolute setting
542 	 * of 1600 Mhz and a relative setting at 50%.
543 	 */
544 	list = &sc->all_levels;
545 	if (TAILQ_EMPTY(list)) {
546 		TAILQ_INSERT_HEAD(list, fill, link);
547 	} else {
548 		TAILQ_FOREACH_REVERSE(itr, list, cf_level_lst, link) {
549 			itr_set = &itr->total_set;
550 			if (CPUFREQ_CMP(fill_set->freq, itr_set->freq)) {
551 				free(fill, M_TEMP);
552 				fill = NULL;
553 				break;
554 			} else if (fill_set->freq < itr_set->freq) {
555 				TAILQ_INSERT_AFTER(list, itr, fill, link);
556 				sc->all_count++;
557 				break;
558 			}
559 		}
560 	}
561 
562 	return (fill);
563 }
564 
565 static int
566 cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS)
567 {
568 	struct cpufreq_softc *sc;
569 	struct cf_level *levels;
570 	int count, error, freq, i;
571 
572 	sc = oidp->oid_arg1;
573 	count = CF_MAX_LEVELS;
574 	levels = malloc(count * sizeof(*levels), M_TEMP, M_NOWAIT);
575 	if (levels == NULL)
576 		return (ENOMEM);
577 
578 	error = CPUFREQ_GET(sc->dev, &levels[0]);
579 	if (error)
580 		goto out;
581 	freq = levels[0].total_set.freq;
582 	error = sysctl_handle_int(oidp, &freq, 0, req);
583 	if (error != 0 || req->newptr == NULL)
584 		goto out;
585 
586 	error = CPUFREQ_LEVELS(sc->dev, levels, &count);
587 	if (error)
588 		goto out;
589 	for (i = 0; i < count; i++) {
590 		if (CPUFREQ_CMP(levels[i].total_set.freq, freq)) {
591 			error = CPUFREQ_SET(sc->dev, &levels[i],
592 			    CPUFREQ_PRIO_USER);
593 			break;
594 		}
595 	}
596 	if (i == count)
597 		error = EINVAL;
598 
599 out:
600 	if (levels)
601 		free(levels, M_TEMP);
602 	return (error);
603 }
604 
605 static int
606 cpufreq_levels_sysctl(SYSCTL_HANDLER_ARGS)
607 {
608 	struct cpufreq_softc *sc;
609 	struct cf_level *levels;
610 	struct cf_setting *set;
611 	struct sbuf sb;
612 	int count, error, i;
613 
614 	sc = oidp->oid_arg1;
615 	sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
616 
617 	/* Get settings from the device and generate the output string. */
618 	count = CF_MAX_LEVELS;
619 	levels = malloc(count * sizeof(*levels), M_TEMP, M_NOWAIT);
620 	if (levels == NULL)
621 		return (ENOMEM);
622 	error = CPUFREQ_LEVELS(sc->dev, levels, &count);
623 	if (error)
624 		goto out;
625 	if (count) {
626 		for (i = 0; i < count; i++) {
627 			set = &levels[i].total_set;
628 			sbuf_printf(&sb, "%d/%d ", set->freq, set->power);
629 		}
630 	} else
631 		sbuf_cpy(&sb, "0");
632 	sbuf_trim(&sb);
633 	sbuf_finish(&sb);
634 	error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
635 
636 out:
637 	free(levels, M_TEMP);
638 	sbuf_delete(&sb);
639 	return (error);
640 }
641 
642 int
643 cpufreq_register(device_t dev)
644 {
645 	device_t cf_dev, cpu_dev;
646 
647 	/*
648 	 * Only add one cpufreq device (on cpu0) for all control.  Once
649 	 * independent multi-cpu control appears, we can assign one cpufreq
650 	 * device per cpu.
651 	 */
652 	cf_dev = devclass_get_device(cpufreq_dc, 0);
653 	if (cf_dev)
654 		return (0);
655 
656 	/* Add the child device and sysctls. */
657 	cpu_dev = devclass_get_device(devclass_find("cpu"), 0);
658 	cf_dev = BUS_ADD_CHILD(cpu_dev, 0, "cpufreq", 0);
659 	if (cf_dev == NULL)
660 		return (ENOMEM);
661 	device_quiet(cf_dev);
662 
663 	return (device_probe_and_attach(cf_dev));
664 }
665 
666 int
667 cpufreq_unregister(device_t dev)
668 {
669 	device_t cf_dev, *devs;
670 	int cfcount, count, devcount, error, i, type;
671 	struct cf_setting set;
672 
673 	/*
674 	 * If this is the last cpufreq child device, remove the control
675 	 * device as well.  We identify cpufreq children by calling a method
676 	 * they support.
677 	 */
678 	error = device_get_children(device_get_parent(dev), &devs, &devcount);
679 	if (error)
680 		return (error);
681 	cf_dev = devclass_get_device(cpufreq_dc, 0);
682 	KASSERT(cf_dev != NULL, ("unregister with no cpufreq dev"));
683 	cfcount = 0;
684 	for (i = 0; i < devcount; i++) {
685 		if (!device_is_attached(devs[i]))
686 			continue;
687 		count = 1;
688 		if (CPUFREQ_DRV_SETTINGS(devs[i], &set, &count, &type) == 0)
689 			cfcount++;
690 	}
691 	if (cfcount <= 1) {
692 		device_delete_child(device_get_parent(cf_dev), cf_dev);
693 	}
694 	free(devs, M_TEMP);
695 
696 	return (0);
697 }
698