gator: ARM DS-5.3 Streamline gator driver
[android-sdk/arm-ds5-gator.git] / gator_events_armv6.c
1 /**
2  * Copyright (C) ARM Limited 2010. All rights reserved.
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 version 2 as
6  * published by the Free Software Foundation.
7  */
9 #include <linux/types.h>
10 #include <linux/sched.h>
11 #include <linux/interrupt.h>
12 #include <linux/irq.h>
13 #include <linux/fs.h>
15 #include "gator.h"
17 #if defined(__arm__)
19 #define ARM1136         0xb36
20 #define ARM1156         0xb56
21 #define ARM1176         0xb76
23 static const char *pmnc_name;
25 extern u32 gator_cpuid(void);
27 /*
28  * Per-CPU PMCR
29  */
30 #define PMCR_E                  (1 << 0)        /* Enable */
31 #define PMCR_P                  (1 << 1)        /* Count reset */
32 #define PMCR_C                  (1 << 2)        /* Cycle counter reset */
33 #define PMCR_OFL_PMN0   (1 << 8)        /* Count reg 0 overflow */
34 #define PMCR_OFL_PMN1   (1 << 9)        /* Count reg 1 overflow */
35 #define PMCR_OFL_CCNT   (1 << 10)       /* Cycle counter overflow */
37 #define PMN0 0
38 #define PMN1 1
39 #define CCNT 2
40 #define CNTMAX  (CCNT+1)
42 static int pmnc_count = 0;
43 static unsigned long pmnc_enabled[CNTMAX];
44 static unsigned long pmnc_event[CNTMAX];
45 static unsigned long pmnc_key[CNTMAX];
47 static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
48 static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
50 static inline void armv6_pmnc_write(u32 val)
51 {
52         /* upper 4bits and 7, 11 are write-as-0 */
53         val &= 0x0ffff77f;
54         asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val));
55 }
57 static inline u32 armv6_pmnc_read(void)
58 {
59         u32 val;
60         asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val));
61         return val;
62 }
64 static void armv6_pmnc_reset_counter(unsigned int cnt)
65 {
66         u32 val = 0;
67         switch (cnt) {
68         case CCNT:
69                 asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val));
70                 break;
71         case PMN0:
72                 asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val));
73                 break;
74         case PMN1:
75                 asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val));
76                 break;
77         }
78 }
80 int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root)
81 {
82         struct dentry *dir;
83         int i;
85         pmnc_count = 3;
87         for (i = PMN0; i <= CCNT; i++) {
88                 char buf[40];
89                 if (i == CCNT) {
90                         snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
91                 } else {
92                         snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i);
93                 }
94                 dir = gatorfs_mkdir(sb, root, buf);
95                 if (!dir) {
96                         return -1;
97                 }
98                 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
99                 if (i != CCNT) {
100                         gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
101                 }
102                 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
103         }
105         return 0;
108 static int gator_events_armv6_init(int *key)
110         unsigned int cnt;
112         for (cnt = PMN0; cnt <= CCNT; cnt++) {
113                 pmnc_enabled[cnt] = 0;
114                 pmnc_event[cnt] = 0;
115                 pmnc_key[cnt] = *key;
116                 *key = *key + 1;
117         }
119         return 0;
122 static void __gator_events_armv6_start(void* unused)
124         unsigned int cnt;
125         u32 pmnc;
127         if (armv6_pmnc_read() & PMCR_E) {
128                 pr_err("gator: CPU%u PMNC still enabled when setup new event counter.\n", smp_processor_id());
129                 pmnc_count = 0;
130                 return;
131         }
133         /* initialize PMNC, reset overflow, D bit, C bit and P bit. */
134         armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT |
135                         PMCR_C | PMCR_P);
137         /* configure control register */
138         for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) {
139                 unsigned long event;
141                 per_cpu(perfPrev, raw_smp_processor_id())[cnt] = 0;
143                 if (!pmnc_enabled[cnt])
144                         continue;
146                 event = pmnc_event[cnt] & 255;
148                 /*
149                  * Set event (if destined for PMNx counters)
150                  */
151                 if (cnt == PMN0) {
152                         pmnc |= event << 20;
153                 } else if (cnt == PMN1) {
154                         pmnc |= event << 12;
155                 }
157                 /*
158                  * Reset counter
159                  */
160                 armv6_pmnc_reset_counter(cnt);
161         }
162         armv6_pmnc_write(pmnc | PMCR_E);
165 static int gator_events_armv6_start(void)
167         if (!pmnc_count)
168                 return 0;
169         return on_each_cpu(__gator_events_armv6_start, NULL, 1);
172 static void __gator_events_armv6_stop(void* unused)
174         unsigned int cnt;
176         armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E);
178         for (cnt = PMN0; cnt <= CCNT; cnt++) {
179                 armv6_pmnc_reset_counter(cnt);
180                 pmnc_enabled[cnt] = 0;
181                 pmnc_event[cnt] = 0;
182         }
185 static void gator_events_armv6_stop(void)
187         if (!pmnc_count)
188                 return;
189         on_each_cpu(__gator_events_armv6_stop, NULL, 1);
192 static int gator_events_armv6_read(int **buffer)
194         int cnt, len = 0;
195         int cpu = raw_smp_processor_id();
197         if (!pmnc_count)
198                 return 0;
200         for (cnt = PMN0; cnt <= CCNT; cnt++) {
201                 if (pmnc_enabled[cnt]) {
202                         u32 value = 0;
203                         switch (cnt) {
204                         case CCNT:
205                                 asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value));
206                                 break;
207                         case PMN0:
208                                 asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value));
209                                 break;
210                         case PMN1:
211                                 asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value));
212                                 break;
213                         }
214                         armv6_pmnc_reset_counter(cnt);
215                         if (value != per_cpu(perfPrev, cpu)[cnt]) {
216                                 per_cpu(perfPrev, cpu)[cnt] = value;
217                                 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
218                                 per_cpu(perfCnt, cpu)[len++] = value;
219                         }
220                 }
221         }
223         // update or discard
224         if (buffer)
225                 *buffer = per_cpu(perfCnt, cpu);
227         return len;
229 #endif
231 int gator_events_armv6_install(gator_interface *gi) {
232 #if defined(__arm__)
233         switch (gator_cpuid()) {
234         case ARM1136:
235         case ARM1156:
236         case ARM1176:
237                 pmnc_name = "ARM11";
238                 break;
239         default:
240                 return -1;
241         }
243         gi->create_files = gator_events_armv6_create_files;
244         gi->init = gator_events_armv6_init;
245         gi->start = gator_events_armv6_start;
246         gi->stop = gator_events_armv6_stop;
247         gi->read = gator_events_armv6_read;
248 #endif
249         return 0;