2 * AVR32 Performance Counter Driver
4 * Copyright (C) 2005-2007 Atmel Corporation
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * Author: Ronny Pedersen
12 #include <linux/errno.h>
13 #include <linux/interrupt.h>
14 #include <linux/irq.h>
15 #include <linux/oprofile.h>
16 #include <linux/sched.h>
17 #include <linux/types.h>
19 #include <asm/sysreg.h>
20 #include <asm/system.h>
22 #define AVR32_PERFCTR_IRQ_GROUP 0
23 #define AVR32_PERFCTR_IRQ_LINE 1
25 void avr32_backtrace(struct pt_regs
* const regs
, unsigned int depth
);
27 enum { PCCNT
, PCNT0
, PCNT1
, NR_counter
};
29 struct avr32_perf_counter
{
30 unsigned long enabled
;
33 unsigned long unit_mask
;
41 static struct avr32_perf_counter counter
[NR_counter
] = {
43 .ie_mask
= SYSREG_BIT(IEC
),
44 .flag_mask
= SYSREG_BIT(FC
),
46 .ie_mask
= SYSREG_BIT(IE0
),
47 .flag_mask
= SYSREG_BIT(F0
),
49 .ie_mask
= SYSREG_BIT(IE1
),
50 .flag_mask
= SYSREG_BIT(F1
),
54 static void avr32_perf_counter_reset(void)
56 /* Reset all counter and disable/clear all interrupts */
57 sysreg_write(PCCR
, (SYSREG_BIT(PCCR_R
)
64 static irqreturn_t
avr32_perf_counter_interrupt(int irq
, void *dev_id
)
66 struct avr32_perf_counter
*ctr
= dev_id
;
70 if (likely(!(intc_get_pending(AVR32_PERFCTR_IRQ_GROUP
)
71 & (1 << AVR32_PERFCTR_IRQ_LINE
))))
74 regs
= get_irq_regs();
75 pccr
= sysreg_read(PCCR
);
77 /* Clear the interrupt flags we're about to handle */
78 sysreg_write(PCCR
, pccr
);
81 if (ctr
->enabled
&& (pccr
& ctr
->flag_mask
)) {
82 sysreg_write(PCCNT
, -ctr
->count
);
83 oprofile_add_sample(regs
, PCCNT
);
87 if (ctr
->enabled
&& (pccr
& ctr
->flag_mask
)) {
88 sysreg_write(PCNT0
, -ctr
->count
);
89 oprofile_add_sample(regs
, PCNT0
);
93 if (ctr
->enabled
&& (pccr
& ctr
->flag_mask
)) {
94 sysreg_write(PCNT1
, -ctr
->count
);
95 oprofile_add_sample(regs
, PCNT1
);
101 static int avr32_perf_counter_create_files(struct super_block
*sb
,
108 for (i
= 0; i
< NR_counter
; i
++) {
109 snprintf(filename
, sizeof(filename
), "%u", i
);
110 dir
= oprofilefs_mkdir(sb
, root
, filename
);
112 oprofilefs_create_ulong(sb
, dir
, "enabled",
113 &counter
[i
].enabled
);
114 oprofilefs_create_ulong(sb
, dir
, "event",
116 oprofilefs_create_ulong(sb
, dir
, "count",
120 oprofilefs_create_ulong(sb
, dir
, "kernel",
122 oprofilefs_create_ulong(sb
, dir
, "user",
124 oprofilefs_create_ulong(sb
, dir
, "unit_mask",
125 &counter
[i
].unit_mask
);
131 static int avr32_perf_counter_setup(void)
133 struct avr32_perf_counter
*ctr
;
138 pr_debug("avr32_perf_counter_setup\n");
140 if (sysreg_read(PCCR
) & SYSREG_BIT(PCCR_E
)) {
142 "oprofile: setup: perf counter already enabled\n");
146 ret
= request_irq(AVR32_PERFCTR_IRQ_GROUP
,
147 avr32_perf_counter_interrupt
, IRQF_SHARED
,
148 "oprofile", counter
);
152 avr32_perf_counter_reset();
155 for (i
= PCCNT
; i
< NR_counter
; i
++) {
160 pr_debug("enabling counter %d...\n", i
);
162 pccr
|= ctr
->ie_mask
;
166 /* PCCNT always counts cycles, so no events */
167 sysreg_write(PCCNT
, -ctr
->count
);
170 pccr
|= SYSREG_BF(CONF0
, ctr
->event
);
171 sysreg_write(PCNT0
, -ctr
->count
);
174 pccr
|= SYSREG_BF(CONF1
, ctr
->event
);
175 sysreg_write(PCNT1
, -ctr
->count
);
180 pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr
);
182 sysreg_write(PCCR
, pccr
);
187 static void avr32_perf_counter_shutdown(void)
189 pr_debug("avr32_perf_counter_shutdown\n");
191 avr32_perf_counter_reset();
192 free_irq(AVR32_PERFCTR_IRQ_GROUP
, counter
);
195 static int avr32_perf_counter_start(void)
197 pr_debug("avr32_perf_counter_start\n");
199 sysreg_write(PCCR
, sysreg_read(PCCR
) | SYSREG_BIT(PCCR_E
));
204 static void avr32_perf_counter_stop(void)
206 pr_debug("avr32_perf_counter_stop\n");
208 sysreg_write(PCCR
, sysreg_read(PCCR
) & ~SYSREG_BIT(PCCR_E
));
211 static struct oprofile_operations avr32_perf_counter_ops __initdata
= {
212 .create_files
= avr32_perf_counter_create_files
,
213 .setup
= avr32_perf_counter_setup
,
214 .shutdown
= avr32_perf_counter_shutdown
,
215 .start
= avr32_perf_counter_start
,
216 .stop
= avr32_perf_counter_stop
,
220 int __init
oprofile_arch_init(struct oprofile_operations
*ops
)
222 if (!(current_cpu_data
.features
& AVR32_FEATURE_PCTR
))
225 memcpy(ops
, &avr32_perf_counter_ops
,
226 sizeof(struct oprofile_operations
));
228 ops
->backtrace
= avr32_backtrace
;
230 printk(KERN_INFO
"oprofile: using AVR32 performance monitoring.\n");
235 void oprofile_arch_exit(void)