aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/pm-debug.c
diff options
context:
space:
mode:
authorTero Kristo <tero.kristo@nokia.com>2008-10-29 07:31:24 -0400
committerKevin Hilman <khilman@deeprootsystems.com>2009-09-02 18:08:25 -0400
commit2811d6b3237c9b77007a6b2b10ee5b576da0574e (patch)
tree84febe1c3cdd2ece0b1f097d64cf9c597b1c833a /arch/arm/mach-omap2/pm-debug.c
parent6199ab2690ca6f44f838603da079b3faa837e502 (diff)
OMAP: PM debug: Add PRCM register dump support
Allows dumping out current register contents from the debug filesystem, and also allows user to add arbitrary register save points into code. Current register contents are available under debugfs at: [debugfs]/pm_debug/registers/current To add a save point, do following: From module init (or somewhere before the save call, called only once): pm_dbg_init_regset(n); // n=1..4, allocates memory for dump area #n From arbitrary code location: pm_dbg_regset_save(n); // n=1..4, saves registers to dump area #n After this, the register dump can be seen under [debugfs]/pm_debug/registers/n Signed-off-by: Tero Kristo <tero.kristo@nokia.com> Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
Diffstat (limited to 'arch/arm/mach-omap2/pm-debug.c')
-rw-r--r--arch/arm/mach-omap2/pm-debug.c226
1 files changed, 225 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c
index 7383e85eac26..982aa67f2685 100644
--- a/arch/arm/mach-omap2/pm-debug.c
+++ b/arch/arm/mach-omap2/pm-debug.c
@@ -52,6 +52,8 @@ int omap2_pm_debug;
52 regs[reg_count].name = #reg; \ 52 regs[reg_count].name = #reg; \
53 regs[reg_count++].val = __raw_readl(OMAP2_IO_ADDRESS(0x480fe000 + (off))) 53 regs[reg_count++].val = __raw_readl(OMAP2_IO_ADDRESS(0x480fe000 + (off)))
54 54
55static int __init pm_dbg_init(void);
56
55void omap2_pm_dump(int mode, int resume, unsigned int us) 57void omap2_pm_dump(int mode, int resume, unsigned int us)
56{ 58{
57 struct reg { 59 struct reg {
@@ -157,6 +159,8 @@ void omap2_pm_dump(int mode, int resume, unsigned int us)
157#include <linux/debugfs.h> 159#include <linux/debugfs.h>
158#include <linux/seq_file.h> 160#include <linux/seq_file.h>
159 161
162static void pm_dbg_regset_store(u32 *ptr);
163
160struct dentry *pm_dbg_dir; 164struct dentry *pm_dbg_dir;
161 165
162static int pm_dbg_init_done; 166static int pm_dbg_init_done;
@@ -166,6 +170,160 @@ enum {
166 DEBUG_FILE_TIMERS, 170 DEBUG_FILE_TIMERS,
167}; 171};
168 172
173struct pm_module_def {
174 char name[8]; /* Name of the module */
175 short type; /* CM or PRM */
176 unsigned short offset;
177 int low; /* First register address on this module */
178 int high; /* Last register address on this module */
179};
180
181#define MOD_CM 0
182#define MOD_PRM 1
183
184static const struct pm_module_def *pm_dbg_reg_modules;
185static const struct pm_module_def omap3_pm_reg_modules[] = {
186 { "IVA2", MOD_CM, OMAP3430_IVA2_MOD, 0, 0x4c },
187 { "OCP", MOD_CM, OCP_MOD, 0, 0x10 },
188 { "MPU", MOD_CM, MPU_MOD, 4, 0x4c },
189 { "CORE", MOD_CM, CORE_MOD, 0, 0x4c },
190 { "SGX", MOD_CM, OMAP3430ES2_SGX_MOD, 0, 0x4c },
191 { "WKUP", MOD_CM, WKUP_MOD, 0, 0x40 },
192 { "CCR", MOD_CM, PLL_MOD, 0, 0x70 },
193 { "DSS", MOD_CM, OMAP3430_DSS_MOD, 0, 0x4c },
194 { "CAM", MOD_CM, OMAP3430_CAM_MOD, 0, 0x4c },
195 { "PER", MOD_CM, OMAP3430_PER_MOD, 0, 0x4c },
196 { "EMU", MOD_CM, OMAP3430_EMU_MOD, 0x40, 0x54 },
197 { "NEON", MOD_CM, OMAP3430_NEON_MOD, 0x20, 0x48 },
198 { "USB", MOD_CM, OMAP3430ES2_USBHOST_MOD, 0, 0x4c },
199
200 { "IVA2", MOD_PRM, OMAP3430_IVA2_MOD, 0x50, 0xfc },
201 { "OCP", MOD_PRM, OCP_MOD, 4, 0x1c },
202 { "MPU", MOD_PRM, MPU_MOD, 0x58, 0xe8 },
203 { "CORE", MOD_PRM, CORE_MOD, 0x58, 0xf8 },
204 { "SGX", MOD_PRM, OMAP3430ES2_SGX_MOD, 0x58, 0xe8 },
205 { "WKUP", MOD_PRM, WKUP_MOD, 0xa0, 0xb0 },
206 { "CCR", MOD_PRM, PLL_MOD, 0x40, 0x70 },
207 { "DSS", MOD_PRM, OMAP3430_DSS_MOD, 0x58, 0xe8 },
208 { "CAM", MOD_PRM, OMAP3430_CAM_MOD, 0x58, 0xe8 },
209 { "PER", MOD_PRM, OMAP3430_PER_MOD, 0x58, 0xe8 },
210 { "EMU", MOD_PRM, OMAP3430_EMU_MOD, 0x58, 0xe4 },
211 { "GLBL", MOD_PRM, OMAP3430_GR_MOD, 0x20, 0xe4 },
212 { "NEON", MOD_PRM, OMAP3430_NEON_MOD, 0x58, 0xe8 },
213 { "USB", MOD_PRM, OMAP3430ES2_USBHOST_MOD, 0x58, 0xe8 },
214 { "", 0, 0, 0, 0 },
215};
216
217#define PM_DBG_MAX_REG_SETS 4
218
219static void *pm_dbg_reg_set[PM_DBG_MAX_REG_SETS];
220
221static int pm_dbg_get_regset_size(void)
222{
223 static int regset_size;
224
225 if (regset_size == 0) {
226 int i = 0;
227
228 while (pm_dbg_reg_modules[i].name[0] != 0) {
229 regset_size += pm_dbg_reg_modules[i].high +
230 4 - pm_dbg_reg_modules[i].low;
231 i++;
232 }
233 }
234 return regset_size;
235}
236
237static int pm_dbg_show_regs(struct seq_file *s, void *unused)
238{
239 int i, j;
240 unsigned long val;
241 int reg_set = (int)s->private;
242 u32 *ptr;
243 void *store = NULL;
244 int regs;
245 int linefeed;
246
247 if (reg_set == 0) {
248 store = kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL);
249 ptr = store;
250 pm_dbg_regset_store(ptr);
251 } else {
252 ptr = pm_dbg_reg_set[reg_set - 1];
253 }
254
255 i = 0;
256
257 while (pm_dbg_reg_modules[i].name[0] != 0) {
258 regs = 0;
259 linefeed = 0;
260 if (pm_dbg_reg_modules[i].type == MOD_CM)
261 seq_printf(s, "MOD: CM_%s (%08x)\n",
262 pm_dbg_reg_modules[i].name,
263 (u32)(OMAP3430_CM_BASE +
264 pm_dbg_reg_modules[i].offset));
265 else
266 seq_printf(s, "MOD: PRM_%s (%08x)\n",
267 pm_dbg_reg_modules[i].name,
268 (u32)(OMAP3430_PRM_BASE +
269 pm_dbg_reg_modules[i].offset));
270
271 for (j = pm_dbg_reg_modules[i].low;
272 j <= pm_dbg_reg_modules[i].high; j += 4) {
273 val = *(ptr++);
274 if (val != 0) {
275 regs++;
276 if (linefeed) {
277 seq_printf(s, "\n");
278 linefeed = 0;
279 }
280 seq_printf(s, " %02x => %08lx", j, val);
281 if (regs % 4 == 0)
282 linefeed = 1;
283 }
284 }
285 seq_printf(s, "\n");
286 i++;
287 }
288
289 if (store != NULL)
290 kfree(store);
291
292 return 0;
293}
294
295static void pm_dbg_regset_store(u32 *ptr)
296{
297 int i, j;
298 u32 val;
299
300 i = 0;
301
302 while (pm_dbg_reg_modules[i].name[0] != 0) {
303 for (j = pm_dbg_reg_modules[i].low;
304 j <= pm_dbg_reg_modules[i].high; j += 4) {
305 if (pm_dbg_reg_modules[i].type == MOD_CM)
306 val = cm_read_mod_reg(
307 pm_dbg_reg_modules[i].offset, j);
308 else
309 val = prm_read_mod_reg(
310 pm_dbg_reg_modules[i].offset, j);
311 *(ptr++) = val;
312 }
313 i++;
314 }
315}
316
317int pm_dbg_regset_save(int reg_set)
318{
319 if (pm_dbg_reg_set[reg_set-1] == NULL)
320 return -EINVAL;
321
322 pm_dbg_regset_store(pm_dbg_reg_set[reg_set-1]);
323
324 return 0;
325}
326
169static const char pwrdm_state_names[][4] = { 327static const char pwrdm_state_names[][4] = {
170 "OFF", 328 "OFF",
171 "RET", 329 "RET",
@@ -280,6 +438,11 @@ static int pm_dbg_open(struct inode *inode, struct file *file)
280 }; 438 };
281} 439}
282 440
441static int pm_dbg_reg_open(struct inode *inode, struct file *file)
442{
443 return single_open(file, pm_dbg_show_regs, inode->i_private);
444}
445
283static const struct file_operations debug_fops = { 446static const struct file_operations debug_fops = {
284 .open = pm_dbg_open, 447 .open = pm_dbg_open,
285 .read = seq_read, 448 .read = seq_read,
@@ -287,6 +450,40 @@ static const struct file_operations debug_fops = {
287 .release = single_release, 450 .release = single_release,
288}; 451};
289 452
453static const struct file_operations debug_reg_fops = {
454 .open = pm_dbg_reg_open,
455 .read = seq_read,
456 .llseek = seq_lseek,
457 .release = single_release,
458};
459
460int pm_dbg_regset_init(int reg_set)
461{
462 char name[2];
463
464 if (!pm_dbg_init_done)
465 pm_dbg_init();
466
467 if (reg_set < 1 || reg_set > PM_DBG_MAX_REG_SETS ||
468 pm_dbg_reg_set[reg_set-1] != NULL)
469 return -EINVAL;
470
471 pm_dbg_reg_set[reg_set-1] =
472 kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL);
473
474 if (pm_dbg_reg_set[reg_set-1] == NULL)
475 return -ENOMEM;
476
477 if (pm_dbg_dir != NULL) {
478 sprintf(name, "%d", reg_set);
479
480 (void) debugfs_create_file(name, S_IRUGO,
481 pm_dbg_dir, (void *)reg_set, &debug_reg_fops);
482 }
483
484 return 0;
485}
486
290static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) 487static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
291{ 488{
292 int i; 489 int i;
@@ -304,8 +501,20 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
304 501
305static int __init pm_dbg_init(void) 502static int __init pm_dbg_init(void)
306{ 503{
504 int i;
307 struct dentry *d; 505 struct dentry *d;
506 char name[2];
308 507
508 if (pm_dbg_init_done)
509 return 0;
510
511 if (cpu_is_omap34xx())
512 pm_dbg_reg_modules = omap3_pm_reg_modules;
513 else {
514 printk(KERN_ERR "%s: only OMAP3 supported\n", __func__);
515 return -ENODEV;
516 }
517
309 d = debugfs_create_dir("pm_debug", NULL); 518 d = debugfs_create_dir("pm_debug", NULL);
310 if (IS_ERR(d)) 519 if (IS_ERR(d))
311 return PTR_ERR(d); 520 return PTR_ERR(d);
@@ -317,11 +526,26 @@ static int __init pm_dbg_init(void)
317 526
318 pwrdm_for_each(pwrdms_setup, NULL); 527 pwrdm_for_each(pwrdms_setup, NULL);
319 528
529 pm_dbg_dir = debugfs_create_dir("registers", d);
530 if (IS_ERR(pm_dbg_dir))
531 return PTR_ERR(pm_dbg_dir);
532
533 (void) debugfs_create_file("current", S_IRUGO,
534 pm_dbg_dir, (void *)0, &debug_reg_fops);
535
536 for (i = 0; i < PM_DBG_MAX_REG_SETS; i++)
537 if (pm_dbg_reg_set[i] != NULL) {
538 sprintf(name, "%d", i+1);
539 (void) debugfs_create_file(name, S_IRUGO,
540 pm_dbg_dir, (void *)(i+1), &debug_reg_fops);
541
542 }
543
320 pm_dbg_init_done = 1; 544 pm_dbg_init_done = 1;
321 545
322 return 0; 546 return 0;
323} 547}
324late_initcall(pm_dbg_init); 548arch_initcall(pm_dbg_init);
325 549
326#else 550#else
327void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) {} 551void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) {}