30881c8fd3fd94443d9e9781ab5c218b33939e09
[firefly-linux-kernel-4.4.55.git] / drivers / gator / gator_events_armv7.c
1 /**
2  * Copyright (C) ARM Limited 2010-2013. 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
18 // gator_events_perf_pmu.c is used if perf is supported
19 #if GATOR_NO_PERF_SUPPORT
20
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 */
26
27 // ccnt reg
28 #define CCNT_REG        (1 << 31)
29
30 #define CCNT            0
31 #define CNT0            1
32 #define CNTMAX          (6+1)
33
34 static const char *pmnc_name;
35 static int pmnc_counters;
36
37 static unsigned long pmnc_enabled[CNTMAX];
38 static unsigned long pmnc_event[CNTMAX];
39 static unsigned long pmnc_key[CNTMAX];
40
41 static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
42
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 }
48
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 }
55
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;
62
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);
69
70         return val;
71 }
72
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;
80
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);
88
89         return oldval;
90 }
91
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 }
97
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 }
107
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 }
114
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 }
121
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 }
128
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 }
135
136 static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
137 {
138         struct dentry *dir;
139         int i;
140
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         }
158
159         return 0;
160 }
161
162 static int gator_events_armv7_online(int **buffer, bool migrate)
163 {
164         unsigned int cnt, len = 0, cpu = smp_processor_id();
165
166         if (armv7_pmnc_read() & PMNC_E) {
167                 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
168         }
169
170         // Initialize & Reset PMNC: C bit and P bit
171         armv7_pmnc_write(PMNC_P | PMNC_C);
172
173         // Reset overflow flags
174         armv7_pmnc_reset_interrupt();
175
176         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
177                 unsigned long event;
178
179                 if (!pmnc_enabled[cnt])
180                         continue;
181
182                 // Disable counter
183                 armv7_pmnc_disable_counter(cnt);
184
185                 event = pmnc_event[cnt] & 255;
186
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);
190
191                 armv7_pmnc_disable_interrupt(cnt);
192
193                 // Reset counter
194                 cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0);
195
196                 // Enable counter
197                 armv7_pmnc_enable_counter(cnt);
198         }
199
200         // enable
201         armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
202
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         }
210
211         if (buffer)
212                 *buffer = per_cpu(perfCnt, cpu);
213
214         return len;
215 }
216
217 static int gator_events_armv7_offline(int **buffer, bool migrate)
218 {
219         // disable all counters, including PMCCNTR; overflow IRQs will not be signaled
220         armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
221
222         return 0;
223 }
224
225 static void gator_events_armv7_stop(void)
226 {
227         unsigned int cnt;
228
229         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
230                 pmnc_enabled[cnt] = 0;
231                 pmnc_event[cnt] = 0;
232         }
233 }
234
235 static int gator_events_armv7_read(int **buffer)
236 {
237         int cnt, len = 0;
238         int cpu = smp_processor_id();
239
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         }
244
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         }
257
258         if (buffer)
259                 *buffer = per_cpu(perfCnt, cpu);
260
261         return len;
262 }
263
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 };
271
272 int gator_events_armv7_init(void)
273 {
274         unsigned int cnt;
275
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         }
300
301         pmnc_counters++;        // CNT[n] + CCNT
302
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         }
308
309         return gator_events_install(&gator_events_armv7_interface);
310 }
311
312 #endif