root/arch/arm/mach-omap2/pm-debug.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP Power Management debug routines
 *
 * Copyright (C) 2005 Texas Instruments, Inc.
 * Copyright (C) 2006-2008 Nokia Corporation
 *
 * Written by:
 * Richard Woodruff <r-woodruff2@ti.com>
 * Tony Lindgren
 * Juha Yrjola
 * Amit Kucheria <amit.kucheria@nokia.com>
 * Igor Stoppa <igor.stoppa@nokia.com>
 * Jouni Hogander
 *
 * Based on pm.c for omap2
 */

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/slab.h>

#include "clock.h"
#include "powerdomain.h"
#include "clockdomain.h"

#include "soc.h"
#include "cm2xxx_3xxx.h"
#include "prm2xxx_3xxx.h"
#include "pm.h"

#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/seq_file.h>

static int pm_dbg_init_done;

static int pm_dbg_init(void);

static const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = {
        "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)\n", clkdm->name, clkdm->pwrdm.ptr->name,
                   clkdm->usecount);

        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 < PWRDM_MAX_PWRSTS; i++)
                seq_printf(s, ",%s:%d", pwrdm_state_names[i],
                        pwrdm->state_counter[i]);

        seq_printf(s, ",RET-LOGIC-OFF:%d", pwrdm->ret_logic_off_counter);
        for (i = 0; i < pwrdm->banks; i++)
                seq_printf(s, ",RET-MEMBANK%d-OFF:%d", i + 1,
                                pwrdm->ret_mem_off_counter[i]);

        seq_putc(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_putc(s, '\n');
        return 0;
}

static int pm_dbg_counters_show(struct seq_file *s, void *unused)
{
        pwrdm_for_each(pwrdm_dbg_show_counter, s);
        clkdm_for_each(clkdm_dbg_show_counter, s);

        return 0;
}
DEFINE_SHOW_ATTRIBUTE(pm_dbg_counters);

static int pm_dbg_timers_show(struct seq_file *s, void *unused)
{
        pwrdm_for_each(pwrdm_dbg_show_timer, s);
        return 0;
}
DEFINE_SHOW_ATTRIBUTE(pm_dbg_timers);

static int pwrdm_suspend_get(void *data, u64 *val)
{
        int ret = -EINVAL;

        if (cpu_is_omap34xx())
                ret = omap3_pm_get_suspend_state((struct powerdomain *)data);
        *val = ret;

        if (ret >= 0)
                return 0;
        return *val;
}

static int pwrdm_suspend_set(void *data, u64 val)
{
        if (cpu_is_omap34xx())
                return omap3_pm_set_suspend_state(
                        (struct powerdomain *)data, (int)val);
        return -EINVAL;
}

DEFINE_DEBUGFS_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get,
                          pwrdm_suspend_set, "%llu\n");

static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir)
{
        int i;
        s64 t;
        struct dentry *d;

        t = sched_clock();

        for (i = 0; i < 4; i++)
                pwrdm->state_timer[i] = 0;

        pwrdm->timer = t;

        if (strncmp(pwrdm->name, "dpll", 4) == 0)
                return 0;

        d = debugfs_create_dir(pwrdm->name, (struct dentry *)dir);
        debugfs_create_file("suspend", S_IRUGO|S_IWUSR, d, pwrdm,
                            &pwrdm_suspend_fops);

        return 0;
}

static int option_get(void *data, u64 *val)
{
        u32 *option = data;

        *val = *option;

        return 0;
}

static int option_set(void *data, u64 val)
{
        u32 *option = data;

        *option = val;

        if (option == &enable_off_mode) {
                if (cpu_is_omap34xx())
                        omap3_pm_off_mode_enable(val);
        }

        return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(pm_dbg_option_fops, option_get, option_set, "%llu\n");

static int __init pm_dbg_init(void)
{
        struct dentry *d;

        if (pm_dbg_init_done)
                return 0;

        d = debugfs_create_dir("pm_debug", NULL);

        debugfs_create_file("count", 0444, d, NULL, &pm_dbg_counters_fops);
        debugfs_create_file("time", 0444, d, NULL, &pm_dbg_timers_fops);

        pwrdm_for_each(pwrdms_setup, (void *)d);

        debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUSR, d,
                            &enable_off_mode, &pm_dbg_option_fops);
        pm_dbg_init_done = 1;

        return 0;
}
omap_arch_initcall(pm_dbg_init);

#endif