kern_cpu.c revision 142032
118099Spst/*- 218099Spst * Copyright (c) 2004-2005 Nate Lawson (SDG) 318099Spst * All rights reserved. 418099Spst * 518099Spst * Redistribution and use in source and binary forms, with or without 618099Spst * modification, are permitted provided that the following conditions 718099Spst * are met: 818099Spst * 1. Redistributions of source code must retain the above copyright 918099Spst * notice, this list of conditions and the following disclaimer. 1018099Spst * 2. Redistributions in binary form must reproduce the above copyright 1118099Spst * notice, this list of conditions and the following disclaimer in the 1218099Spst * documentation and/or other materials provided with the distribution. 1318099Spst * 1418099Spst * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1518099Spst * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1618099Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1718099Spst * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1818099Spst * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1918099Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2018099Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2118099Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2218099Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2318099Spst * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2418099Spst * SUCH DAMAGE. 2518099Spst */ 2618099Spst 2718099Spst#include <sys/cdefs.h> 2818099Spst__FBSDID("$FreeBSD: head/sys/kern/kern_cpu.c 142032 2005-02-18 00:23:36Z njl $"); 2918099Spst 3018099Spst#include <sys/param.h> 3118099Spst#include <sys/bus.h> 3218099Spst#include <sys/cpu.h> 3318099Spst#include <sys/eventhandler.h> 3418099Spst#include <sys/kernel.h> 3518099Spst#include <sys/malloc.h> 3618099Spst#include <sys/module.h> 3718099Spst#include <sys/proc.h> 3818099Spst#include <sys/queue.h> 3918099Spst#include <sys/sched.h> 4018099Spst#include <sys/sysctl.h> 4118099Spst#include <sys/systm.h> 4218099Spst#include <sys/sbuf.h> 4318099Spst#include <sys/timetc.h> 4418099Spst 4518099Spst#include "cpufreq_if.h" 4618099Spst 4718099Spst/* 4818099Spst * Common CPU frequency glue code. Drivers for specific hardware can 4918099Spst * attach this interface to allow users to get/set the CPU frequency. 5018099Spst */ 5118099Spst 5218099Spst/* 5318099Spst * Number of levels we can handle. Levels are synthesized from settings 5418099Spst * so for N settings there may be N^2 levels. 5518099Spst */ 5618099Spst#define CF_MAX_LEVELS 32 5718099Spst 5818099Spststruct cpufreq_softc { 5918099Spst struct cf_level curr_level; 6018099Spst int curr_priority; 6118099Spst struct cf_level saved_level; 6218099Spst int saved_priority; 6318099Spst struct cf_level_lst all_levels; 6418099Spst int all_count; 6518099Spst int max_mhz; 6618099Spst device_t dev; 6718099Spst struct sysctl_ctx_list sysctl_ctx; 6818099Spst}; 6918099Spst 7018099Spststruct cf_setting_array { 7118099Spst struct cf_setting sets[MAX_SETTINGS]; 7218099Spst int count; 7318099Spst TAILQ_ENTRY(cf_setting_array) link; 7418099Spst}; 7518099Spst 7618099SpstTAILQ_HEAD(cf_setting_lst, cf_setting_array); 7718099Spst 7818099Spststatic int cpufreq_attach(device_t dev); 7918099Spststatic int cpufreq_detach(device_t dev); 8018099Spststatic void cpufreq_evaluate(void *arg); 8118099Spststatic int cf_set_method(device_t dev, const struct cf_level *level, 8218099Spst int priority); 8318099Spststatic int cf_get_method(device_t dev, struct cf_level *level); 8418099Spststatic int cf_levels_method(device_t dev, struct cf_level *levels, 8518099Spst int *count); 8618099Spststatic int cpufreq_insert_abs(struct cpufreq_softc *sc, 8718099Spst struct cf_setting *sets, int count); 8818099Spststatic int cpufreq_expand_set(struct cpufreq_softc *sc, 8918099Spst struct cf_setting_array *set_arr); 9018099Spststatic struct cf_level *cpufreq_dup_set(struct cpufreq_softc *sc, 9118099Spst struct cf_level *dup, struct cf_setting *set); 9218099Spststatic int cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS); 9318099Spststatic int cpufreq_levels_sysctl(SYSCTL_HANDLER_ARGS); 9418099Spst 9518099Spststatic device_method_t cpufreq_methods[] = { 9618099Spst DEVMETHOD(device_probe, bus_generic_probe), 9718099Spst DEVMETHOD(device_attach, cpufreq_attach), 9818099Spst DEVMETHOD(device_detach, cpufreq_detach), 9918099Spst 10018099Spst DEVMETHOD(cpufreq_set, cf_set_method), 10118099Spst DEVMETHOD(cpufreq_get, cf_get_method), 10218099Spst DEVMETHOD(cpufreq_levels, cf_levels_method), 10318099Spst {0, 0} 104}; 105static driver_t cpufreq_driver = { 106 "cpufreq", cpufreq_methods, sizeof(struct cpufreq_softc) 107}; 108static devclass_t cpufreq_dc; 109DRIVER_MODULE(cpufreq, cpu, cpufreq_driver, cpufreq_dc, 0, 0); 110 111static eventhandler_tag cf_ev_tag; 112 113static int 114cpufreq_attach(device_t dev) 115{ 116 struct cpufreq_softc *sc; 117 device_t parent; 118 int numdevs; 119 120 sc = device_get_softc(dev); 121 parent = device_get_parent(dev); 122 sc->dev = dev; 123 sysctl_ctx_init(&sc->sysctl_ctx); 124 TAILQ_INIT(&sc->all_levels); 125 sc->curr_level.total_set.freq = CPUFREQ_VAL_UNKNOWN; 126 sc->saved_level.total_set.freq = CPUFREQ_VAL_UNKNOWN; 127 sc->max_mhz = CPUFREQ_VAL_UNKNOWN; 128 129 /* 130 * Only initialize one set of sysctls for all CPUs. In the future, 131 * if multiple CPUs can have different settings, we can move these 132 * sysctls to be under every CPU instead of just the first one. 133 */ 134 numdevs = devclass_get_count(cpufreq_dc); 135 if (numdevs > 1) 136 return (0); 137 138 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 139 SYSCTL_CHILDREN(device_get_sysctl_tree(parent)), 140 OID_AUTO, "freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 141 cpufreq_curr_sysctl, "I", "Current CPU frequency"); 142 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 143 SYSCTL_CHILDREN(device_get_sysctl_tree(parent)), 144 OID_AUTO, "freq_levels", CTLTYPE_STRING | CTLFLAG_RD, sc, 0, 145 cpufreq_levels_sysctl, "A", "CPU frequency levels"); 146 cf_ev_tag = EVENTHANDLER_REGISTER(cpufreq_changed, cpufreq_evaluate, 147 NULL, EVENTHANDLER_PRI_ANY); 148 149 return (0); 150} 151 152static int 153cpufreq_detach(device_t dev) 154{ 155 struct cpufreq_softc *sc; 156 int numdevs; 157 158 sc = device_get_softc(dev); 159 sysctl_ctx_free(&sc->sysctl_ctx); 160 161 /* Only clean up these resources when the last device is detaching. */ 162 numdevs = devclass_get_count(cpufreq_dc); 163 if (numdevs == 1) 164 EVENTHANDLER_DEREGISTER(cpufreq_changed, cf_ev_tag); 165 166 return (0); 167} 168 169static void 170cpufreq_evaluate(void *arg) 171{ 172 /* TODO: Re-evaluate when notified of changes to drivers. */ 173} 174 175static int 176cf_set_method(device_t dev, const struct cf_level *level, int priority) 177{ 178 struct cpufreq_softc *sc; 179 const struct cf_setting *set; 180 struct pcpu *pc; 181 int cpu_id, error, i; 182 183 sc = device_get_softc(dev); 184 185 /* 186 * Check that the TSC isn't being used as a timecounter. 187 * If it is, then return EBUSY and refuse to change the 188 * clock speed. 189 */ 190 if (strcmp(timecounter->tc_name, "TSC") == 0) 191 return (EBUSY); 192 193 /* 194 * If the caller didn't specify a level and one is saved, prepare to 195 * restore the saved level. If none has been saved, return an error. 196 * If they did specify one, but the requested level has a lower 197 * priority, don't allow the new level right now. 198 */ 199 if (level == NULL) { 200 if (sc->saved_level.total_set.freq != CPUFREQ_VAL_UNKNOWN) { 201 level = &sc->saved_level; 202 priority = sc->saved_priority; 203 } else 204 return (ENXIO); 205 } else if (priority < sc->curr_priority) 206 return (EPERM); 207 208 /* If already at this level, just return. */ 209 if (CPUFREQ_CMP(sc->curr_level.total_set.freq, level->total_set.freq)) 210 return (0); 211 212 /* First, set the absolute frequency via its driver. */ 213 set = &level->abs_set; 214 if (set->dev) { 215 if (!device_is_attached(set->dev)) { 216 error = ENXIO; 217 goto out; 218 } 219 220 /* Bind to the target CPU before switching, if necessary. */ 221 cpu_id = PCPU_GET(cpuid); 222 pc = cpu_get_pcpu(set->dev); 223 if (cpu_id != pc->pc_cpuid) { 224 mtx_lock_spin(&sched_lock); 225 sched_bind(curthread, pc->pc_cpuid); 226 mtx_unlock_spin(&sched_lock); 227 } 228 error = CPUFREQ_DRV_SET(set->dev, set); 229 if (cpu_id != pc->pc_cpuid) { 230 mtx_lock_spin(&sched_lock); 231 sched_unbind(curthread); 232 mtx_unlock_spin(&sched_lock); 233 } 234 if (error) { 235 goto out; 236 } 237 } 238 239 /* Next, set any/all relative frequencies via their drivers. */ 240 for (i = 0; i < level->rel_count; i++) { 241 set = &level->rel_set[i]; 242 if (!device_is_attached(set->dev)) { 243 error = ENXIO; 244 goto out; 245 } 246 247 /* Bind to the target CPU before switching, if necessary. */ 248 cpu_id = PCPU_GET(cpuid); 249 pc = cpu_get_pcpu(set->dev); 250 if (cpu_id != pc->pc_cpuid) { 251 mtx_lock_spin(&sched_lock); 252 sched_bind(curthread, pc->pc_cpuid); 253 mtx_unlock_spin(&sched_lock); 254 } 255 error = CPUFREQ_DRV_SET(set->dev, set); 256 if (cpu_id != pc->pc_cpuid) { 257 mtx_lock_spin(&sched_lock); 258 sched_unbind(curthread); 259 mtx_unlock_spin(&sched_lock); 260 } 261 if (error) { 262 /* XXX Back out any successful setting? */ 263 goto out; 264 } 265 } 266 267 /* If we were restoring a saved state, reset it to "unused". */ 268 if (level == &sc->saved_level) { 269 sc->saved_level.total_set.freq = CPUFREQ_VAL_UNKNOWN; 270 sc->saved_priority = 0; 271 } 272 273 /* 274 * Before recording the current level, check if we're going to a 275 * higher priority and have not saved a level yet. If so, save the 276 * previous level and priority. 277 */ 278 if (sc->curr_level.total_set.freq != CPUFREQ_VAL_UNKNOWN && 279 sc->saved_level.total_set.freq == CPUFREQ_VAL_UNKNOWN && 280 priority > sc->curr_priority) { 281 sc->saved_level = sc->curr_level; 282 sc->saved_priority = sc->curr_priority; 283 } 284 sc->curr_level = *level; 285 sc->curr_priority = priority; 286 error = 0; 287 288out: 289 if (error) 290 device_printf(set->dev, "set freq failed, err %d\n", error); 291 return (error); 292} 293 294static int 295cf_get_method(device_t dev, struct cf_level *level) 296{ 297 struct cpufreq_softc *sc; 298 struct cf_level *levels; 299 struct cf_setting *curr_set, set; 300 struct pcpu *pc; 301 device_t *devs; 302 int count, error, i, numdevs; 303 uint64_t rate; 304 305 sc = device_get_softc(dev); 306 curr_set = &sc->curr_level.total_set; 307 levels = NULL; 308 309 /* If we already know the current frequency, we're done. */ 310 if (curr_set->freq != CPUFREQ_VAL_UNKNOWN) 311 goto out; 312 313 /* 314 * We need to figure out the current level. Loop through every 315 * driver, getting the current setting. Then, attempt to get a best 316 * match of settings against each level. 317 */ 318 count = CF_MAX_LEVELS; 319 levels = malloc(count * sizeof(*levels), M_TEMP, M_NOWAIT); 320 if (levels == NULL) 321 return (ENOMEM); 322 error = CPUFREQ_LEVELS(sc->dev, levels, &count); 323 if (error) 324 goto out; 325 error = device_get_children(device_get_parent(dev), &devs, &numdevs); 326 if (error) 327 goto out; 328 for (i = 0; i < numdevs && curr_set->freq == CPUFREQ_VAL_UNKNOWN; i++) { 329 if (!device_is_attached(devs[i])) 330 continue; 331 error = CPUFREQ_DRV_GET(devs[i], &set); 332 if (error) 333 continue; 334 for (i = 0; i < count; i++) { 335 if (CPUFREQ_CMP(set.freq, levels[i].total_set.freq)) { 336 sc->curr_level = levels[i]; 337 break; 338 } 339 } 340 } 341 free(devs, M_TEMP); 342 if (curr_set->freq != CPUFREQ_VAL_UNKNOWN) 343 goto out; 344 345 /* 346 * We couldn't find an exact match, so attempt to estimate and then 347 * match against a level. 348 */ 349 pc = cpu_get_pcpu(dev); 350 if (pc == NULL) { 351 error = ENXIO; 352 goto out; 353 } 354 cpu_est_clockrate(pc->pc_cpuid, &rate); 355 rate /= 1000000; 356 for (i = 0; i < count; i++) { 357 if (CPUFREQ_CMP(rate, levels[i].total_set.freq)) { 358 sc->curr_level = levels[i]; 359 break; 360 } 361 } 362 363out: 364 if (levels) 365 free(levels, M_TEMP); 366 *level = sc->curr_level; 367 return (0); 368} 369 370static int 371cf_levels_method(device_t dev, struct cf_level *levels, int *count) 372{ 373 struct cf_setting_array *set_arr; 374 struct cf_setting_lst rel_sets; 375 struct cpufreq_softc *sc; 376 struct cf_level *lev; 377 struct cf_setting *sets; 378 struct pcpu *pc; 379 device_t *devs; 380 int error, i, numdevs, set_count, type; 381 uint64_t rate; 382 383 if (levels == NULL || count == NULL) 384 return (EINVAL); 385 386 TAILQ_INIT(&rel_sets); 387 sc = device_get_softc(dev); 388 error = device_get_children(device_get_parent(dev), &devs, &numdevs); 389 if (error) 390 return (error); 391 sets = malloc(MAX_SETTINGS * sizeof(*sets), M_TEMP, M_NOWAIT); 392 if (sets == NULL) { 393 free(devs, M_TEMP); 394 return (ENOMEM); 395 } 396 397 /* Get settings from all cpufreq drivers. */ 398 for (i = 0; i < numdevs; i++) { 399 /* Skip devices that aren't ready. */ 400 if (!device_is_attached(devs[i])) 401 continue; 402 403 /* 404 * Get settings, skipping drivers that offer no settings or 405 * provide settings for informational purposes only. 406 */ 407 error = CPUFREQ_DRV_TYPE(devs[i], &type); 408 if (error || (type & CPUFREQ_FLAG_INFO_ONLY)) 409 continue; 410 set_count = MAX_SETTINGS; 411 error = CPUFREQ_DRV_SETTINGS(devs[i], sets, &set_count); 412 if (error || set_count == 0) 413 continue; 414 415 /* Add the settings to our absolute/relative lists. */ 416 switch (type & CPUFREQ_TYPE_MASK) { 417 case CPUFREQ_TYPE_ABSOLUTE: 418 error = cpufreq_insert_abs(sc, sets, set_count); 419 break; 420 case CPUFREQ_TYPE_RELATIVE: 421 set_arr = malloc(sizeof(*set_arr), M_TEMP, M_NOWAIT); 422 if (set_arr == NULL) { 423 error = ENOMEM; 424 goto out; 425 } 426 bcopy(sets, set_arr->sets, set_count * sizeof(*sets)); 427 set_arr->count = set_count; 428 TAILQ_INSERT_TAIL(&rel_sets, set_arr, link); 429 break; 430 default: 431 error = EINVAL; 432 break; 433 } 434 if (error) 435 goto out; 436 } 437 438 /* 439 * If there are no absolute levels, create a fake one at 100%. We 440 * then cache the clockrate for later use as our base frequency. 441 * 442 * XXX This assumes that the first time through, if we only have 443 * relative drivers, the CPU is currently running at 100%. 444 */ 445 if (TAILQ_EMPTY(&sc->all_levels)) { 446 if (sc->max_mhz == CPUFREQ_VAL_UNKNOWN) { 447 pc = cpu_get_pcpu(dev); 448 cpu_est_clockrate(pc->pc_cpuid, &rate); 449 sc->max_mhz = rate / 1000000; 450 } 451 memset(&sets[0], CPUFREQ_VAL_UNKNOWN, sizeof(*sets)); 452 sets[0].freq = sc->max_mhz; 453 sets[0].dev = NULL; 454 error = cpufreq_insert_abs(sc, sets, 1); 455 if (error) 456 goto out; 457 } 458 459 /* Create a combined list of absolute + relative levels. */ 460 TAILQ_FOREACH(set_arr, &rel_sets, link) 461 cpufreq_expand_set(sc, set_arr); 462 463 /* If the caller doesn't have enough space, return the actual count. */ 464 if (sc->all_count > *count) { 465 *count = sc->all_count; 466 error = E2BIG; 467 goto out; 468 } 469 470 /* Finally, output the list of levels. */ 471 i = 0; 472 TAILQ_FOREACH(lev, &sc->all_levels, link) { 473 levels[i] = *lev; 474 i++; 475 } 476 *count = sc->all_count; 477 error = 0; 478 479out: 480 /* Clear all levels since we regenerate them each time. */ 481 while ((lev = TAILQ_FIRST(&sc->all_levels)) != NULL) { 482 TAILQ_REMOVE(&sc->all_levels, lev, link); 483 free(lev, M_TEMP); 484 } 485 while ((set_arr = TAILQ_FIRST(&rel_sets)) != NULL) { 486 TAILQ_REMOVE(&rel_sets, set_arr, link); 487 free(set_arr, M_TEMP); 488 } 489 sc->all_count = 0; 490 free(devs, M_TEMP); 491 free(sets, M_TEMP); 492 return (error); 493} 494 495/* 496 * Create levels for an array of absolute settings and insert them in 497 * sorted order in the specified list. 498 */ 499static int 500cpufreq_insert_abs(struct cpufreq_softc *sc, struct cf_setting *sets, 501 int count) 502{ 503 struct cf_level_lst *list; 504 struct cf_level *level, *search; 505 int i; 506 507 list = &sc->all_levels; 508 for (i = 0; i < count; i++) { 509 level = malloc(sizeof(*level), M_TEMP, M_NOWAIT | M_ZERO); 510 if (level == NULL) 511 return (ENOMEM); 512 level->abs_set = sets[i]; 513 level->total_set = sets[i]; 514 level->total_set.dev = NULL; 515 sc->all_count++; 516 517 if (TAILQ_EMPTY(list)) { 518 TAILQ_INSERT_HEAD(list, level, link); 519 continue; 520 } 521 522 TAILQ_FOREACH_REVERSE(search, list, cf_level_lst, link) { 523 if (sets[i].freq <= search->total_set.freq) { 524 TAILQ_INSERT_AFTER(list, search, level, link); 525 break; 526 } 527 } 528 } 529 return (0); 530} 531 532/* 533 * Expand a group of relative settings, creating derived levels from them. 534 */ 535static int 536cpufreq_expand_set(struct cpufreq_softc *sc, struct cf_setting_array *set_arr) 537{ 538 struct cf_level *fill, *search; 539 struct cf_setting *set; 540 int i; 541 542 TAILQ_FOREACH(search, &sc->all_levels, link) { 543 /* Skip this level if we've already modified it. */ 544 for (i = 0; i < search->rel_count; i++) { 545 if (search->rel_set[i].dev == set_arr->sets[0].dev) 546 break; 547 } 548 if (i != search->rel_count) 549 continue; 550 551 /* Add each setting to the level, duplicating if necessary. */ 552 for (i = 0; i < set_arr->count; i++) { 553 set = &set_arr->sets[i]; 554 555 /* 556 * If this setting is less than 100%, split the level 557 * into two and add this setting to the new level. 558 */ 559 fill = search; 560 if (set->freq < 10000) 561 fill = cpufreq_dup_set(sc, search, set); 562 563 /* 564 * The new level was a duplicate of an existing level 565 * so we freed it. Go to the next setting. 566 */ 567 if (fill == NULL) 568 continue; 569 570 /* Add this setting to the existing or new level. */ 571 KASSERT(fill->rel_count < MAX_SETTINGS, 572 ("cpufreq: too many relative drivers (%d)", 573 MAX_SETTINGS)); 574 fill->rel_set[fill->rel_count] = *set; 575 fill->rel_count++; 576 } 577 } 578 579 return (0); 580} 581 582static struct cf_level * 583cpufreq_dup_set(struct cpufreq_softc *sc, struct cf_level *dup, 584 struct cf_setting *set) 585{ 586 struct cf_level_lst *list; 587 struct cf_level *fill, *itr; 588 struct cf_setting *fill_set, *itr_set; 589 int i; 590 591 /* 592 * Create a new level, copy it from the old one, and update the 593 * total frequency and power by the percentage specified in the 594 * relative setting. 595 */ 596 fill = malloc(sizeof(*fill), M_TEMP, M_NOWAIT); 597 if (fill == NULL) 598 return (NULL); 599 *fill = *dup; 600 fill_set = &fill->total_set; 601 fill_set->freq = 602 ((uint64_t)fill_set->freq * set->freq) / 10000; 603 if (fill_set->power != CPUFREQ_VAL_UNKNOWN) { 604 fill_set->power = ((uint64_t)fill_set->power * set->freq) 605 / 10000; 606 } 607 if (set->lat != CPUFREQ_VAL_UNKNOWN) { 608 if (fill_set->lat != CPUFREQ_VAL_UNKNOWN) 609 fill_set->lat += set->lat; 610 else 611 fill_set->lat = set->lat; 612 } 613 614 /* 615 * If we copied an old level that we already modified (say, at 100%), 616 * we need to remove that setting before adding this one. Since we 617 * process each setting array in order, we know any settings for this 618 * driver will be found at the end. 619 */ 620 for (i = fill->rel_count; i != 0; i--) { 621 if (fill->rel_set[i - 1].dev != set->dev) 622 break; 623 fill->rel_count--; 624 } 625 626 /* 627 * Insert the new level in sorted order. If we find a duplicate, 628 * free the new level. We can do this since any existing level will 629 * be guaranteed to have the same or less settings and thus consume 630 * less power. For example, a level with one absolute setting of 631 * 800 Mhz uses less power than one composed of an absolute setting 632 * of 1600 Mhz and a relative setting at 50%. 633 */ 634 list = &sc->all_levels; 635 if (TAILQ_EMPTY(list)) { 636 TAILQ_INSERT_HEAD(list, fill, link); 637 } else { 638 TAILQ_FOREACH_REVERSE(itr, list, cf_level_lst, link) { 639 itr_set = &itr->total_set; 640 if (CPUFREQ_CMP(fill_set->freq, itr_set->freq)) { 641 free(fill, M_TEMP); 642 fill = NULL; 643 break; 644 } else if (fill_set->freq < itr_set->freq) { 645 TAILQ_INSERT_AFTER(list, itr, fill, link); 646 sc->all_count++; 647 break; 648 } 649 } 650 } 651 652 return (fill); 653} 654 655static int 656cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS) 657{ 658 struct cpufreq_softc *sc; 659 struct cf_level *levels; 660 int count, devcount, error, freq, i, n; 661 device_t *devs; 662 663 devs = NULL; 664 sc = oidp->oid_arg1; 665 levels = malloc(CF_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); 666 if (levels == NULL) 667 return (ENOMEM); 668 669 error = CPUFREQ_GET(sc->dev, &levels[0]); 670 if (error) 671 goto out; 672 freq = levels[0].total_set.freq; 673 error = sysctl_handle_int(oidp, &freq, 0, req); 674 if (error != 0 || req->newptr == NULL) 675 goto out; 676 677 /* 678 * While we only call cpufreq_get() on one device (assuming all 679 * CPUs have equal levels), we call cpufreq_set() on all CPUs. 680 * This is needed for some MP systems. 681 */ 682 error = devclass_get_devices(cpufreq_dc, &devs, &devcount); 683 if (error) 684 goto out; 685 for (n = 0; n < devcount; n++) { 686 count = CF_MAX_LEVELS; 687 error = CPUFREQ_LEVELS(devs[n], levels, &count); 688 if (error) 689 break; 690 for (i = 0; i < count; i++) { 691 if (CPUFREQ_CMP(levels[i].total_set.freq, freq)) { 692 error = CPUFREQ_SET(devs[n], &levels[i], 693 CPUFREQ_PRIO_USER); 694 break; 695 } 696 } 697 if (i == count) { 698 error = EINVAL; 699 break; 700 } 701 } 702 703out: 704 if (devs) 705 free(devs, M_TEMP); 706 if (levels) 707 free(levels, M_TEMP); 708 return (error); 709} 710 711static int 712cpufreq_levels_sysctl(SYSCTL_HANDLER_ARGS) 713{ 714 struct cpufreq_softc *sc; 715 struct cf_level *levels; 716 struct cf_setting *set; 717 struct sbuf sb; 718 int count, error, i; 719 720 sc = oidp->oid_arg1; 721 sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND); 722 723 /* Get settings from the device and generate the output string. */ 724 count = CF_MAX_LEVELS; 725 levels = malloc(count * sizeof(*levels), M_TEMP, M_NOWAIT); 726 if (levels == NULL) 727 return (ENOMEM); 728 error = CPUFREQ_LEVELS(sc->dev, levels, &count); 729 if (error) 730 goto out; 731 if (count) { 732 for (i = 0; i < count; i++) { 733 set = &levels[i].total_set; 734 sbuf_printf(&sb, "%d/%d ", set->freq, set->power); 735 } 736 } else 737 sbuf_cpy(&sb, "0"); 738 sbuf_trim(&sb); 739 sbuf_finish(&sb); 740 error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); 741 742out: 743 free(levels, M_TEMP); 744 sbuf_delete(&sb); 745 return (error); 746} 747 748int 749cpufreq_register(device_t dev) 750{ 751 struct cpufreq_softc *sc; 752 device_t cf_dev, cpu_dev; 753 754 /* 755 * Add only one cpufreq device to each CPU. Currently, all CPUs 756 * must offer the same levels and be switched at the same time. 757 */ 758 cpu_dev = device_get_parent(dev); 759 if ((cf_dev = device_find_child(cpu_dev, "cpufreq", -1))) { 760 sc = device_get_softc(cf_dev); 761 sc->max_mhz = CPUFREQ_VAL_UNKNOWN; 762 return (0); 763 } 764 765 /* Add the child device and possibly sysctls. */ 766 cf_dev = BUS_ADD_CHILD(cpu_dev, 0, "cpufreq", -1); 767 if (cf_dev == NULL) 768 return (ENOMEM); 769 device_quiet(cf_dev); 770 771 return (device_probe_and_attach(cf_dev)); 772} 773 774int 775cpufreq_unregister(device_t dev) 776{ 777 device_t cf_dev, *devs; 778 int cfcount, devcount, error, i, type; 779 780 /* 781 * If this is the last cpufreq child device, remove the control 782 * device as well. We identify cpufreq children by calling a method 783 * they support. 784 */ 785 error = device_get_children(device_get_parent(dev), &devs, &devcount); 786 if (error) 787 return (error); 788 cf_dev = device_find_child(device_get_parent(dev), "cpufreq", -1); 789 cfcount = 0; 790 for (i = 0; i < devcount; i++) { 791 if (!device_is_attached(devs[i])) 792 continue; 793 if (CPUFREQ_DRV_TYPE(devs[i], &type) == 0) 794 cfcount++; 795 } 796 if (cfcount <= 1) 797 device_delete_child(device_get_parent(cf_dev), cf_dev); 798 free(devs, M_TEMP); 799 800 return (0); 801} 802