162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP Power Management debug routines 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005 Texas Instruments, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2006-2008 Nokia Corporation 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Written by: 962306a36Sopenharmony_ci * Richard Woodruff <r-woodruff2@ti.com> 1062306a36Sopenharmony_ci * Tony Lindgren 1162306a36Sopenharmony_ci * Juha Yrjola 1262306a36Sopenharmony_ci * Amit Kucheria <amit.kucheria@nokia.com> 1362306a36Sopenharmony_ci * Igor Stoppa <igor.stoppa@nokia.com> 1462306a36Sopenharmony_ci * Jouni Hogander 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Based on pm.c for omap2 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/sched.h> 2162306a36Sopenharmony_ci#include <linux/sched/clock.h> 2262306a36Sopenharmony_ci#include <linux/clk.h> 2362306a36Sopenharmony_ci#include <linux/err.h> 2462306a36Sopenharmony_ci#include <linux/io.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "clock.h" 2962306a36Sopenharmony_ci#include "powerdomain.h" 3062306a36Sopenharmony_ci#include "clockdomain.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "soc.h" 3362306a36Sopenharmony_ci#include "cm2xxx_3xxx.h" 3462306a36Sopenharmony_ci#include "prm2xxx_3xxx.h" 3562306a36Sopenharmony_ci#include "pm.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 3862306a36Sopenharmony_ci#include <linux/debugfs.h> 3962306a36Sopenharmony_ci#include <linux/seq_file.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int pm_dbg_init_done; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int pm_dbg_init(void); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = { 4662306a36Sopenharmony_ci "OFF", 4762306a36Sopenharmony_ci "RET", 4862306a36Sopenharmony_ci "INA", 4962306a36Sopenharmony_ci "ON" 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_civoid pm_dbg_update_time(struct powerdomain *pwrdm, int prev) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci s64 t; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!pm_dbg_init_done) 5762306a36Sopenharmony_ci return ; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* Update timer for previous state */ 6062306a36Sopenharmony_ci t = sched_clock(); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci pwrdm->state_timer[prev] += t - pwrdm->timer; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci pwrdm->timer = t; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct seq_file *s = (struct seq_file *)user; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (strcmp(clkdm->name, "emu_clkdm") == 0 || 7262306a36Sopenharmony_ci strcmp(clkdm->name, "wkup_clkdm") == 0 || 7362306a36Sopenharmony_ci strncmp(clkdm->name, "dpll", 4) == 0) 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci seq_printf(s, "%s->%s (%d)\n", clkdm->name, clkdm->pwrdm.ptr->name, 7762306a36Sopenharmony_ci clkdm->usecount); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct seq_file *s = (struct seq_file *)user; 8562306a36Sopenharmony_ci int i; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || 8862306a36Sopenharmony_ci strcmp(pwrdm->name, "wkup_pwrdm") == 0 || 8962306a36Sopenharmony_ci strncmp(pwrdm->name, "dpll", 4) == 0) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (pwrdm->state != pwrdm_read_pwrst(pwrdm)) 9362306a36Sopenharmony_ci printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n", 9462306a36Sopenharmony_ci pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm)); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci seq_printf(s, "%s (%s)", pwrdm->name, 9762306a36Sopenharmony_ci pwrdm_state_names[pwrdm->state]); 9862306a36Sopenharmony_ci for (i = 0; i < PWRDM_MAX_PWRSTS; i++) 9962306a36Sopenharmony_ci seq_printf(s, ",%s:%d", pwrdm_state_names[i], 10062306a36Sopenharmony_ci pwrdm->state_counter[i]); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci seq_printf(s, ",RET-LOGIC-OFF:%d", pwrdm->ret_logic_off_counter); 10362306a36Sopenharmony_ci for (i = 0; i < pwrdm->banks; i++) 10462306a36Sopenharmony_ci seq_printf(s, ",RET-MEMBANK%d-OFF:%d", i + 1, 10562306a36Sopenharmony_ci pwrdm->ret_mem_off_counter[i]); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci seq_putc(s, '\n'); 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct seq_file *s = (struct seq_file *)user; 11462306a36Sopenharmony_ci int i; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || 11762306a36Sopenharmony_ci strcmp(pwrdm->name, "wkup_pwrdm") == 0 || 11862306a36Sopenharmony_ci strncmp(pwrdm->name, "dpll", 4) == 0) 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci pwrdm_state_switch(pwrdm); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci seq_printf(s, "%s (%s)", pwrdm->name, 12462306a36Sopenharmony_ci pwrdm_state_names[pwrdm->state]); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (i = 0; i < 4; i++) 12762306a36Sopenharmony_ci seq_printf(s, ",%s:%lld", pwrdm_state_names[i], 12862306a36Sopenharmony_ci pwrdm->state_timer[i]); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci seq_putc(s, '\n'); 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int pm_dbg_counters_show(struct seq_file *s, void *unused) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci pwrdm_for_each(pwrdm_dbg_show_counter, s); 13762306a36Sopenharmony_ci clkdm_for_each(clkdm_dbg_show_counter, s); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(pm_dbg_counters); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int pm_dbg_timers_show(struct seq_file *s, void *unused) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci pwrdm_for_each(pwrdm_dbg_show_timer, s); 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(pm_dbg_timers); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int pwrdm_suspend_get(void *data, u64 *val) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int ret = -EINVAL; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (cpu_is_omap34xx()) 15562306a36Sopenharmony_ci ret = omap3_pm_get_suspend_state((struct powerdomain *)data); 15662306a36Sopenharmony_ci *val = ret; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (ret >= 0) 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci return *val; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int pwrdm_suspend_set(void *data, u64 val) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci if (cpu_is_omap34xx()) 16662306a36Sopenharmony_ci return omap3_pm_set_suspend_state( 16762306a36Sopenharmony_ci (struct powerdomain *)data, (int)val); 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get, 17262306a36Sopenharmony_ci pwrdm_suspend_set, "%llu\n"); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci int i; 17762306a36Sopenharmony_ci s64 t; 17862306a36Sopenharmony_ci struct dentry *d; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci t = sched_clock(); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for (i = 0; i < 4; i++) 18362306a36Sopenharmony_ci pwrdm->state_timer[i] = 0; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci pwrdm->timer = t; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (strncmp(pwrdm->name, "dpll", 4) == 0) 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci d = debugfs_create_dir(pwrdm->name, (struct dentry *)dir); 19162306a36Sopenharmony_ci debugfs_create_file("suspend", S_IRUGO|S_IWUSR, d, pwrdm, 19262306a36Sopenharmony_ci &pwrdm_suspend_fops); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int option_get(void *data, u64 *val) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci u32 *option = data; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci *val = *option; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int option_set(void *data, u64 val) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci u32 *option = data; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci *option = val; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (option == &enable_off_mode) { 21362306a36Sopenharmony_ci if (cpu_is_omap34xx()) 21462306a36Sopenharmony_ci omap3_pm_off_mode_enable(val); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(pm_dbg_option_fops, option_get, option_set, "%llu\n"); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int __init pm_dbg_init(void) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct dentry *d; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (pm_dbg_init_done) 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci d = debugfs_create_dir("pm_debug", NULL); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci debugfs_create_file("count", 0444, d, NULL, &pm_dbg_counters_fops); 23262306a36Sopenharmony_ci debugfs_create_file("time", 0444, d, NULL, &pm_dbg_timers_fops); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci pwrdm_for_each(pwrdms_setup, (void *)d); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUSR, d, 23762306a36Sopenharmony_ci &enable_off_mode, &pm_dbg_option_fops); 23862306a36Sopenharmony_ci pm_dbg_init_done = 1; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ciomap_arch_initcall(pm_dbg_init); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci#endif 245