1 /**
2 * Copyright (C) ARM Limited 2010-2012. 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 /* Disabling interrupts
10 * Many of the functions below disable interrupts via local_irq_save(). This disabling of interrupts is done to prevent any race conditions
11 * between multiple entities (e.g. hrtimer interrupts and event based interrupts) calling the same functions. As accessing the pmu involves
12 * several steps (disable, select, read, enable), these steps must be performed atomically. Normal synchronization routines cannot be used
13 * as these functions are being called from interrupt context.
14 */
16 #include "gator.h"
18 // gator_events_perf_pmu.c is used if perf is supported
19 #if GATOR_NO_PERF_SUPPORT
21 // Per-CPU PMNC: config reg
22 #define PMNC_E (1 << 0) /* Enable all counters */
23 #define PMNC_P (1 << 1) /* Reset all counters */
24 #define PMNC_C (1 << 2) /* Cycle counter reset */
25 #define PMNC_MASK 0x3f /* Mask for writable bits */
27 // ccnt reg
28 #define CCNT_REG (1 << 31)
30 #define CCNT 0
31 #define CNT0 1
32 #define CNTMAX (6+1)
34 static const char *pmnc_name;
35 static int pmnc_counters;
37 static unsigned long pmnc_enabled[CNTMAX];
38 static unsigned long pmnc_event[CNTMAX];
39 static unsigned long pmnc_key[CNTMAX];
41 static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
42 static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
44 inline void armv7_pmnc_write(u32 val)
45 {
46 val &= PMNC_MASK;
47 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
48 }
50 inline u32 armv7_pmnc_read(void)
51 {
52 u32 val;
53 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
54 return val;
55 }
57 inline u32 armv7_ccnt_read(u32 reset_value)
58 {
59 unsigned long flags;
60 u32 newval = -reset_value;
61 u32 den = CCNT_REG;
62 u32 val;
64 local_irq_save(flags);
65 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
66 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); // read
67 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval));// new value
68 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
69 local_irq_restore(flags);
71 return val;
72 }
74 inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value)
75 {
76 unsigned long flags;
77 u32 newval = -reset_value;
78 u32 sel = (cnt - CNT0);
79 u32 den = 1 << sel;
80 u32 oldval;
82 local_irq_save(flags);
83 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
84 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); // select
85 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval)); // read
86 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval));// new value
87 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
88 local_irq_restore(flags);
90 return oldval;
91 }
93 static inline void armv7_pmnc_disable_interrupt(unsigned int cnt)
94 {
95 u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31);
96 asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val));
97 }
99 inline u32 armv7_pmnc_reset_interrupt(void)
100 {
101 // Get and reset overflow status flags
102 u32 flags;
103 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags));
104 flags &= 0x8000003f;
105 asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags));
106 return flags;
107 }
109 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
110 {
111 u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
112 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
113 return cnt;
114 }
116 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
117 {
118 u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
119 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
120 return cnt;
121 }
123 static inline int armv7_pmnc_select_counter(unsigned int cnt)
124 {
125 u32 val = (cnt - CNT0);
126 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
127 return cnt;
128 }
130 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
131 {
132 if (armv7_pmnc_select_counter(cnt) == cnt) {
133 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
134 }
135 }
137 static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
138 {
139 struct dentry *dir;
140 int i;
142 for (i = 0; i < pmnc_counters; i++) {
143 char buf[40];
144 if (i == 0) {
145 snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
146 } else {
147 snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1);
148 }
149 dir = gatorfs_mkdir(sb, root, buf);
150 if (!dir) {
151 return -1;
152 }
153 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
154 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
155 if (i > 0) {
156 gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
157 }
158 }
160 return 0;
161 }
163 static int gator_events_armv7_online(int** buffer)
164 {
165 unsigned int cnt, len = 0, cpu = smp_processor_id();
167 if (armv7_pmnc_read() & PMNC_E) {
168 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
169 }
171 // Initialize & Reset PMNC: C bit and P bit
172 armv7_pmnc_write(PMNC_P | PMNC_C);
174 // Reset overflow flags
175 armv7_pmnc_reset_interrupt();
177 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
178 unsigned long event;
180 per_cpu(perfPrev, cpu)[cnt] = 0;
182 if (!pmnc_enabled[cnt])
183 continue;
185 // Disable counter
186 armv7_pmnc_disable_counter(cnt);
188 event = pmnc_event[cnt] & 255;
190 // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count
191 if (cnt != CCNT)
192 armv7_pmnc_write_evtsel(cnt, event);
194 armv7_pmnc_disable_interrupt(cnt);
196 // Reset counter
197 cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0);
199 // Enable counter
200 armv7_pmnc_enable_counter(cnt);
201 }
203 // enable
204 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
206 // return zero values, no need to read as the counters were just reset
207 for (cnt = 0; cnt < pmnc_counters; cnt++) {
208 if (pmnc_enabled[cnt]) {
209 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
210 per_cpu(perfCnt, cpu)[len++] = 0;
211 }
212 }
214 if (buffer)
215 *buffer = per_cpu(perfCnt, cpu);
217 return len;
218 }
220 static int gator_events_armv7_offline(int** buffer)
221 {
222 // disbale all counters, including PMCCNTR; overflow IRQs will not be signaled
223 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
225 return 0;
226 }
228 static void gator_events_armv7_stop(void)
229 {
230 unsigned int cnt;
232 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
233 pmnc_enabled[cnt] = 0;
234 pmnc_event[cnt] = 0;
235 }
236 }
238 static int gator_events_armv7_read(int **buffer)
239 {
240 int cnt, len = 0;
241 int cpu = smp_processor_id();
243 for (cnt = 0; cnt < pmnc_counters; cnt++) {
244 if (pmnc_enabled[cnt]) {
245 int value;
246 if (cnt == CCNT) {
247 value = armv7_ccnt_read(0);
248 } else {
249 value = armv7_cntn_read(cnt, 0);
250 }
251 if (value != per_cpu(perfPrev, cpu)[cnt]) {
252 per_cpu(perfPrev, cpu)[cnt] = value;
253 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
254 per_cpu(perfCnt, cpu)[len++] = value;
255 }
256 }
257 }
259 if (buffer)
260 *buffer = per_cpu(perfCnt, cpu);
262 return len;
263 }
265 static struct gator_interface gator_events_armv7_interface = {
266 .create_files = gator_events_armv7_create_files,
267 .stop = gator_events_armv7_stop,
268 .online = gator_events_armv7_online,
269 .offline = gator_events_armv7_offline,
270 .read = gator_events_armv7_read,
271 };
273 int gator_events_armv7_init(void)
274 {
275 unsigned int cnt;
277 switch (gator_cpuid()) {
278 case CORTEX_A5:
279 pmnc_name = "Cortex-A5";
280 pmnc_counters = 2;
281 break;
282 case CORTEX_A7:
283 pmnc_name = "Cortex-A7";
284 pmnc_counters = 4;
285 break;
286 case CORTEX_A8:
287 pmnc_name = "Cortex-A8";
288 pmnc_counters = 4;
289 break;
290 case CORTEX_A9:
291 pmnc_name = "Cortex-A9";
292 pmnc_counters = 6;
293 break;
294 case CORTEX_A15:
295 pmnc_name = "Cortex-A15";
296 pmnc_counters = 6;
297 break;
298 default:
299 return -1;
300 }
302 pmnc_counters++; // CNT[n] + CCNT
304 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
305 pmnc_enabled[cnt] = 0;
306 pmnc_event[cnt] = 0;
307 pmnc_key[cnt] = gator_events_get_key();
308 }
310 return gator_events_install(&gator_events_armv7_interface);
311 }
313 gator_events_init(gator_events_armv7_init);
315 #else
316 int gator_events_armv7_init(void)
317 {
318 return -1;
319 }
320 #endif