Linux Audio

Check our new training course

Loading...
v3.5.6
  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
 24void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
 25
 26enum { PCCNT, PCNT0, PCNT1, NR_counter };
 27
 28struct 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
 40static 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
 53static 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
 63static 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
100static int avr32_perf_counter_create_files(struct super_block *sb,
101		struct dentry *root)
102{
103	struct dentry *dir;
104	unsigned int i;
105	char filename[4];
106
107	for (i = 0; i < NR_counter; i++) {
108		snprintf(filename, sizeof(filename), "%u", i);
109		dir = oprofilefs_mkdir(sb, root, filename);
110
111		oprofilefs_create_ulong(sb, dir, "enabled",
112				&counter[i].enabled);
113		oprofilefs_create_ulong(sb, dir, "event",
114				&counter[i].event);
115		oprofilefs_create_ulong(sb, dir, "count",
116				&counter[i].count);
117
118		/* Dummy entries */
119		oprofilefs_create_ulong(sb, dir, "kernel",
120				&counter[i].kernel);
121		oprofilefs_create_ulong(sb, dir, "user",
122				&counter[i].user);
123		oprofilefs_create_ulong(sb, dir, "unit_mask",
124				&counter[i].unit_mask);
125	}
126
127	return 0;
128}
129
130static int avr32_perf_counter_setup(void)
131{
132	struct avr32_perf_counter *ctr;
133	u32 pccr;
134	int ret;
135	int i;
136
137	pr_debug("avr32_perf_counter_setup\n");
138
139	if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {
140		printk(KERN_ERR
141			"oprofile: setup: perf counter already enabled\n");
142		return -EBUSY;
143	}
144
145	ret = request_irq(AVR32_PERFCTR_IRQ_GROUP,
146			avr32_perf_counter_interrupt, IRQF_SHARED,
147			"oprofile", counter);
148	if (ret)
149		return ret;
150
151	avr32_perf_counter_reset();
152
153	pccr = 0;
154	for (i = PCCNT; i < NR_counter; i++) {
155		ctr = &counter[i];
156		if (!ctr->enabled)
157			continue;
158
159		pr_debug("enabling counter %d...\n", i);
160
161		pccr |= ctr->ie_mask;
162
163		switch (i) {
164		case PCCNT:
165			/* PCCNT always counts cycles, so no events */
166			sysreg_write(PCCNT, -ctr->count);
167			break;
168		case PCNT0:
169			pccr |= SYSREG_BF(CONF0, ctr->event);
170			sysreg_write(PCNT0, -ctr->count);
171			break;
172		case PCNT1:
173			pccr |= SYSREG_BF(CONF1, ctr->event);
174			sysreg_write(PCNT1, -ctr->count);
175			break;
176		}
177	}
178
179	pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);
180
181	sysreg_write(PCCR, pccr);
182
183	return 0;
184}
185
186static void avr32_perf_counter_shutdown(void)
187{
188	pr_debug("avr32_perf_counter_shutdown\n");
189
190	avr32_perf_counter_reset();
191	free_irq(AVR32_PERFCTR_IRQ_GROUP, counter);
192}
193
194static int avr32_perf_counter_start(void)
195{
196	pr_debug("avr32_perf_counter_start\n");
197
198	sysreg_write(PCCR, sysreg_read(PCCR) | SYSREG_BIT(PCCR_E));
199
200	return 0;
201}
202
203static void avr32_perf_counter_stop(void)
204{
205	pr_debug("avr32_perf_counter_stop\n");
206
207	sysreg_write(PCCR, sysreg_read(PCCR) & ~SYSREG_BIT(PCCR_E));
208}
209
210static struct oprofile_operations avr32_perf_counter_ops __initdata = {
211	.create_files	= avr32_perf_counter_create_files,
212	.setup		= avr32_perf_counter_setup,
213	.shutdown	= avr32_perf_counter_shutdown,
214	.start		= avr32_perf_counter_start,
215	.stop		= avr32_perf_counter_stop,
216	.cpu_type	= "avr32",
217};
218
219int __init oprofile_arch_init(struct oprofile_operations *ops)
220{
221	if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))
222		return -ENODEV;
223
224	memcpy(ops, &avr32_perf_counter_ops,
225			sizeof(struct oprofile_operations));
226
227	ops->backtrace = avr32_backtrace;
228
229	printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
230
231	return 0;
232}
233
234void oprofile_arch_exit(void)
235{
236
237}
v3.1
  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}