summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/dvfs.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-11-19 17:28:31 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2012-03-22 23:49:38 -0700
commit6a4ffd69b83309a9010d5b7fa4a82450c03321bb (patch)
tree4d0a0eb38b5eb14ac52cc28722d4f7e61e901d9a /arch/arm/mach-tegra/dvfs.c
parentac1d42a3d5cb02bc107345830612dcfe83828767 (diff)
ARM: tegra: dvfs: Add DVFS rails statistic
On Tegra3: complete account of in- and out-of-bound rails control. On Tegra2: out-of-bound vdd_cpu control in LP2 state is not accounted. Change-Id: Ib68cbbfe3e4f965e758aca17a0ba30277d530347 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/67340 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com> Rebase-Id: Rf2aa9242f09fc393fa5114cd059894ebdbeedcee
Diffstat (limited to 'arch/arm/mach-tegra/dvfs.c')
-rw-r--r--arch/arm/mach-tegra/dvfs.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index c39a10a9e46b..22c666081c90 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -39,6 +39,11 @@
#include "clock.h"
#include "dvfs.h"
+#define DVFS_RAIL_STATS_BIN 25
+#define DVFS_RAIL_STATS_SCALE 2
+#define DVFS_RAIL_STATS_RANGE ((DVFS_RAIL_STATS_TOP_BIN - 1) * \
+ DVFS_RAIL_STATS_BIN / DVFS_RAIL_STATS_SCALE)
+
static LIST_HEAD(dvfs_rail_list);
static DEFINE_MUTEX(dvfs_lock);
static DEFINE_MUTEX(rail_disable_lock);
@@ -89,6 +94,74 @@ static int dvfs_solve_relationship(struct dvfs_relationship *rel)
return rel->solve(rel->from, rel->to);
}
+/* rail statistic - called during rail init, or under dfs_lock, or with
+ CPU0 only on-line, and interrupts disabled */
+static void dvfs_rail_stats_init(struct dvfs_rail *rail, int millivolts)
+{
+ rail->stats.last_update = ktime_get();
+ if (millivolts >= rail->min_millivolts) {
+ int i = 1 + (2 * (millivolts - rail->min_millivolts) *
+ DVFS_RAIL_STATS_SCALE + DVFS_RAIL_STATS_BIN) /
+ (2 * DVFS_RAIL_STATS_BIN);
+ rail->stats.last_index = min(i, DVFS_RAIL_STATS_TOP_BIN);
+ }
+
+ if (rail->max_millivolts >
+ rail->min_millivolts + DVFS_RAIL_STATS_RANGE)
+ pr_warn("tegra_dvfs: %s: stats above %d mV will be squashed\n",
+ rail->reg_id,
+ rail->min_millivolts + DVFS_RAIL_STATS_RANGE);
+}
+
+static void dvfs_rail_stats_update(
+ struct dvfs_rail *rail, int millivolts, ktime_t now)
+{
+ rail->stats.time_at_mv[rail->stats.last_index] = ktime_add(
+ rail->stats.time_at_mv[rail->stats.last_index], ktime_sub(
+ now, rail->stats.last_update));
+ rail->stats.last_update = now;
+
+ if (rail->stats.off)
+ return;
+
+ if (millivolts >= rail->min_millivolts) {
+ int i = 1 + (2 * (millivolts - rail->min_millivolts) *
+ DVFS_RAIL_STATS_SCALE + DVFS_RAIL_STATS_BIN) /
+ (2 * DVFS_RAIL_STATS_BIN);
+ rail->stats.last_index = min(i, DVFS_RAIL_STATS_TOP_BIN);
+ } else if (millivolts == 0)
+ rail->stats.last_index = 0;
+}
+
+static void dvfs_rail_stats_pause(struct dvfs_rail *rail,
+ ktime_t delta, bool on)
+{
+ int i = on ? rail->stats.last_index : 0;
+ rail->stats.time_at_mv[i] = ktime_add(rail->stats.time_at_mv[i], delta);
+}
+
+void tegra_dvfs_rail_off(struct dvfs_rail *rail, ktime_t now)
+{
+ if (rail) {
+ dvfs_rail_stats_update(rail, 0, now);
+ rail->stats.off = true;
+ }
+}
+
+void tegra_dvfs_rail_on(struct dvfs_rail *rail, ktime_t now)
+{
+ if (rail) {
+ rail->stats.off = false;
+ dvfs_rail_stats_update(rail, rail->millivolts, now);
+ }
+}
+
+void tegra_dvfs_rail_pause(struct dvfs_rail *rail, ktime_t delta, bool on)
+{
+ if (rail)
+ dvfs_rail_stats_pause(rail, delta, on);
+}
+
/* Sets the voltage on a dvfs rail to a specific value, and updates any
* rails that depend on this rail. */
static int dvfs_rail_set_voltage(struct dvfs_rail *rail, int millivolts)
@@ -148,6 +221,7 @@ static int dvfs_rail_set_voltage(struct dvfs_rail *rail, int millivolts)
}
rail->millivolts = rail->new_millivolts;
+ dvfs_rail_stats_update(rail, rail->millivolts, ktime_get());
/* After changing the voltage, tell each rail that depends
* on this rail that the voltage has changed.
@@ -242,6 +316,7 @@ static int dvfs_rail_connect_to_regulator(struct dvfs_rail *rail)
}
rail->millivolts = v / 1000;
rail->new_millivolts = rail->millivolts;
+ dvfs_rail_stats_init(rail, rail->millivolts);
return 0;
}
@@ -664,6 +739,53 @@ static const struct file_operations dvfs_tree_fops = {
.release = single_release,
};
+static int rail_stats_show(struct seq_file *s, void *data)
+{
+ int i;
+ struct dvfs_rail *rail;
+
+ seq_printf(s, "%-12s %-10s (bin: %d.%dmV)\n", "millivolts", "time",
+ DVFS_RAIL_STATS_BIN / DVFS_RAIL_STATS_SCALE,
+ ((DVFS_RAIL_STATS_BIN * 100) / DVFS_RAIL_STATS_SCALE) % 100);
+
+ mutex_lock(&dvfs_lock);
+
+ list_for_each_entry(rail, &dvfs_rail_list, node) {
+ seq_printf(s, "%s\n", rail->reg_id);
+ dvfs_rail_stats_update(rail, -1, ktime_get());
+
+ seq_printf(s, "%-12d %-10llu\n", 0,
+ cputime64_to_clock_t(msecs_to_jiffies(
+ ktime_to_ms(rail->stats.time_at_mv[0]))));
+
+ for (i = 1; i <= DVFS_RAIL_STATS_TOP_BIN; i++) {
+ ktime_t ktime_zero = ktime_set(0, 0);
+ if (ktime_equal(rail->stats.time_at_mv[i], ktime_zero))
+ continue;
+ seq_printf(s, "%-12d %-10llu\n",
+ rail->min_millivolts + (i - 1) *
+ DVFS_RAIL_STATS_BIN / DVFS_RAIL_STATS_SCALE,
+ cputime64_to_clock_t(msecs_to_jiffies(
+ ktime_to_ms(rail->stats.time_at_mv[i])))
+ );
+ }
+ }
+ mutex_unlock(&dvfs_lock);
+ return 0;
+}
+
+static int rail_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, rail_stats_show, inode->i_private);
+}
+
+static const struct file_operations rail_stats_fops = {
+ .open = rail_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
int __init dvfs_debugfs_init(struct dentry *clk_debugfs_root)
{
struct dentry *d;
@@ -673,6 +795,11 @@ int __init dvfs_debugfs_init(struct dentry *clk_debugfs_root)
if (!d)
return -ENOMEM;
+ d = debugfs_create_file("rails", S_IRUGO, clk_debugfs_root, NULL,
+ &rail_stats_fops);
+ if (!d)
+ return -ENOMEM;
+
return 0;
}