Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1057595
  • 博文数量: 166
  • 博客积分: 10217
  • 博客等级: 上将
  • 技术积分: 2133
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-09 19:45
文章分类

全部博文(166)

文章存档

2012年(3)

2011年(7)

2010年(18)

2009年(59)

2008年(79)

我的朋友

分类: LINUX

2009-10-21 18:05:10

一. DPM 概念 Dynamic Power Management

二. Basic Concept of DPM



操作点:
/* internal representation of an operating point */
struct dpm_opt {
char *name; /* name */
struct list_head list; /* all installed op points */
dpm_md_pp_t pp[DPM_PP_NBR]; /* initialization params */
struct dpm_md_opt md_opt; /* machine dependent part */
int constrained; /* is this opt constrained? */
struct dpm_stats stats; /* statistics */
};

/* Instances of this structure define valid Innovator operating points for DPM.
Voltages are represented in mV, and frequencies are represented in KHz. */

struct dpm_md_opt {
unsigned int v; /* Target voltage in mV */
unsigned int dpll; /* in KHz */
unsigned int cpu; /* CPU frequency in KHz */
unsigned int tc; /* in KHz */
unsigned int per; /* in KHz */
unsigned int dsp; /* in KHz */
unsigned int dspmmu; /* in KHz */
unsigned int lcd; /* in KHz */
struct dpm_regs regs; /* Register values */
};

dpm操做点结构,里面包含了大约16个参数dpm_md_pp_t pp[DPM_PP_NBR];
dpm_md_pp_t是个int型

