aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ppc64/oprofile
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc64/oprofile
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/ppc64/oprofile')
-rw-r--r--arch/ppc64/oprofile/Kconfig23
-rw-r--r--arch/ppc64/oprofile/Makefile9
-rw-r--r--arch/ppc64/oprofile/common.c186
-rw-r--r--arch/ppc64/oprofile/op_impl.h108
-rw-r--r--arch/ppc64/oprofile/op_model_power4.c313
-rw-r--r--arch/ppc64/oprofile/op_model_rs64.c219
6 files changed, 858 insertions, 0 deletions
diff --git a/arch/ppc64/oprofile/Kconfig b/arch/ppc64/oprofile/Kconfig
new file mode 100644
index 000000000000..5ade19801b97
--- /dev/null
+++ b/arch/ppc64/oprofile/Kconfig
@@ -0,0 +1,23 @@
1
2menu "Profiling support"
3 depends on EXPERIMENTAL
4
5config PROFILING
6 bool "Profiling support (EXPERIMENTAL)"
7 help
8 Say Y here to enable the extended profiling support mechanisms used
9 by profilers such as OProfile.
10
11
12config OPROFILE
13 tristate "OProfile system profiling (EXPERIMENTAL)"
14 depends on PROFILING
15 help
16 OProfile is a profiling system capable of profiling the
17 whole system, include the kernel, kernel modules, libraries,
18 and applications.
19
20 If unsure, say N.
21
22endmenu
23
diff --git a/arch/ppc64/oprofile/Makefile b/arch/ppc64/oprofile/Makefile
new file mode 100644
index 000000000000..162dbf06c142
--- /dev/null
+++ b/arch/ppc64/oprofile/Makefile
@@ -0,0 +1,9 @@
1obj-$(CONFIG_OPROFILE) += oprofile.o
2
3DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \
4 oprof.o cpu_buffer.o buffer_sync.o \
5 event_buffer.o oprofile_files.o \
6 oprofilefs.o oprofile_stats.o \
7 timer_int.o )
8
9oprofile-y := $(DRIVER_OBJS) common.o op_model_rs64.o op_model_power4.o
diff --git a/arch/ppc64/oprofile/common.c b/arch/ppc64/oprofile/common.c
new file mode 100644
index 000000000000..b28bfda23d94
--- /dev/null
+++ b/arch/ppc64/oprofile/common.c
@@ -0,0 +1,186 @@
1/*
2 * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
3 *
4 * Based on alpha version.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/oprofile.h>
13#include <linux/init.h>
14#include <linux/smp.h>
15#include <linux/errno.h>
16#include <asm/ptrace.h>
17#include <asm/system.h>
18#include <asm/pmc.h>
19
20#include "op_impl.h"
21
22extern struct op_ppc64_model op_model_rs64;
23extern struct op_ppc64_model op_model_power4;
24static struct op_ppc64_model *model;
25
26static struct op_counter_config ctr[OP_MAX_COUNTER];
27static struct op_system_config sys;
28
29static void op_handle_interrupt(struct pt_regs *regs)
30{
31 model->handle_interrupt(regs, ctr);
32}
33
34static int op_ppc64_setup(void)
35{
36 int err;
37
38 /* Grab the hardware */
39 err = reserve_pmc_hardware(op_handle_interrupt);
40 if (err)
41 return err;
42
43 /* Pre-compute the values to stuff in the hardware registers. */
44 model->reg_setup(ctr, &sys, model->num_counters);
45
46 /* Configure the registers on all cpus. */
47 on_each_cpu(model->cpu_setup, NULL, 0, 1);
48
49 return 0;
50}
51
52static void op_ppc64_shutdown(void)
53{
54 release_pmc_hardware();
55}
56
57static void op_ppc64_cpu_start(void *dummy)
58{
59 model->start(ctr);
60}
61
62static int op_ppc64_start(void)
63{
64 on_each_cpu(op_ppc64_cpu_start, NULL, 0, 1);
65 return 0;
66}
67
68static inline void op_ppc64_cpu_stop(void *dummy)
69{
70 model->stop();
71}
72
73static void op_ppc64_stop(void)
74{
75 on_each_cpu(op_ppc64_cpu_stop, NULL, 0, 1);
76}
77
78static int op_ppc64_create_files(struct super_block *sb, struct dentry *root)
79{
80 int i;
81
82 /*
83 * There is one mmcr0, mmcr1 and mmcra for setting the events for
84 * all of the counters.
85 */
86 oprofilefs_create_ulong(sb, root, "mmcr0", &sys.mmcr0);
87 oprofilefs_create_ulong(sb, root, "mmcr1", &sys.mmcr1);
88 oprofilefs_create_ulong(sb, root, "mmcra", &sys.mmcra);
89
90 for (i = 0; i < model->num_counters; ++i) {
91 struct dentry *dir;
92 char buf[3];
93
94 snprintf(buf, sizeof buf, "%d", i);
95 dir = oprofilefs_mkdir(sb, root, buf);
96
97 oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
98 oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
99 oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
100 /*
101 * We dont support per counter user/kernel selection, but
102 * we leave the entries because userspace expects them
103 */
104 oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
105 oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
106 oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
107 }
108
109 oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel);
110 oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user);
111 oprofilefs_create_ulong(sb, root, "backtrace_spinlocks",
112 &sys.backtrace_spinlocks);
113
114 /* Default to tracing both kernel and user */
115 sys.enable_kernel = 1;
116 sys.enable_user = 1;
117
118 /* Turn on backtracing through spinlocks by default */
119 sys.backtrace_spinlocks = 1;
120
121 return 0;
122}
123
124int __init oprofile_arch_init(struct oprofile_operations *ops)
125{
126 unsigned int pvr;
127
128 pvr = mfspr(SPRN_PVR);
129
130 switch (PVR_VER(pvr)) {
131 case PV_630:
132 case PV_630p:
133 model = &op_model_rs64;
134 model->num_counters = 8;
135 ops->cpu_type = "ppc64/power3";
136 break;
137
138 case PV_NORTHSTAR:
139 case PV_PULSAR:
140 case PV_ICESTAR:
141 case PV_SSTAR:
142 model = &op_model_rs64;
143 model->num_counters = 8;
144 ops->cpu_type = "ppc64/rs64";
145 break;
146
147 case PV_POWER4:
148 case PV_POWER4p:
149 model = &op_model_power4;
150 model->num_counters = 8;
151 ops->cpu_type = "ppc64/power4";
152 break;
153
154 case PV_970:
155 case PV_970FX:
156 model = &op_model_power4;
157 model->num_counters = 8;
158 ops->cpu_type = "ppc64/970";
159 break;
160
161 case PV_POWER5:
162 case PV_POWER5p:
163 model = &op_model_power4;
164 model->num_counters = 6;
165 ops->cpu_type = "ppc64/power5";
166 break;
167
168 default:
169 return -ENODEV;
170 }
171
172 ops->create_files = op_ppc64_create_files;
173 ops->setup = op_ppc64_setup;
174 ops->shutdown = op_ppc64_shutdown;
175 ops->start = op_ppc64_start;
176 ops->stop = op_ppc64_stop;
177
178 printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
179 ops->cpu_type);
180
181 return 0;
182}
183
184void oprofile_arch_exit(void)
185{
186}
diff --git a/arch/ppc64/oprofile/op_impl.h b/arch/ppc64/oprofile/op_impl.h
new file mode 100644
index 000000000000..7fa7eaabc035
--- /dev/null
+++ b/arch/ppc64/oprofile/op_impl.h
@@ -0,0 +1,108 @@
1/*
2 * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
3 *
4 * Based on alpha version.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#ifndef OP_IMPL_H
13#define OP_IMPL_H 1
14
15#define OP_MAX_COUNTER 8
16
17/* Per-counter configuration as set via oprofilefs. */
18struct op_counter_config {
19 unsigned long valid;
20 unsigned long enabled;
21 unsigned long event;
22 unsigned long count;
23 unsigned long kernel;
24 /* We dont support per counter user/kernel selection */
25 unsigned long user;
26 unsigned long unit_mask;
27};
28
29/* System-wide configuration as set via oprofilefs. */
30struct op_system_config {
31 unsigned long mmcr0;
32 unsigned long mmcr1;
33 unsigned long mmcra;
34 unsigned long enable_kernel;
35 unsigned long enable_user;
36 unsigned long backtrace_spinlocks;
37};
38
39/* Per-arch configuration */
40struct op_ppc64_model {
41 void (*reg_setup) (struct op_counter_config *,
42 struct op_system_config *,
43 int num_counters);
44 void (*cpu_setup) (void *);
45 void (*start) (struct op_counter_config *);
46 void (*stop) (void);
47 void (*handle_interrupt) (struct pt_regs *,
48 struct op_counter_config *);
49 int num_counters;
50};
51
52static inline unsigned int ctr_read(unsigned int i)
53{
54 switch(i) {
55 case 0:
56 return mfspr(SPRN_PMC1);
57 case 1:
58 return mfspr(SPRN_PMC2);
59 case 2:
60 return mfspr(SPRN_PMC3);
61 case 3:
62 return mfspr(SPRN_PMC4);
63 case 4:
64 return mfspr(SPRN_PMC5);
65 case 5:
66 return mfspr(SPRN_PMC6);
67 case 6:
68 return mfspr(SPRN_PMC7);
69 case 7:
70 return mfspr(SPRN_PMC8);
71 default:
72 return 0;
73 }
74}
75
76static inline void ctr_write(unsigned int i, unsigned int val)
77{
78 switch(i) {
79 case 0:
80 mtspr(SPRN_PMC1, val);
81 break;
82 case 1:
83 mtspr(SPRN_PMC2, val);
84 break;
85 case 2:
86 mtspr(SPRN_PMC3, val);
87 break;
88 case 3:
89 mtspr(SPRN_PMC4, val);
90 break;
91 case 4:
92 mtspr(SPRN_PMC5, val);
93 break;
94 case 5:
95 mtspr(SPRN_PMC6, val);
96 break;
97 case 6:
98 mtspr(SPRN_PMC7, val);
99 break;
100 case 7:
101 mtspr(SPRN_PMC8, val);
102 break;
103 default:
104 break;
105 }
106}
107
108#endif
diff --git a/arch/ppc64/oprofile/op_model_power4.c b/arch/ppc64/oprofile/op_model_power4.c
new file mode 100644
index 000000000000..3d103d66870d
--- /dev/null
+++ b/arch/ppc64/oprofile/op_model_power4.c
@@ -0,0 +1,313 @@
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...)
21
22#include "op_impl.h"
23
24static unsigned long reset_value[OP_MAX_COUNTER];
25
26static int num_counters;
27static int oprofile_running;
28static int mmcra_has_sihv;
29
30/* mmcr values are set in power4_reg_setup, used in power4_cpu_setup */
31static u32 mmcr0_val;
32static u64 mmcr1_val;
33static u32 mmcra_val;
34
35/*
36 * Since we do not have an NMI, backtracing through spinlocks is
37 * only a best guess. In light of this, allow it to be disabled at
38 * runtime.
39 */
40static int backtrace_spinlocks;
41
42static void power4_reg_setup(struct op_counter_config *ctr,
43 struct op_system_config *sys,
44 int num_ctrs)
45{
46 int i;
47
48 num_counters = num_ctrs;
49
50 /*
51 * SIHV / SIPR bits are only implemented on POWER4+ (GQ) and above.
52 * However we disable it on all POWER4 until we verify it works
53 * (I was seeing some strange behaviour last time I tried).
54 *
55 * It has been verified to work on POWER5 so we enable it there.
56 */
57 if (cpu_has_feature(CPU_FTR_MMCRA_SIHV))
58 mmcra_has_sihv = 1;
59
60 /*
61 * The performance counter event settings are given in the mmcr0,
62 * mmcr1 and mmcra values passed from the user in the
63 * op_system_config structure (sys variable).
64 */
65 mmcr0_val = sys->mmcr0;
66 mmcr1_val = sys->mmcr1;
67 mmcra_val = sys->mmcra;
68
69 backtrace_spinlocks = sys->backtrace_spinlocks;
70
71 for (i = 0; i < num_counters; ++i)
72 reset_value[i] = 0x80000000UL - ctr[i].count;
73
74 /* setup user and kernel profiling */
75 if (sys->enable_kernel)
76 mmcr0_val &= ~MMCR0_KERNEL_DISABLE;
77 else
78 mmcr0_val |= MMCR0_KERNEL_DISABLE;
79
80 if (sys->enable_user)
81 mmcr0_val &= ~MMCR0_PROBLEM_DISABLE;
82 else
83 mmcr0_val |= MMCR0_PROBLEM_DISABLE;
84}
85
86extern void ppc64_enable_pmcs(void);
87
88static void power4_cpu_setup(void *unused)
89{
90 unsigned int mmcr0 = mmcr0_val;
91 unsigned long mmcra = mmcra_val;
92
93 ppc64_enable_pmcs();
94
95 /* set the freeze bit */
96 mmcr0 |= MMCR0_FC;
97 mtspr(SPRN_MMCR0, mmcr0);
98
99 mmcr0 |= MMCR0_FCM1|MMCR0_PMXE|MMCR0_FCECE;
100 mmcr0 |= MMCR0_PMC1CE|MMCR0_PMCjCE;
101 mtspr(SPRN_MMCR0, mmcr0);
102
103 mtspr(SPRN_MMCR1, mmcr1_val);
104
105 mmcra |= MMCRA_SAMPLE_ENABLE;
106 mtspr(SPRN_MMCRA, mmcra);
107
108 dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
109 mfspr(SPRN_MMCR0));
110 dbg("setup on cpu %d, mmcr1 %lx\n", smp_processor_id(),
111 mfspr(SPRN_MMCR1));
112 dbg("setup on cpu %d, mmcra %lx\n", smp_processor_id(),
113 mfspr(SPRN_MMCRA));
114}
115
116static void power4_start(struct op_counter_config *ctr)
117{
118 int i;
119 unsigned int mmcr0;
120
121 /* set the PMM bit (see comment below) */
122 mtmsrd(mfmsr() | MSR_PMM);
123
124 for (i = 0; i < num_counters; ++i) {
125 if (ctr[i].enabled) {
126 ctr_write(i, reset_value[i]);
127 } else {
128 ctr_write(i, 0);
129 }
130 }
131
132 mmcr0 = mfspr(SPRN_MMCR0);
133
134 /*
135 * We must clear the PMAO bit on some (GQ) chips. Just do it
136 * all the time
137 */
138 mmcr0 &= ~MMCR0_PMAO;
139
140 /*
141 * now clear the freeze bit, counting will not start until we
142 * rfid from this excetion, because only at that point will
143 * the PMM bit be cleared
144 */
145 mmcr0 &= ~MMCR0_FC;
146 mtspr(SPRN_MMCR0, mmcr0);
147
148 oprofile_running = 1;
149
150 dbg("start on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
151}
152
153static void power4_stop(void)
154{
155 unsigned int mmcr0;
156
157 /* freeze counters */
158 mmcr0 = mfspr(SPRN_MMCR0);
159 mmcr0 |= MMCR0_FC;
160 mtspr(SPRN_MMCR0, mmcr0);
161
162 oprofile_running = 0;
163
164 dbg("stop on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
165
166 mb();
167}
168
169/* Fake functions used by canonicalize_pc */
170static void __attribute_used__ hypervisor_bucket(void)
171{
172}
173
174static void __attribute_used__ rtas_bucket(void)
175{
176}
177
178static void __attribute_used__ kernel_unknown_bucket(void)
179{
180}
181
182static unsigned long check_spinlock_pc(struct pt_regs *regs,
183 unsigned long profile_pc)
184{
185 unsigned long pc = instruction_pointer(regs);
186
187 /*
188 * If both the SIAR (sampled instruction) and the perfmon exception
189 * occurred in a spinlock region then we account the sample to the
190 * calling function. This isnt 100% correct, we really need soft
191 * IRQ disable so we always get the perfmon exception at the
192 * point at which the SIAR is set.
193 */
194 if (backtrace_spinlocks && in_lock_functions(pc) &&
195 in_lock_functions(profile_pc))
196 return regs->link;
197 else
198 return profile_pc;
199}
200
201/*
202 * On GQ and newer the MMCRA stores the HV and PR bits at the time
203 * the SIAR was sampled. We use that to work out if the SIAR was sampled in
204 * the hypervisor, our exception vectors or RTAS.
205 */
206static unsigned long get_pc(struct pt_regs *regs)
207{
208 unsigned long pc = mfspr(SPRN_SIAR);
209 unsigned long mmcra;
210
211 /* Cant do much about it */
212 if (!mmcra_has_sihv)
213 return check_spinlock_pc(regs, pc);
214
215 mmcra = mfspr(SPRN_MMCRA);
216
217 /* Were we in the hypervisor? */
218 if ((systemcfg->platform == PLATFORM_PSERIES_LPAR) &&
219 (mmcra & MMCRA_SIHV))
220 /* function descriptor madness */
221 return *((unsigned long *)hypervisor_bucket);
222
223 /* We were in userspace, nothing to do */
224 if (mmcra & MMCRA_SIPR)
225 return pc;
226
227#ifdef CONFIG_PPC_RTAS
228 /* Were we in RTAS? */
229 if (pc >= rtas.base && pc < (rtas.base + rtas.size))
230 /* function descriptor madness */
231 return *((unsigned long *)rtas_bucket);
232#endif
233
234 /* Were we in our exception vectors or SLB real mode miss handler? */
235 if (pc < 0x1000000UL)
236 return (unsigned long)__va(pc);
237
238 /* Not sure where we were */
239 if (pc < KERNELBASE)
240 /* function descriptor madness */
241 return *((unsigned long *)kernel_unknown_bucket);
242
243 return check_spinlock_pc(regs, pc);
244}
245
246static int get_kernel(unsigned long pc)
247{
248 int is_kernel;
249
250 if (!mmcra_has_sihv) {
251 is_kernel = (pc >= KERNELBASE);
252 } else {
253 unsigned long mmcra = mfspr(SPRN_MMCRA);
254 is_kernel = ((mmcra & MMCRA_SIPR) == 0);
255 }
256
257 return is_kernel;
258}
259
260static void power4_handle_interrupt(struct pt_regs *regs,
261 struct op_counter_config *ctr)
262{
263 unsigned long pc;
264 int is_kernel;
265 int val;
266 int i;
267 unsigned int mmcr0;
268
269 pc = get_pc(regs);
270 is_kernel = get_kernel(pc);
271
272 /* set the PMM bit (see comment below) */
273 mtmsrd(mfmsr() | MSR_PMM);
274
275 for (i = 0; i < num_counters; ++i) {
276 val = ctr_read(i);
277 if (val < 0) {
278 if (oprofile_running && ctr[i].enabled) {
279 oprofile_add_pc(pc, is_kernel, i);
280 ctr_write(i, reset_value[i]);
281 } else {
282 ctr_write(i, 0);
283 }
284 }
285 }
286
287 mmcr0 = mfspr(SPRN_MMCR0);
288
289 /* reset the perfmon trigger */
290 mmcr0 |= MMCR0_PMXE;
291
292 /*
293 * We must clear the PMAO bit on some (GQ) chips. Just do it
294 * all the time
295 */
296 mmcr0 &= ~MMCR0_PMAO;
297
298 /*
299 * now clear the freeze bit, counting will not start until we
300 * rfid from this exception, because only at that point will
301 * the PMM bit be cleared
302 */
303 mmcr0 &= ~MMCR0_FC;
304 mtspr(SPRN_MMCR0, mmcr0);
305}
306
307struct op_ppc64_model op_model_power4 = {
308 .reg_setup = power4_reg_setup,
309 .cpu_setup = power4_cpu_setup,
310 .start = power4_start,
311 .stop = power4_stop,
312 .handle_interrupt = power4_handle_interrupt,
313};
diff --git a/arch/ppc64/oprofile/op_model_rs64.c b/arch/ppc64/oprofile/op_model_rs64.c
new file mode 100644
index 000000000000..bcec506c266a
--- /dev/null
+++ b/arch/ppc64/oprofile/op_model_rs64.c
@@ -0,0 +1,219 @@
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
18#define dbg(args...)
19
20#include "op_impl.h"
21
22static void ctrl_write(unsigned int i, unsigned int val)
23{
24 unsigned int tmp = 0;
25 unsigned long shift = 0, mask = 0;
26
27 dbg("ctrl_write %d %x\n", i, val);
28
29 switch(i) {
30 case 0:
31 tmp = mfspr(SPRN_MMCR0);
32 shift = 6;
33 mask = 0x7F;
34 break;
35 case 1:
36 tmp = mfspr(SPRN_MMCR0);
37 shift = 0;
38 mask = 0x3F;
39 break;
40 case 2:
41 tmp = mfspr(SPRN_MMCR1);
42 shift = 31 - 4;
43 mask = 0x1F;
44 break;
45 case 3:
46 tmp = mfspr(SPRN_MMCR1);
47 shift = 31 - 9;
48 mask = 0x1F;
49 break;
50 case 4:
51 tmp = mfspr(SPRN_MMCR1);
52 shift = 31 - 14;
53 mask = 0x1F;
54 break;
55 case 5:
56 tmp = mfspr(SPRN_MMCR1);
57 shift = 31 - 19;
58 mask = 0x1F;
59 break;
60 case 6:
61 tmp = mfspr(SPRN_MMCR1);
62 shift = 31 - 24;
63 mask = 0x1F;
64 break;
65 case 7:
66 tmp = mfspr(SPRN_MMCR1);
67 shift = 31 - 28;
68 mask = 0xF;
69 break;
70 }
71
72 tmp = tmp & ~(mask << shift);
73 tmp |= val << shift;
74
75 switch(i) {
76 case 0:
77 case 1:
78 mtspr(SPRN_MMCR0, tmp);
79 break;
80 default:
81 mtspr(SPRN_MMCR1, tmp);
82 }
83
84 dbg("ctrl_write mmcr0 %lx mmcr1 %lx\n", mfspr(SPRN_MMCR0),
85 mfspr(SPRN_MMCR1));
86}
87
88static unsigned long reset_value[OP_MAX_COUNTER];
89
90static int num_counters;
91
92static void rs64_reg_setup(struct op_counter_config *ctr,
93 struct op_system_config *sys,
94 int num_ctrs)
95{
96 int i;
97
98 num_counters = num_ctrs;
99
100 for (i = 0; i < num_counters; ++i)
101 reset_value[i] = 0x80000000UL - ctr[i].count;
102
103 /* XXX setup user and kernel profiling */
104}
105
106static void rs64_cpu_setup(void *unused)
107{
108 unsigned int mmcr0;
109
110 /* reset MMCR0 and set the freeze bit */
111 mmcr0 = MMCR0_FC;
112 mtspr(SPRN_MMCR0, mmcr0);
113
114 /* reset MMCR1, MMCRA */
115 mtspr(SPRN_MMCR1, 0);
116
117 if (cpu_has_feature(CPU_FTR_MMCRA))
118 mtspr(SPRN_MMCRA, 0);
119
120 mmcr0 |= MMCR0_FCM1|MMCR0_PMXE|MMCR0_FCECE;
121 /* Only applies to POWER3, but should be safe on RS64 */
122 mmcr0 |= MMCR0_PMC1CE|MMCR0_PMCjCE;
123 mtspr(SPRN_MMCR0, mmcr0);
124
125 dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
126 mfspr(SPRN_MMCR0));
127 dbg("setup on cpu %d, mmcr1 %lx\n", smp_processor_id(),
128 mfspr(SPRN_MMCR1));
129}
130
131static void rs64_start(struct op_counter_config *ctr)
132{
133 int i;
134 unsigned int mmcr0;
135
136 /* set the PMM bit (see comment below) */
137 mtmsrd(mfmsr() | MSR_PMM);
138
139 for (i = 0; i < num_counters; ++i) {
140 if (ctr[i].enabled) {
141 ctr_write(i, reset_value[i]);
142 ctrl_write(i, ctr[i].event);
143 } else {
144 ctr_write(i, 0);
145 }
146 }
147
148 mmcr0 = mfspr(SPRN_MMCR0);
149
150 /*
151 * now clear the freeze bit, counting will not start until we
152 * rfid from this excetion, because only at that point will
153 * the PMM bit be cleared
154 */
155 mmcr0 &= ~MMCR0_FC;
156 mtspr(SPRN_MMCR0, mmcr0);
157
158 dbg("start on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
159}
160
161static void rs64_stop(void)
162{
163 unsigned int mmcr0;
164
165 /* freeze counters */
166 mmcr0 = mfspr(SPRN_MMCR0);
167 mmcr0 |= MMCR0_FC;
168 mtspr(SPRN_MMCR0, mmcr0);
169
170 dbg("stop on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
171
172 mb();
173}
174
175static void rs64_handle_interrupt(struct pt_regs *regs,
176 struct op_counter_config *ctr)
177{
178 unsigned int mmcr0;
179 int val;
180 int i;
181 unsigned long pc = mfspr(SPRN_SIAR);
182 int is_kernel = (pc >= KERNELBASE);
183
184 /* set the PMM bit (see comment below) */
185 mtmsrd(mfmsr() | MSR_PMM);
186
187 for (i = 0; i < num_counters; ++i) {
188 val = ctr_read(i);
189 if (val < 0) {
190 if (ctr[i].enabled) {
191 oprofile_add_pc(pc, is_kernel, i);
192 ctr_write(i, reset_value[i]);
193 } else {
194 ctr_write(i, 0);
195 }
196 }
197 }
198
199 mmcr0 = mfspr(SPRN_MMCR0);
200
201 /* reset the perfmon trigger */
202 mmcr0 |= MMCR0_PMXE;
203
204 /*
205 * now clear the freeze bit, counting will not start until we
206 * rfid from this exception, because only at that point will
207 * the PMM bit be cleared
208 */
209 mmcr0 &= ~MMCR0_FC;
210 mtspr(SPRN_MMCR0, mmcr0);
211}
212
213struct op_ppc64_model op_model_rs64 = {
214 .reg_setup = rs64_reg_setup,
215 .cpu_setup = rs64_cpu_setup,
216 .start = rs64_start,
217 .stop = rs64_stop,
218 .handle_interrupt = rs64_handle_interrupt,
219};