S3C2416睡眠的底层实现

环境:
ARM CPU: S3C2416
Linux-Kernel: 3.6.0

针对S3C2416睡眠和唤醒的实现方法,参见上一篇:

从sys/power/state分析并实现S3C2416的睡眠和唤醒

本文分析S3C2416睡眠的底层实现,分两个部分:

1、CPU相关的实现过程

2、内核怎么把睡眠唤醒的功能加入

一、CPU相关的实现过程

S3C2416睡眠的寄存器设置在pm-s3c2416.c中(同色为调用关系)

arch/arm/mach-s3c24xx/pm-s3c2416.c

static int s3c2416_cpu_suspend(unsigned long arg):使能睡眠功能,让CPU进入睡眠

{
	/* enable wakeup sources regardless of battery state */
	/* #define S3C2443_PWRCFG_SLEEP		(1<<15) */
	__raw_writel(S3C2443_PWRCFG_SLEEP, S3C2443_PWRCFG);

	/* set the mode as sleep, 2BED represents "Go to BED" */
	__raw_writel(0x2BED, S3C2443_PWRMODE);

	s3c2412_sleep_enter();
	panic("sleep resumed to originator?");
}

__raw_writel(S3C2443_PWRCFG_SLEEP, S3C2443_PWRCFG)函数将寄存器PWRCFG[15]位置1,开启睡眠唤醒源,如手册:

那么我们拓展一下,如果实现的不是睡眠模式,而是Deep-Stop模式,那就可以依样操作寄存器PWRCFG[16]位

至于第2个寄存器操作,对照datasheet,就可以明白;同样的,换个Power模式就操作相应的寄存器。

这里有一个问题:Datasheet上说(位于STOP mode (Normal and Deep-stop)小节)To enter the Deep-STOP mode,
PWRMODE[18]
register should be configured before entering STOP mode.但是上图PWRMODE寄存器明显标明,PWRMODE[18]是“RESERVED”,尚未使用的。哪里来的BUG?

以上只是配置寄存器,进入睡眠在s3c2412_sleep_enter()函数中实现,该函数由汇编代码实现,在arch/arm/mach-s3c24xx/sleep-s3c2412.S。等有机会再详述该函数。

static void s3c2416_pm_prepare(void):睡眠的寄存器配置保存起来(存到寄存器INFORM0、INFORM1)

{
	/*
	 * write the magic value u-boot uses to check for resume into
	 * the INFORM0 register, and ensure INFORM1 is set to the
	 * correct address to resume from.
	 */
	__raw_writel(0x2BED, S3C2412_INFORM0);
	__raw_writel(virt_to_phys(s3c_cpu_resume), S3C2412_INFORM1);
}

注释说的很清楚,配置这两个寄存器的目的,一是用于U-boot启动时判断是否为唤醒式重启,二是获取唤醒恢复后的运行地址。关于第一点,对照u-boot就可以十分清楚:

u-boot-1.3.4/board/samsung/smdk2416/lowlevel_init.S
#ifdef CONFIG_PM
	/* PM check */
	@ Check if this is a wake-up from sleep
        ldr     r0, =INFORM0_REG	/* INFORM0 register */
        ldr     r1, [r0]
        ldr     r2, =0x2BED
        cmp     r2, r1

	ldreq 	r0, =INFORM1_REG	/* INFORM1 register */
        ldreq 	r1, [r0]
        moveq 	pc, r1 			 /* end PM check */
#endif

以上两个背景色标注的函数在s3c2416_pm_add函数中被调用:

static int s3c2416_pm_add(struct device *dev, struct subsys_interface *sif)

{

pm_cpu_prep = s3c2416_pm_prepare;

pm_cpu_sleep = s3c2416_cpu_suspend;

return 0;

}

pm_cpu_prep和pm_cpu_sleep是两个函数指针,定义如下:

void (*pm_cpu_prep)(void);
int (*pm_cpu_sleep)(unsigned long);

至此可以推测,睡眠实现的位置在以上两个指针的调用处,可以找到是在s3c_pm_enter()函数中:

arch/arm/plat-samsung/pm.c

static int s3c_pm_enter(suspend_state_t state)

{

/* ensure the debug is initialised (if enabled) */

s3c_pm_debug_init();

S3C_PMDBG("%s(%d)\n", __func__, state);

/* 若没有实现上述两个指针的具体指向,即没有睡眠的操作函数,退出 */

if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {

printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__);

return -EINVAL;

}

/* 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 "%s: No wake-up sources!\n", __func__);

printk(KERN_ERR "%s: Aborting sleep\n", __func__);

return -EINVAL;

}

/* save all necessary core registers not covered by the drivers */

samsung_pm_save_gpios();

samsung_pm_saved_gpios();

s3c_pm_save_uarts();

s3c_pm_save_core();

/* set the irq configuration for wake */

s3c_pm_configure_extint();

S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n",

s3c_irqwake_intmask, s3c_irqwake_eintmask);

s3c_pm_arch_prepare_irqs();

/* call cpu specific preparation */

pm_cpu_prep();

/* flush cache back to ram */

flush_cache_all();

s3c_pm_check_store();

/* send the cpu to sleep... */

s3c_pm_arch_stop_clocks();

/* this will also act as our return point from when

* we resume as it saves its own register state and restores it

* during the resume.  */

/*
这里运行后即睡眠 */

cpu_suspend(0, pm_cpu_sleep);

/* restore the system state */

/* 以下是唤醒过程,以后再细说
*/

.......

return 0;

}

二、内核怎么把睡眠唤醒的功能加入

