diff options
-rw-r--r-- | arch/sh/oprofile/op_model_sh7750.c | 172 |
1 files changed, 78 insertions, 94 deletions
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 | }; | ||