分类: LINUX
2011-04-23 15:31:35
我们来分析enter_state这个函数,这个函数应该是一个重量级的函数,首先再次通过valid_state检测state的有效性,前面我们已经分析过
这个检测方法通过全局的变量suspend_ops是否为空,其成员函数valid是否为空,以及调用成员函数valid的返回值来判断,s5pc110的平台 只接受PM_SUSPEND_MEM的suspend,获取互斥锁,同步文件系统之后,
调用suspend_prepare()函数,这个函数也定义在suspend.c中,做一些 进入suspend状态时的准备工作,我们分析这个函数
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
static
int suspend_prepare(void)
{
int error;
if (!suspend_ops || !suspend_ops->enter)
return -EPERM;
pm_prepare_console();
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if
(error)
goto Finish;
error = usermodehelper_disable();
if (error)
goto Finish;
error = suspend_freeze_processes();
if (!error)
return 0;
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return
error;
}
我们看pm_notifier_call_chain(PM_SUSPEND_PREPARE),这里涉及到PM
notifier的一个机制,关于这个机制,我们简单介绍一下
pm在power/main.c中定义了一个pm_chain_head,用于pm在转换过程中的通知路径
static
BLOCKING_NOTIFIER_HEAD(pm_chain_head);
pm在休眠和唤醒的过程中会发出一些EVENTS,这些事件定义在notifier.h中
/*
Hibernation and suspend events */
#define PM_HIBERNATION_PREPARE 0x0001 /*
Going to hibernate */
#define PM_POST_HIBERNATION 0x0002 /* Hibernation
finished */
#define PM_SUSPEND_PREPARE 0x0003 /* Going to suspend the system
*/
#define PM_POST_SUSPEND 0x0004 /* Suspend finished */
#define
PM_RESTORE_PREPARE 0x0005 /* Going to restore a saved image */
#define
PM_POST_RESTORE 0x0006 /* Restore failed
*/
如果哪个内核模块想得到这些EVENTS,就需要定义一个notifier_block,
并把它挂载到pm_chain_head上。为此,PM定义了挂载和卸载函数
int register_pm_notifier(struct
notifier_block *nb)
{
return
blocking_notifier_chain_register(&pm_chain_head,
nb);
}
EXPORT_SYMBOL_GPL(register_pm_notifier);
int unregister_pm_notifier(struct notifier_block *nb)
{
return
blocking_notifier_chain_unregister(&pm_chain_head,
nb);
}
EXPORT_SYMBOL_GPL(unregister_pm_notifier);
在PM休眠或者唤醒进行到某个特定阶段时,PM就会使用下面这个函数向链上所有的内核模块发送一个EVENT
int
pm_notifier_call_chain(unsigned long val)
{
return
(blocking_notifier_call_chain(&pm_chain_head, val, NULL)
==
NOTIFY_BAD) ? -EINVAL :
0;
}
我们看到就是在suspend_prepare中调用了pm_notifier_call_chain(PM_SUSPEND_PREPARE)
我们以s5pc110平台为例,看看触发了那些事件
在文件drivers/char/apm-emulation.c中定义了apm_suspend_notifier
因此pm_notifier_call_chain会触发apm_suspend_notifier的相应事件,这里执行apm_suspend_notifier的PM_SUSPEND_PREPARE分支
下面执行函数usermodehelper_disable(),这个函数在kernel/kmod.c中
int
usermodehelper_disable(void)
{
long retval;
usermodehelper_disabled = 1;
smp_mb();
/*
* From now on
call_usermodehelper_exec() won't start any new
* helpers, so it is
sufficient if running_helpers turns out to
* be zero at one point (it may
be increased later, but that
* doesn't matter).
*/
retval =
wait_event_timeout(running_helpers_waitq,
atomic_read(&running_helpers)
== 0,
RUNNING_HELPERS_TIMEOUT);
if (retval)
return 0;
usermodehelper_disabled = 0;
return
-EAGAIN;
}
这个函数中主要是调用等待事件wait_event_timeout(queue, condition,
timeout);
如果condition为真,则立即返回值RUNNING_HELPERS_TIMEOUT,如果调用call_usermodehelper_exec中的
helper_lock()会增加计数running_helpers,使得条件为假,当调用helper_unlock时会减小计数running_helpers,并判断引用计数 running_helpers是否为0,如果为零则调用wake_up(&running_helpers_waitq),
唤醒以running_helpers_waitq作为等待队列头的所有等待 队列对应的进程。
因此这个等待事件用于等待以running_helpers_waitq作为等待队列头的进程被唤醒,如果条件为真则立即返回,如果条件为假则阻塞,如果 超出等待时间RUNNING_HELPERS_TIMEOUT返回-EAGAIN。
下面调用函数suspend_freeze_processes()
调用power/process.c文件中的函数freeze_processes(void)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
做完以上的工作之后,调用suspend_devices_and_enter
这是一个重量级的函数,我们分析一下
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* suspend_devices_and_enter
- suspend devices and enter the desired system
* sleep
state.
* @state: state to enter
*/
int
suspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
return -ENOSYS;
if (suspend_ops->begin) {
error =
suspend_ops->begin(state);
if (error)
goto
Close;
}
suspend_console();
suspend_test_start();
error =
dpm_suspend_start(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:
Some devices failed to suspend\n");
goto
Recover_platform;
}
suspend_test_finish("suspend devices");
if
(suspend_test(TEST_DEVICES))
goto Recover_platform;
suspend_enter(state);
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resume
devices");
resume_console();
Close:
if
(suspend_ops->end)
suspend_ops->end();
return error;
Recover_platform:
if
(suspend_ops->recover)
suspend_ops->recover();
goto
Resume_devices;
}
首先我们看suspend_ops->begin,根据以前的分析这个函数指针指向s3c_pm_ops的begin成员,即s3c_pm_begin,这里需要在kernel中配置
CONFIG_REGULATOR,
s3c_pm_begin函数会调用函数regulator_suspend_prepare,这个函数在/drivers/regulator/core.c中
int
regulator_suspend_prepare(suspend_state_t state)
{
struct
regulator_dev *rdev;
int ret = 0;
/* ON is handled by regulator active state */
printk("++ %s,
state:%d\n", __func__, state);
if (state == PM_SUSPEND_ON)
return
-EINVAL;
mutex_lock(®ulator_list_mutex);
list_for_each_entry(rdev,
®ulator_list, list) {
printk("rdev :%p\n",
rdev);
mutex_lock(&rdev->mutex);
ret =
suspend_prepare(rdev, state);
mutex_unlock(&rdev->mutex);
if (ret < 0) {
printk(KERN_ERR "%s: failed to prepare
%s\n",
__func__, rdev->desc->name);
goto
out;
}
}
out:
mutex_unlock(®ulator_list_mutex);
return
ret;
}
在这个函数中,首先判断参数state。接下来遍历链表regulator_list,这个链表将结构体struct
regulator_dev通过成员list链接在一起
这个链表是在max8698_pmic_probe中的regulator_register中建立的,在regulator_register函数中有一句
list_add(&rdev->list,
®ulator_list);
遍历每一个rdev,调用函数suspend_prepare
static int
suspend_prepare(struct regulator_dev *rdev, suspend_state_t
state)
{
if (!rdev->constraints)
return -EINVAL;
printk("%s, rdev->constraints:%p, state:%d\n",
__func__,rdev->constraints, state );
switch (state) {
case
PM_SUSPEND_STANDBY:
return
suspend_set_state(rdev,
&rdev->constraints->state_standby);
case
PM_SUSPEND_MEM:
return
suspend_set_state(rdev,
&rdev->constraints->state_mem);
case
PM_SUSPEND_MAX:
return
suspend_set_state(rdev,
&rdev->constraints->state_disk);
default:
return
-EINVAL;
}
}
这个函数中根据相应的state调用函数suspend_set_state,这里我们肯定state参数为PM_SUSPEND_MEM,于是调用
suspend_set_state(rdev,&rdev->constraints->state_mem);
static
int suspend_set_state(struct regulator_dev *rdev,
struct regulator_state
*rstate)
{
int ret = 0;
/* enable & disable are mandatory for suspend control */
if
(!rdev->desc->ops->set_suspend_enable
||
!rdev->desc->ops->set_suspend_disable)
{
printk(KERN_ERR "%s: no way to set suspend
state\n",
__func__);
return -EINVAL;
}
printk("rstate->enabled:%d\n", rstate->enabled);
if
(rstate->enabled)
ret =
rdev->desc->ops->set_suspend_enable(rdev);
else
ret =
rdev->desc->ops->set_suspend_disable(rdev);
if (ret < 0)
{
printk(KERN_ERR "%s: failed to enabled/disable\n",
__func__);
return ret;
}
if (rdev->desc->ops->set_suspend_voltage &&
rstate->uV > 0) {
ret =
rdev->desc->ops->set_suspend_voltage(rdev, rstate->uV);
if
(ret < 0) {
printk(KERN_ERR "%s: failed to set
voltage\n",
_func__);
return ret;
}
}
if (rdev->desc->ops->set_suspend_mode && rstate->mode
> 0) {
ret = rdev->desc->ops->set_suspend_mode(rdev,
rstate->mode);
if (ret < 0) {
printk(KERN_ERR "%s: failed
to set mode\n", __func__);
return ret;
}
}
return
ret;
}
这个函数中,根据传递的rdev,rstate进行相应的操作,首先做一些简单的判断,确认rdev->desc->ops->set_suspend_enable和
rdev->desc->ops->set_suspend_disable两个函数非空。这两个函数在max8698_pmic_probe时被初始化,这里分别初始化为函数
max8698_ldo_enable和max8698_ldo_disable,根据rstate->enabled的值确定是调用max8698_ldo_enable和max8698_ldo_disable哪一个函数
然后判断是否要调整电压,执行函数rdev->desc->ops->set_suspend_voltage,这个函数指针指向max8698_set_suspend_voltage;
最后如果rdev->desc->ops->set_suspend_mode非空并且rstate->mode为正,则执行rdev->desc->ops->set_suspend_mode指向的函数
我们不妨看看在S5PC110平台下定义的相关结构体,在文件arch/arm/mach-s5pv210/mach-smdkc110.c中
static
struct regulator_consumer_supply buck1_consumers[] = {
{
.supply =
"vddarm",
},
};
static struct regulator_init_data max8698_buck1_data =
{
.constraints = {
.name = "VCC_ARM",
.min_uV =
750000,
.max_uV = 1500000,
.always_on =
1,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.state_mem =
{
.uV = 1250000,
.mode =
REGULATOR_MODE_STANDBY,
.enabled =
0,
},
},
.num_consumer_supplies =
ARRAY_SIZE(buck1_consumers),
.consumer_supplies =
buck1_consumers,
};
static struct regulator_consumer_supply
buck2_consumers[] = {
{
.supply = "vddint",
},
};
static struct regulator_init_data max8698_buck2_data =
{
.constraints = {
.name = "VCC_INTERNAL",
.min_uV =
950000,
.max_uV = 1200000,
.always_on =
1,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.state_mem =
{
.uV = 1100000,
.mode = REGULATOR_MODE_NORMAL,
.enabled
= 0,
},
},
.num_consumer_supplies =
ARRAY_SIZE(buck2_consumers),
.consumer_supplies =
buck2_consumers,
};
static struct regulator_init_data max8698_buck3_data =
{
.constraints = {
.name = "VCC_MEM",
.min_uV =
1800000,
.max_uV = 1800000,
.apply_uV = 1,
.state_mem =
{
.uV = 1800000,
.mode = REGULATOR_MODE_NORMAL,
.enabled
= 1,
},
/* .initial_state = PM_SUSPEND_MEM,
*/
},
};
static struct regulator_init_data max8698_ldo2_data =
{
.constraints = {
.name = "VALIVE_1.1V",
.min_uV =
1100000,
.max_uV = 1100000,
.apply_uV = 1,
.always_on =
1,
.state_mem = {
.uV = 1100000,
.mode =
REGULATOR_MODE_NORMAL,
.enabled =
1,
},
},
};
.......
.......
static struct max8698_subdev_data smdkc110_regulators[] = {
{
MAX8698_LDO2, &max8698_ldo2_data },
{ MAX8698_LDO3,
&max8698_ldo3_data },
{ MAX8698_LDO4, &max8698_ldo4_data
},
{ MAX8698_LDO5, &max8698_ldo5_data },
{ MAX8698_LDO6,
&max8698_ldo6_data },
{ MAX8698_LDO7, &max8698_ldo7_data
},
{ MAX8698_LDO8, &max8698_ldo8_data },
{ MAX8698_LDO9,
&max8698_ldo9_data },
{ MAX8698_BUCK1, &max8698_buck1_data
},
{ MAX8698_BUCK2, &max8698_buck2_data },
{ MAX8698_BUCK3,
&max8698_buck3_data },
};
/* 1Ghz default voltage */
static
struct max8698_platform_data max8698_platform_data = {
.num_regulators =
ARRAY_SIZE(smdkc110_regulators),
.regulators =
smdkc110_regulators,
.set1 = S5PV210_GPH1(6),
.set2 =
S5PV210_GPH1(7),
.set3 =
S5PV210_GPH0(4),
.dvsarm1 = 0xa, // 1.25v
.dvsarm2
= 0x9, // 1.20V
.dvsarm3 = 0x6, // 1.05V
.dvsarm4
= 0x4, // 0.95V
.dvsint1 = 0x7, // 1.10v
.dvsint2 = 0x5, //
1.00V
};
在这个文件一共定义了11个regulator_init_data结构,这些平台初始化数据都会在max8698_pmic_probe时被初始化到相应的结构中
这里我们逐个分析下这11个LDO做了什么事情
对于max8698_buck3_data,这个肯定是在链表regulator_list的表头,这个显然是给内存供电的,其.constraints.state_mem.enabled
= 1
那么调用max8698_ldo_enable函数
static int max8698_ldo_enable(struct
regulator_dev *rdev)
{
struct max8698_data *max8698 =
rdev_get_drvdata(rdev);
int reg, shift = 8, value, ret;
ret =
max8698_get_register(rdev, ®, &shift);
if (ret)
return
ret;
value = max8698_read_reg(max8698, reg);
if (!(value & (1
<< shift))) {
value |= (1 << shift);
ret =
max8698_write_reg(max8698, reg, value);
}
printk("%s[%d] ldo %d\n",
__func__, __LINE__, max8698_get_ldo(rdev));
return
ret;
}
在这个函数中首先根据参数rdev,获得相应的驱动数据,即成员reg_data,这是指向max8698的;然后通过函数max8698_get_register获取
相应的reg和shift,这里max8698_buck3_data对应的ldo是MAX8698_BUCK3,所以reg为MAX8698_REG_ONOFF1,shift为相应的位
通过max8698_read_reg读取数组max8698_cache_regs[16]
中的相应寄存器对应的值;然后判断如果这个值的相应shift位为0的话,那么将其
改为1,更新max8698_cache_regs数组元素,并通过i2c_smbus_write_byte_data写相应的设备寄存器。
我们看max8698_buck2_data,这里其state_mem.enabled=0,因此调用max8698_ldo_disable,这里max8698_ldo_disable执行的是 max8698_ldo_enable的反操作,如果从cache数组中读取的相关值的shift位为1的话,则更新这一位为0,并写设备寄存器
以上两个regulator中,其state_mem.uV不为零,因此都需要调用函数max8698_set_suspend_voltage
static
int max8698_set_suspend_voltage(struct regulator_dev *rdev, int
uV)
{
int ldo = max8698_get_ldo(rdev);
switch (ldo) {
case MAX8698_BUCK1 ...
MAX8698_BUCK3:
return max8698_buck_set_voltage(rdev, uV,
uV);
case MAX8698_LDO1 ... MAX8698_LDO9:
return
max8698_set_voltage(rdev, uV, uV);
default:
return
-EINVAL;
}
}
根据参数rdev调用max8698_buck_set_voltage或者max8698_set_voltage
至此,s3c_pm_begin函数就结束了,回到函数suspend_devices_and_enter,下面会调用函数suspend_console(),
这个函数定义在kernel/printk.c,这里修改全局变量console_suspended为1,此时控制台挂起无输出
然后调用函数suspend_test_start(),如果kernel配置CONFIG_PM_TEST_SUSPEND,那么就调用
void
suspend_test_start(void)
{
/* FIXME Use better timebase than
"jiffies", ideally a clocksource.
* What we want is a hardware counter
that will work correctly even
* during the irqs-are-off stages of the
suspend/resume cycle...
*/
suspend_test_start_time =
jiffies;
}
然后执行
error =
dpm_suspend_start(PMSG_SUSPEND);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
******************************************************************************************
if
(debug_mask & DEBUG_EXIT_SUSPEND) {
struct timespec ts;
struct
rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec,
&tm);
pr_info("suspend: exit suspend, ret = %d "
"(%d-%02d-%02d
%02d:%02d:%02d.%09lu UTC)\n", ret,
tm.tm_year + 1900, tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
}
register_pm_notifier
if (current_event_num == entry_event_num) {
if (debug_mask &
DEBUG_SUSPEND)
pr_info("suspend: pm_suspend returned with no
event\n");
wake_lock_timeout(&unknown_wakeup, HZ /
2);
}
}
static DECLARE_WORK(suspend_work, suspend);
需要时通过调用queue_work(suspend_work_queue, &suspend_work)去执行suspend_work上的成员函数suspend
suspend_work_queue是一个全局的struct workqueue_struct *类型结构
在系统初始化时通过static int
__init wakelocks_init(void)创建
suspend_work_queue = create_singlethread_workqueue("suspend");