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/sched/clock.h> 25 #include <linux/clk.h> 26 #include <linux/err.h> 27 #include <linux/io.h> 28 #include <linux/module.h> 29 #include <linux/slab.h> 30 31 #include "clock.h" 32 #include "powerdomain.h" 33 #include "clockdomain.h" 34 35 #include "soc.h" 36 #include "cm2xxx_3xxx.h" 37 #include "prm2xxx_3xxx.h" 38 #include "pm.h" 39 40 u32 enable_off_mode; 41 42 #ifdef CONFIG_DEBUG_FS 43 #include <linux/debugfs.h> 44 #include <linux/seq_file.h> 45 46 static int pm_dbg_init_done; 47 48 static int pm_dbg_init(void); 49 50 static const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = { 51 "OFF", 52 "RET", 53 "INA", 54 "ON" 55 }; 56 57 void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) 58 { 59 s64 t; 60 61 if (!pm_dbg_init_done) 62 return ; 63 64 /* Update timer for previous state */ 65 t = sched_clock(); 66 67 pwrdm->state_timer[prev] += t - pwrdm->timer; 68 69 pwrdm->timer = t; 70 } 71 72 static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user) 73 { 74 struct seq_file *s = (struct seq_file *)user; 75 76 if (strcmp(clkdm->name, "emu_clkdm") == 0 || 77 strcmp(clkdm->name, "wkup_clkdm") == 0 || 78 strncmp(clkdm->name, "dpll", 4) == 0) 79 return 0; 80 81 seq_printf(s, "%s->%s (%d)\n", clkdm->name, clkdm->pwrdm.ptr->name, 82 clkdm->usecount); 83 84 return 0; 85 } 86 87 static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user) 88 { 89 struct seq_file *s = (struct seq_file *)user; 90 int i; 91 92 if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || 93 strcmp(pwrdm->name, "wkup_pwrdm") == 0 || 94 strncmp(pwrdm->name, "dpll", 4) == 0) 95 return 0; 96 97 if (pwrdm->state != pwrdm_read_pwrst(pwrdm)) 98 printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n", 99 pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm)); 100 101 seq_printf(s, "%s (%s)", pwrdm->name, 102 pwrdm_state_names[pwrdm->state]); 103 for (i = 0; i < PWRDM_MAX_PWRSTS; i++) 104 seq_printf(s, ",%s:%d", pwrdm_state_names[i], 105 pwrdm->state_counter[i]); 106 107 seq_printf(s, ",RET-LOGIC-OFF:%d", pwrdm->ret_logic_off_counter); 108 for (i = 0; i < pwrdm->banks; i++) 109 seq_printf(s, ",RET-MEMBANK%d-OFF:%d", i + 1, 110 pwrdm->ret_mem_off_counter[i]); 111 112 seq_putc(s, '\n'); 113 return 0; 114 } 115 116 static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user) 117 { 118 struct seq_file *s = (struct seq_file *)user; 119 int i; 120 121 if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || 122 strcmp(pwrdm->name, "wkup_pwrdm") == 0 || 123 strncmp(pwrdm->name, "dpll", 4) == 0) 124 return 0; 125 126 pwrdm_state_switch(pwrdm); 127 128 seq_printf(s, "%s (%s)", pwrdm->name, 129 pwrdm_state_names[pwrdm->state]); 130 131 for (i = 0; i < 4; i++) 132 seq_printf(s, ",%s:%lld", pwrdm_state_names[i], 133 pwrdm->state_timer[i]); 134 135 seq_putc(s, '\n'); 136 return 0; 137 } 138 139 static int pm_dbg_counters_show(struct seq_file *s, void *unused) 140 { 141 pwrdm_for_each(pwrdm_dbg_show_counter, s); 142 clkdm_for_each(clkdm_dbg_show_counter, s); 143 144 return 0; 145 } 146 DEFINE_SHOW_ATTRIBUTE(pm_dbg_counters); 147 148 static int pm_dbg_timers_show(struct seq_file *s, void *unused) 149 { 150 pwrdm_for_each(pwrdm_dbg_show_timer, s); 151 return 0; 152 } 153 DEFINE_SHOW_ATTRIBUTE(pm_dbg_timers); 154 155 static int pwrdm_suspend_get(void *data, u64 *val) 156 { 157 int ret = -EINVAL; 158 159 if (cpu_is_omap34xx()) 160 ret = omap3_pm_get_suspend_state((struct powerdomain *)data); 161 *val = ret; 162 163 if (ret >= 0) 164 return 0; 165 return *val; 166 } 167 168 static int pwrdm_suspend_set(void *data, u64 val) 169 { 170 if (cpu_is_omap34xx()) 171 return omap3_pm_set_suspend_state( 172 (struct powerdomain *)data, (int)val); 173 return -EINVAL; 174 } 175 176 DEFINE_SIMPLE_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get, 177 pwrdm_suspend_set, "%llu\n"); 178 179 static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir) 180 { 181 int i; 182 s64 t; 183 struct dentry *d; 184 185 t = sched_clock(); 186 187 for (i = 0; i < 4; i++) 188 pwrdm->state_timer[i] = 0; 189 190 pwrdm->timer = t; 191 192 if (strncmp(pwrdm->name, "dpll", 4) == 0) 193 return 0; 194 195 d = debugfs_create_dir(pwrdm->name, (struct dentry *)dir); 196 if (d) 197 (void) debugfs_create_file("suspend", S_IRUGO|S_IWUSR, d, 198 (void *)pwrdm, &pwrdm_suspend_fops); 199 200 return 0; 201 } 202 203 static int option_get(void *data, u64 *val) 204 { 205 u32 *option = data; 206 207 *val = *option; 208 209 return 0; 210 } 211 212 static int option_set(void *data, u64 val) 213 { 214 u32 *option = data; 215 216 *option = val; 217 218 if (option == &enable_off_mode) { 219 if (cpu_is_omap34xx()) 220 omap3_pm_off_mode_enable(val); 221 } 222 223 return 0; 224 } 225 226 DEFINE_SIMPLE_ATTRIBUTE(pm_dbg_option_fops, option_get, option_set, "%llu\n"); 227 228 static int __init pm_dbg_init(void) 229 { 230 struct dentry *d; 231 232 if (pm_dbg_init_done) 233 return 0; 234 235 d = debugfs_create_dir("pm_debug", NULL); 236 if (!d) 237 return -EINVAL; 238 239 (void) debugfs_create_file("count", 0444, d, NULL, &pm_dbg_counters_fops); 240 (void) debugfs_create_file("time", 0444, d, NULL, &pm_dbg_timers_fops); 241 242 pwrdm_for_each(pwrdms_setup, (void *)d); 243 244 (void) debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUSR, d, 245 &enable_off_mode, &pm_dbg_option_fops); 246 pm_dbg_init_done = 1; 247 248 return 0; 249 } 250 omap_arch_initcall(pm_dbg_init); 251 252 #endif 253