//
// 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