类和策略:
* internal representation of a class of op points (to be mapped to an
* operating state */
struct dpm_class {
char *name; /* name */
struct list_head list; /* all installed classes */
unsigned nops; /* nbr ops in this class */
struct dpm_opt **ops; /* the ops in this class */
struct dpm_opt *opt; /* the selected op point */
struct dpm_stats stats; /* statistics */
};


/* internal representation of an installed power policy */
struct dpm_policy {
char *name; /* name */
struct list_head list; /* all installed policies */
struct dpm_class *classes[DPM_STATES]; /* the classes */
struct dpm_stats stats; /* statistics */
};


可以看到操作点,类和策略在系统中以链的形式维护
/* curently installed policies, classes and operating points */
extern struct list_head dpm_policies;
extern struct list_head dpm_classes;
extern struct list_head dpm_opts;

三. Interfaces of DPM

3.1 User Space Interface
这个是dpm给用户态应用程序(进程级)提供的接口,我们看到它是一个系统调用。意思就是说,进程可以通过这个系统调用来完成与dpm的控制或管理操作。


/* single system call to invoke all functions. the "params" argument can be
* NULL for DPM_INIT, DPM_TERMINATE, DPM_DISABLE, and DPM_ENABLE */

#if defined(__KERNEL__)
extern int sys_dpm(int func, struct dpm_param *params);
#else
#define sys_dpm(FUNC, PARAMS)
syscall(__NR_sys_dpm, FUNC, PARAMS)
#endif

下面列出了dpm提供给用户程序的操作:
/* function names for sys_dpm system call interface */
typedef enum {
DPM_INIT, /* initialize the DPM */
DPM_TERMINATE, /* terminate the DPM */
DPM_DISABLE, /* temporarily disable the DPM */
DPM_ENABLE, /* re-enable the disabled DPM */
DPM_CREATE_OPT, /* create an operating point */
DPM_CREATE_CLASS, /* create a class of operating points */
DPM_CREATE_POLICY, /* create a policy */
DPM_DESTROY_POLICY, /* destroy a policy */
DPM_SET_POLICY, /* set the active policy */
DPM_GET_POLICY, /* get the name of the active policy */
DPM_GET_ALL_POLICIES, /* get the names of all active policies */
DPM_GET_CLASSES, /* get the names of all active policy */
DPM_SET_TASK_STATE, /* set a task-specific operating state */
DPM_GET_TASK_STATE, /* get a task's task-specific oper state */
/* get statistics */
DPM_GET_POLICY_STATS,
DPM_GET_CLASS_STATS,
DPM_GET_OPT_STATS,
DPM_GET_OS_STATS,
/* debug */
DPM_DISPLAY_POLICY,
DPM_SET_STATE, /* set a (non-scheduling-based) op state */
} dpm_func_t;

asmlinkage int sys_dpm(int func, struct dpm_param *params);

3.2 Interface for Drivers

然后看看dpm为内核和其他驱动程序提供的接口:

/*
* kernel's operating state interface
*/

/* set operating state */
EXPORT_SYMBOL(dpm_set_os);
void dpm_set_os(dpm_state_t state);

EXPORT_SYMBOL(dpm_init);
EXPORT_SYMBOL(dpm_terminate);
EXPORT_SYMBOL(dpm_disable);
EXPORT_SYMBOL(dpm_enable);
EXPORT_SYMBOL(dpm_create_opt);
EXPORT_SYMBOL(dpm_create_class);
EXPORT_SYMBOL(dpm_create_policy);
EXPORT_SYMBOL(dpm_destroy_policy);
EXPORT_SYMBOL(dpm_set_policy);
/* ?? Needed in kernel mode ?? EXPORT_SYMBOL(dpm_get_policy);*/
EXPORT_SYMBOL(dpm_set_task_state);
EXPORT_SYMBOL(dpm_get_task_state);

/*****************************************************************************
* Suspend/Resume DPM
* The current operating point is saved and restored. This
* interface is designed to be used by system suspend/resume code, to safely
* save/restore the DPM operating point across a system power-down, where the
* firmware may resume the system at a random operating point. This does not
* require DPM to be enabled. Note that DPM remains locked across the
* suspend/resume.
*****************************************************************************/
int dpm_suspend(void)
int dpm_resume(void)

3.3 proc fs interface

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* /proc/driver/dpm/cmd (Write-Only)
*
* Writing a string to this file is equivalent to issuing a DPM command.
* Currently only one command per "write" is allowed, and there is a maximum on
* the number of tokens that will be accepted (PAGE_SIZE / sizeof(char *)).
* DPM can be initialized by a linewise copy of a configuration file to this
* /proc file.
*
* DPM Control
* -----------
*
* init : dpm_init()
* enable : dpm_enable()
* disable : dpm_disable()
* terminate : dpm_terminate()
*
* Policy Control
* --------------
*
* set_policy : Set the policy by name
* set_task_state : Set the task state for a given pid, 0 = self
*
* Policy Creation
* ---------------
*
* create_opt ...
* Create a named operating point from DPM_PP_NBR paramaters. All
* parameters must be given. Parameter order and meaning are machine
* dependent.
*
* create_class [ ... ]
* Create a named class from 1 or more named operating points. All
* operating points must be defined before the call.
*
* create_policy ...
* Create a named policy from DPM_STATES class names. All classes must be
* defined before the call. The order is machine dependent.
*
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/



四. DPM states

那么dpm的状态如何定义的呢?

machine dependent operating state

An operating state is a cpu execution state that (has implications for)(有关,牵涉) power
management. The DPM will select operating points (操作点)based largely on the
current operating state.

*
* DPM_STATES is the number of supported operating states. Valid operating
* states are from 0 to DPM_STATES-1 but when setting an operating state the
* kernel should only specify a state from the set of "base states" and should
* do so by name. During the context switch the new operating state is simply
* extracted from current->dpm_state.
*
* task states:
*
* APIs that reference task states use the range -(DPM_TASK_STATE_LIMIT + 1)
* through +DPM_TASK_STATE_LIMIT. This value is added to DPM_TASK_STATE to
* obtain the downward or upward adjusted task state value. The
* -(DPM_TASK_STATE_LIMIT + 1) value is interpreted specially, and equates to
* DPM_NO_STATE.
*
* Tasks inherit their task operating states across calls to
* fork(). DPM_TASK_STATE is the default operating state for all tasks, and is
* inherited from init. Tasks can change (or have changed) their tasks states
* using the DPM_SET_TASK_STATE variant of the sys_dpm() system call. */




#define DPM_NO_STATE -1 , 这个状态是不受电源管理的状态,有些任务根本不受dpm的托管。

以下三个是dpm的基本状态
/*states below are dpm base states, dpm基本状态*/
#define DPM_RELOCK_STATE 0 //??

#define DPM_IDLE_TASK_STATE 1
#define DPM_IDLE_STATE 2
#define DPM_SLEEP_STATE 3


#define DPM_BASE_STATES 4 , 定义了dpm的基本状态的数量,只有四种。


以下几个定义了dpm task state的状态,这些state和task相关。可以通过sys_dpm(DPM_SET_TASK_STATE)来设定。

#define DPM_TASK_STATE_LIMIT 4 ,定义了dpm的task operating state的最大数量

/*DPM_TASK_STATE is the 缺省状态 for all tasks, and is
* inherited from init. */
#define DPM_TASK_STATE (DPM_BASE_STATES + DPM_TASK_STATE_LIMIT)

/*number of supported operating states*/
#define DPM_STATES (DPM_TASK_STATE + DPM_TASK_STATE_LIMIT + 1) ,定义了整个dpm operating state的数量。

#define DPM_TASK_STATES (DPM_STATES - DPM_BASE_STATES)

#define DPM_STATE_NAMES
{ "relock", "idle-task", "idle", "sleep",
"task-4", "task-3", "task-2", "task-1",
"task",
"task+1", "task+2", "task+3", "task+4"
}

sys_dpm() -> u_dpm_set_task_state() -> dpm_set_task_state() -> dpm_set_os()

在看一个设定task state的操作:

static int u_dpm_set_task_state(pid_t pid, dpm_state_t task_state)
{
return dpm_set_task_state(pid, task_state);
}

/*****************************************************************************
* set a task state
*****************************************************************************/

int
dpm_set_task_state(pid_t pid, dpm_state_t task_state)
{
struct task_struct *p;

/*task_state值的合法性检查*/
if (task_state == -(DPM_TASK_STATE_LIMIT + 1))
task_state = DPM_NO_STATE;
else if (abs(task_state) > DPM_TASK_STATE_LIMIT)
{
dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, -EINVAL);
return -EINVAL;
}
else
{
/*task_state值得调整,DPM_TASK_STATE是一个默认值
设定的值比默认值或大或小,这样相当于加了
个基准值而已-(DPM_TASK_STATE_LIMIT + 1) < task_state <= DPM_TASK_STATE_LIMIT*/
task_state += DPM_TASK_STATE;
}


read_lock(&tasklist_lock);

if (pid == 0)
p = current;
else
p = find_task_by_pid(pid);

if (!p) {
read_unlock(&tasklist_lock);
dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, -ENOENT);
return -ENOENT;
}

