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