一個Linux平臺PM功能初探
WatchDog
用戶空間節點
/dev/watchdog
/sys/bus/platform/devices/zx29_ap_wdt.0
/sys/bus/platform/drivers/zx29_ap_wdt
時鐘資源
arch/arm/mach-zx297520v3/include/mach/iomap.h #define ZX_LSP_CRPM_BASE (ZX_LSP_BASE) drivers/clk/zte/clk-zx297520v3.c /************************************************************************** static struct zx29_hwclk ap_wdt_apb = static struct zx29_hwclk ap_wdt_work =
struct clk_lookup periph_clocks_lookups[] = { }; |
platform設備代碼
中:
arch/arm/mach-zx297520v3/include/mach/iomap.h #define ZX_AP_WDT_BASE (ZX_LSP_BASE + 0xE000) arch/arm/mach-zx297520v3/zx297520v3-devices.c (這其中的資源會在probe中使用到) static struct resource wdt_res[] = { static struct platform_device zx297520v2_wdt_device = { struct platform_device *zx29_device_table[] __initdata={ … |
platform驅動代碼
drivers/watchdog/zx29_wdt.c中:
時鐘相關
watchdog_init->platform_driver_register(&zx29_wdt_driver)-|->zx29_wdt_probe -|->__devexit_p(zx29_wdt_remove) -|->zx29_wdt_shutdown -|->zx29_wdt_suspend -|->zx29_wdt_resume watchdog_exit->platform_driver_unregister(&zx29_wdt_driver) |
cpuidle
menuconfig
CONFIG_CPU_IDLE=y |
cpuidle driver
在本平臺中cpuidle driver沒有作為單一的模塊,二是放在zx_pm_init中進行。
drivers/soc/zte/power/zx-cpuidle.c
zx_pm_init-|->pm_debug_init->idle_debug_init (/sys/zte_pm/cpuidle/) |->zx_cpuidle_init-> |
drivers/soc/zte/power/zx297520v3-cpuidle.c中定義了cpuidle的狀態
#define WHOLE_CHIP_EXIT_LATENCY (4000) /* us */ #define LP2_DEFAULT_EXIT_LATENCY (500 + WHOLE_CHIP_EXIT_LATENCY) /* us */ #define LP2_DELTA_EXIT_LATENCY (100) /* us -- for timer setting refresh time, should > 2us.
static struct cpuidle_state zx297520v3_cpuidle_set[] __initdata = |
cpu_idle在start_kernel->rest_init->cpu_idle->while(1)中調用。cpu_idle調用cpuidle_idle_call:
cpu_idle->cpuidle_idle_call-|->trace_cpu_idle_rcuidle(next_state, dev->cpu); |->trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); |
cpuidle_curr_governor->select(drv, dev) 根據當前的governor,選擇合適的cpuidle狀態 entered_state = cpuidle_enter_state(dev, drv, next_state); cpuidle_curr_governor->reflect(dev, entered_state) |
cpuidle_curr_governor在cpuidle_switch_governor中進行設置,一個是在cpuidle_register_governor註冊時的時候,另一個是在store_current_governor通過sysfs節點設置cpuidle governor的時候。由於默認使用的是menu_governor,下面就來重點分析一下:
static struct cpuidle_governor menu_governor = {
.name = "menu",
.rating = 20,
.enable = menu_enable_device,
.select = menu_select,
.reflect = menu_reflect,
.owner = THIS_MODULE,
};
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
cpuidle_enter_state是執行根據governor確定的狀態執行,然後返回進入的狀態值。
cpuidle_enable_device設置cpuidle_enter_ops的值,cpuidle_enter_ops = drv->en_core_tk_irqen?cpuidle_enter_tk:cpuidle_enter;
cpuidle_enter調用target_state->enter(dev, drv, index);,指向zx_enter_idle。
zx_enter_idle-|->zx_pm_idle_prepare |
cpuidle core
cpuidle governors
cpufreq
cpufreq driver
drivers/soc/zte/power/zx-cpufreq.c
drvers/soc/zte/power/zx297520v3-cpufreq.c
設置cpufreq的DVFS數據,在struct zx_dvfs_info中。
struct zx_dvfs_info { |
cpufreq driver想初始化DVFS通過調用zx29xx_cpufreq_init。
static int zx297520v3_cpufreq_init(struct zx_dvfs_info *info) cpu_clk = clk_get(NULL, "cpu_clk"); info->freq_cur_idx = L1; cpufreq_driver_inited = 1; INIT_DELAYED_WORK_DEFERRABLE(&pm_freq_work, pm_freq_func);
return 0; |
cpu_clk如下:
static struct zx29_hwclk cpu_work = { CLK_ZX29_CONFIG(NULL, "cpu_clk", &cpu_work_clk), |
這裏通過zx297520v3_volt_table和zx297520v3_freq_table來達到OPP的概念,zx297520v3_set_frequency作為設置頻率的底層函數。
static unsigned int zx297520v3_volt_table[CPUFREQ_LEVEL_END] = { static struct cpufreq_frequency_table zx297520v3_freq_table[] = { 實際的頻率只有兩種,而且不可以調壓 |
cpufreq governors
cpufreq core
cpu hotplug
由於是單核CPU,所以不能使用hotplug功能。
wakelock
kernel/power/main.c
#ifdef CONFIG_PM_WAKELOCKS static ssize_t wake_lock_store(struct kobject *kobj, power_attr(wake_lock); static ssize_t wake_unlock_show(struct kobject *kobj, static ssize_t wake_unlock_store(struct kobject *kobj, power_attr(wake_unlock); #endif /* CONFIG_PM_WAKELOCKS */
static struct attribute * g[] = { |
kernel/power/wakelock.c
增加wakelock tracepoint
修改config.linux:
CONFIG_PM_DEBUG=y
CONFIG_PM_SLEEP_DEBUG=y
CONFIG_FTRACE=y
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_KPROBES=y
CONFIG_KPROBES_ON_FTRACE=y
|
修改include/trace/events/power.h,增加wakelock相關trace包括pm_wake_lock和pm_wake_unlock。
修改kernel/power/wakelock.c,在pm_wake_lock和pm_wake_unlock中添加trace。
Index: wakelock.c |
Runtime PM
Suspend and Resume
Clock、Regulator
一個Linux平臺PM功能初探