Chinaunix首页 | 论坛 | 博客
  • 博客访问: 812923
  • 博文数量: 296
  • 博客积分: 5376
  • 博客等级: 大校
  • 技术积分: 2298
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-14 19:02
文章分类

全部博文(296)

文章存档

2023年(2)

2020年(2)

2018年(2)

2017年(26)

2016年(4)

2015年(19)

2014年(12)

2013年(26)

2012年(84)

2011年(50)

2010年(41)

2009年(28)

分类: LINUX

2013-01-17 09:21:09

1.  基本介绍

   1) 调整CPU运行频率是一个节能的好方法,CPU运行频率越低,CPU功耗越小。

   2) 下面的我现在正在使用的CPU为例进行说明。触发CPU频率调整的有两个源:

      1)根据CPU负荷进行调整(代码位于:kernel/drivers/cpufreq,下面以cpufreq_interactive.c为例,当/sys/drivers/system/cpu/cpu0/cpufreq/scaling_governor中的值为interactive时生效)

      2)根据CPU的温度时行调整(需要CPU内有温度传感器,且在cpufreq_driver中实现,它由各个芯片厂家实现)

2. 根据CPU负荷进行调整的调用流程

 cpufreq_interactive_up_task(或cpufreq_interactive_freq_down
                                              或cpufreq_governor_interactive :cpufreq_interactive.c)->
      __cpufreq_driver_target->
             cpufreq_driver->target(mycpu_cpufreq_driver.target=mycpu_target)->
                   mycpu_target->
                        clk_set_rate->
                             dvfs_set_rate->
                                  dvfs_target_cpu (或dvfs_target_core)->
                                       dvfs_clk_get_ref_volt(获得与频率对应的参考电压)
                                       dvfs_scale_volt_direct(设置电压)
                                       clk_set_rate_locked->clk_set_rate_nolock->clk->set_rate(设置频率)

3. cpufreq_interactive工作流程

1) 驱动入口:cpufreq_interactive_init

     a) 注册CPU timer函数:cpufreq_interactive_timer

     b) 创建线程:kinteractiveup,入口函数:cpufreq_interactive_up_task

     c) 创建workqueue “knteractive_down”,其工作处理函数为:cpufreq_interactive_freq_down

     d) 注册idle notify函数cpufreq_interactive_idle_nb

     e) 注册cpufreq_governor cpufreq_gov_interactive,其中的governor函数为:cpufreq_governor_interactive

2) cpufreq_interactive_timer

    这个函数通过timer触发,它执行cpu负荷计算。timer的默认间隔为20ms。根据CPU负荷计算CPU的频率的代码如下:

         /*
	 * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time,
	 * this lets idle exit know the current idle time sample has
	 * been processed, and idle exit can generate a new sample and
	 * re-arm the timer.  This prevents a concurrent idle
	 * exit on that CPU from writing a new set of info at the same time
	 * the timer function runs (the timer function can't use that info
	 * until more time passes).
	 */
	time_in_idle = pcpu->time_in_idle;
	idle_exit_time = pcpu->idle_exit_time;
	now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time);
	smp_wmb();

	/* If we raced with cancelling a timer, skip. */
	if (!idle_exit_time)
		goto exit;

	delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle);
	delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time,
						  idle_exit_time);

	/*
	 * If timer ran less than 1ms after short-term sample started, retry.
	 */
	if (delta_time < 1000)
		goto rearm;

	if (delta_idle > delta_time)
		cpu_load = 0;
	else
		cpu_load = 100 * (delta_time - delta_idle) / delta_time;

	delta_idle = (unsigned int) cputime64_sub(now_idle,
						pcpu->freq_change_time_in_idle);
	delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time,
						  pcpu->freq_change_time);

	if ((delta_time == 0) || (delta_idle > delta_time))
		load_since_change = 0;
	else
		load_since_change =
			100 * (delta_time - delta_idle) / delta_time;

	/*
	 * Choose greater of short-term load (since last idle timer
	 * started or timer function re-armed itself) or long-term load
	 * (since last frequency change).
	 */
	if (load_since_change > cpu_load)
		cpu_load = load_since_change;

	if (cpu_load >= go_hispeed_load) {
		if (pcpu->policy->cur == pcpu->policy->min)
			new_freq = hispeed_freq;
		else
			new_freq = pcpu->policy->max * cpu_load / 100;
	} else {
		new_freq = pcpu->policy->cur * cpu_load / 100;
	}

	if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
					   new_freq, CPUFREQ_RELATION_H,
					   &index)) {
		pr_warn_once("timer %d: cpufreq_frequency_table_target error\n",
			     (int) data);
		goto rearm;
	}

	new_freq = pcpu->freq_table[index].frequency;

a) 如果新的频率大于当前工作频率,则唤醒进程kinteractiveup进行处理,此进程调用__cpufreq_driver_target设置新的工作频率。

        b) 如果新的频率小于当前工作频率,则把一个工作放于workqueue “knteractive_down”,它调用__cpufreq_driver_target设置新的工作频率。

4. 根据温度调节CPU频率

    把温度检测和频率调整工作放于一个work中, 然后加入一个workqueue,delay 2ms再执行。其调用流程如下:

温度检测及频率计算函数->

   cpufreq_driver_target->

       __cpufreq_driver_target->

           ...与第2.中的一样。



http://blog.csdn.net/myarrow/article/details/8101212

阅读(2149) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~