1. 前言
本文从平台驱动工程师的角度,介绍怎么编写cpufreq驱动。
注1:本文基于linux-3.18-rc4内核,其它版本内核可能会稍有不同。
2. cpufreq driver的编写步骤
cpufreq driver主要完成平台相关的CPU频率/电压的控制,它在cpufreq framework中是非常简单的一个模块,编写步骤包括:
1)平台相关的初始化动作,包括CPU core的clock/regulator获取、初始化等。
2)生成frequency table,即CPU core所支持的频率/电压列表。并在初始化时将该table保存在policy中。
3)定义一个struct cpufreq_driver变量,填充必要的字段,并根据平台的特性,实现其中的回调函数。
4)调用cpufreq_register_driver将driver注册到cpufreq framework中。
5)cpufreq core会在CPU设备添加时,调用driver的init接口。driver需要在该接口中初始化struct cpufreq_policy变量。
6)系统运行过程中,cpufreq core会根据实际情况,调用driver的setpolicy或者target/target_index等接口,设置CPU的调频策略或者频率值。
7)系统suspend的时中,会将CPU的频率设置为指定的值,或者调用driver的suspend回调函数;系统resume时,调用driver的resume回调函数。
具体请参考后面的分析。
3. cpufreq driver有关的API即功能分析
3.1 frequency table
frequency table是CPU core可以正确运行的一组频率/电压组合,一般情况下,会在项目启动的初期,通过“try频点”的方法,确定出稳定性、通用性都符合要求的频点。
frequency table之所以存在的一个思考点是:table是频率和电压之间的一个一一对应的组合,因此cpufreq framework只需要关心频率,所有的策略都称做“调频”策略。而cpufreq driver可以在“调频”的同时,通过table取出和频率对应的电压,进行修改CPU core电压,实现“调压”的功能。这简化了设计。
cpufreq framework以cpufreq_frequency_table抽象frequency table,如下:
1: /* Special Values of .frequency field */
2: #define CPUFREQ_ENTRY_INVALID ~0u
3: #define CPUFREQ_TABLE_END ~1u
4: /* Special Values of .flags field */
5: #define CPUFREQ_BOOST_FREQ (1 << 0)
6:
7: struct cpufreq_frequency_table {
8: unsigned int flags;
9: unsigned int driver_data; /* driver specific data, not used by core */
10: unsigned int frequency; /* kHz - doesn't need to be in ascending
11: * order */
12: };
frequency,频率值,单位为kHz,不需要特别的排序。这里定义了两个特殊的频率值:CPUFREQ_ENTRY_INVALID,用来表示table中的一个无效频率值;CPUFREQ_TABLE_END,用于表示table的结束。
driver_data,由名字就可以知道,这个字段由driver使用,具体意义由driver定义,可以是电压,也可以是其它(如OPP index,后续文章会详细描述)。
flags,现在只有一个----CPUFREQ_BOOST_FREQ,表示这个频率值是一个boost频率。
注2:Boost表示智能超频技术,是一个在x86平台上的功能,具体可参考“turbo-boost-technology.html”,本文不做过多描述。
假设CPU device的OPP 列表已经由cpu subsystem driver调用of_init_opp_table解析出来了,cpufreq driver可以借助dev_pm_opp_init_cpufreq_table将OPP列表转换为frequency table。
of_init_opp_table接口请参考“Linux电源管理(15)_PM OPP Interface”,dev_pm_opp_init_cpufreq_table接口的声明如下:
1: /* include/linux/cpufreq.h */
2: int dev_pm_opp_init_cpufreq_table(struct device *dev,
3: struct cpufreq_frequency_table **table);
该接口的逻辑很简单,根据传入的设备指针,遍历OPP列表,转换为frequency table,并通过table返回给调用者。具体请参考后面的文章。
3.2 struct cpufreq_driver
struct cpufreq_driver是cpufreq driver的核心数据结构,我们在“linux cpufreq framework(1)_概述”中有简单介绍过,这里再详细分析一下:
1: struct cpufreq_driver {
2: char name[CPUFREQ_NAME_LEN];
3: u8 flags;
4: void *driver_data;
5:
6: /* needed by all drivers */
7: int (*init) (struct cpufreq_policy *policy);
8: int (*verify) (struct cpufreq_policy *policy);
9:
10: /* define one out of two */
11: int (*setpolicy) (struct cpufreq_policy *policy);
12:
13: /*
14: * On failure, should always restore frequency to policy->restore_freq
15: * (i.e. old freq).
16: */
17: int (*target) (struct cpufreq_policy *policy, /* Deprecated */
18: unsigned int target_freq,
19: unsigned int relation);
20: int (*target_index) (struct cpufreq_policy *policy,
21: unsigned int index);
22: /*
23: * Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION
24: * unset.
25: *
26: * get_intermediate should return a stable intermediate frequency
27: * platform wants to switch to and target_intermediate() should set CPU
28: * to to that frequency, before jumping to the frequency corresponding
29: * to 'index'. Core will take care of sending notifications and driver
30: * doesn't have to handle them in target_intermediate() or
31: * target_index().
32: *
33: * Drivers can return '0' from get_intermediate() in case they don't
34: * wish to switch to intermediate frequency for some target frequency.
35: * In that case core will directly call ->target_index().
36: */
37: unsigned int (*get_intermediate)(struct cpufreq_policy *policy,
38: unsigned int index);
39: int (*target_intermediate)(struct cpufreq_policy *policy,
40: unsigned int index);
41:
42: /* should be defined, if possible */
43: unsigned int (*get) (unsigned int cpu);
44:
45: /* optional */
46: int (*bios_limit) (int cpu, unsigned int *limit);
47:
48: int (*exit) (struct cpufreq_policy *policy);
49: void (*stop_cpu) (struct cpufreq_policy *policy);
50: int (*suspend) (struct cpufreq_policy *policy);
51: int (*resume) (struct cpufreq_policy *policy);
52: struct freq_attr **attr;
53:
54: /* platform specific boost support code */
55: bool boost_supported;
56: bool boost_enabled;
57: int (*set_boost) (int state);
58: };
1)init回调函数
init回调函数是cpufreq driver的入口,由cpufreq core在CPU device添加之后调用,其主要功能就是初始化policy变量(把它想象成cpufreq device)。
对driver而言,不需要太关心struct cpufreq_policy的内部实现(其实cpufreq framework也在努力实现这个目标,包括将相应的初始化过程封装成一个API等),为了分析方便,我们再把这个结构贴一次:
1: struct cpufreq_cpuinfo {
2: unsigned int max_freq;
3: unsigned int min_freq;
4:
5: /* in 10^(-9) s = nanoseconds */
6: unsigned int transition_latency;
7: };
8:
9: struct cpufreq_real_policy {
10: unsigned int min; /* in kHz */
11: unsigned int max; /* in kHz */
12: unsigned int policy; /* see above */
13: struct cpufreq_governor *governor; /* see below */
14: };
15:
16: struct cpufreq_policy {
17: /* CPUs sharing clock, require sw coordination */
18: cpumask_var_t cpus; /* Online CPUs only */
19: cpumask_var_t related_cpus; /* Online + Offline CPUs */
20:
21: unsigned int shared_type; /* ACPI: ANY or ALL affected CPUs
22: should set cpufreq */
23: unsigned int cpu; /* cpu nr of CPU managing this policy */
24: unsigned int last_cpu; /* cpu nr of previous CPU that managed
25: * this policy */
26: struct clk *clk;
27: struct cpufreq_cpuinfo cpuinfo;/* see above */
28:
29: unsigned int min; /* in kHz */
30: unsigned int max; /* in kHz */
31: unsigned int cur; /* in kHz, only needed if cpufreq
32: * governors are used */
33: unsigned int restore_freq; /* = policy->cur before transition */
34: unsigned int suspend_freq; /* freq to set during suspend */
35:
36: unsigned int policy; /* see above */
37: struct cpufreq_governor *governor; /* see below */
38: void *governor_data;
39: bool governor_enabled; /* governor start/stop flag */
40:
41: struct work_struct update; /* if update_policy() needs to be
42: * called, but you're in IRQ context */
43:
44: struct cpufreq_real_policy user_policy;
45: struct cpufreq_frequency_table *freq_table;
46:
47: struct list_head policy_list;
48: struct kobject kobj;
49: struct completion kobj_unregister;
50:
51: /*
52: * The rules for this semaphore:
53: * - Any routine that wants to read from the policy structure will
54: * do a down_read on this semaphore.
55: * - Any routine that will write to the policy structure and/or may take away
56: * the policy altogether (eg. CPU hotplug), will hold this lock in write
57: * mode before doing so.
58: *
59: * Additional rules:
60: * - Lock should not be held across
61: * __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
62: */
63: struct rw_semaphore rwsem;
64:
65: /* Synchronization for frequency transitions */
66: bool transition_ongoing; /* Tracks transition status */
67: spinlock_t transition_lock;
68: wait_queue_head_t transition_wait;
69: struct task_struct *transition_task; /* Task which is doing the transition */
70:
71: /* For cpufreq driver's internal use */
72: void *driver_data;
73: };
对driver而言,需要在init中初始化policy的如下内容:
cpus,告诉cpufreq core,该policy适用于哪些cpu。大多数情况下,系统中所有的cpu core都由相同的硬件逻辑,统一控制cpu frequency,因此只需要一个policy,就可以管理所有的cpu core。“linux cpufreq framework(3)_cpufreq core”中会重点介绍。
clk,clock指针,cpufreq core可以利用该指针,获取当前实际的frequency值。
cpuinfo,该cpu调频相关的固定信息,包括最大频率、最小频率、切换延迟,其中最大频率、最小频率可以通过frequency table推导得出。
min、max,调频策略所对应的最小频率、最大频率,初始化时,可以和上面的cpuinfo中的min、max相同。
freq_table,所对应的frequency table。
除了clk指针外,cpuinfo、min、max、freq_table等都可以通过cpufreq_generic_init接口初始化:
1: int cpufreq_generic_init(struct cpufreq_policy *policy,
2: struct cpufreq_frequency_table *table,
3: unsigned int transition_latency);
该接口以需要初始化的policy、frequency table以及切换延迟为参数,从table中解析policy初始化所需的信息,初始化该policy。
一般情况下,该接口是enough的,因此在init中调用它即可。
2)verify回调函数
当上层软件需要设定一个新的policy的时候,会调用driver的verify回调函数,检查该policy是否合法。cpufreq core封装了下面两个接口,辅助完成这个功能:
1: int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
2: struct cpufreq_frequency_table *table);
3: int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy);
cpufreq_frequency_table_verify根据指定的frequency table,检查policy是否合法,检查逻辑很简单:policy的频率范围{min,max},是否超出policy->cpuinfo的频率范围,是否超出frequency table中的频率范围。
cpufreq_generic_frequency_table_verify更简单,它以policy中保存的frequency table为参数(policy->freq_table),调用cpufreq_frequency_table_verify接口。
注3:在这里先提一下cpufreq framework中“频率”的几个层次。
最底层,是frequency table中定义的频率,有限的离散频率,代表了cpu的调频能力。
往上,是policy->cpuinfo中的频率范围,它对cpu调频进行的简单的限制,该限制可以和frequency table一致,也可以小于table中的范围。必须在driver初始化时给定,之后不能再修改。
再往上,是policy的频率范围,代表调频策略。对于可以自动调频的CPU,只需要把这个范围告知CPU即可,此时它是调频的基本单位。对于不可以自动调频的CPU,它是软件层面的一个限制。该范围也可以通过sysfs修改。
最上面,是policy中的频率值,对那些不可以调频的CPU,该值就是CPU的运行频率。
3)setpolicy回调函数
对于可以自动调频的CPU,driver需要提供该接口,通过该接口,将调频范围告知CPU。
4)target回调函数,不建议使用了,就不讲了。
5)target_index回调函数
对于不可以自动调频的CPU,该接口用于指定CPU的运行频率。index表示frequency table中的index。
driver需要通过index,将频率值取出,通过clock framework提供的API,将CPU的频率设置为对应的值。
同时,driver可以调用OPP interface,获取该频率对应的电压值,通过regulator framework提供的API,将CPU的电压设置为对应的值。
6)get_intermediate、target_intermediate,在没有提供target接口的时候使用,希望看这篇文章对的工程师不要使用。不详细介绍了。
7)get回调函数
用于获取指定cpu的频率值,如果可以的话,driver应尽可能提供。如果在init接口中给policy->clk赋值的话,则可以使用cpufreq framework提供的通用接口:
1: unsigned int cpufreq_generic_get(unsigned int cpu);
该接口会直接调用clock framework API,从policy->clk中获取频率值。
8)exit,和init对应,在CPU device被remove时调用。
9)stop_cpu,在CPU被stop时调用。
10)suspend、resume回调函数
系统给suspend的时候,clock、regulator等driver有可能被suspend,因此需要在这之前将CPU设置为一个确定的频率值。driver可以通过suspend回调设置,也可以通过policy中的suspend_freq字段设置(cpufreq core会自动切换)。
同理,系统resume后,CPU的运行频率是什么,可以通过resume回调设置,也可以通过policy中的restore_freq字段设置。
11)freq_attr
如果cpufreq driver需要提供一些额外的sysfs attribute,可以通过如下的attribute宏设置,然后保存在cpufreq_driver的attr数组中:
1: struct freq_attr {
2: struct attribute attr;
3: ssize_t (*show)(struct cpufreq_policy *, char *);
4: ssize_t (*store)(struct cpufreq_policy *, const char *, size_t count);
5: };
6:
7: #define cpufreq_freq_attr_ro(_name) \
8: static struct freq_attr _name = \
9: __ATTR(_name, 0444, show_##_name, NULL)
10:
11: #define cpufreq_freq_attr_ro_perm(_name, _perm) \
12: static struct freq_attr _name = \
13: __ATTR(_name, _perm, show_##_name, NULL)
14:
15: #define cpufreq_freq_attr_rw(_name) \
16: static struct freq_attr _name = \
17: __ATTR(_name, 0644, show_##_name, store_##_name)
3.3 cpufreq_driver flags
注册cpufreq driver时,可以通过flag字段指定一些特性,包括:
1: /* flags */
2: #define CPUFREQ_STICKY (1 << 0) /* driver isn't removed even if
3: all ->init() calls failed */
4: #define CPUFREQ_CONST_LOOPS (1 << 1) /* loops_per_jiffy or other
5: kernel "constants" aren't
6: affected by frequency
7: transitions */
8: #define CPUFREQ_PM_NO_WARN (1 << 2) /* don't warn on suspend/resume
9: speed mismatches */
10:
11: /*
12: * This should be set by platforms having multiple clock-domains, i.e.
13: * supporting multiple policies. With this sysfs directories of governor would
14: * be created in cpu/cpu/cpufreq/ directory and so they can use the same
15: * governor with different tunables for different clusters.
16: */
17: #define CPUFREQ_HAVE_GOVERNOR_PER_POLICY (1 << 3)
18:
19: /*
20: * Driver will do POSTCHANGE notifications from outside of their ->target()
21: * routine and so must set cpufreq_driver->flags with this flag, so that core
22: * can handle them specially.
23: */
24: #define CPUFREQ_ASYNC_NOTIFICATION (1 << 4)
25:
26: /*
27: * Set by drivers which want cpufreq core to check if CPU is running at a
28: * frequency present in freq-table exposed by the driver. For these drivers if
29: * CPU is found running at an out of table freq, we will try to set it to a freq
30: * from the table. And if that fails, we will stop further boot process by
31: * issuing a BUG_ON().
32: */
33: #define CPUFREQ_NEED_INITIAL_FREQ_CHECK (1 << 5)
CPUFREQ_STICKY,表示就算所有的init调用都失败了,driver也不被remove。具体应用场景不明。
CPUFREQ_CONST_LOOPS,表示频率的调整,不影响loops_per_jiffy等kernel常来的计算。
CPUFREQ_PM_NO_WARN,suspend/resume过程相关的flag,后面文章如果分析相应的功能的话,再详细描述。
CPUFREQ_HAVE_GOVERNOR_PER_POLICY,表示不同的CPU,有不同的频率控制方式,因此cpufreq core会为每一个CPU创建一个cpufreq调频接口。否则(也是正常情况下),一个调频接口可以调整所有CPU的频率。
其它flag用到时再分析。
3.4 cpufreq_register_driver
driver的注册接口比较简单,进行一些必要的检查后,将driver保存在一个全局指针(cpufreq_driver)中,如果该指针不为空,则说明已经有driver注册过了,返回错误(-EEXIST)。
最后,会调用subsys_interface_register接口,注册一个subsystem interface(struct subsys_interface cpufreq_interface),有关subsystem interface的介绍,可参考“Linux设备模型(6)_Bus”中的描述。cpufreq与此有关的逻辑,会在下一篇文章详细说明(linux cpufreq framework(3)_cpufreq core)。