p->dpm_state = task_state;
read_unlock(&tasklist_lock);

dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, 0);

/*如果是当前进程,马上切换状态*/
if (pid == 0)
dpm_set_os(p->dpm_state);

return 0;
}

以下的低层调用 dpm_set_os() -> dpm_resync() -> dpm_set_opt_async() -> dpm_md_set_opt() -> dpm_omap24xx_set_opt()
dpm_omap24xx_set_opt() 是一个与体系结构相关的set opt函数,在初始化的时候被注册。下面还会分析。

以下更低层的的调用最终会调用到omap24xx_fscaler(),定义在中,
这个函数做实际的事情,根据设定好的操作点设定cpu和相关硬件的状态,完成了dpm状态的切换。比如:

//= POWER MODES =//
#define PRCM_OFF 0
#define PRCM_ON 1
#define PRCM_RETENTION 2
#define PRCM_SLEEP 3
#define PRCM_STANDBY 4
#define PRCM_DORMANT 5

static void omap24xx_fscaler(struct dpm_regs *regs) -> omap24xx_pm_suspend(PRCM_STANDBY) ...

Just a piece of code:

if(dpm_fscaler_flags & DPM_FSCALER_ANY_SLEEPMODE) {
/* omap24xx_pm_suspend() handles voltage scaling */
DPRINT(" suspending devices ");
device_suspend(0, SUSPEND_POWER_DOWN);

if(dpm_fscaler_flags & DPM_FSCALER_SLEEP_DEVICEON) {
omap24xx_pm_suspend(PRCM_ON);
}
else if(dpm_fscaler_flags & DPM_FSCALER_SLEEP_STANDBY){
omap24xx_pm_suspend(PRCM_STANDBY);
}
else if(dpm_fscaler_flags & DPM_FSCALER_SLEEP_SLEEP){
omap24xx_pm_suspend(PRCM_SLEEP);
}
else if(dpm_fscaler_flags & DPM_FSCALER_SLEEP_RETENTION){
omap24xx_pm_suspend(PRCM_RETENTION);
}

/* Here when we wake up. */

/* Power on devices again */
device_resume(RESUME_POWER_ON);



五. Kernel supported on DPM

5.1 task_struct

在task_struct中,添加了对dpm的支持:

/* dynamic power management */
int dpm_state;

这个状态就设定为上述提到的dpm task struct task状态之一。用dpm_set_task_state()来进行状态的切换。
子进程继承父进程的dpm状态,

另外,在支持dpm的kernel中,有许多类似的结构都添加了对dpm的支持。例如:

struct device_driver {
char * name;
struct bus_type * bus;
struct device_class * devclass;

struct semaphore unload_sem;
struct kobject kobj;
struct list_head bus_list;
struct list_head class_list;
struct list_head devices;

int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
#if 1 /* linux-pm */
int (*scale) (struct bus_op_point * op, u32 level);
#endif /* linux-pm */
};

整个dpm架构在Linux kernel中以device driver的形式存在,提供了支持应用程序和内核及其他driver的接口。

六. DPM Initializing

了解了这些知识,看看dpm的初始化:

一看这个目录就知道了这个可能是个driver,至少是个内核模块,至于怎么编译就不管了,可以静态编译到内核中.所以你肯定会去找module_init拉.
#ifdef MODULE
module_init(dpm_init_module);
module_exit(dpm_exit_module);
#else
__initcall(dpm_init_module);
#endif

看见了初始化函数是dpm_init_module:
int dpm_init_module(void)
{
#ifndef CONFIG_MACH_OMAP1610_STBPM
/* ensure syscall slot is available */
if (sys_call_table[sys_dpm_nr] != sys_call_table[0])
return 1;

/* install our syscall */
sys_call_table[sys_dpm_nr] = sys_dpm;

#ifdef CONFIG_PROC_FS
{
void dpm_proc_init(void);
dpm_proc_init();
}
#endif

dpm_md_init();

trace("DPM is now installed ");
#endif /* CONFIG_MACH_OMAP1610_STBPM */
return 0;

}

(1) 注册自己的系统调用 sys_dpm,作为用户态应用程序的接口
(2) 调用dpm_md_init()完成自己的初始化.

static inline void dpm_md_init(void)
{
if (dpm_md.init)
dpm_md.init();
}


int __init dpm_omap24xx_init(void)
{
printk("OMAP24XX Dynamic Power Management ");

/*在这个地方连接cpu相关的初始化函数*/
dpm_md.init = NULL;
dpm_md.init_opt = dpm_omap24xx_init_opt;
dpm_md.set_opt = dpm_omap24xx_set_opt;
dpm_md.get_opt = dpm_omap24xx_get_opt;
dpm_md.validate_opt = dpm_omap24xx_validate_opt;
dpm_md.idle_set_parms = NULL;
dpm_md.cleanup = dpm_omap24xx_cleanup;

#ifdef MAYBE
/* +++ necessary? */
dpm_omap24xx_board_setup();
#endif
#ifdef LATER
dpm_bd.init();
#endif

omap24xx_clk_init();
omap24xx_vcs_init();

return 0;
}


七. DPM idle and DPM suspend

说一下idle:



int __init
dpm_omap_init(void)
{
printk("TI OMAP Dynamic Power Management. ");

dpm_md.init_opt = dpm_omap_init_opt;
dpm_md.set_opt = dpm_omap_set_opt;
dpm_md.get_opt = dpm_omap_get_opt;
dpm_md.validate_opt = dpm_omap_validate_opt;
dpm_md.idle_set_parms = NULL;
dpm_md.cleanup = dpm_omap_cleanup;

return 0;
}

__initcall(dpm_omap_init);

发现了对硬件的实际操作基本都在中定义。

static int __init omap_pm_init(void)
{
unsigned char addr, data;
u32 yy;
u32 zz;

printk("Power Management for TI OMAP. ");
pm_idle = dpm_idle;

memcpy((void *)ISRAM_PROGRAM, omap2420_cpu_suspend,
omap2420_cpu_suspend_sz);


addr = 0x55; /* IOSTATE2 */
Spi1_read(SPI1_CS1, 1, &addr, &data);

if((data & 0x04) == 0x04){ /* BRB? */
pm_BRB_mode = 1;
}
else {
pm_BRB_mode = 0;
}

/* GPIO wakeup settings */
__raw_writel(0x0002A000, OMAP2420_GPIO1_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO1_SETWKUENA */
__raw_writel(0x0000000C, OMAP2420_GPIO2_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO2_SETWKUENA */
__raw_writel(0x00004010, OMAP2420_GPIO3_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO3_SETWKUENA */
__raw_writel(0x00100020, OMAP2420_GPIO4_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO4_SETWKUENA */
__raw_writel(0x00000000, OMAP2420_GPIO5_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO5_SETWKUENA */

#ifdef CONFIG_ICE_PATCH_PM
if(pm_BRB_mode)
{
request_irq(INT_GPIO_121,gpio121_dummy_handler,SA_INTERRUPT,"gpio121_dummy",NULL);
__REG32(0x49012034) |= (1<<25) ;
__REG32(0x49012040) &= ~(1<<25);
__REG32(0x49012044) |= (1<<25) ;
__REG32(0x4901201C) |= (1<<25) ;
__REG32(0x49012048) |= (1<<25) ;
__raw_writel(0x02100020, OMAP2420_GPIO4_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO4_SETWKUENA */
}
#endif
if(stm_dev_sts[DRV_UART1] == STAT_OFF) {
/* disable UART1 function clock */
CM_FCLKEN1_CORE &= ~(1<<21); /* CM_FCLKEN1_CORE:EN_UART1 */
CM_ICLKEN1_CORE &= ~(1<<21); /* CM_ICLKEN1_CORE:EN_UART1 */
}

yy = __REG32(0x6D000060) & 0x0000FF00; /* SDRC_DLLA_CTRL(0x6D000060) 0x0000yy00 */
zz = __REG32(0x6D000068) & 0x0000FF00; /* SDRC_DLLB_CTRL(0x6D000068) 0x0000zz00 */
*((u32 *)(ISRAM_PROGRAM + omap2420_val_SDRC_DLLA_CTRL_yy_offset)) = yy;
*((u32 *)(ISRAM_PROGRAM + omap2420_val_SDRC_DLLB_CTRL_zz_offset)) = zz;

pm_loops_per_jiffy = loops_per_jiffy;

return 0;
}
__initcall(omap_pm_init);

可以看到很多都是对硬件设备的直接操作。

那么既然这里把pm_idle初始化成了dpm_idle(),所以dpm_idle就是实际的dpm idle state的处理函数。

我们看在linux中何时会处理dpm idle state,也就是说硬件驱动层和内核的接口。
,定义了这个接口。
void (*pm_power_off)(void);
void (*pm_idle)(void);

EXPORT_SYMBOL(pm_idle); pm_idle也会被kernel export出来,做为驱动程序的接口

/*
* The idle thread. We try to conserve power, while trying to keep
* overall latency low. The architecture specific idle is passed
* a value to indicate the level of "idleness" of the system.
*/

void cpu_idle(void)
{
/* endless idle loop with no priority at all */

while (1) {
while (!need_resched()) {
void (*idle)(void) = pm_idle;
if (!idle)
idle = default_idle;
leds_event(led_idle_start);
dpm_set_os(DPM_IDLE_STATE);
idle();
dpm_set_os(DPM_IDLE_TASK_STATE);
leds_event(led_idle_end);
}
schedule();
#ifndef CONFIG_NO_PGT_CACHE
check_pgt_cache();
#endif
}
}

最关键的几句:
dpm_set_os(DPM_IDLE_STATE);
idle(); (由上述分析,实际会调用dpm_idle())
dpm_set_os(DPM_IDLE_TASK_STATE);


看看dpm_idle里面到底做了什么:
/* If DPM is currently disabled here we simply do the standard
idle wait.

If we're not actually in DPM_IDLE_TASK_STATE, we need to go back and get
into this state. This could happen in rare instances - an interrupt between
dpm_set_os() and the critical section.

If we are not yet at the idle-task operating point, or if there is no
difference between idle-task and idle, we can enter/exit the idle state
quickly since it's only for statistical purposes. This is also true if for
some reason we can't get the DPM lock, since obviously an asynchronous event
is going to have to occur to clear the lock, and this event is going to take
us out of idle.

Otherwise the full idle shutdown is done. */

dpm_idle() -> full_idle()-> omap24xx_pm_idle()

这个是NEC dpm中做实际事情的最内层的调用函数:
void omap24xx_pm_idle(void)
{
TRACE_PROCESS(TRACE_EV_PROCESS_IDLE_BEGIN, 0, 0);

if(dpm_enabled) {

if(status_idle_judge_func() == STM_IDLE) {

device_suspend(0, SUSPEND_POWER_DOWN);

local_irq_disable();
local_fiq_disable();

if( OMAP_SLEEP_ENABLE == do_sleep_setup() ) {
int dummy = PRCM_SLEEP;
omap24xx_pm_suspend(dummy);
}

local_fiq_enable();
local_irq_enable();

device_resume(RESUME_POWER_ON);

}
}
}

可以看到做的基本的事情就是:
device_suspend(0, SUSPEND_POWER_DOWN);
omap24xx_pm_suspend(dummy);
device_resume(RESUME_POWER_ON);

实际做事情的函数是
omap24xx_pm_suspend(PRCM_SLEEP)
里面有一句:
func_ptr = (void *)(ISRAM_PROGRAM);
ISRAM_PROGRAM是一个IO space的地址。
在上面提到的初始化的时候有这么一句:

memcpy((void *)ISRAM_PROGRAM, omap2420_cpu_suspend,
omap2420_cpu_suspend_sz);

#define ISRAM_PROGRAM IO_ADDRESS(0x40200000)
所以这里实际会执行omap2420_cpu_suspend,另外其余部分还完成了许多对硬件的操作。


现在回到, 看device_suspend:
这个是个大范围的suspend,涉及到所有已经注册device.

int device_suspend(u32 state, u32 level)
{
struct list_head * node;
int error = 0;

#if 0 /* linux-pm */
printk(KERN_EMERG "Suspending devices ");
#endif /* linux-pm */

down(&device_sem);
list_for_each_prev(node,&global_device_list) {
struct device * dev = to_dev(node);
#ifdef CONFIG_SERIAL_CONSOLE
if (dev == sercons_dev)
continue;
#endif
#ifdef CONFIG_I2C_OMAP24XX
if ((dev == i2c_device[0]) || (dev == i2c_device[1]))
continue;
#endif
if (dev->driver && dev->driver->suspend) {
pr_debug("suspending device %s ",dev->name);
error = dev->driver->suspend(dev,state,level);
if (error)
printk(KERN_ERR "%s: suspend returned %d ",dev->name,error);
}
}

#ifdef CONFIG_SERIAL_CONSOLE
if (sercons_dev)
sercons_dev->driver->suspend(sercons_dev,state,level);
#endif
#ifdef CONFIG_I2C_OMAP24XX
if (i2c_device[0])
i2c_device[0]->driver->suspend(i2c_device[0],state,level);
if (i2c_device[1])
i2c_device[1]->driver->suspend(i2c_device[1],state,level);
#endif
up(&device_sem);
return error;
}


意思不用过多阐述了. 不明白的看第八部分.


八. Linux Device Module

中定义了device_suspend和device_resume,这个是linux提供给driver的上层接口。说到这个wrapper层, 就要先说一下Linux Driver Module.

中定义了一个global_device_list, list 了在内核中注册的device.
LIST_HEAD(global_device_list);

每个device都是这样的一个结构:
struct device {
struct list_head g_list; /* node in depth-first order list */
struct list_head node; /* node in sibling list */
struct list_head bus_list; /* node in bus's list */
struct list_head driver_list;
struct list_head children;
struct list_head intf_list;
struct device * parent;

struct kobject kobj;
char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */
char bus_id[BUS_ID_SIZE]; /* position on parent bus */

struct bus_type * bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *driver_data; /* data private to the driver */

u32 class_num; /* class-enumerated value */
void * class_data; /* class-specific data */

void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */

u32 power_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0
being fully functional, and D3
being off. */

#if 1 /* linux-pm */
struct constraints *constraints;
#endif /* linux-pm */
unsigned char *saved_state; /* saved device state */

void (*release)(struct device * dev);
};

#define to_dev(obj) container_of(obj,struct device,kobj)


#define KOBJ_NAME_LEN 16

struct kobject {
char name[KOBJ_NAME_LEN];
atomic_t refcount;
struct list_head entry;
struct kobject * parent;
struct subsystem * subsys;
struct dentry * dentry;
};

/**
* get_device - increment reference count for device.
* @dev: device.
*
* This simply forwards the call to kobject_get(), though
* we do take care to provide for the case that we get a NULL
* pointer passed in.
*/

struct device * get_device(struct device * dev)
{
return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
}

to_dev() 将kobject_get()加引用计数后的kobj结构还原(找到上层"wrapper")到dev结构
#define to_dev(obj) container_of(obj,struct device,kobj)

/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({
const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})

上述内容属于Linux Driver Moudule的东西,只是Linux内核提供给驱动程序的接口,是一层wrapper,或者说屏蔽了底层硬件的差异,提供了一个统一的逻辑接口。
相关的操作有:
EXPORT_SYMBOL(device_initialize);
EXPORT_SYMBOL(device_add);
EXPORT_SYMBOL(device_register);

EXPORT_SYMBOL(device_del);
EXPORT_SYMBOL(device_unregister);
EXPORT_SYMBOL(get_device);
EXPORT_SYMBOL(put_device);

EXPORT_SYMBOL(device_create_file);
EXPORT_SYMBOL(device_remove_file);

你可以从中找到这些函数的实现。

我们在device结构中可以看到这样的一个结构
struct device_driver *driver; /* which driver has allocated this device */
这就是一个driver的结构, 什么意思, 比如你的系统里面有pci,i2c等总线,这些总线上可能会挂上一个或者多个同一标准接口的device,所以就把这个总线的公共操作能够抽象出来,也就 可以看成是总线的driver.这个结构定义在
其实也可以这样想,每个device如果关联一个driver结构的话,肯定会比较灵活.因为即便是一个pci总线,也可以对不同的 device有不同的driver,这样就提供了一个中间wrapper层,理论上是总线控制设备,但是实际实现的时候操作会经过这个中间层,比如一个特 定的device最终会找到它对应的driver,实现它特有的操作.这就灵活了.
上面这段文字实际上是给你们把这个架构举了一个例子,实际上这个例子只是给用户便于理解.其实suspend和resume,shutdown 诸如此类的命令并不是总线直接发出的,真正的主宰还是用户的驱动程序,它们通过调用Linux Device Module这一层提供的公共接口去控制其他注册在系统中的设备,或者控制它自己.

struct device_driver {
char * name;
struct bus_type * bus;
struct device_class * devclass;

struct semaphore unload_sem;
struct kobject kobj;
struct list_head bus_list;
struct list_head class_list;
struct list_head devices;

int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
#if 1 /* linux-pm */
int (*scale) (struct bus_op_point * op, u32 level);
#endif /* linux-pm */
};

shutdown不说了,suspend和resume是挂起和恢复的操作,比如进入省电模式时这些设备可能会suspend以节省功耗,恢复时 肯定是用resume. remove是移出一个设备. probe的意思是探测,负责完成对硬件的检测工作, 在适当的时刻会被调用,比如设备初始化或者打开设备的时候.

很显然,driver肯定也有一套接口给出,给其他的驱动程序调用.
EXPORT_SYMBOL(driver_register);
EXPORT_SYMBOL(driver_unregister);
EXPORT_SYMBOL(get_driver);
EXPORT_SYMBOL(put_driver);

EXPORT_SYMBOL(driver_create_file);
EXPORT_SYMBOL(driver_remove_file);

中.

举个例子:
void omap2_bus_register(struct device *device,
struct device_driver *driver)
{
if(!device || !driver)
return ;

if(!driver->suspend || !driver->resume)
return ;

driver->bus = &omap2_private_bus ;
(void) driver_register(driver) ;
device->parent = &omap2_private_device ;
device->bus = &omap2_private_bus ;
device_register(device) ;
return ;
}

呵呵。很抱歉没能介绍一些概念性的东西。这是前两天给同事做的一个presentation. 但是写的时候没有写太细。DPM直译为动态电源管理,和传统电源管理的区别是更为灵活,粒度更细。这个思想就是把整个系统的功耗状态(包括CPU, 总线,其他一些设备)等和进程联系起来,以达到更细的电源管理,节省系统的功耗。
所谓系统状态是指一系列参数,比如CPU 电压,频率等等(里面的结构有涉及),这些参数决定了系统的功耗,所以一组这样的参数就可以看成是系统的一种状态,这个就是操作点.(operating state, op for short)。 大家可以查相关的资料网上有论述。

由于时间的关系,我没能阐述的太细。而且我只是进行了代码级的分析,自己也是刚看这部分,可能会有不正确的地方,只是想给出一个参考,如果大家能 用得上。我现在没有这个实际的环境,过一段时间会出去在实际的环境下调试和验证这些代码,回来会修改这个版本,让它更加精确,不至于给人误导。呵呵。



分析得不错,不过私下认为对于一个东西,分析完之后是不是应该说说它的不足? 呵呵。

dpm的致命缺点有好几个:

- MontaVista 私自做出来的一个概念并且做炒作,呵呵,实际上并没有什么实用价值;
- Linux kernel 中已经把cpufreq 放到mainline,在cpufreq上添加对电压调整的支持就达到了动态调节频率和电压的目的;
- DPM 的 memory footprint 太大。如果我定义若干个policy,定义一系列的opt point,那么对于内存的占用是很大的,而且本身的设计造成了没有必要的复杂;

因为以上的原因,dpm可能永远都没有机会放到mainline,呵呵。
别中了montavista的毒。



个人认为DPM真的不怎么样,呵呵!通过task_state的状态来设置不同的操作点,比如两个进程在运行,两 个task_state不相同,这两个进程切换时,DPM就会不停的去切换操作点,由于时间都比较短,这就对DVFM有很高的要求,要求变频和变压在很短 的时间完成,呵呵

我觉得IPM相对来说比较好一些,根据cpu的繁忙程度来动态提高或者降低频率,从整体上考虑,应该是个比较好的选择,最近正在研究这个,呵呵!



转载自:http:///linux/clf/embedded/archive/00/00/64/98/649848.html
阅读(2977) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~