gator-driver: ARM DS-5.8 Streamline gator driver (RC1)
[ubuntu-omap:arm-gator.git] / driver / gator_events_armv7.c
1 /**
2  * Copyright (C) ARM Limited 2010-2011. 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
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  */
15
16 #include "gator.h"
17 #include "gator_events_armv7.h"
18
19 const char *pmnc_name;
20 int pmnc_counters;
21
22 unsigned long pmnc_enabled[CNTMAX];
23 unsigned long pmnc_event[CNTMAX];
24 unsigned long pmnc_count[CNTMAX];
25 unsigned long pmnc_key[CNTMAX];
26
27 static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
28 static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
29
30 inline void armv7_pmnc_write(u32 val)
31 {
32         val &= PMNC_MASK;
33         asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
34 }
35
36 inline u32 armv7_pmnc_read(void)
37 {
38         u32 val;
39         asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
40         return val;
41 }
42
43 inline u32 armv7_ccnt_read(u32 reset_value)
44 {
45         unsigned long flags;
46         u32 newval = -reset_value;
47         u32 den = CCNT_REG;
48         u32 val;
49
50         local_irq_save(flags);
51         asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den));       // disable
52         asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));        // read
53         asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval));// new value
54         asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den));       // enable
55         local_irq_restore(flags);
56
57         return val;
58 }
59
60 inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value)
61 {
62         unsigned long flags;
63         u32 newval = -reset_value;
64         u32 sel = (cnt - CNT0);
65         u32 den = 1 << sel;
66         u32 oldval;
67
68         local_irq_save(flags);
69         asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den));       // disable
70         asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel));       // select
71         asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval));     // read
72     asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval));// new value
73         asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den));       // enable
74         local_irq_restore(flags);
75
76         return oldval;
77 }
78
79 static inline void armv7_pmnc_enable_interrupt(unsigned int cnt)
80 {
81         u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31);
82         asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
83 }
84
85 static inline void armv7_pmnc_disable_interrupt(unsigned int cnt)
86 {
87         u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31);
88         asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val));
89 }
90
91 inline u32 armv7_pmnc_reset_interrupt()
92 {
93         // Get and reset overflow status flags
94         u32 flags;
95         asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags));
96         flags &= 0x8000003f;
97         asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags));
98         return flags;
99 }
100
101 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
102 {
103         u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
104         asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
105         return cnt;
106 }
107
108 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
109 {
110         u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
111         asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
112         return cnt;
113 }
114
115 static inline int armv7_pmnc_select_counter(unsigned int cnt)
116 {
117         u32 val = (cnt - CNT0);
118         asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
119         return cnt;
120 }
121
122 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
123 {
124         if (armv7_pmnc_select_counter(cnt) == cnt) {
125                 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
126         }
127 }
128
129 static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
130 {
131         struct dentry *dir;
132         int i;
133
134         for (i = 0; i < pmnc_counters; i++) {
135                 char buf[40];
136                 if (i == 0) {
137                         snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
138                 } else {
139                         snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1);
140                 }
141                 dir = gatorfs_mkdir(sb, root, buf);
142                 if (!dir) {
143                         return -1;
144                 }
145                 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
146                 gatorfs_create_ulong(sb, dir, "count", &pmnc_count[i]);
147                 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
148                 if (i > 0) {
149                         gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
150                 }
151         }
152
153         return 0;
154 }
155
156 static void gator_events_armv7_online(void)
157 {
158         unsigned int cnt;
159         int cpu = smp_processor_id();
160
161         if (armv7_pmnc_read() & PMNC_E) {
162                 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
163         }
164
165         // Initialize & Reset PMNC: C bit and P bit
166         armv7_pmnc_write(PMNC_P | PMNC_C);
167
168         // Reset overflow flags
169         armv7_pmnc_reset_interrupt();
170
171         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
172                 unsigned long event;
173
174                 per_cpu(perfPrev, cpu)[cnt] = 0;
175
176                 if (!pmnc_enabled[cnt])
177                         continue;
178
179                 // Disable counter
180                 armv7_pmnc_disable_counter(cnt);
181
182                 event = pmnc_event[cnt] & 255;
183
184                 // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count
185                 if (cnt != CCNT)
186                         armv7_pmnc_write_evtsel(cnt, event);
187
188                 // Enable/disable interrupt
189                 if (pmnc_count[cnt] > 0)
190                         armv7_pmnc_enable_interrupt(cnt);
191                 else
192                         armv7_pmnc_disable_interrupt(cnt);
193
194                 // Reset counter
195                 cnt ? armv7_cntn_read(cnt, pmnc_count[cnt]) : armv7_ccnt_read(pmnc_count[cnt]);
196
197                 // Enable counter
198                 armv7_pmnc_enable_counter(cnt);
199         }
200
201         // enable
202         armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
203 }
204
205 static void gator_events_armv7_offline(void)
206 {
207         // disbale all counters, including PMCCNTR; overflow IRQs will not be signaled
208         armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
209 }
210
211 static void gator_events_armv7_stop(void)
212 {
213         unsigned int cnt;
214
215         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
216                 pmnc_enabled[cnt] = 0;
217                 pmnc_event[cnt] = 0;
218                 pmnc_count[cnt] = 0;
219         }
220 }
221
222 static int gator_events_armv7_read(int **buffer)
223 {
224         int cnt, len = 0;
225         int cpu = smp_processor_id();
226
227         if (!pmnc_counters)
228                 return 0;
229
230         for (cnt = 0; cnt < pmnc_counters; cnt++) {
231                 if (pmnc_enabled[cnt] && pmnc_count[cnt] == 0) {
232                         int value;
233                         if (cnt == CCNT) {
234                                 value = armv7_ccnt_read(0);
235                         } else {
236                                 value = armv7_cntn_read(cnt, 0);
237                         }
238                         if (value != per_cpu(perfPrev, cpu)[cnt]) {
239                                 per_cpu(perfPrev, cpu)[cnt] = value;
240                                 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
241                                 per_cpu(perfCnt, cpu)[len++] = value;
242                         }
243                 }
244         }
245
246         // update or discard
247         if (buffer)
248                 *buffer = per_cpu(perfCnt, cpu);
249
250         return len;
251 }
252
253 static struct gator_interface gator_events_armv7_interface = {
254         .create_files = gator_events_armv7_create_files,
255         .stop = gator_events_armv7_stop,
256         .online = gator_events_armv7_online,
257         .offline = gator_events_armv7_offline,
258         .read = gator_events_armv7_read,
259 };
260
261 int gator_events_armv7_init(void)
262 {
263         unsigned int cnt;
264
265         switch (gator_cpuid()) {
266         case CORTEX_A5:
267                 pmnc_name = "Cortex-A5";
268                 pmnc_counters = 2;
269                 break;
270         case CORTEX_A8:
271                 pmnc_name = "Cortex-A8";
272                 pmnc_counters = 4;
273                 break;
274         case CORTEX_A9:
275                 pmnc_name = "Cortex-A9";
276                 pmnc_counters = 6;
277                 break;
278         case CORTEX_A15:
279                 pmnc_name = "Cortex-A15";
280                 pmnc_counters = 6;
281                 break;
282         default:
283                 return -1;
284         }
285
286         pmnc_counters++; // CNT[n] + CCNT
287
288         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
289                 pmnc_enabled[cnt] = 0;
290                 pmnc_event[cnt] = 0;
291                 pmnc_count[cnt] = 0;
292                 pmnc_key[cnt] = gator_events_get_key();
293         }
294
295         return gator_events_install(&gator_events_armv7_interface);
296 }
297 gator_events_init(gator_events_armv7_init);