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