blob: 7383e85eac26f91e6fb0467d05dce63604e427de [file] [log] [blame]
Kevin Hilman8bd22942009-05-28 10:56:16 -07001/*
2 * OMAP Power Management debug routines
3 *
4 * Copyright (C) 2005 Texas Instruments, Inc.
5 * Copyright (C) 2006-2008 Nokia Corporation
6 *
7 * Written by:
8 * Richard Woodruff <r-woodruff2@ti.com>
9 * Tony Lindgren
10 * Juha Yrjola
11 * Amit Kucheria <amit.kucheria@nokia.com>
12 * Igor Stoppa <igor.stoppa@nokia.com>
13 * Jouni Hogander
14 *
15 * Based on pm.c for omap2
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License version 2 as
19 * published by the Free Software Foundation.
20 */
21
22#include <linux/kernel.h>
Peter 'p2' De Schrijver331b93f2008-10-15 18:13:48 +030023#include <linux/sched.h>
Kevin Hilman8bd22942009-05-28 10:56:16 -070024#include <linux/clk.h>
25#include <linux/err.h>
26#include <linux/io.h>
27
28#include <mach/clock.h>
29#include <mach/board.h>
Peter 'p2' De Schrijver331b93f2008-10-15 18:13:48 +030030#include <mach/powerdomain.h>
31#include <mach/clockdomain.h>
Kevin Hilman8bd22942009-05-28 10:56:16 -070032
33#include "prm.h"
34#include "cm.h"
35#include "pm.h"
36
37int omap2_pm_debug;
38
39#define DUMP_PRM_MOD_REG(mod, reg) \
40 regs[reg_count].name = #mod "." #reg; \
41 regs[reg_count++].val = prm_read_mod_reg(mod, reg)
42#define DUMP_CM_MOD_REG(mod, reg) \
43 regs[reg_count].name = #mod "." #reg; \
44 regs[reg_count++].val = cm_read_mod_reg(mod, reg)
45#define DUMP_PRM_REG(reg) \
46 regs[reg_count].name = #reg; \
47 regs[reg_count++].val = __raw_readl(reg)
48#define DUMP_CM_REG(reg) \
49 regs[reg_count].name = #reg; \
50 regs[reg_count++].val = __raw_readl(reg)
51#define DUMP_INTC_REG(reg, off) \
52 regs[reg_count].name = #reg; \
Tony Lindgren94113262009-08-28 10:50:33 -070053 regs[reg_count++].val = __raw_readl(OMAP2_IO_ADDRESS(0x480fe000 + (off)))
Kevin Hilman8bd22942009-05-28 10:56:16 -070054
55void omap2_pm_dump(int mode, int resume, unsigned int us)
56{
57 struct reg {
58 const char *name;
59 u32 val;
60 } regs[32];
61 int reg_count = 0, i;
62 const char *s1 = NULL, *s2 = NULL;
63
64 if (!resume) {
65#if 0
66 /* MPU */
67 DUMP_PRM_MOD_REG(OCP_MOD, OMAP2_PRM_IRQENABLE_MPU_OFFSET);
68 DUMP_CM_MOD_REG(MPU_MOD, CM_CLKSTCTRL);
69 DUMP_PRM_MOD_REG(MPU_MOD, PM_PWSTCTRL);
70 DUMP_PRM_MOD_REG(MPU_MOD, PM_PWSTST);
71 DUMP_PRM_MOD_REG(MPU_MOD, PM_WKDEP);
72#endif
73#if 0
74 /* INTC */
75 DUMP_INTC_REG(INTC_MIR0, 0x0084);
76 DUMP_INTC_REG(INTC_MIR1, 0x00a4);
77 DUMP_INTC_REG(INTC_MIR2, 0x00c4);
78#endif
79#if 0
80 DUMP_CM_MOD_REG(CORE_MOD, CM_FCLKEN1);
81 if (cpu_is_omap24xx()) {
82 DUMP_CM_MOD_REG(CORE_MOD, OMAP24XX_CM_FCLKEN2);
83 DUMP_PRM_MOD_REG(OMAP24XX_GR_MOD,
84 OMAP2_PRCM_CLKEMUL_CTRL_OFFSET);
85 DUMP_PRM_MOD_REG(OMAP24XX_GR_MOD,
86 OMAP2_PRCM_CLKSRC_CTRL_OFFSET);
87 }
88 DUMP_CM_MOD_REG(WKUP_MOD, CM_FCLKEN);
89 DUMP_CM_MOD_REG(CORE_MOD, CM_ICLKEN1);
90 DUMP_CM_MOD_REG(CORE_MOD, CM_ICLKEN2);
91 DUMP_CM_MOD_REG(WKUP_MOD, CM_ICLKEN);
92 DUMP_CM_MOD_REG(PLL_MOD, CM_CLKEN);
93 DUMP_CM_MOD_REG(PLL_MOD, CM_AUTOIDLE);
94 DUMP_PRM_MOD_REG(CORE_MOD, PM_PWSTST);
95#endif
96#if 0
97 /* DSP */
98 if (cpu_is_omap24xx()) {
99 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_FCLKEN);
100 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_ICLKEN);
101 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_IDLEST);
102 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_AUTOIDLE);
103 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_CLKSEL);
104 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_CLKSTCTRL);
105 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, RM_RSTCTRL);
106 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, RM_RSTST);
107 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, PM_PWSTCTRL);
108 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, PM_PWSTST);
109 }
110#endif
111 } else {
112 DUMP_PRM_MOD_REG(CORE_MOD, PM_WKST1);
113 if (cpu_is_omap24xx())
114 DUMP_PRM_MOD_REG(CORE_MOD, OMAP24XX_PM_WKST2);
115 DUMP_PRM_MOD_REG(WKUP_MOD, PM_WKST);
116 DUMP_PRM_MOD_REG(OCP_MOD, OMAP2_PRCM_IRQSTATUS_MPU_OFFSET);
117#if 1
118 DUMP_INTC_REG(INTC_PENDING_IRQ0, 0x0098);
119 DUMP_INTC_REG(INTC_PENDING_IRQ1, 0x00b8);
120 DUMP_INTC_REG(INTC_PENDING_IRQ2, 0x00d8);
121#endif
122 }
123
124 switch (mode) {
125 case 0:
126 s1 = "full";
127 s2 = "retention";
128 break;
129 case 1:
130 s1 = "MPU";
131 s2 = "retention";
132 break;
133 case 2:
134 s1 = "MPU";
135 s2 = "idle";
136 break;
137 }
138
139 if (!resume)
140#ifdef CONFIG_NO_HZ
141 printk(KERN_INFO
142 "--- Going to %s %s (next timer after %u ms)\n", s1, s2,
143 jiffies_to_msecs(get_next_timer_interrupt(jiffies) -
144 jiffies));
145#else
146 printk(KERN_INFO "--- Going to %s %s\n", s1, s2);
147#endif
148 else
149 printk(KERN_INFO "--- Woke up (slept for %u.%03u ms)\n",
150 us / 1000, us % 1000);
151
152 for (i = 0; i < reg_count; i++)
153 printk(KERN_INFO "%-20s: 0x%08x\n", regs[i].name, regs[i].val);
154}
Peter 'p2' De Schrijver331b93f2008-10-15 18:13:48 +0300155
156#ifdef CONFIG_DEBUG_FS
157#include <linux/debugfs.h>
158#include <linux/seq_file.h>
159
160struct dentry *pm_dbg_dir;
161
162static int pm_dbg_init_done;
163
164enum {
165 DEBUG_FILE_COUNTERS = 0,
166 DEBUG_FILE_TIMERS,
167};
168
169static const char pwrdm_state_names[][4] = {
170 "OFF",
171 "RET",
172 "INA",
173 "ON"
174};
175
176void 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
191static 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
208static 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
233static 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
256static 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
264static 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
270static 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
283static 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
290static 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
305static 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}
324late_initcall(pm_dbg_init);
325
326#else
327void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) {}
328#endif