1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2004, 05, 06 by Ralf Baechle 7 * Copyright (C) 2005 by MIPS Technologies, Inc. 8 */ 9#include <linux/oprofile.h> 10#include <linux/interrupt.h> 11#include <linux/smp.h> 12#include <asm/irq_regs.h> 13 14#include "op_impl.h" 15 16#define M_PERFCTL_EXL (1UL << 0) 17#define M_PERFCTL_KERNEL (1UL << 1) 18#define M_PERFCTL_SUPERVISOR (1UL << 2) 19#define M_PERFCTL_USER (1UL << 3) 20#define M_PERFCTL_INTERRUPT_ENABLE (1UL << 4) 21#define M_PERFCTL_EVENT(event) (((event) & 0x3f) << 5) 22#define M_PERFCTL_VPEID(vpe) ((vpe) << 16) 23#define M_PERFCTL_MT_EN(filter) ((filter) << 20) 24#define M_TC_EN_ALL M_PERFCTL_MT_EN(0) 25#define M_TC_EN_VPE M_PERFCTL_MT_EN(1) 26#define M_TC_EN_TC M_PERFCTL_MT_EN(2) 27#define M_PERFCTL_TCID(tcid) ((tcid) << 22) 28#define M_PERFCTL_WIDE (1UL << 30) 29#define M_PERFCTL_MORE (1UL << 31) 30 31#define M_COUNTER_OVERFLOW (1UL << 31) 32 33#ifdef CONFIG_MIPS_MT_SMP 34#define WHAT (M_TC_EN_VPE | M_PERFCTL_VPEID(smp_processor_id())) 35#define vpe_id() smp_processor_id() 36#else 37#define WHAT 0 38#define vpe_id() 0 39#endif 40 41#define __define_perf_accessors(r, n, np) \ 42 \ 43static inline unsigned int r_c0_ ## r ## n(void) \ 44{ \ 45 unsigned int cpu = vpe_id(); \ 46 \ 47 switch (cpu) { \ 48 case 0: \ 49 return read_c0_ ## r ## n(); \ 50 case 1: \ 51 return read_c0_ ## r ## np(); \ 52 default: \ 53 BUG(); \ 54 } \ 55 return 0; \ 56} \ 57 \ 58static inline void w_c0_ ## r ## n(unsigned int value) \ 59{ \ 60 unsigned int cpu = vpe_id(); \ 61 \ 62 switch (cpu) { \ 63 case 0: \ 64 write_c0_ ## r ## n(value); \ 65 return; \ 66 case 1: \ 67 write_c0_ ## r ## np(value); \ 68 return; \ 69 default: \ 70 BUG(); \ 71 } \ 72 return; \ 73} \ 74 75__define_perf_accessors(perfcntr, 0, 2) 76__define_perf_accessors(perfcntr, 1, 3) 77__define_perf_accessors(perfcntr, 2, 0) 78__define_perf_accessors(perfcntr, 3, 1) 79 80__define_perf_accessors(perfctrl, 0, 2) 81__define_perf_accessors(perfctrl, 1, 3) 82__define_perf_accessors(perfctrl, 2, 0) 83__define_perf_accessors(perfctrl, 3, 1) 84 85struct op_mips_model op_model_mipsxx_ops; 86 87static struct mipsxx_register_config { 88 unsigned int control[4]; 89 unsigned int counter[4]; 90} reg; 91 92/* Compute all of the registers in preparation for enabling profiling. */ 93 94static void mipsxx_reg_setup(struct op_counter_config *ctr) 95{ 96 unsigned int counters = op_model_mipsxx_ops.num_counters; 97 int i; 98 99 /* Compute the performance counter control word. */ 100 for (i = 0; i < counters; i++) { 101 reg.control[i] = 0; 102 reg.counter[i] = 0; 103 104 if (!ctr[i].enabled) 105 continue; 106 107 reg.control[i] = M_PERFCTL_EVENT(ctr[i].event) | 108 M_PERFCTL_INTERRUPT_ENABLE; 109 if (ctr[i].kernel) 110 reg.control[i] |= M_PERFCTL_KERNEL; 111 if (ctr[i].user) 112 reg.control[i] |= M_PERFCTL_USER; 113 if (ctr[i].exl) 114 reg.control[i] |= M_PERFCTL_EXL; 115 reg.counter[i] = 0x80000000 - ctr[i].count; 116 } 117} 118 119/* Program all of the registers in preparation for enabling profiling. */ 120 121static void mipsxx_cpu_setup (void *args) 122{ 123 unsigned int counters = op_model_mipsxx_ops.num_counters; 124 125 switch (counters) { 126 case 4: 127 w_c0_perfctrl3(0); 128 w_c0_perfcntr3(reg.counter[3]); 129 case 3: 130 w_c0_perfctrl2(0); 131 w_c0_perfcntr2(reg.counter[2]); 132 case 2: 133 w_c0_perfctrl1(0); 134 w_c0_perfcntr1(reg.counter[1]); 135 case 1: 136 w_c0_perfctrl0(0); 137 w_c0_perfcntr0(reg.counter[0]); 138 } 139} 140 141/* Start all counters on current CPU */ 142static void mipsxx_cpu_start(void *args) 143{ 144 unsigned int counters = op_model_mipsxx_ops.num_counters; 145 146 switch (counters) { 147 case 4: 148 w_c0_perfctrl3(WHAT | reg.control[3]); 149 case 3: 150 w_c0_perfctrl2(WHAT | reg.control[2]); 151 case 2: 152 w_c0_perfctrl1(WHAT | reg.control[1]); 153 case 1: 154 w_c0_perfctrl0(WHAT | reg.control[0]); 155 } 156} 157 158/* Stop all counters on current CPU */ 159static void mipsxx_cpu_stop(void *args) 160{ 161 unsigned int counters = op_model_mipsxx_ops.num_counters; 162 163 switch (counters) { 164 case 4: 165 w_c0_perfctrl3(0); 166 case 3: 167 w_c0_perfctrl2(0); 168 case 2: 169 w_c0_perfctrl1(0); 170 case 1: 171 w_c0_perfctrl0(0); 172 } 173} 174 175static int mipsxx_perfcount_handler(void) 176{ 177 unsigned int counters = op_model_mipsxx_ops.num_counters; 178 unsigned int control; 179 unsigned int counter; 180 int handled = IRQ_NONE; 181 182 if (cpu_has_mips_r2 && !(read_c0_cause() & (1 << 26))) 183 return handled; 184 185 switch (counters) { 186#define HANDLE_COUNTER(n) \ 187 case n + 1: \ 188 control = r_c0_perfctrl ## n(); \ 189 counter = r_c0_perfcntr ## n(); \ 190 if ((control & M_PERFCTL_INTERRUPT_ENABLE) && \ 191 (counter & M_COUNTER_OVERFLOW)) { \ 192 oprofile_add_sample(get_irq_regs(), n); \ 193 w_c0_perfcntr ## n(reg.counter[n]); \ 194 handled = IRQ_HANDLED; \ 195 } 196 HANDLE_COUNTER(3) 197 HANDLE_COUNTER(2) 198 HANDLE_COUNTER(1) 199 HANDLE_COUNTER(0) 200 } 201 202 return handled; 203} 204 205#define M_CONFIG1_PC (1 << 4) 206 207static inline int __n_counters(void) 208{ 209 if (!(read_c0_config1() & M_CONFIG1_PC)) 210 return 0; 211 if (!(r_c0_perfctrl0() & M_PERFCTL_MORE)) 212 return 1; 213 if (!(r_c0_perfctrl1() & M_PERFCTL_MORE)) 214 return 2; 215 if (!(r_c0_perfctrl2() & M_PERFCTL_MORE)) 216 return 3; 217 218 return 4; 219} 220 221static inline int n_counters(void) 222{ 223 int counters; 224 225 switch (current_cpu_data.cputype) { 226 case CPU_R10000: 227 counters = 2; 228 break; 229 230 case CPU_R12000: 231 case CPU_R14000: 232 counters = 4; 233 break; 234 235 default: 236 counters = __n_counters(); 237 } 238 239 return counters; 240} 241 242static inline void reset_counters(int counters) 243{ 244 switch (counters) { 245 case 4: 246 w_c0_perfctrl3(0); 247 w_c0_perfcntr3(0); 248 case 3: 249 w_c0_perfctrl2(0); 250 w_c0_perfcntr2(0); 251 case 2: 252 w_c0_perfctrl1(0); 253 w_c0_perfcntr1(0); 254 case 1: 255 w_c0_perfctrl0(0); 256 w_c0_perfcntr0(0); 257 } 258} 259 260static int __init mipsxx_init(void) 261{ 262 int counters; 263 264 counters = n_counters(); 265 if (counters == 0) { 266 printk(KERN_ERR "Oprofile: CPU has no performance counters\n"); 267 return -ENODEV; 268 } 269 270 reset_counters(counters); 271 272#ifdef CONFIG_MIPS_MT_SMP 273 counters >>= 1; 274#endif 275 276 op_model_mipsxx_ops.num_counters = counters; 277 switch (current_cpu_data.cputype) { 278 case CPU_20KC: 279 op_model_mipsxx_ops.cpu_type = "mips/20K"; 280 break; 281 282 case CPU_24K: 283 op_model_mipsxx_ops.cpu_type = "mips/24K"; 284 break; 285 286 case CPU_25KF: 287 op_model_mipsxx_ops.cpu_type = "mips/25K"; 288 break; 289 290 case CPU_34K: 291 op_model_mipsxx_ops.cpu_type = "mips/34K"; 292 break; 293 294 case CPU_74K: 295 op_model_mipsxx_ops.cpu_type = "mips/74K"; 296 break; 297 298 case CPU_5KC: 299 op_model_mipsxx_ops.cpu_type = "mips/5K"; 300 break; 301 302 case CPU_R10000: 303 if ((current_cpu_data.processor_id & 0xff) == 0x20) 304 op_model_mipsxx_ops.cpu_type = "mips/r10000-v2.x"; 305 else 306 op_model_mipsxx_ops.cpu_type = "mips/r10000"; 307 break; 308 309 case CPU_R12000: 310 case CPU_R14000: 311 op_model_mipsxx_ops.cpu_type = "mips/r12000"; 312 break; 313 314 case CPU_SB1: 315 case CPU_SB1A: 316 op_model_mipsxx_ops.cpu_type = "mips/sb1"; 317 break; 318 319 default: 320 printk(KERN_ERR "Profiling unsupported for this CPU\n"); 321 322 return -ENODEV; 323 } 324 325 perf_irq = mipsxx_perfcount_handler; 326 327 return 0; 328} 329 330static void mipsxx_exit(void) 331{ 332 int counters = op_model_mipsxx_ops.num_counters; 333#ifdef CONFIG_MIPS_MT_SMP 334 counters <<= 1; 335#endif 336 reset_counters(counters); 337 338 perf_irq = null_perf_irq; 339} 340 341struct op_mips_model op_model_mipsxx_ops = { 342 .reg_setup = mipsxx_reg_setup, 343 .cpu_setup = mipsxx_cpu_setup, 344 .init = mipsxx_init, 345 .exit = mipsxx_exit, 346 .cpu_start = mipsxx_cpu_start, 347 .cpu_stop = mipsxx_cpu_stop, 348}; 349