aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/tegra2_mc.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /arch/arm/mach-tegra/tegra2_mc.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_mc.c')
-rw-r--r--arch/arm/mach-tegra/tegra2_mc.c1017
1 files changed, 1017 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra2_mc.c b/arch/arm/mach-tegra/tegra2_mc.c
new file mode 100644
index 00000000000..6df9c232c02
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_mc.c
@@ -0,0 +1,1017 @@
1/*
2 * arch/arm/mach-tegra/tegra2_mc.c
3 *
4 * Memory controller bandwidth profiling interface
5 *
6 * Copyright (c) 2009-2011, NVIDIA Corporation.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23#include <linux/slab.h>
24#include <linux/kobject.h>
25#include <linux/string.h>
26#include <linux/sysfs.h>
27#include <linux/sysdev.h>
28#include <linux/ktime.h>
29#include <linux/hrtimer.h>
30#include <linux/parser.h>
31#include <linux/io.h>
32#include <linux/module.h>
33#include <linux/init.h>
34#include <linux/clk.h>
35
36#include <mach/iomap.h>
37
38#include <asm/uaccess.h>
39
40#include "clock.h"
41#include "tegra2_mc.h"
42
43static void stat_start(void);
44static void stat_stop(void);
45static void stat_log(void);
46
47static struct hrtimer sample_timer;
48
49#define MC_COUNTER_INITIALIZER() \
50 { \
51 .enabled = false, \
52 .period = 10, \
53 .mode = FILTER_CLIENT, \
54 .address_low = 0, \
55 .address_length = 0xfffffffful, \
56 .sample_data = { \
57 .signature = 0xdeadbeef, \
58 } \
59 }
60
61static struct tegra_mc_counter mc_counter0 = MC_COUNTER_INITIALIZER();
62static struct tegra_mc_counter mc_counter1 = MC_COUNTER_INITIALIZER();
63static struct tegra_mc_counter emc_llp_counter = MC_COUNTER_INITIALIZER();
64
65/* /sys/devices/system/tegra_mc */
66static bool sample_enable = SAMPLE_ENABLE_DEFAULT;
67static u16 sample_quantum = SAMPLE_QUANTUM_DEFAULT;
68static u8 sample_log[SAMPLE_LOG_SIZE];
69
70static DEFINE_SPINLOCK(sample_enable_lock);
71static DEFINE_SPINLOCK(sample_log_lock);
72
73static u8 *sample_log_wptr = sample_log, *sample_log_rptr = sample_log;
74static int sample_log_size = SAMPLE_LOG_SIZE - 1;
75static struct clk *emc_clock = NULL;
76
77static bool sampling(void)
78{
79 bool ret;
80
81 spin_lock_bh(&sample_enable_lock);
82 ret = (sample_enable == true)? true : false;
83 spin_unlock_bh(&sample_enable_lock);
84
85 return ret;
86}
87
88static struct sysdev_class tegra_mc_sysclass = {
89 .name = "tegra_mc",
90};
91
92static ssize_t tegra_mc_enable_show(struct sysdev_class *class,
93 struct sysdev_class_attribute *attr, char *buf)
94{
95 return sprintf(buf, "%d\n", sample_enable);
96}
97
98static ssize_t tegra_mc_enable_store(struct sysdev_class *class,
99 struct sysdev_class_attribute *attr,
100 const char *buf, size_t count)
101{
102 int value, i;
103 struct tegra_mc_counter *counters[] = {
104 &mc_counter0,
105 &mc_counter1,
106 &emc_llp_counter
107 };
108
109 sscanf(buf, "%d", &value);
110
111 if (value == 0 || value == 1)
112 sample_enable = value;
113 else
114 return -EINVAL;
115
116 if (!sample_enable) {
117 stat_stop();
118 hrtimer_cancel(&sample_timer);
119 return count;
120 }
121
122 hrtimer_cancel(&sample_timer);
123
124 /* we need to initialize variables that change during sampling */
125 sample_log_wptr = sample_log_rptr = sample_log;
126 sample_log_size = SAMPLE_LOG_SIZE - 1;
127
128 for (i = 0; i < ARRAY_SIZE(counters); i++) {
129 struct tegra_mc_counter *c = counters[i];
130
131 if (!c->enabled)
132 continue;
133
134 c->current_client_index = 0;
135 }
136
137 stat_start();
138
139 hrtimer_start(&sample_timer,
140 ktime_add_ns(ktime_get(), (u64)sample_quantum * 1000000),
141 HRTIMER_MODE_ABS);
142
143 return count;
144}
145
146static ssize_t tegra_mc_log_show(struct sysdev_class *class,
147 struct sysdev_class_attribute *attr, char *buf)
148{
149 int index = 0, count = 0;
150 unsigned long flags;
151
152 spin_lock_irqsave(&sample_log_lock, flags);
153
154 while (sample_log_rptr != sample_log_wptr) {
155 if (sample_log_rptr < sample_log_wptr) {
156 count = sample_log_wptr - sample_log_rptr;
157 memcpy(buf + index, sample_log_rptr, count);
158 sample_log_rptr = sample_log_wptr;
159 sample_log_size += count;
160 } else {
161 count = SAMPLE_LOG_SIZE -
162 (sample_log_rptr - sample_log);
163 memcpy(buf + index, sample_log_rptr, count);
164 sample_log_rptr = sample_log;
165 sample_log_size += count;
166 }
167 index += count;
168 }
169
170 spin_unlock_irqrestore(&sample_log_lock, flags);
171
172 return index;
173}
174
175static ssize_t tegra_mc_log_store(struct sysdev_class *class,
176 struct sysdev_class_attribute *attr,
177 const char *buf, size_t count)
178{
179 return -EPERM;
180}
181
182static ssize_t tegra_mc_quantum_show(struct sysdev_class *class,
183 struct sysdev_class_attribute *attr, char *buf)
184{
185 return sprintf(buf, "%d\n", sample_quantum);
186}
187
188static ssize_t tegra_mc_quantum_store(struct sysdev_class *class,
189 struct sysdev_class_attribute *attr,
190 const char *buf, size_t count)
191{
192 int value;
193
194 if (sampling())
195 return -EINVAL;
196
197 sscanf(buf, "%d", &value);
198 sample_quantum = value;
199
200 return count;
201}
202
203#define TEGRA_MC_EXPAND(_attr,_mode) \
204 static SYSDEV_CLASS_ATTR( \
205 _attr, _mode, tegra_mc_##_attr##_show, tegra_mc_##_attr##_store);
206
207#define TEGRA_MC_ATTRIBUTES(_attr1,_mode1,_attr2,_mode2,_attr3,_mode3) \
208 TEGRA_MC_EXPAND(_attr1,_mode1) \
209 TEGRA_MC_EXPAND(_attr2,_mode2) \
210 TEGRA_MC_EXPAND(_attr3,_mode3)
211
212TEGRA_MC_ATTRIBUTES(enable,0666,log,0444,quantum,0666)
213
214#undef TEGRA_MC_EXPAND
215
216#define TEGRA_MC_EXPAND(_attr,_mode) \
217 &attr_##_attr,
218
219static struct sysdev_class_attribute *tegra_mc_attrs[] = {
220 TEGRA_MC_ATTRIBUTES(enable,0666,log,0444,quantum,0666)
221 NULL
222};
223
224/* /sys/devices/system/tegra_mc/client */
225static bool tegra_mc_client_0_enabled = CLIENT_ENABLED_DEFAULT;
226static u8 tegra_mc_client_0_on_schedule_buffer[CLIENT_ON_SCHEDULE_LENGTH];
227static struct kobject *tegra_mc_client_kobj, *tegra_mc_client_0_kobj;
228
229struct match_mode {
230 const char *name;
231 int mode;
232};
233
234static const struct match_mode mode_list[] = {
235 [0] = {
236 .name = "none",
237 .mode = FILTER_NONE,
238 },
239 [1] = {
240 .name = "address",
241 .mode = FILTER_ADDR,
242 },
243 [2] = {
244 .name = "client",
245 .mode = FILTER_CLIENT,
246 },
247};
248
249static int tegra_mc_parse_mode(const char* str) {
250 int i;
251
252 for (i = 0; i < ARRAY_SIZE(mode_list); i++) {
253 if (!strncmp(str, mode_list[i].name, strlen(mode_list[i].name)))
254 return mode_list[i].mode;
255 }
256 return -EINVAL;
257}
258
259static int tegra_mc_client_parse(const char *buf, size_t count,
260 tegra_mc_counter_t *counter0, tegra_mc_counter_t *counter1,
261 tegra_mc_counter_t *llp)
262{
263 char *options, *p, *ptr;
264 tegra_mc_counter_t *counter;
265 substring_t args[MAX_OPT_ARGS];
266 enum {
267 opt_period,
268 opt_mode,
269 opt_client,
270 opt_address_low,
271 opt_address_length,
272 opt_err,
273 };
274 const match_table_t tokens = {
275 {opt_period, "period=%s"},
276 {opt_mode, "mode=%s"},
277 {opt_client, "client=%s"},
278 {opt_address_low, "address_low=%s"},
279 {opt_address_length, "address_length=%s"},
280 {opt_err, NULL},
281 };
282 int ret = 0, i, token, index = 0;
283 bool aggregate = false;
284 int period, *client_ids, mode;
285 u64 address_low = 0;
286 u64 address_length = 1ull << 32;
287
288 client_ids = kmalloc(sizeof(int) * (MC_COUNTER_CLIENT_SIZE + 1),
289 GFP_KERNEL);
290 if (!client_ids)
291 return -ENOMEM;
292
293 memset(client_ids, -1, (sizeof(int) * (MC_COUNTER_CLIENT_SIZE + 1)));
294
295 options = kstrdup(buf, GFP_KERNEL);
296 if (!options) {
297 ret = -ENOMEM;
298 goto end;
299 }
300
301 while ((p = strsep(&options, " ")) != NULL) {
302 if (!*p)
303 continue;
304
305 pr_debug("\t %s\n", p);
306
307 token = match_token(p, tokens, args);
308 switch (token) {
309 case opt_period:
310 if (match_int(&args[0], &period) || period <= 0) {
311 ret = -EINVAL;
312 goto end;
313 }
314 break;
315
316 case opt_mode:
317 mode = tegra_mc_parse_mode(args[0].from);
318 if (mode < 0) {
319 ret = mode;
320 goto end;
321 }
322 break;
323
324 case opt_client:
325 ptr = get_options(args[0].from,
326 MC_COUNTER_CLIENT_SIZE + 1, client_ids);
327
328 if (client_ids[1] == MC_STAT_AGGREGATE) {
329 aggregate = true;
330 break;
331 }
332 break;
333
334 case opt_address_low:
335 address_low = simple_strtoull(args[0].from, NULL, 0);
336 break;
337
338 case opt_address_length:
339 address_length = simple_strtoull(args[0].from, NULL, 0);
340 break;
341
342 default:
343 ret = -EINVAL;
344 goto end;
345 }
346 }
347
348 address_low &= PAGE_MASK;
349 address_length += PAGE_SIZE - 1;
350 address_length &= ~((1ull << PAGE_SHIFT) - 1ull);
351
352 if (mode == FILTER_CLIENT) {
353 counter = counter0;
354 llp->enabled = false;
355 counter1->enabled = false;
356 } else if (mode == FILTER_ADDR || mode == FILTER_NONE) {
357 if (aggregate) {
358 counter = counter1;
359 llp->enabled = false;
360 counter0->enabled = false;
361 } else {
362 counter = counter0;
363 counter1->enabled = false;
364 llp->enabled = false;
365 }
366 } else {
367 ret = -EINVAL;
368 goto end;
369 }
370
371 counter->mode = mode;
372 counter->enabled = true;
373 counter->address_low = (u32)address_low;
374 counter->address_length = (u32)(address_length - 1);
375
376 for (i = 1; i < MC_COUNTER_CLIENT_SIZE; i++) {
377 if (client_ids[i] != -1)
378 counter->clients[index++] = client_ids[i];
379 }
380
381 counter->total_clients = index;
382
383 if (llp->enabled) {
384 llp->mode = counter->mode;
385 llp->period = counter->period;
386 llp->address_low = counter->address_low;
387 llp->address_length = counter->address_length;
388 }
389
390end:
391 if (options)
392 kfree(options);
393 if (client_ids)
394 kfree(client_ids);
395
396 return ret;
397}
398
399static ssize_t tegra_mc_client_0_show(struct kobject *kobj,
400 struct kobj_attribute *attr, char *buf)
401{
402 if (strcmp(attr->attr.name, "enable") == 0)
403 return sprintf(buf, "%d\n", tegra_mc_client_0_enabled);
404 else if (strcmp(attr->attr.name, "on_schedule") == 0)
405 return sprintf(buf, "%s", tegra_mc_client_0_on_schedule_buffer);
406 else
407 return -EINVAL;
408}
409
410static ssize_t tegra_mc_client_0_store(struct kobject *kobj,
411 struct kobj_attribute *attr, const char *buf, size_t count)
412{
413 int value;
414
415 if (sampling())
416 return -EINVAL;
417
418 if (strcmp(attr->attr.name, "enable") == 0) {
419 sscanf(buf, "%d\n", &value);
420 if (value == 0 || value == 1)
421 tegra_mc_client_0_enabled = value;
422 else
423 return -EINVAL;
424
425 return count;
426 } else if (strcmp(attr->attr.name, "on_schedule") == 0) {
427 if (tegra_mc_client_parse(buf, count,
428 &mc_counter0, &mc_counter1,
429 &emc_llp_counter) == 0) {
430
431 strncpy(tegra_mc_client_0_on_schedule_buffer,
432 buf, count);
433
434 return count;
435 } else
436 return -EINVAL;
437 } else
438 return -EINVAL;
439}
440
441static struct kobj_attribute tegra_mc_client_0_enable =
442 __ATTR(enable, 0660, tegra_mc_client_0_show, tegra_mc_client_0_store);
443
444static struct kobj_attribute tegra_mc_client_0_on_schedule =
445 __ATTR(on_schedule, 0660, tegra_mc_client_0_show, tegra_mc_client_0_store);
446
447static struct attribute *tegra_mc_client_0_attrs[] = {
448 &tegra_mc_client_0_enable.attr,
449 &tegra_mc_client_0_on_schedule.attr,
450 NULL,
451};
452
453static struct attribute_group tegra_mc_client_0_attr_group = {
454 .attrs = tegra_mc_client_0_attrs
455};
456
457/* /sys/devices/system/tegra_mc/dram */
458#define dram_counters(_x) \
459 _x(activate_cnt, ACTIVATE_CNT) \
460 _x(read_cnt, READ_CNT) \
461 _x(write_cnt, WRITE_CNT) \
462 _x(ref_cnt, REF_CNT) \
463 _x(cumm_banks_active_cke_eq1, CUMM_BANKS_ACTIVE_CKE_EQ1) \
464 _x(cumm_banks_active_cke_eq0, CUMM_BANKS_ACTIVE_CKE_EQ0) \
465 _x(cke_eq1_clks, CKE_EQ1_CLKS) \
466 _x(extclks_cke_eq1, EXTCLKS_CKE_EQ1) \
467 _x(extclks_cke_eq0, EXTCLKS_CKE_EQ0) \
468 _x(no_banks_active_cke_eq1, NO_BANKS_ACTIVE_CKE_EQ1) \
469 _x(no_banks_active_cke_eq0, NO_BANKS_ACTIVE_CKE_EQ0)
470
471#define DEFINE_COUNTER(_name, _val) { .enabled = false, .device_mask = 0, },
472
473static tegra_emc_dram_counter_t dram_counters[] = {
474 dram_counters(DEFINE_COUNTER)
475};
476
477#define DEFINE_SYSFS(_name, _val) \
478 \
479static struct kobject *tegra_mc_dram_##_name##_kobj; \
480 \
481static ssize_t tegra_mc_dram_##_name##_show(struct kobject *kobj, \
482 struct kobj_attribute *attr, char *buf) \
483{ \
484 return tegra_mc_dram_show(kobj, attr, buf, \
485 _val - EMC_DRAM_STAT_BEGIN); \
486} \
487 \
488static ssize_t tegra_mc_dram_##_name##_store(struct kobject *kobj, \
489 struct kobj_attribute *attr, const char *buf, size_t count) \
490{ \
491 if (sampling()) \
492 return 0; \
493 \
494 return tegra_mc_dram_store(kobj, attr, buf, count, \
495 _val - EMC_DRAM_STAT_BEGIN); \
496} \
497 \
498 \
499static struct kobj_attribute tegra_mc_dram_##_name##_enable = \
500 __ATTR(enable, 0660, tegra_mc_dram_##_name##_show, \
501 tegra_mc_dram_##_name##_store); \
502 \
503static struct kobj_attribute tegra_mc_dram_##_name##_device_mask = \
504 __ATTR(device_mask, 0660, tegra_mc_dram_##_name##_show, \
505 tegra_mc_dram_##_name##_store); \
506 \
507static struct attribute *tegra_mc_dram_##_name##_attrs[] = { \
508 &tegra_mc_dram_##_name##_enable.attr, \
509 &tegra_mc_dram_##_name##_device_mask.attr, \
510 NULL, \
511}; \
512 \
513static struct attribute_group tegra_mc_dram_##_name##_attr_group = { \
514 .attrs = tegra_mc_dram_##_name##_attrs, \
515};
516
517static struct kobject *tegra_mc_dram_kobj;
518
519static ssize_t tegra_mc_dram_show(struct kobject *kobj,
520 struct kobj_attribute *attr, char *buf, int index)
521{
522 if (index >= EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN)
523 return -EINVAL;
524
525 if (strcmp(attr->attr.name, "enable") == 0)
526 return sprintf(buf, "%d\n", dram_counters[index].enabled);
527 else if (strcmp(attr->attr.name, "device_mask") == 0)
528 return sprintf(buf, "%d\n", dram_counters[index].device_mask);
529 else
530 return -EINVAL;
531}
532static ssize_t tegra_mc_dram_store(struct kobject *kobj,
533 struct kobj_attribute *attr, const char *buf, size_t count, int index)
534{
535 int value;
536
537 if (index >= EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN)
538 return -EINVAL;
539
540 if (strcmp(attr->attr.name, "enable") == 0) {
541 sscanf(buf, "%d\n", &value);
542 if (value == 0 || value == 1)
543 dram_counters[index].enabled = value;
544 else
545 return -EINVAL;
546
547 return count;
548 } else if (strcmp(attr->attr.name, "device_mask") == 0) {
549 sscanf(buf, "%d\n", &value);
550 dram_counters[index].device_mask = (u8)value;
551
552 return count;
553 } else
554 return -EINVAL;
555}
556
557dram_counters(DEFINE_SYSFS)
558
559/* Tegra Statistics */
560typedef struct {
561 void __iomem *mmio;
562} tegra_device_t;
563
564static tegra_device_t mc = {
565 .mmio = IO_ADDRESS(TEGRA_MC_BASE),
566};
567
568static tegra_device_t emc = {
569 .mmio = IO_ADDRESS(TEGRA_EMC_BASE),
570};
571
572void mc_stat_start(tegra_mc_counter_t *counter0, tegra_mc_counter_t *counter1)
573{
574 struct tegra_mc_counter *c;
575 u32 filter_client = ARMC_STAT_CONTROL_FILTER_CLIENT_DISABLE;
576 u32 filter_addr = ARMC_STAT_CONTROL_FILTER_ADDR_DISABLE;
577
578 if (!tegra_mc_client_0_enabled)
579 return;
580
581 c = (counter0->enabled) ? counter0 : counter1;
582
583 /* disable statistics */
584 writel((MC_STAT_CONTROL_0_EMC_GATHER_DISABLE << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
585 mc.mmio + MC_STAT_CONTROL_0);
586
587 if (c->enabled && c->mode == FILTER_ADDR)
588 filter_addr = ARMC_STAT_CONTROL_FILTER_ADDR_ENABLE;
589 else if (c->enabled && c->mode == FILTER_CLIENT)
590 filter_client = ARMC_STAT_CONTROL_FILTER_CLIENT_ENABLE;
591
592 filter_addr <<= ARMC_STAT_CONTROL_FILTER_ADDR_SHIFT;
593 filter_client <<= ARMC_STAT_CONTROL_FILTER_CLIENT_SHIFT;
594
595 if (c->enabled) {
596 u32 reg = 0;
597 reg |= (ARMC_STAT_CONTROL_MODE_BANDWIDTH <<
598 ARMC_STAT_CONTROL_MODE_SHIFT);
599 reg |= (ARMC_STAT_CONTROL_EVENT_QUALIFIED <<
600 ARMC_STAT_CONTROL_EVENT_SHIFT);
601 reg |= (ARMC_STAT_CONTROL_FILTER_PRI_DISABLE <<
602 ARMC_STAT_CONTROL_FILTER_PRI_SHIFT);
603 reg |= (ARMC_STAT_CONTROL_FILTER_COALESCED_DISABLE <<
604 ARMC_STAT_CONTROL_FILTER_COALESCED_SHIFT);
605 reg |= filter_client;
606 reg |= filter_addr;
607 reg |= (c->clients[c->current_client_index] <<
608 ARMC_STAT_CONTROL_CLIENT_ID_SHIFT);
609
610 /* note these registers are shared */
611 writel(c->address_low,
612 mc.mmio + MC_STAT_EMC_ADDR_LOW_0);
613 writel((c->address_low + c->address_length),
614 mc.mmio + MC_STAT_EMC_ADDR_HIGH_0);
615 writel(0xFFFFFFFF, mc.mmio + MC_STAT_EMC_CLOCK_LIMIT_0);
616
617 writel(reg, mc.mmio + MC_STAT_EMC_CONTROL_0_0);
618 }
619
620 /* reset then enable statistics */
621 writel((MC_STAT_CONTROL_0_EMC_GATHER_CLEAR << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
622 mc.mmio + MC_STAT_CONTROL_0);
623
624 writel((MC_STAT_CONTROL_0_EMC_GATHER_ENABLE << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
625 mc.mmio + MC_STAT_CONTROL_0);
626}
627
628void mc_stat_stop(tegra_mc_counter_t *counter0,
629 tegra_mc_counter_t *counter1)
630{
631 u32 total_counts = readl(mc.mmio + MC_STAT_EMC_CLOCKS_0);
632
633 /* Disable statistics */
634 writel((MC_STAT_CONTROL_0_EMC_GATHER_DISABLE << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
635 mc.mmio + MC_STAT_CONTROL_0);
636
637 if (counter0->enabled) {
638 counter0->sample_data.client_counts = readl(mc.mmio + MC_STAT_EMC_COUNT_0_0);
639 counter0->sample_data.total_counts = total_counts;
640 counter0->sample_data.emc_clock_rate = clk_get_rate(emc_clock);
641 }
642 else {
643 counter1->sample_data.client_counts = readl(mc.mmio + MC_STAT_EMC_COUNT_1_0);
644 counter1->sample_data.total_counts = total_counts;
645 counter1->sample_data.emc_clock_rate = clk_get_rate(emc_clock);
646 }
647}
648
649void emc_stat_start(tegra_mc_counter_t *llp_counter,
650 tegra_emc_dram_counter_t *dram_counter)
651{
652 u32 llmc_stat = 0;
653 u32 llmc_ctrl =
654 (AREMC_STAT_CONTROL_MODE_BANDWIDTH <<
655 AREMC_STAT_CONTROL_MODE_SHIFT) |
656 (AREMC_STAT_CONTROL_CLIENT_TYPE_MPCORER <<
657 AREMC_STAT_CONTROL_CLIENT_TYPE_SHIFT) |
658 (AREMC_STAT_CONTROL_EVENT_QUALIFIED <<
659 AREMC_STAT_CONTROL_EVENT_SHIFT);
660
661 /* disable statistics */
662 llmc_stat |= (EMC_STAT_CONTROL_0_LLMC_GATHER_DISABLE <<
663 EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT);
664 llmc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_DISABLE <<
665 EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
666 writel(llmc_stat, emc.mmio + EMC_STAT_CONTROL_0);
667
668 if (tegra_mc_client_0_enabled && llp_counter->enabled) {
669 if (llp_counter->mode == FILTER_ADDR) {
670 llmc_ctrl |=
671 (AREMC_STAT_CONTROL_FILTER_ADDR_ENABLE <<
672 AREMC_STAT_CONTROL_FILTER_ADDR_SHIFT);
673 llmc_ctrl |=
674 (AREMC_STAT_CONTROL_FILTER_CLIENT_DISABLE <<
675 AREMC_STAT_CONTROL_FILTER_CLIENT_SHIFT);
676 } else if (llp_counter->mode == FILTER_CLIENT) {
677 /* not allow aggregate client in client mode */
678 llmc_ctrl |=
679 (AREMC_STAT_CONTROL_FILTER_ADDR_DISABLE <<
680 AREMC_STAT_CONTROL_FILTER_ADDR_SHIFT);
681 llmc_ctrl |=
682 (AREMC_STAT_CONTROL_FILTER_CLIENT_DISABLE <<
683 AREMC_STAT_CONTROL_FILTER_CLIENT_SHIFT);
684 } else if (llp_counter->mode == FILTER_NONE) {
685 llmc_ctrl |=
686 (AREMC_STAT_CONTROL_FILTER_ADDR_DISABLE <<
687 AREMC_STAT_CONTROL_FILTER_ADDR_SHIFT);
688 llmc_ctrl |=
689 (AREMC_STAT_CONTROL_FILTER_CLIENT_DISABLE <<
690 AREMC_STAT_CONTROL_FILTER_CLIENT_SHIFT);
691 }
692
693 writel(llp_counter->address_low,
694 emc.mmio + EMC_STAT_LLMC_ADDR_LOW_0);
695 writel( (llp_counter->address_low + llp_counter->address_length),
696 emc.mmio + EMC_STAT_LLMC_ADDR_HIGH_0);
697 writel(0xFFFFFFFF, emc.mmio + EMC_STAT_LLMC_CLOCK_LIMIT_0);
698 writel(llmc_ctrl, emc.mmio + EMC_STAT_LLMC_CONTROL_0_0);
699 }
700
701 writel(0xFFFFFFFF, emc.mmio + EMC_STAT_DRAM_CLOCK_LIMIT_LO_0);
702 writel(0xFF, emc.mmio + EMC_STAT_DRAM_CLOCK_LIMIT_HI_0);
703
704 llmc_stat = 0;
705 /* Reset then enable statistics */
706 llmc_stat |= (EMC_STAT_CONTROL_0_LLMC_GATHER_CLEAR <<
707 EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT);
708 llmc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_CLEAR <<
709 EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
710 writel(llmc_stat, emc.mmio + EMC_STAT_CONTROL_0);
711
712 llmc_stat = 0;
713 llmc_stat |= (EMC_STAT_CONTROL_0_LLMC_GATHER_ENABLE <<
714 EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT);
715 llmc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_ENABLE <<
716 EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
717 writel(llmc_stat, emc.mmio + EMC_STAT_CONTROL_0);
718}
719
720void emc_stat_stop(tegra_mc_counter_t *llp_counter,
721 tegra_emc_dram_counter_t *dram_counter)
722{
723 u32 llmc_stat = 0;
724 int i;
725 int dev0_offsets_lo[] = {
726 EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_LO_0,
727 EMC_STAT_DRAM_DEV0_READ_CNT_LO_0,
728 EMC_STAT_DRAM_DEV0_WRITE_CNT_LO_0,
729 EMC_STAT_DRAM_DEV0_REF_CNT_LO_0,
730 EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ1_LO_0,
731 EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ0_LO_0,
732 EMC_STAT_DRAM_DEV0_CKE_EQ1_CLKS_LO_0,
733 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_LO_0,
734 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_LO_0,
735 EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ1_LO_0,
736 EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ0_LO_0,
737 };
738 int dev0_offsets_hi[] = {
739 EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_HI_0,
740 EMC_STAT_DRAM_DEV0_READ_CNT_HI_0,
741 EMC_STAT_DRAM_DEV0_WRITE_CNT_HI_0,
742 EMC_STAT_DRAM_DEV0_REF_CNT_HI_0,
743 EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ1_HI_0,
744 EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ0_HI_0,
745 EMC_STAT_DRAM_DEV0_CKE_EQ1_CLKS_HI_0,
746 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_HI_0,
747 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_HI_0,
748 EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ1_HI_0,
749 EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ0_HI_0,
750 };
751 int dev1_offsets_lo[] = {
752 EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_LO_0,
753 EMC_STAT_DRAM_DEV1_READ_CNT_LO_0,
754 EMC_STAT_DRAM_DEV1_WRITE_CNT_LO_0,
755 EMC_STAT_DRAM_DEV1_REF_CNT_LO_0,
756 EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ1_LO_0,
757 EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ0_LO_0,
758 EMC_STAT_DRAM_DEV1_CKE_EQ1_CLKS_LO_0,
759 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_LO_0,
760 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_LO_0,
761 EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ1_LO_0,
762 EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ0_LO_0,
763 };
764 int dev1_offsets_hi[] = {
765 EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_HI_0,
766 EMC_STAT_DRAM_DEV1_READ_CNT_HI_0,
767 EMC_STAT_DRAM_DEV1_WRITE_CNT_HI_0,
768 EMC_STAT_DRAM_DEV1_REF_CNT_HI_0,
769 EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ1_HI_0,
770 EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ0_HI_0,
771 EMC_STAT_DRAM_DEV1_CKE_EQ1_CLKS_HI_0,
772 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_HI_0,
773 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_HI_0,
774 EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ1_HI_0,
775 EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ0_HI_0,
776 };
777
778 /* Disable statistics */
779 llmc_stat |= (EMC_STAT_CONTROL_0_LLMC_GATHER_DISABLE <<
780 EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT);
781 llmc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_DISABLE <<
782 EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
783 writel(llmc_stat, emc.mmio + EMC_STAT_CONTROL_0);
784
785 if (tegra_mc_client_0_enabled == true && llp_counter->enabled) {
786 u32 total_counts = readl(mc.mmio + MC_STAT_EMC_CLOCKS_0);
787 llp_counter->sample_data.client_counts = readl(emc.mmio + EMC_STAT_LLMC_COUNT_0_0);
788 llp_counter->sample_data.total_counts = total_counts;
789 llp_counter->sample_data.emc_clock_rate = clk_get_rate(emc_clock);
790 }
791
792 for (i = 0; i < EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN; i++) {
793 if (dram_counter[i].enabled) {
794
795 dram_counter[i].sample_data.client_counts = 0;
796 dram_counter[i].sample_data.emc_clock_rate = clk_get_rate(emc_clock);
797
798 if (!(dram_counter[i].device_mask & 0x1)) {
799 if (readl(emc.mmio + dev0_offsets_hi[i]) != 0) {
800 dram_counter[i].sample_data.client_counts = 0xFFFFFFFF;
801 continue;
802 }
803 dram_counter[i].sample_data.client_counts +=
804 readl(emc.mmio + dev0_offsets_lo[i]);
805 }
806
807 if (!(dram_counter[i].device_mask & 0x2)) {
808 if (readl(emc.mmio + dev1_offsets_hi[i]) != 0) {
809 dram_counter[i].sample_data.client_counts = 0xFFFFFFFF;
810 continue;
811 }
812 dram_counter[i].sample_data.client_counts +=
813 readl(emc.mmio + dev1_offsets_lo[i]);
814 }
815 }
816 }
817}
818
819static void stat_start(void)
820{
821 mc_stat_start(&mc_counter0, &mc_counter1);
822 emc_stat_start(&emc_llp_counter, dram_counters);
823}
824
825static void stat_stop(void)
826{
827 mc_stat_stop(&mc_counter0, &mc_counter1);
828 emc_stat_stop(&emc_llp_counter, dram_counters);
829}
830
831#define statcpy(_buf, _bufstart, _buflen, _elem) \
832 do { \
833 size_t s = sizeof(_elem); \
834 memcpy(_buf, &_elem, s); \
835 _buf += s; \
836 if (_buf >= _bufstart + _buflen) \
837 _buf = _bufstart; \
838 } while (0);
839
840static void stat_log(void)
841{
842 int i;
843 unsigned long flags;
844
845 struct tegra_mc_counter *counters[] = {
846 &mc_counter0,
847 &mc_counter1,
848 &emc_llp_counter
849 };
850
851 spin_lock_irqsave(&sample_log_lock, flags);
852
853 if (tegra_mc_client_0_enabled) {
854 for (i = 0; i < ARRAY_SIZE(counters); i++) {
855 struct tegra_mc_counter *c = counters[i];
856
857 if (!c->enabled)
858 continue;
859
860 c->sample_data.client_number = c->clients[c->current_client_index];
861
862 c->current_client_index++;
863 if (c->current_client_index == c->total_clients)
864 c->current_client_index = 0;
865
866 statcpy(sample_log_wptr, sample_log,
867 SAMPLE_LOG_SIZE, c->sample_data);
868 }
869 }
870
871 for (i = 0; i < EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN; i++) {
872 if (dram_counters[i].enabled) {
873 statcpy(sample_log_wptr, sample_log,
874 SAMPLE_LOG_SIZE, dram_counters[i].sample_data);
875 }
876 }
877
878 spin_unlock_irqrestore(&sample_log_lock, flags);
879}
880
881static enum hrtimer_restart sample_timer_function(struct hrtimer *handle)
882{
883 stat_stop();
884 stat_log();
885
886 if (!sample_enable)
887 return HRTIMER_NORESTART;
888
889 stat_start();
890
891 hrtimer_add_expires_ns(&sample_timer, (u64)sample_quantum * 1000000);
892 return HRTIMER_RESTART;
893}
894
895/* module init */
896#define REGISTER_SYSFS(_name, _val) \
897 tegra_mc_dram_##_name##_kobj = \
898 kobject_create_and_add(#_name, tegra_mc_dram_kobj); \
899 sysfs_create_group(tegra_mc_dram_##_name##_kobj, \
900 &tegra_mc_dram_##_name##_attr_group);
901
902static int tegra_mc_init(void)
903{
904 int i;
905 int rc;
906
907 /* /sys/devices/system/tegra_mc */
908 rc = sysdev_class_register(&tegra_mc_sysclass);
909 if(rc)
910 goto out;
911
912 for (i = 0; i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
913 rc = sysdev_class_create_file(&tegra_mc_sysclass,
914 tegra_mc_attrs[i]);
915 if(rc) {
916 printk("\n sysdev_class_create_file : failed \n");
917 goto out_unreg_class;
918 }
919 }
920
921 /* /sys/devices/system/tegra_mc/client */
922 tegra_mc_client_kobj = kobject_create_and_add("client",
923 &tegra_mc_sysclass.kset.kobj);
924 if(!tegra_mc_client_kobj)
925 goto out_remove_sysdev_files;
926
927 tegra_mc_client_0_kobj = kobject_create_and_add("0",
928 tegra_mc_client_kobj);
929 if(!tegra_mc_client_0_kobj)
930 goto out_put_kobject_client;
931
932 rc = sysfs_create_group(tegra_mc_client_0_kobj,
933 &tegra_mc_client_0_attr_group);
934 if(rc)
935 goto out_put_kobject_client_0;
936
937 /* /sys/devices/system/tegra_mc/dram */
938 tegra_mc_dram_kobj = kobject_create_and_add("dram",
939 &tegra_mc_sysclass.kset.kobj);
940 if(!tegra_mc_dram_kobj)
941 goto out_remove_group_client_0;
942
943 dram_counters(REGISTER_SYSFS)
944
945 /* hrtimer */
946 hrtimer_init(&sample_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
947 sample_timer.function = sample_timer_function;
948
949 for (i = 0; i < EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN; i++) {
950 dram_counters[i].sample_data.client_number = EMC_DRAM_STAT_BEGIN + i;
951 dram_counters[i].sample_data.signature = 0xdeadbeef;
952 }
953
954 emc_clock = clk_get_sys(NULL, "emc");
955 if (!emc_clock) {
956 pr_err("Could not get EMC clock\n");
957 goto out_remove_group_client_0;
958 }
959
960 return 0;
961
962out_remove_group_client_0:
963 sysfs_remove_group(tegra_mc_client_0_kobj, &tegra_mc_client_0_attr_group);
964
965out_put_kobject_client_0:
966 kobject_put(tegra_mc_client_0_kobj);
967
968out_put_kobject_client:
969 kobject_put(tegra_mc_client_kobj);
970
971out_remove_sysdev_files:
972 for (i = 0; i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
973 sysdev_class_remove_file(&tegra_mc_sysclass, tegra_mc_attrs[i]);
974 }
975
976out_unreg_class:
977 sysdev_class_unregister(&tegra_mc_sysclass);
978
979out:
980 return rc;
981}
982
983/* module deinit */
984#define REMOVE_SYSFS(_name, _val) \
985 sysfs_remove_group(tegra_mc_dram_##_name##_kobj, \
986 &tegra_mc_dram_##_name##_attr_group); \
987 kobject_put(tegra_mc_dram_##_name##_kobj);
988
989static void tegra_mc_exit(void)
990{
991 int i;
992
993 stat_stop();
994
995 /* hrtimer */
996 hrtimer_cancel(&sample_timer);
997
998 /* /sys/devices/system/tegra_mc/client */
999 sysfs_remove_group(tegra_mc_client_0_kobj,
1000 &tegra_mc_client_0_attr_group);
1001 kobject_put(tegra_mc_client_0_kobj);
1002 kobject_put(tegra_mc_client_kobj);
1003
1004 /* /sys/devices/system/tegra_mc/dram */
1005 dram_counters(REMOVE_SYSFS)
1006 kobject_put(tegra_mc_dram_kobj);
1007
1008 /* /sys/devices/system/tegra_mc */
1009 for (i = 0; i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
1010 sysdev_class_remove_file(&tegra_mc_sysclass, tegra_mc_attrs[i]);
1011 }
1012 sysdev_class_unregister(&tegra_mc_sysclass);
1013}
1014
1015module_init(tegra_mc_init);
1016module_exit(tegra_mc_exit);
1017MODULE_LICENSE("Dual BSD/GPL");