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));
102 }
104 static inline u32 armv7_pmnc_read(void)
105 {
106 u32 val;
107 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
108 return val;
109 }
111 static inline u32 armv7_ccnt_read(void)
112 {
113 u32 val;
114 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
115 return val;
116 }
118 static inline u32 armv7_cntn_read(void)
119 {
120 u32 val;
121 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
122 return val;
123 }
125 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
126 {
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;
143 }
145 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
146 {
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;
163 }
165 static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
166 {
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;
183 }
185 static inline u32 armv7_pmnc_getreset_flags(void)
186 {
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;
197 }
199 static inline int armv7_pmnc_select_counter(unsigned int cnt)
200 {
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;
212 }
214 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
215 {
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 }
220 }
222 static void armv7_pmnc_reset_counter(unsigned int cnt)
223 {
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 }
245 }
247 static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
248 {
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;
271 }
273 static int gator_events_armv7_init(int *key)
274 {
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;
285 }
287 static void __gator_events_armv7_start(void* unused)
288 {
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);
340 }
342 static int gator_events_armv7_start(void)
343 {
344 if (!pmnc_count)
345 return 0;
346 return on_each_cpu(__gator_events_armv7_start, NULL, 1);
347 }
349 static void __gator_events_armv7_stop(void* unused)
350 {
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 }
358 }
360 static void gator_events_armv7_stop(void)
361 {
362 if (!pmnc_count)
363 return;
364 on_each_cpu(__gator_events_armv7_stop, NULL, 1);
365 }
367 static int gator_events_armv7_read(int **buffer)
368 {
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;
400 }
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;
427 }