1.s3c6410无法进入休眠
执行echo mem > /sys/power/state后系统无法进行休眠 跟踪发生pm相关函数没有注册 修改arch/arm/plat-samsung/pm.c在最后加上
arch_initcall(s3c_pm_init);//modify by hclydao 20150406初始化 同时修改如下两个变量
unsigned long s3c_irqwake_intmask = 0xffff0fffL;//eint0-3 can resume unsigned long s3c_irqwake_eintmask = 0x0L;设置唤醒中断 这样休眠后就可以通过设置的中断唤醒
2.s3c6410休眠与唤醒之rtc
休眠后rtc会打印一个BUG信息 修改如下drivers/rtc/rtc-s3c.c
增加
static bool wake_en;//add by hclydao 20150522修改suspend和resume函数
static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) { /* save TICNT for anyone using periodic interrupts */ ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); if (s3c_rtc_cpu_type == TYPE_S3C64XX) { ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON); ticnt_en_save &= S3C64XX_RTCCON_TICEN; } s3c_rtc_enable(pdev, 0); #if 0 //modify by hclydao 20150507 if (device_may_wakeup(&pdev->dev)) enable_irq_wake(s3c_rtc_alarmno); #else if (device_may_wakeup(&pdev->dev) && !wake_en) if(enable_irq_wake(s3c_rtc_alarmno) == 0) wake_en = true; else dev_err(&pdev->dev,"enable_irq_wake failed\n"); #endif return 0; } static int s3c_rtc_resume(struct platform_device *pdev) { unsigned int tmp; s3c_rtc_enable(pdev, 1); writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) { tmp = readw(s3c_rtc_base + S3C2410_RTCCON); writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); } #if 0 //modify by hclydao 20150507 if (device_may_wakeup(&pdev->dev)) disable_irq_wake(s3c_rtc_alarmno); #else if (device_may_wakeup(&pdev->dev) && wake_en) { disable_irq_wake(s3c_rtc_alarmno); wake_en = false; } #endif return 0; }不通过rtc唤醒
3.s3c6410 休眠与唤醒之lcd
echo mem > /sys/power/state休眠后 唤醒 lcd显示为白屏 修改如下
drivers/video/samsung/s3cfb_fimd4x.c中的s3cfb_set_gpio函数在最前面修改如下本身也有说明 这个寄存器要设置成0
#if 1 /* See mach-smdk6410.c:smdk6410_map_io() - S3C64XX_MODEM_MIFPCON */ /* Must be '0' for Normal-path instead of By-pass */ //writel(0x0, S3C_HOSTIFB_MIFPCON); val = __raw_readl(S3C64XX_MODEM_MIFPCON); //add by hclydao 20150526 val &= ~MIFPCON_LCD_BYPASS; __raw_writel(val, S3C64XX_MODEM_MIFPCON); #endif这里修改后 白屏问题解决 但是唤醒后是黑屏 要点击一下屏幕才会有界面显示出来 同时有一些区域还是显示的是黑色
跟踪发现里面执行fbcon_switch lcd休眠的时候切换了控制台 导致fb数据被清空 去现虚拟控制台后发现lcd完全没显示 所以接到源头
kernel/power/suspend.c 中suspend_prepare函数调用了pm_prepare_console以及pm_restore_console把这两个注释掉 解决问题
4.s3c6410 休眠与唤醒之nandflash
休眠唤醒后 往flash里写数据 重新开机后发现数据没有保存 跟踪到代码drivers/mtd/nand/s3c_nand.c
里面有suspend和resume函数 加入打印flash寄存器代码 发现两次打印的flash寄存器值不一样 修改如下
#include <plat/pm.h> //add by hclydao 20150528 static int nand_regs[] = {//add by hclydao 20150528 S3C_NFCONF, S3C_NFCONT, S3C_NFCMMD, S3C_NFADDR, }; static long nand_value[ARRAY_SIZE(nand_regs)];//add by hclydao 20150528 static void s3c_nand_pm(int state) {//add by hclydao 20150528 int i; if(state) { for(i=0;i<ARRAY_SIZE(nand_regs);i++) { nand_value[i] = readl(s3c_nand.regs + nand_regs[i]); } } else { for(i=0;i<ARRAY_SIZE(nand_regs);i++) { writel(nand_value[i], (s3c_nand.regs + nand_regs[i])); } } } static void print_nand() { //add by hclydao 20150528 printk("+++++++%s : nfconf is 0x%x cont is 0x%x cmmd is 0x%x nfaddr is 0x%x\n",__func__, readl(s3c_nand.regs+S3C_NFCONF),readl(s3c_nand.regs+S3C_NFCONT), readl(s3c_nand.regs+S3C_NFCMMD),readl(s3c_nand.regs+S3C_NFADDR)); } static int s3c_nand_suspend(struct platform_device *dev, pm_message_t pm) { struct s3c_nand *info = platform_get_drvdata(dev); s3c_nand_pm(1); //add by hclydao 20150528 //print_nand(); //s3c_pm_do_save(s3c_nand_save, ARRAY_SIZE(s3c_nand_save)); clk_disable(s3c_nand.clk); return 0; } static int s3c_nand_resume(struct platform_device *dev) { struct s3c_nand *info = platform_get_drvdata(dev); //s3c_pm_do_restore(s3c_nand_save, ARRAY_SIZE(s3c_nand_save)); s3c_nand_pm(0); //add by hclydao 20150528 //print_nand(); clk_enable(s3c_nand.clk); return 0; }编译下载 正常 问题解决
5. s3c6410休眠与唤醒之wm8960
最初测试中发现唤醒后无法播放声音,量波型发现 i2s 时钟信号正常 数据信号没有 没有数据发送给wm8960 跟踪发现 唤醒后播放声音dma没有产生中断 偏移地址没有发生变化 导致数据传送失败 本来想通过sound/soc/samsung/dma.c中的dma_trigger去实现dmac数据保存与恢复 跟踪发现suspend信号根本都没有发到这里来 跟踪半天 找到了源头 但是无法解决 所以直接在sound/soc/samsung/i2s.c里加了算了 修改如下
#ifdef CONFIG_PM #include <asm/hardware/pl080.h>//add by hclydao 20150601 static int dma_regs[] = { //add by hclydao 20150601 PL080_TC_CLEAR, PL080_ERR_CLEAR, PL080_SOFT_BREQ, PL080_SOFT_SREQ, PL080_CONFIG, PL080_SYNC, }; static long dma_value0[ARRAY_SIZE(dma_regs)];//add by hclydao 20150601 static long dma_value1[ARRAY_SIZE(dma_regs)];//add by hclydao 20150601 void s3c_pm_save_dma(void) {//add by hclydao 20150601 void __iomem *regs; int i = 0; regs = ioremap(0x75000000, 0x200); if (!regs) { printk(KERN_ERR "%s: %d failed to ioremap()\n", __func__,__LINE__); return ; } for(i=0;i<ARRAY_SIZE(dma_regs);i++) { dma_value0[i] = readl(regs + dma_regs[i]); } iounmap(regs); regs = ioremap(0x75100000, 0x200); if (!regs) { printk(KERN_ERR "%s: %d failed to ioremap()\n", __func__,__LINE__); return ; } for(i=0;i<ARRAY_SIZE(dma_regs);i++) { dma_value1[i] = readl(regs + dma_regs[i]); } iounmap(regs); return; } void s3c_pm_restore_dma(void) {//add by hclydao 20150601 void __iomem *regs; int i = 0; regs = ioremap(0x75000000, 0x200); if (!regs) { printk(KERN_ERR "%s: %d failed to ioremap()\n", __func__,__LINE__); return ; } for(i=0;i<ARRAY_SIZE(dma_regs);i++) { writel(dma_value0[i],(regs + dma_regs[i])); } iounmap(regs); regs = ioremap(0x75100000, 0x200); if (!regs) { printk(KERN_ERR "%s: %d failed to ioremap()\n", __func__,__LINE__); return ; } for(i=0;i<ARRAY_SIZE(dma_regs);i++) { writel(dma_value1[i],(regs + dma_regs[i])); } iounmap(regs); return; } static void i2s_printk(struct i2s_dai *i2s) { //add by hclydao 20150528 printk("++++I2SMOD is 0x%x I2SCON : 0x%x I2SPSR : 0x%x I2SFIC: 0x%x\n",readl(i2s->addr + I2SMOD),readl(i2s->addr + I2SCON), readl(i2s->addr + I2SPSR),readl(i2s->addr + I2SFIC)); } static int i2s_suspend(struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); s3c_pm_save_dma();//add by hclydao 20150601 //if (dai->active) {//modify by hclydao 20150601 i2s->suspend_i2smod = readl(i2s->addr + I2SMOD); i2s->suspend_i2scon = readl(i2s->addr + I2SCON); i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR); i2s->suspend_i2sfic = readl(i2s->addr + I2SFIC); //} return 0; } static int i2s_resume(struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); s3c_pm_restore_dma();//add by hclydao 20150601 //if (dai->active) {//modify by hclydao 20150601 writel(i2s->suspend_i2scon, i2s->addr + I2SCON); writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); writel(i2s->suspend_i2sfic, i2s->addr + I2SFIC); //} return 0; } #else #define i2s_suspend NULL #define i2s_resume NULL #endif将串口和pcm用的dmac保存再恢复 同时将i2s相关寄存器也作相应处理 还要修改sound/soc/codecs/wm8960.c
static int wm8960_suspend(struct snd_soc_codec *codec, pm_message_t state) { #if 1 //modify by hclydao youhua 20150601 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); #endif return 0; } static int wm8960_resume(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); #if 0 //modify by hclydao youhua 20150601 int i; u8 data[2]; u16 *cache = codec->reg_cache; /* Sync reg_cache with the hardware */ for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) { data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); data[1] = cache[i] & 0x00ff; codec->hw_write(codec->control_data, data, 2); } #else wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); #endif return 0; }还有一个系统控制选择dma还是sdma的地方arch/arm/mach-s3c64xx/pm.c在static struct sleep_save core_save[]最后加上
SAVE_ITEM(S3C_SYSREG(0x110)),//add by hclydao 20150529编译下载 问题解决
6.s3c6410休眠与唤醒之usb host
唤醒后usb host无法工作 修改如下
drivers/usb/host/ohci-s3c2410.c
#ifdef CONFIG_PM //add by hclydao 20150522 static int ohci_hcd_s3c2410_drv_suspend(struct device *dev) { #ifdef CONFIG_MACH_GZSD6410 s3c_otg_phy_config(0); #endif } static int ohci_hcd_s3c2410_drv_resume(struct device *dev) { #ifdef CONFIG_MACH_GZSD6410 s3c_otg_phy_config(1); #endif } #endif static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_drv_probe, .remove = ohci_hcd_s3c2410_drv_remove, .shutdown = usb_hcd_platform_shutdown, #if 0 //modify by hclydao 20150522 /*.suspend = ohci_hcd_s3c2410_drv_suspend, */ /*.resume = ohci_hcd_s3c2410_drv_resume, */ #else #ifdef CONFIG_PM .suspend = ohci_hcd_s3c2410_drv_suspend, .resume = ohci_hcd_s3c2410_drv_resume, #endif #endif .driver = { .owner = THIS_MODULE, .name = "s3c2410-ohci", }, };