arm: tegra: renaming tegra3_mc_stats sysfs to tegra3_mc
[linux-2.6.git] / arch / arm / mach-tegra / tegra3_mc.c
1 /*
2  * arch/arm/mach-tegra/tegra3_mc.c
3  *
4  * Memory controller bandwidth profiling interface
5  *
6  * Copyright (c) 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 <mach/iomap.h>
35 #include <asm/uaccess.h>
36 #include "tegra3_mc.h"
37
38 #define MC_STAT_SETS 2
39 static unsigned int trace_mask = 0x0;
40
41 #define TRACE_FLOW      1
42 #define TRACE_REG       2
43 #define TRACE_ERR       4
44 #define TRACE_OPT       8
45
46 #define emc_trace(x, args...) \
47         do { \
48                 if (x & trace_mask) \
49                         pr_err(args); \
50         } while (0)
51
52 #define MC_COUNTER_INITIALIZER()                \
53 {                                               \
54         .enabled = false,                       \
55         .reschedule = false,                    \
56         .period = 10,                           \
57         .mode = FILTER_CLIENT,                  \
58         .address_low = 0,                       \
59         .address_length_1 = 0xfffffffful,       \
60         .address_window_size_1 = PAGE_SIZE,     \
61         .num_clients = 0,                       \
62 }
63
64 static struct tegra_mc_counter mc_counter0 = MC_COUNTER_INITIALIZER();
65 static struct tegra_mc_counter mc_counter1 = MC_COUNTER_INITIALIZER();
66 static struct tegra_mc_counter emc_dram_counter = MC_COUNTER_INITIALIZER();
67
68 static bool sample_enable = SAMPLE_ENABLE_DEFAULT;
69 static u16 sample_quantum = SAMPLE_QUANTUM_DEFAULT_IN_MS;
70 static u8 sample_log[SAMPLE_LOG_SIZE];
71
72 static DEFINE_SPINLOCK(sample_enable_lock);
73 static DEFINE_SPINLOCK(sample_log_lock);
74
75 static u8 *sample_log_wptr = sample_log, *sample_log_rptr = sample_log;
76 static int sample_log_size = SAMPLE_LOG_SIZE - 1;
77 static struct hrtimer sample_timer;
78
79 static void stat_start(void);
80 static void stat_stop(void);
81 static void stat_log(void);
82
83 static bool sampling(void)
84 {
85         bool ret;
86
87         spin_lock_bh(&sample_enable_lock);
88         ret = (sample_enable == true)? true : false;
89         spin_unlock_bh(&sample_enable_lock);
90
91         return ret;
92 }
93
94 /* /sys/devices/system/tegra_mc */
95 static struct sysdev_class tegra_mc_sysclass = {
96         .name = "tegra_mc",
97 };
98
99 static ssize_t tegra_mc_enable_show(struct sysdev_class *class,
100         struct sysdev_class_attribute *attr, char *buf)
101 {
102         return sprintf(buf, "%d\n", sample_enable);
103 }
104
105 static ssize_t tegra_mc_enable_store(struct sysdev_class *class,
106         struct sysdev_class_attribute *attr,
107         const char *buf, size_t count)
108 {
109         int value, i;
110         struct tegra_mc_counter *counters[] = {
111                 &mc_counter0,
112                 &mc_counter1,
113                 &emc_dram_counter
114         };
115
116         emc_trace(TRACE_FLOW, "%s\n", __func__);
117         sscanf(buf, "%d", &value);
118
119         if (value == 0 || value == 1)
120                 sample_enable = value;
121         else
122                 return -EINVAL;
123
124         if (!sample_enable) {
125                 stat_stop();
126                 hrtimer_cancel(&sample_timer);
127                 return count;
128         }
129
130         hrtimer_cancel(&sample_timer);
131
132         /* we need to initialize variables that change during sampling */
133         sample_log_wptr = sample_log_rptr = sample_log;
134         sample_log_size = SAMPLE_LOG_SIZE - 1;
135
136         for (i = 0; i < ARRAY_SIZE(counters); i++) {
137                 struct tegra_mc_counter *c = counters[i];
138
139                 if (!c->enabled)
140                         continue;
141
142                 c->current_address_low = c->address_low;
143                 c->current_address_high = c->address_low;
144                 c->address_range_change = (c->mode == FILTER_ADDR);
145                 if (c->address_range_change)
146                         c->current_address_high += c->address_window_size_1;
147                 else
148                         c->current_address_high += c->address_length_1;
149
150                 c->current_client = 0;
151                 c->sample_count = 0;
152         }
153
154         emc_trace(TRACE_FLOW, "\nstarting stats\n");
155         stat_start();
156
157         hrtimer_start(&sample_timer,
158                 ktime_add_ns(ktime_get(), (u64)sample_quantum * 1000000),
159                 HRTIMER_MODE_ABS);
160
161         return count;
162 }
163
164 static ssize_t tegra_mc_log_show(struct sysdev_class *class,
165         struct sysdev_class_attribute *attr, char *buf)
166 {
167         int index = 0, count = 0;
168
169         emc_trace(TRACE_FLOW, "%s\n", __func__);
170         spin_lock(&sample_log_lock);
171
172         while (sample_log_rptr != sample_log_wptr) {
173                 if (sample_log_rptr < sample_log_wptr) {
174                         count = sample_log_wptr - sample_log_rptr;
175                         memcpy(buf + index, sample_log_rptr, count);
176                         sample_log_rptr = sample_log_wptr;
177                         sample_log_size += count;
178                 } else {
179                         count = SAMPLE_LOG_SIZE -
180                                 (sample_log_rptr - sample_log);
181                         memcpy(buf + index, sample_log_rptr, count);
182                         sample_log_rptr = sample_log;
183                         sample_log_size += count;
184                 }
185                 index += count;
186         }
187
188         spin_unlock(&sample_log_lock);
189         return index;
190 }
191
192 static ssize_t tegra_mc_log_store(struct sysdev_class *class,
193         struct sysdev_class_attribute *attr,
194         const char *buf, size_t count)
195 {
196         return -EPERM;
197 }
198
199 static ssize_t tegra_mc_quantum_show(struct sysdev_class *class,
200         struct sysdev_class_attribute *attr, char *buf)
201 {
202         emc_trace(TRACE_FLOW, "%s\n", __func__);
203         return sprintf(buf, "%d\n", sample_quantum);
204 }
205
206 static ssize_t tegra_mc_quantum_store(struct sysdev_class *class,
207         struct sysdev_class_attribute *attr,
208         const char *buf, size_t count)
209 {
210         int value;
211
212         if (sampling())
213                 return -EINVAL;
214
215         sscanf(buf, "%d", &value);
216         sample_quantum = value;
217         emc_trace(TRACE_FLOW, "%s, sample_quantum=%d\n", __func__, sample_quantum);
218         return count;
219 }
220
221 #define TEGRA_MC_EXPAND(_attr,_mode) \
222         static SYSDEV_CLASS_ATTR( \
223         _attr, _mode, tegra_mc_##_attr##_show, tegra_mc_##_attr##_store);
224
225 #define TEGRA_MC_ATTRIBUTES(_attr1,_mode1,_attr2,_mode2,_attr3,_mode3) \
226         TEGRA_MC_EXPAND(_attr1,_mode1) \
227         TEGRA_MC_EXPAND(_attr2,_mode2) \
228         TEGRA_MC_EXPAND(_attr3,_mode3)
229
230 TEGRA_MC_ATTRIBUTES(enable, 0644, log, 0444, quantum, 0644)
231
232 #undef TEGRA_MC_EXPAND
233
234 #define TEGRA_MC_EXPAND(_attr,_mode) \
235         &attr_##_attr,
236
237 /* /sys/devices/system/tegra_mc/enable */
238 /* /sys/devices/system/tegra_mc/log */
239 /* /sys/devices/system/tegra_mc/quantum */
240 static struct sysdev_class_attribute *tegra_mc_attrs[] = {
241         TEGRA_MC_ATTRIBUTES(enable, 0644, log, 0444, quantum, 0644)
242         NULL
243 };
244
245 /* /sys/devices/system/tegra_mc/client/ */
246 /* /sys/devices/system/tegra_mc/client/0/ */
247 static bool tegra_mc_client_0_enabled = CLIENT_ENABLED_DEFAULT;
248 static u8 tegra_mc_client_0_on_schedule_buffer[CLIENT_ON_SCHEDULE_LENGTH_IN_BYTES];
249 static struct kobject *tegra_mc_client_kobj, *tegra_mc_client_0_kobj;
250
251 struct match_mode {
252         const char *name;
253         int mode;
254 };
255
256 static const struct match_mode mode_list[] = {
257         [0] = {
258                 .name = "none",
259                 .mode = FILTER_NONE,
260         },
261         [1] = {
262                 .name = "address",
263                 .mode = FILTER_ADDR,
264         },
265         [2] = {
266                 .name = "client",
267                 .mode = FILTER_CLIENT,
268         },
269 };
270
271 static int tegra_mc_parse_mode(const char* str) {
272         int i;
273
274         for (i = 0; i < ARRAY_SIZE(mode_list); i++) {
275                 if (!strncmp(str, mode_list[i].name, strlen(mode_list[i].name))) {
276                         emc_trace(TRACE_OPT, "mode=%s\n", mode_list[i].name);
277                         return mode_list[i].mode;
278                 }
279         }
280         return -EINVAL;
281 }
282
283 static int tegra_mc_client_parse(const char *buf, size_t count,
284         tegra_mc_counter_t *counter0, tegra_mc_counter_t *counter1,
285         tegra_mc_counter_t *llp)
286 {
287         char *options, *p, *ptr;
288         tegra_mc_counter_t *counter;
289         substring_t args[MAX_OPT_ARGS];
290         enum {
291                 opt_period,
292                 opt_mode,
293                 opt_client,
294                 opt_address_low,
295                 opt_address_length,
296                 opt_address_window_size,
297                 opt_err,
298         };
299         const match_table_t tokens = {
300                 {opt_period, "period=%s"},
301                 {opt_mode, "mode=%s"},
302                 {opt_client, "client=%s"},
303                 {opt_address_low, "address_low=%s"},
304                 {opt_address_length, "address_length=%s"},
305                 {opt_address_window_size, "address_window_size=%s"},
306                 {opt_err, NULL},
307         };
308         int ret = 0, i, token, num_clients;
309         bool aggregate = false;
310         int  period, *client_ids, mode;
311         bool fperiod = false, fmode = false, fclient = false;
312         u64 address_low = 0;
313         u64 address_length = 1ull<<32;
314         u64 address_window_size = PAGE_SIZE;
315
316         emc_trace(TRACE_OPT, "\n%s:%s\n", __func__, buf);
317         client_ids = kmalloc(sizeof(int) * (MC_COUNTER_CLIENT_SIZE + 1),
318                 GFP_KERNEL);
319         if (!client_ids)
320                 return -ENOMEM;
321
322         options = kstrdup(buf, GFP_KERNEL);
323         if (!options) {
324                 ret = -ENOMEM;
325                 goto end;
326         }
327
328         while ((p = strsep(&options, " ")) != NULL) {
329                 if (!*p)
330                         continue;
331
332                 pr_debug("\t %s\n", p);
333
334                 token = match_token(p, tokens, args);
335                 switch (token) {
336                 case opt_period:
337                         if (match_int(&args[0], &period) || period<=0) {
338                                 ret = -EINVAL;
339                                 goto end;
340                         }
341                         fperiod = true;
342                         break;
343
344                 case opt_mode:
345                         mode = tegra_mc_parse_mode(args[0].from);
346                         if (mode<0) {
347                                 ret = mode;
348                                 goto end;
349                         }
350                         fmode = true;
351                         break;
352
353                 case opt_client:
354                         client_ids[0] = 0;
355
356                         ptr = get_options(args[0].from,
357                                 MC_COUNTER_CLIENT_SIZE+1 , client_ids);
358
359                         if (client_ids[0] <= 0) {
360                                 ret = -EINVAL;
361                                 goto end;
362                         }
363
364                         for (i = 1; i <= client_ids[0]; i++) {
365                                 if (client_ids[i] < MC_STAT_END)
366                                         continue;
367
368                                 if ((client_ids[i] != MC_STAT_AGGREGATE) ||
369                                     client_ids[0] != 1) {
370                                         ret = -EINVAL;
371                                         goto end;
372                                 } else {
373                                         aggregate = true;
374                                         emc_trace(TRACE_OPT,
375                                                   "aggregate=true\n");
376                                         client_ids[0] = 1;
377                                         client_ids[1] = MC_STAT_AGGREGATE;
378                                         break;
379                                 }
380                         }
381
382                         num_clients = client_ids[0];
383                         fclient = true;
384                         emc_trace(TRACE_OPT, "num_clients=%d\n", num_clients);
385                         break;
386
387                 case opt_address_low:
388                         address_low = simple_strtoull(args[0].from, NULL, 0);
389                         emc_trace(TRACE_OPT, "address_low=0x%llx\n", address_low);
390                         break;
391
392                 case opt_address_length:
393                         address_length = simple_strtoull(args[0].from, NULL, 0);
394                         emc_trace(TRACE_OPT, "address_length =0x%llx\n", address_length);
395                         break;
396
397                 case opt_address_window_size:
398                         address_window_size = simple_strtoull(args[0].from,
399                                 NULL, 0);
400                         emc_trace(TRACE_OPT, "address_window_size =0x%llx\n", address_window_size);
401                         break;
402
403                 default:
404                         ret = -EINVAL;
405                         goto end;
406                 }
407         }
408         if (!fmode || !fclient) {
409                 ret = -EINVAL;
410                 goto end;
411         }
412
413         address_low &= PAGE_MASK;
414         address_length += PAGE_SIZE-1;
415         address_length &= ~((1ull << PAGE_SHIFT)-1ull);
416
417         address_window_size += PAGE_SIZE-1;
418         address_window_size &= ~((1ull << PAGE_SHIFT)-1ull);
419
420         if (mode == FILTER_CLIENT) {
421                 counter = counter0;
422                 counter->reschedule = (num_clients > 1);
423                 counter->num_clients = num_clients;
424                 llp->enabled = false;
425                 counter1->enabled = false;
426                 for (i = 1; (i <= num_clients) && (i < MC_COUNTER_CLIENT_SIZE); i++)
427                         counter->clients[i - 1] = client_ids[i];
428                 emc_trace(TRACE_OPT,
429                           "\ncounter0 is enabled (counter1 and llc are disabled)\n");
430         } else if (mode == FILTER_ADDR || mode == FILTER_NONE) {
431                 /* emc_trace(TRACE_ERR,
432                    "\n****using unsupported addr mode****\n"); */
433                 if (aggregate) {
434                         counter = counter1;
435                         llp->enabled = true; /* NOTE: was false in tegra2. */
436                         counter0->enabled = false;
437                 } else {
438                         counter = counter0;
439                         counter1->enabled = false;
440                         llp->enabled = false;
441                 }
442                 counter->num_clients = 1;
443                 counter->clients[0] = client_ids[1];
444                 counter->reschedule = (mode != FILTER_NONE);
445         } else {
446                 ret = -EINVAL;
447                 goto end;
448         }
449
450         counter->mode = mode;
451         counter->enabled = true;
452         counter->address_low = (u32)address_low;
453         counter->address_length_1 = (u32)(address_length-1);
454         counter->address_window_size_1 = (u32)(address_window_size-1);
455         if (fperiod)
456                 counter->period = period;
457
458         if (llp->enabled) {
459                 llp->mode = counter->mode;
460                 llp->reschedule = counter->reschedule;
461                 llp->period = counter->period;
462                 llp->address_low = counter->address_low;
463                 llp->address_length_1 = counter->address_length_1;
464                 llp->address_window_size_1 = counter->address_window_size_1;
465         }
466
467 end:
468         if (options)
469                 kfree(options);
470         if (client_ids)
471                 kfree(client_ids);
472
473         return ret;
474 }
475
476 static ssize_t tegra_mc_client_0_show(struct kobject *kobj,
477         struct kobj_attribute *attr, char *buf)
478 {
479         if (strcmp(attr->attr.name, "enable") == 0)
480                 return sprintf(buf, "%d\n", tegra_mc_client_0_enabled);
481         else if (strcmp(attr->attr.name, "on_schedule") == 0)
482                 return sprintf(buf, "%s", tegra_mc_client_0_on_schedule_buffer);
483         else
484                 return -EINVAL;
485 }
486
487 static ssize_t tegra_mc_client_0_store(struct kobject *kobj,
488         struct kobj_attribute *attr, const char *buf, size_t count)
489 {
490         int value;
491
492         if (sampling())
493                 return -EINVAL;
494
495         if (strcmp(attr->attr.name, "enable") == 0) {
496                 sscanf(buf, "%d\n", &value);
497                 if (value == 0 || value == 1)
498                         tegra_mc_client_0_enabled = value;
499                 else
500                         return -EINVAL;
501
502                 return count;
503         } else if (strcmp(attr->attr.name, "on_schedule") == 0) {
504                 if (tegra_mc_client_parse(buf, count,
505                         &mc_counter0, &mc_counter1,
506                         &emc_dram_counter)== 0) {
507
508                         strncpy(tegra_mc_client_0_on_schedule_buffer,
509                                 buf, count);
510
511                         return count;
512                 } else
513                         return -EINVAL;
514         } else
515                 return -EINVAL;
516 }
517
518 static struct kobj_attribute tegra_mc_client_0_enable =
519         __ATTR(enable, 0660, tegra_mc_client_0_show, tegra_mc_client_0_store);
520
521 static struct kobj_attribute tegra_mc_client_0_on_schedule =
522         __ATTR(on_schedule, 0660, tegra_mc_client_0_show, tegra_mc_client_0_store);
523
524 static struct attribute *tegra_mc_client_0_attrs[] = {
525         &tegra_mc_client_0_enable.attr,
526         &tegra_mc_client_0_on_schedule.attr,
527         NULL,
528 };
529
530 static struct attribute_group tegra_mc_client_0_attr_group = {
531         .attrs = tegra_mc_client_0_attrs
532 };
533
534 /* /sys/devices/system/tegra_mc/dram */
535 #define dram_counters(_x)                                                        \
536         _x(activate_cnt, ACTIVATE_CNT)                                           \
537         _x(read_cnt, READ_CNT)                                                   \
538         _x(read8_cnt, READ8_CNT)                                                 \
539         _x(write_cnt, WRITE_CNT)                                                 \
540         _x(write8_cnt, WRITE8_CNT)                                               \
541         _x(ref_cnt, REF_CNT)                                                     \
542         _x(extclks_cke_eq0_no_banks_active, EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE)     \
543         _x(clkstop_cke_eq0_no_banks_active, CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE)     \
544         _x(extclks_cke_eq1_no_banks_active, EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE)     \
545         _x(clkstop_cke_eq1_no_banks_active, CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE)     \
546         _x(extclks_cke_eq0_some_banks_active, EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE) \
547         _x(clkstop_cke_eq0_some_banks_active, CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE) \
548         _x(extclks_cke_eq1_some_banks_active, EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE) \
549         _x(clkstop_cke_eq1_some_banks_active, CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE) \
550         _x(sr_cke_eq0_clks, SR_CKE_EQ0_CLKS)                                     \
551         _x(dsr, DSR)
552
553 #define DEFINE_COUNTER(_name, _val) { .enabled = false, .device_mask = 0, },
554
555 static tegra_emc_dram_counter_t dram_counters_array[] = {
556         dram_counters(DEFINE_COUNTER)
557 };
558
559 #define DEFINE_SYSFS(_name, _val)                                       \
560                                                                         \
561 static struct kobject *tegra_mc_dram_##_name##_kobj;                    \
562                                                                         \
563 static ssize_t tegra_mc_dram_##_name##_show(struct kobject *kobj,       \
564         struct kobj_attribute *attr, char *buf)                         \
565 {                                                                       \
566         return tegra_mc_dram_show(kobj, attr, buf,                      \
567                                   _val - EMC_DRAM_STAT_BEGIN);          \
568 }                                                                       \
569                                                                         \
570 static ssize_t tegra_mc_dram_##_name##_store(struct kobject *kobj,      \
571         struct kobj_attribute *attr, const char *buf, size_t count)     \
572 {                                                                       \
573         if (sampling())                                                 \
574                 return 0;                                               \
575                                                                         \
576         return tegra_mc_dram_store(kobj, attr, buf, count,              \
577                                    _val - EMC_DRAM_STAT_BEGIN);         \
578 }                                                                       \
579                                                                         \
580                                                                         \
581 static struct kobj_attribute tegra_mc_dram_##_name##_enable =           \
582                __ATTR(enable, 0660, tegra_mc_dram_##_name##_show,       \
583                       tegra_mc_dram_##_name##_store);                   \
584                                                                         \
585 static struct kobj_attribute tegra_mc_dram_##_name##_device_mask =      \
586                __ATTR(device_mask, 0660, tegra_mc_dram_##_name##_show,  \
587                       tegra_mc_dram_##_name##_store);                   \
588                                                                         \
589 static struct attribute *tegra_mc_dram_##_name##_attrs[] = {            \
590         &tegra_mc_dram_##_name##_enable.attr,                           \
591         &tegra_mc_dram_##_name##_device_mask.attr,                      \
592         NULL,                                                           \
593 };                                                                      \
594                                                                         \
595 static struct attribute_group tegra_mc_dram_##_name##_attr_group = {    \
596         .attrs = tegra_mc_dram_##_name##_attrs,                         \
597 };
598
599 static struct kobject *tegra_mc_dram_kobj;
600
601 static ssize_t tegra_mc_dram_show(struct kobject *kobj,
602         struct kobj_attribute *attr, char *buf, int index)
603 {
604         if (index >= EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN)
605                 return -EINVAL;
606
607         if (strcmp(attr->attr.name, "enable") == 0)
608                 return sprintf(buf, "%d\n", dram_counters_array[index].enabled);
609         else if (strcmp(attr->attr.name, "device_mask") == 0)
610                 return sprintf(buf, "%d\n", dram_counters_array[index].device_mask);
611         else
612                 return -EINVAL;
613 }
614 static ssize_t tegra_mc_dram_store(struct kobject *kobj,
615         struct kobj_attribute *attr, const char *buf, size_t count, int index)
616 {
617         int value;
618
619         if (index >= EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN)
620                 return -EINVAL;
621
622         if (strcmp(attr->attr.name, "enable") == 0) {
623                 sscanf(buf, "%d\n", &value);
624                 if (value == 0 || value == 1)
625                         dram_counters_array[index].enabled = value;
626                 else
627                         return -EINVAL;
628
629                 return count;
630         } else if (strcmp(attr->attr.name, "device_mask") == 0) {
631                 sscanf(buf, "%d\n", &value);
632                 dram_counters_array[index].device_mask = (u8)value;
633
634                 return count;
635         } else
636                 return -EINVAL;
637 }
638
639 dram_counters(DEFINE_SYSFS)
640
641 /* Tegra Statistics */
642 typedef struct {
643         void __iomem *mmio;
644 } tegra_device_t;
645
646 static tegra_device_t mc = {
647         .mmio = IO_ADDRESS(TEGRA_MC_BASE),
648 };
649
650 static tegra_device_t emc = {
651         .mmio = IO_ADDRESS(TEGRA_EMC_BASE),
652 };
653
654 void mc_stat_start(tegra_mc_counter_t *counter0, tegra_mc_counter_t *counter1)
655 {
656         struct tegra_mc_counter *c;
657
658         emc_trace(TRACE_OPT, "tegra_mc_client_0_enabled=%d\n",
659                   tegra_mc_client_0_enabled);
660         emc_trace(TRACE_OPT, "counter0->enabled=%d, counter1->enabled=%d\n",
661                   counter0->enabled, counter1->enabled);
662         if (!tegra_mc_client_0_enabled)
663                 return;
664
665         c = (counter0->enabled) ? counter0 : counter1;
666
667         /* disable statistics */
668         writel((MC_STAT_CONTROL_0_EMC_GATHER_DISABLE <<
669                 MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
670                 mc.mmio + MC_STAT_CONTROL_0);
671
672         if (c->enabled) {
673                 u32 reg = 0;
674                 u32 reg_num;
675                 reg |= (MC_STAT_EMC_FILTER_SET0_MISCELLANEOUS_0_COALESCED_DIS <<
676                         MC_STAT_EMC_FILTER_SET0_MISCELLANEOUS_0_COALESCED_SHIFT);
677                 /* Note these registers are shared */
678                 writel(c->current_address_low,
679                        mc.mmio + MC_STAT_EMC_FILTER_SET0_ADDR__LIMIT_LO_0);
680                 writel(c->current_address_high,
681                        mc.mmio + MC_STAT_EMC_FILTER_SET0_ADDR_LIMIT_HI_0);
682                 emc_trace(TRACE_REG, "addr_limit low=0x%x, high=0x%x\n",
683                         readl(mc.mmio + MC_STAT_EMC_FILTER_SET0_ADDR__LIMIT_LO_0),
684                         readl(mc.mmio + MC_STAT_EMC_FILTER_SET0_ADDR_LIMIT_HI_0));
685                 writel(0xFFFFFFFF, mc.mmio + MC_STAT_EMC_CLOCK_LIMIT_0);
686                 writel(0xFFFF, mc.mmio + MC_STAT_EMC_CLOCK_LIMIT_MSBS_0);
687                 writel(reg, mc.mmio + MC_STAT_EMC_FILTER_SET0_MISCELLANEOUS_0);
688
689                 if (c->clients[0] == MC_STAT_AGGREGATE) {
690                         /* enable all clients */
691                         writel(0xFFFFFFFF,
692                                mc.mmio + MC_STAT_EMC_FILTER_SET0_CLIENT_0_0);
693                         writel(0xFFFFFFFF,
694                                mc.mmio + MC_STAT_EMC_FILTER_SET0_CLIENT_1_0);
695                         writel(0x00000003,
696                                mc.mmio + MC_STAT_EMC_FILTER_SET0_CLIENT_2_0);
697                         emc_trace(TRACE_REG, "select all clients\n");
698                 } else {
699                         /* enable a selected client at a time */
700                         writel(0, mc.mmio + MC_STAT_EMC_FILTER_SET0_CLIENT_0_0);
701                         writel(0, mc.mmio + MC_STAT_EMC_FILTER_SET0_CLIENT_1_0);
702                         writel(0, mc.mmio + MC_STAT_EMC_FILTER_SET0_CLIENT_2_0);
703                         reg_num = c->clients[c->current_client] / 32;
704                         reg = 1 << (c->clients[c->current_client] % 32);
705                         writel(reg,
706                                mc.mmio + MC_STAT_EMC_FILTER_SET0_CLIENT_0_0 +
707                                (reg_num * 4));
708                         emc_trace(TRACE_REG,
709                                   "idx=%d, client=%d, reg=0x%x, val=0x%x\n",
710                                   c->current_client,
711                                   c->clients[c->current_client],
712                                   MC_STAT_EMC_FILTER_SET0_CLIENT_0_0 +
713                                   (reg_num * 4), reg);
714                         emc_trace(TRACE_REG,
715                                   "client_0=0x%x, client_1=0x%x, client_2=0x%x\n",
716                                   readl(mc.mmio +
717                                         MC_STAT_EMC_FILTER_SET0_CLIENT_0_0),
718                                   readl(mc.mmio +
719                                         MC_STAT_EMC_FILTER_SET0_CLIENT_1_0),
720                                   readl(mc.mmio +
721                                         MC_STAT_EMC_FILTER_SET0_CLIENT_2_0));
722                 }
723         }
724
725         /* reset then enable statistics */
726         writel((MC_STAT_CONTROL_0_EMC_GATHER_RST << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
727                 mc.mmio + MC_STAT_CONTROL_0);
728         writel((MC_STAT_CONTROL_0_EMC_GATHER_ENABLE << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
729                 mc.mmio + MC_STAT_CONTROL_0);
730 }
731
732 void mc_stat_stop(tegra_mc_counter_t *counter0,
733         tegra_mc_counter_t *counter1)
734 {
735         /* Disable statistics */
736         writel((MC_STAT_CONTROL_0_EMC_GATHER_DISABLE << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
737                 mc.mmio + MC_STAT_CONTROL_0);
738
739         if (counter0->enabled) {
740                 counter0->value = (((u64)readl(mc.mmio + MC_STAT_EMC_SET0_COUNT_MSBS_0)) << 32);
741                 counter0->value |= readl(mc.mmio + MC_STAT_EMC_SET0_COUNT_0);
742                 emc_trace(TRACE_REG,"%s:counter0->value=0x%llx\n ", __func__, counter0->value);
743         }
744         else {
745                 counter1->value = (((u64)readl(mc.mmio + MC_STAT_EMC_SET1_COUNT_MSBS_0)) << 32);
746                 counter1->value = readl(mc.mmio + MC_STAT_EMC_SET1_COUNT_0);
747                 emc_trace(TRACE_REG,"%s:counter0->value=0x%llx\n  ", __func__, counter1->value);
748         }
749 }
750
751 void emc_stat_start(tegra_mc_counter_t *dram_mc_counter,
752         tegra_emc_dram_counter_t *dram_counter)
753 {
754         u32 emc_stat = 0;
755
756         /* disable statistics */
757         emc_stat = (EMC_STAT_CONTROL_0_DRAM_GATHER_DISABLE <<
758                         EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
759         writel(emc_stat, emc.mmio + EMC_STAT_CONTROL_0);
760
761         if (tegra_mc_client_0_enabled && dram_mc_counter->enabled) {
762                 /* FIXME: should check if mode makes sense for emc stats. */
763                 if (dram_mc_counter->mode == FILTER_ADDR) {
764
765                 } else if (dram_mc_counter->mode == FILTER_CLIENT) {
766
767                 } else if (dram_mc_counter->mode == FILTER_NONE) {
768
769                 }
770         }
771
772         writel(0xFFFFFFFF, emc.mmio + EMC_STAT_DRAM_CLOCK_LIMIT_LO_0);
773         writel(0xFF, emc.mmio + EMC_STAT_DRAM_CLOCK_LIMIT_HI_0);
774
775         /* Reset then enable statistics */
776         emc_stat = (EMC_STAT_CONTROL_0_DRAM_GATHER_RST <<
777                         EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
778         writel(emc_stat, emc.mmio + EMC_STAT_CONTROL_0);
779
780         emc_stat = (EMC_STAT_CONTROL_0_DRAM_GATHER_ENABLE <<
781                         EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
782         writel(emc_stat, emc.mmio + EMC_STAT_CONTROL_0);
783 }
784
785 void emc_stat_stop(tegra_mc_counter_t *dram_mc_counter,
786         tegra_emc_dram_counter_t *dram_counter)
787 {
788         u32 emc_stat = 0;
789         int i;
790         int dev0_offsets_lo[] = {
791                 EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_LO_0,
792                 EMC_STAT_DRAM_DEV0_READ_CNT_LO_0,
793                 EMC_STAT_DRAM_DEV0_READ8_CNT_LO_0,
794                 EMC_STAT_DRAM_DEV0_WRITE_CNT_LO_0,
795                 EMC_STAT_DRAM_DEV0_WRITE8_CNT_LO_0,
796                 EMC_STAT_DRAM_DEV0_REF_CNT_LO_0,
797                 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO_0,
798                 EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO_0,
799                 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO_0,
800                 EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO_0,
801                 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO_0,
802                 EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO_0,
803                 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO_0,
804                 EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO_0,
805                 EMC_STAT_DRAM_DEV0_SR_CKE_EQ0_CLKS_LO_0,
806                 EMC_STAT_DRAM_DEV0_DSR_0
807         };
808         int dev0_offsets_hi[] = {
809                 EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_HI_0,
810                 EMC_STAT_DRAM_DEV0_READ_CNT_HI_0,
811                 EMC_STAT_DRAM_DEV0_READ8_CNT_HI_0,
812                 EMC_STAT_DRAM_DEV0_WRITE_CNT_HI_0,
813                 EMC_STAT_DRAM_DEV0_WRITE8_CNT_HI_0,
814                 EMC_STAT_DRAM_DEV0_REF_CNT_HI_0,
815                 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI_0,
816                 EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI_0,
817                 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI_0,
818                 EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI_0,
819                 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI_0,
820                 EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI_0,
821                 EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI_0,
822                 EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI_0,
823                 EMC_STAT_DRAM_DEV0_SR_CKE_EQ0_CLKS_HI_0,
824                 EMC_STAT_DRAM_DEV0_DSR_0
825         };
826         int dev1_offsets_lo[] = {
827                 EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_LO_0,
828                 EMC_STAT_DRAM_DEV1_READ_CNT_LO_0,
829                 EMC_STAT_DRAM_DEV1_READ8_CNT_LO_0,
830                 EMC_STAT_DRAM_DEV1_WRITE_CNT_LO_0,
831                 EMC_STAT_DRAM_DEV1_WRITE8_CNT_LO_0,
832                 EMC_STAT_DRAM_DEV1_REF_CNT_LO_0,
833                 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO_0,
834                 EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO_0,
835                 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO_0,
836                 EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO_0,
837                 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO_0,
838                 EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO_0,
839                 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO_0,
840                 EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO_0,
841                 EMC_STAT_DRAM_DEV1_SR_CKE_EQ0_CLKS_LO_0,
842                 EMC_STAT_DRAM_DEV1_DSR_0
843         };
844         int dev1_offsets_hi[] = {
845                 EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_HI_0,
846                 EMC_STAT_DRAM_DEV1_READ_CNT_HI_0,
847                 EMC_STAT_DRAM_DEV1_READ8_CNT_HI_0,
848                 EMC_STAT_DRAM_DEV1_WRITE_CNT_HI_0,
849                 EMC_STAT_DRAM_DEV1_WRITE8_CNT_HI_0,
850                 EMC_STAT_DRAM_DEV1_REF_CNT_HI_0,
851                 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI_0,
852                 EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI_0,
853                 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI_0,
854                 EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI_0,
855                 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI_0,
856                 EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI_0,
857                 EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI_0,
858                 EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI_0,
859                 EMC_STAT_DRAM_DEV1_SR_CKE_EQ0_CLKS_HI_0,
860                 EMC_STAT_DRAM_DEV1_DSR_0
861         };
862
863         /* Disable statistics */
864         emc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_DISABLE <<
865                         EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
866         writel(emc_stat, emc.mmio + EMC_STAT_CONTROL_0);
867
868         //if (tegra_mc_client_0_enabled == true)
869         //      dram_mc_counter->value = readl(emc.mmio + EMC_STAT_LLMC_COUNT_0_0);
870
871         for (i = 0; i < EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN; i++) {
872                 if (dram_counter[i].enabled) {
873                         dram_counter[i].value = 0;
874                         if (!(dram_counter[i].device_mask & 0x1)) {
875                                 if (readl(emc.mmio + dev0_offsets_hi[i]) != 0) {
876                                         dram_counter[i].value = 0xFFFFFFFF;
877                                         continue;
878                                 }
879                                 dram_counter[i].value +=
880                                         readl(emc.mmio + dev0_offsets_lo[i]);
881                         }
882                         if (!(dram_counter[i].device_mask & 0x2)) {
883                                 if (readl(emc.mmio + dev1_offsets_hi[i]) != 0) {
884                                         dram_counter[i].value = 0xFFFFFFFF;
885                                         continue;
886                                 }
887                                 dram_counter[i].value +=
888                                         readl(emc.mmio + dev1_offsets_lo[i]);
889                         }
890                 }
891         }
892 }
893
894 static void stat_reschedule(tegra_mc_counter_t *counter0,
895         tegra_mc_counter_t *counter1,
896         tegra_mc_counter_t *llp)
897 {
898         int i;
899         struct tegra_mc_counter *counters[] = {
900                 counter0,
901                 counter1,
902                 llp
903         };
904
905         if (!tegra_mc_client_0_enabled)
906                 return;
907
908         emc_trace(TRACE_FLOW, "%s\n", __func__);
909         for (i = 0; i < ARRAY_SIZE(counters); i++) {
910                 struct tegra_mc_counter *c = counters[i];
911
912                 c->address_range_change = false;
913                 if (!c->enabled || !c->reschedule)
914                         continue;
915
916                 c->sample_count++;
917
918                 if (c->sample_count < c->period)
919                         continue;
920
921                 c->sample_count = 0;
922
923                 if (c->mode == FILTER_CLIENT) {
924                         c->current_client++;
925                         if (c->current_client == c->num_clients)
926                                 c->current_client = 0;
927                         continue;
928                 }
929
930                 c->address_range_change = true;
931                 c->current_address_low = c->current_address_high+1;
932
933                 if (c->current_address_low >= c->address_low+c->address_length_1)
934                         c->current_address_low = c->address_low;
935
936                 c->current_address_high = c->current_address_low +
937                         c->address_window_size_1;
938         }
939 }
940
941 static void stat_start(void)
942 {
943         emc_trace(TRACE_FLOW, "%s\n", __func__);
944         mc_stat_start(&mc_counter0, &mc_counter1);
945         emc_stat_start(&emc_dram_counter, dram_counters_array);
946 }
947
948 static void stat_stop(void)
949 {
950         emc_trace(TRACE_FLOW, "%s\n", __func__);
951         mc_stat_stop(&mc_counter0, &mc_counter1);
952         emc_stat_stop(&emc_dram_counter, dram_counters_array);
953 }
954
955 static size_t stat_log_counter(struct tegra_mc_counter *c,
956         struct tegra_mc_counter *l, log_event_t *e, u32* value)
957 {
958         size_t size = 0;
959
960         emc_trace(TRACE_FLOW, "%s\n", __func__);
961         *value = c->value;
962         if (l)
963                 *value += l->value;
964
965         if (!c->enabled || (l && !l->enabled))// || !*value)
966                 return 0;
967
968         e->word0.enabled = 1;
969         e->word0.address_range_change = c->address_range_change;
970         e->word0.event_id = (l) ? MC_STAT_AGGREGATE :
971                 c->clients[c->current_client];
972         e->word0.address_range_low_pfn = __phys_to_pfn(c->current_address_low);
973         size += sizeof(e->word0);
974
975         if (c->address_range_change) {
976                 e->word1.address_range_length_pfn =
977                         __phys_to_pfn(c->address_window_size_1+1);
978                 size += sizeof(e->word1);
979         }
980
981         size += sizeof(*value);
982         return size;
983 }
984
985 #define statcpy(_buf, _bufstart, _buflen, _elem)        \
986         do {                                            \
987                 size_t s = sizeof(_elem);               \
988                 memcpy(_buf, &_elem, s);                \
989                 _buf += s;                              \
990                 if (_buf >= _bufstart + _buflen)        \
991                         _buf = _bufstart;               \
992         } while (0);
993
994 static void stat_log(void)
995 {
996         log_header_t    header = {0, 0};
997         log_event_t     event[LOG_EVENT_NUMBER_MAX];
998         u32             value[LOG_EVENT_NUMBER_MAX];
999         int             i, count = 0;
1000         unsigned long   flags;
1001         size_t elem;
1002         int     required_log_size = 0;
1003
1004         emc_trace(TRACE_FLOW, "%s\n", __func__);
1005         required_log_size += sizeof(header);
1006
1007         if (tegra_mc_client_0_enabled) {
1008                 elem = stat_log_counter(&mc_counter0, NULL, &event[count],
1009                                         &value[count]);
1010                 if (elem) {
1011                         required_log_size += elem;
1012                         count++;
1013                 }
1014
1015                 elem = stat_log_counter(&mc_counter1, &emc_dram_counter,
1016                                         &event[count], &value[count]);
1017
1018                 if (elem) {
1019                         required_log_size += elem;
1020                         count++;
1021                 }
1022         }
1023
1024         for (i = 0; i < (EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN) &&
1025                 count < LOG_EVENT_NUMBER_MAX; i++) {
1026                 if (dram_counters_array[i].enabled && (dram_counters_array[i].value != 0)) {
1027                         event[count].word0.enabled = 1;
1028                         event[count].word0.address_range_change = false;
1029                         event[count].word0.event_id = i + EMC_DRAM_STAT_BEGIN;
1030                         event[count].word0.address_range_low_pfn = 0;
1031                         required_log_size += sizeof(event[count].word0);
1032
1033                         event[count].word1.address_range_length_pfn =
1034                                 0xFFFFFFFFUL >> SHIFT_4K;
1035
1036                         value[count] = dram_counters_array[i].value;
1037                         required_log_size += sizeof(value[count]);
1038
1039                         count++;
1040                 }
1041         }
1042
1043         header.time_quantum = sample_quantum * MILLISECONDS_TO_TIME_QUANTUM;
1044         for (i = 0; i < count; i++) {
1045                 header.event_state_change |= 1 << i;
1046         }
1047
1048         if (header.event_state_change != 0) {
1049                 spin_lock_irqsave(&sample_log_lock, flags);
1050                 if (unlikely(required_log_size > sample_log_size)) {
1051                         pr_err("%s: sample log too small!\n", __func__);
1052                         WARN_ON(1);
1053                         spin_unlock_irqrestore(&sample_log_lock, flags);
1054                         goto reschedule;
1055                 }
1056
1057                 statcpy(sample_log_wptr, sample_log, SAMPLE_LOG_SIZE, header);
1058
1059                 for (i=0; i<count; i++) {
1060                         statcpy(sample_log_wptr, sample_log,
1061                                 SAMPLE_LOG_SIZE, event[i].word0);
1062                         if (!event[i].word0.address_range_change)
1063                                 continue;
1064                         statcpy(sample_log_wptr, sample_log,
1065                                 SAMPLE_LOG_SIZE, event[i].word1);
1066                 }
1067
1068                 for (i=0; i<count; i++) {
1069                         statcpy(sample_log_wptr, sample_log,
1070                                 SAMPLE_LOG_SIZE, value[i]);
1071                 }
1072
1073                 sample_log_size -= required_log_size;
1074                 spin_unlock_irqrestore(&sample_log_lock, flags);
1075         }
1076
1077 reschedule:
1078         stat_reschedule(&mc_counter0, &mc_counter1, &emc_dram_counter);
1079 }
1080
1081 static enum hrtimer_restart sample_timer_function(struct hrtimer *handle)
1082 {
1083         stat_stop();
1084         stat_log();
1085
1086         if (!sample_enable)
1087                 return HRTIMER_NORESTART;
1088
1089         emc_trace(TRACE_FLOW, "%s\n", __func__);
1090         stat_start();
1091
1092         hrtimer_add_expires_ns(&sample_timer, (u64)sample_quantum * 1000000);
1093         return HRTIMER_RESTART;
1094 }
1095
1096 /* module init */
1097 #define REGISTER_SYSFS(_name, _val)                                     \
1098         tegra_mc_dram_##_name##_kobj =                                  \
1099                 kobject_create_and_add(#_name, tegra_mc_dram_kobj);     \
1100         if (sysfs_create_group(tegra_mc_dram_##_name##_kobj,            \
1101                                 &tegra_mc_dram_##_name##_attr_group) != 0) { \
1102                 pr_err("\n%s:sysfs_create_group failed",__func__);\
1103                 return -EINVAL; \
1104         }
1105
1106 static int tegra_mc_init(void)
1107 {
1108         int i;
1109         int rc;
1110
1111         emc_trace(TRACE_FLOW, "%s\n", __func__);
1112         /* /sys/devices/system/tegra_mc */
1113         rc = sysdev_class_register(&tegra_mc_sysclass);
1114         if(rc)
1115                 goto out;
1116
1117         for (i = 0;  i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
1118                 rc = sysdev_class_create_file(&tegra_mc_sysclass,
1119                         tegra_mc_attrs[i]);
1120                 if(rc) {
1121                         printk("\n sysdev_class_create_file : failed \n");
1122                         goto out_unreg_class;
1123                 }
1124         }
1125
1126         /* /sys/devices/system/tegra_mc/client */
1127         tegra_mc_client_kobj = kobject_create_and_add("client",
1128                 &tegra_mc_sysclass.kset.kobj);
1129         if(!tegra_mc_client_kobj)
1130                 goto out_remove_sysdev_files;
1131
1132         /* /sys/devices/system/tegra_mc/client/0 */
1133         tegra_mc_client_0_kobj = kobject_create_and_add("0",
1134                 tegra_mc_client_kobj);
1135         if(!tegra_mc_client_0_kobj)
1136                 goto out_put_kobject_client;
1137
1138         rc = sysfs_create_group(tegra_mc_client_0_kobj,
1139                 &tegra_mc_client_0_attr_group);
1140         if(rc)
1141                 goto out_put_kobject_client_0;
1142
1143         /* /sys/devices/system/tegra_mc/dram */
1144         tegra_mc_dram_kobj = kobject_create_and_add("dram",
1145                 &tegra_mc_sysclass.kset.kobj);
1146         if(!tegra_mc_dram_kobj)
1147                 goto out_remove_group_client_0;
1148
1149         dram_counters(REGISTER_SYSFS)
1150
1151         /* hrtimer */
1152         hrtimer_init(&sample_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
1153         sample_timer.function = sample_timer_function;
1154
1155         return 0;
1156
1157 out_remove_group_client_0:
1158         sysfs_remove_group(tegra_mc_client_0_kobj, &tegra_mc_client_0_attr_group);
1159
1160 out_put_kobject_client_0:
1161         kobject_put(tegra_mc_client_0_kobj);
1162
1163 out_put_kobject_client:
1164         kobject_put(tegra_mc_client_kobj);
1165
1166 out_remove_sysdev_files:
1167         for (i = 0;  i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
1168                 sysdev_class_remove_file(&tegra_mc_sysclass, tegra_mc_attrs[i]);
1169         }
1170
1171 out_unreg_class:
1172         sysdev_class_unregister(&tegra_mc_sysclass);
1173
1174 out:
1175         return rc;
1176 }
1177
1178 /* module deinit */
1179 #define REMOVE_SYSFS(_name, _val)                                       \
1180         sysfs_remove_group(tegra_mc_dram_##_name##_kobj,                \
1181                            &tegra_mc_dram_##_name##_attr_group);        \
1182         kobject_put(tegra_mc_dram_##_name##_kobj);
1183
1184 static void tegra_mc_exit(void)
1185 {
1186         int i;
1187
1188         emc_trace(TRACE_FLOW, "%s\n", __func__);
1189         stat_stop();
1190
1191         /* hrtimer */
1192         hrtimer_cancel(&sample_timer);
1193
1194         /* /sys/devices/system/tegra_mc/client */
1195         sysfs_remove_group(tegra_mc_client_0_kobj,
1196                 &tegra_mc_client_0_attr_group);
1197         kobject_put(tegra_mc_client_0_kobj);
1198         kobject_put(tegra_mc_client_kobj);
1199
1200         /* /sys/devices/system/tegra_mc/dram */
1201         dram_counters(REMOVE_SYSFS)
1202         kobject_put(tegra_mc_dram_kobj);
1203
1204         /* /sys/devices/system/tegra_mc */
1205         for (i = 0;  i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
1206                 sysdev_class_remove_file(&tegra_mc_sysclass, tegra_mc_attrs[i]);
1207         }
1208         sysdev_class_unregister(&tegra_mc_sysclass);
1209 }
1210
1211 module_init(tegra_mc_init);
1212 module_exit(tegra_mc_exit);
1213 MODULE_LICENSE("Dual BSD/GPL");