Commit c265a762 authored by Russell King's avatar Russell King Committed by Russell King

[ARM] oprofile: add ARM11 core support

Add basic support for the ARM11 profiling hardware.  This is shared
between the ARM11 UP and ARM11 SMP oprofile support code.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 62d0cfcb
...@@ -19,5 +19,8 @@ config OPROFILE ...@@ -19,5 +19,8 @@ config OPROFILE
If unsure, say N. If unsure, say N.
config OPROFILE_ARM11_CORE
bool
endmenu endmenu
...@@ -8,4 +8,4 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ ...@@ -8,4 +8,4 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprofile-y := $(DRIVER_OBJS) common.o backtrace.o oprofile-y := $(DRIVER_OBJS) common.o backtrace.o
oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o
oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o
/**
* @file op_model_arm11_core.c
* ARM11 Event Monitor Driver
* @remark Copyright 2004 ARM SMP Development Team
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/oprofile.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/smp.h>
#include "op_counter.h"
#include "op_arm_model.h"
#include "op_model_arm11_core.h"
/*
* ARM11 PMU support
*/
static inline void arm11_write_pmnc(u32 val)
{
/* upper 4bits and 7, 11 are write-as-0 */
val &= 0x0ffff77f;
asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val));
}
static inline u32 arm11_read_pmnc(void)
{
u32 val;
asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val));
return val;
}
static void arm11_reset_counter(unsigned int cnt)
{
u32 val = -(u32)counter_config[CPU_COUNTER(smp_processor_id(), cnt)].count;
switch (cnt) {
case CCNT:
asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val));
break;
case PMN0:
asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val));
break;
case PMN1:
asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val));
break;
}
}
int arm11_setup_pmu(void)
{
unsigned int cnt;
u32 pmnc;
if (arm11_read_pmnc() & PMCR_E) {
printk(KERN_ERR "oprofile: CPU%u PMU still enabled when setup new event counter.\n", smp_processor_id());
return -EBUSY;
}
/* initialize PMNC, reset overflow, D bit, C bit and P bit. */
arm11_write_pmnc(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT |
PMCR_C | PMCR_P);
for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) {
unsigned long event;
if (!counter_config[CPU_COUNTER(smp_processor_id(), cnt)].enabled)
continue;
event = counter_config[CPU_COUNTER(smp_processor_id(), cnt)].event & 255;
/*
* Set event (if destined for PMNx counters)
*/
if (cnt == PMN0) {
pmnc |= event << 20;
} else if (cnt == PMN1) {
pmnc |= event << 12;
}
/*
* We don't need to set the event if it's a cycle count
* Enable interrupt for this counter
*/
pmnc |= PMCR_IEN_PMN0 << cnt;
arm11_reset_counter(cnt);
}
arm11_write_pmnc(pmnc);
return 0;
}
int arm11_start_pmu(void)
{
arm11_write_pmnc(arm11_read_pmnc() | PMCR_E);
return 0;
}
int arm11_stop_pmu(void)
{
unsigned int cnt;
arm11_write_pmnc(arm11_read_pmnc() & ~PMCR_E);
for (cnt = PMN0; cnt <= CCNT; cnt++)
arm11_reset_counter(cnt);
return 0;
}
/*
* CPU counters' IRQ handler (one IRQ per CPU)
*/
static irqreturn_t arm11_pmu_interrupt(int irq, void *arg)
{
struct pt_regs *regs = get_irq_regs();
unsigned int cnt;
u32 pmnc;
pmnc = arm11_read_pmnc();
for (cnt = PMN0; cnt <= CCNT; cnt++) {
if ((pmnc & (PMCR_OFL_PMN0 << cnt)) && (pmnc & (PMCR_IEN_PMN0 << cnt))) {
arm11_reset_counter(cnt);
oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), cnt));
}
}
/* Clear counter flag(s) */
arm11_write_pmnc(pmnc);
return IRQ_HANDLED;
}
int arm11_request_interrupts(int *irqs, int nr)
{
unsigned int i;
int ret = 0;
for(i = 0; i < nr; i++) {
ret = request_irq(irqs[i], arm11_pmu_interrupt, IRQF_DISABLED, "CP15 PMU", NULL);
if (ret != 0) {
printk(KERN_ERR "oprofile: unable to request IRQ%u for MPCORE-EM\n",
irqs[i]);
break;
}
}
if (i != nr)
while (i-- != 0)
free_irq(irqs[i], NULL);
return ret;
}
void arm11_release_interrupts(int *irqs, int nr)
{
unsigned int i;
for (i = 0; i < nr; i++)
free_irq(irqs[i], NULL);
}
/**
* @file op_model_arm11_core.h
* ARM11 Event Monitor Driver
* @remark Copyright 2004 ARM SMP Development Team
* @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com>
* @remark Copyright 2000-2004 MontaVista Software Inc
* @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com>
* @remark Copyright 2004 Intel Corporation
* @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk>
* @remark Copyright 2004 Oprofile Authors
*
* @remark Read the file COPYING
*
* @author Zwane Mwaikambo
*/
#ifndef OP_MODEL_ARM11_CORE_H
#define OP_MODEL_ARM11_CORE_H
/*
* Per-CPU PMCR
*/
#define PMCR_E (1 << 0) /* Enable */
#define PMCR_P (1 << 1) /* Count reset */
#define PMCR_C (1 << 2) /* Cycle counter reset */
#define PMCR_D (1 << 3) /* Cycle counter counts every 64th cpu cycle */
#define PMCR_IEN_PMN0 (1 << 4) /* Interrupt enable count reg 0 */
#define PMCR_IEN_PMN1 (1 << 5) /* Interrupt enable count reg 1 */
#define PMCR_IEN_CCNT (1 << 6) /* Interrupt enable cycle counter */
#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */
#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */
#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */
#define PMN0 0
#define PMN1 1
#define CCNT 2
#define CPU_COUNTER(cpu, counter) ((cpu) * 3 + (counter))
int arm11_setup_pmu(void);
int arm11_start_pmu(void);
int arm11_stop_pmu(void);
int arm11_request_interrupts(int *, int);
void arm11_release_interrupts(int *, int);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment