Commit | Line | Data |
---|---|---|
d4413732 | 1 | /* |
6852fd9b | 2 | * @file op_model_amd.c |
bd87f1f0 | 3 | * athlon / K7 / K8 / Family 10h model-specific MSR operations |
1da177e4 | 4 | * |
ae735e99 | 5 | * @remark Copyright 2002-2009 OProfile authors |
1da177e4 LT |
6 | * @remark Read the file COPYING |
7 | * | |
8 | * @author John Levon | |
9 | * @author Philippe Elie | |
10 | * @author Graydon Hoare | |
adf5ec0b | 11 | * @author Robert Richter <robert.richter@amd.com> |
4d4036e0 JY |
12 | * @author Barry Kasindorf <barry.kasindorf@amd.com> |
13 | * @author Jason Yeh <jason.yeh@amd.com> | |
14 | * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> | |
ae735e99 | 15 | */ |
1da177e4 LT |
16 | |
17 | #include <linux/oprofile.h> | |
56784f11 BK |
18 | #include <linux/device.h> |
19 | #include <linux/pci.h> | |
4d4036e0 | 20 | #include <linux/percpu.h> |
56784f11 | 21 | |
1da177e4 LT |
22 | #include <asm/ptrace.h> |
23 | #include <asm/msr.h> | |
3e4ff115 | 24 | #include <asm/nmi.h> |
013cfc50 | 25 | #include <asm/apic.h> |
64683da6 RR |
26 | #include <asm/processor.h> |
27 | #include <asm/cpufeature.h> | |
d4413732 | 28 | |
1da177e4 LT |
29 | #include "op_x86_model.h" |
30 | #include "op_counter.h" | |
31 | ||
4c168eaf | 32 | #define NUM_COUNTERS 4 |
4d4036e0 JY |
33 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
34 | #define NUM_VIRT_COUNTERS 32 | |
4d4036e0 JY |
35 | #else |
36 | #define NUM_VIRT_COUNTERS NUM_COUNTERS | |
4d4036e0 JY |
37 | #endif |
38 | ||
3370d358 | 39 | #define OP_EVENT_MASK 0x0FFF |
42399adb | 40 | #define OP_CTR_OVERFLOW (1ULL<<31) |
3370d358 RR |
41 | |
42 | #define MSR_AMD_EVENTSEL_RESERVED ((0xFFFFFCF0ULL<<32)|(1ULL<<21)) | |
1da177e4 | 43 | |
4d4036e0 | 44 | static unsigned long reset_value[NUM_VIRT_COUNTERS]; |
852402cc | 45 | |
c572ae4e RR |
46 | #define IBS_FETCH_SIZE 6 |
47 | #define IBS_OP_SIZE 12 | |
56784f11 | 48 | |
64683da6 | 49 | static u32 ibs_caps; |
56784f11 | 50 | |
53b39e94 | 51 | struct ibs_config { |
56784f11 BK |
52 | unsigned long op_enabled; |
53 | unsigned long fetch_enabled; | |
54 | unsigned long max_cnt_fetch; | |
55 | unsigned long max_cnt_op; | |
56 | unsigned long rand_en; | |
57 | unsigned long dispatched_ops; | |
58 | }; | |
59 | ||
53b39e94 RR |
60 | struct ibs_state { |
61 | u64 ibs_op_ctl; | |
62 | }; | |
63 | ||
64 | static struct ibs_config ibs_config; | |
65 | static struct ibs_state ibs_state; | |
d4413732 | 66 | |
64683da6 RR |
67 | /* |
68 | * IBS cpuid feature detection | |
69 | */ | |
70 | ||
71 | #define IBS_CPUID_FEATURES 0x8000001b | |
72 | ||
73 | /* | |
74 | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but | |
75 | * bit 0 is used to indicate the existence of IBS. | |
76 | */ | |
4ac945f0 RR |
77 | #define IBS_CAPS_AVAIL (1U<<0) |
78 | #define IBS_CAPS_FETCHSAM (1U<<1) | |
79 | #define IBS_CAPS_OPSAM (1U<<2) | |
80 | #define IBS_CAPS_RDWROPCNT (1U<<3) | |
81 | #define IBS_CAPS_OPCNT (1U<<4) | |
82 | ||
83 | #define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \ | |
84 | | IBS_CAPS_FETCHSAM \ | |
85 | | IBS_CAPS_OPSAM) | |
86 | ||
87 | /* | |
88 | * IBS APIC setup | |
89 | */ | |
90 | #define IBSCTL 0x1cc | |
91 | #define IBSCTL_LVT_OFFSET_VALID (1ULL<<8) | |
92 | #define IBSCTL_LVT_OFFSET_MASK 0x0F | |
64683da6 | 93 | |
ba52078e RR |
94 | /* |
95 | * IBS randomization macros | |
96 | */ | |
97 | #define IBS_RANDOM_BITS 12 | |
98 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) | |
99 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) | |
100 | ||
64683da6 RR |
101 | static u32 get_ibs_caps(void) |
102 | { | |
103 | u32 ibs_caps; | |
104 | unsigned int max_level; | |
105 | ||
106 | if (!boot_cpu_has(X86_FEATURE_IBS)) | |
107 | return 0; | |
108 | ||
109 | /* check IBS cpuid feature flags */ | |
110 | max_level = cpuid_eax(0x80000000); | |
111 | if (max_level < IBS_CPUID_FEATURES) | |
4ac945f0 | 112 | return IBS_CAPS_DEFAULT; |
64683da6 RR |
113 | |
114 | ibs_caps = cpuid_eax(IBS_CPUID_FEATURES); | |
115 | if (!(ibs_caps & IBS_CAPS_AVAIL)) | |
116 | /* cpuid flags not valid */ | |
4ac945f0 | 117 | return IBS_CAPS_DEFAULT; |
64683da6 RR |
118 | |
119 | return ibs_caps; | |
120 | } | |
121 | ||
f125be14 SS |
122 | /* |
123 | * 16-bit Linear Feedback Shift Register (LFSR) | |
124 | * | |
125 | * 16 14 13 11 | |
126 | * Feedback polynomial = X + X + X + X + 1 | |
127 | */ | |
128 | static unsigned int lfsr_random(void) | |
129 | { | |
130 | static unsigned int lfsr_value = 0xF00D; | |
131 | unsigned int bit; | |
132 | ||
133 | /* Compute next bit to shift in */ | |
134 | bit = ((lfsr_value >> 0) ^ | |
135 | (lfsr_value >> 2) ^ | |
136 | (lfsr_value >> 3) ^ | |
137 | (lfsr_value >> 5)) & 0x0001; | |
138 | ||
139 | /* Advance to next register value */ | |
140 | lfsr_value = (lfsr_value >> 1) | (bit << 15); | |
141 | ||
142 | return lfsr_value; | |
143 | } | |
144 | ||
ba52078e RR |
145 | /* |
146 | * IBS software randomization | |
147 | * | |
148 | * The IBS periodic op counter is randomized in software. The lower 12 | |
149 | * bits of the 20 bit counter are randomized. IbsOpCurCnt is | |
150 | * initialized with a 12 bit random value. | |
151 | */ | |
152 | static inline u64 op_amd_randomize_ibs_op(u64 val) | |
153 | { | |
154 | unsigned int random = lfsr_random(); | |
155 | ||
156 | if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) | |
157 | /* | |
158 | * Work around if the hw can not write to IbsOpCurCnt | |
159 | * | |
160 | * Randomize the lower 8 bits of the 16 bit | |
161 | * IbsOpMaxCnt [15:0] value in the range of -128 to | |
162 | * +127 by adding/subtracting an offset to the | |
163 | * maximum count (IbsOpMaxCnt). | |
164 | * | |
165 | * To avoid over or underflows and protect upper bits | |
166 | * starting at bit 16, the initial value for | |
167 | * IbsOpMaxCnt must fit in the range from 0x0081 to | |
168 | * 0xff80. | |
169 | */ | |
170 | val += (s8)(random >> 4); | |
171 | else | |
172 | val |= (u64)(random & IBS_RANDOM_MASK) << 32; | |
173 | ||
174 | return val; | |
175 | } | |
176 | ||
4680e64a | 177 | static inline void |
7939d2bf RR |
178 | op_amd_handle_ibs(struct pt_regs * const regs, |
179 | struct op_msrs const * const msrs) | |
1da177e4 | 180 | { |
c572ae4e | 181 | u64 val, ctl; |
1acda878 | 182 | struct op_entry entry; |
1da177e4 | 183 | |
64683da6 | 184 | if (!ibs_caps) |
4680e64a | 185 | return; |
1da177e4 | 186 | |
7939d2bf | 187 | if (ibs_config.fetch_enabled) { |
c572ae4e RR |
188 | rdmsrl(MSR_AMD64_IBSFETCHCTL, ctl); |
189 | if (ctl & IBS_FETCH_VAL) { | |
190 | rdmsrl(MSR_AMD64_IBSFETCHLINAD, val); | |
191 | oprofile_write_reserve(&entry, regs, val, | |
14f0ca8e | 192 | IBS_FETCH_CODE, IBS_FETCH_SIZE); |
51563a0e RR |
193 | oprofile_add_data64(&entry, val); |
194 | oprofile_add_data64(&entry, ctl); | |
c572ae4e | 195 | rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, val); |
51563a0e | 196 | oprofile_add_data64(&entry, val); |
14f0ca8e | 197 | oprofile_write_commit(&entry); |
56784f11 | 198 | |
fd13f6c8 | 199 | /* reenable the IRQ */ |
a163b109 | 200 | ctl &= ~(IBS_FETCH_VAL | IBS_FETCH_CNT); |
c572ae4e RR |
201 | ctl |= IBS_FETCH_ENABLE; |
202 | wrmsrl(MSR_AMD64_IBSFETCHCTL, ctl); | |
56784f11 BK |
203 | } |
204 | } | |
205 | ||
7939d2bf | 206 | if (ibs_config.op_enabled) { |
c572ae4e RR |
207 | rdmsrl(MSR_AMD64_IBSOPCTL, ctl); |
208 | if (ctl & IBS_OP_VAL) { | |
209 | rdmsrl(MSR_AMD64_IBSOPRIP, val); | |
210 | oprofile_write_reserve(&entry, regs, val, | |
14f0ca8e | 211 | IBS_OP_CODE, IBS_OP_SIZE); |
51563a0e | 212 | oprofile_add_data64(&entry, val); |
c572ae4e | 213 | rdmsrl(MSR_AMD64_IBSOPDATA, val); |
51563a0e | 214 | oprofile_add_data64(&entry, val); |
c572ae4e | 215 | rdmsrl(MSR_AMD64_IBSOPDATA2, val); |
51563a0e | 216 | oprofile_add_data64(&entry, val); |
c572ae4e | 217 | rdmsrl(MSR_AMD64_IBSOPDATA3, val); |
51563a0e | 218 | oprofile_add_data64(&entry, val); |
c572ae4e | 219 | rdmsrl(MSR_AMD64_IBSDCLINAD, val); |
51563a0e | 220 | oprofile_add_data64(&entry, val); |
c572ae4e | 221 | rdmsrl(MSR_AMD64_IBSDCPHYSAD, val); |
51563a0e | 222 | oprofile_add_data64(&entry, val); |
14f0ca8e | 223 | oprofile_write_commit(&entry); |
56784f11 BK |
224 | |
225 | /* reenable the IRQ */ | |
53b39e94 | 226 | ctl = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl); |
c572ae4e | 227 | wrmsrl(MSR_AMD64_IBSOPCTL, ctl); |
56784f11 BK |
228 | } |
229 | } | |
1da177e4 LT |
230 | } |
231 | ||
90637595 RR |
232 | static inline void op_amd_start_ibs(void) |
233 | { | |
c572ae4e | 234 | u64 val; |
64683da6 RR |
235 | |
236 | if (!ibs_caps) | |
237 | return; | |
238 | ||
53b39e94 RR |
239 | memset(&ibs_state, 0, sizeof(ibs_state)); |
240 | ||
64683da6 | 241 | if (ibs_config.fetch_enabled) { |
a163b109 | 242 | val = (ibs_config.max_cnt_fetch >> 4) & IBS_FETCH_MAX_CNT; |
c572ae4e RR |
243 | val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0; |
244 | val |= IBS_FETCH_ENABLE; | |
245 | wrmsrl(MSR_AMD64_IBSFETCHCTL, val); | |
90637595 RR |
246 | } |
247 | ||
64683da6 | 248 | if (ibs_config.op_enabled) { |
53b39e94 | 249 | val = ibs_config.max_cnt_op >> 4; |
ba52078e RR |
250 | if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) { |
251 | /* | |
252 | * IbsOpCurCnt not supported. See | |
253 | * op_amd_randomize_ibs_op() for details. | |
254 | */ | |
53b39e94 | 255 | val = clamp(val, 0x0081ULL, 0xFF80ULL); |
ba52078e RR |
256 | } else { |
257 | /* | |
258 | * The start value is randomized with a | |
259 | * positive offset, we need to compensate it | |
260 | * with the half of the randomized range. Also | |
261 | * avoid underflows. | |
262 | */ | |
53b39e94 RR |
263 | val = min(val + IBS_RANDOM_MAXCNT_OFFSET, |
264 | IBS_OP_MAX_CNT); | |
ba52078e | 265 | } |
53b39e94 RR |
266 | val |= ibs_config.dispatched_ops ? IBS_OP_CNT_CTL : 0; |
267 | val |= IBS_OP_ENABLE; | |
268 | ibs_state.ibs_op_ctl = val; | |
269 | val = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl); | |
c572ae4e | 270 | wrmsrl(MSR_AMD64_IBSOPCTL, val); |
90637595 RR |
271 | } |
272 | } | |
273 | ||
274 | static void op_amd_stop_ibs(void) | |
275 | { | |
64683da6 RR |
276 | if (!ibs_caps) |
277 | return; | |
278 | ||
279 | if (ibs_config.fetch_enabled) | |
90637595 | 280 | /* clear max count and enable */ |
c572ae4e | 281 | wrmsrl(MSR_AMD64_IBSFETCHCTL, 0); |
90637595 | 282 | |
64683da6 | 283 | if (ibs_config.op_enabled) |
90637595 | 284 | /* clear max count and enable */ |
c572ae4e | 285 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
90637595 RR |
286 | } |
287 | ||
da759fe5 RR |
288 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
289 | ||
290 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, | |
291 | struct op_msrs const * const msrs) | |
292 | { | |
293 | u64 val; | |
294 | int i; | |
295 | ||
296 | /* enable active counters */ | |
297 | for (i = 0; i < NUM_COUNTERS; ++i) { | |
298 | int virt = op_x86_phys_to_virt(i); | |
299 | if (!reset_value[virt]) | |
300 | continue; | |
301 | rdmsrl(msrs->controls[i].addr, val); | |
302 | val &= model->reserved; | |
303 | val |= op_x86_get_ctrl(model, &counter_config[virt]); | |
304 | wrmsrl(msrs->controls[i].addr, val); | |
305 | } | |
306 | } | |
307 | ||
308 | #endif | |
309 | ||
310 | /* functions for op_amd_spec */ | |
311 | ||
312 | static void op_amd_shutdown(struct op_msrs const * const msrs) | |
313 | { | |
314 | int i; | |
315 | ||
316 | for (i = 0; i < NUM_COUNTERS; ++i) { | |
317 | if (!msrs->counters[i].addr) | |
318 | continue; | |
319 | release_perfctr_nmi(MSR_K7_PERFCTR0 + i); | |
320 | release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | |
321 | } | |
322 | } | |
323 | ||
324 | static int op_amd_fill_in_addresses(struct op_msrs * const msrs) | |
325 | { | |
326 | int i; | |
327 | ||
328 | for (i = 0; i < NUM_COUNTERS; i++) { | |
329 | if (!reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) | |
330 | goto fail; | |
331 | if (!reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) { | |
332 | release_perfctr_nmi(MSR_K7_PERFCTR0 + i); | |
333 | goto fail; | |
334 | } | |
335 | /* both registers must be reserved */ | |
336 | msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; | |
337 | msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; | |
338 | continue; | |
339 | fail: | |
340 | if (!counter_config[i].enabled) | |
341 | continue; | |
342 | op_x86_warn_reserved(i); | |
343 | op_amd_shutdown(msrs); | |
344 | return -EBUSY; | |
345 | } | |
346 | ||
347 | return 0; | |
348 | } | |
349 | ||
350 | static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, | |
351 | struct op_msrs const * const msrs) | |
352 | { | |
353 | u64 val; | |
354 | int i; | |
355 | ||
356 | /* setup reset_value */ | |
357 | for (i = 0; i < NUM_VIRT_COUNTERS; ++i) { | |
358 | if (counter_config[i].enabled | |
359 | && msrs->counters[op_x86_virt_to_phys(i)].addr) | |
360 | reset_value[i] = counter_config[i].count; | |
361 | else | |
362 | reset_value[i] = 0; | |
363 | } | |
364 | ||
365 | /* clear all counters */ | |
366 | for (i = 0; i < NUM_COUNTERS; ++i) { | |
367 | if (!msrs->controls[i].addr) | |
368 | continue; | |
369 | rdmsrl(msrs->controls[i].addr, val); | |
370 | if (val & ARCH_PERFMON_EVENTSEL_ENABLE) | |
371 | op_x86_warn_in_use(i); | |
372 | val &= model->reserved; | |
373 | wrmsrl(msrs->controls[i].addr, val); | |
374 | /* | |
375 | * avoid a false detection of ctr overflows in NMI | |
376 | * handler | |
377 | */ | |
378 | wrmsrl(msrs->counters[i].addr, -1LL); | |
379 | } | |
380 | ||
381 | /* enable active counters */ | |
382 | for (i = 0; i < NUM_COUNTERS; ++i) { | |
383 | int virt = op_x86_phys_to_virt(i); | |
384 | if (!reset_value[virt]) | |
385 | continue; | |
386 | ||
387 | /* setup counter registers */ | |
388 | wrmsrl(msrs->counters[i].addr, -(u64)reset_value[virt]); | |
389 | ||
390 | /* setup control registers */ | |
391 | rdmsrl(msrs->controls[i].addr, val); | |
392 | val &= model->reserved; | |
393 | val |= op_x86_get_ctrl(model, &counter_config[virt]); | |
394 | wrmsrl(msrs->controls[i].addr, val); | |
395 | } | |
bae663bc RR |
396 | |
397 | if (ibs_caps) | |
398 | setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); | |
399 | } | |
400 | ||
401 | static void op_amd_cpu_shutdown(void) | |
402 | { | |
403 | if (ibs_caps) | |
404 | setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); | |
da759fe5 RR |
405 | } |
406 | ||
7939d2bf RR |
407 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
408 | struct op_msrs const * const msrs) | |
409 | { | |
42399adb | 410 | u64 val; |
7939d2bf RR |
411 | int i; |
412 | ||
6e63ea4b | 413 | for (i = 0; i < NUM_COUNTERS; ++i) { |
d8471ad3 RR |
414 | int virt = op_x86_phys_to_virt(i); |
415 | if (!reset_value[virt]) | |
7939d2bf | 416 | continue; |
42399adb RR |
417 | rdmsrl(msrs->counters[i].addr, val); |
418 | /* bit is clear if overflowed: */ | |
419 | if (val & OP_CTR_OVERFLOW) | |
420 | continue; | |
d8471ad3 RR |
421 | oprofile_add_sample(regs, virt); |
422 | wrmsrl(msrs->counters[i].addr, -(u64)reset_value[virt]); | |
7939d2bf RR |
423 | } |
424 | ||
425 | op_amd_handle_ibs(regs, msrs); | |
426 | ||
427 | /* See op_model_ppro.c */ | |
428 | return 1; | |
429 | } | |
d4413732 | 430 | |
6657fe4f | 431 | static void op_amd_start(struct op_msrs const * const msrs) |
1da177e4 | 432 | { |
dea3766c | 433 | u64 val; |
1da177e4 | 434 | int i; |
4d4036e0 | 435 | |
6e63ea4b | 436 | for (i = 0; i < NUM_COUNTERS; ++i) { |
d8471ad3 RR |
437 | if (!reset_value[op_x86_phys_to_virt(i)]) |
438 | continue; | |
439 | rdmsrl(msrs->controls[i].addr, val); | |
bb1165d6 | 440 | val |= ARCH_PERFMON_EVENTSEL_ENABLE; |
d8471ad3 | 441 | wrmsrl(msrs->controls[i].addr, val); |
1da177e4 | 442 | } |
852402cc | 443 | |
90637595 | 444 | op_amd_start_ibs(); |
1da177e4 LT |
445 | } |
446 | ||
6657fe4f | 447 | static void op_amd_stop(struct op_msrs const * const msrs) |
1da177e4 | 448 | { |
dea3766c | 449 | u64 val; |
1da177e4 LT |
450 | int i; |
451 | ||
fd13f6c8 RR |
452 | /* |
453 | * Subtle: stop on all counters to avoid race with setting our | |
454 | * pm callback | |
455 | */ | |
6e63ea4b | 456 | for (i = 0; i < NUM_COUNTERS; ++i) { |
d8471ad3 | 457 | if (!reset_value[op_x86_phys_to_virt(i)]) |
cb9c448c | 458 | continue; |
dea3766c | 459 | rdmsrl(msrs->controls[i].addr, val); |
bb1165d6 | 460 | val &= ~ARCH_PERFMON_EVENTSEL_ENABLE; |
dea3766c | 461 | wrmsrl(msrs->controls[i].addr, val); |
1da177e4 | 462 | } |
56784f11 | 463 | |
90637595 | 464 | op_amd_stop_ibs(); |
1da177e4 LT |
465 | } |
466 | ||
bae663bc | 467 | static int __init_ibs_nmi(void) |
7d77f2dc RR |
468 | { |
469 | #define IBSCTL_LVTOFFSETVAL (1 << 8) | |
470 | #define IBSCTL 0x1cc | |
471 | struct pci_dev *cpu_cfg; | |
472 | int nodes; | |
473 | u32 value = 0; | |
bae663bc | 474 | u8 ibs_eilvt_off; |
7d77f2dc | 475 | |
bae663bc | 476 | ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); |
7d77f2dc RR |
477 | |
478 | nodes = 0; | |
479 | cpu_cfg = NULL; | |
480 | do { | |
481 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | |
482 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | |
483 | cpu_cfg); | |
484 | if (!cpu_cfg) | |
485 | break; | |
486 | ++nodes; | |
487 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | |
488 | | IBSCTL_LVTOFFSETVAL); | |
489 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | |
490 | if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { | |
83bd9243 | 491 | pci_dev_put(cpu_cfg); |
7d77f2dc RR |
492 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " |
493 | "IBSCTL = 0x%08x", value); | |
494 | return 1; | |
495 | } | |
496 | } while (1); | |
497 | ||
498 | if (!nodes) { | |
499 | printk(KERN_DEBUG "No CPU node configured for IBS"); | |
500 | return 1; | |
501 | } | |
502 | ||
7d77f2dc RR |
503 | return 0; |
504 | } | |
505 | ||
fd13f6c8 | 506 | /* initialize the APIC for the IBS interrupts if available */ |
bae663bc | 507 | static void init_ibs(void) |
56784f11 | 508 | { |
64683da6 | 509 | ibs_caps = get_ibs_caps(); |
56784f11 | 510 | |
64683da6 | 511 | if (!ibs_caps) |
56784f11 BK |
512 | return; |
513 | ||
bae663bc | 514 | if (__init_ibs_nmi()) { |
64683da6 | 515 | ibs_caps = 0; |
852402cc RR |
516 | return; |
517 | } | |
518 | ||
64683da6 RR |
519 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", |
520 | (unsigned)ibs_caps); | |
56784f11 BK |
521 | } |
522 | ||
25ad2913 | 523 | static int (*create_arch_files)(struct super_block *sb, struct dentry *root); |
270d3e1a | 524 | |
25ad2913 | 525 | static int setup_ibs_files(struct super_block *sb, struct dentry *root) |
56784f11 | 526 | { |
56784f11 | 527 | struct dentry *dir; |
270d3e1a RR |
528 | int ret = 0; |
529 | ||
530 | /* architecture specific files */ | |
531 | if (create_arch_files) | |
532 | ret = create_arch_files(sb, root); | |
533 | ||
534 | if (ret) | |
535 | return ret; | |
56784f11 | 536 | |
64683da6 | 537 | if (!ibs_caps) |
270d3e1a RR |
538 | return ret; |
539 | ||
540 | /* model specific files */ | |
56784f11 BK |
541 | |
542 | /* setup some reasonable defaults */ | |
543 | ibs_config.max_cnt_fetch = 250000; | |
544 | ibs_config.fetch_enabled = 0; | |
545 | ibs_config.max_cnt_op = 250000; | |
546 | ibs_config.op_enabled = 0; | |
64683da6 | 547 | ibs_config.dispatched_ops = 0; |
2d55a478 | 548 | |
4ac945f0 RR |
549 | if (ibs_caps & IBS_CAPS_FETCHSAM) { |
550 | dir = oprofilefs_mkdir(sb, root, "ibs_fetch"); | |
551 | oprofilefs_create_ulong(sb, dir, "enable", | |
552 | &ibs_config.fetch_enabled); | |
553 | oprofilefs_create_ulong(sb, dir, "max_count", | |
554 | &ibs_config.max_cnt_fetch); | |
555 | oprofilefs_create_ulong(sb, dir, "rand_enable", | |
556 | &ibs_config.rand_en); | |
557 | } | |
558 | ||
559 | if (ibs_caps & IBS_CAPS_OPSAM) { | |
560 | dir = oprofilefs_mkdir(sb, root, "ibs_op"); | |
561 | oprofilefs_create_ulong(sb, dir, "enable", | |
562 | &ibs_config.op_enabled); | |
563 | oprofilefs_create_ulong(sb, dir, "max_count", | |
564 | &ibs_config.max_cnt_op); | |
565 | if (ibs_caps & IBS_CAPS_OPCNT) | |
566 | oprofilefs_create_ulong(sb, dir, "dispatched_ops", | |
567 | &ibs_config.dispatched_ops); | |
568 | } | |
fc2bd734 RR |
569 | |
570 | return 0; | |
56784f11 BK |
571 | } |
572 | ||
adf5ec0b RR |
573 | static int op_amd_init(struct oprofile_operations *ops) |
574 | { | |
bae663bc | 575 | init_ibs(); |
270d3e1a RR |
576 | create_arch_files = ops->create_files; |
577 | ops->create_files = setup_ibs_files; | |
adf5ec0b RR |
578 | return 0; |
579 | } | |
580 | ||
259a83a8 | 581 | struct op_x86_model_spec op_amd_spec = { |
c92960fc | 582 | .num_counters = NUM_COUNTERS, |
d0e4120f | 583 | .num_controls = NUM_COUNTERS, |
4d4036e0 | 584 | .num_virt_counters = NUM_VIRT_COUNTERS, |
3370d358 RR |
585 | .reserved = MSR_AMD_EVENTSEL_RESERVED, |
586 | .event_mask = OP_EVENT_MASK, | |
587 | .init = op_amd_init, | |
c92960fc RR |
588 | .fill_in_addresses = &op_amd_fill_in_addresses, |
589 | .setup_ctrs = &op_amd_setup_ctrs, | |
bae663bc | 590 | .cpu_down = &op_amd_cpu_shutdown, |
c92960fc RR |
591 | .check_ctrs = &op_amd_check_ctrs, |
592 | .start = &op_amd_start, | |
593 | .stop = &op_amd_stop, | |
3370d358 | 594 | .shutdown = &op_amd_shutdown, |
4d4036e0 | 595 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
7e7478c6 | 596 | .switch_ctrl = &op_mux_switch_ctrl, |
4d4036e0 | 597 | #endif |
1da177e4 | 598 | }; |