summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-02-08 21:49:07 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:42:21 -0800
commit41d47bdeca1b9eb34fd89fbe875bdab9208fb00c (patch)
tree71f5cba2684b27d624a72fd0998d71a7482a5b43 /arch
parentcfc0cea5155994af296d7b19ccde236a2053ffe3 (diff)
ARM: tegra: cpu: Add Tegra3 auto-hotplug statistic
Add auto-hotplug statistic to track number of transitions and on-line time for each CPU/cluster. Original-Change-Id: Iefaf4f69068401eb7a9d4abbf725df4e21d35db9 Reviewed-on: http://git-master/r/19168 Tested-by: Aleksandr Frid <afrid@nvidia.com> Reviewed-by: Narendra Damahe <ndamahe@nvidia.com> Reviewed-by: Scott Williams <scwilliams@nvidia.com> Original-Change-Id: I62209733054dddbc18741b7fca0c481c90f3aba7 Rebase-Id: R3ef66d1e09da307a7aac93082692d8ee27075299
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/Kconfig2
-rw-r--r--arch/arm/mach-tegra/cpu-tegra3.c145
2 files changed, 137 insertions, 10 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index d477116db830..9a105cdc35c0 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -226,7 +226,7 @@ config TEGRA_CLOCK_DEBUG_WRITE
config TEGRA_AUTO_HOTPLUG
bool "Enable automatic CPU hot-plugging"
- depends on HOTPLUG_CPU && CPU_FREQ
+ depends on HOTPLUG_CPU && CPU_FREQ && !ARCH_CPU_PROBE_RELEASE
default y
help
This option enables turning CPUs off/on and switching tegra
diff --git a/arch/arm/mach-tegra/cpu-tegra3.c b/arch/arm/mach-tegra/cpu-tegra3.c
index 6208e5fa9241..ac5699599270 100644
--- a/arch/arm/mach-tegra/cpu-tegra3.c
+++ b/arch/arm/mach-tegra/cpu-tegra3.c
@@ -29,6 +29,8 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/cpu.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include "pm.h"
@@ -54,6 +56,12 @@ module_param(idle_bottom_freq, uint, 0644);
static unsigned int lpcpu_max_freq;
+static struct {
+ cputime64_t time_up_total;
+ u64 last_update;
+ unsigned int up_down_count;
+} hp_stats[CONFIG_NR_CPUS + 1]; /* Append LP CPU entry at the end */
+
enum {
TEGRA_HP_DISABLED = 0,
TEGRA_HP_IDLE,
@@ -109,6 +117,29 @@ static struct kernel_param_ops tegra_hp_state_ops = {
module_param_cb(auto_hotplug, &tegra_hp_state_ops, &hp_state, 0644);
+static void hp_stats_update(unsigned int cpu, bool up)
+{
+ u64 cur_jiffies = get_jiffies_64();
+ bool was_up = hp_stats[cpu].up_down_count & 0x1;
+
+ if (was_up)
+ hp_stats[cpu].time_up_total = cputime64_add(
+ hp_stats[cpu].time_up_total, cputime64_sub(
+ cur_jiffies, hp_stats[cpu].last_update));
+
+ if (was_up != up) {
+ hp_stats[cpu].up_down_count++;
+ if ((hp_stats[cpu].up_down_count & 0x1) != up) {
+ /* FIXME: sysfs user space CPU control breaks stats */
+ pr_err("tegra hotplug stats out of sync with %s CPU%d",
+ (cpu < CONFIG_NR_CPUS) ? "G" : "LP",
+ (cpu < CONFIG_NR_CPUS) ? cpu : 0);
+ hp_stats[cpu].up_down_count ^= 0x1;
+ }
+ }
+ hp_stats[cpu].last_update = cur_jiffies;
+}
+
static void tegra_auto_hotplug_work_func(struct work_struct *work)
{
bool up = false;
@@ -131,10 +162,13 @@ static void tegra_auto_hotplug_work_func(struct work_struct *work)
up = false;
queue_delayed_work(
hotplug_wq, &hotplug_work, down_delay);
- }
- else
+ hp_stats_update(cpu, false);
+ } else {
tegra_cluster_control(0, TEGRA_POWER_CLUSTER_LP |
- TEGRA_POWER_CLUSTER_IMMEDIATE);
+ TEGRA_POWER_CLUSTER_IMMEDIATE);
+ hp_stats_update(CONFIG_NR_CPUS, true);
+ hp_stats_update(0, false);
+ }
break;
case TEGRA_HP_UP:
cpu = cpumask_next_zero(0, cpu_online_mask);
@@ -142,6 +176,7 @@ static void tegra_auto_hotplug_work_func(struct work_struct *work)
up = true;
queue_delayed_work(
hotplug_wq, &hotplug_work, up_delay);
+ hp_stats_update(cpu, true);
}
break;
default:
@@ -165,6 +200,8 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq)
if (is_lp_cluster() && (cpu_freq > lpcpu_max_freq)) {
tegra_cluster_control(0, TEGRA_POWER_CLUSTER_G |
TEGRA_POWER_CLUSTER_IMMEDIATE);
+ hp_stats_update(CONFIG_NR_CPUS, false);
+ hp_stats_update(0, true);
}
switch (hp_state) {
@@ -175,8 +212,7 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq)
hp_state = TEGRA_HP_UP;
queue_delayed_work(
hotplug_wq, &hotplug_work, up_delay);
- }
- else if (cpu_freq <= idle_bottom_freq) {
+ } else if (cpu_freq <= idle_bottom_freq) {
hp_state = TEGRA_HP_DOWN;
queue_delayed_work(
hotplug_wq, &hotplug_work, down_delay);
@@ -187,8 +223,7 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq)
hp_state = TEGRA_HP_UP;
queue_delayed_work(
hotplug_wq, &hotplug_work, up_delay);
- }
- else if (cpu_freq > idle_bottom_freq) {
+ } else if (cpu_freq > idle_bottom_freq) {
hp_state = TEGRA_HP_IDLE;
}
break;
@@ -197,8 +232,7 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq)
hp_state = TEGRA_HP_DOWN;
queue_delayed_work(
hotplug_wq, &hotplug_work, down_delay);
- }
- else if (cpu_freq <= idle_top_freq) {
+ } else if (cpu_freq <= idle_top_freq) {
hp_state = TEGRA_HP_IDLE;
}
break;
@@ -212,6 +246,9 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq)
int tegra_auto_hotplug_init(void)
{
+ int i;
+ u64 cur_jiffies = get_jiffies_64();
+
/*
* Not bound to the issuer CPU (=> high-priority), has rescue worker
* task, single-threaded, frrezeable.
@@ -233,10 +270,100 @@ int tegra_auto_hotplug_init(void)
pr_info("Tegra auto-hotplug initialized: %s\n",
(hp_state == TEGRA_HP_DISABLED) ? "disabled" : "enabled");
+ for (i = 0; i < CONFIG_NR_CPUS; i++) {
+ hp_stats[i].time_up_total = 0;
+ hp_stats[i].last_update = cur_jiffies;
+
+ hp_stats[i].up_down_count = 0;
+ if (is_lp_cluster()) {
+ if (i == CONFIG_NR_CPUS)
+ hp_stats[i].up_down_count = 1;
+ } else {
+ if ((i < nr_cpu_ids) && cpu_online(i))
+ hp_stats[i].up_down_count = 1;
+ }
+ }
+
return 0;
}
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *hp_debugfs_root;
+
+static int hp_stats_show(struct seq_file *s, void *data)
+{
+ int i;
+ u64 cur_jiffies = get_jiffies_64();
+
+ mutex_lock(&tegra_hp_lock);
+ for (i = 0; i < CONFIG_NR_CPUS; i++) {
+ bool was_up = (hp_stats[i].up_down_count & 0x1);
+ hp_stats_update(i, was_up);
+ }
+ mutex_unlock(&tegra_hp_lock);
+
+ seq_printf(s, "%-15s ", "cpu:");
+ for (i = 0; i < CONFIG_NR_CPUS; i++) {
+ seq_printf(s, "G%-9d ", i);
+ }
+ seq_printf(s, "LP\n");
+
+ seq_printf(s, "%-15s ", "transitions:");
+ for (i = 0; i <= CONFIG_NR_CPUS; i++) {
+ seq_printf(s, "%-10u ", hp_stats[i].up_down_count);
+ }
+ seq_printf(s, "\n");
+
+ seq_printf(s, "%-15s ", "time plugged:");
+ for (i = 0; i <= CONFIG_NR_CPUS; i++) {
+ seq_printf(s, "%-10llu ",
+ cputime64_to_clock_t(hp_stats[i].time_up_total));
+ }
+ seq_printf(s, "\n");
+
+ seq_printf(s, "%-15s %llu\n", "time-stamp:",
+ cputime64_to_clock_t(cur_jiffies));
+
+ return 0;
+}
+
+static int hp_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hp_stats_show, inode->i_private);
+}
+
+static const struct file_operations hp_stats_fops = {
+ .open = hp_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_auto_hotplug_debug_init(void)
+{
+ hp_debugfs_root = debugfs_create_dir("tegra_hotplug", NULL);
+ if (!hp_debugfs_root)
+ return -ENOMEM;
+
+ if (!debugfs_create_file(
+ "stats", S_IRUGO, hp_debugfs_root, NULL, &hp_stats_fops))
+ goto err_out;
+
+ return 0;
+
+err_out:
+ debugfs_remove_recursive(hp_debugfs_root);
+ return -ENOMEM;
+}
+
+late_initcall(tegra_auto_hotplug_debug_init);
+#endif
+
void tegra_auto_hotplug_exit(void)
{
destroy_workqueue(hotplug_wq);
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(hp_debugfs_root);
+#endif
}