diff options
Diffstat (limited to 'arch/sh/oprofile')
-rw-r--r-- | arch/sh/oprofile/Makefile | 13 | ||||
-rw-r--r-- | arch/sh/oprofile/backtrace.c | 114 | ||||
-rw-r--r-- | arch/sh/oprofile/common.c | 150 | ||||
-rw-r--r-- | arch/sh/oprofile/op_impl.h | 33 | ||||
-rw-r--r-- | arch/sh/oprofile/op_model_null.c | 23 | ||||
-rw-r--r-- | arch/sh/oprofile/op_model_sh7750.c | 172 |
6 files changed, 379 insertions, 126 deletions
diff --git a/arch/sh/oprofile/Makefile b/arch/sh/oprofile/Makefile index 2efc2e79fd29..8e6eec91c14c 100644 --- a/arch/sh/oprofile/Makefile +++ b/arch/sh/oprofile/Makefile | |||
@@ -6,13 +6,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ | |||
6 | oprofilefs.o oprofile_stats.o \ | 6 | oprofilefs.o oprofile_stats.o \ |
7 | timer_int.o ) | 7 | timer_int.o ) |
8 | 8 | ||
9 | profdrvr-y := op_model_null.o | 9 | oprofile-y := $(DRIVER_OBJS) common.o backtrace.o |
10 | 10 | ||
11 | # SH7750-style performance counters exist across 7750/7750S and 7091. | 11 | oprofile-$(CONFIG_CPU_SUBTYPE_SH7750S) += op_model_sh7750.o |
12 | profdrvr-$(CONFIG_CPU_SUBTYPE_SH7750S) := op_model_sh7750.o | 12 | oprofile-$(CONFIG_CPU_SUBTYPE_SH7750) += op_model_sh7750.o |
13 | profdrvr-$(CONFIG_CPU_SUBTYPE_SH7750) := op_model_sh7750.o | 13 | oprofile-$(CONFIG_CPU_SUBTYPE_SH7091) += op_model_sh7750.o |
14 | profdrvr-$(CONFIG_CPU_SUBTYPE_SH7091) := op_model_sh7750.o | ||
15 | |||
16 | oprofile-y := $(DRIVER_OBJS) $(profdrvr-y) | ||
17 | |||
18 | EXTRA_CFLAGS += -Werror | ||
diff --git a/arch/sh/oprofile/backtrace.c b/arch/sh/oprofile/backtrace.c new file mode 100644 index 000000000000..9499a2914f89 --- /dev/null +++ b/arch/sh/oprofile/backtrace.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * SH specific backtracing code for oprofile | ||
3 | * | ||
4 | * Copyright 2007 STMicroelectronics Ltd. | ||
5 | * | ||
6 | * Author: Dave Peverley <dpeverley@mpc-data.co.uk> | ||
7 | * | ||
8 | * Based on ARM oprofile backtrace code by Richard Purdie and in turn, i386 | ||
9 | * oprofile backtrace code by John Levon, David Smith | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | */ | ||
16 | #include <linux/oprofile.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/kallsyms.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <asm/ptrace.h> | ||
21 | #include <asm/uaccess.h> | ||
22 | #include <asm/sections.h> | ||
23 | |||
24 | /* Limit to stop backtracing too far. */ | ||
25 | static int backtrace_limit = 20; | ||
26 | |||
27 | static unsigned long * | ||
28 | user_backtrace(unsigned long *stackaddr, struct pt_regs *regs) | ||
29 | { | ||
30 | unsigned long buf_stack; | ||
31 | |||
32 | /* Also check accessibility of address */ | ||
33 | if (!access_ok(VERIFY_READ, stackaddr, sizeof(unsigned long))) | ||
34 | return NULL; | ||
35 | |||
36 | if (__copy_from_user_inatomic(&buf_stack, stackaddr, sizeof(unsigned long))) | ||
37 | return NULL; | ||
38 | |||
39 | /* Quick paranoia check */ | ||
40 | if (buf_stack & 3) | ||
41 | return NULL; | ||
42 | |||
43 | oprofile_add_trace(buf_stack); | ||
44 | |||
45 | stackaddr++; | ||
46 | |||
47 | return stackaddr; | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * | | /\ Higher addresses | ||
52 | * | | | ||
53 | * --------------- stack base (address of current_thread_info) | ||
54 | * | thread info | | ||
55 | * . . | ||
56 | * | stack | | ||
57 | * --------------- saved regs->regs[15] value if valid | ||
58 | * . . | ||
59 | * --------------- struct pt_regs stored on stack (struct pt_regs *) | ||
60 | * | | | ||
61 | * . . | ||
62 | * | | | ||
63 | * --------------- ??? | ||
64 | * | | | ||
65 | * | | \/ Lower addresses | ||
66 | * | ||
67 | * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values | ||
68 | */ | ||
69 | static int valid_kernel_stack(unsigned long *stackaddr, struct pt_regs *regs) | ||
70 | { | ||
71 | unsigned long stack = (unsigned long)regs; | ||
72 | unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE; | ||
73 | |||
74 | return ((unsigned long)stackaddr > stack) && ((unsigned long)stackaddr < stack_base); | ||
75 | } | ||
76 | |||
77 | static unsigned long * | ||
78 | kernel_backtrace(unsigned long *stackaddr, struct pt_regs *regs) | ||
79 | { | ||
80 | unsigned long addr; | ||
81 | |||
82 | /* | ||
83 | * If not a valid kernel address, keep going till we find one | ||
84 | * or the SP stops being a valid address. | ||
85 | */ | ||
86 | do { | ||
87 | addr = *stackaddr++; | ||
88 | oprofile_add_trace(addr); | ||
89 | } while (valid_kernel_stack(stackaddr, regs)); | ||
90 | |||
91 | return stackaddr; | ||
92 | } | ||
93 | |||
94 | void sh_backtrace(struct pt_regs * const regs, unsigned int depth) | ||
95 | { | ||
96 | unsigned long *stackaddr; | ||
97 | |||
98 | /* | ||
99 | * Paranoia - clip max depth as we could get lost in the weeds. | ||
100 | */ | ||
101 | if (depth > backtrace_limit) | ||
102 | depth = backtrace_limit; | ||
103 | |||
104 | stackaddr = (unsigned long *)regs->regs[15]; | ||
105 | if (!user_mode(regs)) { | ||
106 | while (depth-- && valid_kernel_stack(stackaddr, regs)) | ||
107 | stackaddr = kernel_backtrace(stackaddr, regs); | ||
108 | |||
109 | return; | ||
110 | } | ||
111 | |||
112 | while (depth-- && (stackaddr != NULL)) | ||
113 | stackaddr = user_backtrace(stackaddr, regs); | ||
114 | } | ||
diff --git a/arch/sh/oprofile/common.c b/arch/sh/oprofile/common.c new file mode 100644 index 000000000000..1d97d64cb95f --- /dev/null +++ b/arch/sh/oprofile/common.c | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | * arch/sh/oprofile/init.c | ||
3 | * | ||
4 | * Copyright (C) 2003 - 2008 Paul Mundt | ||
5 | * | ||
6 | * Based on arch/mips/oprofile/common.c: | ||
7 | * | ||
8 | * Copyright (C) 2004, 2005 Ralf Baechle | ||
9 | * Copyright (C) 2005 MIPS Technologies, Inc. | ||
10 | * | ||
11 | * This file is subject to the terms and conditions of the GNU General Public | ||
12 | * License. See the file "COPYING" in the main directory of this archive | ||
13 | * for more details. | ||
14 | */ | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/oprofile.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/smp.h> | ||
20 | #include <asm/processor.h> | ||
21 | #include "op_impl.h" | ||
22 | |||
23 | extern struct op_sh_model op_model_sh7750_ops __weak; | ||
24 | extern struct op_sh_model op_model_sh4a_ops __weak; | ||
25 | |||
26 | static struct op_sh_model *model; | ||
27 | |||
28 | static struct op_counter_config ctr[20]; | ||
29 | |||
30 | extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth); | ||
31 | |||
32 | static int op_sh_setup(void) | ||
33 | { | ||
34 | /* Pre-compute the values to stuff in the hardware registers. */ | ||
35 | model->reg_setup(ctr); | ||
36 | |||
37 | /* Configure the registers on all cpus. */ | ||
38 | on_each_cpu(model->cpu_setup, NULL, 1); | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int op_sh_create_files(struct super_block *sb, struct dentry *root) | ||
44 | { | ||
45 | int i, ret = 0; | ||
46 | |||
47 | for (i = 0; i < model->num_counters; i++) { | ||
48 | struct dentry *dir; | ||
49 | char buf[4]; | ||
50 | |||
51 | snprintf(buf, sizeof(buf), "%d", i); | ||
52 | dir = oprofilefs_mkdir(sb, root, buf); | ||
53 | |||
54 | ret |= oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled); | ||
55 | ret |= oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event); | ||
56 | ret |= oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel); | ||
57 | ret |= oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user); | ||
58 | |||
59 | if (model->create_files) | ||
60 | ret |= model->create_files(sb, dir); | ||
61 | else | ||
62 | ret |= oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count); | ||
63 | |||
64 | /* Dummy entries */ | ||
65 | ret |= oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask); | ||
66 | } | ||
67 | |||
68 | return ret; | ||
69 | } | ||
70 | |||
71 | static int op_sh_start(void) | ||
72 | { | ||
73 | /* Enable performance monitoring for all counters. */ | ||
74 | on_each_cpu(model->cpu_start, NULL, 1); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static void op_sh_stop(void) | ||
80 | { | ||
81 | /* Disable performance monitoring for all counters. */ | ||
82 | on_each_cpu(model->cpu_stop, NULL, 1); | ||
83 | } | ||
84 | |||
85 | int __init oprofile_arch_init(struct oprofile_operations *ops) | ||
86 | { | ||
87 | struct op_sh_model *lmodel = NULL; | ||
88 | int ret; | ||
89 | |||
90 | /* | ||
91 | * Always assign the backtrace op. If the counter initialization | ||
92 | * fails, we fall back to the timer which will still make use of | ||
93 | * this. | ||
94 | */ | ||
95 | ops->backtrace = sh_backtrace; | ||
96 | |||
97 | switch (current_cpu_data.type) { | ||
98 | /* SH-4 types */ | ||
99 | case CPU_SH7750: | ||
100 | case CPU_SH7750S: | ||
101 | lmodel = &op_model_sh7750_ops; | ||
102 | break; | ||
103 | |||
104 | /* SH-4A types */ | ||
105 | case CPU_SH7763: | ||
106 | case CPU_SH7770: | ||
107 | case CPU_SH7780: | ||
108 | case CPU_SH7781: | ||
109 | case CPU_SH7785: | ||
110 | case CPU_SH7723: | ||
111 | case CPU_SHX3: | ||
112 | lmodel = &op_model_sh4a_ops; | ||
113 | break; | ||
114 | |||
115 | /* SH4AL-DSP types */ | ||
116 | case CPU_SH7343: | ||
117 | case CPU_SH7722: | ||
118 | case CPU_SH7366: | ||
119 | lmodel = &op_model_sh4a_ops; | ||
120 | break; | ||
121 | } | ||
122 | |||
123 | if (!lmodel) | ||
124 | return -ENODEV; | ||
125 | if (!(current_cpu_data.flags & CPU_HAS_PERF_COUNTER)) | ||
126 | return -ENODEV; | ||
127 | |||
128 | ret = lmodel->init(); | ||
129 | if (unlikely(ret != 0)) | ||
130 | return ret; | ||
131 | |||
132 | model = lmodel; | ||
133 | |||
134 | ops->setup = op_sh_setup; | ||
135 | ops->create_files = op_sh_create_files; | ||
136 | ops->start = op_sh_start; | ||
137 | ops->stop = op_sh_stop; | ||
138 | ops->cpu_type = lmodel->cpu_type; | ||
139 | |||
140 | printk(KERN_INFO "oprofile: using %s performance monitoring.\n", | ||
141 | lmodel->cpu_type); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | void oprofile_arch_exit(void) | ||
147 | { | ||
148 | if (model && model->exit) | ||
149 | model->exit(); | ||
150 | } | ||
diff --git a/arch/sh/oprofile/op_impl.h b/arch/sh/oprofile/op_impl.h new file mode 100644 index 000000000000..4d509975eba6 --- /dev/null +++ b/arch/sh/oprofile/op_impl.h | |||
@@ -0,0 +1,33 @@ | |||
1 | #ifndef __OP_IMPL_H | ||
2 | #define __OP_IMPL_H | ||
3 | |||
4 | /* Per-counter configuration as set via oprofilefs. */ | ||
5 | struct op_counter_config { | ||
6 | unsigned long enabled; | ||
7 | unsigned long event; | ||
8 | |||
9 | unsigned long long count; | ||
10 | |||
11 | /* Dummy values for userspace tool compliance */ | ||
12 | unsigned long kernel; | ||
13 | unsigned long user; | ||
14 | unsigned long unit_mask; | ||
15 | }; | ||
16 | |||
17 | /* Per-architecture configury and hooks. */ | ||
18 | struct op_sh_model { | ||
19 | void (*reg_setup)(struct op_counter_config *); | ||
20 | int (*create_files)(struct super_block *sb, struct dentry *dir); | ||
21 | void (*cpu_setup)(void *dummy); | ||
22 | int (*init)(void); | ||
23 | void (*exit)(void); | ||
24 | void (*cpu_start)(void *args); | ||
25 | void (*cpu_stop)(void *args); | ||
26 | char *cpu_type; | ||
27 | unsigned char num_counters; | ||
28 | }; | ||
29 | |||
30 | /* arch/sh/oprofile/common.c */ | ||
31 | extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth); | ||
32 | |||
33 | #endif /* __OP_IMPL_H */ | ||
diff --git a/arch/sh/oprofile/op_model_null.c b/arch/sh/oprofile/op_model_null.c deleted file mode 100644 index a845b088edb4..000000000000 --- a/arch/sh/oprofile/op_model_null.c +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | /* | ||
2 | * arch/sh/oprofile/op_model_null.c | ||
3 | * | ||
4 | * Copyright (C) 2003 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/oprofile.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/errno.h> | ||
14 | |||
15 | int __init oprofile_arch_init(struct oprofile_operations *ops) | ||
16 | { | ||
17 | return -ENODEV; | ||
18 | } | ||
19 | |||
20 | void oprofile_arch_exit(void) | ||
21 | { | ||
22 | } | ||
23 | |||
diff --git a/arch/sh/oprofile/op_model_sh7750.c b/arch/sh/oprofile/op_model_sh7750.c index 008b3b03750a..c892c7c30c2f 100644 --- a/arch/sh/oprofile/op_model_sh7750.c +++ b/arch/sh/oprofile/op_model_sh7750.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * OProfile support for SH7750/SH7750S Performance Counters | 4 | * OProfile support for SH7750/SH7750S Performance Counters |
5 | * | 5 | * |
6 | * Copyright (C) 2003, 2004 Paul Mundt | 6 | * Copyright (C) 2003 - 2008 Paul Mundt |
7 | * | 7 | * |
8 | * This file is subject to the terms and conditions of the GNU General Public | 8 | * This file is subject to the terms and conditions of the GNU General Public |
9 | * License. See the file "COPYING" in the main directory of this archive | 9 | * License. See the file "COPYING" in the main directory of this archive |
@@ -15,19 +15,16 @@ | |||
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
17 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
18 | #include <linux/io.h> | ||
18 | #include <linux/fs.h> | 19 | #include <linux/fs.h> |
19 | #include <asm/uaccess.h> | 20 | #include "op_impl.h" |
20 | #include <asm/io.h> | ||
21 | 21 | ||
22 | #define PM_CR_BASE 0xff000084 /* 16-bit */ | 22 | #define PM_CR_BASE 0xff000084 /* 16-bit */ |
23 | #define PM_CTR_BASE 0xff100004 /* 32-bit */ | 23 | #define PM_CTR_BASE 0xff100004 /* 32-bit */ |
24 | 24 | ||
25 | #define PMCR1 (PM_CR_BASE + 0x00) | 25 | #define PMCR(n) (PM_CR_BASE + ((n) * 0x04)) |
26 | #define PMCR2 (PM_CR_BASE + 0x04) | 26 | #define PMCTRH(n) (PM_CTR_BASE + 0x00 + ((n) * 0x08)) |
27 | #define PMCTR1H (PM_CTR_BASE + 0x00) | 27 | #define PMCTRL(n) (PM_CTR_BASE + 0x04 + ((n) * 0x08)) |
28 | #define PMCTR1L (PM_CTR_BASE + 0x04) | ||
29 | #define PMCTR2H (PM_CTR_BASE + 0x08) | ||
30 | #define PMCTR2L (PM_CTR_BASE + 0x0c) | ||
31 | 28 | ||
32 | #define PMCR_PMM_MASK 0x0000003f | 29 | #define PMCR_PMM_MASK 0x0000003f |
33 | 30 | ||
@@ -36,25 +33,15 @@ | |||
36 | #define PMCR_PMST 0x00004000 | 33 | #define PMCR_PMST 0x00004000 |
37 | #define PMCR_PMEN 0x00008000 | 34 | #define PMCR_PMEN 0x00008000 |
38 | 35 | ||
39 | #define PMCR_ENABLE (PMCR_PMST | PMCR_PMEN) | 36 | struct op_sh_model op_model_sh7750_ops; |
40 | 37 | ||
41 | /* | ||
42 | * SH7750/SH7750S have 2 perf counters | ||
43 | */ | ||
44 | #define NR_CNTRS 2 | 38 | #define NR_CNTRS 2 |
45 | 39 | ||
46 | struct op_counter_config { | 40 | static struct sh7750_ppc_register_config { |
47 | unsigned long enabled; | 41 | unsigned int ctrl; |
48 | unsigned long event; | 42 | unsigned long cnt_hi; |
49 | unsigned long count; | 43 | unsigned long cnt_lo; |
50 | 44 | } regcache[NR_CNTRS]; | |
51 | /* Dummy values for userspace tool compliance */ | ||
52 | unsigned long kernel; | ||
53 | unsigned long user; | ||
54 | unsigned long unit_mask; | ||
55 | }; | ||
56 | |||
57 | static struct op_counter_config ctr[NR_CNTRS]; | ||
58 | 45 | ||
59 | /* | 46 | /* |
60 | * There are a number of events supported by each counter (33 in total). | 47 | * There are a number of events supported by each counter (33 in total). |
@@ -116,12 +103,8 @@ static int sh7750_timer_notify(struct pt_regs *regs) | |||
116 | 103 | ||
117 | static u64 sh7750_read_counter(int counter) | 104 | static u64 sh7750_read_counter(int counter) |
118 | { | 105 | { |
119 | u32 hi, lo; | 106 | return (u64)((u64)(__raw_readl(PMCTRH(counter)) & 0xffff) << 32) | |
120 | 107 | __raw_readl(PMCTRL(counter)); | |
121 | hi = (counter == 0) ? ctrl_inl(PMCTR1H) : ctrl_inl(PMCTR2H); | ||
122 | lo = (counter == 0) ? ctrl_inl(PMCTR1L) : ctrl_inl(PMCTR2L); | ||
123 | |||
124 | return (u64)((u64)(hi & 0xffff) << 32) | lo; | ||
125 | } | 108 | } |
126 | 109 | ||
127 | /* | 110 | /* |
@@ -170,11 +153,7 @@ static ssize_t sh7750_write_count(struct file *file, const char __user *buf, | |||
170 | */ | 153 | */ |
171 | WARN_ON(val != 0); | 154 | WARN_ON(val != 0); |
172 | 155 | ||
173 | if (counter == 0) { | 156 | __raw_writew(__raw_readw(PMCR(counter)) | PMCR_PMCLR, PMCR(counter)); |
174 | ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1); | ||
175 | } else { | ||
176 | ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2); | ||
177 | } | ||
178 | 157 | ||
179 | return count; | 158 | return count; |
180 | } | 159 | } |
@@ -184,88 +163,93 @@ static const struct file_operations count_fops = { | |||
184 | .write = sh7750_write_count, | 163 | .write = sh7750_write_count, |
185 | }; | 164 | }; |
186 | 165 | ||
187 | static int sh7750_perf_counter_create_files(struct super_block *sb, struct dentry *root) | 166 | static int sh7750_ppc_create_files(struct super_block *sb, struct dentry *dir) |
188 | { | 167 | { |
189 | int i; | 168 | return oprofilefs_create_file(sb, dir, "count", &count_fops); |
169 | } | ||
190 | 170 | ||
191 | for (i = 0; i < NR_CNTRS; i++) { | 171 | static void sh7750_ppc_reg_setup(struct op_counter_config *ctr) |
192 | struct dentry *dir; | 172 | { |
193 | char buf[4]; | 173 | unsigned int counters = op_model_sh7750_ops.num_counters; |
174 | int i; | ||
194 | 175 | ||
195 | snprintf(buf, sizeof(buf), "%d", i); | 176 | for (i = 0; i < counters; i++) { |
196 | dir = oprofilefs_mkdir(sb, root, buf); | 177 | regcache[i].ctrl = 0; |
178 | regcache[i].cnt_hi = 0; | ||
179 | regcache[i].cnt_lo = 0; | ||
197 | 180 | ||
198 | oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled); | 181 | if (!ctr[i].enabled) |
199 | oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event); | 182 | continue; |
200 | oprofilefs_create_file(sb, dir, "count", &count_fops); | ||
201 | 183 | ||
202 | /* Dummy entries */ | 184 | regcache[i].ctrl |= ctr[i].event | PMCR_PMEN | PMCR_PMST; |
203 | oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel); | 185 | regcache[i].cnt_hi = (unsigned long)((ctr->count >> 32) & 0xffff); |
204 | oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user); | 186 | regcache[i].cnt_lo = (unsigned long)(ctr->count & 0xffffffff); |
205 | oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask); | ||
206 | } | 187 | } |
207 | |||
208 | return 0; | ||
209 | } | 188 | } |
210 | 189 | ||
211 | static int sh7750_perf_counter_start(void) | 190 | static void sh7750_ppc_cpu_setup(void *args) |
212 | { | 191 | { |
213 | u16 pmcr; | 192 | unsigned int counters = op_model_sh7750_ops.num_counters; |
214 | 193 | int i; | |
215 | /* Enable counter 1 */ | ||
216 | if (ctr[0].enabled) { | ||
217 | pmcr = ctrl_inw(PMCR1); | ||
218 | WARN_ON(pmcr & PMCR_PMEN); | ||
219 | |||
220 | pmcr &= ~PMCR_PMM_MASK; | ||
221 | pmcr |= ctr[0].event; | ||
222 | ctrl_outw(pmcr | PMCR_ENABLE, PMCR1); | ||
223 | } | ||
224 | |||
225 | /* Enable counter 2 */ | ||
226 | if (ctr[1].enabled) { | ||
227 | pmcr = ctrl_inw(PMCR2); | ||
228 | WARN_ON(pmcr & PMCR_PMEN); | ||
229 | 194 | ||
230 | pmcr &= ~PMCR_PMM_MASK; | 195 | for (i = 0; i < counters; i++) { |
231 | pmcr |= ctr[1].event; | 196 | __raw_writew(0, PMCR(i)); |
232 | ctrl_outw(pmcr | PMCR_ENABLE, PMCR2); | 197 | __raw_writel(regcache[i].cnt_hi, PMCTRH(i)); |
198 | __raw_writel(regcache[i].cnt_lo, PMCTRL(i)); | ||
233 | } | 199 | } |
234 | |||
235 | return register_timer_hook(sh7750_timer_notify); | ||
236 | } | 200 | } |
237 | 201 | ||
238 | static void sh7750_perf_counter_stop(void) | 202 | static void sh7750_ppc_cpu_start(void *args) |
239 | { | 203 | { |
240 | ctrl_outw(ctrl_inw(PMCR1) & ~PMCR_PMEN, PMCR1); | 204 | unsigned int counters = op_model_sh7750_ops.num_counters; |
241 | ctrl_outw(ctrl_inw(PMCR2) & ~PMCR_PMEN, PMCR2); | 205 | int i; |
242 | 206 | ||
243 | unregister_timer_hook(sh7750_timer_notify); | 207 | for (i = 0; i < counters; i++) |
208 | __raw_writew(regcache[i].ctrl, PMCR(i)); | ||
244 | } | 209 | } |
245 | 210 | ||
246 | static struct oprofile_operations sh7750_perf_counter_ops = { | 211 | static void sh7750_ppc_cpu_stop(void *args) |
247 | .create_files = sh7750_perf_counter_create_files, | ||
248 | .start = sh7750_perf_counter_start, | ||
249 | .stop = sh7750_perf_counter_stop, | ||
250 | }; | ||
251 | |||
252 | int __init oprofile_arch_init(struct oprofile_operations *ops) | ||
253 | { | 212 | { |
254 | if (!(current_cpu_data.flags & CPU_HAS_PERF_COUNTER)) | 213 | unsigned int counters = op_model_sh7750_ops.num_counters; |
255 | return -ENODEV; | 214 | int i; |
256 | 215 | ||
257 | ops = &sh7750_perf_counter_ops; | 216 | /* Disable the counters */ |
258 | ops->cpu_type = "sh/sh7750"; | 217 | for (i = 0; i < counters; i++) |
218 | __raw_writew(__raw_readw(PMCR(i)) & ~PMCR_PMEN, PMCR(i)); | ||
219 | } | ||
259 | 220 | ||
260 | printk(KERN_INFO "oprofile: using SH-4 performance monitoring.\n"); | 221 | static inline void sh7750_ppc_reset(void) |
222 | { | ||
223 | unsigned int counters = op_model_sh7750_ops.num_counters; | ||
224 | int i; | ||
261 | 225 | ||
262 | /* Clear the counters */ | 226 | /* Clear the counters */ |
263 | ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1); | 227 | for (i = 0; i < counters; i++) |
264 | ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2); | 228 | __raw_writew(__raw_readw(PMCR(i)) | PMCR_PMCLR, PMCR(i)); |
229 | } | ||
265 | 230 | ||
266 | return 0; | 231 | static int sh7750_ppc_init(void) |
232 | { | ||
233 | sh7750_ppc_reset(); | ||
234 | |||
235 | return register_timer_hook(sh7750_timer_notify); | ||
267 | } | 236 | } |
268 | 237 | ||
269 | void oprofile_arch_exit(void) | 238 | static void sh7750_ppc_exit(void) |
270 | { | 239 | { |
240 | unregister_timer_hook(sh7750_timer_notify); | ||
241 | |||
242 | sh7750_ppc_reset(); | ||
271 | } | 243 | } |
244 | |||
245 | struct op_sh_model op_model_sh7750_ops = { | ||
246 | .cpu_type = "sh/sh7750", | ||
247 | .num_counters = NR_CNTRS, | ||
248 | .reg_setup = sh7750_ppc_reg_setup, | ||
249 | .cpu_setup = sh7750_ppc_cpu_setup, | ||
250 | .cpu_start = sh7750_ppc_cpu_start, | ||
251 | .cpu_stop = sh7750_ppc_cpu_stop, | ||
252 | .init = sh7750_ppc_init, | ||
253 | .exit = sh7750_ppc_exit, | ||
254 | .create_files = sh7750_ppc_create_files, | ||
255 | }; | ||