×
单片机 > 单片机程序设计 > 详情

S3C2440电源管理有关问题及其解决方法

发布时间:2020-06-06 发布时间:
|

关于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);//tfirst
 s3c2410_gpio_cfgpin(S3C2410_GPG14,S3C2410_GPG14_INP);//tfirst
 s3c2410_gpio_cfgpin(S3C2410_GPG15,S3C2410_GPG15_INP);//tfirst

 /* 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 ,tfirst add ,2009-05-27 */
    __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)
{
&bsp;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
#include
#include
#include
#include
#include
#include
#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;
}



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
单片机的抗干扰措施有哪些