diff options
author | Alex Frid <afrid@nvidia.com> | 2011-11-19 17:28:31 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2012-03-22 23:49:38 -0700 |
commit | 6a4ffd69b83309a9010d5b7fa4a82450c03321bb (patch) | |
tree | 4d0a0eb38b5eb14ac52cc28722d4f7e61e901d9a /arch/arm/mach-tegra/dvfs.c | |
parent | ac1d42a3d5cb02bc107345830612dcfe83828767 (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.c | 127 |
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; } |