27 #include <sys/cdefs.h>
30 #include <sys/param.h>
33 #include <sys/eventhandler.h>
34 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
39 #include <sys/queue.h>
41 #include <sys/sched.h>
43 #include <sys/sysctl.h>
44 #include <sys/systm.h>
46 #include <sys/timetc.h>
47 #include <sys/taskqueue.h>
49 #include "cpufreq_if.h"
60 #define CF_MAX_LEVELS 64
70 struct cf_level curr_level;
73 struct cf_level_lst all_levels;
77 struct sysctl_ctx_list sysctl_ctx;
78 struct task startup_task;
79 struct cf_level *levels_buf;
83 struct cf_setting
sets[MAX_SETTINGS];
85 TAILQ_ENTRY(cf_setting_array) link;
90 #define CF_MTX_INIT(x) sx_init((x), "cpufreq lock")
91 #define CF_MTX_LOCK(x) sx_xlock((x))
92 #define CF_MTX_UNLOCK(x) sx_xunlock((x))
93 #define CF_MTX_ASSERT(x) sx_assert((x), SX_XLOCKED)
95 #define CF_DEBUG(msg...) do { \
97 printf("cpufreq: " msg); \
109 struct cf_setting *
sets,
int count);
111 struct cf_setting_array *set_arr);
113 struct cf_level *dup,
struct cf_setting *
set);
136 TUNABLE_INT(
"debug.cpufreq.lowest", &cf_lowest_freq);
139 "cpufreq debugging");
140 SYSCTL_INT(_debug_cpufreq, OID_AUTO, lowest, CTLFLAG_RW, &cf_lowest_freq, 1,
141 "Don't provide levels below this frequency.");
142 SYSCTL_INT(_debug_cpufreq, OID_AUTO, verbose, CTLFLAG_RW, &cf_verbose, 1,
143 "Print verbose debugging messages");
148 struct cpufreq_softc *sc;
159 TAILQ_INIT(&sc->all_levels);
161 sc->
curr_level.total_set.freq = CPUFREQ_VAL_UNKNOWN;
162 SLIST_INIT(&sc->saved_freq);
164 sc->max_mhz = cpu_get_nominal_mhz(dev);
166 if (sc->max_mhz <= 0) {
167 pc = cpu_get_pcpu(dev);
168 if (cpu_est_clockrate(pc->pc_cpuid, &rate) == 0)
169 sc->max_mhz = rate / 1000000;
171 sc->max_mhz = CPUFREQ_VAL_UNKNOWN;
183 CF_DEBUG(
"initializing one-time data for %s\n",
187 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
189 OID_AUTO,
"freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
191 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
193 OID_AUTO,
"freq_levels", CTLTYPE_STRING | CTLFLAG_RD, sc, 0,
217 struct cpufreq_softc *sc;
225 while ((saved_freq = SLIST_FIRST(&sc->saved_freq)) != NULL) {
226 SLIST_REMOVE_HEAD(&sc->saved_freq, link);
227 free(saved_freq, M_TEMP);
234 free(sc->levels_buf, M_DEVBUF);
243 struct cpufreq_softc *sc;
244 const struct cf_setting *
set;
255 EVENTHANDLER_INVOKE(cpufreq_pre_change, level, &error);
257 EVENTHANDLER_INVOKE(cpufreq_post_change, level, error);
272 device_printf(dev,
"rejecting change, SMP not started yet\n");
282 if (priority < sc->curr_priority) {
283 CF_DEBUG(
"ignoring, curr prio %d less than %d\n", priority,
294 saved_freq = SLIST_FIRST(&sc->saved_freq);
295 if (saved_freq == NULL) {
296 CF_DEBUG(
"NULL level, no saved level\n");
300 level = &saved_freq->
level;
302 CF_DEBUG(
"restoring saved level, freq %d prio %d\n",
303 level->total_set.freq, priority);
307 if (level->total_set.freq < cf_lowest_freq) {
308 CF_DEBUG(
"rejecting freq %d, less than %d limit\n",
309 level->total_set.freq, cf_lowest_freq);
315 if (sc->
curr_level.total_set.freq == level->total_set.freq) {
316 CF_DEBUG(
"skipping freq %d, same as current level %d\n",
317 level->total_set.freq, sc->
curr_level.total_set.freq);
322 set = &level->abs_set;
330 pc = cpu_get_pcpu(set->dev);
331 thread_lock(curthread);
333 thread_unlock(curthread);
334 CF_DEBUG(
"setting abs freq %d on %s (cpu %d)\n", set->freq,
336 error = CPUFREQ_DRV_SET(set->dev, set);
337 thread_lock(curthread);
339 thread_unlock(curthread);
346 for (i = 0; i < level->rel_count; i++) {
347 set = &level->rel_set[i];
354 pc = cpu_get_pcpu(set->dev);
355 thread_lock(curthread);
357 thread_unlock(curthread);
358 CF_DEBUG(
"setting rel freq %d on %s (cpu %d)\n", set->freq,
360 error = CPUFREQ_DRV_SET(set->dev, set);
361 thread_lock(curthread);
363 thread_unlock(curthread);
375 if (sc->
curr_level.total_set.freq != CPUFREQ_VAL_UNKNOWN &&
377 CF_DEBUG(
"saving level, freq %d prio %d\n",
379 curr_freq =
malloc(
sizeof(*curr_freq), M_TEMP, M_NOWAIT);
380 if (curr_freq == NULL) {
386 SLIST_INSERT_HEAD(&sc->saved_freq, curr_freq, link);
392 if (saved_freq != NULL) {
393 CF_DEBUG(
"resetting saved level\n");
394 sc->
curr_level.total_set.freq = CPUFREQ_VAL_UNKNOWN;
395 SLIST_REMOVE_HEAD(&sc->saved_freq, link);
396 free(saved_freq, M_TEMP);
406 EVENTHANDLER_INVOKE(cpufreq_post_change, level, error);
416 struct cpufreq_softc *sc;
418 struct cf_setting *curr_set,
set;
421 int bdiff,
count, diff, error, i, n, numdevs;
431 if (curr_set->freq != CPUFREQ_VAL_UNKNOWN) {
432 CF_DEBUG(
"get returning known freq %d\n", curr_set->freq);
443 levels =
malloc(count *
sizeof(*levels), M_TEMP, M_NOWAIT);
446 error = CPUFREQ_LEVELS(sc->dev, levels, &count);
449 printf(
"cpufreq: need to increase CF_MAX_LEVELS\n");
450 free(levels, M_TEMP);
455 free(levels, M_TEMP);
468 for (n = 0; n < numdevs && curr_set->freq == CPUFREQ_VAL_UNKNOWN; n++) {
471 if (CPUFREQ_DRV_GET(devs[n], &set) != 0)
473 for (i = 0; i <
count; i++) {
474 if (set.freq == levels[i].total_set.freq) {
481 if (curr_set->freq != CPUFREQ_VAL_UNKNOWN) {
482 CF_DEBUG(
"get matched freq %d from drivers\n", curr_set->freq);
490 pc = cpu_get_pcpu(dev);
495 cpu_est_clockrate(pc->pc_cpuid, &rate);
498 for (i = 0; i <
count; i++) {
499 diff = abs(levels[i].total_set.freq - rate);
505 CF_DEBUG(
"get estimated freq %d\n", curr_set->freq);
513 free(levels, M_TEMP);
520 struct cf_setting_array *set_arr;
521 struct cf_setting_lst rel_sets;
522 struct cpufreq_softc *sc;
523 struct cf_level *lev;
524 struct cf_setting *
sets;
527 int error, i, numdevs, set_count,
type;
530 if (levels == NULL || count == NULL)
533 TAILQ_INIT(&rel_sets);
538 sets =
malloc(MAX_SETTINGS *
sizeof(*sets), M_TEMP, M_NOWAIT);
546 for (i = 0; i < numdevs; i++) {
555 error = CPUFREQ_DRV_TYPE(devs[i], &type);
556 if (error || (type & CPUFREQ_FLAG_INFO_ONLY)) {
558 CF_DEBUG(
"skipping info-only driver %s\n",
563 set_count = MAX_SETTINGS;
564 error = CPUFREQ_DRV_SETTINGS(devs[i], sets, &set_count);
565 if (error || set_count == 0)
569 switch (type & CPUFREQ_TYPE_MASK) {
570 case CPUFREQ_TYPE_ABSOLUTE:
573 case CPUFREQ_TYPE_RELATIVE:
574 CF_DEBUG(
"adding %d relative settings\n", set_count);
575 set_arr =
malloc(
sizeof(*set_arr), M_TEMP, M_NOWAIT);
576 if (set_arr == NULL) {
580 bcopy(sets, set_arr->
sets, set_count *
sizeof(*sets));
581 set_arr->
count = set_count;
582 TAILQ_INSERT_TAIL(&rel_sets, set_arr, link);
595 if (TAILQ_EMPTY(&sc->all_levels)) {
596 if (sc->max_mhz == CPUFREQ_VAL_UNKNOWN) {
597 sc->max_mhz = cpu_get_nominal_mhz(dev);
603 if (sc->max_mhz <= 0) {
604 pc = cpu_get_pcpu(dev);
605 cpu_est_clockrate(pc->pc_cpuid, &rate);
606 sc->max_mhz = rate / 1000000;
609 memset(&sets[0], CPUFREQ_VAL_UNKNOWN,
sizeof(*sets));
610 sets[0].freq = sc->max_mhz;
618 TAILQ_FOREACH(set_arr, &rel_sets, link)
622 if (sc->all_count > *count) {
623 *count = sc->all_count;
630 TAILQ_FOREACH(lev, &sc->all_levels, link) {
633 if (lev->total_set.freq < cf_lowest_freq) {
641 *count = sc->all_count;
646 while ((lev = TAILQ_FIRST(&sc->all_levels)) != NULL) {
647 TAILQ_REMOVE(&sc->all_levels, lev, link);
653 while ((set_arr = TAILQ_FIRST(&rel_sets)) != NULL) {
654 TAILQ_REMOVE(&rel_sets, set_arr, link);
655 free(set_arr, M_TEMP);
670 struct cf_level_lst *list;
671 struct cf_level *
level, *search;
676 list = &sc->all_levels;
677 for (i = 0; i <
count; i++) {
678 level =
malloc(
sizeof(*level), M_TEMP, M_NOWAIT | M_ZERO);
681 level->abs_set = sets[i];
682 level->total_set = sets[i];
683 level->total_set.dev = NULL;
686 if (TAILQ_EMPTY(list)) {
687 CF_DEBUG(
"adding abs setting %d at head\n",
689 TAILQ_INSERT_HEAD(list, level, link);
693 TAILQ_FOREACH_REVERSE(search, list, cf_level_lst, link) {
694 if (sets[i].freq <= search->total_set.freq) {
695 CF_DEBUG(
"adding abs setting %d after %d\n",
696 sets[i].freq, search->total_set.freq);
697 TAILQ_INSERT_AFTER(list, search, level, link);
711 struct cf_level *fill, *search;
712 struct cf_setting *
set;
725 TAILQ_FOREACH_REVERSE(search, &sc->all_levels, cf_level_lst, link) {
727 for (i = 0; i < set_arr->
count; i++) {
728 set = &set_arr->
sets[i];
735 if (set->freq < 10000) {
750 KASSERT(fill->rel_count < MAX_SETTINGS,
751 (
"cpufreq: too many relative drivers (%d)",
753 fill->rel_set[fill->rel_count] = *
set;
756 "expand set added rel setting %d%% to %d level\n",
757 set->freq / 100, fill->total_set.freq);
764 static struct cf_level *
766 struct cf_setting *
set)
768 struct cf_level_lst *list;
769 struct cf_level *fill, *itr;
770 struct cf_setting *fill_set, *itr_set;
780 fill =
malloc(
sizeof(*fill), M_TEMP, M_NOWAIT);
784 fill_set = &fill->total_set;
786 ((uint64_t)fill_set->freq * set->freq) / 10000;
787 if (fill_set->power != CPUFREQ_VAL_UNKNOWN) {
788 fill_set->power = ((uint64_t)fill_set->power * set->freq)
791 if (set->lat != CPUFREQ_VAL_UNKNOWN) {
792 if (fill_set->lat != CPUFREQ_VAL_UNKNOWN)
793 fill_set->lat += set->lat;
795 fill_set->lat = set->lat;
797 CF_DEBUG(
"dup set considering derived setting %d\n", fill_set->freq);
805 for (i = fill->rel_count; i != 0; i--) {
806 if (fill->rel_set[i - 1].dev != set->dev)
808 CF_DEBUG(
"removed last relative driver: %s\n",
823 list = &sc->all_levels;
824 KASSERT(!TAILQ_EMPTY(list), (
"all levels list empty in dup set"));
825 TAILQ_FOREACH_REVERSE(itr, list, cf_level_lst, link) {
826 itr_set = &itr->total_set;
827 if (CPUFREQ_CMP(fill_set->freq, itr_set->freq)) {
828 CF_DEBUG(
"dup set rejecting %d (dupe)\n",
832 }
else if (fill_set->freq < itr_set->freq) {
833 if (fill->abs_set.freq <= itr->abs_set.freq) {
835 "dup done, inserting new level %d after %d\n",
836 fill_set->freq, itr_set->freq);
837 TAILQ_INSERT_AFTER(list, itr, fill, link);
840 CF_DEBUG(
"dup set rejecting %d (abs too big)\n",
850 CF_DEBUG(
"dup set freeing new level %d (not optimal)\n",
862 struct cpufreq_softc *sc;
864 int best,
count, diff, bdiff, devcount, error, freq, i, n;
869 levels = sc->levels_buf;
871 error = CPUFREQ_GET(sc->dev, &levels[0]);
874 freq = levels[0].total_set.freq;
876 if (error != 0 || req->newptr == NULL)
887 for (n = 0; n < devcount; n++) {
889 error = CPUFREQ_LEVELS(devs[n], levels, &count);
893 "cpufreq: need to increase CF_MAX_LEVELS\n");
898 for (i = 0; i <
count; i++) {
899 diff = abs(levels[i].total_set.freq - freq);
905 error = CPUFREQ_SET(devs[n], &levels[best], CPUFREQ_PRIO_USER);
917 struct cpufreq_softc *sc;
919 struct cf_setting *
set;
924 sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
928 levels = sc->levels_buf;
929 if (levels == NULL) {
933 error = CPUFREQ_LEVELS(sc->dev, levels, &count);
936 printf(
"cpufreq: need to increase CF_MAX_LEVELS\n");
940 for (i = 0; i <
count; i++) {
941 set = &levels[i].total_set;
959 struct cf_setting *
sets;
961 int error, i, set_count;
963 dev = oidp->oid_arg1;
964 sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
967 set_count = MAX_SETTINGS;
968 sets =
malloc(set_count *
sizeof(*sets), M_TEMP, M_NOWAIT);
973 error = CPUFREQ_DRV_SETTINGS(dev, sets, &set_count);
977 for (i = 0; i < set_count; i++)
978 sbuf_printf(&sb,
"%d/%d ", sets[i].freq, sets[i].power);
994 struct cpufreq_softc *sc;
995 device_t cf_dev, cpu_dev;
1000 OID_AUTO,
"freq_settings", CTLTYPE_STRING | CTLFLAG_RD, dev, 0,
1010 sc->max_mhz = CPUFREQ_VAL_UNKNOWN;
1015 cf_dev = BUS_ADD_CHILD(cpu_dev, 0,
"cpufreq", -1);
1026 device_t cf_dev, *devs;
1027 int cfcount, devcount, error, i,
type;
1038 if (cf_dev == NULL) {
1040 "warning: cpufreq_unregister called with no cpufreq device active\n");
1045 for (i = 0; i < devcount; i++) {
1048 if (CPUFREQ_DRV_TYPE(devs[i], &type) == 0)
1062 EVENTHANDLER_INVOKE(cpufreq_levels_changed,
device_t device_find_child(device_t dev, const char *classname, int unit)
Find a device given a unit number.
static int cf_lowest_freq
static int cpufreq_detach(device_t dev)
int device_is_attached(device_t dev)
Return non-zero if the device currently has a driver attached to it.
int device_printf(device_t dev, const char *fmt,...)
Print the name of the device followed by a colon, a space and the result of calling vprintf() with th...
static int cpufreq_levels_sysctl(SYSCTL_HANDLER_ARGS)
static SYSCTL_NODE(_debug, OID_AUTO, cpufreq, CTLFLAG_RD, NULL,"cpufreq debugging")
ssize_t sbuf_len(struct sbuf *s)
static int cpufreq_expand_set(struct cpufreq_softc *sc, struct cf_setting_array *set_arr)
struct cf_setting sets[MAX_SETTINGS]
TUNABLE_INT("debug.cpufreq.lowest",&cf_lowest_freq)
void * malloc(unsigned long size, struct malloc_type *mtp, int flags)
void device_quiet(device_t dev)
Set the DF_QUIET flag for the device.
int device_get_unit(device_t dev)
Return the device's unit number.
int cpufreq_settings_changed(device_t dev)
int devclass_get_devices(devclass_t dc, device_t **devlistp, int *devcountp)
Get a list of devices in the devclass.
static int cf_set_method(device_t dev, const struct cf_level *level, int priority)
int devclass_get_count(devclass_t dc)
Get the number of devices in a devclass.
static devclass_t cpufreq_dc
int sysctl_handle_string(SYSCTL_HANDLER_ARGS)
int device_probe(device_t dev)
Probe a device, and return this status.
static int cpufreq_insert_abs(struct cpufreq_softc *sc, struct cf_setting *sets, int count)
struct sysctl_ctx_list * device_get_sysctl_ctx(device_t dev)
static void cpufreq_startup_task(void *ctx, int pending)
static int cf_get_method(device_t dev, struct cf_level *level)
int device_probe_and_attach(device_t dev)
Probe a device and attach a driver if possible.
static int cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS)
int cpufreq_unregister(device_t dev)
device_t device_get_parent(device_t dev)
Return the parent of a device.
int bus_generic_probe(device_t dev)
Helper function for implementing DEVICE_PROBE()
TAILQ_HEAD(cf_setting_lst, cf_setting_array)
static int cpufreq_attach(device_t dev)
struct sysctl_oid * device_get_sysctl_tree(device_t dev)
static int cpufreq_settings_sysctl(SYSCTL_HANDLER_ARGS)
int sbuf_printf(struct sbuf *s, const char *fmt,...)
int device_detach(device_t dev)
Detach a driver from a device.
DRIVER_MODULE(cpufreq, cpu, cpufreq_driver, cpufreq_dc, 0, 0)
int sysctl_handle_int(SYSCTL_HANDLER_ARGS)
struct sbuf * sbuf_new(struct sbuf *s, char *buf, int length, int flags)
static driver_t cpufreq_driver
static struct cf_level * cpufreq_dup_set(struct cpufreq_softc *sc, struct cf_level *dup, struct cf_setting *set)
void free(void *addr, struct malloc_type *mtp)
struct cf_level curr_level
int printf(const char *fmt,...)
static device_method_t cpufreq_methods[]
int taskqueue_enqueue(struct taskqueue *queue, struct task *task)
void sbuf_delete(struct sbuf *s)
void * device_get_softc(device_t dev)
Return the device's softc field.
int cpufreq_register(device_t dev)
void sched_bind(struct thread *td, int cpu)
char * sbuf_data(struct sbuf *s)
int sbuf_finish(struct sbuf *s)
int sbuf_cpy(struct sbuf *s, const char *str)
int sbuf_trim(struct sbuf *s)
SYSCTL_INT(_debug_cpufreq, OID_AUTO, lowest, CTLFLAG_RW,&cf_lowest_freq, 1,"Don't provide levels below this frequency.")
const char * device_get_nameunit(device_t dev)
Return a string containing the device's devclass name followed by an ascii representation of the devi...
int device_attach(device_t dev)
Attach a device driver to a device.
int device_delete_child(device_t dev, device_t child)
Delete a device.
SLIST_HEAD(et_eventtimers_list, eventtimer)
int sysctl_ctx_free(struct sysctl_ctx_list *clist)
int device_get_children(device_t dev, device_t **devlistp, int *devcountp)
Get a list of children of a device.
void sched_unbind(struct thread *td)
static int cf_levels_method(device_t dev, struct cf_level *levels, int *count)
int sysctl_ctx_init(struct sysctl_ctx_list *c)