以6410为例
休眠过程如下
static int s3c6410_pm_enter(suspend_state_t state)
{
unsigned long regs_save[16];
unsigned int tmp;
/* ensure the debug is initialised (if enabled) */
DBG("s3c6410_pm_enter(%d)\n", state);
if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
printk(KERN_ERR PFX "error: no cpu sleep functions set\n");
return -EINVAL;
}
/* prepare check area if configured */
s3c6410_pm_check_prepare();
/* store the physical address of the register recovery block */
s3c6410_sleep_save_phys = virt_to_phys(regs_save);
printk("s3c6410_sleep_save_phys=0x%08lx\n", s3c6410_sleep_save_phys);
/* save all necessary core registers not covered by the drivers */
s3c6410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save));
s3c6410_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
s3c6410_pm_do_save(core_save, ARRAY_SIZE(core_save));
s3c6410_pm_do_save(sromc_save, ARRAY_SIZE(sromc_save));
/* ensure INF_REG0 has the resume address */
__raw_writel(virt_to_phys(s3c6410_cpu_resume), S3C_INFORM0);
/* set the irq configuration for wake */
s3c6410_pm_configure_extint();
/* call cpu specific preperation */
pm_cpu_prep();
/* flush cache back to ram */
flush_cache_all();
s3c6410_pm_check_store();
/* send the cpu to sleep... */
__raw_writel(0xffffffff, S3C64XX_VIC0INTENCLEAR);
__raw_writel(0xffffffff, S3C64XX_VIC1INTENCLEAR);
__raw_writel(0xffffffff, S3C64XX_VIC0SOFTINTCLEAR);
__raw_writel(0xffffffff, S3C64XX_VIC1SOFTINTCLEAR);
__raw_writel(1, S3C_OSC_STABLE);
__raw_writel(1, S3C_PWR_STABLE);
/* Set WFI instruction to SLEEP mode */
tmp = __raw_readl(S3C_PWR_CFG);
tmp &= ~(0x60<<0);
tmp |= (0x3<<5);
__raw_writel(tmp, S3C_PWR_CFG);
tmp = __raw_readl(S3C_SLEEP_CFG);
tmp &= ~(0x61<<0);
__raw_writel(tmp, S3C_SLEEP_CFG);
/* Clear WAKEUP_STAT register for next wakeup -jc.lee */
/* If this register do not be cleared, Wakeup will be failed */
tmp = __raw_readl(S3C_WAKEUP_STAT);
__raw_writel(tmp, S3C_WAKEUP_STAT);
/* ALL sub block "ON" before enterring sleep mode - EVT0 bug*/
__raw_writel(0xffffff00, S3C_NORMAL_CFG);
/* Open all clock gate to enter sleep mode - EVT0 bug*/
__raw_writel(0xffffffff, S3C_HCLK_GATE);
__raw_writel(0xffffffff, S3C_PCLK_GATE);
__raw_writel(0xffffffff, S3C_SCLK_GATE);
/* s3c6410_cpu_save will also act as our return point from when
* we resume as it saves its own register state, so use the return
* code to differentiate return from save and return from sleep */
if (s3c6410_cpu_save(regs_save) == 0) {
flush_cache_all();
pm_cpu_sleep();
}
/* restore the cpu state */
cpu_init();
/* restore the system state */
s3c6410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
s3c6410_pm_do_restore(sromc_save, ARRAY_SIZE(sromc_save));
s3c6410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_save));
s3c6410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
tmp = __raw_readl(S3C64XX_EINT0PEND);
__raw_writel(tmp, S3C64XX_EINT0PEND);
DBG("post sleep, preparing to return\n");
s3c6410_pm_check_restore();
/* ok, let's return from sleep */
DBG("S3C6410 PM Resume (post-restore)\n");
return 0;
}
唤醒过程如下
ENTRY(s3c6410_cpu_resume)
msr cpsr_c, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
ldr r2, =LL_UART /* for debug */
#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
/* Initialise the GPIO state if we are debugging via the SMDK LEDs,
* as the uboot version supplied resets these to inputs during the
* resume checks.
*/
ldr r3, =S3C64XX_PA_GPIO
ldr r0, [ r3, #S3C64XX_GPNCON ]
bic r0, r0, #(S3C64XX_GPN_CONMASK(12) | S3C64XX_GPN_CONMASK(13) | \
S3C64XX_GPN_CONMASK(14) | S3C64XX_GPN_CONMASK(15))
orr r0, r0, #(S3C64XX_GPN_OUTPUT(12) | S3C64XX_GPN_OUTPUT(13) | \
S3C64XX_GPN_OUTPUT(14) | S3C64XX_GPN_OUTPUT(15))
str r0, [ r3, #S3C64XX_GPNCON ]
ldr r0, [ r3, #S3C64XX_GPNDAT ]
bic r0, r0, #0xf << 12 @ GPN12..15
orr r0, r0, #1 << 15 @ GPN15
str r0, [ r3, #S3C64XX_GPNDAT ]
#endif
/* __v6_setup from arch/arm/mm/proc-v6.S, ensure that the caches
* are thoroughly cleaned just in case the bootloader didn't do it
* for us. */
mov r0, #0
mcr p15, 0, r0, c7, c14, 0 @ clean+invalidate D cache
mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache
mcr p15, 0, r0, c7, c15, 0 @ clean+invalidate cache
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
@@mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs
@@mcr p15, 0, r0, c7, c7, 0 @ Invalidate I + D caches
ldr r0, s3c6410_sleep_save_phys
ldmia r0, { r4 - r13 }
mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID
mcr p15, 0, r5, c3, c0, 0 @ Domain ID
mcr p15, 0, r6, c2, c0, 0 @ Translation Table BASE0
mcr p15, 0, r7, c2, c0, 1 @ Translation Table BASE1
mcr p15, 0, r8, c2, c0, 2 @ Translation Table Control
mcr p15, 0, r10, c1, c0, 1 @ Auxiliary control register
mov r0, #0 @ restore copro access controls
mcr p15, 0, r11, c1, c0, 2 @ Co-processor access controls
mcr p15, 0, r0, c7, c5, 4
ldr r2, =resume_with_mmu
mcr p15, 0, r9, c1, c0, 0 /* turn mmu back on */
nop
mov pc, r2 /* jump back */
.end
调试的过程中发现了一个问题
mcr p15, 0, r9, c1, c0, 0 /* turn mmu back on */
之后程序就飞了。
原因我明白,6410的sdram物理地址是50000000,开启mmu之后地址变为了c0000000。所以程序飞了。
目前想到了这个问题的两种解决办法。
1。内核运行后,一直保持50000000->50000000 和 c0000000->50000000的映射。
2。在sleep之前。remap一下,把虚拟地址的50000000和物理地址的50000000对应起来,唤醒之后,再去掉这个映射关系。
显然第二种关系比较好。(自己感觉理论可行,还没有来得及测试)
对于这个问题,想听听对电源管理熟悉的朋友的建议。
期待您的答复 |