rpi_pmu.h

//
// ARMv6 Performance Monitor support
//

//
// Author: P.J. Drongowski
// Date:   17 May 2013
//
// This module uses code from linux/arch/arm/kernel/perf_event_v6.c.
// Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles
//

#ifndef _RPI_PMU_H_
#define _RPI_PMU_H_

#include <stdint.h>
#include <stdio.h>

#define ARMV6_EVENT_ICACHE_MISS	    0x0
#define ARMV6_EVENT_IBUF_STALL	    0x1
#define ARMV6_EVENT_DDEP_STALL	    0x2
#define ARMV6_EVENT_ITLB_MISS	    0x3
#define ARMV6_EVENT_DTLB_MISS	    0x4
#define ARMV6_EVENT_BR_EXEC	    0x5
#define ARMV6_EVENT_BR_MISPREDICT   0x6
#define ARMV6_EVENT_INSTR_EXEC	    0x7
#define ARMV6_EVENT_DCACHE_CACCESS  0x9
#define ARMV6_EVENT_DCACHE_ACCESS   0xA
#define ARMV6_EVENT_DCACHE_MISS	    0xB
#define ARMV6_EVENT_DCACHE_WBACK    0xC
#define ARMV6_EVENT_SW_PC_CHANGE    0xD
#define ARMV6_EVENT_MAIN_TLB_MISS   0xF
#define ARMV6_EVENT_EXPL_D_ACCESS   0x10
#define ARMV6_EVENT_LSU_FULL_STALL  0x11
#define ARMV6_EVENT_WBUF_DRAINED    0x12
#define ARMV6_EVENT_NOP		    0x20
#define ARMV6_EVENT_CPU_CYCLES	    0xFF

//
// Only the ARM1176 supports the following four events
//
#define ARMV6_EVENT_CALL_EXEC       0x23
#define ARMV6_EVENT_RET_EXEC        0x24
#define ARMV6_EVENT_RET_PREDICT     0x25
#define ARMV6_EVENT_RET_MISPREDICT  0x26


enum armv6_counters {
	ARMV6_CYCLE_COUNTER = 0,
	ARMV6_COUNTER0,
	ARMV6_COUNTER1,
};

//
// Read the Performance Monitor Control Register and return its value.
//
static inline unsigned long
armv6_pmcr_read(void)
{
	uint32_t val;
	asm volatile("mrc   p15, 0, %0, c15, c12, 0" : "=r"(val));
	return val;
}

//
// Write the Performance Monitor Control Register using the specified value.
//
static inline void
armv6_pmcr_write(unsigned long val)
{
	asm volatile("mcr   p15, 0, %0, c15, c12, 0" : : "r"(val));
}

//
// Define bit fields within the Performance Monitor Control Register (PMCR).
//
#define ARMV6_PMCR_ENABLE		(1 << 0)
#define ARMV6_PMCR_CTR01_RESET		(1 << 1)
#define ARMV6_PMCR_CCOUNT_RESET		(1 << 2)
#define ARMV6_PMCR_CCOUNT_DIV		(1 << 3)
#define ARMV6_PMCR_COUNT0_IEN		(1 << 4)
#define ARMV6_PMCR_COUNT1_IEN		(1 << 5)
#define ARMV6_PMCR_CCOUNT_IEN		(1 << 6)
#define ARMV6_PMCR_COUNT0_OVERFLOW	(1 << 8)
#define ARMV6_PMCR_COUNT1_OVERFLOW	(1 << 9)
#define ARMV6_PMCR_CCOUNT_OVERFLOW	(1 << 10)
#define ARMV6_PMCR_EVT_COUNT0_SHIFT	20
#define ARMV6_PMCR_EVT_COUNT0_MASK	(0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
#define ARMV6_PMCR_EVT_COUNT1_SHIFT	12
#define ARMV6_PMCR_EVT_COUNT1_MASK	(0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)

#define ARMV6_PMCR_OVERFLOWED_MASK \
	(ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
	 ARMV6_PMCR_CCOUNT_OVERFLOW)

static inline int
armv6_pmcr_has_overflowed(unsigned long pmcr)
{
	return pmcr & ARMV6_PMCR_OVERFLOWED_MASK;
}

//
// Test the specified Performance Monitor counter for an overflow.
//
static inline int
armv6_pmcr_counter_has_overflowed(unsigned long pmcr,
				  enum armv6_counters counter)
{
	int ret = 0;

	if (ARMV6_CYCLE_COUNTER == counter)
		ret = pmcr & ARMV6_PMCR_CCOUNT_OVERFLOW;
	else if (ARMV6_COUNTER0 == counter)
		ret = pmcr & ARMV6_PMCR_COUNT0_OVERFLOW;
	else if (ARMV6_COUNTER1 == counter)
		ret = pmcr & ARMV6_PMCR_COUNT1_OVERFLOW;
	else
		fprintf(stderr, "Invalid counter number (%d)\n", counter) ;

	return ret;
}

//
// Read the specified Performance Monitor counter and return its value.
//
static inline uint32_t
armv6pmu_read_counter(int counter)
{
	unsigned long value = 0;

	if (ARMV6_CYCLE_COUNTER == counter)
		asm volatile("mrc   p15, 0, %0, c15, c12, 1" : "=r"(value));
	else if (ARMV6_COUNTER0 == counter)
		asm volatile("mrc   p15, 0, %0, c15, c12, 2" : "=r"(value));
	else if (ARMV6_COUNTER1 == counter)
		asm volatile("mrc   p15, 0, %0, c15, c12, 3" : "=r"(value));
	else
		fprintf(stderr, "Invalid counter number (%d)\n", counter) ;

	return value;
}

//
// Write a value into the  specified Performance Monitor counter.
//
static inline void
armv6pmu_write_counter(int counter,
		       uint32_t value)
{
	if (ARMV6_CYCLE_COUNTER == counter)
		asm volatile("mcr   p15, 0, %0, c15, c12, 1" : : "r"(value));
	else if (ARMV6_COUNTER0 == counter)
		asm volatile("mcr   p15, 0, %0, c15, c12, 2" : : "r"(value));
	else if (ARMV6_COUNTER1 == counter)
		asm volatile("mcr   p15, 0, %0, c15, c12, 3" : : "r"(value));
	else
		fprintf(stderr, "Invalid counter number (%d)\n", counter) ;
}


extern void start_counting(int evt0, int evt1) ;
extern void stop_counting(void) ;
extern void get_counts(uint64_t* cycles, uint64_t* evt0, uint64_t* evt1) ;
extern void accumulate_counts(void) ;
extern void print_counts(FILE* result_file) ;

#endif