/* * Copyright (c) 2013-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "bpmp.h" struct bpmp_cpuidle_state { int id; const char *name; }; static struct bpmp_cpuidle_state plat_cpuidle_state[] = { { TEGRA_PM_CC4, "CC4" }, { TEGRA_PM_CC6, "CC6" }, { TEGRA_PM_CC7, "CC7" }, { TEGRA_PM_SC2, "SC2" }, { TEGRA_PM_SC3, "SC3" }, { TEGRA_PM_SC4, "SC4" }, { TEGRA_PM_SC7, "SC7" }, { 0, NULL } }; static int bpmp_cpuidle_name_show(struct seq_file *file, void *data) { struct bpmp_cpuidle_state *state = file->private; seq_printf(file, "%s\n", state->name); return 0; } static int bpmp_cpuidle_name_open(struct inode *inode, struct file *file) { return single_open(file, bpmp_cpuidle_name_show, inode->i_private); } static const struct file_operations cpuidle_name_fops = { .open = bpmp_cpuidle_name_open, .read = seq_read, .release = single_release }; static int __bpmp_cpuidle_show(struct seq_file *file, bool show_time) { struct bpmp_cpuidle_state *state = file->private; struct { uint64_t usage; uint64_t time; } __packed mb[3]; uint32_t id = state->id; int ret; ret = tegra_bpmp_send_receive(MRQ_CPUIDLE_USAGE, &id, sizeof(id), &mb, sizeof(mb)); if (ret) { seq_printf(file, "%d\n", ret); return ret; } if (show_time) { seq_printf(file, "%llu (%llu, %llu)\n", mb[0].time, mb[1].time, mb[2].time); } else { seq_printf(file, "%llu (%llu, %llu)\n", mb[0].usage, mb[1].usage, mb[2].usage); } return 0; } static int bpmp_cpuidle_usage_show(struct seq_file *file, void *data) { return __bpmp_cpuidle_show(file, false); } static int bpmp_cpuidle_usage_open(struct inode *inode, struct file *file) { return single_open(file, bpmp_cpuidle_usage_show, inode->i_private); } static const struct file_operations cpuidle_usage_fops = { .open = bpmp_cpuidle_usage_open, .read = seq_read, .release = single_release }; static int bpmp_cpuidle_time_show(struct seq_file *file, void *data) { return __bpmp_cpuidle_show(file, true); } static int bpmp_cpuidle_time_open(struct inode *inode, struct file *file) { return single_open(file, bpmp_cpuidle_time_show, inode->i_private); } static const struct file_operations cpuidle_time_fops = { .open = bpmp_cpuidle_time_open, .read = seq_read, .release = single_release }; static const struct fops_entry cpuidle_attrs[] = { { "name", &cpuidle_name_fops, S_IRUGO }, { "usage", &cpuidle_usage_fops, S_IRUGO }, { "time", &cpuidle_time_fops, S_IRUGO }, { NULL, NULL, 0 } }; static int bpmp_create_cpuidle_debug(int index, struct dentry *parent, struct bpmp_cpuidle_state *state) { struct dentry *top; char name[16]; sprintf(name, "state%d", index); top = debugfs_create_dir(name, parent); if (IS_ERR_OR_NULL(top)) return -EFAULT; return bpmp_create_attrs(cpuidle_attrs, top, state); } int bpmp_init_cpuidle_debug(struct dentry *root) { struct bpmp_cpuidle_state *state; struct dentry *d; unsigned int i; d = debugfs_create_dir("cpuidle", root); if (IS_ERR_OR_NULL(d)) return -EFAULT; for (i = 0, state = plat_cpuidle_state; state->name; i++, state++) { if (bpmp_create_cpuidle_debug(i, d, state)) return -EFAULT; } return 0; }