OMAP: PM: Add pm-debug counters
authorPeter 'p2' De Schrijver <peter.de-schrijver@nokia.com>
Wed, 15 Oct 2008 15:13:48 +0000 (18:13 +0300)
committerKevin Hilman <khilman@deeprootsystems.com>
Wed, 2 Sep 2009 22:08:24 +0000 (15:08 -0700)
This patch provides the debugfs entries and a function which will be
called by the PM code to register the time spent per domain per
state. Also some new fields are added to the powerdomain struct to
keep the time information.

NOTE: As of v2.6.29, using getnstimeofday() after drivers are
suspended is no longer safe since the timekeeping subsystem is also
suspended as part of the suspend process.  Instead use sched_clock()
which on OMAP returns the 32k SYNC timer in nanoseconds.

Also, do not print out status for meta powerdomains (dpll*)

Signed-off-by: Peter 'p2' De Schrijver <peter.de-schrijver@nokia.com>
Signed-off-by: Tero Kristo <tero.kristo@nokia.com>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
arch/arm/mach-omap2/pm-debug.c
arch/arm/mach-omap2/pm.h
arch/arm/plat-omap/include/mach/powerdomain.h

index da3a53f8ccc8005e6efb36ab35827d0d2a48f5c8..7383e85eac26f91e6fb0467d05dce63604e427de 100644 (file)
  */
 
 #include <linux/kernel.h>
-#include <linux/timer.h>
+#include <linux/sched.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
 
 #include <mach/clock.h>
 #include <mach/board.h>
+#include <mach/powerdomain.h>
+#include <mach/clockdomain.h>
 
 #include "prm.h"
 #include "cm.h"
