puppy居
puppy居士
posts - 41,comments - 27,trackbacks - 0
s3c2410 电源管理(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
posted on 2008-08-22 15:04 puppy 阅读(574) 评论(0)  编辑 收藏 引用
只有注册用户登录后才能发表评论。