|
Packit |
577717 |
/* $Id: arm.c,v 1.1.2.3 2009/06/11 12:33:51 mikpe Exp $
|
|
Packit |
577717 |
* ARM/XScale performance-monitoring counters driver.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Copyright (C) 2005-2009 Mikael Pettersson
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
#include <linux/version.h>
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
|
Packit |
577717 |
#include <linux/config.h>
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
#define __NO_VERSION__
|
|
Packit |
577717 |
#include <linux/module.h>
|
|
Packit |
577717 |
#include <linux/init.h>
|
|
Packit |
577717 |
#include <linux/sched.h>
|
|
Packit |
577717 |
#include <linux/perfctr.h>
|
|
Packit |
577717 |
#include <asm/cputype.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include "compat.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Support for lazy evntsel and perfctr register updates. */
|
|
Packit |
577717 |
struct per_cpu_cache { /* roughly a subset of perfctr_cpu_state */
|
|
Packit |
577717 |
union {
|
|
Packit |
577717 |
unsigned int id; /* cache owner id */
|
|
Packit |
577717 |
} k1;
|
|
Packit |
577717 |
union {
|
|
Packit |
577717 |
struct {
|
|
Packit |
577717 |
unsigned int pmnc;
|
|
Packit |
577717 |
} xsc1;
|
|
Packit |
577717 |
struct {
|
|
Packit |
577717 |
unsigned int evtsel;
|
|
Packit |
577717 |
unsigned int inten;
|
|
Packit |
577717 |
unsigned int pmnc;
|
|
Packit |
577717 |
} xsc2;
|
|
Packit |
577717 |
} arm;
|
|
Packit |
577717 |
} ____cacheline_aligned;
|
|
Packit |
577717 |
static struct per_cpu_cache per_cpu_cache[NR_CPUS] __cacheline_aligned;
|
|
Packit |
577717 |
#define __get_cpu_cache(cpu) (&per_cpu_cache[cpu])
|
|
Packit |
577717 |
#define get_cpu_cache() (&per_cpu_cache[smp_processor_id()])
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Structure for counter snapshots, as 32-bit values. */
|
|
Packit |
577717 |
struct perfctr_low_ctrs {
|
|
Packit |
577717 |
unsigned int tsc;
|
|
Packit |
577717 |
unsigned int pmc[4];
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define PMU_XSC1 1
|
|
Packit |
577717 |
#define PMU_XSC2 2
|
|
Packit |
577717 |
static unsigned int pmu_type;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int new_id(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
static DEFINE_SPINLOCK(lock);
|
|
Packit |
577717 |
static unsigned int counter;
|
|
Packit |
577717 |
int id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
spin_lock(&lock);
|
|
Packit |
577717 |
id = ++counter;
|
|
Packit |
577717 |
spin_unlock(&lock);
|
|
Packit |
577717 |
return id;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifndef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
#define perfctr_cstatus_has_ictrs(cstatus) 0
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if defined(CONFIG_SMP) && defined(CONFIG_PERFCTR_INTERRUPT_SUPPORT)
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void
|
|
Packit |
577717 |
set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
state->k1.isuspend_cpu = cpu;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline int
|
|
Packit |
577717 |
is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return state->k1.isuspend_cpu == cpu;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
state->k1.isuspend_cpu = NR_CPUS;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) { }
|
|
Packit |
577717 |
static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) { return 1; }
|
|
Packit |
577717 |
static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { }
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Driver procedures. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* XScale1 driver procedures.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xsc1_read_pmnc(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c0, c0, 0" : "=r"(val));
|
|
Packit |
577717 |
/* bits 1, 2, 7, 11, 28-31 are read-unpredictable */
|
|
Packit |
577717 |
val &= 0x0ffff779;
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xsc1_write_pmnc(u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* bits 7, 11, 28-31 are write-as-0 */
|
|
Packit |
577717 |
val &= 0x0ffff77f;
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c0, c0, 0" : : "r"(val));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xsc1_read_ccnt(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c1, c0, 0" : "=r"(val));
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if 0
|
|
Packit |
577717 |
static void xsc1_write_ccnt(u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c1, c0, 0" : : "r"(val));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xsc1_read_pmc(u32 counter)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch (counter) {
|
|
Packit |
577717 |
default: /* impossible, but silences gcc warning */
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c2, c0, 0" : "=r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c3, c0, 0" : "=r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if 0
|
|
Packit |
577717 |
static void xsc1_write_pmc(u32 counter, u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (counter) {
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c2, c0, 0" : : "r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c3, c0, 0" : : "r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xsc1_clear_counters(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 pmnc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pmnc = xsc1_read_pmnc();
|
|
Packit |
577717 |
/* preserve CCNT settings */
|
|
Packit |
577717 |
pmnc &= (1 << 10) | (1 << 6) | (1 << 3) | (1 << 0);
|
|
Packit |
577717 |
/* update non-CCNT settings: set event selectors to idle, and
|
|
Packit |
577717 |
reset the performance counters and their overflow flags. */
|
|
Packit |
577717 |
pmnc |= (0xFF << 20) | (0xFF << 12) | (0x3 << 8) | (1 << 1);
|
|
Packit |
577717 |
xsc1_write_pmnc(pmnc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int xsc1_nr_pmcs(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return 2;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* XScale2 driver procedures.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xsc2_read_pmnc(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c0, c1, 0" : "=r"(val));
|
|
Packit |
577717 |
/* bits 1, 2, 4-23 are read-unpredictable */
|
|
Packit |
577717 |
val &= 0xff000009;
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xsc2_write_pmnc(u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* bits 4-23 are write-as-0, 24-31 are write ignored */
|
|
Packit |
577717 |
val &= 0x0000000f;
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c0, c1, 0" : : "r"(val));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xsc2_read_ccnt(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c1, c1, 0" : "=r"(val));
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if 0
|
|
Packit |
577717 |
static void xsc2_write_ccnt(u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c1, c1, 0" : : "r"(val));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xsc2_read_inten(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c4, c1, 0" : "=r"(val));
|
|
Packit |
577717 |
/* bits 5-31 are read-unpredictable */
|
|
Packit |
577717 |
val &= 0x0000001f;
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xsc2_write_inten(u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* bits 5-31 are write-as-zero */
|
|
Packit |
577717 |
val &= 0x0000001f;
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c4, c1, 0" : : "r"(val));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if 0
|
|
Packit |
577717 |
static u32 xsc2_read_flag(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c5, c1, 0" : "=r"(val));
|
|
Packit |
577717 |
/* bits 5-31 are read-unpredictable */
|
|
Packit |
577717 |
val &= 0x0000001f;
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xsc2_write_flag(u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* bits 5-31 are write-as-zero */
|
|
Packit |
577717 |
val &= 0x0000001f;
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c5, c1, 0" : : "r"(val));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if 0
|
|
Packit |
577717 |
static u32 xsc2_read_evtsel(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c8, c1, 0" : "=r"(val));
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xsc2_write_evtsel(u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c8, c1, 0" : : "r"(val));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xsc2_read_pmc(u32 counter)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch (counter) {
|
|
Packit |
577717 |
default: /* impossible, but silences gcc warning */
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c0, c2, 0" : "=r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c1, c2, 0" : "=r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c2, c2, 0" : "=r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
__asm__ __volatile__("mrc p14, 0, %0, c3, c2, 0" : "=r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return val;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if 0
|
|
Packit |
577717 |
static void xsc2_write_pmc(u32 counter, u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (counter) {
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c0, c2, 0" : : "r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c1, c2, 0" : : "r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c2, c2, 0" : : "r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
__asm__ __volatile__("mcr p14, 0, %0, c3, c2, 0" : : "r"(val));
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xsc2_clear_counters(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
u32 val;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* clear interrupt enable bits */
|
|
Packit |
577717 |
val = xsc2_read_inten();
|
|
Packit |
577717 |
val &= (1 << 0); /* all but CCNT */
|
|
Packit |
577717 |
xsc2_write_inten(val);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* set event selectors to idle */
|
|
Packit |
577717 |
xsc2_write_evtsel(0xFFFFFFFF);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* reset the performance counters */
|
|
Packit |
577717 |
val = xsc2_read_pmnc();
|
|
Packit |
577717 |
val &= (1 << 3) | (1 << 0); /* preserve CCNT settings */
|
|
Packit |
577717 |
val |= (1 << 1); /* reset the performance counters */
|
|
Packit |
577717 |
xsc2_write_pmnc(val);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* clear overflow status bits */
|
|
Packit |
577717 |
xsc2_write_flag(0x1E); /* all but CCNT */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int xsc2_nr_pmcs(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return 4;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* XScale driver procedures.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xscale_read_ccnt(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (pmu_type == PMU_XSC1)
|
|
Packit |
577717 |
return xsc1_read_ccnt();
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
return xsc2_read_ccnt();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static u32 xscale_read_pmc(u32 counter)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (pmu_type == PMU_XSC1)
|
|
Packit |
577717 |
return xsc1_read_pmc(counter);
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
return xsc2_read_pmc(counter);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xscale_clear_counters(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (pmu_type == PMU_XSC1)
|
|
Packit |
577717 |
xsc1_clear_counters();
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
xsc2_clear_counters();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int xscale_nr_pmcs(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (pmu_type == PMU_XSC1)
|
|
Packit |
577717 |
return xsc1_nr_pmcs();
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
return xsc2_nr_pmcs();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int xscale_check_event(unsigned int evntsel)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* Events 0x00-0x0D are defined for the XScale core
|
|
Packit |
577717 |
and the IXP42x family.
|
|
Packit |
577717 |
Event 0xFF is defined as an "idle" event, but users
|
|
Packit |
577717 |
have no reason to specify it, so we reject it.
|
|
Packit |
577717 |
Events 0x10-0x16 are defined in oprofile, but not
|
|
Packit |
577717 |
in the XScale core or IXP42x manuals. */
|
|
Packit |
577717 |
return (evntsel <= 0x0D) ? 0 : -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xscale_read_counters(struct perfctr_cpu_state *state,
|
|
Packit |
577717 |
struct perfctr_low_ctrs *ctrs)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs, i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cstatus = state->cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
ctrs->tsc = xscale_read_ccnt();
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nrctrs; ++i) {
|
|
Packit |
577717 |
unsigned int pmc = state->pmc[i].map;
|
|
Packit |
577717 |
ctrs->pmc[i] = xscale_read_pmc(pmc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int xscale_check_control(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i, nractrs, nrctrs, pmc_mask, pmi_mask, pmc;
|
|
Packit |
577717 |
unsigned int nr_pmcs, evntsel[4];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
nr_pmcs = xscale_nr_pmcs();
|
|
Packit |
577717 |
nractrs = state->control.nractrs;
|
|
Packit |
577717 |
nrctrs = nractrs + state->control.nrictrs;
|
|
Packit |
577717 |
if (nrctrs < nractrs || nrctrs > nr_pmcs)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pmc_mask = 0;
|
|
Packit |
577717 |
pmi_mask = 0;
|
|
Packit |
577717 |
memset(evntsel, 0, sizeof evntsel);
|
|
Packit |
577717 |
for(i = 0; i < nrctrs; ++i) {
|
|
Packit |
577717 |
pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
state->pmc[i].map = pmc;
|
|
Packit |
577717 |
if (pmc >= nr_pmcs || (pmc_mask & (1<
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
pmc_mask |= (1<
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (i >= nractrs)
|
|
Packit |
577717 |
pmi_mask |= (1<
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evntsel[pmc] = state->control.evntsel[i];
|
|
Packit |
577717 |
if (xscale_check_event(evntsel[pmc]))
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch (pmu_type) {
|
|
Packit |
577717 |
case PMU_XSC1:
|
|
Packit |
577717 |
state->arm.xsc1.pmnc =
|
|
Packit |
577717 |
((evntsel[1] << 20) |
|
|
Packit |
577717 |
(evntsel[0] << 12) |
|
|
Packit |
577717 |
(pmi_mask << 4) | /* inten field */
|
|
Packit |
577717 |
1); /* enable */
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PMU_XSC2:
|
|
Packit |
577717 |
state->arm.xsc2.evtsel =
|
|
Packit |
577717 |
((evntsel[3] << 24) |
|
|
Packit |
577717 |
(evntsel[2] << 16) |
|
|
Packit |
577717 |
(evntsel[1] << 8) |
|
|
Packit |
577717 |
evntsel[0]);
|
|
Packit |
577717 |
state->arm.xsc2.inten = pmi_mask << 1;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
state->k1.id = new_id();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
/* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */
|
|
Packit |
577717 |
/* PRE: counters frozen *//* XXX: that is FALSE on XScale! */
|
|
Packit |
577717 |
static void xscale_isuspend(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct per_cpu_cache *cache;
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs, i;
|
|
Packit |
577717 |
int cpu;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpu = smp_processor_id();
|
|
Packit |
577717 |
set_isuspend_cpu(state, cpu); /* early to limit cpu's live range */
|
|
Packit |
577717 |
cache = __get_cpu_cache(cpu);
|
|
Packit |
577717 |
cstatus = state->cstatus;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) {
|
|
Packit |
577717 |
unsigned int pmc = state->pmc[i].map;
|
|
Packit |
577717 |
unsigned int now = read_pmc(pmc);
|
|
Packit |
577717 |
state->pmc[i].sum += now - state->pmc[i].start;
|
|
Packit |
577717 |
state->pmc[i].start = now;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/* cache->k1.id is still == state->k1.id */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xscale_iresume(const struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct per_cpu_cache *cache;
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs, i;
|
|
Packit |
577717 |
int cpu;
|
|
Packit |
577717 |
unsigned int pmc[4];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpu = smp_processor_id();
|
|
Packit |
577717 |
cache = __get_cpu_cache(cpu);
|
|
Packit |
577717 |
if (cache->k1.id == state->k1.id) {
|
|
Packit |
577717 |
cache->k1.id = 0; /* force reload of cleared EVNTSELs */
|
|
Packit |
577717 |
if (is_isuspend_cpu(state, cpu))
|
|
Packit |
577717 |
return; /* skip reload of PMCs */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/* XXX: the rest is still the PPC code */
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* The CPU state wasn't ours.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* The counters must be frozen before being reinitialised,
|
|
Packit |
577717 |
* to prevent unexpected increments and missed overflows.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* All unused counters must be reset to a non-overflow state.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (!(cache->ppc_mmcr[0] & MMCR0_FC)) {
|
|
Packit |
577717 |
cache->ppc_mmcr[0] |= MMCR0_FC;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, cache->ppc_mmcr[0]);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
memset(&pmc[0], 0, sizeof pmc);
|
|
Packit |
577717 |
cstatus = state->cstatus;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i)
|
|
Packit |
577717 |
pmc[state->pmc[i].map] = state->pmc[i].start;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch (pm_type) {
|
|
Packit |
577717 |
case PM_7450:
|
|
Packit |
577717 |
mtspr(SPRN_PMC6, pmc[6-1]);
|
|
Packit |
577717 |
mtspr(SPRN_PMC5, pmc[5-1]);
|
|
Packit |
577717 |
case PM_7400:
|
|
Packit |
577717 |
case PM_750:
|
|
Packit |
577717 |
case PM_604e:
|
|
Packit |
577717 |
mtspr(SPRN_PMC4, pmc[4-1]);
|
|
Packit |
577717 |
mtspr(SPRN_PMC3, pmc[3-1]);
|
|
Packit |
577717 |
case PM_604:
|
|
Packit |
577717 |
mtspr(SPRN_PMC2, pmc[2-1]);
|
|
Packit |
577717 |
mtspr(SPRN_PMC1, pmc[1-1]);
|
|
Packit |
577717 |
case PM_NONE:
|
|
Packit |
577717 |
;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/* cache->k1.id remains != state->k1.id */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void xscale_write_control(const struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct per_cpu_cache *cache;
|
|
Packit |
577717 |
unsigned int value;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cache = get_cpu_cache();
|
|
Packit |
577717 |
if (cache->k1.id == state->k1.id)
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
switch (pmu_type) {
|
|
Packit |
577717 |
case PMU_XSC1:
|
|
Packit |
577717 |
value = state->arm.xsc1.pmnc;
|
|
Packit |
577717 |
if (value != cache->arm.xsc1.pmnc) {
|
|
Packit |
577717 |
cache->arm.xsc1.pmnc = value;
|
|
Packit |
577717 |
xsc1_write_pmnc(value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PMU_XSC2:
|
|
Packit |
577717 |
value = cache->arm.xsc2.pmnc;
|
|
Packit |
577717 |
if (value & 1) {
|
|
Packit |
577717 |
value &= ~1;
|
|
Packit |
577717 |
cache->arm.xsc2.pmnc = value;
|
|
Packit |
577717 |
xsc2_write_pmnc(value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
value = state->arm.xsc2.evtsel;
|
|
Packit |
577717 |
if (value != cache->arm.xsc2.evtsel) {
|
|
Packit |
577717 |
cache->arm.xsc2.evtsel = value;
|
|
Packit |
577717 |
xsc2_write_evtsel(value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
value = state->arm.xsc2.inten;
|
|
Packit |
577717 |
if (value != cache->arm.xsc2.inten) {
|
|
Packit |
577717 |
cache->arm.xsc2.inten = value;
|
|
Packit |
577717 |
xsc2_write_inten(value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
value = cache->arm.xsc2.pmnc;
|
|
Packit |
577717 |
value |= 1;
|
|
Packit |
577717 |
cache->arm.xsc2.pmnc = value;
|
|
Packit |
577717 |
xsc2_write_pmnc(value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
cache->k1.id = state->k1.id;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Driver methods, internal and exported.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_write_control(const struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return xscale_write_control(state);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_read_counters(struct perfctr_cpu_state *state,
|
|
Packit |
577717 |
struct perfctr_low_ctrs *ctrs)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return xscale_read_counters(state, ctrs);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
static void perfctr_cpu_isuspend(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return xscale_isuspend(state);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_iresume(const struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return xscale_iresume(state);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Call perfctr_cpu_ireload() just before perfctr_cpu_resume() to
|
|
Packit |
577717 |
bypass internal caching and force a reload if the I-mode PMCs. */
|
|
Packit |
577717 |
void perfctr_cpu_ireload(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
//state->ppc_mmcr[0] |= MMCR0_PMXE;
|
|
Packit |
577717 |
#ifdef CONFIG_SMP
|
|
Packit |
577717 |
clear_isuspend_cpu(state);
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
get_cpu_cache()->k1.id = 0;
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PRE: the counters have been suspended and sampled by perfctr_cpu_suspend() */
|
|
Packit |
577717 |
unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* XXX: XScale has an overflow status register */
|
|
Packit |
577717 |
/* XXX: XSC1 A stepping has an erratum making the
|
|
Packit |
577717 |
overflow status bits unreliable */
|
|
Packit |
577717 |
/* XXX: use different procedures for XSC1 and XSC2 */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* XXX: the rest is still the X86 code */
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs, pmc, pmc_mask;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cstatus = state->cstatus;
|
|
Packit |
577717 |
pmc = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(pmc_mask = 0; pmc < nrctrs; ++pmc) {
|
|
Packit |
577717 |
if ((int)state->pmc[pmc].start >= 0) { /* XXX: ">" ? */
|
|
Packit |
577717 |
/* XXX: "+=" to correct for overshots */
|
|
Packit |
577717 |
state->pmc[pmc].start = state->control.ireset[pmc];
|
|
Packit |
577717 |
pmc_mask |= (1 << pmc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return pmc_mask;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline int check_ireset(const struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int nrctrs, i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
i = state->control.nractrs;
|
|
Packit |
577717 |
nrctrs = i + state->control.nrictrs;
|
|
Packit |
577717 |
for(; i < nrctrs; ++i)
|
|
Packit |
577717 |
if (state->control.ireset[i] >= 0) /* XScale-specific */
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void setup_imode_start_values(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs, i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cstatus = state->cstatus;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i)
|
|
Packit |
577717 |
state->pmc[i].start = state->control.ireset[i];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */
|
|
Packit |
577717 |
static inline void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) { }
|
|
Packit |
577717 |
static inline void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) { }
|
|
Packit |
577717 |
static inline int check_ireset(const struct perfctr_cpu_state *state) { return 0; }
|
|
Packit |
577717 |
static inline void setup_imode_start_values(struct perfctr_cpu_state *state) { }
|
|
Packit |
577717 |
#endif /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int check_control(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return xscale_check_control(state);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int perfctr_cpu_update_control(struct perfctr_cpu_state *state, cpumask_t *cpumask)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
clear_isuspend_cpu(state);
|
|
Packit |
577717 |
state->cstatus = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* disallow i-mode counters if we cannot catch the interrupts */
|
|
Packit |
577717 |
if (!(perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT)
|
|
Packit |
577717 |
&& state->control.nrictrs)
|
|
Packit |
577717 |
return -EPERM;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
err = check_ireset(state);
|
|
Packit |
577717 |
if (err < 0)
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
err = check_control(state); /* may initialise state->cstatus */
|
|
Packit |
577717 |
if (err < 0)
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
state->cstatus |= perfctr_mk_cstatus(state->control.tsc_on,
|
|
Packit |
577717 |
state->control.nractrs,
|
|
Packit |
577717 |
state->control.nrictrs);
|
|
Packit |
577717 |
setup_imode_start_values(state);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_suspend(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i, cstatus, nractrs;
|
|
Packit |
577717 |
struct perfctr_low_ctrs now;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (perfctr_cstatus_has_ictrs(state->cstatus))
|
|
Packit |
577717 |
perfctr_cpu_isuspend(state);
|
|
Packit |
577717 |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit |
577717 |
cstatus = state->cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
state->tsc_sum += now.tsc - state->tsc_start;
|
|
Packit |
577717 |
nractrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nractrs; ++i)
|
|
Packit |
577717 |
state->pmc[i].sum += now.pmc[i] - state->pmc[i].start;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_resume(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (perfctr_cstatus_has_ictrs(state->cstatus))
|
|
Packit |
577717 |
perfctr_cpu_iresume(state);
|
|
Packit |
577717 |
perfctr_cpu_write_control(state);
|
|
Packit |
577717 |
//perfctr_cpu_read_counters(state, &state->start);
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_low_ctrs now;
|
|
Packit |
577717 |
unsigned int i, cstatus, nrctrs;
|
|
Packit |
577717 |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit |
577717 |
cstatus = state->cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
state->tsc_start = now.tsc;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nrctrs; ++i)
|
|
Packit |
577717 |
state->pmc[i].start = now.pmc[i];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/* XXX: if (SMP && start.tsc == now.tsc) ++now.tsc; */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_sample(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i, cstatus, nractrs;
|
|
Packit |
577717 |
struct perfctr_low_ctrs now;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit |
577717 |
cstatus = state->cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus)) {
|
|
Packit |
577717 |
state->tsc_sum += now.tsc - state->tsc_start;
|
|
Packit |
577717 |
state->tsc_start = now.tsc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
nractrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nractrs; ++i) {
|
|
Packit |
577717 |
state->pmc[i].sum += now.pmc[i] - state->pmc[i].start;
|
|
Packit |
577717 |
state->pmc[i].start = now.pmc[i];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Processor detection and initialisation procedures. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int __init xscale_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
static char xsc1_name[] __initdata = "XScale1";
|
|
Packit |
577717 |
static char xsc2_name[] __initdata = "XScale2";
|
|
Packit |
577717 |
u32 id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
//asm("mrc p15, 0, %0, c0, c0, 0" : "=r"(id));
|
|
Packit |
577717 |
id = read_cpuid(CPUID_ID);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* check for Intel/V5TE */
|
|
Packit |
577717 |
if ((id & 0xffff0000) != 0x69050000)
|
|
Packit |
577717 |
return -ENODEV;
|
|
Packit |
577717 |
/* check coregen for XSC1 or XSC2 */
|
|
Packit |
577717 |
switch ((id >> 13) & 0x7) {
|
|
Packit |
577717 |
case 0x1:
|
|
Packit |
577717 |
pmu_type = PMU_XSC1;
|
|
Packit |
577717 |
perfctr_info.cpu_type = PERFCTR_ARM_XSC1;
|
|
Packit |
577717 |
perfctr_cpu_name = xsc1_name;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x2:
|
|
Packit |
577717 |
pmu_type = PMU_XSC2;
|
|
Packit |
577717 |
perfctr_info.cpu_type = PERFCTR_ARM_XSC2;
|
|
Packit |
577717 |
perfctr_cpu_name = xsc2_name;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
return -ENODEV;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
perfctr_info.cpu_features = 0;
|
|
Packit |
577717 |
/* XXX: detect cpu_khz by sampling CCNT over mdelay()? */
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_clear_counters(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct per_cpu_cache *cache;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cache = get_cpu_cache();
|
|
Packit |
577717 |
memset(cache, 0, sizeof *cache);
|
|
Packit |
577717 |
cache->k1.id = -1;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
xscale_clear_counters();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_clear_one(void *ignore)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* PREEMPT note: when called via on_each_cpu(),
|
|
Packit |
577717 |
this is in IRQ context with preemption disabled. */
|
|
Packit |
577717 |
perfctr_cpu_clear_counters();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_reset(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
on_each_cpu(perfctr_cpu_clear_one, NULL, 1);
|
|
Packit |
577717 |
perfctr_cpu_set_ihandler(NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
static int reserve_pmu_irq(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = request_irq(XSCALE_PMU_IRQ, xscale_pmu_interrupt, IRQF_DISABLED,
|
|
Packit |
577717 |
"XScale PMU", NULL);
|
|
Packit |
577717 |
if (ret < 0)
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
/* fiddle pmnc PMU_ENABLE, PMU_CNT64 */
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void release_pmu_irq(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
write_pmnc(read_pmnc() & ~PMU_ENABLE);
|
|
Packit |
577717 |
free_irq(XSCALE_PMU_IRQ, NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static inline int reserve_pmu_irq(void) { return 0; }
|
|
Packit |
577717 |
static inline void release_pmu_irq(void) { }
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void do_init_tests(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INIT_TESTS
|
|
Packit |
577717 |
if (reserve_pmu_irq() >= 0) {
|
|
Packit |
577717 |
perfctr_xscale_init_tests();
|
|
Packit |
577717 |
release_pmu_irq();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int __init perfctr_cpu_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
preempt_disable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
err = xscale_init();
|
|
Packit |
577717 |
if (err)
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
do_init_tests();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_info.cpu_khz = 266; /* XXX: perfctr_cpu_khz(); */
|
|
Packit |
577717 |
perfctr_info.tsc_to_cpu_mult = 1;
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
preempt_enable();
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void __exit perfctr_cpu_exit(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Hardware reservation. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static DEFINE_MUTEX(mutex);
|
|
Packit |
577717 |
static const char *current_service = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
const char *perfctr_cpu_reserve(const char *service)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
const char *ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
mutex_lock(&mutex);
|
|
Packit |
577717 |
ret = current_service;
|
|
Packit |
577717 |
if (ret)
|
|
Packit |
577717 |
goto out_unlock;
|
|
Packit |
577717 |
ret = "unknown driver (oprofile? ixp425_eth?)";
|
|
Packit |
577717 |
if (reserve_pmu_irq() < 0)
|
|
Packit |
577717 |
goto out_unlock;
|
|
Packit |
577717 |
current_service = service;
|
|
Packit |
577717 |
__module_get(THIS_MODULE);
|
|
Packit |
577717 |
perfctr_cpu_reset();
|
|
Packit |
577717 |
ret = NULL;
|
|
Packit |
577717 |
out_unlock:
|
|
Packit |
577717 |
mutex_unlock(&mutex);
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_release(const char *service)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
mutex_lock(&mutex);
|
|
Packit |
577717 |
if (service != current_service) {
|
|
Packit |
577717 |
printk(KERN_ERR "%s: attempt by %s to release while reserved by %s\n",
|
|
Packit |
577717 |
__FUNCTION__, service, current_service);
|
|
Packit |
577717 |
goto out_unlock;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/* power down the counters */
|
|
Packit |
577717 |
perfctr_cpu_reset();
|
|
Packit |
577717 |
current_service = 0;
|
|
Packit |
577717 |
release_pmu_irq();
|
|
Packit |
577717 |
module_put(THIS_MODULE);
|
|
Packit |
577717 |
out_unlock:
|
|
Packit |
577717 |
mutex_unlock(&mutex);
|
|
Packit |
577717 |
}
|