Linux cpufreq framework(2)_cpufreq driver作者:wowo 发布于:2015-6-19 22:27 分类:电源管理子系统 1. 前言本文从平台驱动工程师的角度,介绍怎么编写cpufreq驱动。 注1:本文基于linux-3.18-rc4内核,其它版本内核可能会稍有不同。 2. cpufreq driver的编写步骤cpufreq driver主要完成平台相关的CPU频率/电压的控制,它在cpufreq framework中是非常简单的一个模块,编写步骤包括:
具体请参考后面的分析。 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: };
假设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);
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: };
除了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);
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);
注3:在这里先提一下cpufreq framework中“频率”的几个层次。
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);
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)
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)。 原创文章,转发请注明出处。蜗窝科技,www.。 评论: |
|