Broadcom SDK and wireless driver: another attempt to update to ver. 5.10.147.0
[tomato.git] / release / src-rt / linux / linux-2.6 / arch / mips / brcm-boards / bcm947xx / perfcntr.c
blobbd222ef879f5499f3b4fd6894f190b98c9ed4231
1 /*
2 * Broadcom BCM47xx Performance Counters
4 * Copyright (C) 2009, Broadcom Corporation
5 * All Rights Reserved.
6 *
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>
17 #ifdef CONFIG_PROC_FS
18 #include <linux/proc_fs.h>
19 #include <typedefs.h>
20 #include <osl.h>
21 #include <mipsinc.h>
23 /*
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
31 #define PERF_ICOUNT 4
33 asmlinkage uint read_perf_cntr(uint counter)
35 uint32 prid = MFC0(C0_PRID, 0);
36 if (BCM330X(prid)) {
37 #ifdef CONFIG_HND_BMIPS3300_PROF
38 switch (counter) {
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 */
45 return 0;
47 else {
48 switch (counter) {
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);
56 return 0;
60 * 'data' passed to proc entry callback is formatted as:
61 * (reg << 16) + sel
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)
72 __asm__ __volatile__(
73 ".set\tnoreorder\n\t"
74 ".set\tnoat\n\t"
75 "mfc0\t$1,$0\n\t"
76 "move\t$2,$1\n\t"
77 ".set\tat\n\t"
78 ".set\treorder");
81 static void tmtc0(uint val)
83 __asm__ __volatile__(
84 ".set\tnoreorder\n\t"
85 ".set\tnoat\n\t"
86 "move\t$1,$4\n\t"
87 "mtc0\t$1,$0\n\t"
88 ".set\tat\n\t"
89 ".set\treorder");
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
96 * mtc0 instructions.
98 static int cp0_read(char *page, char **start, off_t off,
99 int count, int *eof, void *data)
101 uint reg, sel;
102 uint (*cp0i)(void);
103 size_t len;
105 /* we have done once so stop */
106 if (off) {
107 len = 0;
108 goto done;
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());
120 *start = page;
121 done: return len;
124 static int cp0_write(struct file *file, const char *buf,
125 unsigned long count, void *data)
127 uint reg, sel, val;
128 void (*cp0i)(uint);
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 */
139 cp0i(val);
140 return count;
143 #ifdef CONFIG_HND_BMIPS3300_PROF
145 * Enable/disable cache hits/misses countings - counters are
146 * hard wired as:
147 * d$ - cntr 0 and 1
148 * i$ - cntr 2 and 3
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)
154 if (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");
162 else {
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)
171 if (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");
179 else {
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) {
195 case DCACHE_CNTENAB:
196 bmips3300_dccntenab(val != 0);
197 break;
198 case ICACHE_CNTENAB:
199 bmips3300_iccntenab(val != 0);
200 break;
202 return count;
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))
211 typedef struct
213 uint32 reg_map; /* registers map */
214 uint8 sel_map[MAXSELS]; /* selects map */
215 } cp0_reg_map_t;
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),
226 /* 0 */ CP0SEL(0),
227 /* 1 */ CP0SEL(0),
228 /* 2 */ CP0SEL(0),
229 /* 3 */ CP0SEL(0),
230 /* 4 */ CP0SEL(0),
231 /* 5 */ CP0SEL(0),
232 /* 6 */ CP0SEL(0),
233 /* 7 */ CP0SEL(0),
234 /* 8 */ CP0SEL(0),
235 /* 9 */ CP0SEL(0),
236 /* 10 */ CP0SEL(0),
237 /* 11 */ CP0SEL(0),
238 /* 12 */ CP0SEL(0),
239 /* 13 */ CP0SEL(0),
240 /* 14 */ CP0SEL(0),
241 /* 15 */ CP0SEL(0),
242 /* 16 */ CP0SEL(0)|CP0SEL(1),
248 /* 22 */ CP0SEL(0)|CP0SEL(4)|CP0SEL(5),
249 /* 23 */ CP0SEL(0),
250 /* 24 */ CP0SEL(0),
251 /* 25 */ CP0SEL(0)|CP0SEL(1)|CP0SEL(2)|CP0SEL(3)|CP0SEL(4)|
252 CP0SEL(5)|CP0SEL(6),
255 /* 28 */ CP0SEL(0)|CP0SEL(1),
257 /* 30 */ CP0SEL(0),
258 /* 31 */ CP0SEL(0),
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;
272 uint32 prid;
273 char name[16];
274 int i, j;
276 /* create proc entry cp0 in root */
277 cp0_proc = create_proc_entry("cp0", 0444 | S_IFDIR, &proc_root);
278 if (!cp0_proc)
279 return 0;
281 /* create proc entries for enabling cache hit/miss counting */
282 prid = MFC0(C0_PRID, 0);
283 if (BCM330X(prid)) {
284 #ifdef CONFIG_HND_BMIPS3300_PROF
285 /* D$ */
286 cache_proc = create_proc_entry("dccnt", 0644, cp0_proc);
287 if (!cache_proc)
288 return 0;
289 cache_proc->write_proc = bmips3300_ccntenab;
290 cache_proc->data = (void *)DCACHE_CNTENAB;
291 /* I$ */
292 cache_proc = create_proc_entry("iccnt", 0644, cp0_proc);
293 if (!cache_proc)
294 return 0;
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 */
301 if (BCM330X(prid)) {
302 #ifdef CONFIG_HND_BMIPS3300_PROF
303 reg_map = &bmips3300_cp0regmap;
304 #endif /* CONFIG_HND_BMIPS3300_PROF */
306 if (!reg_map)
307 return 0;
308 /* create proc entry for each register select */
309 for (i = 0; i < MAXREGS; i ++) {
310 if (!(reg_map->reg_map & (1 << i)))
311 continue;
312 sprintf(name, "%u", i);
313 reg_proc = create_proc_entry(name, 0444 | S_IFDIR, cp0_proc);
314 if (!reg_proc)
315 break;
316 for (j = 0; j < MAXSELS; j ++) {
317 if (!(reg_map->sel_map[i] & (1 << j)))
318 continue;
319 sprintf(name, "%u", j);
320 sel_proc = create_proc_entry(name, 0644, reg_proc);
321 if (!sel_proc)
322 break;
323 sel_proc->read_proc = cp0_read;
324 sel_proc->write_proc = cp0_write;
325 sel_proc->data = (void *)((i << REGSHIFT) + j);
328 return 0;
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 */