gator: ARM DS-5.3 Streamline gator driver
[android-sdk/arm-ds5-gator.git] / gator_events_armv7.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  */
8 #include <linux/types.h>
9 #include <linux/sched.h>
10 #include <linux/interrupt.h>
11 #include <linux/irq.h>
12 #include <linux/fs.h>
14 #include "gator.h"
16 #if defined(__arm__)
18 #define CORTEX_A8       0xc08
19 #define CORTEX_A9       0xc09
21 static const char *pmnc_name;
22 static int pmnc_count;
24 extern u32 gator_cpuid(void);
26 /*
27  * Per-CPU PMNC: config reg
28  */
29 #define PMNC_E          (1 << 0)        /* Enable all counters */
30 #define PMNC_P          (1 << 1)        /* Reset all counters */
31 #define PMNC_C          (1 << 2)        /* Cycle counter reset */
32 #define PMNC_D          (1 << 3)        /* CCNT counts every 64th cpu cycle */
33 #define PMNC_X          (1 << 4)        /* Export to ETM */
34 #define PMNC_DP         (1 << 5)        /* Disable CCNT if non-invasive debug*/
35 #define PMNC_MASK       0x3f            /* Mask for writable bits */
37 /*
38  * CNTENS: counters enable reg
39  */
40 #define CNTENS_P0       (1 << 0)
41 #define CNTENS_P1       (1 << 1)
42 #define CNTENS_P2       (1 << 2)
43 #define CNTENS_P3       (1 << 3)
44 #define CNTENS_C        (1 << 31)
45 #define CNTENS_MASK     0x8000000f      /* Mask for writable bits */
47 /*
48  * CNTENC: counters disable reg
49  */
50 #define CNTENC_P0       (1 << 0)
51 #define CNTENC_P1       (1 << 1)
52 #define CNTENC_P2       (1 << 2)
53 #define CNTENC_P3       (1 << 3)
54 #define CNTENC_C        (1 << 31)
55 #define CNTENC_MASK     0x8000000f      /* Mask for writable bits */
57 /*
58  * INTENS: counters overflow interrupt enable reg
59  */
60 #define INTENS_P0       (1 << 0)
61 #define INTENS_P1       (1 << 1)
62 #define INTENS_P2       (1 << 2)
63 #define INTENS_P3       (1 << 3)
64 #define INTENS_C        (1 << 31)
65 #define INTENS_MASK     0x8000000f      /* Mask for writable bits */
67 /*
68  * EVTSEL: Event selection reg
69  */
70 #define EVTSEL_MASK     0x7f            /* Mask for writable bits */
72 /*
73  * SELECT: Counter selection reg
74  */
75 #define SELECT_MASK     0x1f            /* Mask for writable bits */
77 /*
78  * FLAG: counters overflow flag status reg
79  */
80 #define FLAG_P0         (1 << 0)
81 #define FLAG_P1         (1 << 1)
82 #define FLAG_P2         (1 << 2)
83 #define FLAG_P3         (1 << 3)
84 #define FLAG_C          (1 << 31)
85 #define FLAG_MASK       0x8000000f      /* Mask for writable bits */
87 #define CCNT            0
88 #define CNT0            1
89 #define CNTMAX          (6+1)
91 static unsigned long pmnc_enabled[CNTMAX];
92 static unsigned long pmnc_event[CNTMAX];
93 static unsigned long pmnc_key[CNTMAX];
95 static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
96 static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
98 static inline void armv7_pmnc_write(u32 val)
99 {
100         val &= PMNC_MASK;
101         asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
104 static inline u32 armv7_pmnc_read(void)
106         u32 val;
107         asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
108         return val;
111 static inline u32 armv7_ccnt_read(void)
113         u32 val;
114         asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
115         return val;
118 static inline u32 armv7_cntn_read(void)
120         u32 val;
121         asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
122         return val;
125 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
127         u32 val;
129         if (cnt >= CNTMAX) {
130                 pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
131                 return -1;
132         }
134         if (cnt == CCNT)
135                 val = CNTENS_C;
136         else
137                 val = (1 << (cnt - CNT0));
139         val &= CNTENS_MASK;
140         asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
142         return cnt;
145 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
147         u32 val;
149         if (cnt >= CNTMAX) {
150                 pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
151                 return -1;
152         }
154         if (cnt == CCNT)
155                 val = CNTENC_C;
156         else
157                 val = (1 << (cnt - CNT0));
159         val &= CNTENC_MASK;
160         asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
162         return cnt;
165 static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
167         u32 val;
169         if (cnt >= CNTMAX) {
170                 pr_err("gator: CPU%u enabling wrong PMNC counter interrupt enable %d\n", smp_processor_id(), cnt);
171                 return -1;
172         }
174         if (cnt == CCNT)
175                 val = INTENS_C;
176         else
177                 val = (1 << (cnt - CNT0));
179         val &= INTENS_MASK;
180         asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
182         return cnt;
185 static inline u32 armv7_pmnc_getreset_flags(void)
187         u32 val;
189         /* Read */
190         asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
192         /* Write to clear flags */
193         val &= FLAG_MASK;
194         asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
196         return val;
199 static inline int armv7_pmnc_select_counter(unsigned int cnt)
201         u32 val;
203         if ((cnt == CCNT) || (cnt >= CNTMAX)) {
204                 pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt);
205                 return -1;
206         }
208         val = (cnt - CNT0) & SELECT_MASK;
209         asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
211         return cnt;
214 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
216         if (armv7_pmnc_select_counter(cnt) == cnt) {
217                 val &= EVTSEL_MASK;
218                 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
219         }
222 static void armv7_pmnc_reset_counter(unsigned int cnt)
224         u32 val = 0;
226         if (cnt == CCNT) {
227                 armv7_pmnc_disable_counter(cnt);
229                 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
231                 if (pmnc_enabled[cnt] != 0)
232                     armv7_pmnc_enable_counter(cnt);
234         } else if (cnt >= CNTMAX) {
235                 pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt);
236         } else {
237                 armv7_pmnc_disable_counter(cnt);
239                 if (armv7_pmnc_select_counter(cnt) == cnt)
240                     asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
242                 if (pmnc_enabled[cnt] != 0)
243                     armv7_pmnc_enable_counter(cnt);
244         }
247 static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
249         struct dentry *dir;
250         int i;
252         for (i = 0; i < pmnc_count; i++) {
253                 char buf[40];
254                 if (i == 0) {
255                         snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
256                 } else {
257                         snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1);
258                 }
259                 dir = gatorfs_mkdir(sb, root, buf);
260                 if (!dir) {
261                         return -1;
262                 }
263                 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
264                 if (i > 0) {
265                         gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
266                 }
267                 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
268         }
270         return 0;
273 static int gator_events_armv7_init(int *key)
275         unsigned int cnt;
277         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
278                 pmnc_enabled[cnt] = 0;
279                 pmnc_event[cnt] = 0;
280                 pmnc_key[cnt] = *key;
281                 *key = *key + 1;
282         }
284         return 0;
287 static void __gator_events_armv7_start(void* unused)
289         unsigned int cnt;
291         if (armv7_pmnc_read() & PMNC_E) {
292                 pr_err("gator: CPU%u PMNC still enabled when setup new event counter\n", raw_smp_processor_id());
293                 pmnc_count = 0;
294                 return;
295         }
297         /* Initialize & Reset PMNC: C bit and P bit */
298         armv7_pmnc_write(PMNC_P | PMNC_C);
300         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
301                 unsigned long event;
303                 per_cpu(perfPrev, raw_smp_processor_id())[cnt] = 0;
305                 if (!pmnc_enabled[cnt])
306                         continue;
308                 /*
309                  * Disable counter
310                  */
311                 armv7_pmnc_disable_counter(cnt);
313                 event = pmnc_event[cnt] & 255;
315                 /*
316                  * Set event (if destined for PMNx counters)
317                  * We don't need to set the event if it's a cycle count
318                  */
319                 if (cnt != CCNT)
320                         armv7_pmnc_write_evtsel(cnt, event);
322                 /*
323                  * [Do not] Enable interrupt for this counter
324                  */
325                 /* armv7_pmnc_enable_intens(cnt); */
327                 /*
328                  * Reset counter
329                  */
330                 armv7_pmnc_reset_counter(cnt);
332                 /*
333                  * Enable counter
334                  */
335                 armv7_pmnc_enable_counter(cnt);
336         }
338         // enable
339         armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
342 static int gator_events_armv7_start(void)
344         if (!pmnc_count)
345                 return 0;
346         return on_each_cpu(__gator_events_armv7_start, NULL, 1);
349 static void __gator_events_armv7_stop(void* unused)
351         unsigned int cnt;
352         armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
354         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
355                 pmnc_enabled[cnt] = 0;
356                 pmnc_event[cnt] = 0;
357         }
360 static void gator_events_armv7_stop(void)
362         if (!pmnc_count)
363                 return;
364         on_each_cpu(__gator_events_armv7_stop, NULL, 1);
367 static int gator_events_armv7_read(int **buffer)
369         int cnt, len = 0;
370         int cpu = raw_smp_processor_id();
372         if (!pmnc_count)
373                 return 0;
375         armv7_pmnc_getreset_flags();
376         for (cnt = 0; cnt < pmnc_count; cnt++) {
377                 if (pmnc_enabled[cnt]) {
378                         int value;
379                         if (cnt == CCNT) {
380                                 value = armv7_ccnt_read();
381                         } else if (armv7_pmnc_select_counter(cnt) == cnt) {
382                                 value = armv7_cntn_read();
383                         } else {
384                                 value = 0;
385                         }
386                         armv7_pmnc_reset_counter(cnt);
387                         if (value != per_cpu(perfPrev, cpu)[cnt]) {
388                                 per_cpu(perfPrev, cpu)[cnt] = value;
389                                 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
390                                 per_cpu(perfCnt, cpu)[len++] = value;
391                         }
392                 }
393         }
395         // update or discard
396         if (buffer)
397                 *buffer = per_cpu(perfCnt, cpu);
399         return len;
401 #endif
403 int gator_events_armv7_install(gator_interface *gi) {
404 #if defined(__arm__)
405         switch (gator_cpuid()) {
406         case CORTEX_A8:
407                 pmnc_name = "Cortex-A8";
408                 pmnc_count = 4;
409                 break;
410         case CORTEX_A9:
411                 pmnc_name = "Cortex-A9";
412                 pmnc_count = 6;
413                 break;
414         default:
415                 return -1;
416         }
418         pmnc_count++; // CNT[n] + CCNT
420         gi->create_files = gator_events_armv7_create_files;
421         gi->init = gator_events_armv7_init;
422         gi->start = gator_events_armv7_start;
423         gi->stop = gator_events_armv7_stop;
424         gi->read = gator_events_armv7_read;
425 #endif
426         return 0;