1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Performance event support - PPC 8xx 4 * 5 * Copyright 2016 Christophe Leroy, CS Systemes d'Information 6 */ 7 8 #include <linux/kernel.h> 9 #include <linux/sched.h> 10 #include <linux/perf_event.h> 11 #include <linux/percpu.h> 12 #include <linux/hardirq.h> 13 #include <asm/pmc.h> 14 #include <asm/machdep.h> 15 #include <asm/firmware.h> 16 #include <asm/ptrace.h> 17 #include <asm/code-patching.h> 18 19 #define PERF_8xx_ID_CPU_CYCLES 1 20 #define PERF_8xx_ID_HW_INSTRUCTIONS 2 21 #define PERF_8xx_ID_ITLB_LOAD_MISS 3 22 #define PERF_8xx_ID_DTLB_LOAD_MISS 4 23 24 #define C(x) PERF_COUNT_HW_CACHE_##x 25 #define DTLB_LOAD_MISS (C(DTLB) | (C(OP_READ) << 8) | (C(RESULT_MISS) << 16)) 26 #define ITLB_LOAD_MISS (C(ITLB) | (C(OP_READ) << 8) | (C(RESULT_MISS) << 16)) 27 28 extern unsigned long itlb_miss_counter, dtlb_miss_counter; 29 extern atomic_t instruction_counter; 30 31 static atomic_t insn_ctr_ref; 32 static atomic_t itlb_miss_ref; 33 static atomic_t dtlb_miss_ref; 34 35 static s64 get_insn_ctr(void) 36 { 37 int ctr; 38 unsigned long counta; 39 40 do { 41 ctr = atomic_read(&instruction_counter); 42 counta = mfspr(SPRN_COUNTA); 43 } while (ctr != atomic_read(&instruction_counter)); 44 45 return ((s64)ctr << 16) | (counta >> 16); 46 } 47 48 static int event_type(struct perf_event *event) 49 { 50 switch (event->attr.type) { 51 case PERF_TYPE_HARDWARE: 52 if (event->attr.config == PERF_COUNT_HW_CPU_CYCLES) 53 return PERF_8xx_ID_CPU_CYCLES; 54 if (event->attr.config == PERF_COUNT_HW_INSTRUCTIONS) 55 return PERF_8xx_ID_HW_INSTRUCTIONS; 56 break; 57 case PERF_TYPE_HW_CACHE: 58 if (event->attr.config == ITLB_LOAD_MISS) 59 return PERF_8xx_ID_ITLB_LOAD_MISS; 60 if (event->attr.config == DTLB_LOAD_MISS) 61 return PERF_8xx_ID_DTLB_LOAD_MISS; 62 break; 63 case PERF_TYPE_RAW: 64 break; 65 default: 66 return -ENOENT; 67 } 68 return -EOPNOTSUPP; 69 } 70 71 static int mpc8xx_pmu_event_init(struct perf_event *event) 72 { 73 int type = event_type(event); 74 75 if (type < 0) 76 return type; 77 return 0; 78 } 79 80 static int mpc8xx_pmu_add(struct perf_event *event, int flags) 81 { 82 int type = event_type(event); 83 s64 val = 0; 84 85 if (type < 0) 86 return type; 87 88 switch (type) { 89 case PERF_8xx_ID_CPU_CYCLES: 90 val = get_tb(); 91 break; 92 case PERF_8xx_ID_HW_INSTRUCTIONS: 93 if (atomic_inc_return(&insn_ctr_ref) == 1) 94 mtspr(SPRN_ICTRL, 0xc0080007); 95 val = get_insn_ctr(); 96 break; 97 case PERF_8xx_ID_ITLB_LOAD_MISS: 98 if (atomic_inc_return(&itlb_miss_ref) == 1) { 99 unsigned long target = patch_site_addr(&patch__itlbmiss_perf); 100 101 patch_branch_site(&patch__itlbmiss_exit_1, target, 0); 102 #ifndef CONFIG_PIN_TLB_TEXT 103 patch_branch_site(&patch__itlbmiss_exit_2, target, 0); 104 #endif 105 } 106 val = itlb_miss_counter; 107 break; 108 case PERF_8xx_ID_DTLB_LOAD_MISS: 109 if (atomic_inc_return(&dtlb_miss_ref) == 1) { 110 unsigned long target = patch_site_addr(&patch__dtlbmiss_perf); 111 112 patch_branch_site(&patch__dtlbmiss_exit_1, target, 0); 113 patch_branch_site(&patch__dtlbmiss_exit_2, target, 0); 114 patch_branch_site(&patch__dtlbmiss_exit_3, target, 0); 115 } 116 val = dtlb_miss_counter; 117 break; 118 } 119 local64_set(&event->hw.prev_count, val); 120 return 0; 121 } 122 123 static void mpc8xx_pmu_read(struct perf_event *event) 124 { 125 int type = event_type(event); 126 s64 prev, val = 0, delta = 0; 127 128 if (type < 0) 129 return; 130 131 do { 132 prev = local64_read(&event->hw.prev_count); 133 switch (type) { 134 case PERF_8xx_ID_CPU_CYCLES: 135 val = get_tb(); 136 delta = 16 * (val - prev); 137 break; 138 case PERF_8xx_ID_HW_INSTRUCTIONS: 139 val = get_insn_ctr(); 140 delta = prev - val; 141 if (delta < 0) 142 delta += 0x1000000000000LL; 143 break; 144 case PERF_8xx_ID_ITLB_LOAD_MISS: 145 val = itlb_miss_counter; 146 delta = (s64)((s32)val - (s32)prev); 147 break; 148 case PERF_8xx_ID_DTLB_LOAD_MISS: 149 val = dtlb_miss_counter; 150 delta = (s64)((s32)val - (s32)prev); 151 break; 152 } 153 } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); 154 155 local64_add(delta, &event->count); 156 } 157 158 static void mpc8xx_pmu_del(struct perf_event *event, int flags) 159 { 160 mpc8xx_pmu_read(event); 161 162 /* If it was the last user, stop counting to avoid useles overhead */ 163 switch (event_type(event)) { 164 case PERF_8xx_ID_CPU_CYCLES: 165 break; 166 case PERF_8xx_ID_HW_INSTRUCTIONS: 167 if (atomic_dec_return(&insn_ctr_ref) == 0) 168 mtspr(SPRN_ICTRL, 7); 169 break; 170 case PERF_8xx_ID_ITLB_LOAD_MISS: 171 if (atomic_dec_return(&itlb_miss_ref) == 0) { 172 /* mfspr r10, SPRN_SPRG_SCRATCH0 */ 173 unsigned int insn = PPC_INST_MFSPR | __PPC_RS(R10) | 174 __PPC_SPR(SPRN_SPRG_SCRATCH0); 175 176 patch_instruction_site(&patch__itlbmiss_exit_1, insn); 177 #ifndef CONFIG_PIN_TLB_TEXT 178 patch_instruction_site(&patch__itlbmiss_exit_2, insn); 179 #endif 180 } 181 break; 182 case PERF_8xx_ID_DTLB_LOAD_MISS: 183 if (atomic_dec_return(&dtlb_miss_ref) == 0) { 184 /* mfspr r10, SPRN_DAR */ 185 unsigned int insn = PPC_INST_MFSPR | __PPC_RS(R10) | 186 __PPC_SPR(SPRN_DAR); 187 188 patch_instruction_site(&patch__dtlbmiss_exit_1, insn); 189 patch_instruction_site(&patch__dtlbmiss_exit_2, insn); 190 patch_instruction_site(&patch__dtlbmiss_exit_3, insn); 191 } 192 break; 193 } 194 } 195 196 static struct pmu mpc8xx_pmu = { 197 .event_init = mpc8xx_pmu_event_init, 198 .add = mpc8xx_pmu_add, 199 .del = mpc8xx_pmu_del, 200 .read = mpc8xx_pmu_read, 201 .capabilities = PERF_PMU_CAP_NO_INTERRUPT | 202 PERF_PMU_CAP_NO_NMI, 203 }; 204 205 static int init_mpc8xx_pmu(void) 206 { 207 mtspr(SPRN_ICTRL, 7); 208 mtspr(SPRN_CMPA, 0); 209 mtspr(SPRN_COUNTA, 0xffff); 210 211 return perf_pmu_register(&mpc8xx_pmu, "cpu", PERF_TYPE_RAW); 212 } 213 214 early_initcall(init_mpc8xx_pmu); 215