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