diff options
Diffstat (limited to 'arch/arm/mach-tegra/tegra3_actmon.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra3_actmon.c | 847 |
1 files changed, 847 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra3_actmon.c b/arch/arm/mach-tegra/tegra3_actmon.c new file mode 100644 index 00000000000..43130f960ec --- /dev/null +++ b/arch/arm/mach-tegra/tegra3_actmon.c | |||
@@ -0,0 +1,847 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011, NVIDIA Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; version 2 of the License. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along | ||
14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/err.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/clk.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/suspend.h> | ||
25 | #include <linux/debugfs.h> | ||
26 | #include <linux/seq_file.h> | ||
27 | #include <linux/uaccess.h> | ||
28 | |||
29 | #include <mach/iomap.h> | ||
30 | #include <mach/irqs.h> | ||
31 | #include <mach/clk.h> | ||
32 | |||
33 | #include "clock.h" | ||
34 | |||
35 | #define ACTMON_GLB_STATUS 0x00 | ||
36 | #define ACTMON_GLB_PERIOD_CTRL 0x04 | ||
37 | |||
38 | #define ACTMON_DEV_CTRL 0x00 | ||
39 | #define ACTMON_DEV_CTRL_ENB (0x1 << 31) | ||
40 | #define ACTMON_DEV_CTRL_UP_WMARK_ENB (0x1 << 30) | ||
41 | #define ACTMON_DEV_CTRL_DOWN_WMARK_ENB (0x1 << 29) | ||
42 | #define ACTMON_DEV_CTRL_UP_WMARK_NUM_SHIFT 26 | ||
43 | #define ACTMON_DEV_CTRL_UP_WMARK_NUM_MASK (0x7 << 26) | ||
44 | #define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_SHIFT 23 | ||
45 | #define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK (0x7 << 23) | ||
46 | #define ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB (0x1 << 21) | ||
47 | #define ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB (0x1 << 20) | ||
48 | #define ACTMON_DEV_CTRL_PERIODIC_ENB (0x1 << 18) | ||
49 | #define ACTMON_DEV_CTRL_K_VAL_SHIFT 10 | ||
50 | #define ACTMON_DEV_CTRL_K_VAL_MASK (0x7 << 10) | ||
51 | |||
52 | #define ACTMON_DEV_UP_WMARK 0x04 | ||
53 | #define ACTMON_DEV_DOWN_WMARK 0x08 | ||
54 | #define ACTMON_DEV_INIT_AVG 0x0c | ||
55 | #define ACTMON_DEV_AVG_UP_WMARK 0x10 | ||
56 | #define ACTMON_DEV_AVG_DOWN_WMARK 0x14 | ||
57 | |||
58 | #define ACTMON_DEV_COUNT_WEGHT 0x18 | ||
59 | #define ACTMON_DEV_COUNT 0x1c | ||
60 | #define ACTMON_DEV_AVG_COUNT 0x20 | ||
61 | |||
62 | #define ACTMON_DEV_INTR_STATUS 0x24 | ||
63 | #define ACTMON_DEV_INTR_UP_WMARK (0x1 << 31) | ||
64 | #define ACTMON_DEV_INTR_DOWN_WMARK (0x1 << 30) | ||
65 | #define ACTMON_DEV_INTR_AVG_DOWN_WMARK (0x1 << 25) | ||
66 | #define ACTMON_DEV_INTR_AVG_UP_WMARK (0x1 << 24) | ||
67 | |||
68 | #define ACTMON_DEFAULT_AVG_WINDOW_LOG2 6 | ||
69 | #define ACTMON_DEFAULT_AVG_BAND 6 /* 1/10 of % */ | ||
70 | |||
71 | enum actmon_type { | ||
72 | ACTMON_LOAD_SAMPLER, | ||
73 | ACTMON_FREQ_SAMPLER, | ||
74 | }; | ||
75 | |||
76 | enum actmon_state { | ||
77 | ACTMON_UNINITIALIZED = -1, | ||
78 | ACTMON_OFF = 0, | ||
79 | ACTMON_ON = 1, | ||
80 | ACTMON_SUSPENDED = 2, | ||
81 | }; | ||
82 | |||
83 | #define ACTMON_DEFAULT_SAMPLING_PERIOD 12 | ||
84 | static u8 actmon_sampling_period; | ||
85 | |||
86 | static unsigned long actmon_clk_freq; | ||
87 | |||
88 | |||
89 | /* Units: | ||
90 | * - frequency in kHz | ||
91 | * - coefficients, and thresholds in % | ||
92 | * - sampling period in ms | ||
93 | * - window in sample periods (value = setting + 1) | ||
94 | */ | ||
95 | struct actmon_dev { | ||
96 | u32 reg; | ||
97 | u32 glb_status_irq_mask; | ||
98 | const char *dev_id; | ||
99 | const char *con_id; | ||
100 | struct clk *clk; | ||
101 | |||
102 | unsigned long max_freq; | ||
103 | unsigned long target_freq; | ||
104 | unsigned long cur_freq; | ||
105 | |||
106 | unsigned long avg_actv_freq; | ||
107 | unsigned long avg_band_freq; | ||
108 | unsigned int avg_sustain_coef; | ||
109 | u32 avg_count; | ||
110 | |||
111 | unsigned long boost_freq; | ||
112 | unsigned long boost_freq_step; | ||
113 | unsigned int boost_up_coef; | ||
114 | unsigned int boost_down_coef; | ||
115 | unsigned int boost_up_threshold; | ||
116 | unsigned int boost_down_threshold; | ||
117 | |||
118 | u8 up_wmark_window; | ||
119 | u8 down_wmark_window; | ||
120 | u8 avg_window_log2; | ||
121 | u32 count_weight; | ||
122 | |||
123 | enum actmon_type type; | ||
124 | enum actmon_state state; | ||
125 | enum actmon_state saved_state; | ||
126 | |||
127 | spinlock_t lock; | ||
128 | |||
129 | struct notifier_block rate_change_nb; | ||
130 | }; | ||
131 | |||
132 | static void __iomem *actmon_base = IO_ADDRESS(TEGRA_ACTMON_BASE); | ||
133 | |||
134 | static inline u32 actmon_readl(u32 offset) | ||
135 | { | ||
136 | return __raw_readl((u32)actmon_base + offset); | ||
137 | } | ||
138 | static inline void actmon_writel(u32 val, u32 offset) | ||
139 | { | ||
140 | __raw_writel(val, (u32)actmon_base + offset); | ||
141 | } | ||
142 | static inline void actmon_wmb(void) | ||
143 | { | ||
144 | wmb(); | ||
145 | actmon_readl(ACTMON_GLB_STATUS); | ||
146 | } | ||
147 | |||
148 | #define offs(x) (dev->reg + x) | ||
149 | |||
150 | static inline unsigned long do_percent(unsigned long val, unsigned int pct) | ||
151 | { | ||
152 | return val * pct / 100; | ||
153 | } | ||
154 | |||
155 | static inline void actmon_dev_up_wmark_set(struct actmon_dev *dev) | ||
156 | { | ||
157 | u32 val; | ||
158 | unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? | ||
159 | dev->cur_freq : actmon_clk_freq; | ||
160 | |||
161 | val = freq * actmon_sampling_period; | ||
162 | actmon_writel(do_percent(val, dev->boost_up_threshold), | ||
163 | offs(ACTMON_DEV_UP_WMARK)); | ||
164 | } | ||
165 | |||
166 | static inline void actmon_dev_down_wmark_set(struct actmon_dev *dev) | ||
167 | { | ||
168 | u32 val; | ||
169 | unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? | ||
170 | dev->cur_freq : actmon_clk_freq; | ||
171 | |||
172 | val = freq * actmon_sampling_period; | ||
173 | actmon_writel(do_percent(val, dev->boost_down_threshold), | ||
174 | offs(ACTMON_DEV_DOWN_WMARK)); | ||
175 | } | ||
176 | |||
177 | static inline void actmon_dev_wmark_set(struct actmon_dev *dev) | ||
178 | { | ||
179 | u32 val; | ||
180 | unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? | ||
181 | dev->cur_freq : actmon_clk_freq; | ||
182 | |||
183 | val = freq * actmon_sampling_period; | ||
184 | actmon_writel(do_percent(val, dev->boost_up_threshold), | ||
185 | offs(ACTMON_DEV_UP_WMARK)); | ||
186 | actmon_writel(do_percent(val, dev->boost_down_threshold), | ||
187 | offs(ACTMON_DEV_DOWN_WMARK)); | ||
188 | } | ||
189 | |||
190 | static inline void actmon_dev_avg_wmark_set(struct actmon_dev *dev) | ||
191 | { | ||
192 | u32 avg = dev->avg_count; | ||
193 | u32 band = dev->avg_band_freq * actmon_sampling_period; | ||
194 | |||
195 | actmon_writel(avg + band, offs(ACTMON_DEV_AVG_UP_WMARK)); | ||
196 | avg = max(avg, band); | ||
197 | actmon_writel(avg - band, offs(ACTMON_DEV_AVG_DOWN_WMARK)); | ||
198 | } | ||
199 | |||
200 | static unsigned long actmon_dev_avg_freq_get(struct actmon_dev *dev) | ||
201 | { | ||
202 | u64 val; | ||
203 | |||
204 | if (dev->type == ACTMON_FREQ_SAMPLER) | ||
205 | return dev->avg_count / actmon_sampling_period; | ||
206 | |||
207 | val = (u64)dev->avg_count * dev->cur_freq; | ||
208 | do_div(val, actmon_clk_freq * actmon_sampling_period); | ||
209 | return (u32)val; | ||
210 | } | ||
211 | |||
212 | /* Activity monitor sampling operations */ | ||
213 | irqreturn_t actmon_dev_isr(int irq, void *dev_id) | ||
214 | { | ||
215 | u32 val; | ||
216 | unsigned long flags; | ||
217 | struct actmon_dev *dev = (struct actmon_dev *)dev_id; | ||
218 | |||
219 | val = actmon_readl(ACTMON_GLB_STATUS) & dev->glb_status_irq_mask; | ||
220 | if (!val) | ||
221 | return IRQ_NONE; | ||
222 | |||
223 | spin_lock_irqsave(&dev->lock, flags); | ||
224 | |||
225 | dev->avg_count = actmon_readl(offs(ACTMON_DEV_AVG_COUNT)); | ||
226 | actmon_dev_avg_wmark_set(dev); | ||
227 | |||
228 | val = actmon_readl(offs(ACTMON_DEV_INTR_STATUS)); | ||
229 | if (val & ACTMON_DEV_INTR_UP_WMARK) { | ||
230 | val = actmon_readl(offs(ACTMON_DEV_CTRL)) | | ||
231 | ACTMON_DEV_CTRL_UP_WMARK_ENB | | ||
232 | ACTMON_DEV_CTRL_DOWN_WMARK_ENB; | ||
233 | |||
234 | dev->boost_freq = dev->boost_freq_step + | ||
235 | do_percent(dev->boost_freq, dev->boost_up_coef); | ||
236 | if (dev->boost_freq >= dev->max_freq) { | ||
237 | dev->boost_freq = dev->max_freq; | ||
238 | val &= ~ACTMON_DEV_CTRL_UP_WMARK_ENB; | ||
239 | } | ||
240 | actmon_writel(val, offs(ACTMON_DEV_CTRL)); | ||
241 | } else if (val & ACTMON_DEV_INTR_DOWN_WMARK) { | ||
242 | val = actmon_readl(offs(ACTMON_DEV_CTRL)) | | ||
243 | ACTMON_DEV_CTRL_UP_WMARK_ENB | | ||
244 | ACTMON_DEV_CTRL_DOWN_WMARK_ENB; | ||
245 | |||
246 | dev->boost_freq = | ||
247 | do_percent(dev->boost_freq, dev->boost_down_coef); | ||
248 | if (dev->boost_freq < (dev->boost_freq_step >> 1)) { | ||
249 | dev->boost_freq = 0; | ||
250 | val &= ~ACTMON_DEV_CTRL_DOWN_WMARK_ENB; | ||
251 | } | ||
252 | actmon_writel(val, offs(ACTMON_DEV_CTRL)); | ||
253 | } | ||
254 | |||
255 | actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); /* clr all */ | ||
256 | actmon_wmb(); | ||
257 | |||
258 | spin_unlock_irqrestore(&dev->lock, flags); | ||
259 | return IRQ_WAKE_THREAD; | ||
260 | } | ||
261 | |||
262 | irqreturn_t actmon_dev_fn(int irq, void *dev_id) | ||
263 | { | ||
264 | unsigned long flags, freq; | ||
265 | struct actmon_dev *dev = (struct actmon_dev *)dev_id; | ||
266 | |||
267 | spin_lock_irqsave(&dev->lock, flags); | ||
268 | |||
269 | if (dev->state != ACTMON_ON) { | ||
270 | spin_unlock_irqrestore(&dev->lock, flags); | ||
271 | return IRQ_HANDLED; | ||
272 | } | ||
273 | |||
274 | freq = actmon_dev_avg_freq_get(dev); | ||
275 | dev->avg_actv_freq = freq; | ||
276 | freq = do_percent(freq, dev->avg_sustain_coef); | ||
277 | freq += dev->boost_freq; | ||
278 | dev->target_freq = freq; | ||
279 | |||
280 | spin_unlock_irqrestore(&dev->lock, flags); | ||
281 | |||
282 | pr_debug("%s.%s(kHz): avg: %lu, target: %lu current: %lu\n", | ||
283 | dev->dev_id, dev->con_id, dev->avg_actv_freq, | ||
284 | dev->target_freq, dev->cur_freq); | ||
285 | clk_set_rate(dev->clk, freq * 1000); | ||
286 | |||
287 | return IRQ_HANDLED; | ||
288 | } | ||
289 | |||
290 | static int actmon_rate_notify_cb( | ||
291 | struct notifier_block *nb, unsigned long rate, void *v) | ||
292 | { | ||
293 | unsigned long flags; | ||
294 | struct actmon_dev *dev = container_of( | ||
295 | nb, struct actmon_dev, rate_change_nb); | ||
296 | |||
297 | spin_lock_irqsave(&dev->lock, flags); | ||
298 | |||
299 | dev->cur_freq = rate / 1000; | ||
300 | if (dev->type == ACTMON_FREQ_SAMPLER) { | ||
301 | actmon_dev_wmark_set(dev); | ||
302 | actmon_wmb(); | ||
303 | } | ||
304 | |||
305 | spin_unlock_irqrestore(&dev->lock, flags); | ||
306 | return NOTIFY_OK; | ||
307 | }; | ||
308 | |||
309 | /* Activity monitor configuration and control */ | ||
310 | static void actmon_dev_configure(struct actmon_dev *dev, unsigned long freq) | ||
311 | { | ||
312 | u32 val; | ||
313 | |||
314 | dev->cur_freq = freq; | ||
315 | dev->target_freq = freq; | ||
316 | dev->avg_actv_freq = freq; | ||
317 | |||
318 | if (dev->type == ACTMON_FREQ_SAMPLER) { | ||
319 | dev->avg_count = dev->cur_freq * actmon_sampling_period; | ||
320 | dev->avg_band_freq = dev->max_freq * | ||
321 | ACTMON_DEFAULT_AVG_BAND / 1000; | ||
322 | } else { | ||
323 | dev->avg_count = actmon_clk_freq * actmon_sampling_period; | ||
324 | dev->avg_band_freq = actmon_clk_freq * | ||
325 | ACTMON_DEFAULT_AVG_BAND / 1000; | ||
326 | } | ||
327 | actmon_writel(dev->avg_count, offs(ACTMON_DEV_INIT_AVG)); | ||
328 | |||
329 | BUG_ON(!dev->boost_up_threshold); | ||
330 | dev->avg_sustain_coef = 100 * 100 / dev->boost_up_threshold; | ||
331 | actmon_dev_avg_wmark_set(dev); | ||
332 | actmon_dev_wmark_set(dev); | ||
333 | |||
334 | actmon_writel(dev->count_weight, offs(ACTMON_DEV_COUNT_WEGHT)); | ||
335 | actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); /* clr all */ | ||
336 | |||
337 | val = ACTMON_DEV_CTRL_PERIODIC_ENB | ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB | | ||
338 | ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB; | ||
339 | val |= ((dev->avg_window_log2 - 1) << ACTMON_DEV_CTRL_K_VAL_SHIFT) & | ||
340 | ACTMON_DEV_CTRL_K_VAL_MASK; | ||
341 | val |= ((dev->down_wmark_window - 1) << | ||
342 | ACTMON_DEV_CTRL_DOWN_WMARK_NUM_SHIFT) & | ||
343 | ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK; | ||
344 | val |= ((dev->up_wmark_window - 1) << | ||
345 | ACTMON_DEV_CTRL_UP_WMARK_NUM_SHIFT) & | ||
346 | ACTMON_DEV_CTRL_UP_WMARK_NUM_MASK; | ||
347 | val |= ACTMON_DEV_CTRL_DOWN_WMARK_ENB | ACTMON_DEV_CTRL_UP_WMARK_ENB; | ||
348 | actmon_writel(val, offs(ACTMON_DEV_CTRL)); | ||
349 | actmon_wmb(); | ||
350 | } | ||
351 | |||
352 | static void actmon_dev_enable(struct actmon_dev *dev) | ||
353 | { | ||
354 | u32 val; | ||
355 | unsigned long flags; | ||
356 | |||
357 | spin_lock_irqsave(&dev->lock, flags); | ||
358 | |||
359 | if (dev->state == ACTMON_OFF) { | ||
360 | dev->state = ACTMON_ON; | ||
361 | |||
362 | val = actmon_readl(offs(ACTMON_DEV_CTRL)); | ||
363 | val |= ACTMON_DEV_CTRL_ENB; | ||
364 | actmon_writel(val, offs(ACTMON_DEV_CTRL)); | ||
365 | actmon_wmb(); | ||
366 | } | ||
367 | spin_unlock_irqrestore(&dev->lock, flags); | ||
368 | } | ||
369 | |||
370 | static void actmon_dev_disable(struct actmon_dev *dev) | ||
371 | { | ||
372 | u32 val; | ||
373 | unsigned long flags; | ||
374 | |||
375 | spin_lock_irqsave(&dev->lock, flags); | ||
376 | |||
377 | if (dev->state == ACTMON_ON) { | ||
378 | dev->state = ACTMON_OFF; | ||
379 | |||
380 | val = actmon_readl(offs(ACTMON_DEV_CTRL)); | ||
381 | val &= ~ACTMON_DEV_CTRL_ENB; | ||
382 | actmon_writel(val, offs(ACTMON_DEV_CTRL)); | ||
383 | actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); | ||
384 | actmon_wmb(); | ||
385 | } | ||
386 | spin_unlock_irqrestore(&dev->lock, flags); | ||
387 | } | ||
388 | |||
389 | static void actmon_dev_suspend(struct actmon_dev *dev) | ||
390 | { | ||
391 | u32 val; | ||
392 | unsigned long flags; | ||
393 | |||
394 | spin_lock_irqsave(&dev->lock, flags); | ||
395 | |||
396 | if ((dev->state == ACTMON_ON) || (dev->state == ACTMON_OFF)){ | ||
397 | dev->saved_state = dev->state; | ||
398 | dev->state = ACTMON_SUSPENDED; | ||
399 | |||
400 | val = actmon_readl(offs(ACTMON_DEV_CTRL)); | ||
401 | val &= ~ACTMON_DEV_CTRL_ENB; | ||
402 | actmon_writel(val, offs(ACTMON_DEV_CTRL)); | ||
403 | actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); | ||
404 | actmon_wmb(); | ||
405 | } | ||
406 | spin_unlock_irqrestore(&dev->lock, flags); | ||
407 | } | ||
408 | |||
409 | static void actmon_dev_resume(struct actmon_dev *dev) | ||
410 | { | ||
411 | u32 val; | ||
412 | unsigned long flags; | ||
413 | unsigned long freq = clk_get_rate(dev->clk) / 1000; | ||
414 | |||
415 | spin_lock_irqsave(&dev->lock, flags); | ||
416 | |||
417 | if (dev->state == ACTMON_SUSPENDED) { | ||
418 | actmon_dev_configure(dev, freq); | ||
419 | dev->state = dev->saved_state; | ||
420 | if (dev->state == ACTMON_ON) { | ||
421 | val = actmon_readl(offs(ACTMON_DEV_CTRL)); | ||
422 | val |= ACTMON_DEV_CTRL_ENB; | ||
423 | actmon_writel(val, offs(ACTMON_DEV_CTRL)); | ||
424 | actmon_wmb(); | ||
425 | } | ||
426 | } | ||
427 | spin_unlock_irqrestore(&dev->lock, flags); | ||
428 | } | ||
429 | |||
430 | static int __init actmon_dev_init(struct actmon_dev *dev) | ||
431 | { | ||
432 | int ret; | ||
433 | struct clk *p; | ||
434 | unsigned long freq; | ||
435 | |||
436 | spin_lock_init(&dev->lock); | ||
437 | |||
438 | dev->clk = clk_get_sys(dev->dev_id, dev->con_id); | ||
439 | if (IS_ERR(dev->clk)) { | ||
440 | pr_err("Failed to find %s.%s clock\n", | ||
441 | dev->dev_id, dev->con_id); | ||
442 | return -ENODEV; | ||
443 | } | ||
444 | dev->max_freq = clk_round_rate(dev->clk, ULONG_MAX); | ||
445 | clk_set_rate(dev->clk, dev->max_freq); | ||
446 | dev->max_freq /= 1000; | ||
447 | freq = clk_get_rate(dev->clk) / 1000; | ||
448 | actmon_dev_configure(dev, freq); | ||
449 | |||
450 | /* actmon device controls shared bus user clock, but rate | ||
451 | change notification should come from bus clock itself */ | ||
452 | p = clk_get_parent(dev->clk); | ||
453 | BUG_ON(!p); | ||
454 | |||
455 | if (dev->rate_change_nb.notifier_call) { | ||
456 | ret = tegra_register_clk_rate_notifier(p, &dev->rate_change_nb); | ||
457 | if (ret) { | ||
458 | pr_err("Failed to register %s rate change notifier" | ||
459 | " for %s\n", p->name, dev->dev_id); | ||
460 | return ret; | ||
461 | } | ||
462 | } | ||
463 | |||
464 | ret = request_threaded_irq(INT_ACTMON, actmon_dev_isr, actmon_dev_fn, | ||
465 | IRQF_SHARED, dev->dev_id, dev); | ||
466 | if (ret) { | ||
467 | pr_err("Failed irq %d request for %s.%s\n", | ||
468 | INT_ACTMON, dev->dev_id, dev->con_id); | ||
469 | tegra_unregister_clk_rate_notifier(p, &dev->rate_change_nb); | ||
470 | return ret; | ||
471 | } | ||
472 | |||
473 | dev->state = ACTMON_OFF; | ||
474 | actmon_dev_enable(dev); | ||
475 | clk_enable(dev->clk); | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | /* EMC activity monitor: frequency sampling device: | ||
480 | * activity counter is incremented every 256 memory transactions, and | ||
481 | * each transaction takes 2 EMC clocks; count_weight = 512. | ||
482 | */ | ||
483 | static struct actmon_dev actmon_dev_emc = { | ||
484 | .reg = 0x1c0, | ||
485 | .glb_status_irq_mask = (0x1 << 26), | ||
486 | .dev_id = "tegra_actmon", | ||
487 | .con_id = "emc", | ||
488 | |||
489 | .boost_freq_step = 16000, | ||
490 | .boost_up_coef = 200, | ||
491 | .boost_down_coef = 50, | ||
492 | .boost_up_threshold = 60, | ||
493 | .boost_down_threshold = 40, | ||
494 | |||
495 | .up_wmark_window = 1, | ||
496 | .down_wmark_window = 3, | ||
497 | .avg_window_log2 = ACTMON_DEFAULT_AVG_WINDOW_LOG2, | ||
498 | .count_weight = 0x200, | ||
499 | |||
500 | .type = ACTMON_FREQ_SAMPLER, | ||
501 | .state = ACTMON_UNINITIALIZED, | ||
502 | |||
503 | .rate_change_nb = { | ||
504 | .notifier_call = actmon_rate_notify_cb, | ||
505 | }, | ||
506 | }; | ||
507 | |||
508 | /* AVP activity monitor: load sampling device: | ||
509 | * activity counter is incremented on every actmon clock pulse while | ||
510 | * AVP is not halted by flow controller; count_weight = 1. | ||
511 | */ | ||
512 | static struct actmon_dev actmon_dev_avp = { | ||
513 | .reg = 0x0c0, | ||
514 | .glb_status_irq_mask = (0x1 << 30), | ||
515 | .dev_id = "tegra_actmon", | ||
516 | .con_id = "avp", | ||
517 | |||
518 | .boost_freq_step = 8000, | ||
519 | .boost_up_coef = 200, | ||
520 | .boost_down_coef = 50, | ||
521 | .boost_up_threshold = 75, | ||
522 | .boost_down_threshold = 50, | ||
523 | |||
524 | .up_wmark_window = 1, | ||
525 | .down_wmark_window = 3, | ||
526 | .avg_window_log2 = ACTMON_DEFAULT_AVG_WINDOW_LOG2, | ||
527 | .count_weight = 0x1, | ||
528 | |||
529 | .type = ACTMON_LOAD_SAMPLER, | ||
530 | .state = ACTMON_UNINITIALIZED, | ||
531 | |||
532 | .rate_change_nb = { | ||
533 | .notifier_call = actmon_rate_notify_cb, | ||
534 | }, | ||
535 | }; | ||
536 | |||
537 | static struct actmon_dev *actmon_devices[] = { | ||
538 | &actmon_dev_emc, | ||
539 | &actmon_dev_avp, | ||
540 | }; | ||
541 | |||
542 | /* Activity monitor suspend/resume */ | ||
543 | static int actmon_pm_notify(struct notifier_block *nb, | ||
544 | unsigned long event, void *data) | ||
545 | { | ||
546 | int i; | ||
547 | |||
548 | switch (event) { | ||
549 | case PM_SUSPEND_PREPARE: | ||
550 | for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) | ||
551 | actmon_dev_suspend(actmon_devices[i]); | ||
552 | break; | ||
553 | case PM_POST_SUSPEND: | ||
554 | actmon_writel(actmon_sampling_period - 1, | ||
555 | ACTMON_GLB_PERIOD_CTRL); | ||
556 | for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) | ||
557 | actmon_dev_resume(actmon_devices[i]); | ||
558 | break; | ||
559 | } | ||
560 | |||
561 | return NOTIFY_OK; | ||
562 | }; | ||
563 | |||
564 | static struct notifier_block actmon_pm_nb = { | ||
565 | .notifier_call = actmon_pm_notify, | ||
566 | }; | ||
567 | |||
568 | #ifdef CONFIG_DEBUG_FS | ||
569 | |||
570 | #define RW_MODE (S_IWUSR | S_IRUGO) | ||
571 | #define RO_MODE S_IRUGO | ||
572 | |||
573 | static struct dentry *clk_debugfs_root; | ||
574 | |||
575 | static int type_show(struct seq_file *s, void *data) | ||
576 | { | ||
577 | struct actmon_dev *dev = s->private; | ||
578 | |||
579 | seq_printf(s, "%s\n", (dev->type == ACTMON_LOAD_SAMPLER) ? | ||
580 | "Load Activity Monitor" : "Frequency Activity Monitor"); | ||
581 | return 0; | ||
582 | } | ||
583 | static int type_open(struct inode *inode, struct file *file) | ||
584 | { | ||
585 | return single_open(file, type_show, inode->i_private); | ||
586 | } | ||
587 | static const struct file_operations type_fops = { | ||
588 | .open = type_open, | ||
589 | .read = seq_read, | ||
590 | .llseek = seq_lseek, | ||
591 | .release = single_release, | ||
592 | }; | ||
593 | |||
594 | static int actv_get(void *data, u64 *val) | ||
595 | { | ||
596 | unsigned long flags; | ||
597 | struct actmon_dev *dev = data; | ||
598 | |||
599 | spin_lock_irqsave(&dev->lock, flags); | ||
600 | *val = actmon_dev_avg_freq_get(dev); | ||
601 | spin_unlock_irqrestore(&dev->lock, flags); | ||
602 | return 0; | ||
603 | } | ||
604 | DEFINE_SIMPLE_ATTRIBUTE(actv_fops, actv_get, NULL, "%llu\n"); | ||
605 | |||
606 | static int step_get(void *data, u64 *val) | ||
607 | { | ||
608 | struct actmon_dev *dev = data; | ||
609 | *val = dev->boost_freq_step * 100 / dev->max_freq; | ||
610 | return 0; | ||
611 | } | ||
612 | static int step_set(void *data, u64 val) | ||
613 | { | ||
614 | unsigned long flags; | ||
615 | struct actmon_dev *dev = data; | ||
616 | |||
617 | if (val > 100) | ||
618 | val = 100; | ||
619 | |||
620 | spin_lock_irqsave(&dev->lock, flags); | ||
621 | dev->boost_freq_step = do_percent(dev->max_freq, (unsigned int)val); | ||
622 | spin_unlock_irqrestore(&dev->lock, flags); | ||
623 | return 0; | ||
624 | } | ||
625 | DEFINE_SIMPLE_ATTRIBUTE(step_fops, step_get, step_set, "%llu\n"); | ||
626 | |||
627 | static int up_threshold_get(void *data, u64 *val) | ||
628 | { | ||
629 | struct actmon_dev *dev = data; | ||
630 | *val = dev->boost_up_threshold; | ||
631 | return 0; | ||
632 | } | ||
633 | static int up_threshold_set(void *data, u64 val) | ||
634 | { | ||
635 | unsigned long flags; | ||
636 | struct actmon_dev *dev = data; | ||
637 | unsigned int up_threshold = (unsigned int)val; | ||
638 | |||
639 | if (up_threshold > 100) | ||
640 | up_threshold = 100; | ||
641 | |||
642 | spin_lock_irqsave(&dev->lock, flags); | ||
643 | |||
644 | if (up_threshold <= dev->boost_down_threshold) | ||
645 | up_threshold = dev->boost_down_threshold; | ||
646 | if (up_threshold) | ||
647 | dev->avg_sustain_coef = 100 * 100 / up_threshold; | ||
648 | dev->boost_up_threshold = up_threshold; | ||
649 | |||
650 | actmon_dev_up_wmark_set(dev); | ||
651 | actmon_wmb(); | ||
652 | |||
653 | spin_unlock_irqrestore(&dev->lock, flags); | ||
654 | return 0; | ||
655 | } | ||
656 | DEFINE_SIMPLE_ATTRIBUTE(up_threshold_fops, up_threshold_get, | ||
657 | up_threshold_set, "%llu\n"); | ||
658 | |||
659 | static int down_threshold_get(void *data, u64 *val) | ||
660 | { | ||
661 | struct actmon_dev *dev = data; | ||
662 | *val = dev->boost_down_threshold; | ||
663 | return 0; | ||
664 | } | ||
665 | static int down_threshold_set(void *data, u64 val) | ||
666 | { | ||
667 | unsigned long flags; | ||
668 | struct actmon_dev *dev = data; | ||
669 | unsigned int down_threshold = (unsigned int)val; | ||
670 | |||
671 | spin_lock_irqsave(&dev->lock, flags); | ||
672 | |||
673 | if (down_threshold >= dev->boost_up_threshold) | ||
674 | down_threshold = dev->boost_up_threshold; | ||
675 | dev->boost_down_threshold = down_threshold; | ||
676 | |||
677 | actmon_dev_down_wmark_set(dev); | ||
678 | actmon_wmb(); | ||
679 | |||
680 | spin_unlock_irqrestore(&dev->lock, flags); | ||
681 | return 0; | ||
682 | } | ||
683 | DEFINE_SIMPLE_ATTRIBUTE(down_threshold_fops, down_threshold_get, | ||
684 | down_threshold_set, "%llu\n"); | ||
685 | |||
686 | static int state_get(void *data, u64 *val) | ||
687 | { | ||
688 | struct actmon_dev *dev = data; | ||
689 | *val = dev->state; | ||
690 | return 0; | ||
691 | } | ||
692 | static int state_set(void *data, u64 val) | ||
693 | { | ||
694 | struct actmon_dev *dev = data; | ||
695 | |||
696 | if (val) | ||
697 | actmon_dev_enable(dev); | ||
698 | else | ||
699 | actmon_dev_disable(dev); | ||
700 | return 0; | ||
701 | } | ||
702 | DEFINE_SIMPLE_ATTRIBUTE(state_fops, state_get, state_set, "%llu\n"); | ||
703 | |||
704 | static int period_get(void *data, u64 *val) | ||
705 | { | ||
706 | *val = actmon_sampling_period; | ||
707 | return 0; | ||
708 | } | ||
709 | static int period_set(void *data, u64 val) | ||
710 | { | ||
711 | int i; | ||
712 | unsigned long flags; | ||
713 | u8 period = (u8)val; | ||
714 | |||
715 | if (period) { | ||
716 | actmon_sampling_period = period; | ||
717 | actmon_writel(period - 1, ACTMON_GLB_PERIOD_CTRL); | ||
718 | |||
719 | for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { | ||
720 | struct actmon_dev *dev = actmon_devices[i]; | ||
721 | spin_lock_irqsave(&dev->lock, flags); | ||
722 | actmon_dev_wmark_set(dev); | ||
723 | spin_unlock_irqrestore(&dev->lock, flags); | ||
724 | } | ||
725 | actmon_wmb(); | ||
726 | return 0; | ||
727 | } | ||
728 | return -EINVAL; | ||
729 | } | ||
730 | DEFINE_SIMPLE_ATTRIBUTE(period_fops, period_get, period_set, "%llu\n"); | ||
731 | |||
732 | |||
733 | static int actmon_debugfs_create_dev(struct actmon_dev *dev) | ||
734 | { | ||
735 | struct dentry *dir, *d; | ||
736 | |||
737 | if (dev->state == ACTMON_UNINITIALIZED) | ||
738 | return 0; | ||
739 | |||
740 | dir = debugfs_create_dir(dev->con_id, clk_debugfs_root); | ||
741 | if (!dir) | ||
742 | return -ENOMEM; | ||
743 | |||
744 | d = debugfs_create_file( | ||
745 | "actv_type", RO_MODE, dir, dev, &type_fops); | ||
746 | if (!d) | ||
747 | return -ENOMEM; | ||
748 | |||
749 | d = debugfs_create_file( | ||
750 | "avg_activity", RO_MODE, dir, dev, &actv_fops); | ||
751 | if (!d) | ||
752 | return -ENOMEM; | ||
753 | |||
754 | d = debugfs_create_file( | ||
755 | "boost_step", RW_MODE, dir, dev, &step_fops); | ||
756 | if (!d) | ||
757 | return -ENOMEM; | ||
758 | |||
759 | d = debugfs_create_u32( | ||
760 | "boost_rate_dec", RW_MODE, dir, (u32 *)&dev->boost_down_coef); | ||
761 | if (!d) | ||
762 | return -ENOMEM; | ||
763 | |||
764 | d = debugfs_create_u32( | ||
765 | "boost_rate_inc", RW_MODE, dir, (u32 *)&dev->boost_up_coef); | ||
766 | if (!d) | ||
767 | return -ENOMEM; | ||
768 | |||
769 | d = debugfs_create_file( | ||
770 | "boost_threshold_dn", RW_MODE, dir, dev, &down_threshold_fops); | ||
771 | if (!d) | ||
772 | return -ENOMEM; | ||
773 | |||
774 | d = debugfs_create_file( | ||
775 | "boost_threshold_up", RW_MODE, dir, dev, &up_threshold_fops); | ||
776 | if (!d) | ||
777 | return -ENOMEM; | ||
778 | |||
779 | d = debugfs_create_file( | ||
780 | "state", RW_MODE, dir, dev, &state_fops); | ||
781 | if (!d) | ||
782 | return -ENOMEM; | ||
783 | |||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | static int __init actmon_debugfs_init(void) | ||
788 | { | ||
789 | int i; | ||
790 | int ret = -ENOMEM; | ||
791 | struct dentry *d; | ||
792 | |||
793 | d = debugfs_create_dir("tegra_actmon", NULL); | ||
794 | if (!d) | ||
795 | return ret; | ||
796 | clk_debugfs_root = d; | ||
797 | |||
798 | d = debugfs_create_file("period", RW_MODE, d, NULL, &period_fops); | ||
799 | if (!d) | ||
800 | goto err_out; | ||
801 | |||
802 | for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { | ||
803 | ret = actmon_debugfs_create_dev(actmon_devices[i]); | ||
804 | if (ret) | ||
805 | goto err_out; | ||
806 | } | ||
807 | return 0; | ||
808 | |||
809 | err_out: | ||
810 | debugfs_remove_recursive(clk_debugfs_root); | ||
811 | return ret; | ||
812 | } | ||
813 | |||
814 | #endif | ||
815 | |||
816 | static int __init tegra_actmon_init(void) | ||
817 | { | ||
818 | int i, ret; | ||
819 | struct clk *c = tegra_get_clock_by_name("actmon"); | ||
820 | |||
821 | if (!c) { | ||
822 | pr_err("%s: Failed to find actmon clock\n", __func__); | ||
823 | return 0; | ||
824 | } | ||
825 | actmon_clk_freq = clk_get_rate(c) / 1000; | ||
826 | ret = clk_enable(c); | ||
827 | if (ret) { | ||
828 | pr_err("%s: Failed to enable actmon clock\n", __func__); | ||
829 | return 0; | ||
830 | } | ||
831 | actmon_sampling_period = ACTMON_DEFAULT_SAMPLING_PERIOD; | ||
832 | actmon_writel(actmon_sampling_period - 1, ACTMON_GLB_PERIOD_CTRL); | ||
833 | |||
834 | for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { | ||
835 | ret = actmon_dev_init(actmon_devices[i]); | ||
836 | pr_info("%s.%s: %s initialization (%d)\n", | ||
837 | actmon_devices[i]->dev_id, actmon_devices[i]->con_id, | ||
838 | ret ? "Failed" : "Completed", ret); | ||
839 | } | ||
840 | register_pm_notifier(&actmon_pm_nb); | ||
841 | |||
842 | #ifdef CONFIG_DEBUG_FS | ||
843 | actmon_debugfs_init(); | ||
844 | #endif | ||
845 | return 0; | ||
846 | } | ||
847 | late_initcall(tegra_actmon_init); | ||