linux/arch/arc/kernel/perf_event.c
<<
>>
Prefs
   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
  20struct 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! */
  29static 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
  47static 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
  67static void arc_pmu_read(struct perf_event *event)
  68{
  69        arc_perf_event_update(event, &event->hw, event->hw.idx);
  70}
  71
  72static 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 */
  96static 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        switch (event->attr.type) {
 103        case PERF_TYPE_HARDWARE:
 104                if (event->attr.config >= PERF_COUNT_HW_MAX)
 105                        return -ENOENT;
 106                if (arc_pmu->ev_hw_idx[event->attr.config] < 0)
 107                        return -ENOENT;
 108                hwc->config = arc_pmu->ev_hw_idx[event->attr.config];
 109                pr_debug("initializing event %d with cfg %d\n",
 110                         (int) event->attr.config, (int) hwc->config);
 111                return 0;
 112        case PERF_TYPE_HW_CACHE:
 113                ret = arc_pmu_cache_event(event->attr.config);
 114                if (ret < 0)
 115                        return ret;
 116                hwc->config = arc_pmu->ev_hw_idx[ret];
 117                return 0;
 118        default:
 119                return -ENOENT;
 120        }
 121}
 122
 123/* starts all counters */
 124static void arc_pmu_enable(struct pmu *pmu)
 125{
 126        uint32_t tmp;
 127        tmp = read_aux_reg(ARC_REG_PCT_CONTROL);
 128        write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x1);
 129}
 130
 131/* stops all counters */
 132static void arc_pmu_disable(struct pmu *pmu)
 133{
 134        uint32_t tmp;
 135        tmp = read_aux_reg(ARC_REG_PCT_CONTROL);
 136        write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x0);
 137}
 138
 139/*
 140 * Assigns hardware counter to hardware condition.
 141 * Note that there is no separate start/stop mechanism;
 142 * stopping is achieved by assigning the 'never' condition
 143 */
 144static void arc_pmu_start(struct perf_event *event, int flags)
 145{
 146        struct hw_perf_event *hwc = &event->hw;
 147        int idx = hwc->idx;
 148
 149        if (WARN_ON_ONCE(idx == -1))
 150                return;
 151
 152        if (flags & PERF_EF_RELOAD)
 153                WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
 154
 155        event->hw.state = 0;
 156
 157        /* enable ARC pmu here */
 158        write_aux_reg(ARC_REG_PCT_INDEX, idx);
 159        write_aux_reg(ARC_REG_PCT_CONFIG, hwc->config);
 160}
 161
 162static void arc_pmu_stop(struct perf_event *event, int flags)
 163{
 164        struct hw_perf_event *hwc = &event->hw;
 165        int idx = hwc->idx;
 166
 167        if (!(event->hw.state & PERF_HES_STOPPED)) {
 168                /* stop ARC pmu here */
 169                write_aux_reg(ARC_REG_PCT_INDEX, idx);
 170
 171                /* condition code #0 is always "never" */
 172                write_aux_reg(ARC_REG_PCT_CONFIG, 0);
 173
 174                event->hw.state |= PERF_HES_STOPPED;
 175        }
 176
 177        if ((flags & PERF_EF_UPDATE) &&
 178            !(event->hw.state & PERF_HES_UPTODATE)) {
 179                arc_perf_event_update(event, &event->hw, idx);
 180                event->hw.state |= PERF_HES_UPTODATE;
 181        }
 182}
 183
 184static void arc_pmu_del(struct perf_event *event, int flags)
 185{
 186        struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu);
 187
 188        arc_pmu_stop(event, PERF_EF_UPDATE);
 189        __clear_bit(event->hw.idx, arc_pmu->used_mask);
 190
 191        perf_event_update_userpage(event);
 192}
 193
 194/* allocate hardware counter and optionally start counting */
 195static int arc_pmu_add(struct perf_event *event, int flags)
 196{
 197        struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu);
 198        struct hw_perf_event *hwc = &event->hw;
 199        int idx = hwc->idx;
 200
 201        if (__test_and_set_bit(idx, arc_pmu->used_mask)) {
 202                idx = find_first_zero_bit(arc_pmu->used_mask,
 203                                          arc_pmu->n_counters);
 204                if (idx == arc_pmu->n_counters)
 205                        return -EAGAIN;
 206
 207                __set_bit(idx, arc_pmu->used_mask);
 208                hwc->idx = idx;
 209        }
 210
 211        write_aux_reg(ARC_REG_PCT_INDEX, idx);
 212        write_aux_reg(ARC_REG_PCT_CONFIG, 0);
 213        write_aux_reg(ARC_REG_PCT_COUNTL, 0);
 214        write_aux_reg(ARC_REG_PCT_COUNTH, 0);
 215        local64_set(&hwc->prev_count, 0);
 216
 217        hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
 218        if (flags & PERF_EF_START)
 219                arc_pmu_start(event, PERF_EF_RELOAD);
 220
 221        perf_event_update_userpage(event);
 222
 223        return 0;
 224}
 225
 226static int arc_pmu_device_probe(struct platform_device *pdev)
 227{
 228        struct arc_pmu *arc_pmu;
 229        struct arc_reg_pct_build pct_bcr;
 230        struct arc_reg_cc_build cc_bcr;
 231        int i, j, ret;
 232
 233        union cc_name {
 234                struct {
 235                        uint32_t word0, word1;
 236                        char sentinel;
 237                } indiv;
 238                char str[9];
 239        } cc_name;
 240
 241
 242        READ_BCR(ARC_REG_PCT_BUILD, pct_bcr);
 243        if (!pct_bcr.v) {
 244                pr_err("This core does not have performance counters!\n");
 245                return -ENODEV;
 246        }
 247        BUG_ON(pct_bcr.c > ARC_PMU_MAX_HWEVENTS);
 248
 249        READ_BCR(ARC_REG_CC_BUILD, cc_bcr);
 250        if (!cc_bcr.v) {
 251                pr_err("Performance counters exist, but no countable conditions?\n");
 252                return -ENODEV;
 253        }
 254
 255        arc_pmu = devm_kzalloc(&pdev->dev, sizeof(struct arc_pmu), GFP_KERNEL);
 256        if (!arc_pmu)
 257                return -ENOMEM;
 258
 259        arc_pmu->n_counters = pct_bcr.c;
 260        arc_pmu->counter_size = 32 + (pct_bcr.s << 4);
 261
 262        pr_info("ARC perf\t: %d counters (%d bits), %d countable conditions\n",
 263                arc_pmu->n_counters, arc_pmu->counter_size, cc_bcr.c);
 264
 265        cc_name.str[8] = 0;
 266        for (i = 0; i < PERF_COUNT_HW_MAX; i++)
 267                arc_pmu->ev_hw_idx[i] = -1;
 268
 269        for (j = 0; j < cc_bcr.c; j++) {
 270                write_aux_reg(ARC_REG_CC_INDEX, j);
 271                cc_name.indiv.word0 = read_aux_reg(ARC_REG_CC_NAME0);
 272                cc_name.indiv.word1 = read_aux_reg(ARC_REG_CC_NAME1);
 273                for (i = 0; i < ARRAY_SIZE(arc_pmu_ev_hw_map); i++) {
 274                        if (arc_pmu_ev_hw_map[i] &&
 275                            !strcmp(arc_pmu_ev_hw_map[i], cc_name.str) &&
 276                            strlen(arc_pmu_ev_hw_map[i])) {
 277                                pr_debug("mapping %d to idx %d with name %s\n",
 278                                         i, j, cc_name.str);
 279                                arc_pmu->ev_hw_idx[i] = j;
 280                        }
 281                }
 282        }
 283
 284        arc_pmu->pmu = (struct pmu) {
 285                .pmu_enable     = arc_pmu_enable,
 286                .pmu_disable    = arc_pmu_disable,
 287                .event_init     = arc_pmu_event_init,
 288                .add            = arc_pmu_add,
 289                .del            = arc_pmu_del,
 290                .start          = arc_pmu_start,
 291                .stop           = arc_pmu_stop,
 292                .read           = arc_pmu_read,
 293        };
 294
 295        /* ARC 700 PMU does not support sampling events */
 296        arc_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
 297
 298        ret = perf_pmu_register(&arc_pmu->pmu, pdev->name, PERF_TYPE_RAW);
 299
 300        return ret;
 301}
 302
 303#ifdef CONFIG_OF
 304static const struct of_device_id arc_pmu_match[] = {
 305        { .compatible = "snps,arc700-pmu" },
 306        {},
 307};
 308MODULE_DEVICE_TABLE(of, arc_pmu_match);
 309#endif
 310
 311static struct platform_driver arc_pmu_driver = {
 312        .driver = {
 313                .name           = "arc700-pmu",
 314                .of_match_table = of_match_ptr(arc_pmu_match),
 315        },
 316        .probe          = arc_pmu_device_probe,
 317};
 318
 319module_platform_driver(arc_pmu_driver);
 320
 321MODULE_LICENSE("GPL");
 322MODULE_AUTHOR("Mischa Jonker <mjonker@synopsys.com>");
 323MODULE_DESCRIPTION("ARC PMU driver");
 324