aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/tegra2_statmon.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_statmon.c')
-rw-r--r--arch/arm/mach-tegra/tegra2_statmon.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra2_statmon.c b/arch/arm/mach-tegra/tegra2_statmon.c
new file mode 100644
index 00000000000..92f9b883f93
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_statmon.c
@@ -0,0 +1,440 @@
1/*
2 * arch/arm/mach-tegra/tegra2_statmon.c
3 *
4 * Copyright (c) 2011, NVIDIA Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include <linux/kernel.h>
22#include <linux/init.h>
23#include <linux/slab.h>
24#include <linux/clk.h>
25#include <linux/interrupt.h>
26#include <linux/delay.h>
27#include <linux/io.h>
28#include <linux/err.h>
29#include <linux/sysdev.h>
30#include <linux/bitops.h>
31
32#include <mach/iomap.h>
33#include <mach/irqs.h>
34#include <mach/io.h>
35#include <mach/clk.h>
36
37#include "clock.h"
38#include "tegra2_statmon.h"
39
40#define COP_MON_CTRL 0x120
41#define COP_MON_STATUS 0x124
42
43#define SAMPLE_PERIOD_SHIFT 20
44#define SAMPLE_PERIOD_MASK (0xFF << SAMPLE_PERIOD_SHIFT)
45#define INT_STATUS BIT(29) /* write 1 to clear */
46#define INT_ENABLE BIT(30)
47#define MON_ENABLE BIT(31)
48
49#define WINDOW_SIZE 128
50#define FREQ_MULT 1000
51#define UPPER_BAND 1000
52#define LOWER_BAND 1000
53#define BOOST_FRACTION_BITS 8
54
55struct sampler {
56 struct clk *clock;
57 unsigned long active_cycles[WINDOW_SIZE];
58 unsigned long total_active_cycles;
59 unsigned long avg_freq;
60 unsigned long *last_sample;
61 unsigned long idle_cycles;
62 unsigned long boost_freq;
63 unsigned long bumped_freq;
64 unsigned long *table;
65 int table_size;
66 u32 sample_count;
67 bool enable;
68 int sample_time;
69 int window_ms;
70 int min_samples;
71 unsigned long boost_step;
72 u8 boost_inc_coef;
73 u8 boost_dec_coef;
74};
75
76struct tegra2_stat_mon {
77 void __iomem *stat_mon_base;
78 void __iomem *vde_mon_base;
79 struct clk *stat_mon_clock;
80 struct mutex stat_mon_lock;
81 struct sampler avp_sampler;
82};
83
84static unsigned long sclk_table[] = {
85 300000,
86 240000,
87 200000,
88 150000,
89 120000,
90 100000,
91 80000,
92 75000,
93 60000,
94 50000,
95 48000,
96 40000
97};
98
99static struct tegra2_stat_mon *stat_mon;
100
101static inline u32 tegra2_stat_mon_read(u32 offset)
102{
103 return readl(stat_mon->stat_mon_base + offset);
104}
105
106static inline void tegra2_stat_mon_write(u32 value, u32 offset)
107{
108 writel(value, stat_mon->stat_mon_base + offset);
109}
110
111static inline u32 tegra2_vde_mon_read(u32 offset)
112{
113 return readl(stat_mon->vde_mon_base + offset);
114}
115
116static inline void tegra2_vde_mon_write(u32 value, u32 offset)
117{
118 writel(value, stat_mon->vde_mon_base + offset);
119}
120
121/* read the ticks in ISR and store */
122static irqreturn_t stat_mon_isr(int irq, void *data)
123{
124 u32 reg_val;
125
126 /* disable AVP monitor */
127 reg_val = tegra2_stat_mon_read(COP_MON_CTRL);
128 reg_val |= INT_STATUS;
129 tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
130
131 stat_mon->avp_sampler.idle_cycles =
132 tegra2_stat_mon_read(COP_MON_STATUS);
133
134 return IRQ_WAKE_THREAD;
135}
136
137
138static void add_active_sample(struct sampler *s, unsigned long cycles)
139{
140 if (s->last_sample == &s->active_cycles[WINDOW_SIZE - 1])
141 s->last_sample = &s->active_cycles[0];
142 else
143 s->last_sample++;
144
145 s->total_active_cycles -= *s->last_sample;
146 *s->last_sample = cycles;
147 s->total_active_cycles += *s->last_sample;
148}
149
150static unsigned long round_rate(struct sampler *s, unsigned long rate)
151{
152 int i;
153 unsigned long *table = s->table;
154
155 if (rate >= table[0])
156 return table[0];
157
158 for (i = 1; i < s->table_size; i++) {
159 if (rate <= table[i])
160 continue;
161 else {
162 return table[i-1];
163 break;
164 }
165 }
166 if (rate <= table[s->table_size - 1])
167 return table[s->table_size - 1];
168 return rate;
169}
170
171static void set_target_freq(struct sampler *s)
172{
173 unsigned long clock_rate;
174 unsigned long target_freq;
175 unsigned long active_count;
176
177 clock_rate = clk_get_rate(s->clock) / FREQ_MULT;
178 active_count = (s->sample_time + 1) * clock_rate;
179 active_count = (active_count > s->idle_cycles) ?
180 (active_count - s->idle_cycles) : (0);
181
182 s->sample_count++;
183
184 add_active_sample(s, active_count);
185
186 s->avg_freq = s->total_active_cycles / s->window_ms;
187
188 if ((s->idle_cycles >= (1 + (active_count >> 3))) &&
189 (s->bumped_freq >= s->avg_freq)) {
190 s->boost_freq = (s->boost_freq *
191 ((0x1 << BOOST_FRACTION_BITS) - s->boost_dec_coef))
192 >> BOOST_FRACTION_BITS;
193 if (s->boost_freq < s->boost_step)
194 s->boost_freq = 0;
195 } else if (s->sample_count < s->min_samples) {
196 s->sample_count++;
197 } else {
198 s->boost_freq = ((s->boost_freq *
199 ((0x1 << BOOST_FRACTION_BITS) + s->boost_inc_coef))
200 >> BOOST_FRACTION_BITS) + s->boost_step;
201 if (s->boost_freq > s->clock->max_rate)
202 s->boost_freq = s->clock->max_rate;
203 }
204
205 if ((s->avg_freq + LOWER_BAND) < s->bumped_freq)
206 s->bumped_freq = s->avg_freq + LOWER_BAND;
207 else if (s->avg_freq > (s->bumped_freq + UPPER_BAND))
208 s->bumped_freq = s->avg_freq - UPPER_BAND;
209
210 s->bumped_freq += (s->bumped_freq >> 3);
211
212 target_freq = max(s->bumped_freq, s->clock->min_rate);
213 target_freq += s->boost_freq;
214
215 active_count = target_freq;
216 target_freq = round_rate(s, target_freq) * FREQ_MULT;
217 clk_set_rate(s->clock, target_freq);
218}
219
220/* - process ticks in thread context
221 */
222static irqreturn_t stat_mon_isr_thread_fn(int irq, void *data)
223{
224 u32 reg_val = 0;
225
226 mutex_lock(&stat_mon->stat_mon_lock);
227 set_target_freq(&stat_mon->avp_sampler);
228 mutex_unlock(&stat_mon->stat_mon_lock);
229
230 /* start AVP sampler */
231 reg_val = tegra2_stat_mon_read(COP_MON_CTRL);
232 reg_val |= MON_ENABLE;
233 tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
234 return IRQ_HANDLED;
235}
236
237void tegra2_statmon_stop(void)
238{
239 u32 reg_val = 0;
240
241 /* disable AVP monitor */
242 reg_val |= INT_STATUS;
243 tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
244
245 clk_disable(stat_mon->stat_mon_clock);
246 clk_disable(stat_mon->avp_sampler.clock);
247}
248
249int tegra2_statmon_start(void)
250{
251 u32 reg_val = 0;
252
253 clk_enable(stat_mon->avp_sampler.clock);
254 clk_enable(stat_mon->stat_mon_clock);
255
256 /* disable AVP monitor */
257 reg_val |= INT_STATUS;
258 tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
259
260 /* start AVP sampler. also enable INT to CPU */
261 reg_val = 0;
262 reg_val |= MON_ENABLE;
263 reg_val |= INT_ENABLE;
264 reg_val |= ((stat_mon->avp_sampler.sample_time \
265 << SAMPLE_PERIOD_SHIFT) & SAMPLE_PERIOD_MASK);
266 tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
267 return 0;
268}
269
270static ssize_t tegra2_statmon_enable_show(struct sysdev_class *class,
271 struct sysdev_class_attribute *attr, char *buf)
272{
273 return sprintf(buf, "%d\n", stat_mon->avp_sampler.enable);
274}
275
276static ssize_t tegra2_statmon_enable_store(struct sysdev_class *class,
277 struct sysdev_class_attribute *attr, const char *buf, size_t count)
278{
279 int value;
280
281 mutex_lock(&stat_mon->stat_mon_lock);
282 sscanf(buf, "%d", &value);
283
284 if (value == 0 || value == 1)
285 stat_mon->avp_sampler.enable = value;
286 else {
287 mutex_unlock(&stat_mon->stat_mon_lock);
288 return -EINVAL;
289 }
290 mutex_unlock(&stat_mon->stat_mon_lock);
291
292 if (stat_mon->avp_sampler.enable)
293 tegra2_statmon_start();
294 else
295 tegra2_statmon_stop();
296
297 return 0;
298}
299
300static ssize_t tegra2_statmon_sample_time_show(struct sysdev_class *class,
301 struct sysdev_class_attribute *attr, char *buf)
302{
303 return sprintf(buf, "%d\n", stat_mon->avp_sampler.sample_time);
304}
305
306static ssize_t tegra2_statmon_sample_time_store(struct sysdev_class *class,
307 struct sysdev_class_attribute *attr,
308 const char *buf, size_t count)
309{
310 int value;
311
312 mutex_lock(&stat_mon->stat_mon_lock);
313 sscanf(buf, "%d", &value);
314 stat_mon->avp_sampler.sample_time = value;
315 mutex_unlock(&stat_mon->stat_mon_lock);
316
317 return count;
318}
319
320static struct sysdev_class tegra2_statmon_sysclass = {
321 .name = "tegra2_statmon",
322};
323
324#define TEGRA2_STATMON_ATTRIBUTE_EXPAND(_attr, _mode) \
325 static SYSDEV_CLASS_ATTR(_attr, _mode, \
326 tegra2_statmon_##_attr##_show, tegra2_statmon_##_attr##_store)
327
328TEGRA2_STATMON_ATTRIBUTE_EXPAND(enable, 0666);
329TEGRA2_STATMON_ATTRIBUTE_EXPAND(sample_time, 0666);
330
331#define TEGRA2_STATMON_ATTRIBUTE(_name) (&attr_##_name)
332
333static struct sysdev_class_attribute *tegra2_statmon_attrs[] = {
334 TEGRA2_STATMON_ATTRIBUTE(enable),
335 TEGRA2_STATMON_ATTRIBUTE(sample_time),
336 NULL,
337};
338
339static int sampler_init(struct sampler *s)
340{
341 int i;
342 struct clk *clock;
343 unsigned long clock_rate;
344 unsigned long active_count;
345
346 s->enable = false;
347 s->sample_time = 9;
348
349 clock = tegra_get_clock_by_name("mon.sclk");
350 if (IS_ERR(clock)) {
351 pr_err("%s: Couldn't get mon.sckl\n", __func__);
352 return -1;
353 }
354
355 if (clk_set_rate(clock, clock->min_rate)) {
356 pr_err("%s: Failed to set rate\n", __func__);
357 return -1;
358 }
359 clock_rate = clk_get_rate(clock) / FREQ_MULT;
360 active_count = clock_rate * (s->sample_time + 1);
361
362 for (i = 0; i < WINDOW_SIZE; i++)
363 s->active_cycles[i] = active_count;
364
365 s->clock = clock;
366 s->last_sample = &s->active_cycles[0];
367 s->total_active_cycles = active_count << 7;
368 s->window_ms = (s->sample_time + 1) << 7;
369 s->avg_freq = s->total_active_cycles / s->window_ms;
370 s->bumped_freq = s->avg_freq;
371 s->boost_freq = 0;
372
373 return 0;
374}
375
376static int tegra2_stat_mon_init(void)
377{
378 int rc, i;
379 int ret_val = 0;
380
381 stat_mon = kzalloc(sizeof(struct tegra2_stat_mon), GFP_KERNEL);
382 if (stat_mon == NULL) {
383 pr_err("%s: unable to alloc data struct.\n", __func__);
384 return -ENOMEM;
385 }
386
387 stat_mon->stat_mon_base = IO_ADDRESS(TEGRA_STATMON_BASE);
388 stat_mon->vde_mon_base = IO_ADDRESS(TEGRA_VDE_BASE);
389
390 stat_mon->stat_mon_clock = tegra_get_clock_by_name("stat_mon");
391 if (stat_mon->stat_mon_clock == NULL) {
392 pr_err("Failed to get stat mon clock");
393 return -1;
394 }
395
396 if (sampler_init(&stat_mon->avp_sampler))
397 return -1;
398
399 stat_mon->avp_sampler.table = sclk_table;
400 stat_mon->avp_sampler.table_size = ARRAY_SIZE(sclk_table);
401 stat_mon->avp_sampler.boost_step = 1000;
402 stat_mon->avp_sampler.boost_inc_coef = 255;
403 stat_mon->avp_sampler.boost_dec_coef = 128;
404 stat_mon->avp_sampler.min_samples = 3;
405
406 mutex_init(&stat_mon->stat_mon_lock);
407
408 /* /sys/devices/system/tegra2_statmon */
409 rc = sysdev_class_register(&tegra2_statmon_sysclass);
410 if (rc) {
411 pr_err("%s : Couldn't create statmon sysfs entry\n", __func__);
412 return 0;
413 }
414
415 for (i = 0; i < ARRAY_SIZE(tegra2_statmon_attrs) - 1; i++) {
416 rc = sysdev_class_create_file(&tegra2_statmon_sysclass,
417 tegra2_statmon_attrs[i]);
418 if (rc) {
419 pr_err("%s: Failed to create sys class\n", __func__);
420 sysdev_class_unregister(&tegra2_statmon_sysclass);
421 kfree(stat_mon);
422 return 0;
423 }
424 }
425
426 ret_val = request_threaded_irq(INT_SYS_STATS_MON, stat_mon_isr,
427 stat_mon_isr_thread_fn, 0, "stat_mon_int", NULL);
428 if (ret_val) {
429 pr_err("%s: cannot register INT_SYS_STATS_MON handler, \
430 ret_val = 0x%x\n", __func__, ret_val);
431 tegra2_statmon_stop();
432 stat_mon->avp_sampler.enable = false;
433 kfree(stat_mon);
434 return ret_val;
435 }
436
437 return 0;
438}
439
440late_initcall(tegra2_stat_mon_init);