diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/i386/oprofile/op_model_ppro.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/i386/oprofile/op_model_ppro.c')
-rw-r--r-- | arch/i386/oprofile/op_model_ppro.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/arch/i386/oprofile/op_model_ppro.c b/arch/i386/oprofile/op_model_ppro.c new file mode 100644 index 000000000000..d719015fc044 --- /dev/null +++ b/arch/i386/oprofile/op_model_ppro.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /** | ||
2 | * @file op_model_ppro.h | ||
3 | * pentium pro / P6 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/apic.h> | ||
17 | |||
18 | #include "op_x86_model.h" | ||
19 | #include "op_counter.h" | ||
20 | |||
21 | #define NUM_COUNTERS 2 | ||
22 | #define NUM_CONTROLS 2 | ||
23 | |||
24 | #define CTR_READ(l,h,msrs,c) do {rdmsr(msrs->counters[(c)].addr, (l), (h));} while (0) | ||
25 | #define CTR_WRITE(l,msrs,c) do {wrmsr(msrs->counters[(c)].addr, -(u32)(l), -1);} while (0) | ||
26 | #define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) | ||
27 | |||
28 | #define CTRL_READ(l,h,msrs,c) do {rdmsr((msrs->controls[(c)].addr), (l), (h));} while (0) | ||
29 | #define CTRL_WRITE(l,h,msrs,c) do {wrmsr((msrs->controls[(c)].addr), (l), (h));} while (0) | ||
30 | #define CTRL_SET_ACTIVE(n) (n |= (1<<22)) | ||
31 | #define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) | ||
32 | #define CTRL_CLEAR(x) (x &= (1<<21)) | ||
33 | #define CTRL_SET_ENABLE(val) (val |= 1<<20) | ||
34 | #define CTRL_SET_USR(val,u) (val |= ((u & 1) << 16)) | ||
35 | #define CTRL_SET_KERN(val,k) (val |= ((k & 1) << 17)) | ||
36 | #define CTRL_SET_UM(val, m) (val |= (m << 8)) | ||
37 | #define CTRL_SET_EVENT(val, e) (val |= e) | ||
38 | |||
39 | static unsigned long reset_value[NUM_COUNTERS]; | ||
40 | |||
41 | static void ppro_fill_in_addresses(struct op_msrs * const msrs) | ||
42 | { | ||
43 | msrs->counters[0].addr = MSR_P6_PERFCTR0; | ||
44 | msrs->counters[1].addr = MSR_P6_PERFCTR1; | ||
45 | |||
46 | msrs->controls[0].addr = MSR_P6_EVNTSEL0; | ||
47 | msrs->controls[1].addr = MSR_P6_EVNTSEL1; | ||
48 | } | ||
49 | |||
50 | |||
51 | static void ppro_setup_ctrs(struct op_msrs const * const msrs) | ||
52 | { | ||
53 | unsigned int low, high; | ||
54 | int i; | ||
55 | |||
56 | /* clear all counters */ | ||
57 | for (i = 0 ; i < NUM_CONTROLS; ++i) { | ||
58 | CTRL_READ(low, high, msrs, i); | ||
59 | CTRL_CLEAR(low); | ||
60 | CTRL_WRITE(low, high, msrs, i); | ||
61 | } | ||
62 | |||
63 | /* avoid a false detection of ctr overflows in NMI handler */ | ||
64 | for (i = 0; i < NUM_COUNTERS; ++i) { | ||
65 | CTR_WRITE(1, msrs, i); | ||
66 | } | ||
67 | |||
68 | /* enable active counters */ | ||
69 | for (i = 0; i < NUM_COUNTERS; ++i) { | ||
70 | if (counter_config[i].enabled) { | ||
71 | reset_value[i] = counter_config[i].count; | ||
72 | |||
73 | CTR_WRITE(counter_config[i].count, msrs, i); | ||
74 | |||
75 | CTRL_READ(low, high, msrs, i); | ||
76 | CTRL_CLEAR(low); | ||
77 | CTRL_SET_ENABLE(low); | ||
78 | CTRL_SET_USR(low, counter_config[i].user); | ||
79 | CTRL_SET_KERN(low, counter_config[i].kernel); | ||
80 | CTRL_SET_UM(low, counter_config[i].unit_mask); | ||
81 | CTRL_SET_EVENT(low, counter_config[i].event); | ||
82 | CTRL_WRITE(low, high, msrs, i); | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | |||
88 | static int ppro_check_ctrs(struct pt_regs * const regs, | ||
89 | struct op_msrs const * const msrs) | ||
90 | { | ||
91 | unsigned int low, high; | ||
92 | int i; | ||
93 | |||
94 | for (i = 0 ; i < NUM_COUNTERS; ++i) { | ||
95 | CTR_READ(low, high, msrs, i); | ||
96 | if (CTR_OVERFLOWED(low)) { | ||
97 | oprofile_add_sample(regs, i); | ||
98 | CTR_WRITE(reset_value[i], msrs, i); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | /* Only P6 based Pentium M need to re-unmask the apic vector but it | ||
103 | * doesn't hurt other P6 variant */ | ||
104 | apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED); | ||
105 | |||
106 | /* We can't work out if we really handled an interrupt. We | ||
107 | * might have caught a *second* counter just after overflowing | ||
108 | * the interrupt for this counter then arrives | ||
109 | * and we don't find a counter that's overflowed, so we | ||
110 | * would return 0 and get dazed + confused. Instead we always | ||
111 | * assume we found an overflow. This sucks. | ||
112 | */ | ||
113 | return 1; | ||
114 | } | ||
115 | |||
116 | |||
117 | static void ppro_start(struct op_msrs const * const msrs) | ||
118 | { | ||
119 | unsigned int low,high; | ||
120 | CTRL_READ(low, high, msrs, 0); | ||
121 | CTRL_SET_ACTIVE(low); | ||
122 | CTRL_WRITE(low, high, msrs, 0); | ||
123 | } | ||
124 | |||
125 | |||
126 | static void ppro_stop(struct op_msrs const * const msrs) | ||
127 | { | ||
128 | unsigned int low,high; | ||
129 | CTRL_READ(low, high, msrs, 0); | ||
130 | CTRL_SET_INACTIVE(low); | ||
131 | CTRL_WRITE(low, high, msrs, 0); | ||
132 | } | ||
133 | |||
134 | |||
135 | struct op_x86_model_spec const op_ppro_spec = { | ||
136 | .num_counters = NUM_COUNTERS, | ||
137 | .num_controls = NUM_CONTROLS, | ||
138 | .fill_in_addresses = &ppro_fill_in_addresses, | ||
139 | .setup_ctrs = &ppro_setup_ctrs, | ||
140 | .check_ctrs = &ppro_check_ctrs, | ||
141 | .start = &ppro_start, | ||
142 | .stop = &ppro_stop | ||
143 | }; | ||