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 cpupower_read_sysfs(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_frequencies 337 *cpufreq_get_frequencies(const char *type, unsigned int cpu) 338 { 339 struct cpufreq_frequencies *first = NULL; 340 struct cpufreq_frequencies *current = NULL; 341 char one_value[SYSFS_PATH_MAX]; 342 char linebuf[MAX_LINE_LEN]; 343 char fname[MAX_LINE_LEN]; 344 unsigned int pos, i; 345 unsigned int len; 346 347 snprintf(fname, MAX_LINE_LEN, "scaling_%s_frequencies", type); 348 349 len = sysfs_cpufreq_read_file(cpu, fname, 350 linebuf, sizeof(linebuf)); 351 if (len == 0) 352 return NULL; 353 354 pos = 0; 355 for (i = 0; i < len; i++) { 356 if (linebuf[i] == ' ' || linebuf[i] == '\n') { 357 if (i - pos < 2) 358 continue; 359 if (i - pos >= SYSFS_PATH_MAX) 360 goto error_out; 361 if (current) { 362 current->next = malloc(sizeof(*current)); 363 if (!current->next) 364 goto error_out; 365 current = current->next; 366 } else { 367 first = malloc(sizeof(*first)); 368 if (!first) 369 goto error_out; 370 current = first; 371 } 372 current->first = first; 373 current->next = NULL; 374 375 memcpy(one_value, linebuf + pos, i - pos); 376 one_value[i - pos] = '\0'; 377 if (sscanf(one_value, "%lu", ¤t->frequency) != 1) 378 goto error_out; 379 380 pos = i + 1; 381 } 382 } 383 384 return first; 385 386 error_out: 387 while (first) { 388 current = first->next; 389 free(first); 390 first = current; 391 } 392 return NULL; 393 } 394 395 void cpufreq_put_frequencies(struct cpufreq_frequencies *any) 396 { 397 struct cpufreq_frequencies *tmp, *next; 398 399 if (!any) 400 return; 401 402 tmp = any->first; 403 while (tmp) { 404 next = tmp->next; 405 free(tmp); 406 tmp = next; 407 } 408 } 409 410 static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu, 411 const char *file) 412 { 413 struct cpufreq_affected_cpus *first = NULL; 414 struct cpufreq_affected_cpus *current = NULL; 415 char one_value[SYSFS_PATH_MAX]; 416 char linebuf[MAX_LINE_LEN]; 417 unsigned int pos, i; 418 unsigned int len; 419 420 len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf)); 421 if (len == 0) 422 return NULL; 423 424 pos = 0; 425 for (i = 0; i < len; i++) { 426 if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') { 427 if (i - pos < 1) 428 continue; 429 if (i - pos >= SYSFS_PATH_MAX) 430 goto error_out; 431 if (current) { 432 current->next = malloc(sizeof(*current)); 433 if (!current->next) 434 goto error_out; 435 current = current->next; 436 } else { 437 first = malloc(sizeof(*first)); 438 if (!first) 439 goto error_out; 440 current = first; 441 } 442 current->first = first; 443 current->next = NULL; 444 445 memcpy(one_value, linebuf + pos, i - pos); 446 one_value[i - pos] = '\0'; 447 448 if (sscanf(one_value, "%u", ¤t->cpu) != 1) 449 goto error_out; 450 451 pos = i + 1; 452 } 453 } 454 455 return first; 456 457 error_out: 458 while (first) { 459 current = first->next; 460 free(first); 461 first = current; 462 } 463 return NULL; 464 } 465 466 struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu) 467 { 468 return sysfs_get_cpu_list(cpu, "affected_cpus"); 469 } 470 471 void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) 472 { 473 struct cpufreq_affected_cpus *tmp, *next; 474 475 if (!any) 476 return; 477 478 tmp = any->first; 479 while (tmp) { 480 next = tmp->next; 481 free(tmp); 482 tmp = next; 483 } 484 } 485 486 487 struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu) 488 { 489 return sysfs_get_cpu_list(cpu, "related_cpus"); 490 } 491 492 void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) 493 { 494 cpufreq_put_affected_cpus(any); 495 } 496 497 static int verify_gov(char *new_gov, char *passed_gov) 498 { 499 unsigned int i, j = 0; 500 501 if (!passed_gov || (strlen(passed_gov) > 19)) 502 return -EINVAL; 503 504 strncpy(new_gov, passed_gov, 20); 505 for (i = 0; i < 20; i++) { 506 if (j) { 507 new_gov[i] = '\0'; 508 continue; 509 } 510 if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) 511 continue; 512 513 if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) 514 continue; 515 516 if (new_gov[i] == '-') 517 continue; 518 519 if (new_gov[i] == '_') 520 continue; 521 522 if (new_gov[i] == '\0') { 523 j = 1; 524 continue; 525 } 526 return -EINVAL; 527 } 528 new_gov[19] = '\0'; 529 return 0; 530 } 531 532 int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy) 533 { 534 char min[SYSFS_PATH_MAX]; 535 char max[SYSFS_PATH_MAX]; 536 char gov[SYSFS_PATH_MAX]; 537 int ret; 538 unsigned long old_min; 539 int write_max_first; 540 541 if (!policy || !(policy->governor)) 542 return -EINVAL; 543 544 if (policy->max < policy->min) 545 return -EINVAL; 546 547 if (verify_gov(gov, policy->governor)) 548 return -EINVAL; 549 550 snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min); 551 snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max); 552 553 old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); 554 write_max_first = (old_min && (policy->max < old_min) ? 0 : 1); 555 556 if (write_max_first) { 557 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 558 max, strlen(max)); 559 if (ret) 560 return ret; 561 } 562 563 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min, 564 strlen(min)); 565 if (ret) 566 return ret; 567 568 if (!write_max_first) { 569 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 570 max, strlen(max)); 571 if (ret) 572 return ret; 573 } 574 575 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, 576 gov, strlen(gov)); 577 } 578 579 580 int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq) 581 { 582 char value[SYSFS_PATH_MAX]; 583 584 snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq); 585 586 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, 587 value, strlen(value)); 588 } 589 590 591 int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq) 592 { 593 char value[SYSFS_PATH_MAX]; 594 595 snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq); 596 597 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 598 value, strlen(value)); 599 } 600 601 int cpufreq_modify_policy_governor(unsigned int cpu, char *governor) 602 { 603 char new_gov[SYSFS_PATH_MAX]; 604 605 if ((!governor) || (strlen(governor) > 19)) 606 return -EINVAL; 607 608 if (verify_gov(new_gov, governor)) 609 return -EINVAL; 610 611 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, 612 new_gov, strlen(new_gov)); 613 } 614 615 int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency) 616 { 617 struct cpufreq_policy *pol = cpufreq_get_policy(cpu); 618 char userspace_gov[] = "userspace"; 619 char freq[SYSFS_PATH_MAX]; 620 int ret; 621 622 if (!pol) 623 return -ENODEV; 624 625 if (strncmp(pol->governor, userspace_gov, 9) != 0) { 626 ret = cpufreq_modify_policy_governor(cpu, userspace_gov); 627 if (ret) { 628 cpufreq_put_policy(pol); 629 return ret; 630 } 631 } 632 633 cpufreq_put_policy(pol); 634 635 snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency); 636 637 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED, 638 freq, strlen(freq)); 639 } 640 641 struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, 642 unsigned long long *total_time) 643 { 644 struct cpufreq_stats *first = NULL; 645 struct cpufreq_stats *current = NULL; 646 char one_value[SYSFS_PATH_MAX]; 647 char linebuf[MAX_LINE_LEN]; 648 unsigned int pos, i; 649 unsigned int len; 650 651 len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state", 652 linebuf, sizeof(linebuf)); 653 if (len == 0) 654 return NULL; 655 656 *total_time = 0; 657 pos = 0; 658 for (i = 0; i < len; i++) { 659 if (i == strlen(linebuf) || linebuf[i] == '\n') { 660 if (i - pos < 2) 661 continue; 662 if ((i - pos) >= SYSFS_PATH_MAX) 663 goto error_out; 664 if (current) { 665 current->next = malloc(sizeof(*current)); 666 if (!current->next) 667 goto error_out; 668 current = current->next; 669 } else { 670 first = malloc(sizeof(*first)); 671 if (!first) 672 goto error_out; 673 current = first; 674 } 675 current->first = first; 676 current->next = NULL; 677 678 memcpy(one_value, linebuf + pos, i - pos); 679 one_value[i - pos] = '\0'; 680 if (sscanf(one_value, "%lu %llu", 681 ¤t->frequency, 682 ¤t->time_in_state) != 2) 683 goto error_out; 684 685 *total_time = *total_time + current->time_in_state; 686 pos = i + 1; 687 } 688 } 689 690 return first; 691 692 error_out: 693 while (first) { 694 current = first->next; 695 free(first); 696 first = current; 697 } 698 return NULL; 699 } 700 701 void cpufreq_put_stats(struct cpufreq_stats *any) 702 { 703 struct cpufreq_stats *tmp, *next; 704 705 if (!any) 706 return; 707 708 tmp = any->first; 709 while (tmp) { 710 next = tmp->next; 711 free(tmp); 712 tmp = next; 713 } 714 } 715 716 unsigned long cpufreq_get_transitions(unsigned int cpu) 717 { 718 return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS); 719 } 720