1 /* 2 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> 3 * 4 * Licensed under the terms of the GNU GPL License version 2. 5 */ 6 7 8 #include <stdio.h> 9 #include <errno.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 #include <fcntl.h> 15 #include <unistd.h> 16 17 #include "cpufreq.h" 18 #include "cpupower_intern.h" 19 20 /* CPUFREQ sysfs access **************************************************/ 21 22 /* helper function to read file from /sys into given buffer */ 23 /* fname is a relative path under "cpuX/cpufreq" dir */ 24 static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname, 25 char *buf, size_t buflen) 26 { 27 char path[SYSFS_PATH_MAX]; 28 29 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", 30 cpu, fname); 31 return sysfs_read_file(path, buf, buflen); 32 } 33 34 /* helper function to write a new value to a /sys file */ 35 /* fname is a relative path under "cpuX/cpufreq" dir */ 36 static unsigned int sysfs_cpufreq_write_file(unsigned int cpu, 37 const char *fname, 38 const char *value, size_t len) 39 { 40 char path[SYSFS_PATH_MAX]; 41 int fd; 42 ssize_t numwrite; 43 44 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", 45 cpu, fname); 46 47 fd = open(path, O_WRONLY); 48 if (fd == -1) 49 return 0; 50 51 numwrite = write(fd, value, len); 52 if (numwrite < 1) { 53 close(fd); 54 return 0; 55 } 56 57 close(fd); 58 59 return (unsigned int) numwrite; 60 } 61 62 /* read access to files which contain one numeric value */ 63 64 enum cpufreq_value { 65 CPUINFO_CUR_FREQ, 66 CPUINFO_MIN_FREQ, 67 CPUINFO_MAX_FREQ, 68 CPUINFO_LATENCY, 69 SCALING_CUR_FREQ, 70 SCALING_MIN_FREQ, 71 SCALING_MAX_FREQ, 72 STATS_NUM_TRANSITIONS, 73 MAX_CPUFREQ_VALUE_READ_FILES 74 }; 75 76 static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = { 77 [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq", 78 [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq", 79 [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq", 80 [CPUINFO_LATENCY] = "cpuinfo_transition_latency", 81 [SCALING_CUR_FREQ] = "scaling_cur_freq", 82 [SCALING_MIN_FREQ] = "scaling_min_freq", 83 [SCALING_MAX_FREQ] = "scaling_max_freq", 84 [STATS_NUM_TRANSITIONS] = "stats/total_trans" 85 }; 86 87 88 static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, 89 enum cpufreq_value which) 90 { 91 unsigned long value; 92 unsigned int len; 93 char linebuf[MAX_LINE_LEN]; 94 char *endp; 95 96 if (which >= MAX_CPUFREQ_VALUE_READ_FILES) 97 return 0; 98 99 len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which], 100 linebuf, sizeof(linebuf)); 101 102 if (len == 0) 103 return 0; 104 105 value = strtoul(linebuf, &endp, 0); 106 107 if (endp == linebuf || errno == ERANGE) 108 return 0; 109 110 return value; 111 } 112 113 /* read access to files which contain one string */ 114 115 enum cpufreq_string { 116 SCALING_DRIVER, 117 SCALING_GOVERNOR, 118 MAX_CPUFREQ_STRING_FILES 119 }; 120 121 static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = { 122 [SCALING_DRIVER] = "scaling_driver", 123 [SCALING_GOVERNOR] = "scaling_governor", 124 }; 125 126 127 static char *sysfs_cpufreq_get_one_string(unsigned int cpu, 128 enum cpufreq_string which) 129 { 130 char linebuf[MAX_LINE_LEN]; 131 char *result; 132 unsigned int len; 133 134 if (which >= MAX_CPUFREQ_STRING_FILES) 135 return NULL; 136 137 len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which], 138 linebuf, sizeof(linebuf)); 139 if (len == 0) 140 return NULL; 141 142 result = strdup(linebuf); 143 if (result == NULL) 144 return NULL; 145 146 if (result[strlen(result) - 1] == '\n') 147 result[strlen(result) - 1] = '\0'; 148 149 return result; 150 } 151 152 /* write access */ 153 154 enum cpufreq_write { 155 WRITE_SCALING_MIN_FREQ, 156 WRITE_SCALING_MAX_FREQ, 157 WRITE_SCALING_GOVERNOR, 158 WRITE_SCALING_SET_SPEED, 159 MAX_CPUFREQ_WRITE_FILES 160 }; 161 162 static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = { 163 [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq", 164 [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq", 165 [WRITE_SCALING_GOVERNOR] = "scaling_governor", 166 [WRITE_SCALING_SET_SPEED] = "scaling_setspeed", 167 }; 168 169 static int sysfs_cpufreq_write_one_value(unsigned int cpu, 170 enum cpufreq_write which, 171 const char *new_value, size_t len) 172 { 173 if (which >= MAX_CPUFREQ_WRITE_FILES) 174 return 0; 175 176 if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which], 177 new_value, len) != len) 178 return -ENODEV; 179 180 return 0; 181 }; 182 183 unsigned long cpufreq_get_freq_kernel(unsigned int cpu) 184 { 185 return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ); 186 } 187 188 unsigned long cpufreq_get_freq_hardware(unsigned int cpu) 189 { 190 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ); 191 } 192 193 unsigned long cpufreq_get_transition_latency(unsigned int cpu) 194 { 195 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY); 196 } 197 198 int cpufreq_get_hardware_limits(unsigned int cpu, 199 unsigned long *min, 200 unsigned long *max) 201 { 202 if ((!min) || (!max)) 203 return -EINVAL; 204 205 *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ); 206 if (!*min) 207 return -ENODEV; 208 209 *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ); 210 if (!*max) 211 return -ENODEV; 212 213 return 0; 214 } 215 216 char *cpufreq_get_driver(unsigned int cpu) 217 { 218 return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER); 219 } 220 221 void cpufreq_put_driver(char *ptr) 222 { 223 if (!ptr) 224 return; 225 free(ptr); 226 } 227 228 struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu) 229 { 230 struct cpufreq_policy *policy; 231 232 policy = malloc(sizeof(struct cpufreq_policy)); 233 if (!policy) 234 return NULL; 235 236 policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR); 237 if (!policy->governor) { 238 free(policy); 239 return NULL; 240 } 241 policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); 242 policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ); 243 if ((!policy->min) || (!policy->max)) { 244 free(policy->governor); 245 free(policy); 246 return NULL; 247 } 248 249 return policy; 250 } 251 252 void cpufreq_put_policy(struct cpufreq_policy *policy) 253 { 254 if ((!policy) || (!policy->governor)) 255 return; 256 257 free(policy->governor); 258 policy->governor = NULL; 259 free(policy); 260 } 261 262 struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned 263 int cpu) 264 { 265 struct cpufreq_available_governors *first = NULL; 266 struct cpufreq_available_governors *current = NULL; 267 char linebuf[MAX_LINE_LEN]; 268 unsigned int pos, i; 269 unsigned int len; 270 271 len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors", 272 linebuf, sizeof(linebuf)); 273 if (len == 0) 274 return NULL; 275 276 pos = 0; 277 for (i = 0; i < len; i++) { 278 if (linebuf[i] == ' ' || linebuf[i] == '\n') { 279 if (i - pos < 2) 280 continue; 281 if (current) { 282 current->next = malloc(sizeof(*current)); 283 if (!current->next) 284 goto error_out; 285 current = current->next; 286 } else { 287 first = malloc(sizeof(*first)); 288 if (!first) 289 goto error_out; 290 current = first; 291 } 292 current->first = first; 293 current->next = NULL; 294 295 current->governor = malloc(i - pos + 1); 296 if (!current->governor) 297 goto error_out; 298 299 memcpy(current->governor, linebuf + pos, i - pos); 300 current->governor[i - pos] = '\0'; 301 pos = i + 1; 302 } 303 } 304 305 return first; 306 307 error_out: 308 while (first) { 309 current = first->next; 310 if (first->governor) 311 free(first->governor); 312 free(first); 313 first = current; 314 } 315 return NULL; 316 } 317 318 void cpufreq_put_available_governors(struct cpufreq_available_governors *any) 319 { 320 struct cpufreq_available_governors *tmp, *next; 321 322 if (!any) 323 return; 324 325 tmp = any->first; 326 while (tmp) { 327 next = tmp->next; 328 if (tmp->governor) 329 free(tmp->governor); 330 free(tmp); 331 tmp = next; 332 } 333 } 334 335 336 struct cpufreq_available_frequencies 337 *cpufreq_get_available_frequencies(unsigned int cpu) 338 { 339 struct cpufreq_available_frequencies *first = NULL; 340 struct cpufreq_available_frequencies *current = NULL; 341 char one_value[SYSFS_PATH_MAX]; 342 char linebuf[MAX_LINE_LEN]; 343 unsigned int pos, i; 344 unsigned int len; 345 346 len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies", 347 linebuf, sizeof(linebuf)); 348 if (len == 0) 349 return NULL; 350 351 pos = 0; 352 for (i = 0; i < len; i++) { 353 if (linebuf[i] == ' ' || linebuf[i] == '\n') { 354 if (i - pos < 2) 355 continue; 356 if (i - pos >= SYSFS_PATH_MAX) 357 goto error_out; 358 if (current) { 359 current->next = malloc(sizeof(*current)); 360 if (!current->next) 361 goto error_out; 362 current = current->next; 363 } else { 364 first = malloc(sizeof(*first)); 365 if (!first) 366 goto error_out; 367 current = first; 368 } 369 current->first = first; 370 current->next = NULL; 371 372 memcpy(one_value, linebuf + pos, i - pos); 373 one_value[i - pos] = '\0'; 374 if (sscanf(one_value, "%lu", ¤t->frequency) != 1) 375 goto error_out; 376 377 pos = i + 1; 378 } 379 } 380 381 return first; 382 383 error_out: 384 while (first) { 385 current = first->next; 386 free(first); 387 first = current; 388 } 389 return NULL; 390 } 391 392 void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies 393 *any) { 394 struct cpufreq_available_frequencies *tmp, *next; 395 396 if (!any) 397 return; 398 399 tmp = any->first; 400 while (tmp) { 401 next = tmp->next; 402 free(tmp); 403 tmp = next; 404 } 405 } 406 407 static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu, 408 const char *file) 409 { 410 struct cpufreq_affected_cpus *first = NULL; 411 struct cpufreq_affected_cpus *current = NULL; 412 char one_value[SYSFS_PATH_MAX]; 413 char linebuf[MAX_LINE_LEN]; 414 unsigned int pos, i; 415 unsigned int len; 416 417 len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf)); 418 if (len == 0) 419 return NULL; 420 421 pos = 0; 422 for (i = 0; i < len; i++) { 423 if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') { 424 if (i - pos < 1) 425 continue; 426 if (i - pos >= SYSFS_PATH_MAX) 427 goto error_out; 428 if (current) { 429 current->next = malloc(sizeof(*current)); 430 if (!current->next) 431 goto error_out; 432 current = current->next; 433 } else { 434 first = malloc(sizeof(*first)); 435 if (!first) 436 goto error_out; 437 current = first; 438 } 439 current->first = first; 440 current->next = NULL; 441 442 memcpy(one_value, linebuf + pos, i - pos); 443 one_value[i - pos] = '\0'; 444 445 if (sscanf(one_value, "%u", ¤t->cpu) != 1) 446 goto error_out; 447 448 pos = i + 1; 449 } 450 } 451 452 return first; 453 454 error_out: 455 while (first) { 456 current = first->next; 457 free(first); 458 first = current; 459 } 460 return NULL; 461 } 462 463 struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu) 464 { 465 return sysfs_get_cpu_list(cpu, "affected_cpus"); 466 } 467 468 void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) 469 { 470 struct cpufreq_affected_cpus *tmp, *next; 471 472 if (!any) 473 return; 474 475 tmp = any->first; 476 while (tmp) { 477 next = tmp->next; 478 free(tmp); 479 tmp = next; 480 } 481 } 482 483 484 struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu) 485 { 486 return sysfs_get_cpu_list(cpu, "related_cpus"); 487 } 488 489 void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) 490 { 491 cpufreq_put_affected_cpus(any); 492 } 493 494 static int verify_gov(char *new_gov, char *passed_gov) 495 { 496 unsigned int i, j = 0; 497 498 if (!passed_gov || (strlen(passed_gov) > 19)) 499 return -EINVAL; 500 501 strncpy(new_gov, passed_gov, 20); 502 for (i = 0; i < 20; i++) { 503 if (j) { 504 new_gov[i] = '\0'; 505 continue; 506 } 507 if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) 508 continue; 509 510 if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) 511 continue; 512 513 if (new_gov[i] == '-') 514 continue; 515 516 if (new_gov[i] == '_') 517 continue; 518 519 if (new_gov[i] == '\0') { 520 j = 1; 521 continue; 522 } 523 return -EINVAL; 524 } 525 new_gov[19] = '\0'; 526 return 0; 527 } 528 529 int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy) 530 { 531 char min[SYSFS_PATH_MAX]; 532 char max[SYSFS_PATH_MAX]; 533 char gov[SYSFS_PATH_MAX]; 534 int ret; 535 unsigned long old_min; 536 int write_max_first; 537 538 if (!policy || !(policy->governor)) 539 return -EINVAL; 540 541 if (policy->max < policy->min) 542 return -EINVAL; 543 544 if (verify_gov(gov, policy->governor)) 545 return -EINVAL; 546 547 snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min); 548 snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max); 549 550 old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); 551 write_max_first = (old_min && (policy->max < old_min) ? 0 : 1); 552 553 if (write_max_first) { 554 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 555 max, strlen(max)); 556 if (ret) 557 return ret; 558 } 559 560 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min, 561 strlen(min)); 562 if (ret) 563 return ret; 564 565 if (!write_max_first) { 566 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 567 max, strlen(max)); 568 if (ret) 569 return ret; 570 } 571 572 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, 573 gov, strlen(gov)); 574 } 575 576 577 int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq) 578 { 579 char value[SYSFS_PATH_MAX]; 580 581 snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq); 582 583 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, 584 value, strlen(value)); 585 } 586 587 588 int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq) 589 { 590 char value[SYSFS_PATH_MAX]; 591 592 snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq); 593 594 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 595 value, strlen(value)); 596 } 597 598 int cpufreq_modify_policy_governor(unsigned int cpu, char *governor) 599 { 600 char new_gov[SYSFS_PATH_MAX]; 601 602 if ((!governor) || (strlen(governor) > 19)) 603 return -EINVAL; 604 605 if (verify_gov(new_gov, governor)) 606 return -EINVAL; 607 608 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, 609 new_gov, strlen(new_gov)); 610 } 611 612 int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency) 613 { 614 struct cpufreq_policy *pol = cpufreq_get_policy(cpu); 615 char userspace_gov[] = "userspace"; 616 char freq[SYSFS_PATH_MAX]; 617 int ret; 618 619 if (!pol) 620 return -ENODEV; 621 622 if (strncmp(pol->governor, userspace_gov, 9) != 0) { 623 ret = cpufreq_modify_policy_governor(cpu, userspace_gov); 624 if (ret) { 625 cpufreq_put_policy(pol); 626 return ret; 627 } 628 } 629 630 cpufreq_put_policy(pol); 631 632 snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency); 633 634 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED, 635 freq, strlen(freq)); 636 } 637 638 struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, 639 unsigned long long *total_time) 640 { 641 struct cpufreq_stats *first = NULL; 642 struct cpufreq_stats *current = NULL; 643 char one_value[SYSFS_PATH_MAX]; 644 char linebuf[MAX_LINE_LEN]; 645 unsigned int pos, i; 646 unsigned int len; 647 648 len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state", 649 linebuf, sizeof(linebuf)); 650 if (len == 0) 651 return NULL; 652 653 *total_time = 0; 654 pos = 0; 655 for (i = 0; i < len; i++) { 656 if (i == strlen(linebuf) || linebuf[i] == '\n') { 657 if (i - pos < 2) 658 continue; 659 if ((i - pos) >= SYSFS_PATH_MAX) 660 goto error_out; 661 if (current) { 662 current->next = malloc(sizeof(*current)); 663 if (!current->next) 664 goto error_out; 665 current = current->next; 666 } else { 667 first = malloc(sizeof(*first)); 668 if (!first) 669 goto error_out; 670 current = first; 671 } 672 current->first = first; 673 current->next = NULL; 674 675 memcpy(one_value, linebuf + pos, i - pos); 676 one_value[i - pos] = '\0'; 677 if (sscanf(one_value, "%lu %llu", 678 ¤t->frequency, 679 ¤t->time_in_state) != 2) 680 goto error_out; 681 682 *total_time = *total_time + current->time_in_state; 683 pos = i + 1; 684 } 685 } 686 687 return first; 688 689 error_out: 690 while (first) { 691 current = first->next; 692 free(first); 693 first = current; 694 } 695 return NULL; 696 } 697 698 void cpufreq_put_stats(struct cpufreq_stats *any) 699 { 700 struct cpufreq_stats *tmp, *next; 701 702 if (!any) 703 return; 704 705 tmp = any->first; 706 while (tmp) { 707 next = tmp->next; 708 free(tmp); 709 tmp = next; 710 } 711 } 712 713 unsigned long cpufreq_get_transitions(unsigned int cpu) 714 { 715 return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS); 716 } 717