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