linux/tools/power/cpupower/lib/cpufreq.c
<<
>>
Prefs
   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 */
  23static 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 */
  35static 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
  63enum 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
  75static 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
  87static 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
 114enum cpufreq_string {
 115        SCALING_DRIVER,
 116        SCALING_GOVERNOR,
 117        MAX_CPUFREQ_STRING_FILES
 118};
 119
 120static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
 121        [SCALING_DRIVER] = "scaling_driver",
 122        [SCALING_GOVERNOR] = "scaling_governor",
 123};
 124
 125
 126static 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
 153enum 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
 161static 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
 168static 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
 182unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
 183{
 184        return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
 185}
 186
 187unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
 188{
 189        return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
 190}
 191
 192unsigned long cpufreq_get_transition_latency(unsigned int cpu)
 193{
 194        return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
 195}
 196
 197int 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
 215char *cpufreq_get_driver(unsigned int cpu)
 216{
 217        return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
 218}
 219
 220void cpufreq_put_driver(char *ptr)
 221{
 222        if (!ptr)
 223                return;
 224        free(ptr);
 225}
 226
 227struct 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
 251void 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
 261struct 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                                        return NULL;
 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
 317void 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
 335struct 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                                        return NULL;
 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", &current->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
 391struct 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                                        return NULL;
 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", &current->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
 447void 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
 462void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)
 463{
 464        cpufreq_put_available_frequencies(any);
 465}
 466
 467static 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                                        return NULL;
 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", &current->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
 523struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
 524{
 525        return sysfs_get_cpu_list(cpu, "affected_cpus");
 526}
 527
 528void 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
 544struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
 545{
 546        return sysfs_get_cpu_list(cpu, "related_cpus");
 547}
 548
 549void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
 550{
 551        cpufreq_put_affected_cpus(any);
 552}
 553
 554static 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
 589int 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
 637int 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
 648int 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
 658int 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
 672int 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
 698struct 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                                        return NULL;
 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                                        &current->frequency,
 739                                        &current->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
 758void 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
 773unsigned long cpufreq_get_transitions(unsigned int cpu)
 774{
 775        return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
 776}
 777