arm电源管理(3)--s3c2410 pm.c
R.wen
再看看(2)中的enter_state():
/**
* enter_state - Do common work of entering low-power state.
* @state: pm_state structure for state we're entering.
*
* Make sure we're the only ones trying to enter a sleep state. Fail
* if someone has beat us to it, since we don't want anything weird to
* happen when we wake up.
* Then, do the setup for suspend, enter the state, and cleaup (after
* we've woken up).
*/
static int enter_state(suspend_state_t state)
{
int error;
//获得锁, 参见注释
if (down_trylock(&pm_sem))
return -EBUSY;
//挂起磁盘的请求, 不是我我们的请求
if (state == PM_SUSPEND_DISK) {
error = pm_suspend_disk();
goto Unlock;
}
//prepare阶段
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
if ((error = suspend_prepare(state)))
goto Unlock;
//进入阶段
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
error = suspend_enter(state);
//完成挂起, 恢复状态
pr_debug("PM: Finishing wakeup.\n");
suspend_finish(state);
Unlock:
up(&pm_sem);
return error;
}
可以看到, 状态的转换分三个阶段, 分别为prepare, enter, finish.
我们已经再二中说明, 这三个阶段通过体系无关的函数,
最终会调用与体系结构相关的函数.
他们分别是:
pm_ops->prepare(state)
pm_ops->enter(state)
pm_ops->finish(state)
这个pm_ops就是在体系结构初始化的时候注册进来的,
接着看arch/arm/mach-s3c2410/pm.c
/*
* Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
*/
static struct pm_ops s3c2410_pm_ops = {
.pm_disk_mode = PM_DISK_FIRMWARE,
.prepare = s3c2410_pm_prepare,
.enter = s3c2410_pm_enter,
.finish = s3c2410_pm_finish,
};
/* s3c2410_pm_init
*
* Attach the power management functions. This should be called
* from the board specific initialisation if the board supports
* it.
*/
int __init s3c2410_pm_init(void)
{
printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n");
pm_set_ops(&s3c2410_pm_ops);
return 0;
}
这就是实现三个状态转换的三个钩子函数.
/**
* pm_set_ops - Set the global power method table.
* @ops: Pointer to ops structure.
*/
//这个函数较为简单, 只是将/kerenel/power/main.c里的全局变量pm_ops设置成
s3c2410_pm_ops而已了.
//这就完成了这个全局变量的初始化.后续对pm_ops的访问实质上都是访问
s3c2410_pm_ops.
void pm_set_ops(struct pm_ops * ops)
{
down(&pm_sem);
pm_ops = ops;
up(&pm_sem);
}
接着再看他们的实现:
先从最简单的开始,
/*
* Called after processes are frozen, but before we shut down devices.
*/
static int s3c2410_pm_prepare(suspend_state_t state)
{
return 0;
}
/*
* Called after devices are re-setup, but before processes are thawed.
*/
static int s3c2410_pm_finish(suspend_state_t state)
{
return 0;
}
如上, 可以看到, prepare和finishi在这个体系中都是空操作, 就是说, 对于s3c2410,
无需特殊的工作.
而这个结构的核心就是剩下的s3c2410_pm_enter了. 它真正实现suspend/resume
的状态转换.
#define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
/* s3c2410_pm_enter
*
* central control for sleep/resume process
*/
static int s3c2410_pm_enter(suspend_state_t state)
{
unsigned long regs_save[16]; //用于保存16个通用寄存器的栈
unsigned long tmp;
/* ensure the debug is initialised (if enabled) */
s3c2410_pm_debug_init();
DBG("s3c2410_pm_enter(%d)\n", state);
if (state != PM_SUSPEND_MEM) {
printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n");
return -EINVAL;
}
/* check if we have anything to wake-up with... bad things seem
* to happen if you suspend with no wakeup (system will often
* require a full power-cycle)
*/
//检查允许的唤醒中断
if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
!any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
printk(KERN_ERR PFX "No sources enabled for wake-up!\n");
printk(KERN_ERR PFX "Aborting sleep\n");
return -EINVAL;
}
/* prepare check area if configured */
//一些准备工作
s3c2410_pm_check_prepare();
/* store the physical address of the register recovery block */
//寄存器的物理地址
s3c2410_sleep_save_phys = virt_to_phys(regs_save);
DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys);
/* ensure at least GESTATUS3 has the resume address */
//将系统被唤醒后执行的函数s3c2410_cpu_resume物理地址写入S3C2410_GSTATUS3.
__raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3);
DBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3));
DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4));
/* save all necessary core registers not covered by the drivers */
//保存不属于driver的核心寄存器, driver的各自保存
s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save));
s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save));
s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save));
/* set the irq configuration for wake */
//设置外部中断用于唤醒
s3c2410_pm_configure_extint();
DBG("sleep: irq wakeup masks: %08lx,%08lx\n",
s3c_irqwake_intmask, s3c_irqwake_eintmask);
//开中断??
__raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK);
__raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK);
/* ack any outstanding external interrupts before we go to sleep */
__raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND);
/* flush cache back to ram */
arm920_flush_kern_cache_all();
s3c2410_pm_check_store();
/* send the cpu to sleep... */
//关闭时钟
__raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */
//系统进入睡眠, 寄存器值保存在(regs_save)中.
//这个函数和上面的s3c2410_cpu_resume(),都是汇编实现的, 在sleep.S中
s3c2410_cpu_suspend(regs_save);
//当接收到一个外部中断时,系统开始恢复
/* restore the cpu state */
cpu_init();
/* unset the return-from-sleep flag, to ensure reset */
tmp = __raw_readl(S3C2410_GSTATUS2);
tmp &= S3C2410_GSTATUS2_OFFRESET;
__raw_writel(tmp, S3C2410_GSTATUS2);
/* restore the system state */
//上面save的逆操作
s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
s3c2410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_save));
s3c2410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save));
//一下均是上面的一些准备工作的逆操作
s3c2410_pm_debug_init();
/* check what irq (if any) restored the system */
DBG("post sleep: IRQs 0x%08x, 0x%08x\n",
__raw_readl(S3C2410_SRCPND),
__raw_readl(S3C2410_EINTPEND));
s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND),
s3c_irqwake_intmask);
s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND),
s3c_irqwake_eintmask);
DBG("post sleep, preparing to return\n");
s3c2410_pm_check_restore();
/* ok, let's return from sleep */
DBG("S3C2410 PM Resume (post-restore)\n");
return 0;
}
a. 首先是通过
if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
!any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
......
}
检查唤醒中断, 如果不存在唤醒中断源, 那系统就不允许suspend,
否则就没人将它唤醒了.
/* state for IRQs over sleep */
/* default is to allow for EINT0..EINT15, and IRQ_RTC as wakeup sources
*
* set bit to 1 in allow bitfield to enable the wakeup settings on it
*/
//默认是[4-15]和RTC
unsigned long s3c_irqwake_intallow = 1L << (IRQ_RTC - IRQ_EINT0) | 0xfL;
unsigned long s3c_irqwake_intmask = 0xffffffffL;
unsigned long s3c_irqwake_eintallow = 0x0000fff0L;
unsigned long s3c_irqwake_eintmask = 0xffffffffL;
b.接着是
/* ensure at least GESTATUS3 has the resume address */
//将系统被唤醒后执行的函数s3c2410_cpu_resume物理地址写入S3C2410_GSTATUS3.
__raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3);
s3c2410_cpu_resume/s3c2410_cpu_suspend 这对逆操作定义在sleep.S中, 是汇编的实现.
c. 然后就是s3c2410_pm_do_save/s3c2410_pm_do_restore了,
这个宏定义就是用于保存/恢复核心的寄存器值.
===========================
参考: Document/pm.tx Documentation/arm/Samsung-S3C24XX/Suspend.txt
http://hi.baidu.com/rwen2012/blog/item/383f8a11aaa46813b9127bb5.html