s3c2440电源管理主要问题及其解决方法 -- 转
[复制链接]
最近由于项目需要,得做2440上的电源管理,之前没有做过,所以出现了不少问题,现将过程中出现的主要问题及其解决方法列出如下:
1、系统睡眠与唤醒,拿到普通的代码,出现的问题经常是进入睡眠后,GPIO唤醒总是导致系统重新启动,其实这是因为没有设置CPU的运行模式,而这运行模式是通过设置GPG13
,GPG14,GPG15来进行的。所以就要想唤醒后恢复到睡眠之前的状态,则要在进入睡眠前设置这三个GPIO的模式,可以在arch/arm/plat-s3c24xx/pm.c文件中的s3c2410_pm_enter
()中在进入谁面前,也就是执行__raw_writel(0x00, S3C2410_CLKCON)前加入如下三条语句: __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND); __raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND); __raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND); 即可使系统恢复正常。 附: 我修改的s3c2410_pm_enter函数如下: static int s3c2410_pm_enter(suspend_state_t state) { unsigned long regs_save[16]; int tmp; /* ensure the debug is initialised (if enabled) */
s3c2410_pm_debug_init();
DBG("s3c2410_pm_enter(%d)\n", state); /* our board doesn't support hard disk.*/ if (state != PM_SUSPEND_MEM) { printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n"); return -EINVAL; }
if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { printk(KERN_ERR PFX "error: no cpu sleep functions set\n"); return -EINVAL; } /* configure pin GPF4 for wake-up */ // enable_irq_wake(IRQ_EINT4); // s3c2410_gpio_cfgpin(S3C2410_GPF4,S3C2410_GPF4_EINT4); // set_irq_type(IRQ_EINT4, IRQT_BOTHEDGE);
tmp = __raw_readl(S3C2410_EINTMASK); tmp &= ~(1UL<<4); __raw_writel(tmp,S3C2410_EINTMASK);
s3c2410_gpio_cfgpin(S3C2410_GPG13,S3C2410_GPG13_INP); s3c2410_gpio_cfgpin(S3C2410_GPG14,S3C2410_GPG14_INP); s3c2410_gpio_cfgpin(S3C2410_GPG15,S3C2410_GPG15_INP);
/* 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(); /* set USB pad as suspend mode , */ __raw_writel(__raw_readl(S3C2410_MISCCR) | (3<<12),S3C2410_MISCCR);
/* 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); /* save resume address */ __raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3);
/*clear the General Status Registers reg 2*/ __raw_writel(0xff,S3C2410_GSTATUS2);
/* save all necessary core registers not covered by the drivers */ s3c2410_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_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); __raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND); __raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND);
/* call cpu specific preparation */ pm_cpu_prep();
/* flush cache back to ram */
flush_cache_all();
s3c2410_pm_check_store();
/* send the cpu to sleep... */
__raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */
/* s3c2410_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 (s3c2410_cpu_save(regs_save) == 0) { flush_cache_all(); pm_cpu_sleep(); }
/* 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 */
s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); s3c2410_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));// s3c2410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_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 */ dump_irq_reg();
DBG("S3C2410 PM Resume (post-restore)\n"); return 0; }
2、在调试的过程中,出现过系统无法进入睡眠的情况。情况大概是串口终端已经进入睡眠了,系统停止了,但是用精密电源去测,发现电流还是没有下降,通过跟踪,才知道系
统逐个调用各个驱动的suspend,按照s3c2410-ts.c原来提供的驱动结构: static struct platform_driver s3c2410ts_driver = { .name = "s3c2410-ts", .bus = &platform_bus_type, .probe = s3c2410ts_probe, .remove = s3c2410ts_remove, };
,系统无法使得触摸品进入睡眠,至于什么原因,不是很理解,将驱动的系统注册接口改为如下: static struct platform_driver s3c2410ts_driver= { .probe = s3c2410ts_probe, .remove = s3c2410ts_remove, .suspend = s3c2410ts_suspend, .resume = s3c2410ts_resume, .driver = { .name = "s3c2410-ts", .owner = THIS_MODULE, },
};
系统就可以进入睡眠了。
3.在调试的过程中,还出现过跟LCD唤醒相关的问题。主要就是两个问题 一个问题是,在唤醒LCD之后,LCD会花屏。原以为拿到的这个代码已经做好了睡眠/唤醒,看了代码才发现,这个代码在睡眠之前,没有保存LCD的设置,在唤醒之后是重新初始化
LCD的控制器,所以会出现花屏的现象,所以自己重新实现了s3c2410fb_suspend和s3c2410fb_resume两个函数,代码如下: static int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state) { struct fb_info *fbinfo = platform_get_drvdata(dev); struct s3c2410fb_info *info = fbinfo->par; unsigned long flags;
s3c2410fb_stop_lcd(info);
local_irq_save(flags); lcdcon1 = readl(info->io + S3C2410_LCDCON1); lcdcon2 = readl(info->io + S3C2410_LCDCON2); lcdcon3 = readl(info->io + S3C2410_LCDCON3); lcdcon4 = readl(info->io + S3C2410_LCDCON4); lcdcon5 = readl(info->io + S3C2410_LCDCON5);
lcdsaddr1 = readl(info->io + S3C2410_LCDSADDR1); lcdsaddr2 = readl(info->io + S3C2410_LCDSADDR2); lcdsaddr3 = readl(info->io + S3C2410_LCDSADDR3);
redlut = readl(info->io + S3C2410_REDLUT); greenlut = readl(info->io + S3C2410_GREENLUT); bluelut = readl(info->io + S3C2410_BLUELUT);
dithmode = readl(info->io + S3C2410_DITHMODE); tpal = readl(info->io + S3C2410_TPAL); lcdintpnd = readl(info->io + S3C2410_LCDINTPND); lcdsrcpnd = readl(info->io + S3C2410_LCDSRCPND); lcdintmsk = readl(info->io + S3C2410_LCDINTMSK); lpcsel = readl(info->io + S3C2410_LPCSEL); lcdcon1 &= ~S3C2410_LCDCON1_ENVID; local_irq_restore(flags);
/* sleep before disabling the clock, we need to ensure * the LCD DMA engine is not going to get back on the bus * before the clock goes off again (bjd) */
msleep(1); clk_disable(info->clk); /* * shutdown the LCD power and backlight power */ s3c2410_gpio_setpin(S3C2410_GPG12, 0); s3c2410_gpio_setpin(S3C2410_GPG4, 1);
return 0; }
static int s3c2410fb_resume(struct platform_device *dev) { struct fb_info *fbinfo = platform_get_drvdata(dev); struct s3c2410fb_info *info = fbinfo->par; struct s3c2410fb_mach_info *mach_info = info->dev->platform_data; unsigned long flags; int i; printk("=======framebuffer resume.=====\n"); s3c2410_gpio_setpin(S3C2410_GPG4, 0); msleep(10); // s3c2410_gpio_setpin(S3C2410_GPG12, 1); // msleep(10);
clk_enable(info->clk);
msleep(1); local_irq_save(flags); writel(lcdcon1,info->io + S3C2410_LCDCON1); writel(lcdcon2,info->io + S3C2410_LCDCON2); writel(lcdcon3,info->io + S3C2410_LCDCON3); writel(lcdcon4,info->io + S3C2410_LCDCON4); writel(lcdcon5,info->io + S3C2410_LCDCON5);
writel(lcdsaddr1,info->io + S3C2410_LCDSADDR1); writel(lcdsaddr2 ,info->io + S3C2410_LCDSADDR2); writel(lcdsaddr3,info->io + S3C2410_LCDSADDR3);
writel(redlut,info->io + S3C2410_REDLUT); writel(greenlut,info->io + S3C2410_GREENLUT); writel(bluelut,info->io + S3C2410_BLUELUT);
writel(dithmode,info->io + S3C2410_DITHMODE); writel(tpal,info->io + S3C2410_TPAL); writel(lcdintpnd,info->io + S3C2410_LCDINTPND); writel(lcdsrcpnd,info->io + S3C2410_LCDSRCPND); writel(lcdintmsk,info->io + S3C2410_LCDINTMSK); writel(lpcsel,info->io + S3C2410_LPCSEL);
local_irq_restore(flags); msleep(1);
local_irq_save(flags);
/* modify the gpio(s) with interrupts set (bjd) */
modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask); modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask); modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask); modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
// s3c2410fb_init_registers(fbinfo); s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_OUTP); // back light control s3c2410_gpio_cfgpin(S3C2410_GPG4, S3C2410_GPG4_OUTP); local_irq_restore(flags);
// s3c2410_gpio_setpin(S3C2410_GPG12, 1); // s3c2410_gpio_setpin(S3C2410_GPG4, 0);
// s3c2440_pwm_set(1);
writel(readl(info->io + S3C2410_LCDCON1) | S3C2410_LCDCON1_ENVID, info->io+S3C2410_LCDCON1); msleep(50); s3c2410_gpio_setpin(S3C2410_GPG12, 1); msleep(10); return 0; } 大致意思就是在进入睡眠之前要保存LCD的各个控制器中的设置内容,关闭LCD,关闭背光;在唤醒之后打开LCD,恢复LCD控制的内容,特别注意在最后使能LCD,也就是ritel
(readl(info->io + S3C2410_LCDCON1) | S3C2410_LCDCON1_ENVID, info->io+S3C2410_LCDCON1),否则就会出现LCD显示错位的情况,然后打开背光。
另一个问题是:在唤醒之后LCD无显示,跟踪了一下,发现在进入LCD的suspend之前,framebuffer的数据已经被清零了,这个问题困扰了我很长时间,后来在论坛上看到帖子有人
提供了思路说是在进入睡眠之前系统会切换控制台,所以framebuffer的数据会被清零,根据此思路,修改kernel/power/console.c文件中的pm_prepare_console() 修改如下: int pm_prepare_console(void) { acquire_console_sem();
orig_fgconsole = fg_console; #if 0
if (vc_allocate(SUSPEND_CONSOLE)) { /* we can't have a free VC for now. Too bad, * we don't want to mess the screen for now. */ release_console_sem(); return 1; }
if (set_console(SUSPEND_CONSOLE)) { /* * We're unable to switch to the SUSPEND_CONSOLE. * Let the calling function know so it can decide * what to do. */ release_console_sem(); return 1; } #endif release_console_sem(); /* if (vt_waitactive(SUSPEND_CONSOLE)) { pr_debug("Suspend: Can't switch VCs."); return 1; } */ orig_kmsg = kmsg_redirect; kmsg_redirect = SUSPEND_CONSOLE; return 0; } 如上修改即可实现唤醒后,LCD上显示原来的图像。
另,可以用应用程序使得系统进入睡眠
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ioctl.h> #include <unistd.h> #include <fcntl.h> #include <linux/ioctl.h> #define APM_IOC_STANDBY _IO('A', 1) #define APM_IOC_SUSPEND _IO('A', 2) int main (void) { int fd; //char buf[32]; fd = open ("/dev/apm_bios",O_RDWR); if (fd < 0) { printf ("fd open failed\n"); exit(0); }
if (ioctl (fd, APM_IOC_SUSPEND)<0) { printf("\nset suspend err\n"); }
close (fd); return 0; }
(另,我想说说我对论坛的一些看法,有时候我会进论坛看看,发现论坛里绝大多数的帖子都有这样一个情况,就是最开始是问题,但到最后都没有结贴的,感觉就像是有问题才想到来论坛来求救,而在论坛得到帮助之后,却自顾走人,没有想到要把解决问题的方法贴出来,回馈给论坛,使得很多遇到类似问题的网友在重复地在某个问题上无从下手,我想这是不是我们国人有一种习惯,就是只懂得索取?我想,这是我们国家在技术上落后别人的原因之一吧。
以上看法,谢绝有意见的网友进行攻击,如果斑竹觉得有问题可以把我的帖子删了)
|