blob: d25d7f195036b3fbcfa824b2eb77bbfd376560da [file] [log] [blame]
Paolo Ciarrocchid4413732008-02-19 23:51:27 +01001/*
Robert Richteradf5ec02008-07-22 21:08:48 +02002 * @file op_model_athlon.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 Richteradf5ec02008-07-22 21:08:48 +02005 * @remark Copyright 2002-2008 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>
12*/
Linus Torvalds1da177e2005-04-16 15:20:36 -070013
14#include <linux/oprofile.h>
15#include <asm/ptrace.h>
16#include <asm/msr.h>
Don Zickus3e4ff112006-06-26 13:57:01 +020017#include <asm/nmi.h>
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010018
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include "op_x86_model.h"
20#include "op_counter.h"
21
22#define NUM_COUNTERS 4
23#define NUM_CONTROLS 4
24
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010025#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0)
26#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0)
27#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 -070028#define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))
29
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010030#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0)
31#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
32#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#define CTRL_SET_ACTIVE(n) (n |= (1<<22))
34#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +010035#define CTRL_CLEAR_LO(x) (x &= (1<<21))
36#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#define CTRL_SET_ENABLE(val) (val |= 1<<20)
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010038#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
39#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#define CTRL_SET_UM(val, m) (val |= (m << 8))
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +010041#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff))
42#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf))
43#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9))
44#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8))
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
46static unsigned long reset_value[NUM_COUNTERS];
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010047
Robert Richterdfa15422008-07-22 21:08:49 +020048/* functions for op_athlon_spec */
49
Linus Torvalds1da177e2005-04-16 15:20:36 -070050static void athlon_fill_in_addresses(struct op_msrs * const msrs)
51{
Don Zickuscb9c4482006-09-26 10:52:26 +020052 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010054 for (i = 0; i < NUM_COUNTERS; i++) {
Don Zickuscb9c4482006-09-26 10:52:26 +020055 if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
56 msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
57 else
58 msrs->counters[i].addr = 0;
59 }
60
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010061 for (i = 0; i < NUM_CONTROLS; i++) {
Don Zickuscb9c4482006-09-26 10:52:26 +020062 if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
63 msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
64 else
65 msrs->controls[i].addr = 0;
66 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070067}
68
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010069
Linus Torvalds1da177e2005-04-16 15:20:36 -070070static void athlon_setup_ctrs(struct op_msrs const * const msrs)
71{
72 unsigned int low, high;
73 int i;
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010074
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 /* clear all counters */
76 for (i = 0 ; i < NUM_CONTROLS; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010077 if (unlikely(!CTRL_IS_RESERVED(msrs, i)))
Don Zickuscb9c4482006-09-26 10:52:26 +020078 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 CTRL_READ(low, high, msrs, i);
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +010080 CTRL_CLEAR_LO(low);
81 CTRL_CLEAR_HI(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 CTRL_WRITE(low, high, msrs, i);
83 }
Don Zickuscb9c4482006-09-26 10:52:26 +020084
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 /* avoid a false detection of ctr overflows in NMI handler */
86 for (i = 0; i < NUM_COUNTERS; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010087 if (unlikely(!CTR_IS_RESERVED(msrs, i)))
Don Zickuscb9c4482006-09-26 10:52:26 +020088 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 CTR_WRITE(1, msrs, i);
90 }
91
92 /* enable active counters */
93 for (i = 0; i < NUM_COUNTERS; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +010094 if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 reset_value[i] = counter_config[i].count;
96
97 CTR_WRITE(counter_config[i].count, msrs, i);
98
99 CTRL_READ(low, high, msrs, i);
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +0100100 CTRL_CLEAR_LO(low);
101 CTRL_CLEAR_HI(high);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 CTRL_SET_ENABLE(low);
103 CTRL_SET_USR(low, counter_config[i].user);
104 CTRL_SET_KERN(low, counter_config[i].kernel);
105 CTRL_SET_UM(low, counter_config[i].unit_mask);
Barry Kasindorfbd87f1f2007-12-18 18:05:58 +0100106 CTRL_SET_EVENT_LOW(low, counter_config[i].event);
107 CTRL_SET_EVENT_HIGH(high, counter_config[i].event);
108 CTRL_SET_HOST_ONLY(high, 0);
109 CTRL_SET_GUEST_ONLY(high, 0);
110
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 CTRL_WRITE(low, high, msrs, i);
112 } else {
113 reset_value[i] = 0;
114 }
115 }
116}
117
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119static int athlon_check_ctrs(struct pt_regs * const regs,
120 struct op_msrs const * const msrs)
121{
122 unsigned int low, high;
123 int i;
124
125 for (i = 0 ; i < NUM_COUNTERS; ++i) {
Don Zickuscb9c4482006-09-26 10:52:26 +0200126 if (!reset_value[i])
127 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 CTR_READ(low, high, msrs, i);
129 if (CTR_OVERFLOWED(low)) {
130 oprofile_add_sample(regs, i);
131 CTR_WRITE(reset_value[i], msrs, i);
132 }
133 }
134
135 /* See op_model_ppro.c */
136 return 1;
137}
138
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100139
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140static void athlon_start(struct op_msrs const * const msrs)
141{
142 unsigned int low, high;
143 int i;
144 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
145 if (reset_value[i]) {
146 CTRL_READ(low, high, msrs, i);
147 CTRL_SET_ACTIVE(low);
148 CTRL_WRITE(low, high, msrs, i);
149 }
150 }
151}
152
153
154static void athlon_stop(struct op_msrs const * const msrs)
155{
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100156 unsigned int low, high;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 int i;
158
159 /* Subtle: stop on all counters to avoid race with
160 * setting our pm callback */
161 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
Don Zickuscb9c4482006-09-26 10:52:26 +0200162 if (!reset_value[i])
163 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 CTRL_READ(low, high, msrs, i);
165 CTRL_SET_INACTIVE(low);
166 CTRL_WRITE(low, high, msrs, i);
167 }
168}
169
Don Zickuscb9c4482006-09-26 10:52:26 +0200170static void athlon_shutdown(struct op_msrs const * const msrs)
171{
172 int i;
173
174 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100175 if (CTR_IS_RESERVED(msrs, i))
Don Zickuscb9c4482006-09-26 10:52:26 +0200176 release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
177 }
178 for (i = 0 ; i < NUM_CONTROLS ; ++i) {
Paolo Ciarrocchid4413732008-02-19 23:51:27 +0100179 if (CTRL_IS_RESERVED(msrs, i))
Don Zickuscb9c4482006-09-26 10:52:26 +0200180 release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
181 }
182}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
Robert Richteradf5ec02008-07-22 21:08:48 +0200184static int op_amd_init(struct oprofile_operations *ops)
185{
186 return 0;
187}
188
189static void op_amd_exit(void)
190{
191}
192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193struct op_x86_model_spec const op_athlon_spec = {
Robert Richteradf5ec02008-07-22 21:08:48 +0200194 .init = op_amd_init,
195 .exit = op_amd_exit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 .num_counters = NUM_COUNTERS,
197 .num_controls = NUM_CONTROLS,
198 .fill_in_addresses = &athlon_fill_in_addresses,
199 .setup_ctrs = &athlon_setup_ctrs,
200 .check_ctrs = &athlon_check_ctrs,
201 .start = &athlon_start,
Don Zickuscb9c4482006-09-26 10:52:26 +0200202 .stop = &athlon_stop,
203 .shutdown = &athlon_shutdown
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204};