@@ -150,3 +152,177 @@ void omap2_pm_dump(int mode, int resume, unsigned int us)
        for (i = 0; i < reg_count; i++)
                printk(KERN_INFO "%-20s: 0x%08x\n", regs[i].name, regs[i].val);
 }
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+struct dentry *pm_dbg_dir;
+
+static int pm_dbg_init_done;
+
+enum {
+       DEBUG_FILE_COUNTERS = 0,
+       DEBUG_FILE_TIMERS,
+};
+
+static const char pwrdm_state_names[][4] = {
+       "OFF",
+       "RET",
+       "INA",
+       "ON"
+};
+
+void pm_dbg_update_time(struct powerdomain *pwrdm, int prev)
+{
+       s64 t;
+
+       if (!pm_dbg_init_done)
+               return ;
+
+       /* Update timer for previous state */
+       t = sched_clock();
+
+       pwrdm->state_timer[prev] += t - pwrdm->timer;
+
+       pwrdm->timer = t;
+}
+
+static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user)
+{
+       struct seq_file *s = (struct seq_file *)user;
+
+       if (strcmp(clkdm->name, "emu_clkdm") == 0 ||
+               strcmp(clkdm->name, "wkup_clkdm") == 0 ||
+               strncmp(clkdm->name, "dpll", 4) == 0)
+               return 0;
+
+       seq_printf(s, "%s->%s (%d)", clkdm->name,
+                       clkdm->pwrdm.ptr->name,
+                       atomic_read(&clkdm->usecount));
+       seq_printf(s, "\n");
+
+       return 0;
+}
+
+static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user)
+{
+       struct seq_file *s = (struct seq_file *)user;
+       int i;
+
+       if (strcmp(pwrdm->name, "emu_pwrdm") == 0 ||
+               strcmp(pwrdm->name, "wkup_pwrdm") == 0 ||
+               strncmp(pwrdm->name, "dpll", 4) == 0)
+               return 0;
+
+       if (pwrdm->state != pwrdm_read_pwrst(pwrdm))
+               printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n",
+                       pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm));
+
+       seq_printf(s, "%s (%s)", pwrdm->name,
+                       pwrdm_state_names[pwrdm->state]);
+       for (i = 0; i < 4; i++)
+               seq_printf(s, ",%s:%d", pwrdm_state_names[i],
+                       pwrdm->state_counter[i]);
+
+       seq_printf(s, "\n");
+
+       return 0;
+}
+
+static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user)
+{
+       struct seq_file *s = (struct seq_file *)user;
+       int i;
+
+       if (strcmp(pwrdm->name, "emu_pwrdm") == 0 ||
+               strcmp(pwrdm->name, "wkup_pwrdm") == 0 ||
+               strncmp(pwrdm->name, "dpll", 4) == 0)
+               return 0;
+
+       pwrdm_state_switch(pwrdm);
+
+       seq_printf(s, "%s (%s)", pwrdm->name,
+               pwrdm_state_names[pwrdm->state]);
+
+       for (i = 0; i < 4; i++)
+               seq_printf(s, ",%s:%lld", pwrdm_state_names[i],
+                       pwrdm->state_timer[i]);
+
+       seq_printf(s, "\n");
+       return 0;
+}
+
+static int pm_dbg_show_counters(struct seq_file *s, void *unused)
+{
+       pwrdm_for_each(pwrdm_dbg_show_counter, s);
+       clkdm_for_each(clkdm_dbg_show_counter, s);
+
+       return 0;
+}
+
+static int pm_dbg_show_timers(struct seq_file *s, void *unused)
+{
+       pwrdm_for_each(pwrdm_dbg_show_timer, s);
+       return 0;
+}
+
+static int pm_dbg_open(struct inode *inode, struct file *file)
+{
+       switch ((int)inode->i_private) {
+       case DEBUG_FILE_COUNTERS:
+               return single_open(file, pm_dbg_show_counters,
+                       &inode->i_private);
+       case DEBUG_FILE_TIMERS:
+       default:
+               return single_open(file, pm_dbg_show_timers,
+                       &inode->i_private);
+       };
+}
+
+static const struct file_operations debug_fops = {
+       .open           = pm_dbg_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
+{
+       int i;
+       s64 t;
+
+       t = sched_clock();
+
+       for (i = 0; i < 4; i++)
+               pwrdm->state_timer[i] = 0;
+
+       pwrdm->timer = t;
+
+       return 0;
+}
+
+static int __init pm_dbg_init(void)
+{
+       struct dentry *d;
+
+       d = debugfs_create_dir("pm_debug", NULL);
+       if (IS_ERR(d))
+               return PTR_ERR(d);
+
+       (void) debugfs_create_file("count", S_IRUGO,
+               d, (void *)DEBUG_FILE_COUNTERS, &debug_fops);
+       (void) debugfs_create_file("time", S_IRUGO,
+               d, (void *)DEBUG_FILE_TIMERS, &debug_fops);
+
+       pwrdm_for_each(pwrdms_setup, NULL);
+
+       pm_dbg_init_done = 1;
+
+       return 0;
+}
+late_initcall(pm_dbg_init);
+
+#else
+void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) {}
+#endif
index 21201cd4117b5d55a4380422d253687372657bad..4589db1c8af85eb924ce42b94c4ee5cf051559c3 100644 (file)
 #ifndef __ARCH_ARM_MACH_OMAP2_PM_H
 #define __ARCH_ARM_MACH_OMAP2_PM_H
 
+#include <mach/powerdomain.h>
+
 #ifdef CONFIG_PM_DEBUG
 extern void omap2_pm_dump(int mode, int resume, unsigned int us);
 extern int omap2_pm_debug;
+extern void pm_dbg_update_time(struct powerdomain *pwrdm, int prev);
 #else
 #define omap2_pm_dump(mode, resume, us)                do {} while (0);
 #define omap2_pm_debug                         0
+#define pm_dbg_update_time(pwrdm, prev) do {} while (0);
 #endif /* CONFIG_PM_DEBUG */
 
 extern void omap24xx_idle_loop_suspend(void);
index de03f3dbbf3dfef83caa8033c949131f88792a94..6271d8556a40f32d5ef8d360bc6bbf8b74265559 100644 (file)
@@ -119,6 +119,11 @@ struct powerdomain {
 
        int state;
        unsigned state_counter[4];
+
+#ifdef CONFIG_PM_DEBUG
+       s64 timer;
+       s64 state_timer[4];
+#endif
 };