]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - arch/x86/oprofile/op_model_athlon.c
x86/oprofile: introduce model specific init/exit functions
[mirror_ubuntu-artful-kernel.git] / arch / x86 / oprofile / op_model_athlon.c
CommitLineData
d4413732 1/*
adf5ec0b 2 * @file op_model_athlon.c
bd87f1f0 3 * athlon / K7 / K8 / Family 10h model-specific MSR operations
1da177e4 4 *
adf5ec0b 5 * @remark Copyright 2002-2008 OProfile authors
1da177e4
LT
6 * @remark Read the file COPYING
7 *
8 * @author John Levon
9 * @author Philippe Elie
10 * @author Graydon Hoare
adf5ec0b
RR
11 * @author Robert Richter <robert.richter@amd.com>
12*/
1da177e4
LT
13
14#include <linux/oprofile.h>
15#include <asm/ptrace.h>
16#include <asm/msr.h>
3e4ff115 17#include <asm/nmi.h>
d4413732 18
1da177e4
LT
19#include "op_x86_model.h"
20#include "op_counter.h"
21
22#define NUM_COUNTERS 4
23#define NUM_CONTROLS 4
24
d4413732
PC
25#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)
1da177e4
LT
28#define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))
29
d4413732
PC
30#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)
1da177e4
LT
33#define CTRL_SET_ACTIVE(n) (n |= (1<<22))
34#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
bd87f1f0
BK
35#define CTRL_CLEAR_LO(x) (x &= (1<<21))
36#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
1da177e4 37#define CTRL_SET_ENABLE(val) (val |= 1<<20)
d4413732
PC
38#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
39#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
1da177e4 40#define CTRL_SET_UM(val, m) (val |= (m << 8))
bd87f1f0
BK
41#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))
1da177e4
LT
45
46static unsigned long reset_value[NUM_COUNTERS];
d4413732 47
1da177e4
LT
48static void athlon_fill_in_addresses(struct op_msrs * const msrs)
49{
cb9c448c
DZ
50 int i;
51
d4413732 52 for (i = 0; i < NUM_COUNTERS; i++) {
cb9c448c
DZ
53 if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
54 msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
55 else
56 msrs->counters[i].addr = 0;
57 }
58
d4413732 59 for (i = 0; i < NUM_CONTROLS; i++) {
cb9c448c
DZ
60 if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
61 msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
62 else
63 msrs->controls[i].addr = 0;
64 }
1da177e4
LT
65}
66
d4413732 67
1da177e4
LT
68static void athlon_setup_ctrs(struct op_msrs const * const msrs)
69{
70 unsigned int low, high;
71 int i;
d4413732 72
1da177e4
LT
73 /* clear all counters */
74 for (i = 0 ; i < NUM_CONTROLS; ++i) {
d4413732 75 if (unlikely(!CTRL_IS_RESERVED(msrs, i)))
cb9c448c 76 continue;
1da177e4 77 CTRL_READ(low, high, msrs, i);
bd87f1f0
BK
78 CTRL_CLEAR_LO(low);
79 CTRL_CLEAR_HI(high);
1da177e4
LT
80 CTRL_WRITE(low, high, msrs, i);
81 }
cb9c448c 82
1da177e4
LT
83 /* avoid a false detection of ctr overflows in NMI handler */
84 for (i = 0; i < NUM_COUNTERS; ++i) {
d4413732 85 if (unlikely(!CTR_IS_RESERVED(msrs, i)))
cb9c448c 86 continue;
1da177e4
LT
87 CTR_WRITE(1, msrs, i);
88 }
89
90 /* enable active counters */
91 for (i = 0; i < NUM_COUNTERS; ++i) {
d4413732 92 if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) {
1da177e4
LT
93 reset_value[i] = counter_config[i].count;
94
95 CTR_WRITE(counter_config[i].count, msrs, i);
96
97 CTRL_READ(low, high, msrs, i);
bd87f1f0
BK
98 CTRL_CLEAR_LO(low);
99 CTRL_CLEAR_HI(high);
1da177e4
LT
100 CTRL_SET_ENABLE(low);
101 CTRL_SET_USR(low, counter_config[i].user);
102 CTRL_SET_KERN(low, counter_config[i].kernel);
103 CTRL_SET_UM(low, counter_config[i].unit_mask);
bd87f1f0
BK
104 CTRL_SET_EVENT_LOW(low, counter_config[i].event);
105 CTRL_SET_EVENT_HIGH(high, counter_config[i].event);
106 CTRL_SET_HOST_ONLY(high, 0);
107 CTRL_SET_GUEST_ONLY(high, 0);
108
1da177e4
LT
109 CTRL_WRITE(low, high, msrs, i);
110 } else {
111 reset_value[i] = 0;
112 }
113 }
114}
115
d4413732 116
1da177e4
LT
117static int athlon_check_ctrs(struct pt_regs * const regs,
118 struct op_msrs const * const msrs)
119{
120 unsigned int low, high;
121 int i;
122
123 for (i = 0 ; i < NUM_COUNTERS; ++i) {
cb9c448c
DZ
124 if (!reset_value[i])
125 continue;
1da177e4
LT
126 CTR_READ(low, high, msrs, i);
127 if (CTR_OVERFLOWED(low)) {
128 oprofile_add_sample(regs, i);
129 CTR_WRITE(reset_value[i], msrs, i);
130 }
131 }
132
133 /* See op_model_ppro.c */
134 return 1;
135}
136
d4413732 137
1da177e4
LT
138static void athlon_start(struct op_msrs const * const msrs)
139{
140 unsigned int low, high;
141 int i;
142 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
143 if (reset_value[i]) {
144 CTRL_READ(low, high, msrs, i);
145 CTRL_SET_ACTIVE(low);
146 CTRL_WRITE(low, high, msrs, i);
147 }
148 }
149}
150
151
152static void athlon_stop(struct op_msrs const * const msrs)
153{
d4413732 154 unsigned int low, high;
1da177e4
LT
155 int i;
156
157 /* Subtle: stop on all counters to avoid race with
158 * setting our pm callback */
159 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
cb9c448c
DZ
160 if (!reset_value[i])
161 continue;
1da177e4
LT
162 CTRL_READ(low, high, msrs, i);
163 CTRL_SET_INACTIVE(low);
164 CTRL_WRITE(low, high, msrs, i);
165 }
166}
167
cb9c448c
DZ
168static void athlon_shutdown(struct op_msrs const * const msrs)
169{
170 int i;
171
172 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
d4413732 173 if (CTR_IS_RESERVED(msrs, i))
cb9c448c
DZ
174 release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
175 }
176 for (i = 0 ; i < NUM_CONTROLS ; ++i) {
d4413732 177 if (CTRL_IS_RESERVED(msrs, i))
cb9c448c
DZ
178 release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
179 }
180}
1da177e4 181
adf5ec0b
RR
182static int op_amd_init(struct oprofile_operations *ops)
183{
184 return 0;
185}
186
187static void op_amd_exit(void)
188{
189}
190
1da177e4 191struct op_x86_model_spec const op_athlon_spec = {
adf5ec0b
RR
192 .init = op_amd_init,
193 .exit = op_amd_exit,
1da177e4
LT
194 .num_counters = NUM_COUNTERS,
195 .num_controls = NUM_CONTROLS,
196 .fill_in_addresses = &athlon_fill_in_addresses,
197 .setup_ctrs = &athlon_setup_ctrs,
198 .check_ctrs = &athlon_check_ctrs,
199 .start = &athlon_start,
cb9c448c
DZ
200 .stop = &athlon_stop,
201 .shutdown = &athlon_shutdown
1da177e4 202};