• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/avr32/oprofile/
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
25void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
26
27enum { PCCNT, PCNT0, PCNT1, NR_counter };
28
29struct 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
41static 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
54static 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
64static 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
101static 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
131static 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
187static 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
195static 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
204static 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
211static 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
220int __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
235void oprofile_arch_exit(void)
236{
237
238}
239