您的位置:首页 > 其它

关于s5pc110系统休眠的情景分析(2)

2011-05-14 23:43 232 查看
附:http://blogold.chinaunix.net/u/25572/showart_2533630.html 相关android的文章不错。

我们来分析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");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: