aboutsummaryrefslogtreecommitdiffstats
path: root/arch/alpha/oprofile/common.c
blob: b8ce18f485d3ecce23d9903138eeb930ab62cb4d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/**
 * @file arch/alpha/oprofile/common.c
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author Richard Henderson <rth@twiddle.net>
 */

#include <linux/oprofile.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <asm/ptrace.h>
#include <asm/special_insns.h>

#include "op_impl.h"

extern struct op_axp_model op_model_ev4 __attribute__((weak));
extern struct op_axp_model op_model_ev5 __attribute__((weak));
extern struct op_axp_model op_model_pca56 __attribute__((weak));
extern struct op_axp_model op_model_ev6 __attribute__((weak));
extern struct op_axp_model op_model_ev67 __attribute__((weak));

static struct op_axp_model *model;

extern void (*perf_irq)(unsigned long, struct pt_regs *);
static void (*save_perf_irq)(unsigned long, struct pt_regs *);

static struct op_counter_config ctr[20];
static struct op_system_config sys;
static struct op_register_config reg;

/* Called from do_entInt to handle the performance monitor interrupt.  */

static void
op_handle_interrupt(unsigned long which, struct pt_regs *regs)
{
	model->handle_interrupt(which, regs, ctr);

	/* If the user has selected an interrupt frequency that is
	   not exactly the width of the counter, write a new value
	   into the counter such that it'll overflow after N more
	   events.  */
	if ((reg.need_reset >> which) & 1)
		model->reset_ctr(&reg, which);
}
 
static int
op_axp_setup(void)
{
	unsigned long i, e;

	/* Install our interrupt handler into the existing hook.  */
	save_perf_irq = perf_irq;
	perf_irq = op_handle_interrupt;

	/* Compute the mask of enabled counters.  */
	for (i = e = 0; i < model->num_counters; ++i)
		if (ctr[i].enabled)
			e |= 1 << i;
	reg.enable = e;

	/* Pre-compute the values to stuff in the hardware registers.  */
	model->reg_setup(&reg, ctr, &sys);

	/* Configure the registers on all cpus.  */
	(void)smp_call_function(model->cpu_setup, &reg, 1);
	model->cpu_setup(&reg);
	return 0;
}

static void
op_axp_shutdown(void)
{
	/* Remove our interrupt handler.  We may be removing this module.  */
	perf_irq = save_perf_irq;
}

static void
op_axp_cpu_start(void *dummy)
{
	wrperfmon(1, reg.enable);
}

static int
op_axp_start(void)
{
	(void)smp_call_function(op_axp_cpu_start, NULL, 1);
	op_axp_cpu_start(NULL);
	return 0;
}

static inline void
op_axp_cpu_stop(void *dummy)
{
	/* Disable performance monitoring for all counters.  */
	wrperfmon(0, -1);
}

static void
op_axp_stop(void)
{
	(void)smp_call_function(op_axp_cpu_stop, NULL, 1);
	op_axp_cpu_stop(NULL);
}

static int
op_axp_create_files(struct super_block *sb, struct dentry *root)
{
	int i;

	for (i = 0; i < model->num_counters; ++i) {
		struct dentry *dir;
		char buf[4];

		snprintf(buf, sizeof buf, "%d", i);
		dir = oprofilefs_mkdir(sb, root, buf);

		oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
                oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
		oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
		/* Dummies.  */
		oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
		oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
		oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
	}

	if (model->can_set_proc_mode) {
		oprofilefs_create_ulong(sb, root, "enable_pal",
					&sys.enable_pal);
		oprofilefs_create_ulong(sb, root, "enable_kernel",
					&sys.enable_kernel);
		oprofilefs_create_ulong(sb, root, "enable_user",
					&sys.enable_user);
	}

	return 0;
}

int __init
oprofile_arch_init(struct oprofile_operations *ops)
{
	struct op_axp_model *lmodel = NULL;

	switch (implver()) {
	case IMPLVER_EV4:
		lmodel = &op_model_ev4;
		break;
	case IMPLVER_EV5:
		/* 21164PC has a slightly different set of events.
		   Recognize the chip by the presence of the MAX insns.  */
		if (!amask(AMASK_MAX))
			lmodel = &op_model_pca56;
		else
			lmodel = &op_model_ev5;
		break;
	case IMPLVER_EV6:
		/* 21264A supports ProfileMe.
		   Recognize the chip by the presence of the CIX insns.  */
		if (!amask(AMASK_CIX))
			lmodel = &op_model_ev67;
		else
			lmodel = &op_model_ev6;
		break;
	}

	if (!lmodel)
		return -ENODEV;
	model = lmodel;

	ops->create_files = op_axp_create_files;
	ops->setup = op_axp_setup;
	ops->shutdown = op_axp_shutdown;
	ops->start = op_axp_start;
	ops->stop = op_axp_stop;
	ops->cpu_type = lmodel->cpu_type;

	printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
	       lmodel->cpu_type);

	return 0;
}


void
oprofile_arch_exit(void)
{
}