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