• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * AVR32 Performance Counter Driver
3  *
4  * Copyright (C) 2005-2007 Atmel Corporation
5  *
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.
9  *
10  * Author: Ronny Pedersen
11  */
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>
18 
19 #include <asm/sysreg.h>
20 
21 #define AVR32_PERFCTR_IRQ_GROUP	0
22 #define AVR32_PERFCTR_IRQ_LINE	1
23 
24 void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
25 
26 enum { PCCNT, PCNT0, PCNT1, NR_counter };
27 
28 struct avr32_perf_counter {
29 	unsigned long	enabled;
30 	unsigned long	event;
31 	unsigned long	count;
32 	unsigned long	unit_mask;
33 	unsigned long	kernel;
34 	unsigned long	user;
35 
36 	u32		ie_mask;
37 	u32		flag_mask;
38 };
39 
40 static struct avr32_perf_counter counter[NR_counter] = {
41 	{
42 		.ie_mask	= SYSREG_BIT(IEC),
43 		.flag_mask	= SYSREG_BIT(FC),
44 	}, {
45 		.ie_mask	= SYSREG_BIT(IE0),
46 		.flag_mask	= SYSREG_BIT(F0),
47 	}, {
48 		.ie_mask	= SYSREG_BIT(IE1),
49 		.flag_mask	= SYSREG_BIT(F1),
50 	},
51 };
52 
avr32_perf_counter_reset(void)53 static void avr32_perf_counter_reset(void)
54 {
55 	/* Reset all counter and disable/clear all interrupts */
56 	sysreg_write(PCCR, (SYSREG_BIT(PCCR_R)
57 				| SYSREG_BIT(PCCR_C)
58 				| SYSREG_BIT(FC)
59 				| SYSREG_BIT(F0)
60 				| SYSREG_BIT(F1)));
61 }
62 
avr32_perf_counter_interrupt(int irq,void * dev_id)63 static irqreturn_t avr32_perf_counter_interrupt(int irq, void *dev_id)
64 {
65 	struct avr32_perf_counter *ctr = dev_id;
66 	struct pt_regs *regs;
67 	u32 pccr;
68 
69 	if (likely(!(intc_get_pending(AVR32_PERFCTR_IRQ_GROUP)
70 					& (1 << AVR32_PERFCTR_IRQ_LINE))))
71 		return IRQ_NONE;
72 
73 	regs = get_irq_regs();
74 	pccr = sysreg_read(PCCR);
75 
76 	/* Clear the interrupt flags we're about to handle */
77 	sysreg_write(PCCR, pccr);
78 
79 	/* PCCNT */
80 	if (ctr->enabled && (pccr & ctr->flag_mask)) {
81 		sysreg_write(PCCNT, -ctr->count);
82 		oprofile_add_sample(regs, PCCNT);
83 	}
84 	ctr++;
85 	/* PCNT0 */
86 	if (ctr->enabled && (pccr & ctr->flag_mask)) {
87 		sysreg_write(PCNT0, -ctr->count);
88 		oprofile_add_sample(regs, PCNT0);
89 	}
90 	ctr++;
91 	/* PCNT1 */
92 	if (ctr->enabled && (pccr & ctr->flag_mask)) {
93 		sysreg_write(PCNT1, -ctr->count);
94 		oprofile_add_sample(regs, PCNT1);
95 	}
96 
97 	return IRQ_HANDLED;
98 }
99 
avr32_perf_counter_create_files(struct dentry * root)100 static int avr32_perf_counter_create_files(struct dentry *root)
101 {
102 	struct dentry *dir;
103 	unsigned int i;
104 	char filename[4];
105 
106 	for (i = 0; i < NR_counter; i++) {
107 		snprintf(filename, sizeof(filename), "%u", i);
108 		dir = oprofilefs_mkdir(root, filename);
109 
110 		oprofilefs_create_ulong(dir, "enabled",
111 				&counter[i].enabled);
112 		oprofilefs_create_ulong(dir, "event",
113 				&counter[i].event);
114 		oprofilefs_create_ulong(dir, "count",
115 				&counter[i].count);
116 
117 		/* Dummy entries */
118 		oprofilefs_create_ulong(dir, "kernel",
119 				&counter[i].kernel);
120 		oprofilefs_create_ulong(dir, "user",
121 				&counter[i].user);
122 		oprofilefs_create_ulong(dir, "unit_mask",
123 				&counter[i].unit_mask);
124 	}
125 
126 	return 0;
127 }
128 
avr32_perf_counter_setup(void)129 static int avr32_perf_counter_setup(void)
130 {
131 	struct avr32_perf_counter *ctr;
132 	u32 pccr;
133 	int ret;
134 	int i;
135 
136 	pr_debug("avr32_perf_counter_setup\n");
137 
138 	if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {
139 		printk(KERN_ERR
140 			"oprofile: setup: perf counter already enabled\n");
141 		return -EBUSY;
142 	}
143 
144 	ret = request_irq(AVR32_PERFCTR_IRQ_GROUP,
145 			avr32_perf_counter_interrupt, IRQF_SHARED,
146 			"oprofile", counter);
147 	if (ret)
148 		return ret;
149 
150 	avr32_perf_counter_reset();
151 
152 	pccr = 0;
153 	for (i = PCCNT; i < NR_counter; i++) {
154 		ctr = &counter[i];
155 		if (!ctr->enabled)
156 			continue;
157 
158 		pr_debug("enabling counter %d...\n", i);
159 
160 		pccr |= ctr->ie_mask;
161 
162 		switch (i) {
163 		case PCCNT:
164 			/* PCCNT always counts cycles, so no events */
165 			sysreg_write(PCCNT, -ctr->count);
166 			break;
167 		case PCNT0:
168 			pccr |= SYSREG_BF(CONF0, ctr->event);
169 			sysreg_write(PCNT0, -ctr->count);
170 			break;
171 		case PCNT1:
172 			pccr |= SYSREG_BF(CONF1, ctr->event);
173 			sysreg_write(PCNT1, -ctr->count);
174 			break;
175 		}
176 	}
177 
178 	pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);
179 
180 	sysreg_write(PCCR, pccr);
181 
182 	return 0;
183 }
184 
avr32_perf_counter_shutdown(void)185 static void avr32_perf_counter_shutdown(void)
186 {
187 	pr_debug("avr32_perf_counter_shutdown\n");
188 
189 	avr32_perf_counter_reset();
190 	free_irq(AVR32_PERFCTR_IRQ_GROUP, counter);
191 }
192 
avr32_perf_counter_start(void)193 static int avr32_perf_counter_start(void)
194 {
195 	pr_debug("avr32_perf_counter_start\n");
196 
197 	sysreg_write(PCCR, sysreg_read(PCCR) | SYSREG_BIT(PCCR_E));
198 
199 	return 0;
200 }
201 
avr32_perf_counter_stop(void)202 static void avr32_perf_counter_stop(void)
203 {
204 	pr_debug("avr32_perf_counter_stop\n");
205 
206 	sysreg_write(PCCR, sysreg_read(PCCR) & ~SYSREG_BIT(PCCR_E));
207 }
208 
209 static struct oprofile_operations avr32_perf_counter_ops __initdata = {
210 	.create_files	= avr32_perf_counter_create_files,
211 	.setup		= avr32_perf_counter_setup,
212 	.shutdown	= avr32_perf_counter_shutdown,
213 	.start		= avr32_perf_counter_start,
214 	.stop		= avr32_perf_counter_stop,
215 	.cpu_type	= "avr32",
216 };
217 
oprofile_arch_init(struct oprofile_operations * ops)218 int __init oprofile_arch_init(struct oprofile_operations *ops)
219 {
220 	if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))
221 		return -ENODEV;
222 
223 	memcpy(ops, &avr32_perf_counter_ops,
224 			sizeof(struct oprofile_operations));
225 
226 	ops->backtrace = avr32_backtrace;
227 
228 	printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
229 
230 	return 0;
231 }
232 
oprofile_arch_exit(void)233 void oprofile_arch_exit(void)
234 {
235 
236 }
237