diff options
Diffstat (limited to 'arch/x86/oprofile/op_model_athlon.c')
-rw-r--r-- | arch/x86/oprofile/op_model_athlon.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c new file mode 100644 index 000000000000..3057a19e4641 --- /dev/null +++ b/arch/x86/oprofile/op_model_athlon.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /** | ||
2 | * @file op_model_athlon.h | ||
3 | * athlon / K7 model-specific MSR operations | ||
4 | * | ||
5 | * @remark Copyright 2002 OProfile authors | ||
6 | * @remark Read the file COPYING | ||
7 | * | ||
8 | * @author John Levon | ||
9 | * @author Philippe Elie | ||
10 | * @author Graydon Hoare | ||
11 | */ | ||
12 | |||
13 | #include <linux/oprofile.h> | ||
14 | #include <asm/ptrace.h> | ||
15 | #include <asm/msr.h> | ||
16 | #include <asm/nmi.h> | ||
17 | |||
18 | #include "op_x86_model.h" | ||
19 | #include "op_counter.h" | ||
20 | |||
21 | #define NUM_COUNTERS 4 | ||
22 | #define NUM_CONTROLS 4 | ||
23 | |||
24 | #define CTR_IS_RESERVED(msrs,c) (msrs->counters[(c)].addr ? 1 : 0) | ||
25 | #define CTR_READ(l,h,msrs,c) do {rdmsr(msrs->counters[(c)].addr, (l), (h));} while (0) | ||
26 | #define CTR_WRITE(l,msrs,c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1);} while (0) | ||
27 | #define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) | ||
28 | |||
29 | #define CTRL_IS_RESERVED(msrs,c) (msrs->controls[(c)].addr ? 1 : 0) | ||
30 | #define CTRL_READ(l,h,msrs,c) do {rdmsr(msrs->controls[(c)].addr, (l), (h));} while (0) | ||
31 | #define CTRL_WRITE(l,h,msrs,c) do {wrmsr(msrs->controls[(c)].addr, (l), (h));} while (0) | ||
32 | #define CTRL_SET_ACTIVE(n) (n |= (1<<22)) | ||
33 | #define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) | ||
34 | #define CTRL_CLEAR(x) (x &= (1<<21)) | ||
35 | #define CTRL_SET_ENABLE(val) (val |= 1<<20) | ||
36 | #define CTRL_SET_USR(val,u) (val |= ((u & 1) << 16)) | ||
37 | #define CTRL_SET_KERN(val,k) (val |= ((k & 1) << 17)) | ||
38 | #define CTRL_SET_UM(val, m) (val |= (m << 8)) | ||
39 | #define CTRL_SET_EVENT(val, e) (val |= e) | ||
40 | |||
41 | static unsigned long reset_value[NUM_COUNTERS]; | ||
42 | |||
43 | static void athlon_fill_in_addresses(struct op_msrs * const msrs) | ||
44 | { | ||
45 | int i; | ||
46 | |||
47 | for (i=0; i < NUM_COUNTERS; i++) { | ||
48 | if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) | ||
49 | msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; | ||
50 | else | ||
51 | msrs->counters[i].addr = 0; | ||
52 | } | ||
53 | |||
54 | for (i=0; i < NUM_CONTROLS; i++) { | ||
55 | if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) | ||
56 | msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; | ||
57 | else | ||
58 | msrs->controls[i].addr = 0; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | |||
63 | static void athlon_setup_ctrs(struct op_msrs const * const msrs) | ||
64 | { | ||
65 | unsigned int low, high; | ||
66 | int i; | ||
67 | |||
68 | /* clear all counters */ | ||
69 | for (i = 0 ; i < NUM_CONTROLS; ++i) { | ||
70 | if (unlikely(!CTRL_IS_RESERVED(msrs,i))) | ||
71 | continue; | ||
72 | CTRL_READ(low, high, msrs, i); | ||
73 | CTRL_CLEAR(low); | ||
74 | CTRL_WRITE(low, high, msrs, i); | ||
75 | } | ||
76 | |||
77 | /* avoid a false detection of ctr overflows in NMI handler */ | ||
78 | for (i = 0; i < NUM_COUNTERS; ++i) { | ||
79 | if (unlikely(!CTR_IS_RESERVED(msrs,i))) | ||
80 | continue; | ||
81 | CTR_WRITE(1, msrs, i); | ||
82 | } | ||
83 | |||
84 | /* enable active counters */ | ||
85 | for (i = 0; i < NUM_COUNTERS; ++i) { | ||
86 | if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs,i))) { | ||
87 | reset_value[i] = counter_config[i].count; | ||
88 | |||
89 | CTR_WRITE(counter_config[i].count, msrs, i); | ||
90 | |||
91 | CTRL_READ(low, high, msrs, i); | ||
92 | CTRL_CLEAR(low); | ||
93 | CTRL_SET_ENABLE(low); | ||
94 | CTRL_SET_USR(low, counter_config[i].user); | ||
95 | CTRL_SET_KERN(low, counter_config[i].kernel); | ||
96 | CTRL_SET_UM(low, counter_config[i].unit_mask); | ||
97 | CTRL_SET_EVENT(low, counter_config[i].event); | ||
98 | CTRL_WRITE(low, high, msrs, i); | ||
99 | } else { | ||
100 | reset_value[i] = 0; | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | |||
106 | static int athlon_check_ctrs(struct pt_regs * const regs, | ||
107 | struct op_msrs const * const msrs) | ||
108 | { | ||
109 | unsigned int low, high; | ||
110 | int i; | ||
111 | |||
112 | for (i = 0 ; i < NUM_COUNTERS; ++i) { | ||
113 | if (!reset_value[i]) | ||
114 | continue; | ||
115 | CTR_READ(low, high, msrs, i); | ||
116 | if (CTR_OVERFLOWED(low)) { | ||
117 | oprofile_add_sample(regs, i); | ||
118 | CTR_WRITE(reset_value[i], msrs, i); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | /* See op_model_ppro.c */ | ||
123 | return 1; | ||
124 | } | ||
125 | |||
126 | |||
127 | static void athlon_start(struct op_msrs const * const msrs) | ||
128 | { | ||
129 | unsigned int low, high; | ||
130 | int i; | ||
131 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { | ||
132 | if (reset_value[i]) { | ||
133 | CTRL_READ(low, high, msrs, i); | ||
134 | CTRL_SET_ACTIVE(low); | ||
135 | CTRL_WRITE(low, high, msrs, i); | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | |||
140 | |||
141 | static void athlon_stop(struct op_msrs const * const msrs) | ||
142 | { | ||
143 | unsigned int low,high; | ||
144 | int i; | ||
145 | |||
146 | /* Subtle: stop on all counters to avoid race with | ||
147 | * setting our pm callback */ | ||
148 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { | ||
149 | if (!reset_value[i]) | ||
150 | continue; | ||
151 | CTRL_READ(low, high, msrs, i); | ||
152 | CTRL_SET_INACTIVE(low); | ||
153 | CTRL_WRITE(low, high, msrs, i); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | static void athlon_shutdown(struct op_msrs const * const msrs) | ||
158 | { | ||
159 | int i; | ||
160 | |||
161 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { | ||
162 | if (CTR_IS_RESERVED(msrs,i)) | ||
163 | release_perfctr_nmi(MSR_K7_PERFCTR0 + i); | ||
164 | } | ||
165 | for (i = 0 ; i < NUM_CONTROLS ; ++i) { | ||
166 | if (CTRL_IS_RESERVED(msrs,i)) | ||
167 | release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | struct op_x86_model_spec const op_athlon_spec = { | ||
172 | .num_counters = NUM_COUNTERS, | ||
173 | .num_controls = NUM_CONTROLS, | ||
174 | .fill_in_addresses = &athlon_fill_in_addresses, | ||
175 | .setup_ctrs = &athlon_setup_ctrs, | ||
176 | .check_ctrs = &athlon_check_ctrs, | ||
177 | .start = &athlon_start, | ||
178 | .stop = &athlon_stop, | ||
179 | .shutdown = &athlon_shutdown | ||
180 | }; | ||