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