• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/arch/mips/brcm-boards/bcm947xx/
1/*
2 * Broadcom BCM47xx Performance Counters
3 *
4 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: perfcntr.c 241435 2011-02-18 04:04:21Z $
19 */
20
21#include <linux/version.h>
22
23#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
24#include <linux/config.h>
25#endif
26
27#ifdef CONFIG_PROC_FS
28#include <linux/proc_fs.h>
29#include <typedefs.h>
30#include <osl.h>
31#include <asm/mipsregs.h>
32#include <mipsinc.h>
33
34asmlinkage uint
35read_perf_cntr(uint sel)
36{
37	/* Beware: Don't collapse this into a single call to read_c0_perf,
38	 * it is not really a function, it is a macro that generates a
39	 * different assembly instruction in each case.
40	 */
41	switch (sel) {
42#ifdef	read_c0_perf
43	case 0:
44		return read_c0_perf(0);
45	case 1:
46		return read_c0_perf(1);
47	case 2:
48		return read_c0_perf(2);
49	case 3:
50		return read_c0_perf(3);
51	case 4:
52		return read_c0_perf(4);
53	case 5:
54		return read_c0_perf(5);
55	case 6:
56		return read_c0_perf(6);
57	case 7:
58		return read_c0_perf(7);
59#else
60	/* New style register access macros - LR */
61	case 0:	return read_c0_perfctrl0();	/* Control */
62	case 2:	return read_c0_perfctrl1();
63	case 4:	return read_c0_perfctrl2();
64	case 6:	return read_c0_perfctrl3();
65	case 1: return read_c0_perfcntr0();	/* Counter */
66	case 3: return read_c0_perfcntr1();
67	case 5: return read_c0_perfcntr2();
68	case 7: return read_c0_perfcntr3();
69#endif
70	default:
71		return 0;
72	}
73}
74
75/* HACK: map the old-style CP0 access macro */
76#ifndef	write_c0_perf
77#define write_c0_perf(sel,val) __write_32bit_c0_register($25, sel, val)
78#endif
79
80static uint perf_ctrl = 0;
81
82static int
83ctrl_read(char *page, char **start, off_t off,
84          int count, int *eof, void *data)
85{
86	size_t len = 0;
87
88	/* we have done once so stop */
89	if (off)
90		return 0;
91
92	if (data == NULL) {
93		/* return the value in hex string */
94		len = sprintf(page, "0x%08x\n", perf_ctrl);
95	} else {
96		len = sprintf(page, "0x%08x 0x%08x 0x%08x 0x%08x\n",
97		              read_perf_cntr(0), read_perf_cntr(2),
98		              read_perf_cntr(4), read_perf_cntr(6));
99	}
100	*start = page;
101	return len;
102}
103
104static void
105set_ctrl(uint value)
106{
107	uint event;
108
109	event = (value >> 24) & 0x7f;
110	if (event != ((perf_ctrl >> 24) & 0x7f)) {
111		write_c0_perf(0, 0);
112		write_c0_perf(1, 0);
113		if (event != 0x7f)
114			write_c0_perf(0, (event << 5) | 0xf);
115	}
116	event = (value >> 16) & 0x7f;
117	if (event != ((perf_ctrl >> 16) & 0x7f)) {
118		write_c0_perf(2, 0);
119		write_c0_perf(3, 0);
120		if (event != 0x7f)
121			write_c0_perf(2, (event << 5) | 0xf);
122	}
123	event = (value >> 8) & 0x7f;
124	if (event != ((perf_ctrl >> 8) & 0x7f)) {
125		write_c0_perf(4, 0);
126		write_c0_perf(5, 0);
127		if (event != 0x7f)
128			write_c0_perf(4, (event << 5) | 0xf);
129	}
130	event = value & 0x7f;
131	if (event != (perf_ctrl & 0x7f)) {
132		write_c0_perf(6, 0);
133		write_c0_perf(7, 0);
134		if (event != 0x7f)
135			write_c0_perf(6, (event << 5) | 0xf);
136	}
137	perf_ctrl = value & 0x7f7f7f7f;
138}
139
140static int
141ctrl_write(struct file *file, const char *buf,
142           unsigned long count, void *data)
143{
144	uint value;
145
146	value = simple_strtoul(buf, NULL, 0);
147
148	set_ctrl(value);
149
150	return count;
151}
152
153
154static int
155cntrs_read(char *page, char **start, off_t off,
156          int count, int *eof, void *data)
157{
158	size_t len = 0;
159
160	/* we have done once so stop */
161	if (off)
162		return 0;
163
164	/* return the values in hex string */
165	len = sprintf(page, "%10u %10u %10u %10u\n",
166	              read_perf_cntr(1), read_perf_cntr(3),
167	              read_perf_cntr(5), read_perf_cntr(7));
168	*start = page;
169	return len;
170}
171
172static void
173cntrs_clear(void)
174{
175	write_c0_perf(1, 0);
176	write_c0_perf(3, 0);
177	write_c0_perf(5, 0);
178	write_c0_perf(7, 0);
179}
180
181static int
182clear_write(struct file *file, const char *buf,
183           unsigned long count, void *data)
184{
185	cntrs_clear();
186
187	return count;
188}
189
190static struct proc_dir_entry *perf_proc = NULL;
191
192static int __init
193perf_init(void)
194{
195	struct proc_dir_entry * ctrl_proc, * ctrlraw_proc ;
196	struct proc_dir_entry * cntrs_proc, *clear_proc;
197	struct proc_dir_entry * proc_root_ptr ;
198	uint prid;
199
200#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
201	proc_root_ptr = NULL ;
202#else
203	proc_root_ptr = &proc_root ;
204#endif
205
206	/* create proc entries for enabling cache hit/miss counting */
207	prid = read_c0_prid();
208	if (((prid & PRID_COMP_MASK) == PRID_COMP_MIPS) &&
209	    ((prid & PRID_IMP_MASK) == PRID_IMP_74K)) {
210
211		/* create proc entry cp0 in root */
212		perf_proc = create_proc_entry("perf", 0444 | S_IFDIR,
213			proc_root_ptr);
214		if (!perf_proc)
215			return -ENOMEM;
216
217		ctrl_proc = create_proc_entry("ctrl", 0644, perf_proc);
218		if (!ctrl_proc)
219			goto noctrl;
220		ctrl_proc->data = NULL;
221		ctrl_proc->read_proc = ctrl_read;
222		ctrl_proc->write_proc = ctrl_write;
223
224		ctrlraw_proc = create_proc_entry("ctrlraw", 0444, perf_proc);
225		if (!ctrlraw_proc)
226			goto noctrlraw;
227		ctrlraw_proc->data = (void *)1;
228		ctrlraw_proc->read_proc = ctrl_read;
229
230		cntrs_proc = create_proc_entry("cntrs", 0444, perf_proc);
231		if (!cntrs_proc)
232			goto nocntrs;
233		cntrs_proc->read_proc = cntrs_read;
234
235		clear_proc = create_proc_entry("clear", 0444, perf_proc);
236		if (!clear_proc)
237			goto noclear;
238		clear_proc->write_proc = clear_write;
239	}
240
241	/* Initialize off */
242	set_ctrl(0x7f7f7f7f);
243	return 0;
244noclear:
245	remove_proc_entry("cntrs", perf_proc);
246nocntrs:
247	remove_proc_entry("ctrlraw", perf_proc);
248noctrlraw:
249	remove_proc_entry("ctrl", perf_proc);
250noctrl:
251	remove_proc_entry("perf", proc_root_ptr);
252
253	return -ENOMEM;
254}
255
256static void __exit perf_cleanup(void)
257{
258	struct proc_dir_entry * proc_root_ptr ;
259#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
260	proc_root_ptr = NULL ;
261#else
262	proc_root_ptr = &proc_root ;
263#endif
264
265	remove_proc_entry("clear", perf_proc);
266	remove_proc_entry("cntrs", perf_proc);
267	remove_proc_entry("ctrlraw", perf_proc);
268	remove_proc_entry("ctrl", perf_proc);
269	remove_proc_entry("perf", proc_root_ptr);
270	perf_proc = NULL;
271}
272
273/* hook it up with system at boot time */
274module_init(perf_init);
275module_exit(perf_cleanup);
276
277#endif	/* CONFIG_PROC_FS */
278