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

发布者:暗里著迷最新更新时间:2016-12-03 来源: eefocus关键字: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);//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;
}


关键字:S3C2440  电源管理 引用地址:S3C2440电源管理有关问题及其解决方法

上一篇:ARM920T的CP15协处理器
下一篇:s3c2440 三个时钟

推荐阅读最新更新时间:2024-03-16 15:23

MMU工作原理以及S3C2440的MMU
MMU,全称Memory Manage Unit, 中文名——存储器管理单元。 许多年以前,当人们还在使用DOS或是更古老的操作系统的时候,计算机的内存还非常小,一般都是以K为单位进行计算,相应的,当时的程序规模也不大,所以 内存容量虽然小,但还是可以容纳当时的程序。但随着图形界面的兴起还用用户需求的不断增大,应用程序的规模也随之膨胀起来,终于一个难题出现在程序员的面 前,那就是应用程序太大以至于内存容纳不下该程序,通常解决的办法是把程序分割成许多称为覆盖块(overlay)的片段。覆盖块0首先运行,结束时他将 调用另一个覆盖块。虽然覆盖块的交换是由OS完成的,但是必须先由程序员把程序先进行分割,这是一个费时费力的工作,而且相当枯
[单片机]
MMU工作原理以及<font color='red'>S3C2440</font>的MMU
ARM-Linux s3c2440 之UART分析(四)
注册完成之后,设备与驱动是如何连接的呢? 原来在driver_register()注册中已经进行设备连接了。 串口是一种platform设备,在体系框架setup_arch()中完成设备注册,而且设备的注册先于驱动注册,最终串口设备与驱动的连接在driver_register() -- bus_add_driver()中完成。 int bus_add_driver(struct device_driver *drv) { intret; structdevice_driver *other; if(drv- bus- p- drivers_autoprobe)
[单片机]
变电所遥视监控系统研究
  1 引言   随着我国电网调度管理水平的不断提高, 许多地区电力通信网的建设取得了很大的进展,基本上实现了数字化通信。采用光纤、数字微波或无线扩频等通信方式,信道带宽和信道质量明显提高。而要全面直观地了解变电站的真实情况,远程视觉监控是必备的监控管理手段,它可在几十公里及更远之外的调度中心及时准确地掌握变电站的情况, 并对各种情况进行操作控制,做到真正的无人值守。   2 无人值班变电所运行现状   近年来, 随着电网规模的迅速扩大, 电力系统科技水平不断提高, 无人值守变电所的数量日益增加, 尤其随着变电所自动化程度的不断提高,非电气因素造成的事故比例越来越高。如何降低或避免这类事故的发生,已是实现无人值班变电所要解决
[单片机]
变电所遥视监控系统研究
s3c2440裸机-UART编程(二、UART编程实现)
UART编程 1.初始化 我们的2440支持3个UART串口,以uart0为例讲解。 那么我们需要实现以下这几个函数完成串口的最基本功能: (1)uart0_init()用于初始化串口 (2)putchar()用于发送一个字符 (3)getchar()用于接收一个字符 (4)puts()用于发送一串字符 1.uart0_init() 1.配置uart0引脚 (1)根据原理图GPH2,3用于TxD0, RxD0。 (2)查看dataset,配置GPH控制寄存器,让GPH2,3配成uart模式;为了将其保持为高电平,先设置其为上拉。 GPHCON &= ~((3 4) | (3 6)); GPHCON |= ((2 4) | (
[单片机]
<font color='red'>s3c2440</font>裸机-UART编程(二、UART编程实现)
【ARM裸板】S3C2440 时钟设置与分析
由时钟树分析,时钟源通过选择器接入给MPLL(Main PLL)与UPLL(USB PLL) 经过MPLL得到FCLK提供给CPU FCLK分别通过HDIVN与PDIVN分频得到HCLK与PCLK HCLK接入给AHB总线,再给各种高速设备(Nand Flash、内存控制器、中断控制器…) PCLK接入给APB总线,再给各种低速设别(I2C、PWM、GPIO、UART…) 1.时钟源最大值 2.时钟源 2.1 两种时钟源 1.晶振 2.外部引脚时钟输入 2.2 选择时钟源 通过改变OM 引脚来选择时钟源 JZ2440 OM 接入GND,则主时钟源与USB时钟源都选择外部晶振 3.上电时钟分析 复位等待,
[单片机]
【ARM裸板】<font color='red'>S3C2440</font> 时钟设置与分析
肖特基DC/DC转换器需二极管在电源管理中的应用分析
任何非同步直流/直流 转换器 都需要一个所谓的续流 二极管 。为了优化方案的整体效率,通常倾向于选择低正向电压的 肖特基 管。很多设计都采用一个 转换器 设计(网络) 工具推荐的 二极管 。这并非总是 二极管 的最优选择。更何况,如果设计工具不考虑热性能和漏电流之间的动态变化,则极有可能发生实际性能有别于设计工具的分析或模拟出的结果。本文将探讨一些在选择正确的二极管时应仔细考虑的典型参数,以及如何应用这些参数来快速确定选型的正确与否。 检查损耗 图1给出了非同步直流/直流降压 转换器 的基本框图。D1是所需的肖特基管。左侧是开关S1闭合时(时间为T1)的电流情况,右侧是开关S1打开时(时间为T2)的电流情况。
[电源管理]
肖特基DC/DC转换器需二极管在<font color='red'>电源管理</font>中的应用分析
Dialog联合立锜发展大陆便携电源管理市场
据媒体报道,Dialog半导体日前正和台湾立锜科技共同为中国智能手机及平板电脑市场开发电源管理系统。该合作集合了Dialog的电源管理技术以及立锜科技的自身产品组合及其在亚洲的销售渠道。 Dialog公司CEO Jalal Bagherli表示,“中国2014年将生产超过5亿部智能手机,这对于我们两家公司来说都是一个很好的增长机会。” 立锜科技总经理谢叔亮则表示:“我们将合作开发出业界领先的、同时也是符合中国市场定价策略的产品。”
[手机便携]
基于ARM的超声波发射与控制电路设计
随着科学技术的发展,高温、高压、高速和高负荷已成为现代工业的重要标志,但它的实现是建立在材料高质量的基础之上的,为确保这种优异的质量,必须采用不破坏产品原来的形状、不改变其使用性能的检测方法,对产品进行百分之百地检测,以确保其可靠性和安全性,这种技术就是无损检测技术。 超声波检测在无损检测中占据着主要地位,广泛应用于金属、非金属材料以及医学仪器等领域。近年来以微电子学和计算机技术为基础的信息技术飞速发展,超声无损检测仪器也得到了前所未有的发展动力,为了提高检测的可靠性和提高检测效率,研制数字化、智能化、自动化、图像化的超声仪是当今无损检测领域发展的一个重要趋势。而传统的超声波检测仪存在准确性差、精度低、体积大、功耗大
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved