patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / ppc64 / oprofile / op_model_power4.c
1 /*
2  * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version
7  * 2 of the License, or (at your option) any later version.
8  */
9
10 #include <linux/oprofile.h>
11 #include <linux/init.h>
12 #include <linux/smp.h>
13 #include <asm/ptrace.h>
14 #include <asm/system.h>
15 #include <asm/processor.h>
16 #include <asm/cputable.h>
17 #include <asm/systemcfg.h>
18 #include <asm/rtas.h>
19
20 #define dbg(args...) printk(args)
21
22 #include "op_impl.h"
23
24 static unsigned long reset_value[OP_MAX_COUNTER];
25
26 static int num_counters;
27 static int oprofile_running;
28 static int mmcra_has_sihv;
29
30 static void power4_reg_setup(struct op_counter_config *ctr,
31                              struct op_system_config *sys,
32                              int num_ctrs)
33 {
34         int i;
35
36         num_counters = num_ctrs;
37
38         /*
39          * SIHV / SIPR bits are only implemented on POWER4+ (GQ) and above.
40          * However we disable it on all POWER4 until we verify it works
41          * (I was seeing some strange behaviour last time I tried).
42          *
43          * It has been verified to work on POWER5 so we enable it there.
44          */
45         if (!(__is_processor(PV_POWER4) || __is_processor(PV_POWER4p)))
46                 mmcra_has_sihv = 1;
47
48         for (i = 0; i < num_counters; ++i)
49                 reset_value[i] = 0x80000000UL - ctr[i].count;
50
51         /* XXX setup user and kernel profiling */
52 }
53
54 extern void ppc64_enable_pmcs(void);
55
56 static void power4_cpu_setup(void *unused)
57 {
58         unsigned int mmcr0 = mfspr(SPRN_MMCR0);
59         unsigned long mmcra = mfspr(SPRN_MMCRA);
60
61         ppc64_enable_pmcs();
62
63         /* set the freeze bit */
64         mmcr0 |= MMCR0_FC;
65         mtspr(SPRN_MMCR0, mmcr0);
66
67         mmcr0 |= MMCR0_FCM1|MMCR0_PMXE|MMCR0_FCECE;
68         mmcr0 |= MMCR0_PMC1INTCONTROL|MMCR0_PMCNINTCONTROL;
69         mtspr(SPRN_MMCR0, mmcr0);
70
71         mmcra |= MMCRA_SAMPLE_ENABLE;
72         mtspr(SPRN_MMCRA, mmcra);
73
74         dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
75             mfspr(SPRN_MMCR0));
76         dbg("setup on cpu %d, mmcr1 %lx\n", smp_processor_id(),
77             mfspr(SPRN_MMCR1));
78         dbg("setup on cpu %d, mmcra %lx\n", smp_processor_id(),
79             mfspr(SPRN_MMCRA));
80 }
81
82 static void power4_start(struct op_counter_config *ctr)
83 {
84         int i;
85         unsigned int mmcr0;
86
87         /* set the PMM bit (see comment below) */
88         mtmsrd(mfmsr() | MSR_PMM);
89
90         for (i = 0; i < num_counters; ++i) {
91                 if (ctr[i].enabled) {
92                         ctr_write(i, reset_value[i]);
93                 } else {
94                         ctr_write(i, 0);
95                 }
96         }
97
98         mmcr0 = mfspr(SPRN_MMCR0);
99
100         /*
101          * We must clear the PMAO bit on some (GQ) chips. Just do it
102          * all the time
103          */
104         mmcr0 &= ~MMCR0_PMAO;
105
106         /*
107          * now clear the freeze bit, counting will not start until we
108          * rfid from this excetion, because only at that point will
109          * the PMM bit be cleared
110          */
111         mmcr0 &= ~MMCR0_FC;
112         mtspr(SPRN_MMCR0, mmcr0);
113
114         oprofile_running = 1;
115
116         dbg("start on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
117 }
118
119 static void power4_stop(void)
120 {
121         unsigned int mmcr0;
122
123         /* freeze counters */
124         mmcr0 = mfspr(SPRN_MMCR0);
125         mmcr0 |= MMCR0_FC;
126         mtspr(SPRN_MMCR0, mmcr0);
127
128         oprofile_running = 0;
129
130         dbg("stop on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
131
132         mb();
133 }
134
135 /* Fake functions used by canonicalize_pc */
136 static void __attribute_used__ hypervisor_bucket(void)
137 {
138 }
139
140 static void __attribute_used__ rtas_bucket(void)
141 {
142 }
143
144 static void __attribute_used__ kernel_unknown_bucket(void)
145 {
146 }
147
148 /*
149  * On GQ and newer the MMCRA stores the HV and PR bits at the time
150  * the SIAR was sampled. We use that to work out if the SIAR was sampled in
151  * the hypervisor, our exception vectors or RTAS.
152  */
153 static unsigned long get_pc(void)
154 {
155         unsigned long pc = mfspr(SPRN_SIAR);
156         unsigned long mmcra;
157
158         /* Cant do much about it */
159         if (!mmcra_has_sihv)
160                 return pc;
161
162         mmcra = mfspr(SPRN_MMCRA);
163
164         /* Were we in the hypervisor? */
165         if ((systemcfg->platform == PLATFORM_PSERIES_LPAR) &&
166             (mmcra & MMCRA_SIHV))
167                 /* function descriptor madness */
168                 return *((unsigned long *)hypervisor_bucket);
169
170         /* We were in userspace, nothing to do */
171         if (mmcra & MMCRA_SIPR)
172                 return pc;
173
174         /* Were we in our exception vectors? */
175         if (pc < 0x4000UL)
176                 return (unsigned long)__va(pc);
177
178 #ifdef CONFIG_PPC_PSERIES
179         /* Were we in RTAS? */
180         if (pc >= rtas.base && pc < (rtas.base + rtas.size))
181                 /* function descriptor madness */
182                 return *((unsigned long *)rtas_bucket);
183 #endif
184
185         /* Not sure where we were */
186         if (pc < KERNELBASE)
187                 /* function descriptor madness */
188                 return *((unsigned long *)kernel_unknown_bucket);
189
190         return pc;
191 }
192
193 static int get_kernel(unsigned long pc)
194 {
195         int is_kernel;
196
197         if (!mmcra_has_sihv) {
198                 is_kernel = (pc >= KERNELBASE);
199         } else {
200                 unsigned long mmcra = mfspr(SPRN_MMCRA);
201                 is_kernel = ((mmcra & MMCRA_SIPR) == 0);
202         }
203
204         return is_kernel;
205 }
206
207 static void power4_handle_interrupt(struct pt_regs *regs,
208                                     struct op_counter_config *ctr)
209 {
210         unsigned long pc;
211         int is_kernel;
212         int val;
213         int i;
214         unsigned int cpu = smp_processor_id();
215         unsigned int mmcr0;
216
217         pc = get_pc();
218         is_kernel = get_kernel(pc);
219
220         /* set the PMM bit (see comment below) */
221         mtmsrd(mfmsr() | MSR_PMM);
222
223         for (i = 0; i < num_counters; ++i) {
224                 val = ctr_read(i);
225                 if (val < 0) {
226                         if (oprofile_running && ctr[i].enabled) {
227                                 oprofile_add_sample(pc, is_kernel, i, cpu);
228                                 ctr_write(i, reset_value[i]);
229                         } else {
230                                 ctr_write(i, 0);
231                         }
232                 }
233         }
234
235         mmcr0 = mfspr(SPRN_MMCR0);
236
237         /* reset the perfmon trigger */
238         mmcr0 |= MMCR0_PMXE;
239
240         /*
241          * We must clear the PMAO bit on some (GQ) chips. Just do it
242          * all the time
243          */
244         mmcr0 &= ~MMCR0_PMAO;
245
246         /*
247          * now clear the freeze bit, counting will not start until we
248          * rfid from this exception, because only at that point will
249          * the PMM bit be cleared
250          */
251         mmcr0 &= ~MMCR0_FC;
252         mtspr(SPRN_MMCR0, mmcr0);
253 }
254
255 struct op_ppc64_model op_model_power4 = {
256         .reg_setup              = power4_reg_setup,
257         .cpu_setup              = power4_cpu_setup,
258         .start                  = power4_start,
259         .stop                   = power4_stop,
260         .handle_interrupt       = power4_handle_interrupt,
261 };