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/config.h> 22 23#ifdef CONFIG_PROC_FS 24#include <linux/proc_fs.h> 25#include <typedefs.h> 26#include <osl.h> 27#include <asm/mipsregs.h> 28#include <mipsinc.h> 29 30 31asmlinkage uint 32read_perf_cntr(uint sel) 33{ 34 /* Beware: Don't collapse this into a single call to read_c0_perf, 35 * it is not really a function, it is a macro that generates a 36 * different assembly instruction in each case. 37 */ 38 switch (sel) { 39 case 0: 40 return read_c0_perf(0); 41 case 1: 42 return read_c0_perf(1); 43 case 2: 44 return read_c0_perf(2); 45 case 3: 46 return read_c0_perf(3); 47 case 4: 48 return read_c0_perf(4); 49 case 5: 50 return read_c0_perf(5); 51 case 6: 52 return read_c0_perf(6); 53 case 7: 54 return read_c0_perf(7); 55 default: 56 return 0; 57 } 58} 59 60static uint perf_ctrl = 0; 61 62static int 63ctrl_read(char *page, char **start, off_t off, 64 int count, int *eof, void *data) 65{ 66 size_t len = 0; 67 68 /* we have done once so stop */ 69 if (off) 70 return 0; 71 72 if (data == NULL) { 73 /* return the value in hex string */ 74 len = sprintf(page, "0x%08x\n", perf_ctrl); 75 } else { 76 len = sprintf(page, "0x%08x 0x%08x 0x%08x 0x%08x\n", 77 read_perf_cntr(0), read_perf_cntr(2), 78 read_perf_cntr(4), read_perf_cntr(6)); 79 } 80 *start = page; 81 return len; 82} 83 84static void 85set_ctrl(uint value) 86{ 87 uint event; 88 89 event = (value >> 24) & 0x7f; 90 if (event != ((perf_ctrl >> 24) & 0x7f)) { 91 write_c0_perf(0, 0); 92 write_c0_perf(1, 0); 93 if (event != 0x7f) 94 write_c0_perf(0, (event << 5) | 0xf); 95 } 96 event = (value >> 16) & 0x7f; 97 if (event != ((perf_ctrl >> 16) & 0x7f)) { 98 write_c0_perf(2, 0); 99 write_c0_perf(3, 0); 100 if (event != 0x7f) 101 write_c0_perf(2, (event << 5) | 0xf); 102 } 103 event = (value >> 8) & 0x7f; 104 if (event != ((perf_ctrl >> 8) & 0x7f)) { 105 write_c0_perf(4, 0); 106 write_c0_perf(5, 0); 107 if (event != 0x7f) 108 write_c0_perf(4, (event << 5) | 0xf); 109 } 110 event = value & 0x7f; 111 if (event != (perf_ctrl & 0x7f)) { 112 write_c0_perf(6, 0); 113 write_c0_perf(7, 0); 114 if (event != 0x7f) 115 write_c0_perf(6, (event << 5) | 0xf); 116 } 117 perf_ctrl = value & 0x7f7f7f7f; 118} 119 120static int 121ctrl_write(struct file *file, const char *buf, 122 unsigned long count, void *data) 123{ 124 uint value; 125 126 value = simple_strtoul(buf, NULL, 0); 127 128 set_ctrl(value); 129 130 return count; 131} 132 133 134static int 135cntrs_read(char *page, char **start, off_t off, 136 int count, int *eof, void *data) 137{ 138 size_t len = 0; 139 140 /* we have done once so stop */ 141 if (off) 142 return 0; 143 144 /* return the values in hex string */ 145 len = sprintf(page, "%10u %10u %10u %10u\n", 146 read_perf_cntr(1), read_perf_cntr(3), 147 read_perf_cntr(5), read_perf_cntr(7)); 148 *start = page; 149 return len; 150} 151 152static void 153cntrs_clear(void) 154{ 155 write_c0_perf(1, 0); 156 write_c0_perf(3, 0); 157 write_c0_perf(5, 0); 158 write_c0_perf(7, 0); 159} 160 161static int 162clear_write(struct file *file, const char *buf, 163 unsigned long count, void *data) 164{ 165 cntrs_clear(); 166 167 return count; 168} 169 170static struct proc_dir_entry *perf_proc = NULL; 171 172static int __init 173perf_init(void) 174{ 175 struct proc_dir_entry *ctrl_proc, *ctrlraw_proc, *cntrs_proc, *clear_proc; 176 uint prid; 177 178 179 /* create proc entries for enabling cache hit/miss counting */ 180 prid = read_c0_prid(); 181 if (((prid & PRID_COMP_MASK) == PRID_COMP_MIPS) && 182 ((prid & PRID_IMP_MASK) == PRID_IMP_74K)) { 183 184 /* create proc entry cp0 in root */ 185 perf_proc = create_proc_entry("perf", 0444 | S_IFDIR, &proc_root); 186 if (!perf_proc) 187 return -ENOMEM; 188 189 ctrl_proc = create_proc_entry("ctrl", 0644, perf_proc); 190 if (!ctrl_proc) 191 goto noctrl; 192 ctrl_proc->data = NULL; 193 ctrl_proc->read_proc = ctrl_read; 194 ctrl_proc->write_proc = ctrl_write; 195 196 ctrlraw_proc = create_proc_entry("ctrlraw", 0444, perf_proc); 197 if (!ctrlraw_proc) 198 goto noctrlraw; 199 ctrlraw_proc->data = (void *)1; 200 ctrlraw_proc->read_proc = ctrl_read; 201 202 cntrs_proc = create_proc_entry("cntrs", 0444, perf_proc); 203 if (!cntrs_proc) 204 goto nocntrs; 205 cntrs_proc->read_proc = cntrs_read; 206 207 clear_proc = create_proc_entry("clear", 0444, perf_proc); 208 if (!clear_proc) 209 goto noclear; 210 clear_proc->write_proc = clear_write; 211 } 212 213 /* Initialize off */ 214 set_ctrl(0x7f7f7f7f); 215 return 0; 216noclear: 217 remove_proc_entry("cntrs", perf_proc); 218nocntrs: 219 remove_proc_entry("ctrlraw", perf_proc); 220noctrlraw: 221 remove_proc_entry("ctrl", perf_proc); 222noctrl: 223 remove_proc_entry("perf", &proc_root); 224 225 return -ENOMEM; 226} 227 228static void __exit perf_cleanup(void) 229{ 230 remove_proc_entry("clear", perf_proc); 231 remove_proc_entry("cntrs", perf_proc); 232 remove_proc_entry("ctrlraw", perf_proc); 233 remove_proc_entry("ctrl", perf_proc); 234 remove_proc_entry("perf", &proc_root); 235 perf_proc = NULL; 236} 237 238/* hook it up with system at boot time */ 239module_init(perf_init); 240module_exit(perf_cleanup); 241 242#endif /* CONFIG_PROC_FS */ 243