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 * 2], perfCnt);
43 inline void armv7_pmnc_write(u32 val)
44 {
45 val &= PMNC_MASK;
46 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
47 }
49 inline u32 armv7_pmnc_read(void)
50 {
51 u32 val;
52 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
53 return val;
54 }
56 inline u32 armv7_ccnt_read(u32 reset_value)
57 {
58 unsigned long flags;
59 u32 newval = -reset_value;
60 u32 den = CCNT_REG;
61 u32 val;
63 local_irq_save(flags);
64 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
65 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); // read
66 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval));// new value
67 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
68 local_irq_restore(flags);
70 return val;
71 }
73 inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value)
74 {
75 unsigned long flags;
76 u32 newval = -reset_value;
77 u32 sel = (cnt - CNT0);
78 u32 den = 1 << sel;
79 u32 oldval;
81 local_irq_save(flags);
82 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
83 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); // select
84 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval)); // read
85 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval));// new value
86 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
87 local_irq_restore(flags);
89 return oldval;
90 }
92 static inline void armv7_pmnc_disable_interrupt(unsigned int cnt)
93 {
94 u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31);
95 asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val));
96 }
98 inline u32 armv7_pmnc_reset_interrupt(void)
99 {
100 // Get and reset overflow status flags
101 u32 flags;
102 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags));
103 flags &= 0x8000003f;
104 asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags));
105 return flags;
106 }
108 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
109 {
110 u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
111 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
112 return cnt;
113 }
115 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
116 {
117 u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
118 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
119 return cnt;
120 }
122 static inline int armv7_pmnc_select_counter(unsigned int cnt)
123 {
124 u32 val = (cnt - CNT0);
125 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
126 return cnt;
127 }
129 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
130 {
131 if (armv7_pmnc_select_counter(cnt) == cnt) {
132 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
133 }
134 }
136 static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
137 {
138 struct dentry *dir;
139 int i;
141 for (i = 0; i < pmnc_counters; i++) {
142 char buf[40];
143 if (i == 0) {
144 snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
145 } else {
146 snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1);
147 }
148 dir = gatorfs_mkdir(sb, root, buf);
149 if (!dir) {
150 return -1;
151 }
152 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
153 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
154 if (i > 0) {
155 gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
156 }
157 }
159 return 0;
160 }
162 static int gator_events_armv7_online(int** buffer)
163 {
164 unsigned int cnt, len = 0, cpu = smp_processor_id();
166 if (armv7_pmnc_read() & PMNC_E) {
167 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
168 }
170 // Initialize & Reset PMNC: C bit and P bit
171 armv7_pmnc_write(PMNC_P | PMNC_C);
173 // Reset overflow flags
174 armv7_pmnc_reset_interrupt();
176 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
177 unsigned long event;
179 if (!pmnc_enabled[cnt])
180 continue;
182 // Disable counter
183 armv7_pmnc_disable_counter(cnt);
185 event = pmnc_event[cnt] & 255;
187 // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count
188 if (cnt != CCNT)
189 armv7_pmnc_write_evtsel(cnt, event);
191 armv7_pmnc_disable_interrupt(cnt);
193 // Reset counter
194 cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0);
196 // Enable counter
197 armv7_pmnc_enable_counter(cnt);
198 }
200 // enable
201 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
203 // return zero values, no need to read as the counters were just reset
204 for (cnt = 0; cnt < pmnc_counters; cnt++) {
205 if (pmnc_enabled[cnt]) {
206 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
207 per_cpu(perfCnt, cpu)[len++] = 0;
208 }
209 }
211 if (buffer)
212 *buffer = per_cpu(perfCnt, cpu);
214 return len;
215 }
217 static int gator_events_armv7_offline(int** buffer)
218 {
219 // disbale all counters, including PMCCNTR; overflow IRQs will not be signaled
220 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
222 return 0;
223 }
225 static void gator_events_armv7_stop(void)
226 {
227 unsigned int cnt;
229 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
230 pmnc_enabled[cnt] = 0;
231 pmnc_event[cnt] = 0;
232 }
233 }
235 static int gator_events_armv7_read(int **buffer)
236 {
237 int cnt, len = 0;
238 int cpu = smp_processor_id();
240 // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled
241 if (!(armv7_pmnc_read() & PMNC_E)) {
242 return 0;
243 }
245 for (cnt = 0; cnt < pmnc_counters; cnt++) {
246 if (pmnc_enabled[cnt]) {
247 int value;
248 if (cnt == CCNT) {
249 value = armv7_ccnt_read(0);
250 } else {
251 value = armv7_cntn_read(cnt, 0);
252 }
253 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
254 per_cpu(perfCnt, cpu)[len++] = value;
255 }
256 }
258 if (buffer)
259 *buffer = per_cpu(perfCnt, cpu);
261 return len;
262 }
264 static struct gator_interface gator_events_armv7_interface = {
265 .create_files = gator_events_armv7_create_files,
266 .stop = gator_events_armv7_stop,
267 .online = gator_events_armv7_online,
268 .offline = gator_events_armv7_offline,
269 .read = gator_events_armv7_read,
270 };
272 int gator_events_armv7_init(void)
273 {
274 unsigned int cnt;
276 switch (gator_cpuid()) {
277 case CORTEX_A5:
278 pmnc_name = "Cortex-A5";
279 pmnc_counters = 2;
280 break;
281 case CORTEX_A7:
282 pmnc_name = "Cortex-A7";
283 pmnc_counters = 4;
284 break;
285 case CORTEX_A8:
286 pmnc_name = "Cortex-A8";
287 pmnc_counters = 4;
288 break;
289 case CORTEX_A9:
290 pmnc_name = "Cortex-A9";
291 pmnc_counters = 6;
292 break;
293 case CORTEX_A15:
294 pmnc_name = "Cortex-A15";
295 pmnc_counters = 6;
296 break;
297 default:
298 return -1;
299 }
301 pmnc_counters++; // CNT[n] + CCNT
303 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
304 pmnc_enabled[cnt] = 0;
305 pmnc_event[cnt] = 0;
306 pmnc_key[cnt] = gator_events_get_key();
307 }
309 return gator_events_install(&gator_events_armv7_interface);
310 }
312 gator_events_init(gator_events_armv7_init);
314 #else
315 int gator_events_armv7_init(void)
316 {
317 return -1;
318 }
319 #endif