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