1 /* 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> 23 #include <linux/sched.h> 24 #include <linux/clk.h> 25 #include <linux/err.h> 26 #include <linux/io.h> 27 #include <linux/module.h> 28 29 #include <mach/clock.h> 30 #include <mach/board.h> 31 #include <mach/powerdomain.h> 32 #include <mach/clockdomain.h> 33 34 #include "prm.h" 35 #include "cm.h" 36 #include "pm.h" 37 38 int omap2_pm_debug; 39 40 #define DUMP_PRM_MOD_REG(mod, reg) \ 41 regs[reg_count].name = #mod "." #reg; \ 42 regs[reg_count++].val = prm_read_mod_reg(mod, reg) 43 #define DUMP_CM_MOD_REG(mod, reg) \ 44 regs[reg_count].name = #mod "." #reg; \ 45 regs[reg_count++].val = cm_read_mod_reg(mod, reg) 46 #define DUMP_PRM_REG(reg) \ 47 regs[reg_count].name = #reg; \ 48 regs[reg_count++].val = __raw_readl(reg) 49 #define DUMP_CM_REG(reg) \ 50 regs[reg_count].name = #reg; \ 51 regs[reg_count++].val = __raw_readl(reg) 52 #define DUMP_INTC_REG(reg, off) \ 53 regs[reg_count].name = #reg; \ 54 regs[reg_count++].val = __raw_readl(OMAP2_IO_ADDRESS(0x480fe000 + (off))) 55 56 static int __init pm_dbg_init(void); 57 58 void omap2_pm_dump(int mode, int resume, unsigned int us) 59 { 60 struct reg { 61 const char *name; 62 u32 val; 63 } regs[32]; 64 int reg_count = 0, i; 65 const char *s1 = NULL, *s2 = NULL; 66 67 if (!resume) { 68 #if 0 69 /* MPU */ 70 DUMP_PRM_MOD_REG(OCP_MOD, OMAP2_PRM_IRQENABLE_MPU_OFFSET); 71 DUMP_CM_MOD_REG(MPU_MOD, CM_CLKSTCTRL); 72 DUMP_PRM_MOD_REG(MPU_MOD, PM_PWSTCTRL); 73 DUMP_PRM_MOD_REG(MPU_MOD, PM_PWSTST); 74 DUMP_PRM_MOD_REG(MPU_MOD, PM_WKDEP); 75 #endif 76 #if 0 77 /* INTC */ 78 DUMP_INTC_REG(INTC_MIR0, 0x0084); 79 DUMP_INTC_REG(INTC_MIR1, 0x00a4); 80 DUMP_INTC_REG(INTC_MIR2, 0x00c4); 81 #endif 82 #if 0 83 DUMP_CM_MOD_REG(CORE_MOD, CM_FCLKEN1); 84 if (cpu_is_omap24xx()) { 85 DUMP_CM_MOD_REG(CORE_MOD, OMAP24XX_CM_FCLKEN2); 86 DUMP_PRM_MOD_REG(OMAP24XX_GR_MOD, 87 OMAP2_PRCM_CLKEMUL_CTRL_OFFSET); 88 DUMP_PRM_MOD_REG(OMAP24XX_GR_MOD, 89 OMAP2_PRCM_CLKSRC_CTRL_OFFSET); 90 } 91 DUMP_CM_MOD_REG(WKUP_MOD, CM_FCLKEN); 92 DUMP_CM_MOD_REG(CORE_MOD, CM_ICLKEN1); 93 DUMP_CM_MOD_REG(CORE_MOD, CM_ICLKEN2); 94 DUMP_CM_MOD_REG(WKUP_MOD, CM_ICLKEN); 95 DUMP_CM_MOD_REG(PLL_MOD, CM_CLKEN); 96 DUMP_CM_MOD_REG(PLL_MOD, CM_AUTOIDLE); 97 DUMP_PRM_MOD_REG(CORE_MOD, PM_PWSTST); 98 #endif 99 #if 0 100 /* DSP */ 101 if (cpu_is_omap24xx()) { 102 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_FCLKEN); 103 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_ICLKEN); 104 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_IDLEST); 105 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_AUTOIDLE); 106 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_CLKSEL); 107 DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_CLKSTCTRL); 108 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, RM_RSTCTRL); 109 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, RM_RSTST); 110 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, PM_PWSTCTRL); 111 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, PM_PWSTST); 112 } 113 #endif 114 } else { 115 DUMP_PRM_MOD_REG(CORE_MOD, PM_WKST1); 116 if (cpu_is_omap24xx()) 117 DUMP_PRM_MOD_REG(CORE_MOD, OMAP24XX_PM_WKST2); 118 DUMP_PRM_MOD_REG(WKUP_MOD, PM_WKST); 119 DUMP_PRM_MOD_REG(OCP_MOD, OMAP2_PRCM_IRQSTATUS_MPU_OFFSET); 120 #if 1 121 DUMP_INTC_REG(INTC_PENDING_IRQ0, 0x0098); 122 DUMP_INTC_REG(INTC_PENDING_IRQ1, 0x00b8); 123 DUMP_INTC_REG(INTC_PENDING_IRQ2, 0x00d8); 124 #endif 125 } 126 127 switch (mode) { 128 case 0: 129 s1 = "full"; 130 s2 = "retention"; 131 break; 132 case 1: 133 s1 = "MPU"; 134 s2 = "retention"; 135 break; 136 case 2: 137 s1 = "MPU"; 138 s2 = "idle"; 139 break; 140 } 141 142 if (!resume) 143 #ifdef CONFIG_NO_HZ 144 printk(KERN_INFO 145 "--- Going to %s %s (next timer after %u ms)\n", s1, s2, 146 jiffies_to_msecs(get_next_timer_interrupt(jiffies) - 147 jiffies)); 148 #else 149 printk(KERN_INFO "--- Going to %s %s\n", s1, s2); 150 #endif 151 else 152 printk(KERN_INFO "--- Woke up (slept for %u.%03u ms)\n", 153 us / 1000, us % 1000); 154 155 for (i = 0; i < reg_count; i++) 156 printk(KERN_INFO "%-20s: 0x%08x\n", regs[i].name, regs[i].val); 157 } 158 159 #ifdef CONFIG_DEBUG_FS 160 #include <linux/debugfs.h> 161 #include <linux/seq_file.h> 162 163 static void pm_dbg_regset_store(u32 *ptr); 164 165 struct dentry *pm_dbg_dir; 166 167 static int pm_dbg_init_done; 168 169 enum { 170 DEBUG_FILE_COUNTERS = 0, 171 DEBUG_FILE_TIMERS, 172 }; 173 174 struct pm_module_def { 175 char name[8]; /* Name of the module */ 176 short type; /* CM or PRM */ 177 unsigned short offset; 178 int low; /* First register address on this module */ 179 int high; /* Last register address on this module */ 180 }; 181 182 #define MOD_CM 0 183 #define MOD_PRM 1 184 185 static const struct pm_module_def *pm_dbg_reg_modules; 186 static const struct pm_module_def omap3_pm_reg_modules[] = { 187 { "IVA2", MOD_CM, OMAP3430_IVA2_MOD, 0, 0x4c }, 188 { "OCP", MOD_CM, OCP_MOD, 0, 0x10 }, 189 { "MPU", MOD_CM, MPU_MOD, 4, 0x4c }, 190 { "CORE", MOD_CM, CORE_MOD, 0, 0x4c }, 191 { "SGX", MOD_CM, OMAP3430ES2_SGX_MOD, 0, 0x4c }, 192 { "WKUP", MOD_CM, WKUP_MOD, 0, 0x40 }, 193 { "CCR", MOD_CM, PLL_MOD, 0, 0x70 }, 194 { "DSS", MOD_CM, OMAP3430_DSS_MOD, 0, 0x4c }, 195 { "CAM", MOD_CM, OMAP3430_CAM_MOD, 0, 0x4c }, 196 { "PER", MOD_CM, OMAP3430_PER_MOD, 0, 0x4c }, 197 { "EMU", MOD_CM, OMAP3430_EMU_MOD, 0x40, 0x54 }, 198 { "NEON", MOD_CM, OMAP3430_NEON_MOD, 0x20, 0x48 }, 199 { "USB", MOD_CM, OMAP3430ES2_USBHOST_MOD, 0, 0x4c }, 200 201 { "IVA2", MOD_PRM, OMAP3430_IVA2_MOD, 0x50, 0xfc }, 202 { "OCP", MOD_PRM, OCP_MOD, 4, 0x1c }, 203 { "MPU", MOD_PRM, MPU_MOD, 0x58, 0xe8 }, 204 { "CORE", MOD_PRM, CORE_MOD, 0x58, 0xf8 }, 205 { "SGX", MOD_PRM, OMAP3430ES2_SGX_MOD, 0x58, 0xe8 }, 206 { "WKUP", MOD_PRM, WKUP_MOD, 0xa0, 0xb0 }, 207 { "CCR", MOD_PRM, PLL_MOD, 0x40, 0x70 }, 208 { "DSS", MOD_PRM, OMAP3430_DSS_MOD, 0x58, 0xe8 }, 209 { "CAM", MOD_PRM, OMAP3430_CAM_MOD, 0x58, 0xe8 }, 210 { "PER", MOD_PRM, OMAP3430_PER_MOD, 0x58, 0xe8 }, 211 { "EMU", MOD_PRM, OMAP3430_EMU_MOD, 0x58, 0xe4 }, 212 { "GLBL", MOD_PRM, OMAP3430_GR_MOD, 0x20, 0xe4 }, 213 { "NEON", MOD_PRM, OMAP3430_NEON_MOD, 0x58, 0xe8 }, 214 { "USB", MOD_PRM, OMAP3430ES2_USBHOST_MOD, 0x58, 0xe8 }, 215 { "", 0, 0, 0, 0 }, 216 }; 217 218 #define PM_DBG_MAX_REG_SETS 4 219 220 static void *pm_dbg_reg_set[PM_DBG_MAX_REG_SETS]; 221 222 static int pm_dbg_get_regset_size(void) 223 { 224 static int regset_size; 225 226 if (regset_size == 0) { 227 int i = 0; 228 229 while (pm_dbg_reg_modules[i].name[0] != 0) { 230 regset_size += pm_dbg_reg_modules[i].high + 231 4 - pm_dbg_reg_modules[i].low; 232 i++; 233 } 234 } 235 return regset_size; 236 } 237 238 static int pm_dbg_show_regs(struct seq_file *s, void *unused) 239 { 240 int i, j; 241 unsigned long val; 242 int reg_set = (int)s->private; 243 u32 *ptr; 244 void *store = NULL; 245 int regs; 246 int linefeed; 247 248 if (reg_set == 0) { 249 store = kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL); 250 ptr = store; 251 pm_dbg_regset_store(ptr); 252 } else { 253 ptr = pm_dbg_reg_set[reg_set - 1]; 254 } 255 256 i = 0; 257 258 while (pm_dbg_reg_modules[i].name[0] != 0) { 259 regs = 0; 260 linefeed = 0; 261 if (pm_dbg_reg_modules[i].type == MOD_CM) 262 seq_printf(s, "MOD: CM_%s (%08x)\n", 263 pm_dbg_reg_modules[i].name, 264 (u32)(OMAP3430_CM_BASE + 265 pm_dbg_reg_modules[i].offset)); 266 else 267 seq_printf(s, "MOD: PRM_%s (%08x)\n", 268 pm_dbg_reg_modules[i].name, 269 (u32)(OMAP3430_PRM_BASE + 270 pm_dbg_reg_modules[i].offset)); 271 272 for (j = pm_dbg_reg_modules[i].low; 273 j <= pm_dbg_reg_modules[i].high; j += 4) { 274 val = *(ptr++); 275 if (val != 0) { 276 regs++; 277 if (linefeed) { 278 seq_printf(s, "\n"); 279 linefeed = 0; 280 } 281 seq_printf(s, " %02x => %08lx", j, val); 282 if (regs % 4 == 0) 283 linefeed = 1; 284 } 285 } 286 seq_printf(s, "\n"); 287 i++; 288 } 289 290 if (store != NULL) 291 kfree(store); 292 293 return 0; 294 } 295 296 static void pm_dbg_regset_store(u32 *ptr) 297 { 298 int i, j; 299 u32 val; 300 301 i = 0; 302 303 while (pm_dbg_reg_modules[i].name[0] != 0) { 304 for (j = pm_dbg_reg_modules[i].low; 305 j <= pm_dbg_reg_modules[i].high; j += 4) { 306 if (pm_dbg_reg_modules[i].type == MOD_CM) 307 val = cm_read_mod_reg( 308 pm_dbg_reg_modules[i].offset, j); 309 else 310 val = prm_read_mod_reg( 311 pm_dbg_reg_modules[i].offset, j); 312 *(ptr++) = val; 313 } 314 i++; 315 } 316 } 317 318 int pm_dbg_regset_save(int reg_set) 319 { 320 if (pm_dbg_reg_set[reg_set-1] == NULL) 321 return -EINVAL; 322 323 pm_dbg_regset_store(pm_dbg_reg_set[reg_set-1]); 324 325 return 0; 326 } 327 328 static const char pwrdm_state_names[][4] = { 329 "OFF", 330 "RET", 331 "INA", 332 "ON" 333 }; 334 335 void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) 336 { 337 s64 t; 338 339 if (!pm_dbg_init_done) 340 return ; 341 342 /* Update timer for previous state */ 343 t = sched_clock(); 344 345 pwrdm->state_timer[prev] += t - pwrdm->timer; 346 347 pwrdm->timer = t; 348 } 349 350 static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user) 351 { 352 struct seq_file *s = (struct seq_file *)user; 353 354 if (strcmp(clkdm->name, "emu_clkdm") == 0 || 355 strcmp(clkdm->name, "wkup_clkdm") == 0 || 356 strncmp(clkdm->name, "dpll", 4) == 0) 357 return 0; 358 359 seq_printf(s, "%s->%s (%d)", clkdm->name, 360 clkdm->pwrdm.ptr->name, 361 atomic_read(&clkdm->usecount)); 362 seq_printf(s, "\n"); 363 364 return 0; 365 } 366 367 static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user) 368 { 369 struct seq_file *s = (struct seq_file *)user; 370 int i; 371 372 if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || 373 strcmp(pwrdm->name, "wkup_pwrdm") == 0 || 374 strncmp(pwrdm->name, "dpll", 4) == 0) 375 return 0; 376 377 if (pwrdm->state != pwrdm_read_pwrst(pwrdm)) 378 printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n", 379 pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm)); 380 381 seq_printf(s, "%s (%s)", pwrdm->name, 382 pwrdm_state_names[pwrdm->state]); 383 for (i = 0; i < 4; i++) 384 seq_printf(s, ",%s:%d", pwrdm_state_names[i], 385 pwrdm->state_counter[i]); 386 387 seq_printf(s, "\n"); 388 389 return 0; 390 } 391 392 static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user) 393 { 394 struct seq_file *s = (struct seq_file *)user; 395 int i; 396 397 if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || 398 strcmp(pwrdm->name, "wkup_pwrdm") == 0 || 399 strncmp(pwrdm->name, "dpll", 4) == 0) 400 return 0; 401 402 pwrdm_state_switch(pwrdm); 403 404 seq_printf(s, "%s (%s)", pwrdm->name, 405 pwrdm_state_names[pwrdm->state]); 406 407 for (i = 0; i < 4; i++) 408 seq_printf(s, ",%s:%lld", pwrdm_state_names[i], 409 pwrdm->state_timer[i]); 410 411 seq_printf(s, "\n"); 412 return 0; 413 } 414 415 static int pm_dbg_show_counters(struct seq_file *s, void *unused) 416 { 417 pwrdm_for_each(pwrdm_dbg_show_counter, s); 418 clkdm_for_each(clkdm_dbg_show_counter, s); 419 420 return 0; 421 } 422 423 static int pm_dbg_show_timers(struct seq_file *s, void *unused) 424 { 425 pwrdm_for_each(pwrdm_dbg_show_timer, s); 426 return 0; 427 } 428 429 static int pm_dbg_open(struct inode *inode, struct file *file) 430 { 431 switch ((int)inode->i_private) { 432 case DEBUG_FILE_COUNTERS: 433 return single_open(file, pm_dbg_show_counters, 434 &inode->i_private); 435 case DEBUG_FILE_TIMERS: 436 default: 437 return single_open(file, pm_dbg_show_timers, 438 &inode->i_private); 439 }; 440 } 441 442 static int pm_dbg_reg_open(struct inode *inode, struct file *file) 443 { 444 return single_open(file, pm_dbg_show_regs, inode->i_private); 445 } 446 447 static const struct file_operations debug_fops = { 448 .open = pm_dbg_open, 449 .read = seq_read, 450 .llseek = seq_lseek, 451 .release = single_release, 452 }; 453 454 static const struct file_operations debug_reg_fops = { 455 .open = pm_dbg_reg_open, 456 .read = seq_read, 457 .llseek = seq_lseek, 458 .release = single_release, 459 }; 460 461 int pm_dbg_regset_init(int reg_set) 462 { 463 char name[2]; 464 465 if (!pm_dbg_init_done) 466 pm_dbg_init(); 467 468 if (reg_set < 1 || reg_set > PM_DBG_MAX_REG_SETS || 469 pm_dbg_reg_set[reg_set-1] != NULL) 470 return -EINVAL; 471 472 pm_dbg_reg_set[reg_set-1] = 473 kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL); 474 475 if (pm_dbg_reg_set[reg_set-1] == NULL) 476 return -ENOMEM; 477 478 if (pm_dbg_dir != NULL) { 479 sprintf(name, "%d", reg_set); 480 481 (void) debugfs_create_file(name, S_IRUGO, 482 pm_dbg_dir, (void *)reg_set, &debug_reg_fops); 483 } 484 485 return 0; 486 } 487 488 static int pwrdm_suspend_get(void *data, u64 *val) 489 { 490 *val = omap3_pm_get_suspend_state((struct powerdomain *)data); 491 492 if (*val >= 0) 493 return 0; 494 return *val; 495 } 496 497 static int pwrdm_suspend_set(void *data, u64 val) 498 { 499 return omap3_pm_set_suspend_state((struct powerdomain *)data, (int)val); 500 } 501 502 DEFINE_SIMPLE_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get, 503 pwrdm_suspend_set, "%llu\n"); 504 505 static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir) 506 { 507 int i; 508 s64 t; 509 struct dentry *d; 510 511 t = sched_clock(); 512 513 for (i = 0; i < 4; i++) 514 pwrdm->state_timer[i] = 0; 515 516 pwrdm->timer = t; 517 518 if (strncmp(pwrdm->name, "dpll", 4) == 0) 519 return 0; 520 521 d = debugfs_create_dir(pwrdm->name, (struct dentry *)dir); 522 523 (void) debugfs_create_file("suspend", S_IRUGO|S_IWUSR, d, 524 (void *)pwrdm, &pwrdm_suspend_fops); 525 526 return 0; 527 } 528 529 static int __init pm_dbg_init(void) 530 { 531 int i; 532 struct dentry *d; 533 char name[2]; 534 535 if (pm_dbg_init_done) 536 return 0; 537 538 if (cpu_is_omap34xx()) 539 pm_dbg_reg_modules = omap3_pm_reg_modules; 540 else { 541 printk(KERN_ERR "%s: only OMAP3 supported\n", __func__); 542 return -ENODEV; 543 } 544 545 d = debugfs_create_dir("pm_debug", NULL); 546 if (IS_ERR(d)) 547 return PTR_ERR(d); 548 549 (void) debugfs_create_file("count", S_IRUGO, 550 d, (void *)DEBUG_FILE_COUNTERS, &debug_fops); 551 (void) debugfs_create_file("time", S_IRUGO, 552 d, (void *)DEBUG_FILE_TIMERS, &debug_fops); 553 554 pwrdm_for_each(pwrdms_setup, (void *)d); 555 556 pm_dbg_dir = debugfs_create_dir("registers", d); 557 if (IS_ERR(pm_dbg_dir)) 558 return PTR_ERR(pm_dbg_dir); 559 560 (void) debugfs_create_file("current", S_IRUGO, 561 pm_dbg_dir, (void *)0, &debug_reg_fops); 562 563 for (i = 0; i < PM_DBG_MAX_REG_SETS; i++) 564 if (pm_dbg_reg_set[i] != NULL) { 565 sprintf(name, "%d", i+1); 566 (void) debugfs_create_file(name, S_IRUGO, 567 pm_dbg_dir, (void *)(i+1), &debug_reg_fops); 568 569 } 570 571 pm_dbg_init_done = 1; 572 573 return 0; 574 } 575 arch_initcall(pm_dbg_init); 576 577 #else 578 void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) {} 579 #endif 580