在上面一小节中提到 s3c2416_pm_add函数,它的调用如下:

static struct subsys_interface s3c2416_pm_interface = {

.name
= "s3c2416_pm",

.subsys
= &s3c2416_subsys,

.add_dev
= s3c2416_pm_add,

};

static __init int s3c2416_pm_init(void)

{

return subsys_interface_register(&s3c2416_pm_interface);

}

arch_initcall(s3c2416_pm_init);

至此,即实现了睡眠、唤醒的插入。

初版。待修改。。。

时间: 2024-10-11 01:09:45

S3C2416睡眠的底层实现的相关文章

S3C2416睡眠后唤醒的具体细节

〇.引子 书接上回,即:S3C2416睡眠的底层实现. 话说,春梦了无痕,秋鸿有信终需醒.睡醒后的恢复操作以及恢复哪些东西,在s3c_pm_enter(suspend_state_t state)函数的后半部分有具体实现: arch/arm/plat-samsung/pm.c <span style="font-family:Courier New;">static int s3c_pm_enter(suspend_state_t state) { ...... /* re

从sys/power/state分析并实现S3C2416的睡眠和唤醒

环境: PC: debian-7.6.0 ARM CPU: S3C2416 Linux-Kernel: 3.6.0(FriendlyARM) U-boot: 1.3.4 一.问题来源 根据需要,在S3C2416上添加中断睡眠和唤醒功能,于是我就查查Linux支持S3C2416的睡眠模式: cat /sys/power/state 执行完,万万没想到:竟然是空的,该命令没有任何输出!也就是说,我的内核目前不支持任何方式的睡眠. 不可能啊!之前我用S3C2440的CPU(内核版本Linux_2_6_

Linux 2.6 内核阅读笔记 内核同步

2014年7月26日 内核抢占和内核控制路径的设计 内核抢占的一种定义:如果进程正在内核态执行内核函数时,允许发生内核切换(就是被替换的进程是内核函数所在进程),这个内核就是抢占的. linux内核提供了内核抢占的开启和关闭功能,在current_thread_info的preempt_count字段大于0时,内核就是不能抢占的.可以通过preempt_disable和preempt_enable来增加这个字段来控制这个开关. 中断和软中断和抢占的控制主要使用到current_thread_in

ARM linux电源管理——Cortex A系列CPU(32位)睡眠和唤醒的底层汇编实现

ARM linux电源管理——Cortex A系列CPU(32位)睡眠和唤醒的底层汇编实现 承接 http://www.wowotech.net/pm_subsystem/suspend_and_resume.html Linux电源管理(6)_Generic PM之Suspend功能一文中的下图. 本文主要分析平台相关的CPU睡眠和唤醒,即下电和上电流程,以及ARM底层汇编代码实现. 内核版本:3.1.0               CPU:ARM Cortex-A7 1 平台相关函数执行流程

S3C2416裸机开发系列十七_GCC下Fatfs的移植

S3C2416裸机开发系列十七 GCC下Fatfs的移植 象棋小子    1048272975 对于固态存储器,其存储容量可以很大,往往需要一款文件系统对存储器用户数据进行组织文件的管理.它对文件存储器空间进行组织和分配,负责文件的存储并对存入的文件进行保护和检索.在嵌入式系统中,往往需要采用windows兼容的文件系统,像相机的照片.视频监控.语音产品等,很多都需要从windows计算机上提取资源或在windows计算机上进一步处理.Fatfs由于其开源免费,支持fat32,受到了广泛的应用,

linux 内核睡眠与唤醒

休眠(被阻塞)的进程处于一个特殊的不可执行状态.进程休眠由多种原因,但肯定都是为了等待一些事件.事件可能是一 段时间从文件I/O读取更多数据,或者是某个硬件事件.一个进程还由可能在尝试获取一个已被占用的内核信号量时被迫进入休眠.休眠的一个常见原因就是文件 I/O —— 如进程对一个文件执行了read()操作,而这需要从磁盘里读取.还有,进程在获取键盘输入的时候也需要等待.无论哪种情况,内核的操作都相同:进程把自己标记成休眠状态,从可执行红黑树中移出,放入等待队列,然后调用schedule()选择

JUC回顾之-CyclicBarrier底层实现和原理

1.CyclicBarrier 字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时候,屏障才会开门.所有被屏障拦截的线程才会运行. 2.常用的方法: CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作. CyclicBarrier(int parties, R

Android 开发之 ---- 底层驱动开发(一)

驱动概述 说到 Android 驱动是离不开 Linux 驱动的.Android 内核采用的是 Linux2.6 内核 (最近Linux 3.3 已经包含了一些 Android 代码).但 Android 并没有完全照搬 Linux 系统内核,除了对Linux 进行部分修正,还增加了不少内容.android 驱动 主要分两种类型:Android 专用驱动 和 Android 使用的设备驱动(linux). Android 专有驱动程序: 1)Android Ashmem 匿名共享内存: 为用户空

计算机底层知识拾遗(十)理解进程调度【转】

转自:http://www.cnblogs.com/zfyouxi/p/4504042.html 这篇说说内核的进程调度机制,进程调度是内核的一个重要工作,由调度器完毕. 进程状态 内核调度器调度的实体(KSE, kernal schedule entry)是进程和线程.内核必须知道全部进程和线程的状态,比方把时间片给一个堵塞的进程是没有意义的.从内核的角度来看,进程的状态有3种: 1. 执行,表示正在执行的进程 2. 等待,没有执行,可是等待时间片执行的进程 3. 睡眠,也就是堵塞,包含可中断