1/* 2 * Copyright (C) 2006-2007 PA Semi, Inc 3 * 4 * Author: Shashi Rao, PA Semi 5 * 6 * Maintained by: Olof Johansson <olof@lixom.net> 7 * 8 * Based on arch/powerpc/oprofile/op_model_power4.c 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 */ 23 24#include <linux/oprofile.h> 25#include <linux/smp.h> 26#include <linux/percpu.h> 27#include <asm/processor.h> 28#include <asm/cputable.h> 29#include <asm/oprofile_impl.h> 30#include <asm/reg.h> 31 32static unsigned char oprofile_running; 33 34/* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */ 35static u64 mmcr0_val; 36static u64 mmcr1_val; 37 38/* inited in pa6t_reg_setup */ 39static u64 reset_value[OP_MAX_COUNTER]; 40 41static inline u64 ctr_read(unsigned int i) 42{ 43 switch (i) { 44 case 0: 45 return mfspr(SPRN_PA6T_PMC0); 46 case 1: 47 return mfspr(SPRN_PA6T_PMC1); 48 case 2: 49 return mfspr(SPRN_PA6T_PMC2); 50 case 3: 51 return mfspr(SPRN_PA6T_PMC3); 52 case 4: 53 return mfspr(SPRN_PA6T_PMC4); 54 case 5: 55 return mfspr(SPRN_PA6T_PMC5); 56 default: 57 printk(KERN_ERR "ctr_read called with bad arg %u\n", i); 58 return 0; 59 } 60} 61 62static inline void ctr_write(unsigned int i, u64 val) 63{ 64 switch (i) { 65 case 0: 66 mtspr(SPRN_PA6T_PMC0, val); 67 break; 68 case 1: 69 mtspr(SPRN_PA6T_PMC1, val); 70 break; 71 case 2: 72 mtspr(SPRN_PA6T_PMC2, val); 73 break; 74 case 3: 75 mtspr(SPRN_PA6T_PMC3, val); 76 break; 77 case 4: 78 mtspr(SPRN_PA6T_PMC4, val); 79 break; 80 case 5: 81 mtspr(SPRN_PA6T_PMC5, val); 82 break; 83 default: 84 printk(KERN_ERR "ctr_write called with bad arg %u\n", i); 85 break; 86 } 87} 88 89 90/* precompute the values to stuff in the hardware registers */ 91static int pa6t_reg_setup(struct op_counter_config *ctr, 92 struct op_system_config *sys, 93 int num_ctrs) 94{ 95 int pmc; 96 97 /* 98 * adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the 99 * event_mappings file by turning off the counters that the user doesn't 100 * care about 101 * 102 * setup user and kernel profiling 103 */ 104 for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) 105 if (!ctr[pmc].enabled) { 106 sys->mmcr0 &= ~(0x1UL << pmc); 107 sys->mmcr0 &= ~(0x1UL << (pmc+12)); 108 pr_debug("turned off counter %u\n", pmc); 109 } 110 111 if (sys->enable_kernel) 112 sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN; 113 else 114 sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN); 115 116 if (sys->enable_user) 117 sys->mmcr0 |= PA6T_MMCR0_PREN; 118 else 119 sys->mmcr0 &= ~PA6T_MMCR0_PREN; 120 121 /* 122 * The performance counter event settings are given in the mmcr0 and 123 * mmcr1 values passed from the user in the op_system_config 124 * structure (sys variable). 125 */ 126 mmcr0_val = sys->mmcr0; 127 mmcr1_val = sys->mmcr1; 128 pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0); 129 pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1); 130 131 for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) { 132 /* counters are 40 bit. Move to cputable at some point? */ 133 reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count; 134 pr_debug("reset_value for pmc%u inited to 0x%llx\n", 135 pmc, reset_value[pmc]); 136 } 137 138 return 0; 139} 140 141/* configure registers on this cpu */ 142static int pa6t_cpu_setup(struct op_counter_config *ctr) 143{ 144 u64 mmcr0 = mmcr0_val; 145 u64 mmcr1 = mmcr1_val; 146 147 /* Default is all PMCs off */ 148 mmcr0 &= ~(0x3FUL); 149 mtspr(SPRN_PA6T_MMCR0, mmcr0); 150 151 /* program selected programmable events in */ 152 mtspr(SPRN_PA6T_MMCR1, mmcr1); 153 154 pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(), 155 mfspr(SPRN_PA6T_MMCR0)); 156 pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(), 157 mfspr(SPRN_PA6T_MMCR1)); 158 159 return 0; 160} 161 162static int pa6t_start(struct op_counter_config *ctr) 163{ 164 int i; 165 166 /* Hold off event counting until rfid */ 167 u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; 168 169 for (i = 0; i < cur_cpu_spec->num_pmcs; i++) 170 if (ctr[i].enabled) 171 ctr_write(i, reset_value[i]); 172 else 173 ctr_write(i, 0UL); 174 175 mtspr(SPRN_PA6T_MMCR0, mmcr0); 176 177 oprofile_running = 1; 178 179 pr_debug("start on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0); 180 181 return 0; 182} 183 184static void pa6t_stop(void) 185{ 186 u64 mmcr0; 187 188 /* freeze counters */ 189 mmcr0 = mfspr(SPRN_PA6T_MMCR0); 190 mmcr0 |= PA6T_MMCR0_FCM0; 191 mtspr(SPRN_PA6T_MMCR0, mmcr0); 192 193 oprofile_running = 0; 194 195 pr_debug("stop on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0); 196} 197 198/* handle the perfmon overflow vector */ 199static void pa6t_handle_interrupt(struct pt_regs *regs, 200 struct op_counter_config *ctr) 201{ 202 unsigned long pc = mfspr(SPRN_PA6T_SIAR); 203 int is_kernel = is_kernel_addr(pc); 204 u64 val; 205 int i; 206 u64 mmcr0; 207 208 /* disable perfmon counting until rfid */ 209 mmcr0 = mfspr(SPRN_PA6T_MMCR0); 210 mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS); 211 212 /* Record samples. We've got one global bit for whether a sample 213 * was taken, so add it for any counter that triggered overflow. 214 */ 215 for (i = 0; i < cur_cpu_spec->num_pmcs; i++) { 216 val = ctr_read(i); 217 if (val & (0x1UL << 39)) { /* Overflow bit set */ 218 if (oprofile_running && ctr[i].enabled) { 219 if (mmcr0 & PA6T_MMCR0_SIARLOG) 220 oprofile_add_ext_sample(pc, regs, i, is_kernel); 221 ctr_write(i, reset_value[i]); 222 } else { 223 ctr_write(i, 0UL); 224 } 225 } 226 } 227 228 /* Restore mmcr0 to a good known value since the PMI changes it */ 229 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; 230 mtspr(SPRN_PA6T_MMCR0, mmcr0); 231} 232 233struct op_powerpc_model op_model_pa6t = { 234 .reg_setup = pa6t_reg_setup, 235 .cpu_setup = pa6t_cpu_setup, 236 .start = pa6t_start, 237 .stop = pa6t_stop, 238 .handle_interrupt = pa6t_handle_interrupt, 239}; 240