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