标签:cal ror rabl mutex tor ptr menu 数据 dex
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
/************************************************************************** * ap wdt *************************************************************************** */ char* lsp_wdt_wclk_parents[2] = { NAME_CLK(main_clk_32k), NAME_CLK(main_clk_26m) };
static struct zx29_hwclk ap_wdt_apb = { .clk_en_reg ={ZX_LSP_CRPM_BASE+0x40, 0, 1}, .clk_div_reg ={ZX_LSP_CRPM_BASE+0x40, 0, 0}, .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x40, 11, 1}, }; DEFINE_ZX29_CLK(ap_wdt_apb,peripheral_clk_ops,0,NULL);
static struct zx29_hwclk ap_wdt_work = { .clk_en_reg ={ZX_LSP_CRPM_BASE+0x40, 1, 1}, .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x40, 4, 1}, .clk_div_reg ={ZX_LSP_CRPM_BASE+0x40, 12, 4}, .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x40, 10, 1}, }; DEFINE_ZX29_CLK(ap_wdt_work,peripheral_clk_ops,2,lsp_wdt_wclk_parents);
struct clk_lookup periph_clocks_lookups[] = { /* dev_id name clk struct */ … CLK_ZX29_CONFIG("zx29_ap_wdt.0", "work_clk", &ap_wdt_work_clk), 表示设备名称zx29_ap_wdt.0下的work_clk CLK_ZX29_CONFIG("zx29_ap_wdt.0", "apb_clk", &ap_wdt_apb_clk), …
};
|
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[] = { [0] = { .start = (u32)ZX_AP_WDT_BASE, .end = (u32)ZX_AP_WDT_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = WDT_INT, .end = WDT_INT, .flags = IORESOURCE_IRQ, }, };
static struct platform_device zx297520v2_wdt_device = { .name = "zx29_ap_wdt", .id = 0, .resource = wdt_res, .num_resources = ARRAY_SIZE(wdt_res), };
struct platform_device *zx29_device_table[] __initdata={ /* -------------------------------------------------------------------- * ---------- for solution integration department --------- start * -------------------------------------------------------------------- */ … #ifdef CONFIG_ZX29_WATCHDOG &zx297520v2_wdt_device, #endif
… }
|
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 CONFIG_CPU_IDLE_GOV_LADDER=y CONFIG_CPU_IDLE_GOV_MENU=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_MIN_POWER_OFF_TIME (500) /* us */
#define LP2_DELTA_EXIT_LATENCY (100) /* us -- for timer setting refresh time, should > 2us.
static struct cpuidle_state zx297520v3_cpuidle_set[] __initdata = { /* LP3 -- wfi */ [ZX_IDLE_CSTATE_LP3] = { .enter = zx_enter_idle, .exit_latency = 2, .target_residency = 5, .flags = CPUIDLE_FLAG_TIME_VALID, .name = "LP3", .desc = "clock gating(WFI)", }, /* LP2 -- POWEROFF */ [ZX_IDLE_CSTATE_LP2] = { .enter = zx_enter_idle, .exit_latency = LP2_DEFAULT_EXIT_LATENCY, 4500us .target_residency = LP2_DEFAULT_EXIT_LATENCY+LP2_MIN_POWER_OFF_TIME, 5000us .flags = CPUIDLE_FLAG_TIME_VALID, .name = "LP2", .desc = "POWEROFF", }, };
|
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 |->zx_pm_idle_enter-|->zx_enter_deep_idle(ZX_IDLE_CSTATE_LP2) |->cpu_do_idle(ZX_IDLE_CSTATE_LP3) |->dev->last_residency
|
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 { unsigned int freq_cur_idx; unsigned int pll_safe_idx; unsigned int max_support_idx; unsigned int min_support_idx; struct clk *cpu_clk; unsigned int *volt_table; struct cpufreq_frequency_table *freq_table; int (*set_freq)(unsigned int, unsigned int); };
|
cpufreq driver想初始化DVFS通过调用zx29xx_cpufreq_init。
|
static int zx297520v3_cpufreq_init(struct zx_dvfs_info *info) { if(cpufreq_driver_inited) return 0;
cpu_clk = clk_get(NULL, "cpu_clk"); if (IS_ERR(cpu_clk)) { pr_info("[CPUFREQ] get cpu_clk error \n"); return PTR_ERR(cpu_clk); }
info->freq_cur_idx = L1; info->pll_safe_idx = L1; info->max_support_idx = max_support_idx; info->min_support_idx = min_support_idx; info->cpu_clk = cpu_clk; info->volt_table = zx297520v3_volt_table; info->freq_table = zx297520v3_freq_table; info->set_freq = zx297520v3_set_frequency;
cpufreq_driver_inited = 1;
INIT_DELAYED_WORK_DEFERRABLE(&pm_freq_work, pm_freq_func); schedule_delayed_work(&pm_freq_work, PM_FREQ_DELAY); pr_info("[CPUFREQ] zx297520v2_cpufreq_init ok \n"); return 0; }
static int __init zx297520v3_freq_register(void) { zx29xx_cpufreq_init = zx297520v3_cpufreq_init;
return 0; }
|
cpu_clk如下:
|
static struct zx29_hwclk cpu_work = { /* reg_addr bit_offset bit_size */ .clk_en_reg ={0, 0, 0}, .clk_sel_reg={ZX_MATRIX_CRM_BASE+0x40, 0, 2}, .clk_div_reg={0, 0, 0}, }; DEFINE_ZX29_CLK(cpu_work,cpu_clk_ops,ARRAY_SIZE(cpu_clk_parents),cpu_clk_parents);
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] = { 1250000, 1150000, 1050000, /*975000, 950000,*/ };
static struct cpufreq_frequency_table zx297520v3_freq_table[] = { 实际的频率只有两种,而且不可以调压 {L0, 624*1000}, {L1, 312*1000}, //{L2, 156*1000}, {0, CPUFREQ_TABLE_END}, };
|
cpufreq governors
cpufreq core
cpu hotplug
由于是单核CPU,所以不能使用hotplug功能。
wakelock
kernel/power/main.c
|
#ifdef CONFIG_PM_WAKELOCKS static ssize_t wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return pm_show_wakelocks(buf, true); }
static ssize_t wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { int error = pm_wake_lock(buf); return error ? error : n; }
power_attr(wake_lock);
static ssize_t wake_unlock_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return pm_show_wakelocks(buf, false); }
static ssize_t wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { int error = pm_wake_unlock(buf); return error ? error : n; }
power_attr(wake_unlock);
#endif /* CONFIG_PM_WAKELOCKS */
static struct attribute * g[] = { … #ifdef CONFIG_PM_WAKELOCKS &wake_lock_attr.attr, &wake_unlock_attr.attr, #endif … NULL, };
|
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 =================================================================== --- wakelock.c (revision 1887) +++ wakelock.c (working copy) @@ -16,7 +16,9 @@ #include <linux/list.h> #include <linux/rbtree.h> #include <linux/slab.h> +#include <trace/events/power.h> + static DEFINE_MUTEX(wakelocks_lock); struct wakelock { @@ -217,7 +219,7 @@ } else { __pm_stay_awake(&wl->ws); } - + trace_pm_wake_lock(buf); wakelocks_lru_most_recent(wl); out: @@ -249,6 +251,7 @@ goto out; } __pm_relax(&wl->ws); + trace_pm_wake_unlock(buf); wakelocks_lru_most_recent(wl); wakelocks_gc();
|
Runtime PM
Suspend and Resume
Clock、Regulator
一个Linux平台PM功能初探
标签:cal ror rabl mutex tor ptr menu 数据 dex
原文地址:http://www.cnblogs.com/arnoldlu/p/6894158.html