• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * op_model_v7.c
3  * ARM V7 (Cortex A8) Event Monitor Driver
4  *
5  * Copyright 2008 Jean Pihet <jpihet@mvista.com>
6  * Copyright 2004 ARM SMP Development Team
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12 #include <linux/types.h>
13 #include <linux/errno.h>
14 #include <linux/oprofile.h>
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
17 #include <linux/smp.h>
18 
19 #include "op_counter.h"
20 #include "op_arm_model.h"
21 #include "op_model_v7.h"
22 
23 /* #define DEBUG */
24 
25 
26 /*
27  * ARM V7 PMNC support
28  */
29 
30 static u32 cnt_en[CNTMAX];
31 
armv7_pmnc_write(u32 val)32 static inline void armv7_pmnc_write(u32 val)
33 {
34 	val &= PMNC_MASK;
35 	asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
36 }
37 
armv7_pmnc_read(void)38 static inline u32 armv7_pmnc_read(void)
39 {
40 	u32 val;
41 
42 	asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
43 	return val;
44 }
45 
armv7_pmnc_enable_counter(unsigned int cnt)46 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
47 {
48 	u32 val;
49 
50 	if (cnt >= CNTMAX) {
51 		printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
52 			" %d\n", smp_processor_id(), cnt);
53 		return -1;
54 	}
55 
56 	if (cnt == CCNT)
57 		val = CNTENS_C;
58 	else
59 		val = (1 << (cnt - CNT0));
60 
61 	val &= CNTENS_MASK;
62 	asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
63 
64 	return cnt;
65 }
66 
armv7_pmnc_disable_counter(unsigned int cnt)67 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
68 {
69 	u32 val;
70 
71 	if (cnt >= CNTMAX) {
72 		printk(KERN_ERR "oprofile: CPU%u disabling wrong PMNC counter"
73 			" %d\n", smp_processor_id(), cnt);
74 		return -1;
75 	}
76 
77 	if (cnt == CCNT)
78 		val = CNTENC_C;
79 	else
80 		val = (1 << (cnt - CNT0));
81 
82 	val &= CNTENC_MASK;
83 	asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
84 
85 	return cnt;
86 }
87 
armv7_pmnc_enable_intens(unsigned int cnt)88 static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
89 {
90 	u32 val;
91 
92 	if (cnt >= CNTMAX) {
93 		printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
94 			" interrupt enable %d\n", smp_processor_id(), cnt);
95 		return -1;
96 	}
97 
98 	if (cnt == CCNT)
99 		val = INTENS_C;
100 	else
101 		val = (1 << (cnt - CNT0));
102 
103 	val &= INTENS_MASK;
104 	asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
105 
106 	return cnt;
107 }
108 
armv7_pmnc_getreset_flags(void)109 static inline u32 armv7_pmnc_getreset_flags(void)
110 {
111 	u32 val;
112 
113 	/* Read */
114 	asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
115 
116 	/* Write to clear flags */
117 	val &= FLAG_MASK;
118 	asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
119 
120 	return val;
121 }
122 
armv7_pmnc_select_counter(unsigned int cnt)123 static inline int armv7_pmnc_select_counter(unsigned int cnt)
124 {
125 	u32 val;
126 
127 	if ((cnt == CCNT) || (cnt >= CNTMAX)) {
128 		printk(KERN_ERR "oprofile: CPU%u selecting wrong PMNC counteri"
129 			" %d\n", smp_processor_id(), cnt);
130 		return -1;
131 	}
132 
133 	val = (cnt - CNT0) & SELECT_MASK;
134 	asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
135 
136 	return cnt;
137 }
138 
armv7_pmnc_write_evtsel(unsigned int cnt,u32 val)139 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
140 {
141 	if (armv7_pmnc_select_counter(cnt) == cnt) {
142 		val &= EVTSEL_MASK;
143 		asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
144 	}
145 }
146 
armv7_pmnc_reset_counter(unsigned int cnt)147 static void armv7_pmnc_reset_counter(unsigned int cnt)
148 {
149 	u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
150 	u32 val = -(u32)counter_config[cpu_cnt].count;
151 
152 	switch (cnt) {
153 	case CCNT:
154 		armv7_pmnc_disable_counter(cnt);
155 
156 		asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
157 
158 		if (cnt_en[cnt] != 0)
159 		    armv7_pmnc_enable_counter(cnt);
160 
161 		break;
162 
163 	case CNT0:
164 	case CNT1:
165 	case CNT2:
166 	case CNT3:
167 		armv7_pmnc_disable_counter(cnt);
168 
169 		if (armv7_pmnc_select_counter(cnt) == cnt)
170 		    asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
171 
172 		if (cnt_en[cnt] != 0)
173 		    armv7_pmnc_enable_counter(cnt);
174 
175 		break;
176 
177 	default:
178 		printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter"
179 			" %d\n", smp_processor_id(), cnt);
180 		break;
181 	}
182 }
183 
armv7_setup_pmnc(void)184 int armv7_setup_pmnc(void)
185 {
186 	unsigned int cnt;
187 
188 	if (armv7_pmnc_read() & PMNC_E) {
189 		printk(KERN_ERR "oprofile: CPU%u PMNC still enabled when setup"
190 			" new event counter.\n", smp_processor_id());
191 		return -EBUSY;
192 	}
193 
194 	/*
195 	 * Initialize & Reset PMNC: C bit, D bit and P bit.
196 	 *  Note: Using a slower count for CCNT (D bit: divide by 64) results
197 	 *   in a more stable system
198 	 */
199 	armv7_pmnc_write(PMNC_P | PMNC_C | PMNC_D);
200 
201 
202 	for (cnt = CCNT; cnt < CNTMAX; cnt++) {
203 		unsigned long event;
204 		u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
205 
206 		/*
207 		 * Disable counter
208 		 */
209 		armv7_pmnc_disable_counter(cnt);
210 		cnt_en[cnt] = 0;
211 
212 		if (!counter_config[cpu_cnt].enabled)
213 			continue;
214 
215 		event = counter_config[cpu_cnt].event & 255;
216 
217 		/*
218 		 * Set event (if destined for PMNx counters)
219 		 * We don't need to set the event if it's a cycle count
220 		 */
221 		if (cnt != CCNT)
222 			armv7_pmnc_write_evtsel(cnt, event);
223 
224 		/*
225 		 * Enable interrupt for this counter
226 		 */
227 		armv7_pmnc_enable_intens(cnt);
228 
229 		/*
230 		 * Reset counter
231 		 */
232 		armv7_pmnc_reset_counter(cnt);
233 
234 		/*
235 		 * Enable counter
236 		 */
237 		armv7_pmnc_enable_counter(cnt);
238 		cnt_en[cnt] = 1;
239 	}
240 
241 	return 0;
242 }
243 
armv7_start_pmnc(void)244 static inline void armv7_start_pmnc(void)
245 {
246 	armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
247 }
248 
armv7_stop_pmnc(void)249 static inline void armv7_stop_pmnc(void)
250 {
251 	armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
252 }
253 
254 /*
255  * CPU counters' IRQ handler (one IRQ per CPU)
256  */
armv7_pmnc_interrupt(int irq,void * arg)257 static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
258 {
259 	struct pt_regs *regs = get_irq_regs();
260 	unsigned int cnt;
261 	u32 flags;
262 
263 
264 	/*
265 	 * Stop IRQ generation
266 	 */
267 	armv7_stop_pmnc();
268 
269 	/*
270 	 * Get and reset overflow status flags
271 	 */
272 	flags = armv7_pmnc_getreset_flags();
273 
274 	/*
275 	 * Cycle counter
276 	 */
277 	if (flags & FLAG_C) {
278 		u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), CCNT);
279 		armv7_pmnc_reset_counter(CCNT);
280 		oprofile_add_sample(regs, cpu_cnt);
281 	}
282 
283 	/*
284 	 * PMNC counters 0:3
285 	 */
286 	for (cnt = CNT0; cnt < CNTMAX; cnt++) {
287 		if (flags & (1 << (cnt - CNT0))) {
288 			u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
289 			armv7_pmnc_reset_counter(cnt);
290 			oprofile_add_sample(regs, cpu_cnt);
291 		}
292 	}
293 
294 	/*
295 	 * Allow IRQ generation
296 	 */
297 	armv7_start_pmnc();
298 
299 	return IRQ_HANDLED;
300 }
301 
armv7_request_interrupts(int * irqs,int nr)302 int armv7_request_interrupts(int *irqs, int nr)
303 {
304 	unsigned int i;
305 	int ret = 0;
306 
307 	for (i = 0; i < nr; i++) {
308 		ret = request_irq(irqs[i], armv7_pmnc_interrupt,
309 				IRQF_DISABLED, "CP15 PMNC", NULL);
310 		if (ret != 0) {
311 			printk(KERN_ERR "oprofile: unable to request IRQ%u"
312 				" for ARMv7\n",
313 			       irqs[i]);
314 			break;
315 		}
316 	}
317 
318 	if (i != nr)
319 		while (i-- != 0)
320 			free_irq(irqs[i], NULL);
321 
322 	return ret;
323 }
324 
armv7_release_interrupts(int * irqs,int nr)325 void armv7_release_interrupts(int *irqs, int nr)
326 {
327 	unsigned int i;
328 
329 	for (i = 0; i < nr; i++)
330 		free_irq(irqs[i], NULL);
331 }
332 
333 #ifdef DEBUG
armv7_pmnc_dump_regs(void)334 static void armv7_pmnc_dump_regs(void)
335 {
336 	u32 val;
337 	unsigned int cnt;
338 
339 	printk(KERN_INFO "PMNC registers dump:\n");
340 
341 	asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
342 	printk(KERN_INFO "PMNC  =0x%08x\n", val);
343 
344 	asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
345 	printk(KERN_INFO "CNTENS=0x%08x\n", val);
346 
347 	asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
348 	printk(KERN_INFO "INTENS=0x%08x\n", val);
349 
350 	asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
351 	printk(KERN_INFO "FLAGS =0x%08x\n", val);
352 
353 	asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
354 	printk(KERN_INFO "SELECT=0x%08x\n", val);
355 
356 	asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
357 	printk(KERN_INFO "CCNT  =0x%08x\n", val);
358 
359 	for (cnt = CNT0; cnt < CNTMAX; cnt++) {
360 		armv7_pmnc_select_counter(cnt);
361 		asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
362 		printk(KERN_INFO "CNT[%d] count =0x%08x\n", cnt-CNT0, val);
363 		asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
364 		printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", cnt-CNT0, val);
365 	}
366 }
367 #endif
368 
369 
370 static int irqs[] = {
371 #ifdef CONFIG_ARCH_OMAP3
372 	INT_34XX_BENCH_MPU_EMUL,
373 #endif
374 };
375 
armv7_pmnc_stop(void)376 static void armv7_pmnc_stop(void)
377 {
378 #ifdef DEBUG
379 	armv7_pmnc_dump_regs();
380 #endif
381 	armv7_stop_pmnc();
382 	armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
383 }
384 
armv7_pmnc_start(void)385 static int armv7_pmnc_start(void)
386 {
387 	int ret;
388 
389 #ifdef DEBUG
390 	armv7_pmnc_dump_regs();
391 #endif
392 	ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs));
393 	if (ret >= 0)
394 		armv7_start_pmnc();
395 
396 	return ret;
397 }
398 
armv7_detect_pmnc(void)399 static int armv7_detect_pmnc(void)
400 {
401 	return 0;
402 }
403 
404 struct op_arm_model_spec op_armv7_spec = {
405 	.init		= armv7_detect_pmnc,
406 	.num_counters	= 5,
407 	.setup_ctrs	= armv7_setup_pmnc,
408 	.start		= armv7_pmnc_start,
409 	.stop		= armv7_pmnc_stop,
410 	.name		= "arm/armv7",
411 };
412