diff options
Diffstat (limited to 'arch/metag/kernel/perf')
-rw-r--r-- | arch/metag/kernel/perf/Makefile | 3 | ||||
-rw-r--r-- | arch/metag/kernel/perf/perf_event.c | 879 | ||||
-rw-r--r-- | arch/metag/kernel/perf/perf_event.h | 106 |
3 files changed, 0 insertions, 988 deletions
diff --git a/arch/metag/kernel/perf/Makefile b/arch/metag/kernel/perf/Makefile deleted file mode 100644 index b158cb2..0000000 --- a/arch/metag/kernel/perf/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# Makefile for performance event core - -obj-y += perf_event.o diff --git a/arch/metag/kernel/perf/perf_event.c b/arch/metag/kernel/perf/perf_event.c deleted file mode 100644 index 7e793eb..0000000 --- a/arch/metag/kernel/perf/perf_event.c +++ /dev/null @@ -1,879 +0,0 @@ -/* - * Meta performance counter support. - * Copyright (C) 2012 Imagination Technologies Ltd - * - * This code is based on the sh pmu code: - * Copyright (C) 2009 Paul Mundt - * - * and on the arm pmu code: - * Copyright (C) 2009 picoChip Designs, Ltd., James Iles - * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include <linux/atomic.h> -#include <linux/export.h> -#include <linux/init.h> -#include <linux/irqchip/metag.h> -#include <linux/perf_event.h> -#include <linux/slab.h> - -#include <asm/core_reg.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/processor.h> - -#include "perf_event.h" - -static int _hw_perf_event_init(struct perf_event *); -static void _hw_perf_event_destroy(struct perf_event *); - -/* Determines which core type we are */ -static struct metag_pmu *metag_pmu __read_mostly; - -/* Processor specific data */ -static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); - -/* PMU admin */ -const char *perf_pmu_name(void) -{ - if (!metag_pmu) - return NULL; - - return metag_pmu->name; -} -EXPORT_SYMBOL_GPL(perf_pmu_name); - -int perf_num_counters(void) -{ - if (metag_pmu) - return metag_pmu->max_events; - - return 0; -} -EXPORT_SYMBOL_GPL(perf_num_counters); - -static inline int metag_pmu_initialised(void) -{ - return !!metag_pmu; -} - -static void release_pmu_hardware(void) -{ - int irq; - unsigned int version = (metag_pmu->version & - (METAC_ID_MINOR_BITS | METAC_ID_REV_BITS)) >> - METAC_ID_REV_S; - - /* Early cores don't have overflow interrupts */ - if (version < 0x0104) - return; - - irq = internal_irq_map(17); - if (irq >= 0) - free_irq(irq, (void *)1); - - irq = internal_irq_map(16); - if (irq >= 0) - free_irq(irq, (void *)0); -} - -static int reserve_pmu_hardware(void) -{ - int err = 0, irq[2]; - unsigned int version = (metag_pmu->version & - (METAC_ID_MINOR_BITS | METAC_ID_REV_BITS)) >> - METAC_ID_REV_S; - - /* Early cores don't have overflow interrupts */ - if (version < 0x0104) - goto out; - - /* - * Bit 16 on HWSTATMETA is the interrupt for performance counter 0; - * similarly, 17 is the interrupt for performance counter 1. - * We can't (yet) interrupt on the cycle counter, because it's a - * register, however it holds a 32-bit value as opposed to 24-bit. - */ - irq[0] = internal_irq_map(16); - if (irq[0] < 0) { - pr_err("unable to map internal IRQ %d\n", 16); - goto out; - } - err = request_irq(irq[0], metag_pmu->handle_irq, IRQF_NOBALANCING, - "metagpmu0", (void *)0); - if (err) { - pr_err("unable to request IRQ%d for metag PMU counters\n", - irq[0]); - goto out; - } - - irq[1] = internal_irq_map(17); - if (irq[1] < 0) { - pr_err("unable to map internal IRQ %d\n", 17); - goto out_irq1; - } - err = request_irq(irq[1], metag_pmu->handle_irq, IRQF_NOBALANCING, - "metagpmu1", (void *)1); - if (err) { - pr_err("unable to request IRQ%d for metag PMU counters\n", - irq[1]); - goto out_irq1; - } - - return 0; - -out_irq1: - free_irq(irq[0], (void *)0); -out: - return err; -} - -/* PMU operations */ -static void metag_pmu_enable(struct pmu *pmu) -{ -} - -static void metag_pmu_disable(struct pmu *pmu) -{ -} - -static int metag_pmu_event_init(struct perf_event *event) -{ - int err = 0; - atomic_t *active_events = &metag_pmu->active_events; - - if (!metag_pmu_initialised()) { - err = -ENODEV; - goto out; - } - - if (has_branch_stack(event)) - return -EOPNOTSUPP; - - event->destroy = _hw_perf_event_destroy; - - if (!atomic_inc_not_zero(active_events)) { - mutex_lock(&metag_pmu->reserve_mutex); - if (atomic_read(active_events) == 0) - err = reserve_pmu_hardware(); - - if (!err) - atomic_inc(active_events); - - mutex_unlock(&metag_pmu->reserve_mutex); - } - - /* Hardware and caches counters */ - switch (event->attr.type) { - case PERF_TYPE_HARDWARE: - case PERF_TYPE_HW_CACHE: - case PERF_TYPE_RAW: - err = _hw_perf_event_init(event); - break; - - default: - return -ENOENT; - } - - if (err) - event->destroy(event); - -out: - return err; -} - -void metag_pmu_event_update(struct perf_event *event, - struct hw_perf_event *hwc, int idx) -{ - u64 prev_raw_count, new_raw_count; - s64 delta; - - /* - * If this counter is chained, it may be that the previous counter - * value has been changed beneath us. - * - * To get around this, we read and exchange the new raw count, then - * add the delta (new - prev) to the generic counter atomically. - * - * Without interrupts, this is the simplest approach. - */ -again: - prev_raw_count = local64_read(&hwc->prev_count); - new_raw_count = metag_pmu->read(idx); - - if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, - new_raw_count) != prev_raw_count) - goto again; - - /* - * Calculate the delta and add it to the counter. - */ - delta = (new_raw_count - prev_raw_count) & MAX_PERIOD; - - local64_add(delta, &event->count); - local64_sub(delta, &hwc->period_left); -} - -int metag_pmu_event_set_period(struct perf_event *event, - struct hw_perf_event *hwc, int idx) -{ - s64 left = local64_read(&hwc->period_left); - s64 period = hwc->sample_period; - int ret = 0; - - /* The period may have been changed */ - if (unlikely(period != hwc->last_period)) - left += period - hwc->last_period; - - if (unlikely(left <= -period)) { - left = period; - local64_set(&hwc->period_left, left); - hwc->last_period = period; - ret = 1; - } - - if (unlikely(left <= 0)) { - left += period; - local64_set(&hwc->period_left, left); - hwc->last_period = period; - ret = 1; - } - - if (left > (s64)metag_pmu->max_period) - left = metag_pmu->max_period; - - if (metag_pmu->write) { - local64_set(&hwc->prev_count, -(s32)left); - metag_pmu->write(idx, -left & MAX_PERIOD); - } - - perf_event_update_userpage(event); - - return ret; -} - -static void metag_pmu_start(struct perf_event *event, int flags) -{ - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - struct hw_perf_event *hwc = &event->hw; - int idx = hwc->idx; - - if (WARN_ON_ONCE(idx == -1)) - return; - - /* - * We always have to reprogram the period, so ignore PERF_EF_RELOAD. - */ - if (flags & PERF_EF_RELOAD) - WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); - - hwc->state = 0; - - /* - * Reset the period. - * Some counters can't be stopped (i.e. are core global), so when the - * counter was 'stopped' we merely disabled the IRQ. If we don't reset - * the period, then we'll either: a) get an overflow too soon; - * or b) too late if the overflow happened since disabling. - * Obviously, this has little bearing on cores without the overflow - * interrupt, as the performance counter resets to zero on write - * anyway. - */ - if (metag_pmu->max_period) - metag_pmu_event_set_period(event, hwc, hwc->idx); - cpuc->events[idx] = event; - metag_pmu->enable(hwc, idx); -} - -static void metag_pmu_stop(struct perf_event *event, int flags) -{ - struct hw_perf_event *hwc = &event->hw; - - /* - * We should always update the counter on stop; see comment above - * why. - */ - if (!(hwc->state & PERF_HES_STOPPED)) { - metag_pmu_event_update(event, hwc, hwc->idx); - metag_pmu->disable(hwc, hwc->idx); - hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; - } -} - -static int metag_pmu_add(struct perf_event *event, int flags) -{ - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - struct hw_perf_event *hwc = &event->hw; - int idx = 0, ret = 0; - - perf_pmu_disable(event->pmu); - - /* check whether we're counting instructions */ - if (hwc->config == 0x100) { - if (__test_and_set_bit(METAG_INST_COUNTER, - cpuc->used_mask)) { - ret = -EAGAIN; - goto out; - } - idx = METAG_INST_COUNTER; - } else { - /* Check whether we have a spare counter */ - idx = find_first_zero_bit(cpuc->used_mask, - atomic_read(&metag_pmu->active_events)); - if (idx >= METAG_INST_COUNTER) { - ret = -EAGAIN; - goto out; - } - - __set_bit(idx, cpuc->used_mask); - } - hwc->idx = idx; - - /* Make sure the counter is disabled */ - metag_pmu->disable(hwc, idx); - - hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; - if (flags & PERF_EF_START) - metag_pmu_start(event, PERF_EF_RELOAD); - - perf_event_update_userpage(event); -out: - perf_pmu_enable(event->pmu); - return ret; -} - -static void metag_pmu_del(struct perf_event *event, int flags) -{ - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - struct hw_perf_event *hwc = &event->hw; - int idx = hwc->idx; - - WARN_ON(idx < 0); - metag_pmu_stop(event, PERF_EF_UPDATE); - cpuc->events[idx] = NULL; - __clear_bit(idx, cpuc->used_mask); - - perf_event_update_userpage(event); -} - -static void metag_pmu_read(struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - - /* Don't read disabled counters! */ - if (hwc->idx < 0) - return; - - metag_pmu_event_update(event, hwc, hwc->idx); -} - -static struct pmu pmu = { - .pmu_enable = metag_pmu_enable, - .pmu_disable = metag_pmu_disable, - - .event_init = metag_pmu_event_init, - - .add = metag_pmu_add, - .del = metag_pmu_del, - .start = metag_pmu_start, - .stop = metag_pmu_stop, - .read = metag_pmu_read, -}; - -/* Core counter specific functions */ -static const int metag_general_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 0x03, - [PERF_COUNT_HW_INSTRUCTIONS] = 0x100, - [PERF_COUNT_HW_CACHE_REFERENCES] = -1, - [PERF_COUNT_HW_CACHE_MISSES] = -1, - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = -1, - [PERF_COUNT_HW_BRANCH_MISSES] = -1, - [PERF_COUNT_HW_BUS_CYCLES] = -1, - [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = -1, - [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = -1, - [PERF_COUNT_HW_REF_CPU_CYCLES] = -1, -}; - -static const int metag_pmu_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - [C(L1D)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = 0x08, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, - [C(L1I)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = 0x09, - [C(RESULT_MISS)] = 0x0a, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, - [C(LL)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, - [C(DTLB)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = 0xd0, - [C(RESULT_MISS)] = 0xd2, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = 0xd4, - [C(RESULT_MISS)] = 0xd5, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, - [C(ITLB)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = 0xd1, - [C(RESULT_MISS)] = 0xd3, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, - [C(BPU)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, - [C(NODE)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, -}; - - -static void _hw_perf_event_destroy(struct perf_event *event) -{ - atomic_t *active_events = &metag_pmu->active_events; - struct mutex *pmu_mutex = &metag_pmu->reserve_mutex; - - if (atomic_dec_and_mutex_lock(active_events, pmu_mutex)) { - release_pmu_hardware(); - mutex_unlock(pmu_mutex); - } -} - -static int _hw_perf_cache_event(int config, int *evp) -{ - unsigned long type, op, result; - int ev; - - if (!metag_pmu->cache_events) - return -EINVAL; - - /* Unpack config */ - type = config & 0xff; - op = (config >> 8) & 0xff; - result = (config >> 16) & 0xff; - - if (type >= PERF_COUNT_HW_CACHE_MAX || - op >= PERF_COUNT_HW_CACHE_OP_MAX || - result >= PERF_COUNT_HW_CACHE_RESULT_MAX) - return -EINVAL; - - ev = (*metag_pmu->cache_events)[type][op][result]; - if (ev == 0) - return -EOPNOTSUPP; - if (ev == -1) - return -EINVAL; - *evp = ev; - return 0; -} - -static int _hw_perf_event_init(struct perf_event *event) -{ - struct perf_event_attr *attr = &event->attr; - struct hw_perf_event *hwc = &event->hw; - int mapping = 0, err; - - switch (attr->type) { - case PERF_TYPE_HARDWARE: - if (attr->config >= PERF_COUNT_HW_MAX) - return -EINVAL; - - mapping = metag_pmu->event_map(attr->config); - break; - - case PERF_TYPE_HW_CACHE: - err = _hw_perf_cache_event(attr->config, &mapping); - if (err) - return err; - break; - - case PERF_TYPE_RAW: - mapping = attr->config; - break; - } - - /* Return early if the event is unsupported */ - if (mapping == -1) - return -EINVAL; - - /* - * Don't assign an index until the event is placed into the hardware. - * -1 signifies that we're still deciding where to put it. On SMP - * systems each core has its own set of counters, so we can't do any - * constraint checking yet. - */ - hwc->idx = -1; - - /* Store the event encoding */ - hwc->config |= (unsigned long)mapping; - - /* - * For non-sampling runs, limit the sample_period to half of the - * counter width. This way, the new counter value should be less - * likely to overtake the previous one (unless there are IRQ latency - * issues...) - */ - if (metag_pmu->max_period) { - if (!hwc->sample_period) { - hwc->sample_period = metag_pmu->max_period >> 1; - hwc->last_period = hwc->sample_period; - local64_set(&hwc->period_left, hwc->sample_period); - } - } - - return 0; -} - -static void metag_pmu_enable_counter(struct hw_perf_event *event, int idx) -{ - struct cpu_hw_events *events = this_cpu_ptr(&cpu_hw_events); - unsigned int config = event->config; - unsigned int tmp = config & 0xf0; - unsigned long flags; - - raw_spin_lock_irqsave(&events->pmu_lock, flags); - - /* - * Check if we're enabling the instruction counter (index of - * MAX_HWEVENTS - 1) - */ - if (METAG_INST_COUNTER == idx) { - WARN_ONCE((config != 0x100), - "invalid configuration (%d) for counter (%d)\n", - config, idx); - local64_set(&event->prev_count, __core_reg_get(TXTACTCYC)); - goto unlock; - } - - /* Check for a core internal or performance channel event. */ - if (tmp) { - /* PERF_ICORE/PERF_CHAN only exist since Meta2 */ -#ifdef METAC_2_1 - void *perf_addr; - - /* - * Anything other than a cycle count will write the low- - * nibble to the correct counter register. - */ - switch (tmp) { - case 0xd0: - perf_addr = (void *)PERF_ICORE(idx); - break; - - case 0xf0: - perf_addr = (void *)PERF_CHAN(idx); - break; - - default: - perf_addr = NULL; - break; - } - - if (perf_addr) - metag_out32((config & 0x0f), perf_addr); -#endif - - /* - * Now we use the high nibble as the performance event to - * to count. - */ - config = tmp >> 4; - } - - tmp = ((config & 0xf) << 28) | - ((1 << 24) << hard_processor_id()); - if (metag_pmu->max_period) - /* - * Cores supporting overflow interrupts may have had the counter - * set to a specific value that needs preserving. - */ - tmp |= metag_in32(PERF_COUNT(idx)) & 0x00ffffff; - else - /* - * Older cores reset the counter on write, so prev_count needs - * resetting too so we can calculate a correct delta. - */ - local64_set(&event->prev_count, 0); - - metag_out32(tmp, PERF_COUNT(idx)); -unlock: - raw_spin_unlock_irqrestore(&events->pmu_lock, flags); -} - -static void metag_pmu_disable_counter(struct hw_perf_event *event, int idx) -{ - struct cpu_hw_events *events = this_cpu_ptr(&cpu_hw_events); - unsigned int tmp = 0; - unsigned long flags; - - /* - * The cycle counter can't be disabled per se, as it's a hardware - * thread register which is always counting. We merely return if this - * is the counter we're attempting to disable. - */ - if (METAG_INST_COUNTER == idx) - return; - - /* - * The counter value _should_ have been read prior to disabling, - * as if we're running on an early core then the value gets reset to - * 0, and any read after that would be useless. On the newer cores, - * however, it's better to read-modify-update this for purposes of - * the overflow interrupt. - * Here we remove the thread id AND the event nibble (there are at - * least two events that count events that are core global and ignore - * the thread id mask). This only works because we don't mix thread - * performance counts, and event 0x00 requires a thread id mask! - */ - raw_spin_lock_irqsave(&events->pmu_lock, flags); - - tmp = metag_in32(PERF_COUNT(idx)); - tmp &= 0x00ffffff; - metag_out32(tmp, PERF_COUNT(idx)); - - raw_spin_unlock_irqrestore(&events->pmu_lock, flags); -} - -static u64 metag_pmu_read_counter(int idx) -{ - u32 tmp = 0; - - if (METAG_INST_COUNTER == idx) { - tmp = __core_reg_get(TXTACTCYC); - goto out; - } - - tmp = metag_in32(PERF_COUNT(idx)) & 0x00ffffff; -out: - return tmp; -} - -static void metag_pmu_write_counter(int idx, u32 val) -{ - struct cpu_hw_events *events = this_cpu_ptr(&cpu_hw_events); - u32 tmp = 0; - unsigned long flags; - - /* - * This _shouldn't_ happen, but if it does, then we can just - * ignore the write, as the register is read-only and clear-on-write. - */ - if (METAG_INST_COUNTER == idx) - return; - - /* - * We'll keep the thread mask and event id, and just update the - * counter itself. Also , we should bound the value to 24-bits. - */ - raw_spin_lock_irqsave(&events->pmu_lock, flags); - - val &= 0x00ffffff; - tmp = metag_in32(PERF_COUNT(idx)) & 0xff000000; - val |= tmp; - metag_out32(val, PERF_COUNT(idx)); - - raw_spin_unlock_irqrestore(&events->pmu_lock, flags); -} - -static int metag_pmu_event_map(int idx) -{ - return metag_general_events[idx]; -} - -static irqreturn_t metag_pmu_counter_overflow(int irq, void *dev) -{ - int idx = (int)dev; - struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); - struct perf_event *event = cpuhw->events[idx]; - struct hw_perf_event *hwc = &event->hw; - struct pt_regs *regs = get_irq_regs(); - struct perf_sample_data sampledata; - unsigned long flags; - u32 counter = 0; - - /* - * We need to stop the core temporarily from generating another - * interrupt while we disable this counter. However, we don't want - * to flag the counter as free - */ - __global_lock2(flags); - counter = metag_in32(PERF_COUNT(idx)); - metag_out32((counter & 0x00ffffff), PERF_COUNT(idx)); - __global_unlock2(flags); - - /* Update the counts and reset the sample period */ - metag_pmu_event_update(event, hwc, idx); - perf_sample_data_init(&sampledata, 0, hwc->last_period); - metag_pmu_event_set_period(event, hwc, idx); - - /* - * Enable the counter again once core overflow processing has - * completed. Note the counter value may have been modified while it was - * inactive to set it up ready for the next interrupt. - */ - if (!perf_event_overflow(event, &sampledata, regs)) { - __global_lock2(flags); - counter = (counter & 0xff000000) | - (metag_in32(PERF_COUNT(idx)) & 0x00ffffff); - metag_out32(counter, PERF_COUNT(idx)); - __global_unlock2(flags); - } - - return IRQ_HANDLED; -} - -static struct metag_pmu _metag_pmu = { - .handle_irq = metag_pmu_counter_overflow, - .enable = metag_pmu_enable_counter, - .disable = metag_pmu_disable_counter, - .read = metag_pmu_read_counter, - .write = metag_pmu_write_counter, - .event_map = metag_pmu_event_map, - .cache_events = &metag_pmu_cache_events, - .max_period = MAX_PERIOD, - .max_events = MAX_HWEVENTS, -}; - -/* PMU CPU hotplug notifier */ -static int metag_pmu_starting_cpu(unsigned int cpu) -{ - struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); - - memset(cpuc, 0, sizeof(struct cpu_hw_events)); - raw_spin_lock_init(&cpuc->pmu_lock); - - return 0; -} - -/* PMU Initialisation */ -static int __init init_hw_perf_events(void) -{ - int ret = 0, cpu; - u32 version = *(u32 *)METAC_ID; - int major = (version & METAC_ID_MAJOR_BITS) >> METAC_ID_MAJOR_S; - int min_rev = (version & (METAC_ID_MINOR_BITS | METAC_ID_REV_BITS)) - >> METAC_ID_REV_S; - - /* Not a Meta 2 core, then not supported */ - if (0x02 > major) { - pr_info("no hardware counter support available\n"); - goto out; - } else if (0x02 == major) { - metag_pmu = &_metag_pmu; - - if (min_rev < 0x0104) { - /* - * A core without overflow interrupts, and clear-on- - * write counters. - */ - metag_pmu->handle_irq = NULL; - metag_pmu->write = NULL; - metag_pmu->max_period = 0; - } - - metag_pmu->name = "meta2"; - metag_pmu->version = version; - metag_pmu->pmu = pmu; - } - - pr_info("enabled with %s PMU driver, %d counters available\n", - metag_pmu->name, metag_pmu->max_events); - - /* - * Early cores have "limited" counters - they have no overflow - * interrupts - and so are unable to do sampling without extra work - * and timer assistance. - */ - if (metag_pmu->max_period == 0) { - metag_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; - } - - /* Initialise the active events and reservation mutex */ - atomic_set(&metag_pmu->active_events, 0); - mutex_init(&metag_pmu->reserve_mutex); - - /* Clear the counters */ - metag_out32(0, PERF_COUNT(0)); - metag_out32(0, PERF_COUNT(1)); - - cpuhp_setup_state(CPUHP_AP_PERF_METAG_STARTING, - "perf/metag:starting", metag_pmu_starting_cpu, - NULL); - - ret = perf_pmu_register(&pmu, metag_pmu->name, PERF_TYPE_RAW); - if (ret) - cpuhp_remove_state_nocalls(CPUHP_AP_PERF_METAG_STARTING); - return ret; -} -early_initcall(init_hw_perf_events); diff --git a/arch/metag/kernel/perf/perf_event.h b/arch/metag/kernel/perf/perf_event.h deleted file mode 100644 index fd10a13..0000000 --- a/arch/metag/kernel/perf/perf_event.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Meta performance counter support. - * Copyright (C) 2012 Imagination Technologies Ltd - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#ifndef METAG_PERF_EVENT_H_ -#define METAG_PERF_EVENT_H_ - -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/perf_event.h> - -/* For performance counter definitions */ -#include <asm/metag_mem.h> - -/* - * The Meta core has two performance counters, with 24-bit resolution. Newer - * cores generate an overflow interrupt on transition from 0xffffff to 0. - * - * Each counter consists of the counter id, hardware thread id, and the count - * itself; each counter can be assigned to multiple hardware threads at any - * one time, with the returned count being an aggregate of events. A small - * number of events are thread global, i.e. they count the aggregate of all - * threads' events, regardless of the thread selected. - * - * Newer cores can store an arbitrary 24-bit number in the counter, whereas - * older cores will clear the counter bits on write. - * - * We also have a pseudo-counter in the form of the thread active cycles - * counter (which, incidentally, is also bound to - */ - -#define MAX_HWEVENTS 3 -#define MAX_PERIOD ((1UL << 24) - 1) -#define METAG_INST_COUNTER (MAX_HWEVENTS - 1) - -/** - * struct cpu_hw_events - a processor core's performance events - * @events: an array of perf_events active for a given index. - * @used_mask: a bitmap of in-use counters. - * @pmu_lock: a perf counter lock - * - * This is a per-cpu/core structure that maintains a record of its - * performance counters' state. - */ -struct cpu_hw_events { - struct perf_event *events[MAX_HWEVENTS]; - unsigned long used_mask[BITS_TO_LONGS(MAX_HWEVENTS)]; - raw_spinlock_t pmu_lock; -}; - -/** - * struct metag_pmu - the Meta PMU structure - * @pmu: core pmu structure - * @name: pmu name - * @version: core version - * @handle_irq: overflow interrupt handler - * @enable: enable a counter - * @disable: disable a counter - * @read: read the value of a counter - * @write: write a value to a counter - * @event_map: kernel event to counter event id map - * @cache_events: kernel cache counter to core cache counter map - * @max_period: maximum value of the counter before overflow - * @max_events: maximum number of counters available at any one time - * @active_events: number of active counters - * @reserve_mutex: counter reservation mutex - * - * This describes the main functionality and data used by the performance - * event core. - */ -struct metag_pmu { - struct pmu pmu; - const char *name; - u32 version; - irqreturn_t (*handle_irq)(int irq_num, void *dev); - void (*enable)(struct hw_perf_event *evt, int idx); - void (*disable)(struct hw_perf_event *evt, int idx); - u64 (*read)(int idx); - void (*write)(int idx, u32 val); - int (*event_map)(int idx); - const int (*cache_events)[PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX]; - u32 max_period; - int max_events; - atomic_t active_events; - struct mutex reserve_mutex; -}; - -/* Convenience macros for accessing the perf counters */ -/* Define some convenience accessors */ -#define PERF_COUNT(x) (PERF_COUNT0 + (sizeof(u64) * (x))) -#define PERF_ICORE(x) (PERF_ICORE0 + (sizeof(u64) * (x))) -#define PERF_CHAN(x) (PERF_CHAN0 + (sizeof(u64) * (x))) - -/* Cache index macros */ -#define C(x) PERF_COUNT_HW_CACHE_##x -#define CACHE_OP_UNSUPPORTED 0xfffe -#define CACHE_OP_NONSENSE 0xffff - -#endif |