summaryrefslogtreecommitdiff
path: root/drivers/power/qcom
diff options
context:
space:
mode:
authorMahesh Sivasubramanian <msivasub@codeaurora.org>2016-03-09 14:16:41 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 21:24:36 -0700
commitde19c1b1b4f2f2b9369f30a87e6d49cf0521747f (patch)
treeaa128143f682ed242a571a4830e1533b1219268e /drivers/power/qcom
parent05cb6f52907bde05d5bd7bc10245715d3fbfbb80 (diff)
drivers: power: qcom: Snapshot of debug_core
Snapshot of debug_core module from 3.18(as of e70ad0cd5efdd9dc91a77dcdac31d6132e1315c1) Change-Id: Ie7a4c1ba59c3fb2d3745b4582dd14f0147593935
Diffstat (limited to 'drivers/power/qcom')
-rw-r--r--drivers/power/qcom/debug_core.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/drivers/power/qcom/debug_core.c b/drivers/power/qcom/debug_core.c
new file mode 100644
index 000000000000..d3620bbbeafa
--- /dev/null
+++ b/drivers/power/qcom/debug_core.c
@@ -0,0 +1,315 @@
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/debugfs.h>
+#include <linux/ctype.h>
+#include <linux/cpu.h>
+#include "soc/qcom/msm-core.h"
+
+#define MAX_PSTATES 50
+
+enum arg_offset {
+ CPU_OFFSET,
+ FREQ_OFFSET,
+ POWER_OFFSET,
+};
+
+struct core_debug {
+ int cpu;
+ struct cpu_pstate_pwr *head;
+ int enabled;
+ int len;
+ struct cpu_pwr_stats *ptr;
+ struct cpu_pstate_pwr *driver_data;
+ int driver_len;
+};
+
+static DEFINE_PER_CPU(struct core_debug, c_dgfs);
+static struct cpu_pwr_stats *msm_core_data;
+static struct debugfs_blob_wrapper help_msg = {
+ .data =
+"MSM CORE Debug-FS Support\n"
+"\n"
+"Hierarchy schema\n"
+"/sys/kernel/debug/msm_core\n"
+" /help - Static help text\n"
+" /ptable - write to p-state table\n"
+" /enable - enable the written p-state table\n"
+" /ptable_dump - Dump the debug ptable\n"
+"\n"
+"Usage\n"
+" Input test frequency and power information in ptable:\n"
+" echo \"0 300000 120\" > ptable\n"
+" format: <cpu> <frequency in khz> <power>\n"
+"\n"
+" Enable the ptable for the cpu:\n"
+" echo \"0 1\" > enable\n"
+" format: <cpu> <1 to enable, 0 to disable>\n"
+" Note: Writing 0 to disable will reset/clear the ptable\n"
+"\n"
+" Dump the entire ptable:\n"
+" cat ptable\n"
+" ----- CPU0 - Enabled ---------\n"
+" Freq Power\n"
+" 700000 120\n"
+"----- CPU0 - Live numbers -----\n"
+" Freq Power\n"
+" 300000 218\n"
+" ----- CPU1 - Written ---------\n"
+" Freq Power\n"
+" 700000 120\n"
+" Ptable dump will dump the status of the table as well\n"
+" It shows:\n"
+" Enabled -> for a cpu that debug ptable enabled\n"
+" Written -> for a cpu that has debug ptable values written\n"
+" but not enabled\n"
+"\n",
+
+};
+
+static void add_to_ptable(uint64_t *arg)
+{
+ struct core_debug *node;
+ int i, cpu = arg[CPU_OFFSET];
+
+ if (!cpu_possible(cpu))
+ return;
+
+ node = &per_cpu(c_dgfs, cpu);
+ if (!node->head) {
+ node->head = kzalloc(sizeof(struct cpu_pstate_pwr) *
+ (MAX_PSTATES + 1),
+ GFP_KERNEL);
+ if (!node->head)
+ return;
+ }
+ for (i = 0; i < MAX_PSTATES; i++) {
+ if (node->head[i].freq == arg[FREQ_OFFSET]) {
+ node->head[i].power = arg[POWER_OFFSET];
+ return;
+ }
+ if (node->head[i].freq == 0)
+ break;
+ }
+
+ if (i == MAX_PSTATES) {
+ pr_warn("Dropped ptable update - no space left.\n");
+ return;
+ }
+
+ /* Insert a new frequency (may need to move things around to
+ keep in ascending order). */
+ for (i = MAX_PSTATES - 1; i > 0; i--) {
+ if (node->head[i-1].freq > arg[FREQ_OFFSET]) {
+ node->head[i].freq = node->head[i-1].freq;
+ node->head[i].power = node->head[i-1].power;
+ } else if (node->head[i-1].freq != 0) {
+ break;
+ }
+ }
+
+ node->head[i].freq = arg[FREQ_OFFSET];
+ node->head[i].power = arg[POWER_OFFSET];
+ node->len++;
+
+ if (node->ptr)
+ node->ptr->len = node->len;
+}
+
+static int split_ptable_args(char *line, uint64_t *arg)
+{
+ char *args;
+ int i;
+ int ret = 0;
+
+ for (i = 0; line; i++) {
+ args = strsep(&line, " ");
+ ret = kstrtoull(args, 10, &arg[i]);
+ }
+ return ret;
+}
+
+static ssize_t msm_core_ptable_write(struct file *file,
+ const char __user *ubuf, size_t len, loff_t *offp)
+{
+ char *kbuf;
+ int ret;
+ uint64_t arg[3];
+
+ if (len == 0)
+ return 0;
+
+ kbuf = kzalloc(len + 1, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ if (copy_from_user(kbuf, ubuf, len)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ kbuf[len] = '\0';
+ ret = split_ptable_args(kbuf, arg);
+ if (!ret) {
+ add_to_ptable(arg);
+ ret = len;
+ }
+done:
+ kfree(kbuf);
+ return ret;
+}
+
+static void print_table(struct seq_file *m, struct cpu_pstate_pwr *c_n,
+ int len)
+{
+ int i;
+
+ seq_puts(m, " Freq Power\n");
+ for (i = 0; i < len; i++)
+ seq_printf(m, " %d %u\n", c_n[i].freq,
+ c_n[i].power);
+
+}
+
+static int msm_core_ptable_read(struct seq_file *m, void *data)
+{
+ int cpu;
+ struct core_debug *node;
+
+ for_each_possible_cpu(cpu) {
+ node = &per_cpu(c_dgfs, cpu);
+ if (node->head) {
+ seq_printf(m, "----- CPU%d - %s - Debug -------\n",
+ cpu, node->enabled == 1 ? "Enabled" : "Written");
+ print_table(m, node->head, node->len);
+ }
+ if (msm_core_data[cpu].ptable) {
+ seq_printf(m, "--- CPU%d - Live numbers at %ldC---\n",
+ cpu, node->ptr->temp);
+ print_table(m, msm_core_data[cpu].ptable,
+ msm_core_data[cpu].len);
+ }
+ }
+ return 0;
+}
+
+static ssize_t msm_core_enable_write(struct file *file,
+ const char __user *ubuf, size_t len, loff_t *offp)
+{
+ char *kbuf;
+ int ret;
+ uint64_t arg[3];
+ int cpu;
+
+ if (len == 0)
+ return 0;
+
+ kbuf = kzalloc(len + 1, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ if (copy_from_user(kbuf, ubuf, len)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ kbuf[len] = '\0';
+ ret = split_ptable_args(kbuf, arg);
+ if (ret)
+ goto done;
+ cpu = arg[CPU_OFFSET];
+
+ if (cpu_possible(cpu)) {
+ struct core_debug *node = &per_cpu(c_dgfs, cpu);
+
+ if (arg[FREQ_OFFSET]) {
+ msm_core_data[cpu].ptable = node->head;
+ msm_core_data[cpu].len = node->len;
+ } else {
+ msm_core_data[cpu].ptable = node->driver_data;
+ msm_core_data[cpu].len = node->driver_len;
+ node->len = 0;
+ }
+ node->enabled = arg[FREQ_OFFSET];
+ }
+ ret = len;
+ blocking_notifier_call_chain(
+ get_power_update_notifier(), cpu, NULL);
+
+done:
+ kfree(kbuf);
+ return ret;
+}
+
+static const struct file_operations msm_core_enable_ops = {
+ .write = msm_core_enable_write,
+};
+
+static int msm_core_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, msm_core_ptable_read, inode->i_private);
+}
+
+static const struct file_operations msm_core_ptable_ops = {
+ .open = msm_core_dump_open,
+ .read = seq_read,
+ .write = msm_core_ptable_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int msm_core_debug_init(void)
+{
+ struct dentry *dir;
+ struct dentry *file;
+ int i;
+
+ msm_core_data = get_cpu_pwr_stats();
+ if (!msm_core_data)
+ goto fail;
+
+ dir = debugfs_create_dir("msm_core", NULL);
+ if (IS_ERR_OR_NULL(dir))
+ return PTR_ERR(dir);
+
+ file = debugfs_create_file("enable",
+ S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP, dir, NULL,
+ &msm_core_enable_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ptable",
+ S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP, dir, NULL,
+ &msm_core_ptable_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ help_msg.size = strlen(help_msg.data);
+ file = debugfs_create_blob("help", S_IRUGO, dir, &help_msg);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ for (i = 0; i < num_possible_cpus(); i++) {
+ per_cpu(c_dgfs, i).ptr = &msm_core_data[i];
+ per_cpu(c_dgfs, i).driver_data = msm_core_data[i].ptable;
+ per_cpu(c_dgfs, i).driver_len = msm_core_data[i].len;
+ }
+ return 0;
+fail:
+ debugfs_remove(dir);
+ return PTR_ERR(file);
+}
+late_initcall(msm_core_debug_init);