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