pm-debug.c (5137B)
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 41static int pm_dbg_init_done; 42 43static int pm_dbg_init(void); 44 45static const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = { 46 "OFF", 47 "RET", 48 "INA", 49 "ON" 50}; 51 52void 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 67static 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 82static 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 111static 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 134static 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} 141DEFINE_SHOW_ATTRIBUTE(pm_dbg_counters); 142 143static 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} 148DEFINE_SHOW_ATTRIBUTE(pm_dbg_timers); 149 150static 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 163static 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 171DEFINE_DEBUGFS_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get, 172 pwrdm_suspend_set, "%llu\n"); 173 174static 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 197static int option_get(void *data, u64 *val) 198{ 199 u32 *option = data; 200 201 *val = *option; 202 203 return 0; 204} 205 206static 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 220DEFINE_SIMPLE_ATTRIBUTE(pm_dbg_option_fops, option_get, option_set, "%llu\n"); 221 222static 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} 242omap_arch_initcall(pm_dbg_init); 243 244#endif