cpufreq简介

在Linux中,cpufreq子系统是用于管理和调节CPU频率。它提供了一组接口和机制,允许系统根据需求动态地调整CPU的工作频率,以实现功耗优化、性能调节和温度控制等目标。
 

cpufreq_register_driver函数是用于在Linux内核中注册CPU频率调节器驱动程序的函数。它将驱动程序的相关信息注册到内核,使得内核能够使用该驱动程序进行CPU频率的调节和管理。

函数原型如下:

int cpufreq_register_driver(struct cpufreq_driver *driver_data);

参数driver_data是一个指向struct cpufreq_driver结构体的指针,用于描述和配置CPU频率调节器驱动程序的属性和操作。

struct cpufreq_driver结构体包含了驱动程序的相关信息,通常定义如下:

struct cpufreq_driver {
    char        name[CPUFREQ_NAME_LEN];
    u8        flags;
    void        *driver_data;

    /* needed by all drivers */
    int        (*init)(struct cpufreq_policy *policy);
    int        (*verify)(struct cpufreq_policy *policy);

    /* define one out of two */
    int        (*setpolicy)(struct cpufreq_policy *policy);

    /*
     * On failure, should always restore frequency to policy->restore_freq
     * (i.e. old freq).
     */
    int        (*target)(struct cpufreq_policy *policy,
                  unsigned int target_freq,
                  unsigned int relation);    /* Deprecated */
    int        (*target_index)(struct cpufreq_policy *policy,
                    unsigned int index);
    unsigned int    (*fast_switch)(struct cpufreq_policy *policy,
                       unsigned int target_freq);

    /*
     * Caches and returns the lowest driver-supported frequency greater than
     * or equal to the target frequency, subject to any driver limitations.
     * Does not set the frequency. Only to be implemented for drivers with
     * target().
     */
    unsigned int    (*resolve_freq)(struct cpufreq_policy *policy,
                    unsigned int target_freq);

    /*
     * Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION
     * unset.
     *
     * get_intermediate should return a stable intermediate frequency
     * platform wants to switch to and target_intermediate() should set CPU
     * to to that frequency, before jumping to the frequency corresponding
     * to 'index'. Core will take care of sending notifications and driver
     * doesn't have to handle them in target_intermediate() or
     * target_index().
     *
     * Drivers can return '0' from get_intermediate() in case they don't
     * wish to switch to intermediate frequency for some target frequency.
     * In that case core will directly call ->target_index().
     */
    unsigned int    (*get_intermediate)(struct cpufreq_policy *policy,
                        unsigned int index);
    int        (*target_intermediate)(struct cpufreq_policy *policy,
                           unsigned int index);

    /* should be defined, if possible */
    unsigned int    (*get)(unsigned int cpu);

    /* optional */
    int        (*bios_limit)(int cpu, unsigned int *limit);

    int        (*exit)(struct cpufreq_policy *policy);
    void        (*stop_cpu)(struct cpufreq_policy *policy);
    int        (*suspend)(struct cpufreq_policy *policy);
    int        (*resume)(struct cpufreq_policy *policy);

    /* Will be called after the driver is fully initialized */
    void        (*ready)(struct cpufreq_policy *policy);

    struct freq_attr **attr;

    /* platform specific boost support code */
    bool        boost_enabled;
    int        (*set_boost)(int state);
};

从上可以看出对struct cpufreq_driver需要实现一些回调,如下将进行简单的介绍

 int        (*init)(struct cpufreq_policy *policy);此函数将实现对struct cpufreq_policy的部分初始化,在cpufreq_online(unsigned int cpu)函数中被调用。

 int        (*verify)(struct cpufreq_policy *policy);此函数是用于验证policy的函数,在内核中被cpufreq_set_policy函数调用。

    int        (*target_index)(struct cpufreq_policy *policy, unsigned int index);此函数根据index设置频率

unsigned int    (*get)(unsigned int cpu); 此函数用于获取当前cpu的频率。

 struct freq_attr **attr; 用于描述Linux内核中的CPU频率调节器的通用属性。可以使用sysfs接口来读取和修改。

    int        (*suspend)(struct cpufreq_policy *policy);用于休眠
    int        (*resume)(struct cpufreq_policy *policy);用于唤醒

struct cpufreq_governor 是描述 CPU 频率调节器的数据结构,它包含了调节器名称、回调函数等信息。在注册之前,需要填充这个结构体。然后,通过调用 cpufreq_register_governor() 函数,将数据结构传递给函数,以注册调节器。

struct cpufreq_governor {
    char    name[CPUFREQ_NAME_LEN];
    int    (*init)(struct cpufreq_policy *policy);
    void    (*exit)(struct cpufreq_policy *policy);
    int    (*start)(struct cpufreq_policy *policy);
    void    (*stop)(struct cpufreq_policy *policy);
    void    (*limits)(struct cpufreq_policy *policy);
    ssize_t    (*show_setspeed)    (struct cpufreq_policy *policy,
                     char *buf);
    int    (*store_setspeed)    (struct cpufreq_policy *policy,
                     unsigned int freq);
    /* For governors which change frequency dynamically by themselves */
    bool            dynamic_switching;
    struct list_head    governor_list;
    struct module        *owner;
};

