2 * Broadcom BCM47xx Performance Counters
4 * Copyright (C) 2009, Broadcom Corporation
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id: perfcntr.c,v 1.1 2007/09/04 04:45:20 Exp $
15 #include <linux/config.h>
18 #include <linux/proc_fs.h>
24 * BCM4710 performance counter register select values
25 * No even-odd control-counter mapping, just counters
27 #define PERF_DCACHE_HIT 0
28 #define PERF_DCACHE_MISS 1
29 #define PERF_ICACHE_HIT 2
30 #define PERF_ICACHE_MISS 3
33 asmlinkage uint
read_perf_cntr(uint counter
)
35 uint32 prid
= MFC0(C0_PRID
, 0);
37 #ifdef CONFIG_HND_BMIPS3300_PROF
39 case PERF_DCACHE_HIT
: return -MFC0(C0_PERFORMANCE
, 0);
40 case PERF_DCACHE_MISS
: return -MFC0(C0_PERFORMANCE
, 1);
41 case PERF_ICACHE_HIT
: return -MFC0(C0_PERFORMANCE
, 2);
42 case PERF_ICACHE_MISS
: return -MFC0(C0_PERFORMANCE
, 3);
44 #endif /* CONFIG_HND_BMIPS3300_PROF */
49 case PERF_DCACHE_HIT
: return MFC0(C0_PERFORMANCE
, 0);
50 case PERF_DCACHE_MISS
: return MFC0(C0_PERFORMANCE
, 1);
51 case PERF_ICACHE_HIT
: return MFC0(C0_PERFORMANCE
, 2);
52 case PERF_ICACHE_MISS
: return MFC0(C0_PERFORMANCE
, 3);
53 case PERF_ICOUNT
: return MFC0(C0_PERFORMANCE
, 4);
60 * 'data' passed to proc entry callback is formatted as:
63 #define REGSHIFT 16 /* register # is at high 16 bit */
64 #define SELMASK 0xffff /* select # is at low 16 bit */
67 * Template to read/write cp0 register. Caller will modify
68 * the mfc0 and mtc0 instructions before calling.
70 static void tmfc0(void)
81 static void tmtc0(uint val
)
93 * Read/write cp0 register. Assuming code space is writeable
94 * so modify the mfc0 and mtc0 instructions before calling the
95 * function tmfc0() and tmtc0() to build the correct mfc0 and
98 static int cp0_read(char *page
, char **start
, off_t off
,
99 int count
, int *eof
, void *data
)
105 /* we have done once so stop */
111 /* change the mfc0 instr with the right reg/sel */
112 reg
= (uintptr
)data
>> REGSHIFT
;
113 sel
= (uintptr
)data
& SELMASK
;
114 cp0i
= (uint (*)(void))KSEG1ADDR(tmfc0
);
115 *(uint
*)cp0i
= 0x40010000 | (reg
<< 11) | sel
;
116 __asm__
__volatile__("nop; nop; nop; nop;");
118 /* return the value in hex string */
119 len
= sprintf(page
, "0x%08x\n", cp0i());
124 static int cp0_write(struct file
*file
, const char *buf
,
125 unsigned long count
, void *data
)
130 /* change the mtc0 instr with the right reg/sel */
131 reg
= (uintptr
)data
>> REGSHIFT
;
132 sel
= (uintptr
)data
& SELMASK
;
133 val
= simple_strtoul(buf
, NULL
, 0);
134 cp0i
= (void (*)(uint
))KSEG1ADDR(tmtc0
);
135 *((uint
*)cp0i
+ 1) = 0x40810000 | (reg
<< 11) | sel
;
136 __asm__
__volatile__("nop; nop; nop; nop;");
138 /* set the value and we are all done */
143 #ifdef CONFIG_HND_BMIPS3300_PROF
145 * Enable/disable cache hits/misses countings - counters are
149 * They can't be enabled at the same time according to the BMIPS
150 * 3300 documentations, although they are displayed togather.
152 static void bmips3300_dccntenab(bool enable
)
155 MTC0(C0_PERFORMANCE
, 6, 0x80000211); /* enable D$ counting */
156 MTC0(C0_PERFORMANCE
, 4, 0x80248028); /* enable cntr 0 and 1 */
157 MTC0(C0_PERFORMANCE
, 0, 0); /* zero cntr 0 - # hits */
158 MTC0(C0_PERFORMANCE
, 1, 0); /* zero cntr 1 - # misses */
159 printk("enabled performance counter 0 for D$ hits\n");
160 printk("enabled performance counter 1 for D$ misses\n");
163 MTC0(C0_PERFORMANCE
, 4, 0); /* disable cntr 0 and 1 */
164 MTC0(C0_PERFORMANCE
, 6, 0); /* disable D$ counting */
165 printk("disabled performance counters\n");
169 static void bmips3300_iccntenab(bool enable
)
172 MTC0(C0_PERFORMANCE
, 6, 0x80000218); /* enable I$ counting */
173 MTC0(C0_PERFORMANCE
, 5, 0x80148018); /* enable cntr 2 and 3 */
174 MTC0(C0_PERFORMANCE
, 2, 0); /* zero cntr 0 - # hits */
175 MTC0(C0_PERFORMANCE
, 3, 0); /* zero cntr 1 - # misses */
176 printk("enabled performance counter 2 for I$ hits\n");
177 printk("enabled performance counter 3 for I$ misses\n");
180 MTC0(C0_PERFORMANCE
, 5, 0); /* disable cntr 2 and 3 */
181 MTC0(C0_PERFORMANCE
, 6, 0); /* disable I$ counting */
182 printk("disabled performance counters\n");
186 /* cache counting enable/disable proc entry callback */
187 #define DCACHE_CNTENAB 0
188 #define ICACHE_CNTENAB 1
190 static int bmips3300_ccntenab(struct file
*file
, const char *buf
,
191 unsigned long count
, void *data
)
193 uint val
= simple_strtoul(buf
, NULL
, 0);
194 switch ((uint
)data
) {
196 bmips3300_dccntenab(val
!= 0);
199 bmips3300_iccntenab(val
!= 0);
204 #endif /* CONFIG_HND_BMIPS3300_PROF */
206 /* cp0 registers/selects map */
207 #define MAXREGS 32 /* max # registers */
208 #define MAXSELS 32 /* max # selects */
209 #define CP0REG(r) (1<<(r))
210 #define CP0SEL(s) (1<<(s))
213 uint32 reg_map
; /* registers map */
214 uint8 sel_map
[MAXSELS
]; /* selects map */
217 #ifdef CONFIG_HND_BMIPS3300_PROF
218 static cp0_reg_map_t bmips3300_cp0regmap
=
220 CP0REG(0)|CP0REG(1)|CP0REG(2)|CP0REG(3)|CP0REG(4)|CP0REG(5)|
221 CP0REG(6)|CP0REG(7)|CP0REG(8)|CP0REG(9)|CP0REG(10)|
222 CP0REG(11)|CP0REG(12)|CP0REG(13)|CP0REG(14)|CP0REG(15)|
223 CP0REG(16)|CP0REG(22)|CP0REG(23)|CP0REG(24)|CP0REG(25)|
224 CP0REG(28)|CP0REG(30)|CP0REG(31),
242 /* 16 */ CP0SEL(0)|CP0SEL(1),
248 /* 22 */ CP0SEL(0)|CP0SEL(4)|CP0SEL(5),
251 /* 25 */ CP0SEL(0)|CP0SEL(1)|CP0SEL(2)|CP0SEL(3)|CP0SEL(4)|
255 /* 28 */ CP0SEL(0)|CP0SEL(1),
261 #endif /* CONFIG_HND_BMIPS3300_PROF */
263 /* create/remove proc entries - no worry about error handling;-( */
264 static int __init
cp0_init(void)
266 struct proc_dir_entry
*cp0_proc
;
267 #ifdef CONFIG_HND_BMIPS3300_PROF
268 struct proc_dir_entry
*cache_proc
;
269 #endif /* CONFIG_HND_BMIPS3300_PROF */
270 struct proc_dir_entry
*reg_proc
, *sel_proc
;
271 cp0_reg_map_t
*reg_map
= NULL
;
276 /* create proc entry cp0 in root */
277 cp0_proc
= create_proc_entry("cp0", 0444 | S_IFDIR
, &proc_root
);
281 /* create proc entries for enabling cache hit/miss counting */
282 prid
= MFC0(C0_PRID
, 0);
284 #ifdef CONFIG_HND_BMIPS3300_PROF
286 cache_proc
= create_proc_entry("dccnt", 0644, cp0_proc
);
289 cache_proc
->write_proc
= bmips3300_ccntenab
;
290 cache_proc
->data
= (void *)DCACHE_CNTENAB
;
292 cache_proc
= create_proc_entry("iccnt", 0644, cp0_proc
);
295 cache_proc
->write_proc
= bmips3300_ccntenab
;
296 cache_proc
->data
= (void *)ICACHE_CNTENAB
;
297 #endif /* CONFIG_HND_BMIPS3300_PROF */
300 /* select cp0 registers/selects map table */
302 #ifdef CONFIG_HND_BMIPS3300_PROF
303 reg_map
= &bmips3300_cp0regmap
;
304 #endif /* CONFIG_HND_BMIPS3300_PROF */
308 /* create proc entry for each register select */
309 for (i
= 0; i
< MAXREGS
; i
++) {
310 if (!(reg_map
->reg_map
& (1 << i
)))
312 sprintf(name
, "%u", i
);
313 reg_proc
= create_proc_entry(name
, 0444 | S_IFDIR
, cp0_proc
);
316 for (j
= 0; j
< MAXSELS
; j
++) {
317 if (!(reg_map
->sel_map
[i
] & (1 << j
)))
319 sprintf(name
, "%u", j
);
320 sel_proc
= create_proc_entry(name
, 0644, reg_proc
);
323 sel_proc
->read_proc
= cp0_read
;
324 sel_proc
->write_proc
= cp0_write
;
325 sel_proc
->data
= (void *)((i
<< REGSHIFT
) + j
);
331 static void __exit
cp0_cleanup(void)
333 remove_proc_entry("cp0", &proc_root
);
336 /* hook it up with system at boot time */
337 module_init(cp0_init
);
338 module_exit(cp0_cleanup
);
340 #endif /* CONFIG_PROC_FS */