aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/kernel/vecemu.c345
-rw-r--r--arch/powerpc/oprofile/Kconfig23
-rw-r--r--arch/powerpc/oprofile/Makefile11
-rw-r--r--arch/powerpc/oprofile/common.c201
-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
7 files changed, 1290 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/vecemu.c b/arch/powerpc/kernel/vecemu.c
new file mode 100644
index 000000000000..604d0947cb20
--- /dev/null
+++ b/arch/powerpc/kernel/vecemu.c
@@ -0,0 +1,345 @@
1/*
2 * Routines to emulate some Altivec/VMX instructions, specifically
3 * those that can trap when given denormalized operands in Java mode.
4 */
5#include <linux/kernel.h>
6#include <linux/errno.h>
7#include <linux/sched.h>
8#include <asm/ptrace.h>
9#include <asm/processor.h>
10#include <asm/uaccess.h>
11
12/* Functions in vector.S */
13extern void vaddfp(vector128 *dst, vector128 *a, vector128 *b);
14extern void vsubfp(vector128 *dst, vector128 *a, vector128 *b);
15extern void vmaddfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c);
16extern void vnmsubfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c);
17extern void vrefp(vector128 *dst, vector128 *src);
18extern void vrsqrtefp(vector128 *dst, vector128 *src);
19extern void vexptep(vector128 *dst, vector128 *src);
20
21static unsigned int exp2s[8] = {
22 0x800000,
23 0x8b95c2,
24 0x9837f0,
25 0xa5fed7,
26 0xb504f3,
27 0xc5672a,
28 0xd744fd,
29 0xeac0c7
30};
31
32/*
33 * Computes an estimate of 2^x. The `s' argument is the 32-bit
34 * single-precision floating-point representation of x.
35 */
36static unsigned int eexp2(unsigned int s)
37{
38 int exp, pwr;
39 unsigned int mant, frac;
40
41 /* extract exponent field from input */
42 exp = ((s >> 23) & 0xff) - 127;
43 if (exp > 7) {
44 /* check for NaN input */
45 if (exp == 128 && (s & 0x7fffff) != 0)
46 return s | 0x400000; /* return QNaN */
47 /* 2^-big = 0, 2^+big = +Inf */
48 return (s & 0x80000000)? 0: 0x7f800000; /* 0 or +Inf */
49 }
50 if (exp < -23)
51 return 0x3f800000; /* 1.0 */
52
53 /* convert to fixed point integer in 9.23 representation */
54 pwr = (s & 0x7fffff) | 0x800000;
55 if (exp > 0)
56 pwr <<= exp;
57 else
58 pwr >>= -exp;
59 if (s & 0x80000000)
60 pwr = -pwr;
61
62 /* extract integer part, which becomes exponent part of result */
63 exp = (pwr >> 23) + 126;
64 if (exp >= 254)
65 return 0x7f800000;
66 if (exp < -23)
67 return 0;
68
69 /* table lookup on top 3 bits of fraction to get mantissa */
70 mant = exp2s[(pwr >> 20) & 7];
71
72 /* linear interpolation using remaining 20 bits of fraction */
73 asm("mulhwu %0,%1,%2" : "=r" (frac)
74 : "r" (pwr << 12), "r" (0x172b83ff));
75 asm("mulhwu %0,%1,%2" : "=r" (frac) : "r" (frac), "r" (mant));
76 mant += frac;
77
78 if (exp >= 0)
79 return mant + (exp << 23);
80
81 /* denormalized result */
82 exp = -exp;
83 mant += 1 << (exp - 1);
84 return mant >> exp;
85}
86
87/*
88 * Computes an estimate of log_2(x). The `s' argument is the 32-bit
89 * single-precision floating-point representation of x.
90 */
91static unsigned int elog2(unsigned int s)
92{
93 int exp, mant, lz, frac;
94
95 exp = s & 0x7f800000;
96 mant = s & 0x7fffff;
97 if (exp == 0x7f800000) { /* Inf or NaN */
98 if (mant != 0)
99 s |= 0x400000; /* turn NaN into QNaN */
100 return s;
101 }
102 if ((exp | mant) == 0) /* +0 or -0 */
103 return 0xff800000; /* return -Inf */
104
105 if (exp == 0) {
106 /* denormalized */
107 asm("cntlzw %0,%1" : "=r" (lz) : "r" (mant));
108 mant <<= lz - 8;
109 exp = (-118 - lz) << 23;
110 } else {
111 mant |= 0x800000;
112 exp -= 127 << 23;
113 }
114
115 if (mant >= 0xb504f3) { /* 2^0.5 * 2^23 */
116 exp |= 0x400000; /* 0.5 * 2^23 */
117 asm("mulhwu %0,%1,%2" : "=r" (mant)
118 : "r" (mant), "r" (0xb504f334)); /* 2^-0.5 * 2^32 */
119 }
120 if (mant >= 0x9837f0) { /* 2^0.25 * 2^23 */
121 exp |= 0x200000; /* 0.25 * 2^23 */
122 asm("mulhwu %0,%1,%2" : "=r" (mant)
123 : "r" (mant), "r" (0xd744fccb)); /* 2^-0.25 * 2^32 */
124 }
125 if (mant >= 0x8b95c2) { /* 2^0.125 * 2^23 */
126 exp |= 0x100000; /* 0.125 * 2^23 */
127 asm("mulhwu %0,%1,%2" : "=r" (mant)
128 : "r" (mant), "r" (0xeac0c6e8)); /* 2^-0.125 * 2^32 */
129 }
130 if (mant > 0x800000) { /* 1.0 * 2^23 */
131 /* calculate (mant - 1) * 1.381097463 */
132 /* 1.381097463 == 0.125 / (2^0.125 - 1) */
133 asm("mulhwu %0,%1,%2" : "=r" (frac)
134 : "r" ((mant - 0x800000) << 1), "r" (0xb0c7cd3a));
135 exp += frac;
136 }
137 s = exp & 0x80000000;
138 if (exp != 0) {
139 if (s)
140 exp = -exp;
141 asm("cntlzw %0,%1" : "=r" (lz) : "r" (exp));
142 lz = 8 - lz;
143 if (lz > 0)
144 exp >>= lz;
145 else if (lz < 0)
146 exp <<= -lz;
147 s += ((lz + 126) << 23) + exp;
148 }
149 return s;
150}
151
152#define VSCR_SAT 1
153
154static int ctsxs(unsigned int x, int scale, unsigned int *vscrp)
155{
156 int exp, mant;
157
158 exp = (x >> 23) & 0xff;
159 mant = x & 0x7fffff;
160 if (exp == 255 && mant != 0)
161 return 0; /* NaN -> 0 */
162 exp = exp - 127 + scale;
163 if (exp < 0)
164 return 0; /* round towards zero */
165 if (exp >= 31) {
166 /* saturate, unless the result would be -2^31 */
167 if (x + (scale << 23) != 0xcf000000)
168 *vscrp |= VSCR_SAT;
169 return (x & 0x80000000)? 0x80000000: 0x7fffffff;
170 }
171 mant |= 0x800000;
172 mant = (mant << 7) >> (30 - exp);
173 return (x & 0x80000000)? -mant: mant;
174}
175
176static unsigned int ctuxs(unsigned int x, int scale, unsigned int *vscrp)
177{
178 int exp;
179 unsigned int mant;
180
181 exp = (x >> 23) & 0xff;
182 mant = x & 0x7fffff;
183 if (exp == 255 && mant != 0)
184 return 0; /* NaN -> 0 */
185 exp = exp - 127 + scale;
186 if (exp < 0)
187 return 0; /* round towards zero */
188 if (x & 0x80000000) {
189 /* negative => saturate to 0 */
190 *vscrp |= VSCR_SAT;
191 return 0;
192 }
193 if (exp >= 32) {
194 /* saturate */
195 *vscrp |= VSCR_SAT;
196 return 0xffffffff;
197 }
198 mant |= 0x800000;
199 mant = (mant << 8) >> (31 - exp);
200 return mant;
201}
202
203/* Round to floating integer, towards 0 */
204static unsigned int rfiz(unsigned int x)
205{
206 int exp;
207
208 exp = ((x >> 23) & 0xff) - 127;
209 if (exp == 128 && (x & 0x7fffff) != 0)
210 return x | 0x400000; /* NaN -> make it a QNaN */
211 if (exp >= 23)
212 return x; /* it's an integer already (or Inf) */
213 if (exp < 0)
214 return x & 0x80000000; /* |x| < 1.0 rounds to 0 */
215 return x & ~(0x7fffff >> exp);
216}
217
218/* Round to floating integer, towards +/- Inf */
219static unsigned int rfii(unsigned int x)
220{
221 int exp, mask;
222
223 exp = ((x >> 23) & 0xff) - 127;
224 if (exp == 128 && (x & 0x7fffff) != 0)
225 return x | 0x400000; /* NaN -> make it a QNaN */
226 if (exp >= 23)
227 return x; /* it's an integer already (or Inf) */
228 if ((x & 0x7fffffff) == 0)
229 return x; /* +/-0 -> +/-0 */
230 if (exp < 0)
231 /* 0 < |x| < 1.0 rounds to +/- 1.0 */
232 return (x & 0x80000000) | 0x3f800000;
233 mask = 0x7fffff >> exp;
234 /* mantissa overflows into exponent - that's OK,
235 it can't overflow into the sign bit */
236 return (x + mask) & ~mask;
237}
238
239/* Round to floating integer, to nearest */
240static unsigned int rfin(unsigned int x)
241{
242 int exp, half;
243
244 exp = ((x >> 23) & 0xff) - 127;
245 if (exp == 128 && (x & 0x7fffff) != 0)
246 return x | 0x400000; /* NaN -> make it a QNaN */
247 if (exp >= 23)
248 return x; /* it's an integer already (or Inf) */
249 if (exp < -1)
250 return x & 0x80000000; /* |x| < 0.5 -> +/-0 */
251 if (exp == -1)
252 /* 0.5 <= |x| < 1.0 rounds to +/- 1.0 */
253 return (x & 0x80000000) | 0x3f800000;
254 half = 0x400000 >> exp;
255 /* add 0.5 to the magnitude and chop off the fraction bits */
256 return (x + half) & ~(0x7fffff >> exp);
257}
258
259int emulate_altivec(struct pt_regs *regs)
260{
261 unsigned int instr, i;
262 unsigned int va, vb, vc, vd;
263 vector128 *vrs;
264
265 if (get_user(instr, (unsigned int __user *) regs->nip))
266 return -EFAULT;
267 if ((instr >> 26) != 4)
268 return -EINVAL; /* not an altivec instruction */
269 vd = (instr >> 21) & 0x1f;
270 va = (instr >> 16) & 0x1f;
271 vb = (instr >> 11) & 0x1f;
272 vc = (instr >> 6) & 0x1f;
273
274 vrs = current->thread.vr;
275 switch (instr & 0x3f) {
276 case 10:
277 switch (vc) {
278 case 0: /* vaddfp */
279 vaddfp(&vrs[vd], &vrs[va], &vrs[vb]);
280 break;
281 case 1: /* vsubfp */
282 vsubfp(&vrs[vd], &vrs[va], &vrs[vb]);
283 break;
284 case 4: /* vrefp */
285 vrefp(&vrs[vd], &vrs[vb]);
286 break;
287 case 5: /* vrsqrtefp */
288 vrsqrtefp(&vrs[vd], &vrs[vb]);
289 break;
290 case 6: /* vexptefp */
291 for (i = 0; i < 4; ++i)
292 vrs[vd].u[i] = eexp2(vrs[vb].u[i]);
293 break;
294 case 7: /* vlogefp */
295 for (i = 0; i < 4; ++i)
296 vrs[vd].u[i] = elog2(vrs[vb].u[i]);
297 break;
298 case 8: /* vrfin */
299 for (i = 0; i < 4; ++i)
300 vrs[vd].u[i] = rfin(vrs[vb].u[i]);
301 break;
302 case 9: /* vrfiz */
303 for (i = 0; i < 4; ++i)
304 vrs[vd].u[i] = rfiz(vrs[vb].u[i]);
305 break;
306 case 10: /* vrfip */
307 for (i = 0; i < 4; ++i) {
308 u32 x = vrs[vb].u[i];
309 x = (x & 0x80000000)? rfiz(x): rfii(x);
310 vrs[vd].u[i] = x;
311 }
312 break;
313 case 11: /* vrfim */
314 for (i = 0; i < 4; ++i) {
315 u32 x = vrs[vb].u[i];
316 x = (x & 0x80000000)? rfii(x): rfiz(x);
317 vrs[vd].u[i] = x;
318 }
319 break;
320 case 14: /* vctuxs */
321 for (i = 0; i < 4; ++i)
322 vrs[vd].u[i] = ctuxs(vrs[vb].u[i], va,
323 &current->thread.vscr.u[3]);
324 break;
325 case 15: /* vctsxs */
326 for (i = 0; i < 4; ++i)
327 vrs[vd].u[i] = ctsxs(vrs[vb].u[i], va,
328 &current->thread.vscr.u[3]);
329 break;
330 default:
331 return -EINVAL;
332 }
333 break;
334 case 46: /* vmaddfp */
335 vmaddfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]);
336 break;
337 case 47: /* vnmsubfp */
338 vnmsubfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]);
339 break;
340 default:
341 return -EINVAL;
342 }
343
344 return 0;
345}
diff --git a/arch/powerpc/oprofile/Kconfig b/arch/powerpc/oprofile/Kconfig
new file mode 100644
index 000000000000..19d37730b664
--- /dev/null
+++ b/arch/powerpc/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/powerpc/oprofile/Makefile b/arch/powerpc/oprofile/Makefile
new file mode 100644
index 000000000000..0782d0cca89c
--- /dev/null
+++ b/arch/powerpc/oprofile/Makefile
@@ -0,0 +1,11 @@
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
10oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o
11oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o
diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c
new file mode 100644
index 000000000000..88b4118fd0c5
--- /dev/null
+++ b/arch/powerpc/oprofile/common.c
@@ -0,0 +1,201 @@
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 int err;
50
51 /* Grab the hardware */
52 err = reserve_pmc_hardware(op_handle_interrupt);
53 if (err)
54 return err;
55
56 /* Pre-compute the values to stuff in the hardware registers. */
57 model->reg_setup(ctr, &sys, model->num_counters);
58
59 /* Configure the registers on all cpus. */
60#ifdef __powerpc64__
61 on_each_cpu(model->cpu_setup, NULL, 0, 1);
62#else /* __powerpc64__ */
63#if 0
64 /* FIXME: Make multi-cpu work */
65 on_each_cpu(model->reg_setup, NULL, 0, 1);
66#endif
67#endif /* __powerpc64__ */
68
69 return 0;
70}
71
72static void op_powerpc_shutdown(void)
73{
74 release_pmc_hardware();
75}
76
77static void op_powerpc_cpu_start(void *dummy)
78{
79 model->start(ctr);
80}
81
82static int op_powerpc_start(void)
83{
84 on_each_cpu(op_powerpc_cpu_start, NULL, 0, 1);
85 return 0;
86}
87
88static inline void op_powerpc_cpu_stop(void *dummy)
89{
90 model->stop();
91}
92
93static void op_powerpc_stop(void)
94{
95 on_each_cpu(op_powerpc_cpu_stop, NULL, 0, 1);
96}
97
98static int op_powerpc_create_files(struct super_block *sb, struct dentry *root)
99{
100 int i;
101
102#ifdef __powerpc64__
103 /*
104 * There is one mmcr0, mmcr1 and mmcra for setting the events for
105 * all of the counters.
106 */
107 oprofilefs_create_ulong(sb, root, "mmcr0", &sys.mmcr0);
108 oprofilefs_create_ulong(sb, root, "mmcr1", &sys.mmcr1);
109 oprofilefs_create_ulong(sb, root, "mmcra", &sys.mmcra);
110#endif /* __powerpc64__ */
111
112 for (i = 0; i < model->num_counters; ++i) {
113 struct dentry *dir;
114 char buf[3];
115
116 snprintf(buf, sizeof buf, "%d", i);
117 dir = oprofilefs_mkdir(sb, root, buf);
118
119 oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
120 oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
121 oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
122#ifdef __powerpc64__
123 /*
124 * We dont support per counter user/kernel selection, but
125 * we leave the entries because userspace expects them
126 */
127#endif /* __powerpc64__ */
128 oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
129 oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
130
131#ifndef __powerpc64__
132 /* FIXME: Not sure if this is used */
133#endif /* ! __powerpc64__ */
134 oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
135 }
136
137 oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel);
138 oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user);
139#ifdef __powerpc64__
140 oprofilefs_create_ulong(sb, root, "backtrace_spinlocks",
141 &sys.backtrace_spinlocks);
142#endif /* __powerpc64__ */
143
144 /* Default to tracing both kernel and user */
145 sys.enable_kernel = 1;
146 sys.enable_user = 1;
147#ifdef __powerpc64__
148 /* Turn on backtracing through spinlocks by default */
149 sys.backtrace_spinlocks = 1;
150#endif /* __powerpc64__ */
151
152 return 0;
153}
154
155int __init oprofile_arch_init(struct oprofile_operations *ops)
156{
157#ifndef __powerpc64__
158 int cpu_id = smp_processor_id();
159
160#ifdef CONFIG_FSL_BOOKE
161 model = &op_model_fsl_booke;
162#else
163 return -ENODEV;
164#endif
165
166 cpu_type = kmalloc(32, GFP_KERNEL);
167 if (NULL == cpu_type)
168 return -ENOMEM;
169
170 sprintf(cpu_type, "ppc/%s", cur_cpu_spec[cpu_id]->cpu_name);
171
172 model->num_counters = cur_cpu_spec[cpu_id]->num_pmcs;
173
174 ops->cpu_type = cpu_type;
175#else /* __powerpc64__ */
176 if (!cur_cpu_spec->oprofile_model || !cur_cpu_spec->oprofile_cpu_type)
177 return -ENODEV;
178 model = cur_cpu_spec->oprofile_model;
179 model->num_counters = cur_cpu_spec->num_pmcs;
180
181 ops->cpu_type = cur_cpu_spec->oprofile_cpu_type;
182#endif /* __powerpc64__ */
183 ops->create_files = op_powerpc_create_files;
184 ops->setup = op_powerpc_setup;
185 ops->shutdown = op_powerpc_shutdown;
186 ops->start = op_powerpc_start;
187 ops->stop = op_powerpc_stop;
188
189 printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
190 ops->cpu_type);
191
192 return 0;
193}
194
195void oprofile_arch_exit(void)
196{
197#ifndef __powerpc64__
198 kfree(cpu_type);
199 cpu_type = NULL;
200#endif /* ! __powerpc64__ */
201}
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};