linux kernel中默认已经实现了很多governor ,如performance,schedutil等等

struct cpufreq_policy是Linux内核中描述CPU频率调节器的策略的结构体。它包含了当前CPU频率调节器的状态和属性,如最小频率、最大频率、当前频率、可设置的频率等等。

它的定义如下:

struct cpufreq_policy {
    /* CPUs sharing clock, require sw coordination */
    cpumask_var_t        cpus;    /* Online CPUs only */
    cpumask_var_t        related_cpus; /* Online + Offline CPUs */
    cpumask_var_t        real_cpus; /* Related and present */

    unsigned int        shared_type; /* ACPI: ANY or ALL affected CPUs
                        should set cpufreq */
    unsigned int        cpu;    /* cpu managing this policy, must be online */

    struct clk        *clk;
    struct cpufreq_cpuinfo    cpuinfo;/* see above */

    unsigned int        min;    /* in kHz */
    unsigned int        max;    /* in kHz */
    unsigned int        cur;    /* in kHz, only needed if cpufreq
                     * governors are used */
    unsigned int        fix;   /*in kHz, extended for test and other usage*/
    unsigned int        restore_freq; /* = policy->cur before transition */
    unsigned int        suspend_freq; /* freq to set during suspend */
    unsigned int        target_freq; /* freq that the policy wants to set */

    unsigned int        policy; /* see above */
    unsigned int        last_policy; /* policy before unplug */
    struct cpufreq_governor    *governor; /* see below */
    void            *governor_data;
    char            last_governor[CPUFREQ_NAME_LEN]; /* last governor used */

    struct work_struct    update; /* if update_policy() needs to be
                     * called, but you're in IRQ context */

    struct cpufreq_user_policy user_policy;
    struct cpufreq_frequency_table    *freq_table;
    enum cpufreq_table_sorting freq_table_sorted;

    struct list_head        policy_list;
    struct kobject        kobj;
    struct completion    kobj_unregister;
    struct notifier_block pm_qos_freq_nb;

    /*
     * The rules for this semaphore:
     * - Any routine that wants to read from the policy structure will
     *   do a down_read on this semaphore.
     * - Any routine that will write to the policy structure and/or may take away
     *   the policy altogether (eg. CPU hotplug), will hold this lock in write
     *   mode before doing so.
     */
    struct rw_semaphore    rwsem;

    /*
     * Fast switch flags:
     * - fast_switch_possible should be set by the driver if it can
     *   guarantee that frequency can be changed on any CPU sharing the
     *   policy and that the change will affect all of the policy CPUs then.
     * - fast_switch_enabled is to be set by governors that support fast
     *   frequency switching with the help of cpufreq_enable_fast_switch().
     */
    bool            fast_switch_possible;
    bool            fast_switch_enabled;

    /*
     * Preferred average time interval between consecutive invocations of
     * the driver to set the frequency for this policy.  To be set by the
     * scaling driver (0, which is the default, means no preference).
     */
    unsigned int        transition_delay_us;

    /*
     * Remote DVFS flag (Not added to the driver structure as we don't want
     * to access another structure from scheduler hotpath).
     *
     * Should be set if CPUs can do DVFS on behalf of other CPUs from
     * different cpufreq policies.
     */
    bool            dvfs_possible_from_any_cpu;

     /* Cached frequency lookup from cpufreq_driver_resolve_freq. */
    unsigned int cached_target_freq;
    int cached_resolved_idx;

    /* Synchronization for frequency transitions */
    bool            transition_ongoing; /* Tracks transition status */
    spinlock_t        transition_lock;
    wait_queue_head_t    transition_wait;
    struct task_struct    *transition_task; /* Task which is doing the transition */

    /* cpufreq-stats */
    struct cpufreq_stats    *stats;

    /* For cpufreq driver's internal use */
    void            *driver_data;
};

它在cpufreq_online(unsigned int cpu)函数中分配和初始化的。

可以在如下节点下查看policy的信息。

cpu注册时的设备树信息,可以从红色部分看到cpu的频率信息。

