/*
* Copyright 2014 Tilera Corporation. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for
* more details.
*
*
* Perf_events support for Tile processor.
*
* This code is based upon the x86 perf event
* code, which is:
*
* Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
* Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
* Copyright (C) 2009 Jaswinder Singh Rajput
* Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
* Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
* Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
* Copyright (C) 2009 Google, Inc., Stephane Eranian
*/
#include <linux/kprobes.h>
#include <linux/kernel.h>
#include <linux/kdebug.h>
#include <linux/mutex.h>
#include <linux/bitmap.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/perf_event.h>
#include <linux/atomic.h>
#include <asm/traps.h>
#include <asm/stack.h>
#include <asm/pmc.h>
#include <hv/hypervisor.h>
#define TILE_MAX_COUNTERS 4
#define PERF_COUNT_0_IDX 0
#define PERF_COUNT_1_IDX 1
#define AUX_PERF_COUNT_0_IDX 2
#define AUX_PERF_COUNT_1_IDX 3
struct cpu_hw_events {
int n_events;
struct perf_event *events[TILE_MAX_COUNTERS]; /* counter order */
struct perf_event *event_list[TILE_MAX_COUNTERS]; /* enabled
order */
int assign[TILE_MAX_COUNTERS];
unsigned long active_mask[BITS_TO_LONGS(TILE_MAX_COUNTERS)];
unsigned long used_mask;
};
/* TILE arch specific performance monitor unit */
struct tile_pmu {
const char *name;
int version;
const int *hw_events; /* generic hw events table */
/* generic hw cache events table */
const int (*cache_events)[PERF_COUNT_HW_CACHE_MAX]
[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX];
int (*map_hw_event)(u64); /*method used to map
hw events */
int (*map_cache_event)(u64); /*method used to map
cache events */
u64 max_period; /* max sampling period */
u64 cntval_mask; /* counter width mask */
int cntval_bits; /* counter width */
int max_events; /* max generic hw events
in map */
int num_counters; /* number base + aux counters */
int num_base_counters; /* number base counters */
};
DEFINE_PER_CPU(u64, perf_irqs);
static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
#define TILE_OP_UNSUPP (-1)
#ifndef __tilegx__
/* TILEPro hardware events map */
static const int tile_hw_event_map[] = {
[PERF_COUNT_HW_CPU_CYCLES] = 0x01, /* ONE */
[PERF_COUNT_HW_INSTRUCTIONS] = 0x06, /* MP_BUNDLE_RETIRED */
[PERF_COUNT_HW_CACHE_REFERENCES] = TILE_OP_UNSUPP,
[PERF_COUNT_HW_CACHE_MISSES] = TILE_OP_UNSUPP,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x16, /*
MP_CONDITIONAL_BRANCH_ISSUED */
[PERF_COUNT_HW_BRANCH_MISSES] = 0x14, /*
MP_CONDITIONAL_BRANCH_MISSPREDICT */
[PERF_COUNT_HW_BUS_CYCLES] = TILE_OP_UNSUPP,
};
#else
/* TILEGx hardware events map */
static const int tile_hw_event_map[] = {
[PERF_COUNT_HW_CPU_CYCLES] = 0x181, /* ONE */
[PERF_COUNT_HW_INSTRUCTIONS] = 0xdb, /* INSTRUCTION_BUNDLE */
[PERF_COUNT_HW_CACHE_REFERENCES] = TILE_OP_UNSUPP,
[PERF_COUNT_HW_CACHE_MISSES] = TILE_OP_UNSUPP,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0xd9, /*
COND_BRANCH_PRED_CORRECT */
[PERF_COUNT_HW_BRANCH_MISSES] = 0xda, /*
COND_BRANCH_PRED_INCORRECT */
[PERF_COUNT_HW_BUS_CYCLES] = TILE_OP_UNSUPP,
};
#endif
#define C(x) PERF_COUNT_HW_CACHE_##x
/*
* Generalized hw caching related hw_event table, filled
* in on a per model basis. A value of -1 means
* 'not supported', any other value means the
* raw hw_event ID.
*/
#ifndef __tilegx__
/* TILEPro hardware cache event map */
static const int tile_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
[C(L1D)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
[C(RESULT_MISS)] = 0x21, /* RD_MISS */
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
[C(RESULT_MISS)] = 0x22, /* WR_MISS */
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
[C(RESULT_MISS)] = TILE_OP_UNSUPP,
},
},
[C(L1I)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = 0x12, /* MP_ICACHE_HIT_ISSUED */
[C(RESULT_MISS)] = TILE_OP_UNSUPP,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
[C(RESULT_MISS)] = TILE_OP_UNSUPP,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
[C(RESULT_MISS)] = TILE_OP_UNSUPP,
},
},
[C(LL)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
[C(RESULT_MISS)] = TILE_OP_UNSUPP,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = TILE_OP_UNSUPP,