blob: cf310aeb462c20be5a3f82d72a4efd966b486825 [file] [log] [blame]
Paolo Ciarrocchid4413732008-02-19 23:51:27 +01001/*
Robert Richter6852fd92008-07-22 21:09:08 +02002 * @file op_model_amd.c
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +01003 * athlon / K7 / K8 / Family 10h model-specific MSR operations
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Robert Richterae735e92008-12-25 17:26:07 +01005 * @remark Copyright 2002-2009 OProfile authors
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * @remark Read the file COPYING
7 *
8 * @author John Levon
9 * @author Philippe Elie
10 * @author Graydon Hoare
Robert Richteradf5ec02008-07-22 21:08:48 +020011 * @author Robert Richter <robert.richter@amd.com>
Barry Kasindorf56784f12008-07-22 21:08:55 +020012 * @author Barry Kasindorf
Robert Richterae735e92008-12-25 17:26:07 +010013 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070014
15#include <linux/oprofile.h>
Barry Kasindorf56784f12008-07-22 21:08:55 +020016#include <linux/device.h>
17#include <linux/pci.h>
18
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <asm/ptrace.h>
20#include <asm/msr.h>
Don Zickus3e4ff112006-06-26 13:57:01 +020021#include <asm/nmi.h>
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010022
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include "op_x86_model.h"
24#include "op_counter.h"
Robert Richter1acda872009-01-05 10:35:31 +010025#include "../../../drivers/oprofile/cpu_buffer.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Robert Richter4c168ea2008-09-24 11:08:52 +020027#define NUM_COUNTERS 4
28#define NUM_CONTROLS 4
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010030#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0)
31#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0)
32#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))
34
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010035#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0)
36#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
37#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#define CTRL_SET_ACTIVE(n) (n |= (1<<22))
39#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +010040#define CTRL_CLEAR_LO(x) (x &= (1<<21))
41#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#define CTRL_SET_ENABLE(val) (val |= 1<<20)
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010043#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
44#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#define CTRL_SET_UM(val, m) (val |= (m << 8))
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +010046#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff))
47#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf))
48#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9))
49#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8))
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Robert Richter852402c2008-07-22 21:09:06 +020051static unsigned long reset_value[NUM_COUNTERS];
52
53#ifdef CONFIG_OPROFILE_IBS
54
Robert Richter87f0bac2008-07-22 21:09:03 +020055/* IbsFetchCtl bits/masks */
56#define IBS_FETCH_HIGH_VALID_BIT (1UL << 17) /* bit 49 */
57#define IBS_FETCH_HIGH_ENABLE (1UL << 16) /* bit 48 */
58#define IBS_FETCH_LOW_MAX_CNT_MASK 0x0000FFFFUL /* MaxCnt mask */
Barry Kasindorf56784f12008-07-22 21:08:55 +020059
Robert Richter87f0bac2008-07-22 21:09:03 +020060/*IbsOpCtl bits */
61#define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */
62#define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */
Barry Kasindorf56784f12008-07-22 21:08:55 +020063
Robert Richterfd13f6c2008-10-19 21:00:09 +020064/*
65 * The function interface needs to be fixed, something like add
66 * data. Should then be added to linux/oprofile.h.
67 */
Robert Richter1acda872009-01-05 10:35:31 +010068extern
69void oprofile_add_data(struct op_entry *entry, struct pt_regs * const regs,
70 unsigned long pc, int code, int size);
Robert Richter90645702008-07-22 21:08:58 +020071
Robert Richter1acda872009-01-05 10:35:31 +010072#define IBS_FETCH_SIZE 6
73#define IBS_OP_SIZE 12
Barry Kasindorf56784f12008-07-22 21:08:55 +020074
Robert Richterfc81be82008-12-18 00:28:27 +010075static int has_ibs; /* AMD Family10h and later */
Barry Kasindorf56784f12008-07-22 21:08:55 +020076
77struct op_ibs_config {
78 unsigned long op_enabled;
79 unsigned long fetch_enabled;
80 unsigned long max_cnt_fetch;
81 unsigned long max_cnt_op;
82 unsigned long rand_en;
83 unsigned long dispatched_ops;
84};
85
86static struct op_ibs_config ibs_config;
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010087
Robert Richter852402c2008-07-22 21:09:06 +020088#endif
89
Robert Richter6657fe42008-07-22 21:08:50 +020090/* functions for op_amd_spec */
Robert Richterdfa15422008-07-22 21:08:49 +020091
Robert Richter6657fe42008-07-22 21:08:50 +020092static void op_amd_fill_in_addresses(struct op_msrs * const msrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093{
Don Zickuscb9c4482006-09-26 10:52:26 +020094 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010096 for (i = 0; i < NUM_COUNTERS; i++) {
Robert Richter4c168ea2008-09-24 11:08:52 +020097 if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
98 msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
Don Zickuscb9c4482006-09-26 10:52:26 +020099 else
100 msrs->counters[i].addr = 0;
101 }
102
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100103 for (i = 0; i < NUM_CONTROLS; i++) {
Robert Richter4c168ea2008-09-24 11:08:52 +0200104 if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
105 msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
Don Zickuscb9c4482006-09-26 10:52:26 +0200106 else
107 msrs->controls[i].addr = 0;
108 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109}
110
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100111
Robert Richter6657fe42008-07-22 21:08:50 +0200112static void op_amd_setup_ctrs(struct op_msrs const * const msrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113{
114 unsigned int low, high;
115 int i;
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 /* clear all counters */
Robert Richter4c168ea2008-09-24 11:08:52 +0200118 for (i = 0 ; i < NUM_CONTROLS; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100119 if (unlikely(!CTRL_IS_RESERVED(msrs, i)))
Don Zickuscb9c4482006-09-26 10:52:26 +0200120 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 CTRL_READ(low, high, msrs, i);
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +0100122 CTRL_CLEAR_LO(low);
123 CTRL_CLEAR_HI(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 CTRL_WRITE(low, high, msrs, i);
125 }
Don Zickuscb9c4482006-09-26 10:52:26 +0200126
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 /* avoid a false detection of ctr overflows in NMI handler */
Robert Richter4c168ea2008-09-24 11:08:52 +0200128 for (i = 0; i < NUM_COUNTERS; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100129 if (unlikely(!CTR_IS_RESERVED(msrs, i)))
Don Zickuscb9c4482006-09-26 10:52:26 +0200130 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 CTR_WRITE(1, msrs, i);
132 }
133
134 /* enable active counters */
Robert Richter4c168ea2008-09-24 11:08:52 +0200135 for (i = 0; i < NUM_COUNTERS; ++i) {
136 if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) {
137 reset_value[i] = counter_config[i].count;
138
139 CTR_WRITE(counter_config[i].count, msrs, i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140
141 CTRL_READ(low, high, msrs, i);
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +0100142 CTRL_CLEAR_LO(low);
143 CTRL_CLEAR_HI(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 CTRL_SET_ENABLE(low);
Robert Richter4c168ea2008-09-24 11:08:52 +0200145 CTRL_SET_USR(low, counter_config[i].user);
146 CTRL_SET_KERN(low, counter_config[i].kernel);
147 CTRL_SET_UM(low, counter_config[i].unit_mask);
148 CTRL_SET_EVENT_LOW(low, counter_config[i].event);
149 CTRL_SET_EVENT_HIGH(high, counter_config[i].event);
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +0100150 CTRL_SET_HOST_ONLY(high, 0);
151 CTRL_SET_GUEST_ONLY(high, 0);
152
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 CTRL_WRITE(low, high, msrs, i);
Robert Richter4c168ea2008-09-24 11:08:52 +0200154 } else {
155 reset_value[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 }
157 }
158}
159
Robert Richter852402c2008-07-22 21:09:06 +0200160#ifdef CONFIG_OPROFILE_IBS
161
Robert Richter7939d2b2008-07-22 21:08:56 +0200162static inline int
163op_amd_handle_ibs(struct pt_regs * const regs,
164 struct op_msrs const * const msrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
Robert Richter1acda872009-01-05 10:35:31 +0100166 u32 low, high;
167 u64 msr;
168 struct op_entry entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
Robert Richterfc81be82008-12-18 00:28:27 +0100170 if (!has_ibs)
Robert Richter7939d2b2008-07-22 21:08:56 +0200171 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
Robert Richter7939d2b2008-07-22 21:08:56 +0200173 if (ibs_config.fetch_enabled) {
Barry Kasindorf56784f12008-07-22 21:08:55 +0200174 rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
Robert Richter87f0bac2008-07-22 21:09:03 +0200175 if (high & IBS_FETCH_HIGH_VALID_BIT) {
Robert Richter1acda872009-01-05 10:35:31 +0100176 rdmsrl(MSR_AMD64_IBSFETCHLINAD, msr);
177 oprofile_add_data(&entry, regs, msr, IBS_FETCH_CODE,
178 IBS_FETCH_SIZE);
179 op_cpu_buffer_add_data(&entry, (u32)msr);
180 op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
181 op_cpu_buffer_add_data(&entry, low);
182 op_cpu_buffer_add_data(&entry, high);
183 rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, msr);
184 op_cpu_buffer_add_data(&entry, (u32)msr);
185 op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
186 op_cpu_buffer_write_commit(&entry);
Barry Kasindorf56784f12008-07-22 21:08:55 +0200187
Robert Richterfd13f6c2008-10-19 21:00:09 +0200188 /* reenable the IRQ */
Robert Richter87f0bac2008-07-22 21:09:03 +0200189 high &= ~IBS_FETCH_HIGH_VALID_BIT;
190 high |= IBS_FETCH_HIGH_ENABLE;
191 low &= IBS_FETCH_LOW_MAX_CNT_MASK;
Barry Kasindorf56784f12008-07-22 21:08:55 +0200192 wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
193 }
194 }
195
Robert Richter7939d2b2008-07-22 21:08:56 +0200196 if (ibs_config.op_enabled) {
Barry Kasindorf56784f12008-07-22 21:08:55 +0200197 rdmsr(MSR_AMD64_IBSOPCTL, low, high);
Robert Richter87f0bac2008-07-22 21:09:03 +0200198 if (low & IBS_OP_LOW_VALID_BIT) {
Robert Richter1acda872009-01-05 10:35:31 +0100199 rdmsrl(MSR_AMD64_IBSOPRIP, msr);
200 oprofile_add_data(&entry, regs, msr, IBS_OP_CODE,
201 IBS_OP_SIZE);
202 op_cpu_buffer_add_data(&entry, (u32)msr);
203 op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
204 rdmsrl(MSR_AMD64_IBSOPDATA, msr);
205 op_cpu_buffer_add_data(&entry, (u32)msr);
206 op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
207 rdmsrl(MSR_AMD64_IBSOPDATA2, msr);
208 op_cpu_buffer_add_data(&entry, (u32)msr);
209 op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
210 rdmsrl(MSR_AMD64_IBSOPDATA3, msr);
211 op_cpu_buffer_add_data(&entry, (u32)msr);
212 op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
213 rdmsrl(MSR_AMD64_IBSDCLINAD, msr);
214 op_cpu_buffer_add_data(&entry, (u32)msr);
215 op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
216 rdmsrl(MSR_AMD64_IBSDCPHYSAD, msr);
217 op_cpu_buffer_add_data(&entry, (u32)msr);
218 op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
219 op_cpu_buffer_write_commit(&entry);
Barry Kasindorf56784f12008-07-22 21:08:55 +0200220
221 /* reenable the IRQ */
Robert Richter543a1572008-07-22 21:09:04 +0200222 high = 0;
Robert Richter87f0bac2008-07-22 21:09:03 +0200223 low &= ~IBS_OP_LOW_VALID_BIT;
224 low |= IBS_OP_LOW_ENABLE;
Barry Kasindorf56784f12008-07-22 21:08:55 +0200225 wrmsr(MSR_AMD64_IBSOPCTL, low, high);
226 }
227 }
228
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 return 1;
230}
231
Robert Richter852402c2008-07-22 21:09:06 +0200232#endif
233
Robert Richter7939d2b2008-07-22 21:08:56 +0200234static int op_amd_check_ctrs(struct pt_regs * const regs,
235 struct op_msrs const * const msrs)
236{
237 unsigned int low, high;
238 int i;
239
Robert Richter4c168ea2008-09-24 11:08:52 +0200240 for (i = 0 ; i < NUM_COUNTERS; ++i) {
241 if (!reset_value[i])
Robert Richter7939d2b2008-07-22 21:08:56 +0200242 continue;
243 CTR_READ(low, high, msrs, i);
244 if (CTR_OVERFLOWED(low)) {
Robert Richter4c168ea2008-09-24 11:08:52 +0200245 oprofile_add_sample(regs, i);
246 CTR_WRITE(reset_value[i], msrs, i);
Robert Richter7939d2b2008-07-22 21:08:56 +0200247 }
248 }
249
Robert Richter852402c2008-07-22 21:09:06 +0200250#ifdef CONFIG_OPROFILE_IBS
Robert Richter7939d2b2008-07-22 21:08:56 +0200251 op_amd_handle_ibs(regs, msrs);
Robert Richter852402c2008-07-22 21:09:06 +0200252#endif
Robert Richter7939d2b2008-07-22 21:08:56 +0200253
254 /* See op_model_ppro.c */
255 return 1;
256}
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100257
Robert Richter6657fe42008-07-22 21:08:50 +0200258static void op_amd_start(struct op_msrs const * const msrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
260 unsigned int low, high;
261 int i;
Robert Richter4c168ea2008-09-24 11:08:52 +0200262 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
263 if (reset_value[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 CTRL_READ(low, high, msrs, i);
265 CTRL_SET_ACTIVE(low);
266 CTRL_WRITE(low, high, msrs, i);
267 }
268 }
Robert Richter852402c2008-07-22 21:09:06 +0200269
270#ifdef CONFIG_OPROFILE_IBS
Robert Richterfc81be82008-12-18 00:28:27 +0100271 if (has_ibs && ibs_config.fetch_enabled) {
Barry Kasindorf56784f12008-07-22 21:08:55 +0200272 low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF;
Suravee Suthikulpanit5f87dfb2008-10-15 08:15:51 -0500273 high = ((ibs_config.rand_en & 0x1) << 25) /* bit 57 */
274 + IBS_FETCH_HIGH_ENABLE;
Barry Kasindorf56784f12008-07-22 21:08:55 +0200275 wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
276 }
277
Robert Richterfc81be82008-12-18 00:28:27 +0100278 if (has_ibs && ibs_config.op_enabled) {
Suravee Suthikulpanit5f87dfb2008-10-15 08:15:51 -0500279 low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF)
280 + ((ibs_config.dispatched_ops & 0x1) << 19) /* bit 19 */
281 + IBS_OP_LOW_ENABLE;
Barry Kasindorf56784f12008-07-22 21:08:55 +0200282 high = 0;
283 wrmsr(MSR_AMD64_IBSOPCTL, low, high);
284 }
Robert Richter852402c2008-07-22 21:09:06 +0200285#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286}
287
288
Robert Richter6657fe42008-07-22 21:08:50 +0200289static void op_amd_stop(struct op_msrs const * const msrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100291 unsigned int low, high;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 int i;
293
Robert Richterfd13f6c2008-10-19 21:00:09 +0200294 /*
295 * Subtle: stop on all counters to avoid race with setting our
296 * pm callback
297 */
Robert Richter4c168ea2008-09-24 11:08:52 +0200298 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
299 if (!reset_value[i])
Don Zickuscb9c4482006-09-26 10:52:26 +0200300 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 CTRL_READ(low, high, msrs, i);
302 CTRL_SET_INACTIVE(low);
303 CTRL_WRITE(low, high, msrs, i);
304 }
Barry Kasindorf56784f12008-07-22 21:08:55 +0200305
Robert Richter852402c2008-07-22 21:09:06 +0200306#ifdef CONFIG_OPROFILE_IBS
Robert Richterfc81be82008-12-18 00:28:27 +0100307 if (has_ibs && ibs_config.fetch_enabled) {
Robert Richterfd13f6c2008-10-19 21:00:09 +0200308 /* clear max count and enable */
309 low = 0;
Barry Kasindorf56784f12008-07-22 21:08:55 +0200310 high = 0;
311 wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
312 }
313
Robert Richterfc81be82008-12-18 00:28:27 +0100314 if (has_ibs && ibs_config.op_enabled) {
Robert Richterfd13f6c2008-10-19 21:00:09 +0200315 /* clear max count and enable */
316 low = 0;
Barry Kasindorf56784f12008-07-22 21:08:55 +0200317 high = 0;
318 wrmsr(MSR_AMD64_IBSOPCTL, low, high);
319 }
Robert Richter852402c2008-07-22 21:09:06 +0200320#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321}
322
Robert Richter6657fe42008-07-22 21:08:50 +0200323static void op_amd_shutdown(struct op_msrs const * const msrs)
Don Zickuscb9c4482006-09-26 10:52:26 +0200324{
325 int i;
326
Robert Richter4c168ea2008-09-24 11:08:52 +0200327 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100328 if (CTR_IS_RESERVED(msrs, i))
Don Zickuscb9c4482006-09-26 10:52:26 +0200329 release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
330 }
Robert Richter4c168ea2008-09-24 11:08:52 +0200331 for (i = 0 ; i < NUM_CONTROLS ; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100332 if (CTRL_IS_RESERVED(msrs, i))
Don Zickuscb9c4482006-09-26 10:52:26 +0200333 release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
334 }
335}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Robert Richter9fa68122008-11-24 14:21:03 +0100337#ifdef CONFIG_OPROFILE_IBS
Robert Richtera4c408a2008-07-22 21:09:02 +0200338
Robert Richter7d77f2d2008-07-22 21:08:57 +0200339static u8 ibs_eilvt_off;
340
Barry Kasindorf56784f12008-07-22 21:08:55 +0200341static inline void apic_init_ibs_nmi_per_cpu(void *arg)
342{
Robert Richter7d77f2d2008-07-22 21:08:57 +0200343 ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
Barry Kasindorf56784f12008-07-22 21:08:55 +0200344}
345
346static inline void apic_clear_ibs_nmi_per_cpu(void *arg)
347{
348 setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
349}
350
Robert Richterfe615cb2008-11-24 14:58:03 +0100351static int init_ibs_nmi(void)
Robert Richter7d77f2d2008-07-22 21:08:57 +0200352{
353#define IBSCTL_LVTOFFSETVAL (1 << 8)
354#define IBSCTL 0x1cc
355 struct pci_dev *cpu_cfg;
356 int nodes;
357 u32 value = 0;
358
359 /* per CPU setup */
Robert Richterebb535d2008-07-22 21:08:59 +0200360 on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1);
Robert Richter7d77f2d2008-07-22 21:08:57 +0200361
362 nodes = 0;
363 cpu_cfg = NULL;
364 do {
365 cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD,
366 PCI_DEVICE_ID_AMD_10H_NB_MISC,
367 cpu_cfg);
368 if (!cpu_cfg)
369 break;
370 ++nodes;
371 pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
372 | IBSCTL_LVTOFFSETVAL);
373 pci_read_config_dword(cpu_cfg, IBSCTL, &value);
374 if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) {
Robert Richter83bd9242008-12-15 15:09:50 +0100375 pci_dev_put(cpu_cfg);
Robert Richter7d77f2d2008-07-22 21:08:57 +0200376 printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
377 "IBSCTL = 0x%08x", value);
378 return 1;
379 }
380 } while (1);
381
382 if (!nodes) {
383 printk(KERN_DEBUG "No CPU node configured for IBS");
384 return 1;
385 }
386
387#ifdef CONFIG_NUMA
388 /* Sanity check */
389 /* Works only for 64bit with proper numa implementation. */
390 if (nodes != num_possible_nodes()) {
391 printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, "
392 "found: %d, expected %d",
393 nodes, num_possible_nodes());
394 return 1;
395 }
396#endif
397 return 0;
398}
399
Robert Richterfe615cb2008-11-24 14:58:03 +0100400/* uninitialize the APIC for the IBS interrupts if needed */
401static void clear_ibs_nmi(void)
402{
Robert Richterfc81be82008-12-18 00:28:27 +0100403 if (has_ibs)
Robert Richterfe615cb2008-11-24 14:58:03 +0100404 on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
405}
406
Robert Richterfd13f6c2008-10-19 21:00:09 +0200407/* initialize the APIC for the IBS interrupts if available */
Robert Richterfe615cb2008-11-24 14:58:03 +0100408static void ibs_init(void)
Barry Kasindorf56784f12008-07-22 21:08:55 +0200409{
Robert Richterfc81be82008-12-18 00:28:27 +0100410 has_ibs = boot_cpu_has(X86_FEATURE_IBS);
Barry Kasindorf56784f12008-07-22 21:08:55 +0200411
Robert Richterfc81be82008-12-18 00:28:27 +0100412 if (!has_ibs)
Barry Kasindorf56784f12008-07-22 21:08:55 +0200413 return;
414
Robert Richterfe615cb2008-11-24 14:58:03 +0100415 if (init_ibs_nmi()) {
Robert Richterfc81be82008-12-18 00:28:27 +0100416 has_ibs = 0;
Robert Richter852402c2008-07-22 21:09:06 +0200417 return;
418 }
419
420 printk(KERN_INFO "oprofile: AMD IBS detected\n");
Barry Kasindorf56784f12008-07-22 21:08:55 +0200421}
422
Robert Richterfe615cb2008-11-24 14:58:03 +0100423static void ibs_exit(void)
Barry Kasindorf56784f12008-07-22 21:08:55 +0200424{
Robert Richterfc81be82008-12-18 00:28:27 +0100425 if (!has_ibs)
Robert Richterfe615cb2008-11-24 14:58:03 +0100426 return;
427
428 clear_ibs_nmi();
Barry Kasindorf56784f12008-07-22 21:08:55 +0200429}
430
Robert Richter25ad29132008-09-05 17:12:36 +0200431static int (*create_arch_files)(struct super_block *sb, struct dentry *root);
Robert Richter270d3e12008-07-22 21:09:01 +0200432
Robert Richter25ad29132008-09-05 17:12:36 +0200433static int setup_ibs_files(struct super_block *sb, struct dentry *root)
Barry Kasindorf56784f12008-07-22 21:08:55 +0200434{
Barry Kasindorf56784f12008-07-22 21:08:55 +0200435 struct dentry *dir;
Robert Richter270d3e12008-07-22 21:09:01 +0200436 int ret = 0;
437
438 /* architecture specific files */
439 if (create_arch_files)
440 ret = create_arch_files(sb, root);
441
442 if (ret)
443 return ret;
Barry Kasindorf56784f12008-07-22 21:08:55 +0200444
Robert Richterfc81be82008-12-18 00:28:27 +0100445 if (!has_ibs)
Robert Richter270d3e12008-07-22 21:09:01 +0200446 return ret;
447
448 /* model specific files */
Barry Kasindorf56784f12008-07-22 21:08:55 +0200449
450 /* setup some reasonable defaults */
451 ibs_config.max_cnt_fetch = 250000;
452 ibs_config.fetch_enabled = 0;
453 ibs_config.max_cnt_op = 250000;
454 ibs_config.op_enabled = 0;
455 ibs_config.dispatched_ops = 1;
Robert Richter2d55a472008-07-18 17:56:05 +0200456
457 dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
458 oprofilefs_create_ulong(sb, dir, "enable",
459 &ibs_config.fetch_enabled);
460 oprofilefs_create_ulong(sb, dir, "max_count",
461 &ibs_config.max_cnt_fetch);
Barry Kasindorf56784f12008-07-22 21:08:55 +0200462 oprofilefs_create_ulong(sb, dir, "rand_enable",
463 &ibs_config.rand_en);
Robert Richter2d55a472008-07-18 17:56:05 +0200464
Robert Richterccd755c2008-07-29 16:57:10 +0200465 dir = oprofilefs_mkdir(sb, root, "ibs_op");
Barry Kasindorf56784f12008-07-22 21:08:55 +0200466 oprofilefs_create_ulong(sb, dir, "enable",
Robert Richter2d55a472008-07-18 17:56:05 +0200467 &ibs_config.op_enabled);
Barry Kasindorf56784f12008-07-22 21:08:55 +0200468 oprofilefs_create_ulong(sb, dir, "max_count",
Robert Richter2d55a472008-07-18 17:56:05 +0200469 &ibs_config.max_cnt_op);
Barry Kasindorf56784f12008-07-22 21:08:55 +0200470 oprofilefs_create_ulong(sb, dir, "dispatched_ops",
Robert Richter2d55a472008-07-18 17:56:05 +0200471 &ibs_config.dispatched_ops);
Robert Richterfc2bd732008-07-22 21:09:00 +0200472
473 return 0;
Barry Kasindorf56784f12008-07-22 21:08:55 +0200474}
475
Robert Richteradf5ec02008-07-22 21:08:48 +0200476static int op_amd_init(struct oprofile_operations *ops)
477{
Robert Richterfe615cb2008-11-24 14:58:03 +0100478 ibs_init();
Robert Richter270d3e12008-07-22 21:09:01 +0200479 create_arch_files = ops->create_files;
480 ops->create_files = setup_ibs_files;
Robert Richteradf5ec02008-07-22 21:08:48 +0200481 return 0;
482}
483
484static void op_amd_exit(void)
485{
Robert Richterfe615cb2008-11-24 14:58:03 +0100486 ibs_exit();
Robert Richteradf5ec02008-07-22 21:08:48 +0200487}
488
Robert Richter9fa68122008-11-24 14:21:03 +0100489#else
490
491/* no IBS support */
492
493static int op_amd_init(struct oprofile_operations *ops)
494{
495 return 0;
496}
497
498static void op_amd_exit(void) {}
499
500#endif /* CONFIG_OPROFILE_IBS */
Robert Richtera4c408a2008-07-22 21:09:02 +0200501
Robert Richter6657fe42008-07-22 21:08:50 +0200502struct op_x86_model_spec const op_amd_spec = {
Robert Richterc92960f2008-09-05 17:12:36 +0200503 .init = op_amd_init,
504 .exit = op_amd_exit,
505 .num_counters = NUM_COUNTERS,
506 .num_controls = NUM_CONTROLS,
507 .fill_in_addresses = &op_amd_fill_in_addresses,
508 .setup_ctrs = &op_amd_setup_ctrs,
509 .check_ctrs = &op_amd_check_ctrs,
510 .start = &op_amd_start,
511 .stop = &op_amd_stop,
512 .shutdown = &op_amd_shutdown
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513};