cpus {
        #address-cells = <2>;
        #size-cells = <0>;

        cpu-map {
            cluster0 {
                core0 {
                    cpu = <&CPU0>;
                };
                core1 {
                    cpu = <&CPU1>;
                };
                core2 {
                    cpu = <&CPU2>;
                };
                core3 {
                    cpu = <&CPU3>;
                };
            };
            cluster1 {
                core0 {
                    cpu = <&CPU4>;
                };
                core1 {
                    cpu = <&CPU5>;
                };
                core2 {
                    cpu = <&CPU6>;
                };
                core3 {
                    cpu = <&CPU7>;
                };
            };
        };

        CPU0: cpu@0 {
            device_type = "cpu";
            compatible = "arm,cortex-a55","arm,armv8";
            reg = <0x0 0x0>;
            enable-method = "psci";
            cpu-idle-states = <&LIT_CORE_PD>;
            cpufreq-data-v1 = <&cpufreq_clus0>;
            sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
            capacity-dmips-mhz = <544>;
            sugov_slack_timer;
        };
        CPU1: cpu@100 {
            device_type = "cpu";
            compatible = "arm,cortex-a55","arm,armv8";
            reg = <0x0 0x100>;
            enable-method = "psci";
            cpu-idle-states = <&LIT_CORE_PD>;
            cpufreq-data-v1 = <&cpufreq_clus0>;
            sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
            capacity-dmips-mhz = <544>;
        };
        CPU2: cpu@200 {
            device_type = "cpu";
            compatible = "arm,cortex-a55","arm,armv8";
            reg = <0x0 0x200>;
            enable-method = "psci";
            cpu-idle-states = <&LIT_CORE_PD>;
            cpufreq-data-v1 = <&cpufreq_clus0>;
            sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
            capacity-dmips-mhz = <544>;
        };
        CPU3: cpu@300 {
            device_type = "cpu";
            compatible = "arm,cortex-a55","arm,armv8";
            reg = <0x0 0x300>;
            enable-method = "psci";
            cpu-idle-states = <&LIT_CORE_PD>;
            cpufreq-data-v1 = <&cpufreq_clus0>;
            sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
            capacity-dmips-mhz = <544>;
        };
        CPU4: cpu@400 {
            device_type = "cpu";
            compatible = "arm,cortex-a75","arm,armv8";
            reg = <0x0 0x400>;
            enable-method = "psci";
            cpu-idle-states = <&BIG_CORE_PD>;
            cpufreq-data-v1 = <&cpufreq_clus1>;
            sched-energy-costs = <&CPU_COST_1 &CLUSTER_COST_1>;
            capacity-dmips-mhz = <1024>;
        };
        CPU5: cpu@500 {
            device_type = "cpu";
            compatible = "arm,cortex-a75","arm,armv8";
            reg = <0x0 0x500>;
            enable-method = "psci";
            cpu-idle-states = <&BIG_CORE_PD>;
            cpufreq-data-v1 = <&cpufreq_clus1>;
            sched-energy-costs = <&CPU_COST_1 &CLUSTER_COST_1>;
            capacity-dmips-mhz = <1024>;
        };
        CPU6: cpu@600 {
            device_type = "cpu";
            compatible = "arm,cortex-a75","arm,armv8";
            reg = <0x0 0x600>;
            enable-method = "psci";
            cpu-idle-states = <&BIG_CORE_PD>;
            cpufreq-data-v1 = <&cpufreq_clus1>;
            sched-energy-costs = <&CPU_COST_1 &CLUSTER_COST_1>;
            capacity-dmips-mhz = <1024>;
        };
        CPU7: cpu@700 {
            device_type = "cpu";
            compatible = "arm,cortex-a75","arm,armv8";
            reg = <0x0 0x700>;
            enable-method = "psci";
            cpu-idle-states = <&BIG_CORE_PD>;
            cpufreq-data-v1 = <&cpufreq_clus1>;
            sched-energy-costs = <&CPU_COST_1 &CLUSTER_COST_1>;
            capacity-dmips-mhz = <1024>;
        };
    };

cpufreq_clus0: cpufreq-clus0 {
        cpufreq-cluster-id = <0>;
        cpufreq-sub-clusters =  <&cpufreq_scu>,
                    <&cpufreq_periph>,
                    <&cpufreq_gic>;
        clock-latency = <50000>;
        voltage-latency = <6413>;
        cpufreq-boost-disable;
        nvmem-cells = <&lit_bin>;
        nvmem-cell-names = "dvfs_bin";

        operating-points = <
            /*kHZ    uV*/
            1820000 1000000
            1716000 996875
            1534000 937500
            1482000 921875
            1228800 843750
            1144000 815625
            962000  759375
            768000  750000
            614400  750000
        >;

        operating-points-1 = <
            /*kHZ    uV*/
            1820000 928125
            1716000 896875
            1534000 843750
            1482000 828125
            1228800 753125
            1144000 750000
            962000  750000
            768000  750000
            614400  750000
        >;

        operating-points-2 = <
            /*kHZ    uV*/
            1820000 959375
            1716000 928125
            1534000 871875
            1482000 856250
            1228800 778125
            1144000 753125
            962000  750000
            768000  750000
            614400  750000
        >;

        operating-points-3 = <
            /*kHZ    uV*/
            1820000 981250
            1716000 950000
            1534000 893750
            1482000 878125
            1228800 800000
            1144000 775000
            962000  750000
            768000  750000
            614400  750000
        >;

        operating-points-4 = <
            /*kHZ    uV*/
            1820000 1000000
            1716000 996875
            1534000 937500
            1482000 921875
            1228800 843750
            1144000 815625
            962000  759375
            768000  750000
            614400  750000
        >;

    };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值