aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/oprofile/common.c214
-rw-r--r--arch/powerpc/oprofile/op_model_fsl_booke.c183
-rw-r--r--arch/powerpc/oprofile/op_model_power4.c309
-rw-r--r--arch/powerpc/oprofile/op_model_rs64.c218
4 files changed, 924 insertions, 0 deletions
diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c
new file mode 100644
index 000000000000..486314a0defd
--- /dev/null
+++ b/arch/powerpc/oprofile/common.c
@@ -0,0 +1,214 @@
1/*
2 * PPC 64 oprofile support:
3 * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
4 * PPC 32 oprofile support: (based on PPC 64 support)
5 * Copyright (C) Freescale Semiconductor, Inc 2004
6 * Author: Andy Fleming
7 *
8 * Based on alpha version.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version
13 * 2 of the License, or (at your option) any later version.
14 */
15
16#include <linux/oprofile.h>
17#ifndef __powerpc64__
18#include <linux/slab.h>
19#endif /* ! __powerpc64__ */
20#include <linux/init.h>
21#include <linux/smp.h>
22#include <linux/errno.h>
23#include <asm/ptrace.h>
24#include <asm/system.h>
25#ifdef __powerpc64__
26#include <asm/pmc.h>
27#else /* __powerpc64__ */
28#include <asm/perfmon.h>
29#endif /* __powerpc64__ */
30#include <asm/cputable.h>
31#include <asm/oprofile_impl.h>
32
33static struct op_powerpc_model *model;
34
35static struct op_counter_config ctr[OP_MAX_COUNTER];
36static struct op_system_config sys;
37
38#ifndef __powerpc64__
39static char *cpu_type;
40#endif /* ! __powerpc64__ */
41
42static void op_handle_interrupt(struct pt_regs *regs)
43{
44 model->handle_interrupt(regs, ctr);
45}
46
47static int op_powerpc_setup(void)
48{
49#ifdef __powerpc64__
50 int err;
51
52 /* Grab the hardware */
53 err = reserve_pmc_hardware(op_handle_interrupt);
54 if (err)
55 return err;
56#else /* __powerpc64__ */
57 /* Install our interrupt handler into the existing hook. */
58 if (request_perfmon_irq(&op_handle_interrupt))
59 return -EBUSY;
60 mb();
61#endif /* __powerpc64__ */
62
63 /* Pre-compute the values to stuff in the hardware registers. */
64 model->reg_setup(ctr, &sys, model->num_counters);
65
66 /* Configure the registers on all cpus. */
67#ifdef __powerpc64__
68 on_each_cpu(model->cpu_setup, NULL, 0, 1);
69#else /* __powerpc64__ */
70#if 0
71 /* FIXME: Make multi-cpu work */
72 on_each_cpu(model->reg_setup, NULL, 0, 1);
73#endif
74#endif /* __powerpc64__ */
75
76 return 0;
77}
78
79static void op_powerpc_shutdown(void)
80{
81#ifdef __powerpc64__
82 release_pmc_hardware();
83#else /* __powerpc64__ */
84 mb();
85 /* Remove our interrupt handler. We may be removing this module. */
86 free_perfmon_irq();
87#endif /* __powerpc64__ */
88}
89
90static void op_powerpc_cpu_start(void *dummy)
91{
92 model->start(ctr);
93}
94
95static int op_powerpc_start(void)
96{
97 on_each_cpu(op_powerpc_cpu_start, NULL, 0, 1);
98 return 0;
99}
100
101static inline void op_powerpc_cpu_stop(void *dummy)
102{
103 model->stop();
104}
105
106static void op_powerpc_stop(void)
107{
108 on_each_cpu(op_powerpc_cpu_stop, NULL, 0, 1);
109}
110
111static int op_powerpc_create_files(struct super_block *sb, struct dentry *root)
112{
113 int i;
114
115#ifdef __powerpc64__
116 /*
117 * There is one mmcr0, mmcr1 and mmcra for setting the events for
118 * all of the counters.
119 */
120 oprofilefs_create_ulong(sb, root, "mmcr0", &sys.mmcr0);
121 oprofilefs_create_ulong(sb, root, "mmcr1", &sys.mmcr1);
122 oprofilefs_create_ulong(sb, root, "mmcra", &sys.mmcra);
123#endif /* __powerpc64__ */
124
125 for (i = 0; i < model->num_counters; ++i) {
126 struct dentry *dir;
127 char buf[3];
128
129 snprintf(buf, sizeof buf, "%d", i);
130 dir = oprofilefs_mkdir(sb, root, buf);
131
132 oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
133 oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
134 oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
135#ifdef __powerpc64__
136 /*
137 * We dont support per counter user/kernel selection, but
138 * we leave the entries because userspace expects them
139 */
140#endif /* __powerpc64__ */
141 oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
142 oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
143
144#ifndef __powerpc64__
145 /* FIXME: Not sure if this is used */
146#endif /* ! __powerpc64__ */
147 oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
148 }
149
150 oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel);
151 oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user);
152#ifdef __powerpc64__
153 oprofilefs_create_ulong(sb, root, "backtrace_spinlocks",
154 &sys.backtrace_spinlocks);
155#endif /* __powerpc64__ */
156
157 /* Default to tracing both kernel and user */
158 sys.enable_kernel = 1;
159 sys.enable_user = 1;
160#ifdef __powerpc64__
161 /* Turn on backtracing through spinlocks by default */
162 sys.backtrace_spinlocks = 1;
163#endif /* __powerpc64__ */
164
165 return 0;
166}
167
168int __init oprofile_arch_init(struct oprofile_operations *ops)
169{
170#ifndef __powerpc64__
171 int cpu_id = smp_processor_id();
172
173#ifdef CONFIG_FSL_BOOKE
174 model = &op_model_fsl_booke;
175#else
176 return -ENODEV;
177#endif
178
179 cpu_type = kmalloc(32, GFP_KERNEL);
180 if (NULL == cpu_type)
181 return -ENOMEM;
182
183 sprintf(cpu_type, "ppc/%s", cur_cpu_spec[cpu_id]->cpu_name);
184
185 model->num_counters = cur_cpu_spec[cpu_id]->num_pmcs;
186
187 ops->cpu_type = cpu_type;
188#else /* __powerpc64__ */
189 if (!cur_cpu_spec->oprofile_model || !cur_cpu_spec->oprofile_cpu_type)
190 return -ENODEV;
191 model = cur_cpu_spec->oprofile_model;
192 model->num_counters = cur_cpu_spec->num_pmcs;
193
194 ops->cpu_type = cur_cpu_spec->oprofile_cpu_type;
195#endif /* __powerpc64__ */
196 ops->create_files = op_powerpc_create_files;
197 ops->setup = op_powerpc_setup;
198 ops->shutdown = op_powerpc_shutdown;
199 ops->start = op_powerpc_start;
200 ops->stop = op_powerpc_stop;
201
202 printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
203 ops->cpu_type);
204
205 return 0;
206}
207
208void oprofile_arch_exit(void)
209{
210#ifndef __powerpc64__
211 kfree(cpu_type);
212 cpu_type = NULL;
213#endif /* ! __powerpc64__ */
214}
diff --git a/arch/powerpc/oprofile/op_model_fsl_booke.c b/arch/powerpc/oprofile/op_model_fsl_booke.c
new file mode 100644
index 000000000000..1917f8df8a8b
--- /dev/null
+++ b/arch/powerpc/oprofile/op_model_fsl_booke.c
@@ -0,0 +1,183 @@
1/*
2 * oprofile/op_model_e500.c
3 *
4 * Freescale Book-E oprofile support, based on ppc64 oprofile support
5 * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
6 *
7 * Copyright (c) 2004 Freescale Semiconductor, Inc
8 *
9 * Author: Andy Fleming
10 * Maintainer: Kumar Gala <Kumar.Gala@freescale.com>
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 */
17
18#include <linux/oprofile.h>
19#include <linux/init.h>
20#include <linux/smp.h>
21#include <asm/ptrace.h>
22#include <asm/system.h>
23#include <asm/processor.h>
24#include <asm/cputable.h>
25#include <asm/reg_booke.h>
26#include <asm/page.h>
27#include <asm/perfmon.h>
28#include <asm/oprofile_impl.h>
29
30static unsigned long reset_value[OP_MAX_COUNTER];
31
32static int num_counters;
33static int oprofile_running;
34
35static inline unsigned int ctr_read(unsigned int i)
36{
37 switch(i) {
38 case 0:
39 return mfpmr(PMRN_PMC0);
40 case 1:
41 return mfpmr(PMRN_PMC1);
42 case 2:
43 return mfpmr(PMRN_PMC2);
44 case 3:
45 return mfpmr(PMRN_PMC3);
46 default:
47 return 0;
48 }
49}
50
51static inline void ctr_write(unsigned int i, unsigned int val)
52{
53 switch(i) {
54 case 0:
55 mtpmr(PMRN_PMC0, val);
56 break;
57 case 1:
58 mtpmr(PMRN_PMC1, val);
59 break;
60 case 2:
61 mtpmr(PMRN_PMC2, val);
62 break;
63 case 3:
64 mtpmr(PMRN_PMC3, val);
65 break;
66 default:
67 break;
68 }
69}
70
71
72static void fsl_booke_reg_setup(struct op_counter_config *ctr,
73 struct op_system_config *sys,
74 int num_ctrs)
75{
76 int i;
77
78 num_counters = num_ctrs;
79
80 /* freeze all counters */
81 pmc_stop_ctrs();
82
83 /* Our counters count up, and "count" refers to
84 * how much before the next interrupt, and we interrupt
85 * on overflow. So we calculate the starting value
86 * which will give us "count" until overflow.
87 * Then we set the events on the enabled counters */
88 for (i = 0; i < num_counters; ++i) {
89 reset_value[i] = 0x80000000UL - ctr[i].count;
90
91 init_pmc_stop(i);
92
93 set_pmc_event(i, ctr[i].event);
94
95 set_pmc_user_kernel(i, ctr[i].user, ctr[i].kernel);
96 }
97}
98
99static void fsl_booke_start(struct op_counter_config *ctr)
100{
101 int i;
102
103 mtmsr(mfmsr() | MSR_PMM);
104
105 for (i = 0; i < num_counters; ++i) {
106 if (ctr[i].enabled) {
107 ctr_write(i, reset_value[i]);
108 /* Set Each enabled counterd to only
109 * count when the Mark bit is not set */
110 set_pmc_marked(i, 1, 0);
111 pmc_start_ctr(i, 1);
112 } else {
113 ctr_write(i, 0);
114
115 /* Set the ctr to be stopped */
116 pmc_start_ctr(i, 0);
117 }
118 }
119
120 /* Clear the freeze bit, and enable the interrupt.
121 * The counters won't actually start until the rfi clears
122 * the PMM bit */
123 pmc_start_ctrs(1);
124
125 oprofile_running = 1;
126
127 pr_debug("start on cpu %d, pmgc0 %x\n", smp_processor_id(),
128 mfpmr(PMRN_PMGC0));
129}
130
131static void fsl_booke_stop(void)
132{
133 /* freeze counters */
134 pmc_stop_ctrs();
135
136 oprofile_running = 0;
137
138 pr_debug("stop on cpu %d, pmgc0 %x\n", smp_processor_id(),
139 mfpmr(PMRN_PMGC0));
140
141 mb();
142}
143
144
145static void fsl_booke_handle_interrupt(struct pt_regs *regs,
146 struct op_counter_config *ctr)
147{
148 unsigned long pc;
149 int is_kernel;
150 int val;
151 int i;
152
153 /* set the PMM bit (see comment below) */
154 mtmsr(mfmsr() | MSR_PMM);
155
156 pc = regs->nip;
157 is_kernel = (pc >= KERNELBASE);
158
159 for (i = 0; i < num_counters; ++i) {
160 val = ctr_read(i);
161 if (val < 0) {
162 if (oprofile_running && ctr[i].enabled) {
163 oprofile_add_pc(pc, is_kernel, i);
164 ctr_write(i, reset_value[i]);
165 } else {
166 ctr_write(i, 0);
167 }
168 }
169 }
170
171 /* The freeze bit was set by the interrupt. */
172 /* Clear the freeze bit, and reenable the interrupt.
173 * The counters won't actually start until the rfi clears
174 * the PMM bit */
175 pmc_start_ctrs(1);
176}
177
178struct op_powerpc_model op_model_fsl_booke = {
179 .reg_setup = fsl_booke_reg_setup,
180 .start = fsl_booke_start,
181 .stop = fsl_booke_stop,
182 .handle_interrupt = fsl_booke_handle_interrupt,
183};
diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c
new file mode 100644
index 000000000000..886449315847
--- /dev/null
+++ b/arch/powerpc/oprofile/op_model_power4.c
@@ -0,0 +1,309 @@
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#include <asm/oprofile_impl.h>
20
21#define dbg(args...)
22
23static unsigned long reset_value[OP_MAX_COUNTER];
24
25static int oprofile_running;
26static int mmcra_has_sihv;
27
28/* mmcr values are set in power4_reg_setup, used in power4_cpu_setup */
29static u32 mmcr0_val;
30static u64 mmcr1_val;
31static u32 mmcra_val;
32
33/*
34 * Since we do not have an NMI, backtracing through spinlocks is
35 * only a best guess. In light of this, allow it to be disabled at
36 * runtime.
37 */
38static int backtrace_spinlocks;
39
40static void power4_reg_setup(struct op_counter_config *ctr,
41 struct op_system_config *sys,
42 int num_ctrs)
43{
44 int i;
45
46 /*
47 * SIHV / SIPR bits are only implemented on POWER4+ (GQ) and above.
48 * However we disable it on all POWER4 until we verify it works
49 * (I was seeing some strange behaviour last time I tried).
50 *
51 * It has been verified to work on POWER5 so we enable it there.
52 */
53 if (cpu_has_feature(CPU_FTR_MMCRA_SIHV))
54 mmcra_has_sihv = 1;
55
56 /*
57 * The performance counter event settings are given in the mmcr0,
58 * mmcr1 and mmcra values passed from the user in the
59 * op_system_config structure (sys variable).
60 */
61 mmcr0_val = sys->mmcr0;
62 mmcr1_val = sys->mmcr1;
63 mmcra_val = sys->mmcra;
64
65 backtrace_spinlocks = sys->backtrace_spinlocks;
66
67 for (i = 0; i < cur_cpu_spec->num_pmcs; ++i)
68 reset_value[i] = 0x80000000UL - ctr[i].count;
69
70 /* setup user and kernel profiling */
71 if (sys->enable_kernel)
72 mmcr0_val &= ~MMCR0_KERNEL_DISABLE;
73 else
74 mmcr0_val |= MMCR0_KERNEL_DISABLE;
75
76 if (sys->enable_user)
77 mmcr0_val &= ~MMCR0_PROBLEM_DISABLE;
78 else
79 mmcr0_val |= MMCR0_PROBLEM_DISABLE;
80}
81
82extern void ppc64_enable_pmcs(void);
83
84static void power4_cpu_setup(void *unused)
85{
86 unsigned int mmcr0 = mmcr0_val;
87 unsigned long mmcra = mmcra_val;
88
89 ppc64_enable_pmcs();
90
91 /* set the freeze bit */
92 mmcr0 |= MMCR0_FC;
93 mtspr(SPRN_MMCR0, mmcr0);
94
95 mmcr0 |= MMCR0_FCM1|MMCR0_PMXE|MMCR0_FCECE;
96 mmcr0 |= MMCR0_PMC1CE|MMCR0_PMCjCE;
97 mtspr(SPRN_MMCR0, mmcr0);
98
99 mtspr(SPRN_MMCR1, mmcr1_val);
100
101 mmcra |= MMCRA_SAMPLE_ENABLE;
102 mtspr(SPRN_MMCRA, mmcra);
103
104 dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
105 mfspr(SPRN_MMCR0));
106 dbg("setup on cpu %d, mmcr1 %lx\n", smp_processor_id(),
107 mfspr(SPRN_MMCR1));
108 dbg("setup on cpu %d, mmcra %lx\n", smp_processor_id(),
109 mfspr(SPRN_MMCRA));
110}
111
112static void power4_start(struct op_counter_config *ctr)
113{
114 int i;
115 unsigned int mmcr0;
116
117 /* set the PMM bit (see comment below) */
118 mtmsrd(mfmsr() | MSR_PMM);
119
120 for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) {
121 if (ctr[i].enabled) {
122 ctr_write(i, reset_value[i]);
123 } else {
124 ctr_write(i, 0);
125 }
126 }
127
128 mmcr0 = mfspr(SPRN_MMCR0);
129
130 /*
131 * We must clear the PMAO bit on some (GQ) chips. Just do it
132 * all the time
133 */
134 mmcr0 &= ~MMCR0_PMAO;
135
136 /*
137 * now clear the freeze bit, counting will not start until we
138 * rfid from this excetion, because only at that point will
139 * the PMM bit be cleared
140 */
141 mmcr0 &= ~MMCR0_FC;
142 mtspr(SPRN_MMCR0, mmcr0);
143
144 oprofile_running = 1;
145
146 dbg("start on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
147}
148
149static void power4_stop(void)
150{
151 unsigned int mmcr0;
152
153 /* freeze counters */
154 mmcr0 = mfspr(SPRN_MMCR0);
155 mmcr0 |= MMCR0_FC;
156 mtspr(SPRN_MMCR0, mmcr0);
157
158 oprofile_running = 0;
159
160 dbg("stop on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
161
162 mb();
163}
164
165/* Fake functions used by canonicalize_pc */
166static void __attribute_used__ hypervisor_bucket(void)
167{
168}
169
170static void __attribute_used__ rtas_bucket(void)
171{
172}
173
174static void __attribute_used__ kernel_unknown_bucket(void)
175{
176}
177
178static unsigned long check_spinlock_pc(struct pt_regs *regs,
179 unsigned long profile_pc)
180{
181 unsigned long pc = instruction_pointer(regs);
182
183 /*
184 * If both the SIAR (sampled instruction) and the perfmon exception
185 * occurred in a spinlock region then we account the sample to the
186 * calling function. This isnt 100% correct, we really need soft
187 * IRQ disable so we always get the perfmon exception at the
188 * point at which the SIAR is set.
189 */
190 if (backtrace_spinlocks && in_lock_functions(pc) &&
191 in_lock_functions(profile_pc))
192 return regs->link;
193 else
194 return profile_pc;
195}
196
197/*
198 * On GQ and newer the MMCRA stores the HV and PR bits at the time
199 * the SIAR was sampled. We use that to work out if the SIAR was sampled in
200 * the hypervisor, our exception vectors or RTAS.
201 */
202static unsigned long get_pc(struct pt_regs *regs)
203{
204 unsigned long pc = mfspr(SPRN_SIAR);
205 unsigned long mmcra;
206
207 /* Cant do much about it */
208 if (!mmcra_has_sihv)
209 return check_spinlock_pc(regs, pc);
210
211 mmcra = mfspr(SPRN_MMCRA);
212
213 /* Were we in the hypervisor? */
214 if ((systemcfg->platform == PLATFORM_PSERIES_LPAR) &&
215 (mmcra & MMCRA_SIHV))
216 /* function descriptor madness */
217 return *((unsigned long *)hypervisor_bucket);
218
219 /* We were in userspace, nothing to do */
220 if (mmcra & MMCRA_SIPR)
221 return pc;
222
223#ifdef CONFIG_PPC_RTAS
224 /* Were we in RTAS? */
225 if (pc >= rtas.base && pc < (rtas.base + rtas.size))
226 /* function descriptor madness */
227 return *((unsigned long *)rtas_bucket);
228#endif
229
230 /* Were we in our exception vectors or SLB real mode miss handler? */
231 if (pc < 0x1000000UL)
232 return (unsigned long)__va(pc);
233
234 /* Not sure where we were */
235 if (pc < KERNELBASE)
236 /* function descriptor madness */
237 return *((unsigned long *)kernel_unknown_bucket);
238
239 return check_spinlock_pc(regs, pc);
240}
241
242static int get_kernel(unsigned long pc)
243{
244 int is_kernel;
245
246 if (!mmcra_has_sihv) {
247 is_kernel = (pc >= KERNELBASE);
248 } else {
249 unsigned long mmcra = mfspr(SPRN_MMCRA);
250 is_kernel = ((mmcra & MMCRA_SIPR) == 0);
251 }
252
253 return is_kernel;
254}
255
256static void power4_handle_interrupt(struct pt_regs *regs,
257 struct op_counter_config *ctr)
258{
259 unsigned long pc;
260 int is_kernel;
261 int val;
262 int i;
263 unsigned int mmcr0;
264
265 pc = get_pc(regs);
266 is_kernel = get_kernel(pc);
267
268 /* set the PMM bit (see comment below) */
269 mtmsrd(mfmsr() | MSR_PMM);
270
271 for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) {
272 val = ctr_read(i);
273 if (val < 0) {
274 if (oprofile_running && ctr[i].enabled) {
275 oprofile_add_pc(pc, is_kernel, i);
276 ctr_write(i, reset_value[i]);
277 } else {
278 ctr_write(i, 0);
279 }
280 }
281 }
282
283 mmcr0 = mfspr(SPRN_MMCR0);
284
285 /* reset the perfmon trigger */
286 mmcr0 |= MMCR0_PMXE;
287
288 /*
289 * We must clear the PMAO bit on some (GQ) chips. Just do it
290 * all the time
291 */
292 mmcr0 &= ~MMCR0_PMAO;
293
294 /*
295 * now clear the freeze bit, counting will not start until we
296 * rfid from this exception, because only at that point will
297 * the PMM bit be cleared
298 */
299 mmcr0 &= ~MMCR0_FC;
300 mtspr(SPRN_MMCR0, mmcr0);
301}
302
303struct op_powerpc_model op_model_power4 = {
304 .reg_setup = power4_reg_setup,
305 .cpu_setup = power4_cpu_setup,
306 .start = power4_start,
307 .stop = power4_stop,
308 .handle_interrupt = power4_handle_interrupt,
309};
diff --git a/arch/powerpc/oprofile/op_model_rs64.c b/arch/powerpc/oprofile/op_model_rs64.c
new file mode 100644
index 000000000000..e010b85996e8
--- /dev/null
+++ b/arch/powerpc/oprofile/op_model_rs64.c
@@ -0,0 +1,218 @@
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/oprofile_impl.h>
18
19#define dbg(args...)
20
21static void ctrl_write(unsigned int i, unsigned int val)
22{
23 unsigned int tmp = 0;
24 unsigned long shift = 0, mask = 0;
25
26 dbg("ctrl_write %d %x\n", i, val);
27
28 switch(i) {
29 case 0:
30 tmp = mfspr(SPRN_MMCR0);
31 shift = 6;
32 mask = 0x7F;
33 break;
34 case 1:
35 tmp = mfspr(SPRN_MMCR0);
36 shift = 0;
37 mask = 0x3F;
38 break;
39 case 2:
40 tmp = mfspr(SPRN_MMCR1);
41 shift = 31 - 4;
42 mask = 0x1F;
43 break;
44 case 3:
45 tmp = mfspr(SPRN_MMCR1);
46 shift = 31 - 9;
47 mask = 0x1F;
48 break;
49 case 4:
50 tmp = mfspr(SPRN_MMCR1);
51 shift = 31 - 14;
52 mask = 0x1F;
53 break;
54 case 5:
55 tmp = mfspr(SPRN_MMCR1);
56 shift = 31 - 19;
57 mask = 0x1F;
58 break;
59 case 6:
60 tmp = mfspr(SPRN_MMCR1);
61 shift = 31 - 24;
62 mask = 0x1F;
63 break;
64 case 7:
65 tmp = mfspr(SPRN_MMCR1);
66 shift = 31 - 28;
67 mask = 0xF;
68 break;
69 }
70
71 tmp = tmp & ~(mask << shift);
72 tmp |= val << shift;
73
74 switch(i) {
75 case 0:
76 case 1:
77 mtspr(SPRN_MMCR0, tmp);
78 break;
79 default:
80 mtspr(SPRN_MMCR1, tmp);
81 }
82
83 dbg("ctrl_write mmcr0 %lx mmcr1 %lx\n", mfspr(SPRN_MMCR0),
84 mfspr(SPRN_MMCR1));
85}
86
87static unsigned long reset_value[OP_MAX_COUNTER];
88
89static int num_counters;
90
91static void rs64_reg_setup(struct op_counter_config *ctr,
92 struct op_system_config *sys,
93 int num_ctrs)
94{
95 int i;
96
97 num_counters = num_ctrs;
98
99 for (i = 0; i < num_counters; ++i)
100 reset_value[i] = 0x80000000UL - ctr[i].count;
101
102 /* XXX setup user and kernel profiling */
103}
104
105static void rs64_cpu_setup(void *unused)
106{
107 unsigned int mmcr0;
108
109 /* reset MMCR0 and set the freeze bit */
110 mmcr0 = MMCR0_FC;
111 mtspr(SPRN_MMCR0, mmcr0);
112
113 /* reset MMCR1, MMCRA */
114 mtspr(SPRN_MMCR1, 0);
115
116 if (cpu_has_feature(CPU_FTR_MMCRA))
117 mtspr(SPRN_MMCRA, 0);
118
119 mmcr0 |= MMCR0_FCM1|MMCR0_PMXE|MMCR0_FCECE;
120 /* Only applies to POWER3, but should be safe on RS64 */
121 mmcr0 |= MMCR0_PMC1CE|MMCR0_PMCjCE;
122 mtspr(SPRN_MMCR0, mmcr0);
123
124 dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
125 mfspr(SPRN_MMCR0));
126 dbg("setup on cpu %d, mmcr1 %lx\n", smp_processor_id(),
127 mfspr(SPRN_MMCR1));
128}
129
130static void rs64_start(struct op_counter_config *ctr)
131{
132 int i;
133 unsigned int mmcr0;
134
135 /* set the PMM bit (see comment below) */
136 mtmsrd(mfmsr() | MSR_PMM);
137
138 for (i = 0; i < num_counters; ++i) {
139 if (ctr[i].enabled) {
140 ctr_write(i, reset_value[i]);
141 ctrl_write(i, ctr[i].event);
142 } else {
143 ctr_write(i, 0);
144 }
145 }
146
147 mmcr0 = mfspr(SPRN_MMCR0);
148
149 /*
150 * now clear the freeze bit, counting will not start until we
151 * rfid from this excetion, because only at that point will
152 * the PMM bit be cleared
153 */
154 mmcr0 &= ~MMCR0_FC;
155 mtspr(SPRN_MMCR0, mmcr0);
156
157 dbg("start on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
158}
159
160static void rs64_stop(void)
161{
162 unsigned int mmcr0;
163
164 /* freeze counters */
165 mmcr0 = mfspr(SPRN_MMCR0);
166 mmcr0 |= MMCR0_FC;
167 mtspr(SPRN_MMCR0, mmcr0);
168
169 dbg("stop on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0);
170
171 mb();
172}
173
174static void rs64_handle_interrupt(struct pt_regs *regs,
175 struct op_counter_config *ctr)
176{
177 unsigned int mmcr0;
178 int val;
179 int i;
180 unsigned long pc = mfspr(SPRN_SIAR);
181 int is_kernel = (pc >= KERNELBASE);
182
183 /* set the PMM bit (see comment below) */
184 mtmsrd(mfmsr() | MSR_PMM);
185
186 for (i = 0; i < num_counters; ++i) {
187 val = ctr_read(i);
188 if (val < 0) {
189 if (ctr[i].enabled) {
190 oprofile_add_pc(pc, is_kernel, i);
191 ctr_write(i, reset_value[i]);
192 } else {
193 ctr_write(i, 0);
194 }
195 }
196 }
197
198 mmcr0 = mfspr(SPRN_MMCR0);
199
200 /* reset the perfmon trigger */
201 mmcr0 |= MMCR0_PMXE;
202
203 /*
204 * now clear the freeze bit, counting will not start until we
205 * rfid from this exception, because only at that point will
206 * the PMM bit be cleared
207 */
208 mmcr0 &= ~MMCR0_FC;
209 mtspr(SPRN_MMCR0, mmcr0);
210}
211
212struct op_powerpc_model op_model_rs64 = {
213 .reg_setup = rs64_reg_setup,
214 .cpu_setup = rs64_cpu_setup,
215 .start = rs64_start,
216 .stop = rs64_stop,
217 .handle_interrupt = rs64_handle_interrupt,
218};