1 /* 2 * Linux performance counter support for ARC700 series 3 * 4 * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) 5 * 6 * This code is inspired by the perf support of various other architectures. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 #include <linux/errno.h> 14 #include <linux/module.h> 15 #include <linux/of.h> 16 #include <linux/perf_event.h> 17 #include <linux/platform_device.h> 18 #include <asm/arcregs.h> 19 20 struct arc_pmu { 21 struct pmu pmu; 22 int counter_size; /* in bits */ 23 int n_counters; 24 unsigned long used_mask[BITS_TO_LONGS(ARC_PMU_MAX_HWEVENTS)]; 25 int ev_hw_idx[PERF_COUNT_ARC_HW_MAX]; 26 }; 27 28 /* read counter #idx; note that counter# != event# on ARC! */ 29 static uint64_t arc_pmu_read_counter(int idx) 30 { 31 uint32_t tmp; 32 uint64_t result; 33 34 /* 35 * ARC supports making 'snapshots' of the counters, so we don't 36 * need to care about counters wrapping to 0 underneath our feet 37 */ 38 write_aux_reg(ARC_REG_PCT_INDEX, idx); 39 tmp = read_aux_reg(ARC_REG_PCT_CONTROL); 40 write_aux_reg(ARC_REG_PCT_CONTROL, tmp | ARC_REG_PCT_CONTROL_SN); 41 result = (uint64_t) (read_aux_reg(ARC_REG_PCT_SNAPH)) << 32; 42 result |= read_aux_reg(ARC_REG_PCT_SNAPL); 43 44 return result; 45 } 46 47 static void arc_perf_event_update(struct perf_event *event, 48 struct hw_perf_event *hwc, int idx) 49 { 50 struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu); 51 uint64_t prev_raw_count, new_raw_count; 52 int64_t delta; 53 54 do { 55 prev_raw_count = local64_read(&hwc->prev_count); 56 new_raw_count = arc_pmu_read_counter(idx); 57 } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count, 58 new_raw_count) != prev_raw_count); 59 60 delta = (new_raw_count - prev_raw_count) & 61 ((1ULL << arc_pmu->counter_size) - 1ULL); 62 63 local64_add(delta, &event->count); 64 local64_sub(delta, &hwc->period_left); 65 } 66 67 static void arc_pmu_read(struct perf_event *event) 68 { 69 arc_perf_event_update(event, &event->hw, event->hw.idx); 70 } 71 72 static int arc_pmu_cache_event(u64 config) 73 { 74 unsigned int cache_type, cache_op, cache_result; 75 int ret; 76 77 cache_type = (config >> 0) & 0xff; 78 cache_op = (config >> 8) & 0xff; 79 cache_result = (config >> 16) & 0xff; 80 if (cache_type >= PERF_COUNT_HW_CACHE_MAX) 81 return -EINVAL; 82 if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) 83 return -EINVAL; 84 if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) 85 return -EINVAL; 86 87 ret = arc_pmu_cache_map[cache_type][cache_op][cache_result]; 88 89 if (ret == CACHE_OP_UNSUPPORTED) 90 return -ENOENT; 91 92 return ret; 93 } 94 95 /* initializes hw_perf_event structure if event is supported */ 96 static int arc_pmu_event_init(struct perf_event *event) 97 { 98 struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu); 99 struct hw_perf_event *hwc = &event->hw; 100 int ret; 101 102 /* ARC 700 PMU does not support sampling events */ 103 if (is_sampling_event(event)) 104 return -ENOENT; 105 106 switch (event->attr.type) { 107 case PERF_TYPE_HARDWARE: 108 if (event->attr.config >= PERF_COUNT_HW_MAX) 109 return -ENOENT; 110 if (arc_pmu->ev_hw_idx[event->attr.config] < 0) 111 return -ENOENT; 112 hwc->config = arc_pmu->ev_hw_idx[event->attr.config]; 113 pr_debug("initializing event %d with cfg %d\n", 114 (int) event->attr.config, (int) hwc->config); 115 return 0; 116 case PERF_TYPE_HW_CACHE: 117 ret = arc_pmu_cache_event(event->attr.config); 118 if (ret < 0) 119 return ret; 120 hwc->config = arc_pmu->ev_hw_idx[ret]; 121 return 0; 122 default: 123 return -ENOENT; 124 } 125 } 126 127 /* starts all counters */ 128 static void arc_pmu_enable(struct pmu *pmu) 129 { 130 uint32_t tmp; 131 tmp = read_aux_reg(ARC_REG_PCT_CONTROL); 132 write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x1); 133 } 134 135 /* stops all counters */ 136 static void arc_pmu_disable(struct pmu *pmu) 137 { 138 uint32_t tmp; 139 tmp = read_aux_reg(ARC_REG_PCT_CONTROL); 140 write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x0); 141 } 142 143 /* 144 * Assigns hardware counter to hardware condition. 145 * Note that there is no separate start/stop mechanism; 146 * stopping is achieved by assigning the 'never' condition 147 */ 148 static void arc_pmu_start(struct perf_event *event, int flags) 149 { 150 struct hw_perf_event *hwc = &event->hw; 151 int idx = hwc->idx; 152 153 if (WARN_ON_ONCE(idx == -1)) 154 return; 155 156 if (flags & PERF_EF_RELOAD) 157 WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); 158 159 event->hw.state = 0; 160 161 /* enable ARC pmu here */ 162 write_aux_reg(ARC_REG_PCT_INDEX, idx); 163 write_aux_reg(ARC_REG_PCT_CONFIG, hwc->config); 164 } 165 166 static void arc_pmu_stop(struct perf_event *event, int flags) 167 { 168 struct hw_perf_event *hwc = &event->hw; 169 int idx = hwc->idx; 170 171 if (!(event->hw.state & PERF_HES_STOPPED)) { 172 /* stop ARC pmu here */ 173 write_aux_reg(ARC_REG_PCT_INDEX, idx); 174 175 /* condition code #0 is always "never" */ 176 write_aux_reg(ARC_REG_PCT_CONFIG, 0); 177 178 event->hw.state |= PERF_HES_STOPPED; 179 } 180 181 if ((flags & PERF_EF_UPDATE) && 182 !(event->hw.state & PERF_HES_UPTODATE)) { 183 arc_perf_event_update(event, &event->hw, idx); 184 event->hw.state |= PERF_HES_UPTODATE; 185 } 186 } 187 188 static void arc_pmu_del(struct perf_event *event, int flags) 189 { 190 struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu); 191 192 arc_pmu_stop(event, PERF_EF_UPDATE); 193 __clear_bit(event->hw.idx, arc_pmu->used_mask); 194 195 perf_event_update_userpage(event); 196 } 197 198 /* allocate hardware counter and optionally start counting */ 199 static int arc_pmu_add(struct perf_event *event, int flags) 200 { 201 struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu); 202 struct hw_perf_event *hwc = &event->hw; 203 int idx = hwc->idx; 204 205 if (__test_and_set_bit(idx, arc_pmu->used_mask)) { 206 idx = find_first_zero_bit(arc_pmu->used_mask, 207 arc_pmu->n_counters); 208 if (idx == arc_pmu->n_counters) 209 return -EAGAIN; 210 211 __set_bit(idx, arc_pmu->used_mask); 212 hwc->idx = idx; 213 } 214 215 write_aux_reg(ARC_REG_PCT_INDEX, idx); 216 write_aux_reg(ARC_REG_PCT_CONFIG, 0); 217 write_aux_reg(ARC_REG_PCT_COUNTL, 0); 218 write_aux_reg(ARC_REG_PCT_COUNTH, 0); 219 local64_set(&hwc->prev_count, 0); 220 221 hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; 222 if (flags & PERF_EF_START) 223 arc_pmu_start(event, PERF_EF_RELOAD); 224 225 perf_event_update_userpage(event); 226 227 return 0; 228 } 229 230 static int arc_pmu_device_probe(struct platform_device *pdev) 231 { 232 struct arc_pmu *arc_pmu; 233 struct arc_reg_pct_build pct_bcr; 234 struct arc_reg_cc_build cc_bcr; 235 int i, j, ret; 236 237 union cc_name { 238 struct { 239 uint32_t word0, word1; 240 char sentinel; 241 } indiv; 242 char str[9]; 243 } cc_name; 244 245 246 READ_BCR(ARC_REG_PCT_BUILD, pct_bcr); 247 if (!pct_bcr.v) { 248 pr_err("This core does not have performance counters!\n"); 249 return -ENODEV; 250 } 251 252 arc_pmu = devm_kzalloc(&pdev->dev, sizeof(struct arc_pmu), 253 GFP_KERNEL); 254 if (!arc_pmu) 255 return -ENOMEM; 256 257 arc_pmu->n_counters = pct_bcr.c; 258 BUG_ON(arc_pmu->n_counters > ARC_PMU_MAX_HWEVENTS); 259 260 arc_pmu->counter_size = 32 + (pct_bcr.s << 4); 261 pr_info("ARC PMU found with %d counters of size %d bits\n", 262 arc_pmu->n_counters, arc_pmu->counter_size); 263 264 READ_BCR(ARC_REG_CC_BUILD, cc_bcr); 265 266 if (!cc_bcr.v) 267 pr_err("Strange! Performance counters exist, but no countable conditions?\n"); 268 269 pr_info("ARC PMU has %d countable conditions\n", cc_bcr.c); 270 271 cc_name.str[8] = 0; 272 for (i = 0; i < PERF_COUNT_HW_MAX; i++) 273 arc_pmu->ev_hw_idx[i] = -1; 274 275 for (j = 0; j < cc_bcr.c; j++) { 276 write_aux_reg(ARC_REG_CC_INDEX, j); 277 cc_name.indiv.word0 = read_aux_reg(ARC_REG_CC_NAME0); 278 cc_name.indiv.word1 = read_aux_reg(ARC_REG_CC_NAME1); 279 for (i = 0; i < ARRAY_SIZE(arc_pmu_ev_hw_map); i++) { 280 if (arc_pmu_ev_hw_map[i] && 281 !strcmp(arc_pmu_ev_hw_map[i], cc_name.str) && 282 strlen(arc_pmu_ev_hw_map[i])) { 283 pr_debug("mapping %d to idx %d with name %s\n", 284 i, j, cc_name.str); 285 arc_pmu->ev_hw_idx[i] = j; 286 } 287 } 288 } 289 290 arc_pmu->pmu = (struct pmu) { 291 .pmu_enable = arc_pmu_enable, 292 .pmu_disable = arc_pmu_disable, 293 .event_init = arc_pmu_event_init, 294 .add = arc_pmu_add, 295 .del = arc_pmu_del, 296 .start = arc_pmu_start, 297 .stop = arc_pmu_stop, 298 .read = arc_pmu_read, 299 }; 300 301 ret = perf_pmu_register(&arc_pmu->pmu, pdev->name, PERF_TYPE_RAW); 302 303 return ret; 304 } 305 306 #ifdef CONFIG_OF 307 static const struct of_device_id arc_pmu_match[] = { 308 { .compatible = "snps,arc700-pmu" }, 309 {}, 310 }; 311 MODULE_DEVICE_TABLE(of, arc_pmu_match); 312 #endif 313 314 static struct platform_driver arc_pmu_driver = { 315 .driver = { 316 .name = "arc700-pmu", 317 .of_match_table = of_match_ptr(arc_pmu_match), 318 }, 319 .probe = arc_pmu_device_probe, 320 }; 321 322 module_platform_driver(arc_pmu_driver); 323 324 MODULE_LICENSE("GPL"); 325 MODULE_AUTHOR("Mischa Jonker <mjonker@synopsys.com>"); 326 MODULE_DESCRIPTION("ARC PMU driver"); 327