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 #include <linux/slab.h> 29 30 #include <plat/clock.h> 31 #include <plat/board.h> 32 #include <plat/powerdomain.h> 33 #include <plat/clockdomain.h> 34 35 #include "prm.h" 36 #include "cm.h" 37 #include "pm.h" 38 39 int omap2_pm_debug; 40 41 #define DUMP_PRM_MOD_REG(mod, reg) \ 42 regs[reg_count].name = #mod "." #reg; \ 43 regs[reg_count++].val = prm_read_mod_reg(mod, reg) 44 #define DUMP_CM_MOD_REG(mod, reg) \ 45 regs[reg_count].name = #mod "." #reg; \ 46 regs[reg_count++].val = cm_read_mod_reg(mod, reg) 47 #define DUMP_PRM_REG(reg) \ 48 regs[reg_count].name = #reg; \ 49 regs[reg_count++].val = __raw_readl(reg) 50 #define DUMP_CM_REG(reg) \ 51 regs[reg_count].name = #reg; \ 52 regs[reg_count++].val = __raw_readl(reg) 53 #define DUMP_INTC_REG(reg, off) \ 54 regs[reg_count].name = #reg; \ 55 regs[reg_count++].val = \ 56 __raw_readl(OMAP2_L4_IO_ADDRESS(0x480fe000 + (off))) 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, OMAP2_CM_CLKSTCTRL); 72 DUMP_PRM_MOD_REG(MPU_MOD, OMAP2_PM_PWSTCTRL); 73 DUMP_PRM_MOD_REG(MPU_MOD, OMAP2_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, OMAP2_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, OMAP2_CM_CLKSTCTRL); 108 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, OMAP2_RM_RSTCTRL); 109 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, OMAP2_RM_RSTST); 110 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, OMAP2_PM_PWSTCTRL); 111 DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, OMAP2_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 static int __init pm_dbg_init(void); 170 171 enum { 172 DEBUG_FILE_COUNTERS = 0, 173 DEBUG_FILE_TIMERS, 174 }; 175 176 struct pm_module_def { 177 char name[8]; /* Name of the module */ 178 short type; /* CM or PRM */ 179 unsigned short offset; 180 int low; /* First register address on this module */ 181 int high; /* Last register address on this module */ 182 }; 183 184 #define MOD_CM 0 185 #define MOD_PRM 1 186 187 static const struct pm_module_def *pm_dbg_reg_modules; 188 static const struct pm_module_def omap3_pm_reg_modules[] = { 189 { "IVA2", MOD_CM, OMAP3430_IVA2_MOD, 0, 0x4c }, 190 { "OCP", MOD_CM, OCP_MOD, 0, 0x10 }, 191 { "MPU", MOD_CM, MPU_MOD, 4, 0x4c }, 192 { "CORE", MOD_CM, CORE_MOD, 0, 0x4c }, 193 { "SGX", MOD_CM, OMAP3430ES2_SGX_MOD, 0, 0x4c }, 194 { "WKUP", MOD_CM, WKUP_MOD, 0, 0x40 }, 195 { "CCR", MOD_CM, PLL_MOD, 0, 0x70 }, 196 { "DSS", MOD_CM, OMAP3430_DSS_MOD, 0, 0x4c }, 197 { "CAM", MOD_CM, OMAP3430_CAM_MOD, 0, 0x4c }, 198 { "PER", MOD_CM, OMAP3430_PER_MOD, 0, 0x4c }, 199 { "EMU", MOD_CM, OMAP3430_EMU_MOD, 0x40, 0x54 }, 200 { "NEON", MOD_CM, OMAP3430_NEON_MOD, 0x20, 0x48 }, 201 { "USB", MOD_CM, OMAP3430ES2_USBHOST_MOD, 0, 0x4c }, 202 203 { "IVA2", MOD_PRM, OMAP3430_IVA2_MOD, 0x50, 0xfc }, 204 { "OCP", MOD_PRM, OCP_MOD, 4, 0x1c }, 205 { "MPU", MOD_PRM, MPU_MOD, 0x58, 0xe8 }, 206 { "CORE", MOD_PRM, CORE_MOD, 0x58, 0xf8 }, 207 { "SGX", MOD_PRM, OMAP3430ES2_SGX_MOD, 0x58, 0xe8 }, 208 { "WKUP", MOD_PRM, WKUP_MOD, 0xa0, 0xb0 }, 209 { "CCR", MOD_PRM, PLL_MOD, 0x40, 0x70 }, 210 { "DSS", MOD_PRM, OMAP3430_DSS_MOD, 0x58, 0xe8 }, 211 { "CAM", MOD_PRM, OMAP3430_CAM_MOD, 0x58, 0xe8 }, 212 { "PER", MOD_PRM, OMAP3430_PER_MOD, 0x58, 0xe8 }, 213 { "EMU", MOD_PRM, OMAP3430_EMU_MOD, 0x58, 0xe4 }, 214 { "GLBL", MOD_PRM, OMAP3430_GR_MOD, 0x20, 0xe4 }, 215 { "NEON", MOD_PRM, OMAP3430_NEON_MOD, 0x58, 0xe8 }, 216 { "USB", MOD_PRM, OMAP3430ES2_USBHOST_MOD, 0x58, 0xe8 }, 217 { "", 0, 0, 0, 0 }, 218 }; 219 220 #define PM_DBG_MAX_REG_SETS 4 221 222 static void *pm_dbg_reg_set[PM_DBG_MAX_REG_SETS]; 223 224 static int pm_dbg_get_regset_size(void) 225 { 226 static int regset_size; 227 228 if (regset_size == 0) { 229 int i = 0; 230 231 while (pm_dbg_reg_modules[i].name[0] != 0) { 232 regset_size += pm_dbg_reg_modules[i].high + 233 4 - pm_dbg_reg_modules[i].low; 234 i++; 235 } 236 } 237 return regset_size; 238 } 239 240 static int pm_dbg_show_regs(struct seq_file *s, void *unused) 241 { 242 int i, j; 243 unsigned long val; 244 int reg_set = (int)s->private; 245 u32 *ptr; 246 void *store = NULL; 247 int regs; 248 int linefeed; 249 250 if (reg_set == 0) { 251 store = kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL); 252 ptr = store; 253 pm_dbg_regset_store(ptr); 254 } else { 255 ptr = pm_dbg_reg_set[reg_set - 1]; 256 } 257 258 i = 0; 259 260 while (pm_dbg_reg_modules[i].name[0] != 0) { 261 regs = 0; 262 linefeed = 0; 263 if (pm_dbg_reg_modules[i].type == MOD_CM) 264 seq_printf(s, "MOD: CM_%s (%08x)\n", 265 pm_dbg_reg_modules[i].name, 266 (u32)(OMAP3430_CM_BASE + 267 pm_dbg_reg_modules[i].offset)); 268 else 269 seq_printf(s, "MOD: PRM_%s (%08x)\n", 270 pm_dbg_reg_modules[i].name, 271 (u32)(OMAP3430_PRM_BASE + 272 pm_dbg_reg_modules[i].offset)); 273 274 for (j = pm_dbg_reg_modules[i].low; 275 j <= pm_dbg_reg_modules[i].high; j += 4) { 276 val = *(ptr++); 277 if (val != 0) { 278 regs++; 279 if (linefeed) { 280 seq_printf(s, "\n"); 281 linefeed = 0; 282 } 283 seq_printf(s, " %02x => %08lx", j, val); 284 if (regs % 4 == 0) 285 linefeed = 1; 286 } 287 } 288 seq_printf(s, "\n"); 289 i++; 290 } 291 292 if (store != NULL) 293 kfree(store); 294 295 return 0; 296 } 297 298 static void pm_dbg_regset_store(u32 *ptr) 299 { 300 int i, j; 301 u32 val; 302 303 i = 0; 304 305 while (pm_dbg_reg_modules[i].name[0] != 0) { 306 for (j = pm_dbg_reg_modules[i].low; 307 j <= pm_dbg_reg_modules[i].high; j += 4) { 308 if (pm_dbg_reg_modules[i].type == MOD_CM) 309 val = cm_read_mod_reg( 310 pm_dbg_reg_modules[i].offset, j); 311 else 312 val = prm_read_mod_reg( 313 pm_dbg_reg_modules[i].offset, j); 314 *(ptr++) = val; 315 } 316 i++; 317 } 318 } 319 320 int pm_dbg_regset_save(int reg_set) 321 { 322 if (pm_dbg_reg_set[reg_set-1] == NULL) 323 return -EINVAL; 324 325 pm_dbg_regset_store(pm_dbg_reg_set[reg_set-1]); 326 327 return 0; 328 } 329 330 static const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = { 331 "OFF", 332 "RET", 333 "INA", 334 "ON" 335 }; 336 337 void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) 338 { 339 s64 t; 340 341 if (!pm_dbg_init_done) 342 return ; 343 344 /* Update timer for previous state */ 345 t = sched_clock(); 346 347 pwrdm->state_timer[prev] += t - pwrdm->timer; 348 349 pwrdm->timer = t; 350 } 351 352 static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user) 353 { 354 struct seq_file *s = (struct seq_file *)user; 355 356 if (strcmp(clkdm->name, "emu_clkdm") == 0 || 357 strcmp(clkdm->name, "wkup_clkdm") == 0 || 358 strncmp(clkdm->name, "dpll", 4) == 0) 359 return 0; 360 361 seq_printf(s, "%s->%s (%d)", clkdm->name, 362 clkdm->pwrdm.ptr->name, 363 atomic_read(&clkdm->usecount)); 364 seq_printf(s, "\n"); 365 366 return 0; 367 } 368 369 static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user) 370 { 371 struct seq_file *s = (struct seq_file *)user; 372 int i; 373 374 if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || 375 strcmp(pwrdm->name, "wkup_pwrdm") == 0 || 376 strncmp(pwrdm->name, "dpll", 4) == 0) 377 return 0; 378 379 if (pwrdm->state != pwrdm_read_pwrst(pwrdm)) 380 printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n", 381 pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm)); 382 383 seq_printf(s, "%s (%s)", pwrdm->name, 384 pwrdm_state_names[pwrdm->state]); 385 for (i = 0; i < PWRDM_MAX_PWRSTS; i++) 386 seq_printf(s, ",%s:%d", pwrdm_state_names[i], 387 pwrdm->state_counter[i]); 388 389 seq_printf(s, ",RET-LOGIC-OFF:%d", pwrdm->ret_logic_off_counter); 390 for (i = 0; i < pwrdm->banks; i++) 391 seq_printf(s, ",RET-MEMBANK%d-OFF:%d", i + 1, 392 pwrdm->ret_mem_off_counter[i]); 393 394 seq_printf(s, "\n"); 395 396 return 0; 397 } 398 399 static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user) 400 { 401 struct seq_file *s = (struct seq_file *)user; 402 int i; 403 404 if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || 405 strcmp(pwrdm->name, "wkup_pwrdm") == 0 || 406 strncmp(pwrdm->name, "dpll", 4) == 0) 407 return 0; 408 409 pwrdm_state_switch(pwrdm); 410 411 seq_printf(s, "%s (%s)", pwrdm->name, 412 pwrdm_state_names[pwrdm->state]); 413 414 for (i = 0; i < 4; i++) 415 seq_printf(s, ",%s:%lld", pwrdm_state_names[i], 416 pwrdm->state_timer[i]); 417 418 seq_printf(s, "\n"); 419 return 0; 420 } 421 422 static int pm_dbg_show_counters(struct seq_file *s, void *unused) 423 { 424 pwrdm_for_each(pwrdm_dbg_show_counter, s); 425 clkdm_for_each(clkdm_dbg_show_counter, s); 426 427 return 0; 428 } 429 430 static int pm_dbg_show_timers(struct seq_file *s, void *unused) 431 { 432 pwrdm_for_each(pwrdm_dbg_show_timer, s); 433 return 0; 434 } 435 436 static int pm_dbg_open(struct inode *inode, struct file *file) 437 { 438 switch ((int)inode->i_private) { 439 case DEBUG_FILE_COUNTERS: 440 return single_open(file, pm_dbg_show_counters, 441 &inode->i_private); 442 case DEBUG_FILE_TIMERS: 443 default: 444 return single_open(file, pm_dbg_show_timers, 445 &inode->i_private); 446 }; 447 } 448 449 static int pm_dbg_reg_open(struct inode *inode, struct file *file) 450 { 451 return single_open(file, pm_dbg_show_regs, inode->i_private); 452 } 453 454 static const struct file_operations debug_fops = { 455 .open = pm_dbg_open, 456 .read = seq_read, 457 .llseek = seq_lseek, 458 .release = single_release, 459 }; 460 461 static const struct file_operations debug_reg_fops = { 462 .open = pm_dbg_reg_open, 463 .read = seq_read, 464 .llseek = seq_lseek, 465 .release = single_release, 466 }; 467 468 int pm_dbg_regset_init(int reg_set) 469 { 470 char name[2]; 471 472 if (!pm_dbg_init_done) 473 pm_dbg_init(); 474 475 if (reg_set < 1 || reg_set > PM_DBG_MAX_REG_SETS || 476 pm_dbg_reg_set[reg_set-1] != NULL) 477 return -EINVAL; 478 479 pm_dbg_reg_set[reg_set-1] = 480 kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL); 481 482 if (pm_dbg_reg_set[reg_set-1] == NULL) 483 return -ENOMEM; 484 485 if (pm_dbg_dir != NULL) { 486 sprintf(name, "%d", reg_set); 487 488 (void) debugfs_create_file(name, S_IRUGO, 489 pm_dbg_dir, (void *)reg_set, &debug_reg_fops); 490 } 491 492 return 0; 493 } 494 495 static int pwrdm_suspend_get(void *data, u64 *val) 496 { 497 int ret; 498 ret = omap3_pm_get_suspend_state((struct powerdomain *)data); 499 *val = ret; 500 501 if (ret >= 0) 502 return 0; 503 return *val; 504 } 505 506 static int pwrdm_suspend_set(void *data, u64 val) 507 { 508 return omap3_pm_set_suspend_state((struct powerdomain *)data, (int)val); 509 } 510 511 DEFINE_SIMPLE_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get, 512 pwrdm_suspend_set, "%llu\n"); 513 514 static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir) 515 { 516 int i; 517 s64 t; 518 struct dentry *d; 519 520 t = sched_clock(); 521 522 for (i = 0; i < 4; i++) 523 pwrdm->state_timer[i] = 0; 524 525 pwrdm->timer = t; 526 527 if (strncmp(pwrdm->name, "dpll", 4) == 0) 528 return 0; 529 530 d = debugfs_create_dir(pwrdm->name, (struct dentry *)dir); 531 532 (void) debugfs_create_file("suspend", S_IRUGO|S_IWUSR, d, 533 (void *)pwrdm, &pwrdm_suspend_fops); 534 535 return 0; 536 } 537 538 static int option_get(void *data, u64 *val) 539 { 540 u32 *option = data; 541 542 *val = *option; 543 544 return 0; 545 } 546 547 static int option_set(void *data, u64 val) 548 { 549 u32 *option = data; 550 551 *option = val; 552 553 if (option == &enable_off_mode) 554 omap3_pm_off_mode_enable(val); 555 556 return 0; 557 } 558 559 DEFINE_SIMPLE_ATTRIBUTE(pm_dbg_option_fops, option_get, option_set, "%llu\n"); 560 561 static int __init pm_dbg_init(void) 562 { 563 int i; 564 struct dentry *d; 565 char name[2]; 566 567 if (pm_dbg_init_done) 568 return 0; 569 570 if (cpu_is_omap34xx()) 571 pm_dbg_reg_modules = omap3_pm_reg_modules; 572 else { 573 printk(KERN_ERR "%s: only OMAP3 supported\n", __func__); 574 return -ENODEV; 575 } 576 577 d = debugfs_create_dir("pm_debug", NULL); 578 if (IS_ERR(d)) 579 return PTR_ERR(d); 580 581 (void) debugfs_create_file("count", S_IRUGO, 582 d, (void *)DEBUG_FILE_COUNTERS, &debug_fops); 583 (void) debugfs_create_file("time", S_IRUGO, 584 d, (void *)DEBUG_FILE_TIMERS, &debug_fops); 585 586 pwrdm_for_each(pwrdms_setup, (void *)d); 587 588 pm_dbg_dir = debugfs_create_dir("registers", d); 589 if (IS_ERR(pm_dbg_dir)) 590 return PTR_ERR(pm_dbg_dir); 591 592 (void) debugfs_create_file("current", S_IRUGO, 593 pm_dbg_dir, (void *)0, &debug_reg_fops); 594 595 for (i = 0; i < PM_DBG_MAX_REG_SETS; i++) 596 if (pm_dbg_reg_set[i] != NULL) { 597 sprintf(name, "%d", i+1); 598 (void) debugfs_create_file(name, S_IRUGO, 599 pm_dbg_dir, (void *)(i+1), &debug_reg_fops); 600 601 } 602 603 (void) debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUGO, d, 604 &enable_off_mode, &pm_dbg_option_fops); 605 (void) debugfs_create_file("sleep_while_idle", S_IRUGO | S_IWUGO, d, 606 &sleep_while_idle, &pm_dbg_option_fops); 607 (void) debugfs_create_file("wakeup_timer_seconds", S_IRUGO | S_IWUGO, d, 608 &wakeup_timer_seconds, &pm_dbg_option_fops); 609 pm_dbg_init_done = 1; 610 611 return 0; 612 } 613 arch_initcall(pm_dbg_init); 614 615 #endif 616