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