diff options
author | Peter 'p2' De Schrijver <peter.de-schrijver@nokia.com> | 2008-10-15 11:13:48 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@deeprootsystems.com> | 2009-09-02 18:08:24 -0400 |
commit | 331b93f41dff21c8f95709032cb184fb82bf2003 (patch) | |
tree | 6945e875909ae2571bd3344c978395414e61781c | |
parent | a23456e9b02b3fae0fc78cb33fad69803a50e5bc (diff) |
OMAP: PM: Add pm-debug counters
This patch provides the debugfs entries and a function which will be
called by the PM code to register the time spent per domain per
state. Also some new fields are added to the powerdomain struct to
keep the time information.
NOTE: As of v2.6.29, using getnstimeofday() after drivers are
suspended is no longer safe since the timekeeping subsystem is also
suspended as part of the suspend process. Instead use sched_clock()
which on OMAP returns the 32k SYNC timer in nanoseconds.
Also, do not print out status for meta powerdomains (dpll*)
Signed-off-by: Peter 'p2' De Schrijver <peter.de-schrijver@nokia.com>
Signed-off-by: Tero Kristo <tero.kristo@nokia.com>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
-rw-r--r-- | arch/arm/mach-omap2/pm-debug.c | 178 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm.h | 4 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/mach/powerdomain.h | 5 |
3 files changed, 186 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c index da3a53f8ccc8..7383e85eac26 100644 --- a/arch/arm/mach-omap2/pm-debug.c +++ b/arch/arm/mach-omap2/pm-debug.c | |||
@@ -20,13 +20,15 @@ | |||
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/timer.h> | 23 | #include <linux/sched.h> |
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/err.h> | 25 | #include <linux/err.h> |
26 | #include <linux/io.h> | 26 | #include <linux/io.h> |
27 | 27 | ||
28 | #include <mach/clock.h> | 28 | #include <mach/clock.h> |
29 | #include <mach/board.h> | 29 | #include <mach/board.h> |
30 | #include <mach/powerdomain.h> | ||
31 | #include <mach/clockdomain.h> | ||
30 | 32 | ||
31 | #include "prm.h" | 33 | #include "prm.h" |
32 | #include "cm.h" | 34 | #include "cm.h" |
@@ -150,3 +152,177 @@ void omap2_pm_dump(int mode, int resume, unsigned int us) | |||
150 | for (i = 0; i < reg_count; i++) | 152 | for (i = 0; i < reg_count; i++) |
151 | printk(KERN_INFO "%-20s: 0x%08x\n", regs[i].name, regs[i].val); | 153 | printk(KERN_INFO "%-20s: 0x%08x\n", regs[i].name, regs[i].val); |
152 | } | 154 | } |
155 | |||
156 | #ifdef CONFIG_DEBUG_FS | ||
157 | #include <linux/debugfs.h> | ||
158 | #include <linux/seq_file.h> | ||
159 | |||
160 | struct dentry *pm_dbg_dir; | ||
161 | |||
162 | static int pm_dbg_init_done; | ||
163 | |||
164 | enum { | ||
165 | DEBUG_FILE_COUNTERS = 0, | ||
166 | DEBUG_FILE_TIMERS, | ||
167 | }; | ||
168 | |||
169 | static const char pwrdm_state_names[][4] = { | ||
170 | "OFF", | ||
171 | "RET", | ||
172 | "INA", | ||
173 | "ON" | ||
174 | }; | ||
175 | |||
176 | void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) | ||
177 | { | ||
178 | s64 t; | ||
179 | |||
180 | if (!pm_dbg_init_done) | ||
181 | return ; | ||
182 | |||
183 | /* Update timer for previous state */ | ||
184 | t = sched_clock(); | ||
185 | |||
186 | pwrdm->state_timer[prev] += t - pwrdm->timer; | ||
187 | |||
188 | pwrdm->timer = t; | ||
189 | } | ||
190 | |||
191 | static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user) | ||
192 | { | ||
193 | struct seq_file *s = (struct seq_file *)user; | ||
194 | |||
195 | if (strcmp(clkdm->name, "emu_clkdm") == 0 || | ||
196 | strcmp(clkdm->name, "wkup_clkdm") == 0 || | ||
197 | strncmp(clkdm->name, "dpll", 4) == 0) | ||
198 | return 0; | ||
199 | |||
200 | seq_printf(s, "%s->%s (%d)", clkdm->name, | ||
201 | clkdm->pwrdm.ptr->name, | ||
202 | atomic_read(&clkdm->usecount)); | ||
203 | seq_printf(s, "\n"); | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user) | ||
209 | { | ||
210 | struct seq_file *s = (struct seq_file *)user; | ||
211 | int i; | ||
212 | |||
213 | if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || | ||
214 | strcmp(pwrdm->name, "wkup_pwrdm") == 0 || | ||
215 | strncmp(pwrdm->name, "dpll", 4) == 0) | ||
216 | return 0; | ||
217 | |||
218 | if (pwrdm->state != pwrdm_read_pwrst(pwrdm)) | ||
219 | printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n", | ||
220 | pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm)); | ||
221 | |||
222 | seq_printf(s, "%s (%s)", pwrdm->name, | ||
223 | pwrdm_state_names[pwrdm->state]); | ||
224 | for (i = 0; i < 4; i++) | ||
225 | seq_printf(s, ",%s:%d", pwrdm_state_names[i], | ||
226 | pwrdm->state_counter[i]); | ||
227 | |||
228 | seq_printf(s, "\n"); | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user) | ||
234 | { | ||
235 | struct seq_file *s = (struct seq_file *)user; | ||
236 | int i; | ||
237 | |||
238 | if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || | ||
239 | strcmp(pwrdm->name, "wkup_pwrdm") == 0 || | ||
240 | strncmp(pwrdm->name, "dpll", 4) == 0) | ||
241 | return 0; | ||
242 | |||
243 | pwrdm_state_switch(pwrdm); | ||
244 | |||
245 | seq_printf(s, "%s (%s)", pwrdm->name, | ||
246 | pwrdm_state_names[pwrdm->state]); | ||
247 | |||
248 | for (i = 0; i < 4; i++) | ||
249 | seq_printf(s, ",%s:%lld", pwrdm_state_names[i], | ||
250 | pwrdm->state_timer[i]); | ||
251 | |||
252 | seq_printf(s, "\n"); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static int pm_dbg_show_counters(struct seq_file *s, void *unused) | ||
257 | { | ||
258 | pwrdm_for_each(pwrdm_dbg_show_counter, s); | ||
259 | clkdm_for_each(clkdm_dbg_show_counter, s); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int pm_dbg_show_timers(struct seq_file *s, void *unused) | ||
265 | { | ||
266 | pwrdm_for_each(pwrdm_dbg_show_timer, s); | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int pm_dbg_open(struct inode *inode, struct file *file) | ||
271 | { | ||
272 | switch ((int)inode->i_private) { | ||
273 | case DEBUG_FILE_COUNTERS: | ||
274 | return single_open(file, pm_dbg_show_counters, | ||
275 | &inode->i_private); | ||
276 | case DEBUG_FILE_TIMERS: | ||
277 | default: | ||
278 | return single_open(file, pm_dbg_show_timers, | ||
279 | &inode->i_private); | ||
280 | }; | ||
281 | } | ||
282 | |||
283 | static const struct file_operations debug_fops = { | ||
284 | .open = pm_dbg_open, | ||
285 | .read = seq_read, | ||
286 | .llseek = seq_lseek, | ||
287 | .release = single_release, | ||
288 | }; | ||
289 | |||
290 | static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) | ||
291 | { | ||
292 | int i; | ||
293 | s64 t; | ||
294 | |||
295 | t = sched_clock(); | ||
296 | |||
297 | for (i = 0; i < 4; i++) | ||
298 | pwrdm->state_timer[i] = 0; | ||
299 | |||
300 | pwrdm->timer = t; | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static int __init pm_dbg_init(void) | ||
306 | { | ||
307 | struct dentry *d; | ||
308 | |||
309 | d = debugfs_create_dir("pm_debug", NULL); | ||
310 | if (IS_ERR(d)) | ||
311 | return PTR_ERR(d); | ||
312 | |||
313 | (void) debugfs_create_file("count", S_IRUGO, | ||
314 | d, (void *)DEBUG_FILE_COUNTERS, &debug_fops); | ||
315 | (void) debugfs_create_file("time", S_IRUGO, | ||
316 | d, (void *)DEBUG_FILE_TIMERS, &debug_fops); | ||
317 | |||
318 | pwrdm_for_each(pwrdms_setup, NULL); | ||
319 | |||
320 | pm_dbg_init_done = 1; | ||
321 | |||
322 | return 0; | ||
323 | } | ||
324 | late_initcall(pm_dbg_init); | ||
325 | |||
326 | #else | ||
327 | void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) {} | ||
328 | #endif | ||
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index 21201cd4117b..4589db1c8af8 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h | |||
@@ -11,12 +11,16 @@ | |||
11 | #ifndef __ARCH_ARM_MACH_OMAP2_PM_H | 11 | #ifndef __ARCH_ARM_MACH_OMAP2_PM_H |
12 | #define __ARCH_ARM_MACH_OMAP2_PM_H | 12 | #define __ARCH_ARM_MACH_OMAP2_PM_H |
13 | 13 | ||
14 | #include <mach/powerdomain.h> | ||
15 | |||
14 | #ifdef CONFIG_PM_DEBUG | 16 | #ifdef CONFIG_PM_DEBUG |
15 | extern void omap2_pm_dump(int mode, int resume, unsigned int us); | 17 | extern void omap2_pm_dump(int mode, int resume, unsigned int us); |
16 | extern int omap2_pm_debug; | 18 | extern int omap2_pm_debug; |
19 | extern void pm_dbg_update_time(struct powerdomain *pwrdm, int prev); | ||
17 | #else | 20 | #else |
18 | #define omap2_pm_dump(mode, resume, us) do {} while (0); | 21 | #define omap2_pm_dump(mode, resume, us) do {} while (0); |
19 | #define omap2_pm_debug 0 | 22 | #define omap2_pm_debug 0 |
23 | #define pm_dbg_update_time(pwrdm, prev) do {} while (0); | ||
20 | #endif /* CONFIG_PM_DEBUG */ | 24 | #endif /* CONFIG_PM_DEBUG */ |
21 | 25 | ||
22 | extern void omap24xx_idle_loop_suspend(void); | 26 | extern void omap24xx_idle_loop_suspend(void); |
diff --git a/arch/arm/plat-omap/include/mach/powerdomain.h b/arch/arm/plat-omap/include/mach/powerdomain.h index de03f3dbbf3d..6271d8556a40 100644 --- a/arch/arm/plat-omap/include/mach/powerdomain.h +++ b/arch/arm/plat-omap/include/mach/powerdomain.h | |||
@@ -119,6 +119,11 @@ struct powerdomain { | |||
119 | 119 | ||
120 | int state; | 120 | int state; |
121 | unsigned state_counter[4]; | 121 | unsigned state_counter[4]; |
122 | |||
123 | #ifdef CONFIG_PM_DEBUG | ||
124 | s64 timer; | ||
125 | s64 state_timer[4]; | ||
126 | #endif | ||
122 | }; | 127 | }; |
123 | 128 | ||
124 | 129 | ||