| .. | .. |
|---|
| 1 | | -/* |
|---|
| 2 | | - * Linux performance counter support for ARC700 series |
|---|
| 3 | | - * |
|---|
| 4 | | - * Copyright (C) 2013-2015 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 | | - */ |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 2 | +// |
|---|
| 3 | +// Linux performance counter support for ARC CPUs. |
|---|
| 4 | +// This code is inspired by the perf support of various other architectures. |
|---|
| 5 | +// |
|---|
| 6 | +// Copyright (C) 2013-2018 Synopsys, Inc. (www.synopsys.com) |
|---|
| 7 | + |
|---|
| 13 | 8 | #include <linux/errno.h> |
|---|
| 14 | 9 | #include <linux/interrupt.h> |
|---|
| 15 | 10 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 19 | 14 | #include <asm/arcregs.h> |
|---|
| 20 | 15 | #include <asm/stacktrace.h> |
|---|
| 21 | 16 | |
|---|
| 17 | +/* HW holds 8 symbols + one for null terminator */ |
|---|
| 18 | +#define ARCPMU_EVENT_NAME_LEN 9 |
|---|
| 19 | + |
|---|
| 20 | +enum arc_pmu_attr_groups { |
|---|
| 21 | + ARCPMU_ATTR_GR_EVENTS, |
|---|
| 22 | + ARCPMU_ATTR_GR_FORMATS, |
|---|
| 23 | + ARCPMU_NR_ATTR_GR |
|---|
| 24 | +}; |
|---|
| 25 | + |
|---|
| 26 | +struct arc_pmu_raw_event_entry { |
|---|
| 27 | + char name[ARCPMU_EVENT_NAME_LEN]; |
|---|
| 28 | +}; |
|---|
| 29 | + |
|---|
| 22 | 30 | struct arc_pmu { |
|---|
| 23 | 31 | struct pmu pmu; |
|---|
| 24 | 32 | unsigned int irq; |
|---|
| 25 | 33 | int n_counters; |
|---|
| 34 | + int n_events; |
|---|
| 26 | 35 | u64 max_period; |
|---|
| 27 | 36 | int ev_hw_idx[PERF_COUNT_ARC_HW_MAX]; |
|---|
| 37 | + |
|---|
| 38 | + struct arc_pmu_raw_event_entry *raw_entry; |
|---|
| 39 | + struct attribute **attrs; |
|---|
| 40 | + struct perf_pmu_events_attr *attr; |
|---|
| 41 | + const struct attribute_group *attr_groups[ARCPMU_NR_ATTR_GR + 1]; |
|---|
| 28 | 42 | }; |
|---|
| 29 | 43 | |
|---|
| 30 | 44 | struct arc_pmu_cpu { |
|---|
| .. | .. |
|---|
| 49 | 63 | { |
|---|
| 50 | 64 | struct arc_callchain_trace *ctrl = data; |
|---|
| 51 | 65 | struct perf_callchain_entry_ctx *entry = ctrl->perf_stuff; |
|---|
| 66 | + |
|---|
| 52 | 67 | perf_callchain_store(entry, addr); |
|---|
| 53 | 68 | |
|---|
| 54 | 69 | if (ctrl->depth++ < 3) |
|---|
| .. | .. |
|---|
| 57 | 72 | return -1; |
|---|
| 58 | 73 | } |
|---|
| 59 | 74 | |
|---|
| 60 | | -void |
|---|
| 61 | | -perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) |
|---|
| 75 | +void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, |
|---|
| 76 | + struct pt_regs *regs) |
|---|
| 62 | 77 | { |
|---|
| 63 | 78 | struct arc_callchain_trace ctrl = { |
|---|
| 64 | 79 | .depth = 0, |
|---|
| .. | .. |
|---|
| 68 | 83 | arc_unwind_core(NULL, regs, callchain_trace, &ctrl); |
|---|
| 69 | 84 | } |
|---|
| 70 | 85 | |
|---|
| 71 | | -void |
|---|
| 72 | | -perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) |
|---|
| 86 | +void perf_callchain_user(struct perf_callchain_entry_ctx *entry, |
|---|
| 87 | + struct pt_regs *regs) |
|---|
| 73 | 88 | { |
|---|
| 74 | 89 | /* |
|---|
| 75 | 90 | * User stack can't be unwound trivially with kernel dwarf unwinder |
|---|
| .. | .. |
|---|
| 82 | 97 | static DEFINE_PER_CPU(struct arc_pmu_cpu, arc_pmu_cpu); |
|---|
| 83 | 98 | |
|---|
| 84 | 99 | /* read counter #idx; note that counter# != event# on ARC! */ |
|---|
| 85 | | -static uint64_t arc_pmu_read_counter(int idx) |
|---|
| 100 | +static u64 arc_pmu_read_counter(int idx) |
|---|
| 86 | 101 | { |
|---|
| 87 | | - uint32_t tmp; |
|---|
| 88 | | - uint64_t result; |
|---|
| 102 | + u32 tmp; |
|---|
| 103 | + u64 result; |
|---|
| 89 | 104 | |
|---|
| 90 | 105 | /* |
|---|
| 91 | 106 | * ARC supports making 'snapshots' of the counters, so we don't |
|---|
| .. | .. |
|---|
| 94 | 109 | write_aux_reg(ARC_REG_PCT_INDEX, idx); |
|---|
| 95 | 110 | tmp = read_aux_reg(ARC_REG_PCT_CONTROL); |
|---|
| 96 | 111 | write_aux_reg(ARC_REG_PCT_CONTROL, tmp | ARC_REG_PCT_CONTROL_SN); |
|---|
| 97 | | - result = (uint64_t) (read_aux_reg(ARC_REG_PCT_SNAPH)) << 32; |
|---|
| 112 | + result = (u64) (read_aux_reg(ARC_REG_PCT_SNAPH)) << 32; |
|---|
| 98 | 113 | result |= read_aux_reg(ARC_REG_PCT_SNAPL); |
|---|
| 99 | 114 | |
|---|
| 100 | 115 | return result; |
|---|
| .. | .. |
|---|
| 103 | 118 | static void arc_perf_event_update(struct perf_event *event, |
|---|
| 104 | 119 | struct hw_perf_event *hwc, int idx) |
|---|
| 105 | 120 | { |
|---|
| 106 | | - uint64_t prev_raw_count = local64_read(&hwc->prev_count); |
|---|
| 107 | | - uint64_t new_raw_count = arc_pmu_read_counter(idx); |
|---|
| 108 | | - int64_t delta = new_raw_count - prev_raw_count; |
|---|
| 121 | + u64 prev_raw_count = local64_read(&hwc->prev_count); |
|---|
| 122 | + u64 new_raw_count = arc_pmu_read_counter(idx); |
|---|
| 123 | + s64 delta = new_raw_count - prev_raw_count; |
|---|
| 109 | 124 | |
|---|
| 110 | 125 | /* |
|---|
| 111 | 126 | * We aren't afraid of hwc->prev_count changing beneath our feet |
|---|
| .. | .. |
|---|
| 155 | 170 | int ret; |
|---|
| 156 | 171 | |
|---|
| 157 | 172 | if (!is_sampling_event(event)) { |
|---|
| 158 | | - hwc->sample_period = arc_pmu->max_period; |
|---|
| 173 | + hwc->sample_period = arc_pmu->max_period; |
|---|
| 159 | 174 | hwc->last_period = hwc->sample_period; |
|---|
| 160 | 175 | local64_set(&hwc->period_left, hwc->sample_period); |
|---|
| 161 | 176 | } |
|---|
| .. | .. |
|---|
| 192 | 207 | pr_debug("init cache event with h/w %08x \'%s\'\n", |
|---|
| 193 | 208 | (int)hwc->config, arc_pmu_ev_hw_map[ret]); |
|---|
| 194 | 209 | return 0; |
|---|
| 210 | + |
|---|
| 211 | + case PERF_TYPE_RAW: |
|---|
| 212 | + if (event->attr.config >= arc_pmu->n_events) |
|---|
| 213 | + return -ENOENT; |
|---|
| 214 | + |
|---|
| 215 | + hwc->config |= event->attr.config; |
|---|
| 216 | + pr_debug("init raw event with idx %lld \'%s\'\n", |
|---|
| 217 | + event->attr.config, |
|---|
| 218 | + arc_pmu->raw_entry[event->attr.config].name); |
|---|
| 219 | + |
|---|
| 220 | + return 0; |
|---|
| 221 | + |
|---|
| 195 | 222 | default: |
|---|
| 196 | 223 | return -ENOENT; |
|---|
| 197 | 224 | } |
|---|
| .. | .. |
|---|
| 200 | 227 | /* starts all counters */ |
|---|
| 201 | 228 | static void arc_pmu_enable(struct pmu *pmu) |
|---|
| 202 | 229 | { |
|---|
| 203 | | - uint32_t tmp; |
|---|
| 230 | + u32 tmp; |
|---|
| 204 | 231 | tmp = read_aux_reg(ARC_REG_PCT_CONTROL); |
|---|
| 205 | 232 | write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x1); |
|---|
| 206 | 233 | } |
|---|
| .. | .. |
|---|
| 208 | 235 | /* stops all counters */ |
|---|
| 209 | 236 | static void arc_pmu_disable(struct pmu *pmu) |
|---|
| 210 | 237 | { |
|---|
| 211 | | - uint32_t tmp; |
|---|
| 238 | + u32 tmp; |
|---|
| 212 | 239 | tmp = read_aux_reg(ARC_REG_PCT_CONTROL); |
|---|
| 213 | 240 | write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x0); |
|---|
| 214 | 241 | } |
|---|
| .. | .. |
|---|
| 228 | 255 | local64_set(&hwc->period_left, left); |
|---|
| 229 | 256 | hwc->last_period = period; |
|---|
| 230 | 257 | overflow = 1; |
|---|
| 231 | | - } else if (unlikely(left <= 0)) { |
|---|
| 258 | + } else if (unlikely(left <= 0)) { |
|---|
| 232 | 259 | /* left underflowed by less than period. */ |
|---|
| 233 | 260 | left += period; |
|---|
| 234 | 261 | local64_set(&hwc->period_left, left); |
|---|
| .. | .. |
|---|
| 246 | 273 | write_aux_reg(ARC_REG_PCT_INDEX, idx); |
|---|
| 247 | 274 | |
|---|
| 248 | 275 | /* Write value */ |
|---|
| 249 | | - write_aux_reg(ARC_REG_PCT_COUNTL, (u32)value); |
|---|
| 250 | | - write_aux_reg(ARC_REG_PCT_COUNTH, (value >> 32)); |
|---|
| 276 | + write_aux_reg(ARC_REG_PCT_COUNTL, lower_32_bits(value)); |
|---|
| 277 | + write_aux_reg(ARC_REG_PCT_COUNTH, upper_32_bits(value)); |
|---|
| 251 | 278 | |
|---|
| 252 | 279 | perf_event_update_userpage(event); |
|---|
| 253 | 280 | |
|---|
| .. | .. |
|---|
| 277 | 304 | /* Enable interrupt for this counter */ |
|---|
| 278 | 305 | if (is_sampling_event(event)) |
|---|
| 279 | 306 | write_aux_reg(ARC_REG_PCT_INT_CTRL, |
|---|
| 280 | | - read_aux_reg(ARC_REG_PCT_INT_CTRL) | (1 << idx)); |
|---|
| 307 | + read_aux_reg(ARC_REG_PCT_INT_CTRL) | BIT(idx)); |
|---|
| 281 | 308 | |
|---|
| 282 | 309 | /* enable ARC pmu here */ |
|---|
| 283 | 310 | write_aux_reg(ARC_REG_PCT_INDEX, idx); /* counter # */ |
|---|
| .. | .. |
|---|
| 295 | 322 | * Reset interrupt flag by writing of 1. This is required |
|---|
| 296 | 323 | * to make sure pending interrupt was not left. |
|---|
| 297 | 324 | */ |
|---|
| 298 | | - write_aux_reg(ARC_REG_PCT_INT_ACT, 1 << idx); |
|---|
| 325 | + write_aux_reg(ARC_REG_PCT_INT_ACT, BIT(idx)); |
|---|
| 299 | 326 | write_aux_reg(ARC_REG_PCT_INT_CTRL, |
|---|
| 300 | | - read_aux_reg(ARC_REG_PCT_INT_CTRL) & ~(1 << idx)); |
|---|
| 327 | + read_aux_reg(ARC_REG_PCT_INT_CTRL) & ~BIT(idx)); |
|---|
| 301 | 328 | } |
|---|
| 302 | 329 | |
|---|
| 303 | 330 | if (!(event->hw.state & PERF_HES_STOPPED)) { |
|---|
| .. | .. |
|---|
| 349 | 376 | |
|---|
| 350 | 377 | if (is_sampling_event(event)) { |
|---|
| 351 | 378 | /* Mimic full counter overflow as other arches do */ |
|---|
| 352 | | - write_aux_reg(ARC_REG_PCT_INT_CNTL, (u32)arc_pmu->max_period); |
|---|
| 379 | + write_aux_reg(ARC_REG_PCT_INT_CNTL, |
|---|
| 380 | + lower_32_bits(arc_pmu->max_period)); |
|---|
| 353 | 381 | write_aux_reg(ARC_REG_PCT_INT_CNTH, |
|---|
| 354 | | - (arc_pmu->max_period >> 32)); |
|---|
| 382 | + upper_32_bits(arc_pmu->max_period)); |
|---|
| 355 | 383 | } |
|---|
| 356 | 384 | |
|---|
| 357 | 385 | write_aux_reg(ARC_REG_PCT_CONFIG, 0); |
|---|
| .. | .. |
|---|
| 392 | 420 | idx = __ffs(active_ints); |
|---|
| 393 | 421 | |
|---|
| 394 | 422 | /* Reset interrupt flag by writing of 1 */ |
|---|
| 395 | | - write_aux_reg(ARC_REG_PCT_INT_ACT, 1 << idx); |
|---|
| 423 | + write_aux_reg(ARC_REG_PCT_INT_ACT, BIT(idx)); |
|---|
| 396 | 424 | |
|---|
| 397 | 425 | /* |
|---|
| 398 | 426 | * On reset of "interrupt active" bit corresponding |
|---|
| .. | .. |
|---|
| 400 | 428 | * Now we need to re-enable interrupt for the counter. |
|---|
| 401 | 429 | */ |
|---|
| 402 | 430 | write_aux_reg(ARC_REG_PCT_INT_CTRL, |
|---|
| 403 | | - read_aux_reg(ARC_REG_PCT_INT_CTRL) | (1 << idx)); |
|---|
| 431 | + read_aux_reg(ARC_REG_PCT_INT_CTRL) | BIT(idx)); |
|---|
| 404 | 432 | |
|---|
| 405 | 433 | event = pmu_cpu->act_counter[idx]; |
|---|
| 406 | 434 | hwc = &event->hw; |
|---|
| .. | .. |
|---|
| 414 | 442 | arc_pmu_stop(event, 0); |
|---|
| 415 | 443 | } |
|---|
| 416 | 444 | |
|---|
| 417 | | - active_ints &= ~(1U << idx); |
|---|
| 445 | + active_ints &= ~BIT(idx); |
|---|
| 418 | 446 | } while (active_ints); |
|---|
| 419 | 447 | |
|---|
| 420 | 448 | done: |
|---|
| .. | .. |
|---|
| 441 | 469 | write_aux_reg(ARC_REG_PCT_INT_ACT, 0xffffffff); |
|---|
| 442 | 470 | } |
|---|
| 443 | 471 | |
|---|
| 472 | +/* Event field occupies the bottom 15 bits of our config field */ |
|---|
| 473 | +PMU_FORMAT_ATTR(event, "config:0-14"); |
|---|
| 474 | +static struct attribute *arc_pmu_format_attrs[] = { |
|---|
| 475 | + &format_attr_event.attr, |
|---|
| 476 | + NULL, |
|---|
| 477 | +}; |
|---|
| 478 | + |
|---|
| 479 | +static struct attribute_group arc_pmu_format_attr_gr = { |
|---|
| 480 | + .name = "format", |
|---|
| 481 | + .attrs = arc_pmu_format_attrs, |
|---|
| 482 | +}; |
|---|
| 483 | + |
|---|
| 484 | +static ssize_t arc_pmu_events_sysfs_show(struct device *dev, |
|---|
| 485 | + struct device_attribute *attr, |
|---|
| 486 | + char *page) |
|---|
| 487 | +{ |
|---|
| 488 | + struct perf_pmu_events_attr *pmu_attr; |
|---|
| 489 | + |
|---|
| 490 | + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); |
|---|
| 491 | + return sprintf(page, "event=0x%04llx\n", pmu_attr->id); |
|---|
| 492 | +} |
|---|
| 493 | + |
|---|
| 494 | +/* |
|---|
| 495 | + * We don't add attrs here as we don't have pre-defined list of perf events. |
|---|
| 496 | + * We will generate and add attrs dynamically in probe() after we read HW |
|---|
| 497 | + * configuration. |
|---|
| 498 | + */ |
|---|
| 499 | +static struct attribute_group arc_pmu_events_attr_gr = { |
|---|
| 500 | + .name = "events", |
|---|
| 501 | +}; |
|---|
| 502 | + |
|---|
| 503 | +static void arc_pmu_add_raw_event_attr(int j, char *str) |
|---|
| 504 | +{ |
|---|
| 505 | + memmove(arc_pmu->raw_entry[j].name, str, ARCPMU_EVENT_NAME_LEN - 1); |
|---|
| 506 | + arc_pmu->attr[j].attr.attr.name = arc_pmu->raw_entry[j].name; |
|---|
| 507 | + arc_pmu->attr[j].attr.attr.mode = VERIFY_OCTAL_PERMISSIONS(0444); |
|---|
| 508 | + arc_pmu->attr[j].attr.show = arc_pmu_events_sysfs_show; |
|---|
| 509 | + arc_pmu->attr[j].id = j; |
|---|
| 510 | + arc_pmu->attrs[j] = &(arc_pmu->attr[j].attr.attr); |
|---|
| 511 | +} |
|---|
| 512 | + |
|---|
| 513 | +static int arc_pmu_raw_alloc(struct device *dev) |
|---|
| 514 | +{ |
|---|
| 515 | + arc_pmu->attr = devm_kmalloc_array(dev, arc_pmu->n_events + 1, |
|---|
| 516 | + sizeof(*arc_pmu->attr), GFP_KERNEL | __GFP_ZERO); |
|---|
| 517 | + if (!arc_pmu->attr) |
|---|
| 518 | + return -ENOMEM; |
|---|
| 519 | + |
|---|
| 520 | + arc_pmu->attrs = devm_kmalloc_array(dev, arc_pmu->n_events + 1, |
|---|
| 521 | + sizeof(*arc_pmu->attrs), GFP_KERNEL | __GFP_ZERO); |
|---|
| 522 | + if (!arc_pmu->attrs) |
|---|
| 523 | + return -ENOMEM; |
|---|
| 524 | + |
|---|
| 525 | + arc_pmu->raw_entry = devm_kmalloc_array(dev, arc_pmu->n_events, |
|---|
| 526 | + sizeof(*arc_pmu->raw_entry), GFP_KERNEL | __GFP_ZERO); |
|---|
| 527 | + if (!arc_pmu->raw_entry) |
|---|
| 528 | + return -ENOMEM; |
|---|
| 529 | + |
|---|
| 530 | + return 0; |
|---|
| 531 | +} |
|---|
| 532 | + |
|---|
| 533 | +static inline bool event_in_hw_event_map(int i, char *name) |
|---|
| 534 | +{ |
|---|
| 535 | + if (!arc_pmu_ev_hw_map[i]) |
|---|
| 536 | + return false; |
|---|
| 537 | + |
|---|
| 538 | + if (!strlen(arc_pmu_ev_hw_map[i])) |
|---|
| 539 | + return false; |
|---|
| 540 | + |
|---|
| 541 | + if (strcmp(arc_pmu_ev_hw_map[i], name)) |
|---|
| 542 | + return false; |
|---|
| 543 | + |
|---|
| 544 | + return true; |
|---|
| 545 | +} |
|---|
| 546 | + |
|---|
| 547 | +static void arc_pmu_map_hw_event(int j, char *str) |
|---|
| 548 | +{ |
|---|
| 549 | + int i; |
|---|
| 550 | + |
|---|
| 551 | + /* See if HW condition has been mapped to a perf event_id */ |
|---|
| 552 | + for (i = 0; i < ARRAY_SIZE(arc_pmu_ev_hw_map); i++) { |
|---|
| 553 | + if (event_in_hw_event_map(i, str)) { |
|---|
| 554 | + pr_debug("mapping perf event %2d to h/w event \'%8s\' (idx %d)\n", |
|---|
| 555 | + i, str, j); |
|---|
| 556 | + arc_pmu->ev_hw_idx[i] = j; |
|---|
| 557 | + } |
|---|
| 558 | + } |
|---|
| 559 | +} |
|---|
| 560 | + |
|---|
| 444 | 561 | static int arc_pmu_device_probe(struct platform_device *pdev) |
|---|
| 445 | 562 | { |
|---|
| 446 | 563 | struct arc_reg_pct_build pct_bcr; |
|---|
| 447 | 564 | struct arc_reg_cc_build cc_bcr; |
|---|
| 448 | | - int i, j, has_interrupts; |
|---|
| 565 | + int i, has_interrupts, irq = -1; |
|---|
| 449 | 566 | int counter_size; /* in bits */ |
|---|
| 450 | 567 | |
|---|
| 451 | 568 | union cc_name { |
|---|
| 452 | 569 | struct { |
|---|
| 453 | | - uint32_t word0, word1; |
|---|
| 570 | + u32 word0, word1; |
|---|
| 454 | 571 | char sentinel; |
|---|
| 455 | 572 | } indiv; |
|---|
| 456 | | - char str[9]; |
|---|
| 573 | + char str[ARCPMU_EVENT_NAME_LEN]; |
|---|
| 457 | 574 | } cc_name; |
|---|
| 458 | 575 | |
|---|
| 459 | 576 | |
|---|
| .. | .. |
|---|
| 463 | 580 | return -ENODEV; |
|---|
| 464 | 581 | } |
|---|
| 465 | 582 | BUILD_BUG_ON(ARC_PERF_MAX_COUNTERS > 32); |
|---|
| 466 | | - BUG_ON(pct_bcr.c > ARC_PERF_MAX_COUNTERS); |
|---|
| 583 | + if (WARN_ON(pct_bcr.c > ARC_PERF_MAX_COUNTERS)) |
|---|
| 584 | + return -EINVAL; |
|---|
| 467 | 585 | |
|---|
| 468 | 586 | READ_BCR(ARC_REG_CC_BUILD, cc_bcr); |
|---|
| 469 | | - BUG_ON(!cc_bcr.v); /* Counters exist but No countable conditions ? */ |
|---|
| 587 | + if (WARN(!cc_bcr.v, "Counters exist but No countable conditions?")) |
|---|
| 588 | + return -EINVAL; |
|---|
| 470 | 589 | |
|---|
| 471 | 590 | arc_pmu = devm_kzalloc(&pdev->dev, sizeof(struct arc_pmu), GFP_KERNEL); |
|---|
| 472 | 591 | if (!arc_pmu) |
|---|
| 592 | + return -ENOMEM; |
|---|
| 593 | + |
|---|
| 594 | + arc_pmu->n_events = cc_bcr.c; |
|---|
| 595 | + |
|---|
| 596 | + if (arc_pmu_raw_alloc(&pdev->dev)) |
|---|
| 473 | 597 | return -ENOMEM; |
|---|
| 474 | 598 | |
|---|
| 475 | 599 | has_interrupts = is_isa_arcv2() ? pct_bcr.i : 0; |
|---|
| .. | .. |
|---|
| 481 | 605 | |
|---|
| 482 | 606 | pr_info("ARC perf\t: %d counters (%d bits), %d conditions%s\n", |
|---|
| 483 | 607 | arc_pmu->n_counters, counter_size, cc_bcr.c, |
|---|
| 484 | | - has_interrupts ? ", [overflow IRQ support]":""); |
|---|
| 608 | + has_interrupts ? ", [overflow IRQ support]" : ""); |
|---|
| 485 | 609 | |
|---|
| 486 | | - cc_name.str[8] = 0; |
|---|
| 610 | + cc_name.str[ARCPMU_EVENT_NAME_LEN - 1] = 0; |
|---|
| 487 | 611 | for (i = 0; i < PERF_COUNT_ARC_HW_MAX; i++) |
|---|
| 488 | 612 | arc_pmu->ev_hw_idx[i] = -1; |
|---|
| 489 | 613 | |
|---|
| 490 | 614 | /* loop thru all available h/w condition indexes */ |
|---|
| 491 | | - for (j = 0; j < cc_bcr.c; j++) { |
|---|
| 492 | | - write_aux_reg(ARC_REG_CC_INDEX, j); |
|---|
| 615 | + for (i = 0; i < cc_bcr.c; i++) { |
|---|
| 616 | + write_aux_reg(ARC_REG_CC_INDEX, i); |
|---|
| 493 | 617 | cc_name.indiv.word0 = le32_to_cpu(read_aux_reg(ARC_REG_CC_NAME0)); |
|---|
| 494 | 618 | cc_name.indiv.word1 = le32_to_cpu(read_aux_reg(ARC_REG_CC_NAME1)); |
|---|
| 495 | 619 | |
|---|
| 496 | | - /* See if it has been mapped to a perf event_id */ |
|---|
| 497 | | - for (i = 0; i < ARRAY_SIZE(arc_pmu_ev_hw_map); i++) { |
|---|
| 498 | | - if (arc_pmu_ev_hw_map[i] && |
|---|
| 499 | | - !strcmp(arc_pmu_ev_hw_map[i], cc_name.str) && |
|---|
| 500 | | - strlen(arc_pmu_ev_hw_map[i])) { |
|---|
| 501 | | - pr_debug("mapping perf event %2d to h/w event \'%8s\' (idx %d)\n", |
|---|
| 502 | | - i, cc_name.str, j); |
|---|
| 503 | | - arc_pmu->ev_hw_idx[i] = j; |
|---|
| 504 | | - } |
|---|
| 505 | | - } |
|---|
| 620 | + arc_pmu_map_hw_event(i, cc_name.str); |
|---|
| 621 | + arc_pmu_add_raw_event_attr(i, cc_name.str); |
|---|
| 506 | 622 | } |
|---|
| 623 | + |
|---|
| 624 | + arc_pmu_events_attr_gr.attrs = arc_pmu->attrs; |
|---|
| 625 | + arc_pmu->attr_groups[ARCPMU_ATTR_GR_EVENTS] = &arc_pmu_events_attr_gr; |
|---|
| 626 | + arc_pmu->attr_groups[ARCPMU_ATTR_GR_FORMATS] = &arc_pmu_format_attr_gr; |
|---|
| 507 | 627 | |
|---|
| 508 | 628 | arc_pmu->pmu = (struct pmu) { |
|---|
| 509 | 629 | .pmu_enable = arc_pmu_enable, |
|---|
| .. | .. |
|---|
| 514 | 634 | .start = arc_pmu_start, |
|---|
| 515 | 635 | .stop = arc_pmu_stop, |
|---|
| 516 | 636 | .read = arc_pmu_read, |
|---|
| 637 | + .attr_groups = arc_pmu->attr_groups, |
|---|
| 517 | 638 | }; |
|---|
| 518 | 639 | |
|---|
| 519 | 640 | if (has_interrupts) { |
|---|
| 520 | | - int irq = platform_get_irq(pdev, 0); |
|---|
| 641 | + irq = platform_get_irq(pdev, 0); |
|---|
| 642 | + if (irq >= 0) { |
|---|
| 643 | + int ret; |
|---|
| 521 | 644 | |
|---|
| 522 | | - if (irq < 0) { |
|---|
| 523 | | - pr_err("Cannot get IRQ number for the platform\n"); |
|---|
| 524 | | - return -ENODEV; |
|---|
| 645 | + arc_pmu->irq = irq; |
|---|
| 646 | + |
|---|
| 647 | + /* intc map function ensures irq_set_percpu_devid() called */ |
|---|
| 648 | + ret = request_percpu_irq(irq, arc_pmu_intr, "ARC perf counters", |
|---|
| 649 | + this_cpu_ptr(&arc_pmu_cpu)); |
|---|
| 650 | + |
|---|
| 651 | + if (!ret) |
|---|
| 652 | + on_each_cpu(arc_cpu_pmu_irq_init, &irq, 1); |
|---|
| 653 | + else |
|---|
| 654 | + irq = -1; |
|---|
| 525 | 655 | } |
|---|
| 526 | 656 | |
|---|
| 527 | | - arc_pmu->irq = irq; |
|---|
| 657 | + } |
|---|
| 528 | 658 | |
|---|
| 529 | | - /* intc map function ensures irq_set_percpu_devid() called */ |
|---|
| 530 | | - request_percpu_irq(irq, arc_pmu_intr, "ARC perf counters", |
|---|
| 531 | | - this_cpu_ptr(&arc_pmu_cpu)); |
|---|
| 532 | | - |
|---|
| 533 | | - on_each_cpu(arc_cpu_pmu_irq_init, &irq, 1); |
|---|
| 534 | | - |
|---|
| 535 | | - } else |
|---|
| 659 | + if (irq == -1) |
|---|
| 536 | 660 | arc_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; |
|---|
| 537 | 661 | |
|---|
| 538 | | - return perf_pmu_register(&arc_pmu->pmu, pdev->name, PERF_TYPE_RAW); |
|---|
| 662 | + /* |
|---|
| 663 | + * perf parser doesn't really like '-' symbol in events name, so let's |
|---|
| 664 | + * use '_' in arc pct name as it goes to kernel PMU event prefix. |
|---|
| 665 | + */ |
|---|
| 666 | + return perf_pmu_register(&arc_pmu->pmu, "arc_pct", PERF_TYPE_RAW); |
|---|
| 539 | 667 | } |
|---|
| 540 | 668 | |
|---|
| 541 | | -#ifdef CONFIG_OF |
|---|
| 542 | 669 | static const struct of_device_id arc_pmu_match[] = { |
|---|
| 543 | 670 | { .compatible = "snps,arc700-pct" }, |
|---|
| 544 | 671 | { .compatible = "snps,archs-pct" }, |
|---|
| 545 | 672 | {}, |
|---|
| 546 | 673 | }; |
|---|
| 547 | 674 | MODULE_DEVICE_TABLE(of, arc_pmu_match); |
|---|
| 548 | | -#endif |
|---|
| 549 | 675 | |
|---|
| 550 | 676 | static struct platform_driver arc_pmu_driver = { |
|---|
| 551 | 677 | .driver = { |
|---|