|
Packit Service |
a1973e |
/* $Id: ppc.c,v 1.3.2.22 2009/06/11 12:33:51 mikpe Exp $
|
|
Packit Service |
a1973e |
* PPC32 performance-monitoring counters driver.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* Copyright (C) 2004-2009 Mikael Pettersson
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
#include <linux/version.h>
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
|
Packit Service |
a1973e |
#include <linux/config.h>
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
#define __NO_VERSION__
|
|
Packit Service |
a1973e |
#include <linux/module.h>
|
|
Packit Service |
a1973e |
#include <linux/init.h>
|
|
Packit Service |
a1973e |
#include <linux/sched.h>
|
|
Packit Service |
a1973e |
#include <linux/fs.h>
|
|
Packit Service |
a1973e |
#include <linux/perfctr.h>
|
|
Packit Service |
a1973e |
#include <asm/prom.h>
|
|
Packit Service |
a1973e |
#include <asm/time.h> /* tb_ticks_per_jiffy, get_tbl() */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#include "compat.h"
|
|
Packit Service |
a1973e |
#include "ppc_compat.h"
|
|
Packit Service |
a1973e |
#include "ppc_tests.h"
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Support for lazy evntsel and perfctr SPR updates. */
|
|
Packit Service |
a1973e |
struct per_cpu_cache { /* roughly a subset of perfctr_cpu_state */
|
|
Packit Service |
a1973e |
union {
|
|
Packit Service |
a1973e |
unsigned int id; /* cache owner id */
|
|
Packit Service |
a1973e |
} k1;
|
|
Packit Service |
a1973e |
/* Physically indexed cache of the MMCRs. */
|
|
Packit Service |
a1973e |
unsigned int ppc_mmcr[3];
|
|
Packit Service |
a1973e |
} ____cacheline_aligned;
|
|
Packit Service |
a1973e |
static struct per_cpu_cache per_cpu_cache[NR_CPUS] __cacheline_aligned;
|
|
Packit Service |
a1973e |
#define __get_cpu_cache(cpu) (&per_cpu_cache[cpu])
|
|
Packit Service |
a1973e |
#define get_cpu_cache() (&per_cpu_cache[smp_processor_id()])
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Structure for counter snapshots, as 32-bit values. */
|
|
Packit Service |
a1973e |
struct perfctr_low_ctrs {
|
|
Packit Service |
a1973e |
unsigned int tsc;
|
|
Packit Service |
a1973e |
unsigned int pmc[6];
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
enum pm_type {
|
|
Packit Service |
a1973e |
PM_NONE,
|
|
Packit Service |
a1973e |
PM_604,
|
|
Packit Service |
a1973e |
PM_604e,
|
|
Packit Service |
a1973e |
PM_750, /* XXX: Minor event set diffs between IBM and Moto. */
|
|
Packit Service |
a1973e |
PM_7400,
|
|
Packit Service |
a1973e |
PM_7450,
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
static enum pm_type pm_type;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Bits users shouldn't set in control.ppc.mmcr0:
|
|
Packit Service |
a1973e |
* - PMC1SEL/PMC2SEL because event selectors are in control.evntsel[]
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
#define MMCR0_RESERVED (MMCR0_PMC1SEL | MMCR0_PMC2SEL)
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int new_id(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
static DEFINE_SPINLOCK(lock);
|
|
Packit Service |
a1973e |
static unsigned int counter;
|
|
Packit Service |
a1973e |
int id;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
spin_lock(&lock);
|
|
Packit Service |
a1973e |
id = ++counter;
|
|
Packit Service |
a1973e |
spin_unlock(&lock);
|
|
Packit Service |
a1973e |
return id;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifndef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit Service |
a1973e |
#define perfctr_cstatus_has_ictrs(cstatus) 0
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#if defined(CONFIG_SMP) && defined(CONFIG_PERFCTR_INTERRUPT_SUPPORT)
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void
|
|
Packit Service |
a1973e |
set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
state->k1.isuspend_cpu = cpu;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline int
|
|
Packit Service |
a1973e |
is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return state->k1.isuspend_cpu == cpu;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
state->k1.isuspend_cpu = NR_CPUS;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) { }
|
|
Packit Service |
a1973e |
static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) { return 1; }
|
|
Packit Service |
a1973e |
static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { }
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* The ppc driver internally uses cstatus & (1<<30) to record that
|
|
Packit Service |
a1973e |
a context has an asynchronously changing MMCR0. */
|
|
Packit Service |
a1973e |
static inline unsigned int perfctr_cstatus_set_mmcr0_quirk(unsigned int cstatus)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return cstatus | (1 << 30);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline int perfctr_cstatus_has_mmcr0_quirk(unsigned int cstatus)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return cstatus & (1 << 30);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Driver procedures. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* The PowerPC 604/750/74xx family.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* Common features
|
|
Packit Service |
a1973e |
* ---------------
|
|
Packit Service |
a1973e |
* - Per counter event selection data in subfields of control registers.
|
|
Packit Service |
a1973e |
* MMCR0 contains both global control and PMC1/PMC2 event selectors.
|
|
Packit Service |
a1973e |
* - Overflow interrupt support is present in all processors, but an
|
|
Packit Service |
a1973e |
* erratum makes it difficult to use in 750/7400/7410 processors.
|
|
Packit Service |
a1973e |
* - There is no concept of per-counter qualifiers:
|
|
Packit Service |
a1973e |
* - User-mode/supervisor-mode restrictions are global.
|
|
Packit Service |
a1973e |
* - Two groups of counters, PMC1 and PMC2-PMC<highest>. Each group
|
|
Packit Service |
a1973e |
* has a single overflow interrupt/event enable/disable flag.
|
|
Packit Service |
a1973e |
* - The instructions used to read (mfspr) and write (mtspr) the control
|
|
Packit Service |
a1973e |
* and counter registers (SPRs) only support hardcoded register numbers.
|
|
Packit Service |
a1973e |
* There is no support for accessing an SPR via a runtime value.
|
|
Packit Service |
a1973e |
* - Each counter supports its own unique set of events. However, events
|
|
Packit Service |
a1973e |
* 0-1 are common for PMC1-PMC4, and events 2-4 are common for PMC1-PMC4.
|
|
Packit Service |
a1973e |
* - There is no separate high-resolution core clock counter.
|
|
Packit Service |
a1973e |
* The time-base counter is available, but it typically runs an order of
|
|
Packit Service |
a1973e |
* magnitude slower than the core clock.
|
|
Packit Service |
a1973e |
* Any performance counter can be programmed to count core clocks, but
|
|
Packit Service |
a1973e |
* doing this (a) reserves one PMC, and (b) needs indirect accesses
|
|
Packit Service |
a1973e |
* since the SPR number in general isn't known at compile-time.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 604
|
|
Packit Service |
a1973e |
* ---
|
|
Packit Service |
a1973e |
* 604 has MMCR0, PMC1, PMC2, SIA, and SDA.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* MMCR0[THRESHOLD] is not automatically multiplied.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* On the 604, software must always reset MMCR0[ENINT] after
|
|
Packit Service |
a1973e |
* taking a PMI. This is not the case for the 604e.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 604e
|
|
Packit Service |
a1973e |
* ----
|
|
Packit Service |
a1973e |
* 604e adds MMCR1, PMC3, and PMC4.
|
|
Packit Service |
a1973e |
* Bus-to-core multiplier is available via HID1[PLL_CFG].
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* MMCR0[THRESHOLD] is automatically multiplied by 4.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* When the 604e vectors to the PMI handler, it automatically
|
|
Packit Service |
a1973e |
* clears any pending PMIs. Unlike the 604, the 604e does not
|
|
Packit Service |
a1973e |
* require MMCR0[ENINT] to be cleared (and possibly reset)
|
|
Packit Service |
a1973e |
* before external interrupts can be re-enabled.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 750
|
|
Packit Service |
a1973e |
* ---
|
|
Packit Service |
a1973e |
* 750 adds user-readable MMCRn/PMCn/SIA registers, and removes SDA.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* MMCR0[THRESHOLD] is not automatically multiplied.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* Motorola MPC750UM.pdf, page C-78, states: "The performance monitor
|
|
Packit Service |
a1973e |
* of the MPC755 functions the same as that of the MPC750, (...), except
|
|
Packit Service |
a1973e |
* that for both the MPC750 and MPC755, no combination of the thermal
|
|
Packit Service |
a1973e |
* assist unit, the decrementer register, and the performance monitor
|
|
Packit Service |
a1973e |
* can be used at any one time. If exceptions for any two of these
|
|
Packit Service |
a1973e |
* functional blocks are enabled together, multiple exceptions caused
|
|
Packit Service |
a1973e |
* by any of these three blocks cause unpredictable results."
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* IBM 750CXe_Err_DD2X.pdf, Erratum #13, states that a PMI which
|
|
Packit Service |
a1973e |
* occurs immediately after a delayed decrementer exception can
|
|
Packit Service |
a1973e |
* corrupt SRR0, causing the processor to hang. It also states that
|
|
Packit Service |
a1973e |
* PMIs via TB bit transitions can be used to simulate the decrementer.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 750FX adds dual-PLL support and programmable core frequency switching.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 750FX DD2.3 fixed the DEC/PMI SRR0 corruption erratum.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 74xx
|
|
Packit Service |
a1973e |
* ----
|
|
Packit Service |
a1973e |
* 7400 adds MMCR2 and BAMR.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* MMCR0[THRESHOLD] is multiplied by 2 or 32, as specified
|
|
Packit Service |
a1973e |
* by MMCR2[THRESHMULT].
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 74xx changes the semantics of several MMCR0 control bits,
|
|
Packit Service |
a1973e |
* compared to 604/750.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* PPC7410 Erratum No. 10: Like the MPC750 TAU/DECR/PMI erratum.
|
|
Packit Service |
a1973e |
* Erratum No. 14 marks TAU as unsupported in 7410, but this leaves
|
|
Packit Service |
a1973e |
* perfmon and decrementer interrupts as being mutually exclusive.
|
|
Packit Service |
a1973e |
* Affects PPC7410 1.0-1.2 (PVR 0x800C1100-0x800C1102). 1.3 and up
|
|
Packit Service |
a1973e |
* (PVR 0x800C1103 up) are Ok.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 7450 adds PMC5 and PMC6.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* 7455/7445 V3.3 (PVR 80010303) and later use the 7457 PLL table,
|
|
Packit Service |
a1973e |
* earlier revisions use the 7450 PLL table
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline unsigned int read_pmc(unsigned int pmc)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
switch (pmc) {
|
|
Packit Service |
a1973e |
default: /* impossible, but silences gcc warning */
|
|
Packit Service |
a1973e |
case 0:
|
|
Packit Service |
a1973e |
return mfspr(SPRN_PMC1);
|
|
Packit Service |
a1973e |
case 1:
|
|
Packit Service |
a1973e |
return mfspr(SPRN_PMC2);
|
|
Packit Service |
a1973e |
case 2:
|
|
Packit Service |
a1973e |
return mfspr(SPRN_PMC3);
|
|
Packit Service |
a1973e |
case 3:
|
|
Packit Service |
a1973e |
return mfspr(SPRN_PMC4);
|
|
Packit Service |
a1973e |
case 4:
|
|
Packit Service |
a1973e |
return mfspr(SPRN_PMC5);
|
|
Packit Service |
a1973e |
case 5:
|
|
Packit Service |
a1973e |
return mfspr(SPRN_PMC6);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void ppc_read_counters(struct perfctr_cpu_state *state,
|
|
Packit Service |
a1973e |
struct perfctr_low_ctrs *ctrs)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int cstatus, nrctrs, i;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cstatus = state->cstatus;
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit Service |
a1973e |
ctrs->tsc = get_tbl();
|
|
Packit Service |
a1973e |
nrctrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit Service |
a1973e |
for(i = 0; i < nrctrs; ++i) {
|
|
Packit Service |
a1973e |
unsigned int pmc = state->pmc[i].map;
|
|
Packit Service |
a1973e |
ctrs->pmc[i] = read_pmc(pmc);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int pmc_max_event(unsigned int pmc)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
switch (pmc) {
|
|
Packit Service |
a1973e |
default: /* impossible, but silences gcc warning */
|
|
Packit Service |
a1973e |
case 0:
|
|
Packit Service |
a1973e |
return 127;
|
|
Packit Service |
a1973e |
case 1:
|
|
Packit Service |
a1973e |
return 63;
|
|
Packit Service |
a1973e |
case 2:
|
|
Packit Service |
a1973e |
return 31;
|
|
Packit Service |
a1973e |
case 3:
|
|
Packit Service |
a1973e |
return 31;
|
|
Packit Service |
a1973e |
case 4:
|
|
Packit Service |
a1973e |
return 31;
|
|
Packit Service |
a1973e |
case 5:
|
|
Packit Service |
a1973e |
return 63;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int get_nr_pmcs(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
switch (pm_type) {
|
|
Packit Service |
a1973e |
case PM_7450:
|
|
Packit Service |
a1973e |
return 6;
|
|
Packit Service |
a1973e |
case PM_7400:
|
|
Packit Service |
a1973e |
case PM_750:
|
|
Packit Service |
a1973e |
case PM_604e:
|
|
Packit Service |
a1973e |
return 4;
|
|
Packit Service |
a1973e |
case PM_604:
|
|
Packit Service |
a1973e |
return 2;
|
|
Packit Service |
a1973e |
default: /* PM_NONE, but silences gcc warning */
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int ppc_check_control(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int i, nractrs, nrctrs, pmc_mask, pmi_mask, pmc;
|
|
Packit Service |
a1973e |
unsigned int nr_pmcs, evntsel[6];
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
nr_pmcs = get_nr_pmcs();
|
|
Packit Service |
a1973e |
nractrs = state->control.nractrs;
|
|
Packit Service |
a1973e |
nrctrs = nractrs + state->control.nrictrs;
|
|
Packit Service |
a1973e |
if (nrctrs < nractrs || nrctrs > nr_pmcs)
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
pmc_mask = 0;
|
|
Packit Service |
a1973e |
pmi_mask = 0;
|
|
Packit Service |
a1973e |
memset(evntsel, 0, sizeof evntsel);
|
|
Packit Service |
a1973e |
for(i = 0; i < nrctrs; ++i) {
|
|
Packit Service |
a1973e |
pmc = state->control.pmc_map[i];
|
|
Packit Service |
a1973e |
state->pmc[i].map = pmc;
|
|
Packit Service |
a1973e |
if (pmc >= nr_pmcs || (pmc_mask & (1<
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
pmc_mask |= (1<
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (i >= nractrs)
|
|
Packit Service |
a1973e |
pmi_mask |= (1<
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
evntsel[pmc] = state->control.evntsel[i];
|
|
Packit Service |
a1973e |
if (evntsel[pmc] > pmc_max_event(pmc))
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* XXX: temporary limitation */
|
|
Packit Service |
a1973e |
if ((pmi_mask & ~1) && (pmi_mask & ~1) != (pmc_mask & ~1))
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
switch (pm_type) {
|
|
Packit Service |
a1973e |
case PM_7450:
|
|
Packit Service |
a1973e |
case PM_7400:
|
|
Packit Service |
a1973e |
if (state->control.ppc.mmcr2 & MMCR2_RESERVED)
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
state->ppc_mmcr[2] = state->control.ppc.mmcr2;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
default:
|
|
Packit Service |
a1973e |
if (state->control.ppc.mmcr2)
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
state->ppc_mmcr[2] = 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* We do not yet handle TBEE as the only exception cause,
|
|
Packit Service |
a1973e |
so PMXE requires at least one interrupt-mode counter. */
|
|
Packit Service |
a1973e |
if ((state->control.ppc.mmcr0 & MMCR0_PMXE) && !state->control.nrictrs)
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
if (state->control.ppc.mmcr0 & MMCR0_RESERVED)
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
state->ppc_mmcr[0] = (state->control.ppc.mmcr0
|
|
Packit Service |
a1973e |
| (evntsel[0] << (31-25))
|
|
Packit Service |
a1973e |
| (evntsel[1] << (31-31)));
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
state->ppc_mmcr[1] = (( evntsel[2] << (31-4))
|
|
Packit Service |
a1973e |
| (evntsel[3] << (31-9))
|
|
Packit Service |
a1973e |
| (evntsel[4] << (31-14))
|
|
Packit Service |
a1973e |
| (evntsel[5] << (31-20)));
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
state->k1.id = new_id();
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* MMCR0[FC] and MMCR0[TRIGGER] may change on 74xx if FCECE or
|
|
Packit Service |
a1973e |
* TRIGGER is set. At suspends we must read MMCR0 back into
|
|
Packit Service |
a1973e |
* the state and the cache and then freeze the counters, and
|
|
Packit Service |
a1973e |
* at resumes we must unfreeze the counters and reload MMCR0.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
switch (pm_type) {
|
|
Packit Service |
a1973e |
case PM_7450:
|
|
Packit Service |
a1973e |
case PM_7400:
|
|
Packit Service |
a1973e |
if (state->ppc_mmcr[0] & (MMCR0_FCECE | MMCR0_TRIGGER))
|
|
Packit Service |
a1973e |
state->cstatus = perfctr_cstatus_set_mmcr0_quirk(state->cstatus);
|
|
Packit Service |
a1973e |
default:
|
|
Packit Service |
a1973e |
;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* The MMCR0 handling for FCECE and TRIGGER is also needed for PMXE. */
|
|
Packit Service |
a1973e |
if (state->ppc_mmcr[0] & (MMCR0_PMXE | MMCR0_FCECE | MMCR0_TRIGGER))
|
|
Packit Service |
a1973e |
state->cstatus = perfctr_cstatus_set_mmcr0_quirk(state->cstatus);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit Service |
a1973e |
/* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */
|
|
Packit Service |
a1973e |
/* PRE: counters frozen */
|
|
Packit Service |
a1973e |
static void ppc_isuspend(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct per_cpu_cache *cache;
|
|
Packit Service |
a1973e |
unsigned int cstatus, nrctrs, i;
|
|
Packit Service |
a1973e |
int cpu;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cpu = smp_processor_id();
|
|
Packit Service |
a1973e |
set_isuspend_cpu(state, cpu); /* early to limit cpu's live range */
|
|
Packit Service |
a1973e |
cache = __get_cpu_cache(cpu);
|
|
Packit Service |
a1973e |
cstatus = state->cstatus;
|
|
Packit Service |
a1973e |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit Service |
a1973e |
for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) {
|
|
Packit Service |
a1973e |
unsigned int pmc = state->pmc[i].map;
|
|
Packit Service |
a1973e |
unsigned int now = read_pmc(pmc);
|
|
Packit Service |
a1973e |
state->pmc[i].sum += now - state->pmc[i].start;
|
|
Packit Service |
a1973e |
state->pmc[i].start = now;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
/* cache->k1.id is still == state->k1.id */
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void ppc_iresume(const struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct per_cpu_cache *cache;
|
|
Packit Service |
a1973e |
unsigned int cstatus, nrctrs, i;
|
|
Packit Service |
a1973e |
int cpu;
|
|
Packit Service |
a1973e |
unsigned int pmc[6];
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cpu = smp_processor_id();
|
|
Packit Service |
a1973e |
cache = __get_cpu_cache(cpu);
|
|
Packit Service |
a1973e |
if (cache->k1.id == state->k1.id) {
|
|
Packit Service |
a1973e |
/* Clearing cache->k1.id to force write_control()
|
|
Packit Service |
a1973e |
to unfreeze MMCR0 would be done here, but it
|
|
Packit Service |
a1973e |
is subsumed by resume()'s MMCR0 reload logic. */
|
|
Packit Service |
a1973e |
if (is_isuspend_cpu(state, cpu))
|
|
Packit Service |
a1973e |
return; /* skip reload of PMCs */
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* The CPU state wasn't ours.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* The counters must be frozen before being reinitialised,
|
|
Packit Service |
a1973e |
* to prevent unexpected increments and missed overflows.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* All unused counters must be reset to a non-overflow state.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
if (!(cache->ppc_mmcr[0] & MMCR0_FC)) {
|
|
Packit Service |
a1973e |
cache->ppc_mmcr[0] |= MMCR0_FC;
|
|
Packit Service |
a1973e |
mtspr(SPRN_MMCR0, cache->ppc_mmcr[0]);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
memset(&pmc[0], 0, sizeof pmc);
|
|
Packit Service |
a1973e |
cstatus = state->cstatus;
|
|
Packit Service |
a1973e |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit Service |
a1973e |
for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i)
|
|
Packit Service |
a1973e |
pmc[state->pmc[i].map] = state->pmc[i].start;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
switch (pm_type) {
|
|
Packit Service |
a1973e |
case PM_7450:
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC6, pmc[6-1]);
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC5, pmc[5-1]);
|
|
Packit Service |
a1973e |
case PM_7400:
|
|
Packit Service |
a1973e |
case PM_750:
|
|
Packit Service |
a1973e |
case PM_604e:
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC4, pmc[4-1]);
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC3, pmc[3-1]);
|
|
Packit Service |
a1973e |
case PM_604:
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC2, pmc[2-1]);
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC1, pmc[1-1]);
|
|
Packit Service |
a1973e |
case PM_NONE:
|
|
Packit Service |
a1973e |
;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
/* cache->k1.id remains != state->k1.id */
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void ppc_write_control(const struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct per_cpu_cache *cache;
|
|
Packit Service |
a1973e |
unsigned int value;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cache = get_cpu_cache();
|
|
Packit Service |
a1973e |
if (cache->k1.id == state->k1.id)
|
|
Packit Service |
a1973e |
return;
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* Order matters here: update threshmult and event
|
|
Packit Service |
a1973e |
* selectors before updating global control, which
|
|
Packit Service |
a1973e |
* potentially enables PMIs.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* Since mtspr doesn't accept a runtime value for the
|
|
Packit Service |
a1973e |
* SPR number, unroll the loop so each mtspr targets
|
|
Packit Service |
a1973e |
* a constant SPR.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* For processors without MMCR2, we ensure that the
|
|
Packit Service |
a1973e |
* cache and the state indicate the same value for it,
|
|
Packit Service |
a1973e |
* preventing any actual mtspr to it. Ditto for MMCR1.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
value = state->ppc_mmcr[2];
|
|
Packit Service |
a1973e |
if (value != cache->ppc_mmcr[2]) {
|
|
Packit Service |
a1973e |
cache->ppc_mmcr[2] = value;
|
|
Packit Service |
a1973e |
mtspr(SPRN_MMCR2, value);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
value = state->ppc_mmcr[1];
|
|
Packit Service |
a1973e |
if (value != cache->ppc_mmcr[1]) {
|
|
Packit Service |
a1973e |
cache->ppc_mmcr[1] = value;
|
|
Packit Service |
a1973e |
mtspr(SPRN_MMCR1, value);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
value = state->ppc_mmcr[0];
|
|
Packit Service |
a1973e |
if (value != cache->ppc_mmcr[0]) {
|
|
Packit Service |
a1973e |
cache->ppc_mmcr[0] = value;
|
|
Packit Service |
a1973e |
mtspr(SPRN_MMCR0, value);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
cache->k1.id = state->k1.id;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void ppc_clear_counters(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
switch (pm_type) {
|
|
Packit Service |
a1973e |
case PM_7450:
|
|
Packit Service |
a1973e |
case PM_7400:
|
|
Packit Service |
a1973e |
mtspr(SPRN_MMCR2, 0);
|
|
Packit Service |
a1973e |
mtspr(SPRN_BAMR, 0);
|
|
Packit Service |
a1973e |
case PM_750:
|
|
Packit Service |
a1973e |
case PM_604e:
|
|
Packit Service |
a1973e |
mtspr(SPRN_MMCR1, 0);
|
|
Packit Service |
a1973e |
case PM_604:
|
|
Packit Service |
a1973e |
mtspr(SPRN_MMCR0, 0);
|
|
Packit Service |
a1973e |
case PM_NONE:
|
|
Packit Service |
a1973e |
;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
switch (pm_type) {
|
|
Packit Service |
a1973e |
case PM_7450:
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC6, 0);
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC5, 0);
|
|
Packit Service |
a1973e |
case PM_7400:
|
|
Packit Service |
a1973e |
case PM_750:
|
|
Packit Service |
a1973e |
case PM_604e:
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC4, 0);
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC3, 0);
|
|
Packit Service |
a1973e |
case PM_604:
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC2, 0);
|
|
Packit Service |
a1973e |
mtspr(SPRN_PMC1, 0);
|
|
Packit Service |
a1973e |
case PM_NONE:
|
|
Packit Service |
a1973e |
;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* Driver methods, internal and exported.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void perfctr_cpu_write_control(const struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return ppc_write_control(state);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void perfctr_cpu_read_counters(struct perfctr_cpu_state *state,
|
|
Packit Service |
a1973e |
struct perfctr_low_ctrs *ctrs)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return ppc_read_counters(state, ctrs);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit Service |
a1973e |
static void perfctr_cpu_isuspend(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return ppc_isuspend(state);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void perfctr_cpu_iresume(const struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return ppc_iresume(state);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Call perfctr_cpu_ireload() just before perfctr_cpu_resume() to
|
|
Packit Service |
a1973e |
bypass internal caching and force a reload if the I-mode PMCs. */
|
|
Packit Service |
a1973e |
void perfctr_cpu_ireload(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
state->ppc_mmcr[0] |= MMCR0_PMXE;
|
|
Packit Service |
a1973e |
#ifdef CONFIG_SMP
|
|
Packit Service |
a1973e |
clear_isuspend_cpu(state);
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
get_cpu_cache()->k1.id = 0;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* PRE: the counters have been suspended and sampled by perfctr_cpu_suspend() */
|
|
Packit Service |
a1973e |
unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int cstatus, nrctrs, pmc, pmc_mask;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cstatus = state->cstatus;
|
|
Packit Service |
a1973e |
pmc = perfctr_cstatus_nractrs(cstatus);
|
|
Packit Service |
a1973e |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
for(pmc_mask = 0; pmc < nrctrs; ++pmc) {
|
|
Packit Service |
a1973e |
if ((int)state->pmc[pmc].start < 0) { /* PPC-specific */
|
|
Packit Service |
a1973e |
/* XXX: "+=" to correct for overshots */
|
|
Packit Service |
a1973e |
state->pmc[pmc].start = state->control.ireset[pmc];
|
|
Packit Service |
a1973e |
pmc_mask |= (1 << pmc);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
if (!pmc_mask && (state->ppc_mmcr[0] & MMCR0_TBEE))
|
|
Packit Service |
a1973e |
pmc_mask = (1<<8); /* fake TB bit flip indicator */
|
|
Packit Service |
a1973e |
return pmc_mask;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline int check_ireset(const struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int nrctrs, i;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
i = state->control.nractrs;
|
|
Packit Service |
a1973e |
nrctrs = i + state->control.nrictrs;
|
|
Packit Service |
a1973e |
for(; i < nrctrs; ++i)
|
|
Packit Service |
a1973e |
if (state->control.ireset[i] < 0) /* PPC-specific */
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void setup_imode_start_values(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int cstatus, nrctrs, i;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cstatus = state->cstatus;
|
|
Packit Service |
a1973e |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit Service |
a1973e |
for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i)
|
|
Packit Service |
a1973e |
state->pmc[i].start = state->control.ireset[i];
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#else /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */
|
|
Packit Service |
a1973e |
static inline void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) { }
|
|
Packit Service |
a1973e |
static inline void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) { }
|
|
Packit Service |
a1973e |
static inline int check_ireset(const struct perfctr_cpu_state *state) { return 0; }
|
|
Packit Service |
a1973e |
static inline void setup_imode_start_values(struct perfctr_cpu_state *state) { }
|
|
Packit Service |
a1973e |
#endif /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int check_control(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return ppc_check_control(state);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
int perfctr_cpu_update_control(struct perfctr_cpu_state *state, cpumask_t *cpumask)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
int err;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
clear_isuspend_cpu(state);
|
|
Packit Service |
a1973e |
state->cstatus = 0;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* disallow i-mode counters if we cannot catch the interrupts */
|
|
Packit Service |
a1973e |
if (!(perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT)
|
|
Packit Service |
a1973e |
&& state->control.nrictrs)
|
|
Packit Service |
a1973e |
return -EPERM;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
err = check_ireset(state);
|
|
Packit Service |
a1973e |
if (err < 0)
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
err = check_control(state); /* may initialise state->cstatus */
|
|
Packit Service |
a1973e |
if (err < 0)
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
state->cstatus |= perfctr_mk_cstatus(state->control.tsc_on,
|
|
Packit Service |
a1973e |
state->control.nractrs,
|
|
Packit Service |
a1973e |
state->control.nrictrs);
|
|
Packit Service |
a1973e |
setup_imode_start_values(state);
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
void perfctr_cpu_suspend(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int i, cstatus, nractrs;
|
|
Packit Service |
a1973e |
struct perfctr_low_ctrs now;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_mmcr0_quirk(state->cstatus)) {
|
|
Packit Service |
a1973e |
unsigned int mmcr0 = mfspr(SPRN_MMCR0);
|
|
Packit Service |
a1973e |
mtspr(SPRN_MMCR0, mmcr0 | MMCR0_FC);
|
|
Packit Service |
a1973e |
get_cpu_cache()->ppc_mmcr[0] = mmcr0 | MMCR0_FC;
|
|
Packit Service |
a1973e |
state->ppc_mmcr[0] = mmcr0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_ictrs(state->cstatus))
|
|
Packit Service |
a1973e |
perfctr_cpu_isuspend(state);
|
|
Packit Service |
a1973e |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit Service |
a1973e |
cstatus = state->cstatus;
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit Service |
a1973e |
state->tsc_sum += now.tsc - state->tsc_start;
|
|
Packit Service |
a1973e |
nractrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit Service |
a1973e |
for(i = 0; i < nractrs; ++i)
|
|
Packit Service |
a1973e |
state->pmc[i].sum += now.pmc[i] - state->pmc[i].start;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
void perfctr_cpu_resume(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_ictrs(state->cstatus))
|
|
Packit Service |
a1973e |
perfctr_cpu_iresume(state);
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_mmcr0_quirk(state->cstatus))
|
|
Packit Service |
a1973e |
get_cpu_cache()->k1.id = 0; /* force reload of MMCR0 */
|
|
Packit Service |
a1973e |
perfctr_cpu_write_control(state);
|
|
Packit Service |
a1973e |
//perfctr_cpu_read_counters(state, &state->start);
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct perfctr_low_ctrs now;
|
|
Packit Service |
a1973e |
unsigned int i, cstatus, nrctrs;
|
|
Packit Service |
a1973e |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit Service |
a1973e |
cstatus = state->cstatus;
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit Service |
a1973e |
state->tsc_start = now.tsc;
|
|
Packit Service |
a1973e |
nrctrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit Service |
a1973e |
for(i = 0; i < nrctrs; ++i)
|
|
Packit Service |
a1973e |
state->pmc[i].start = now.pmc[i];
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
/* XXX: if (SMP && start.tsc == now.tsc) ++now.tsc; */
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
void perfctr_cpu_sample(struct perfctr_cpu_state *state)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int i, cstatus, nractrs;
|
|
Packit Service |
a1973e |
struct perfctr_low_ctrs now;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit Service |
a1973e |
cstatus = state->cstatus;
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_tsc(cstatus)) {
|
|
Packit Service |
a1973e |
state->tsc_sum += now.tsc - state->tsc_start;
|
|
Packit Service |
a1973e |
state->tsc_start = now.tsc;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
nractrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit Service |
a1973e |
for(i = 0; i < nractrs; ++i) {
|
|
Packit Service |
a1973e |
state->pmc[i].sum += now.pmc[i] - state->pmc[i].start;
|
|
Packit Service |
a1973e |
state->pmc[i].start = now.pmc[i];
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void perfctr_cpu_clear_counters(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct per_cpu_cache *cache;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cache = get_cpu_cache();
|
|
Packit Service |
a1973e |
memset(cache, 0, sizeof *cache);
|
|
Packit Service |
a1973e |
cache->k1.id = -1;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
ppc_clear_counters();
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Processor detection and initialisation procedures. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Derive CPU core frequency from TB frequency and PLL_CFG. */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
enum pll_type {
|
|
Packit Service |
a1973e |
PLL_NONE, /* for e.g. 604 which has no HID1[PLL_CFG] */
|
|
Packit Service |
a1973e |
PLL_604e,
|
|
Packit Service |
a1973e |
PLL_750,
|
|
Packit Service |
a1973e |
PLL_750FX,
|
|
Packit Service |
a1973e |
PLL_7400,
|
|
Packit Service |
a1973e |
PLL_7450,
|
|
Packit Service |
a1973e |
PLL_7457,
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* These are the known bus-to-core ratios, indexed by PLL_CFG.
|
|
Packit Service |
a1973e |
Multiplied by 2 since half-multiplier steps are present. */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned char cfg_ratio_604e[16] __initdata = { // *2
|
|
Packit Service |
a1973e |
2, 2, 14, 2, 4, 13, 5, 9,
|
|
Packit Service |
a1973e |
6, 11, 8, 10, 3, 12, 7, 0
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned char cfg_ratio_750[16] __initdata = { // *2
|
|
Packit Service |
a1973e |
5, 15, 14, 2, 4, 13, 20, 9, // 0b0110 is 18 if L1_TSTCLK=0, but that is abnormal
|
|
Packit Service |
a1973e |
6, 11, 8, 10, 16, 12, 7, 0
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned char cfg_ratio_750FX[32] __initdata = { // *2
|
|
Packit Service |
a1973e |
0, 0, 2, 2, 4, 5, 6, 7,
|
|
Packit Service |
a1973e |
8, 9, 10, 11, 12, 13, 14, 15,
|
|
Packit Service |
a1973e |
16, 17, 18, 19, 20, 22, 24, 26,
|
|
Packit Service |
a1973e |
28, 30, 32, 34, 36, 38, 40, 0
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned char cfg_ratio_7400[16] __initdata = { // *2
|
|
Packit Service |
a1973e |
18, 15, 14, 2, 4, 13, 5, 9,
|
|
Packit Service |
a1973e |
6, 11, 8, 10, 16, 12, 7, 0
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned char cfg_ratio_7450[32] __initdata = { // *2
|
|
Packit Service |
a1973e |
1, 0, 15, 30, 14, 0, 2, 0,
|
|
Packit Service |
a1973e |
4, 0, 13, 26, 5, 0, 9, 18,
|
|
Packit Service |
a1973e |
6, 0, 11, 22, 8, 20, 10, 24,
|
|
Packit Service |
a1973e |
16, 28, 12, 32, 7, 0, 0, 0
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned char cfg_ratio_7457[32] __initdata = { // *2
|
|
Packit Service |
a1973e |
23, 34, 15, 30, 14, 36, 2, 40,
|
|
Packit Service |
a1973e |
4, 42, 13, 26, 17, 48, 19, 18,
|
|
Packit Service |
a1973e |
6, 21, 11, 22, 8, 20, 10, 24,
|
|
Packit Service |
a1973e |
16, 28, 12, 32, 27, 56, 0, 25
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int __init pll_tb_to_core(enum pll_type pll_type)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned char *cfg_ratio;
|
|
Packit Service |
a1973e |
unsigned int shift = 28, mask = 0xF, hid1, pll_cfg, ratio;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
switch (pll_type) {
|
|
Packit Service |
a1973e |
case PLL_604e:
|
|
Packit Service |
a1973e |
cfg_ratio = cfg_ratio_604e;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case PLL_750:
|
|
Packit Service |
a1973e |
cfg_ratio = cfg_ratio_750;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case PLL_750FX:
|
|
Packit Service |
a1973e |
cfg_ratio = cfg_ratio_750FX;
|
|
Packit Service |
a1973e |
hid1 = mfspr(SPRN_HID1);
|
|
Packit Service |
a1973e |
switch ((hid1 >> 16) & 0x3) { /* HID1[PI0,PS] */
|
|
Packit Service |
a1973e |
case 0: /* PLL0 with external config */
|
|
Packit Service |
a1973e |
shift = 31-4; /* access HID1[PCE] */
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 2: /* PLL0 with internal config */
|
|
Packit Service |
a1973e |
shift = 31-20; /* access HID1[PC0] */
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 1: case 3: /* PLL1 */
|
|
Packit Service |
a1973e |
shift = 31-28; /* access HID1[PC1] */
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
mask = 0x1F;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case PLL_7400:
|
|
Packit Service |
a1973e |
cfg_ratio = cfg_ratio_7400;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case PLL_7450:
|
|
Packit Service |
a1973e |
cfg_ratio = cfg_ratio_7450;
|
|
Packit Service |
a1973e |
shift = 12;
|
|
Packit Service |
a1973e |
mask = 0x1F;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case PLL_7457:
|
|
Packit Service |
a1973e |
cfg_ratio = cfg_ratio_7457;
|
|
Packit Service |
a1973e |
shift = 12;
|
|
Packit Service |
a1973e |
mask = 0x1F;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
default:
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
hid1 = mfspr(SPRN_HID1);
|
|
Packit Service |
a1973e |
pll_cfg = (hid1 >> shift) & mask;
|
|
Packit Service |
a1973e |
ratio = cfg_ratio[pll_cfg];
|
|
Packit Service |
a1973e |
if (!ratio)
|
|
Packit Service |
a1973e |
printk(KERN_WARNING "perfctr: unknown PLL_CFG 0x%x\n", pll_cfg);
|
|
Packit Service |
a1973e |
return (4/2) * ratio;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Extract core and timebase frequencies from Open Firmware. */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PPC_OF
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
|
|
Packit Service |
a1973e |
static inline struct device_node *perfctr_of_find_node_by_type(struct device_node *from, const char *type)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return of_find_node_by_type(from, type);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void perfctr_of_node_put(struct device_node *node)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
of_node_put(node);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
#define of_get_property(a, b, c) get_property((a), (b), (c))
|
|
Packit Service |
a1973e |
static inline struct device_node *perfctr_of_find_node_by_type(struct device_node *from, const char *type)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return find_type_devices(type);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void perfctr_of_node_put(struct device_node *node) { }
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int __init of_core_khz(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct device_node *cpu;
|
|
Packit Service |
a1973e |
unsigned int *fp, core;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cpu = perfctr_of_find_node_by_type(NULL, "cpu");
|
|
Packit Service |
a1973e |
if (!cpu)
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
fp = (unsigned int*)of_get_property(cpu, "clock-frequency", NULL);
|
|
Packit Service |
a1973e |
core = 0;
|
|
Packit Service |
a1973e |
if (fp)
|
|
Packit Service |
a1973e |
core = *fp;
|
|
Packit Service |
a1973e |
perfctr_of_node_put(cpu);
|
|
Packit Service |
a1973e |
return core / 1000;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int __init of_bus_khz(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct device_node *cpu;
|
|
Packit Service |
a1973e |
unsigned int *fp, bus;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cpu = perfctr_of_find_node_by_type(NULL, "cpu");
|
|
Packit Service |
a1973e |
if (!cpu)
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
fp = (unsigned int*)of_get_property(cpu, "bus-frequency", NULL);
|
|
Packit Service |
a1973e |
bus = 0;
|
|
Packit Service |
a1973e |
if (!fp || !(bus = *fp)) {
|
|
Packit Service |
a1973e |
fp = (unsigned int*)of_get_property(cpu, "config-bus-frequency", NULL);
|
|
Packit Service |
a1973e |
if (fp)
|
|
Packit Service |
a1973e |
bus = *fp;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
perfctr_of_node_put(cpu);
|
|
Packit Service |
a1973e |
return bus / 1000;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int __init of_bus_to_core_x2(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct device_node *cpu;
|
|
Packit Service |
a1973e |
unsigned int *fp, ratio;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
cpu = perfctr_of_find_node_by_type(NULL, "cpu");
|
|
Packit Service |
a1973e |
if (!cpu)
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
fp = (unsigned int*)of_get_property(cpu, "processor-to-bus-ratio*2", NULL);
|
|
Packit Service |
a1973e |
ratio = 0;
|
|
Packit Service |
a1973e |
if (fp)
|
|
Packit Service |
a1973e |
ratio = *fp;
|
|
Packit Service |
a1973e |
perfctr_of_node_put(cpu);
|
|
Packit Service |
a1973e |
return ratio;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
static inline unsigned int of_core_khz(void) { return 0; }
|
|
Packit Service |
a1973e |
static inline unsigned int of_bus_khz(void) { return 0; }
|
|
Packit Service |
a1973e |
static inline unsigned int of_bus_to_core_x2(void) { return 0; }
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int __init detect_tb_khz(unsigned int bus_khz, unsigned int tb_to_bus)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int tb_khz, bus_tb_khz, diff;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
tb_khz = tb_ticks_per_jiffy * (HZ/10) / (1000/10);
|
|
Packit Service |
a1973e |
if (bus_khz && tb_to_bus) {
|
|
Packit Service |
a1973e |
bus_tb_khz = bus_khz / tb_to_bus;
|
|
Packit Service |
a1973e |
if (bus_tb_khz > tb_khz)
|
|
Packit Service |
a1973e |
diff = bus_tb_khz - tb_khz;
|
|
Packit Service |
a1973e |
else
|
|
Packit Service |
a1973e |
diff = tb_khz - bus_tb_khz;
|
|
Packit Service |
a1973e |
if (diff >= bus_tb_khz/20) {
|
|
Packit Service |
a1973e |
printk(KERN_WARNING "perfctr: timebase frequency %u kHz seems"
|
|
Packit Service |
a1973e |
" out of range, using %u kHz (bus/%u) instead\n",
|
|
Packit Service |
a1973e |
tb_khz, bus_tb_khz, tb_to_bus);
|
|
Packit Service |
a1973e |
return bus_tb_khz;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
return tb_khz;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int __init detect_tb_to_core(enum pll_type pll_type, unsigned int tb_to_bus)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int tb_to_core;
|
|
Packit Service |
a1973e |
unsigned int bus_to_core_x2;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
tb_to_core = pll_tb_to_core(pll_type);
|
|
Packit Service |
a1973e |
if (tb_to_core)
|
|
Packit Service |
a1973e |
return tb_to_core;
|
|
Packit Service |
a1973e |
if (tb_to_bus) {
|
|
Packit Service |
a1973e |
bus_to_core_x2 = of_bus_to_core_x2();
|
|
Packit Service |
a1973e |
if (bus_to_core_x2)
|
|
Packit Service |
a1973e |
return (tb_to_bus * bus_to_core_x2) / 2;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static unsigned int __init detect_core_khz(unsigned int tb_khz, unsigned int tb_to_core)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int core_khz;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (tb_to_core) {
|
|
Packit Service |
a1973e |
perfctr_info.tsc_to_cpu_mult = tb_to_core;
|
|
Packit Service |
a1973e |
return tb_khz * tb_to_core;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
core_khz = of_core_khz();
|
|
Packit Service |
a1973e |
perfctr_info.tsc_to_cpu_mult = core_khz / tb_khz;
|
|
Packit Service |
a1973e |
return core_khz;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* Detect the timebase and core clock frequencies.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* Known issues:
|
|
Packit Service |
a1973e |
* 1. The OF timebase-frequency property is sometimes way off, and
|
|
Packit Service |
a1973e |
* similarly the ppc32 kernel's tb_ticks_per_jiffy variable.
|
|
Packit Service |
a1973e |
* (Observed on a 7447A-based laptop.)
|
|
Packit Service |
a1973e |
* Workaround: Compute the TB frequency from the bus frequency
|
|
Packit Service |
a1973e |
* and the TB-to-bus ratio.
|
|
Packit Service |
a1973e |
* 2. The OF clock-frequency property is sometimes wrong.
|
|
Packit Service |
a1973e |
* (Observed on a Beige G3 with a 7455 upgrade processor.)
|
|
Packit Service |
a1973e |
* Workaround: Compute the core frequency from the TB frequency
|
|
Packit Service |
a1973e |
* and the TB-to-core ratio.
|
|
Packit Service |
a1973e |
* 3. The PLL_CFG details may be unknown.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
static unsigned int __init detect_cpu_khz(enum pll_type pll_type, unsigned int tb_to_bus)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int bus_khz;
|
|
Packit Service |
a1973e |
unsigned int tb_khz;
|
|
Packit Service |
a1973e |
unsigned int tb_to_core;
|
|
Packit Service |
a1973e |
unsigned int core_khz;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
bus_khz = of_bus_khz();
|
|
Packit Service |
a1973e |
tb_khz = detect_tb_khz(bus_khz, tb_to_bus);
|
|
Packit Service |
a1973e |
tb_to_core = detect_tb_to_core(pll_type, tb_to_bus);
|
|
Packit Service |
a1973e |
core_khz = detect_core_khz(tb_khz, tb_to_core);
|
|
Packit Service |
a1973e |
if (!core_khz)
|
|
Packit Service |
a1973e |
printk(KERN_WARNING "perfctr: unable to determine CPU speed\n");
|
|
Packit Service |
a1973e |
return core_khz;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int __init known_init(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
static char known_name[] __initdata = "PowerPC 60x/7xx/74xx";
|
|
Packit Service |
a1973e |
unsigned int features;
|
|
Packit Service |
a1973e |
enum pll_type pll_type;
|
|
Packit Service |
a1973e |
unsigned int pvr;
|
|
Packit Service |
a1973e |
int have_mmcr1;
|
|
Packit Service |
a1973e |
unsigned int tb_to_bus;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
tb_to_bus = 4; /* default, overridden below if necessary */
|
|
Packit Service |
a1973e |
features = PERFCTR_FEATURE_RDTSC | PERFCTR_FEATURE_RDPMC;
|
|
Packit Service |
a1973e |
have_mmcr1 = 1;
|
|
Packit Service |
a1973e |
pvr = mfspr(SPRN_PVR);
|
|
Packit Service |
a1973e |
switch (PVR_VER(pvr)) {
|
|
Packit Service |
a1973e |
case 0x0004: /* 604 */
|
|
Packit Service |
a1973e |
pm_type = PM_604;
|
|
Packit Service |
a1973e |
pll_type = PLL_NONE;
|
|
Packit Service |
a1973e |
features = PERFCTR_FEATURE_RDTSC;
|
|
Packit Service |
a1973e |
have_mmcr1 = 0;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x0009: /* 604e; */
|
|
Packit Service |
a1973e |
case 0x000A: /* 604ev */
|
|
Packit Service |
a1973e |
pm_type = PM_604e;
|
|
Packit Service |
a1973e |
pll_type = PLL_604e;
|
|
Packit Service |
a1973e |
features = PERFCTR_FEATURE_RDTSC;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x0008: /* 750/740 */
|
|
Packit Service |
a1973e |
pm_type = PM_750;
|
|
Packit Service |
a1973e |
pll_type = PLL_750;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x7000: case 0x7001: /* IBM750FX */
|
|
Packit Service |
a1973e |
if ((pvr & 0xFF0F) >= 0x0203)
|
|
Packit Service |
a1973e |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit Service |
a1973e |
pm_type = PM_750;
|
|
Packit Service |
a1973e |
pll_type = PLL_750FX;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x7002: /* IBM750GX */
|
|
Packit Service |
a1973e |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit Service |
a1973e |
pm_type = PM_750;
|
|
Packit Service |
a1973e |
pll_type = PLL_750FX;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x000C: /* 7400 */
|
|
Packit Service |
a1973e |
pm_type = PM_7400;
|
|
Packit Service |
a1973e |
pll_type = PLL_7400;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x800C: /* 7410 */
|
|
Packit Service |
a1973e |
if ((pvr & 0xFFFF) >= 0x1103)
|
|
Packit Service |
a1973e |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit Service |
a1973e |
pm_type = PM_7400;
|
|
Packit Service |
a1973e |
pll_type = PLL_7400;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x8000: /* 7451/7441 */
|
|
Packit Service |
a1973e |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit Service |
a1973e |
pm_type = PM_7450;
|
|
Packit Service |
a1973e |
pll_type = PLL_7450;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x8001: /* 7455/7445 */
|
|
Packit Service |
a1973e |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit Service |
a1973e |
pm_type = PM_7450;
|
|
Packit Service |
a1973e |
pll_type = ((pvr & 0xFFFF) < 0x0303) ? PLL_7450 : PLL_7457;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x8002: /* 7457/7447 */
|
|
Packit Service |
a1973e |
case 0x8003: /* 7447A */
|
|
Packit Service |
a1973e |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit Service |
a1973e |
pm_type = PM_7450;
|
|
Packit Service |
a1973e |
pll_type = PLL_7457;
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case 0x8004: /* 7448 */
|
|
Packit Service |
a1973e |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit Service |
a1973e |
pm_type = PM_7450;
|
|
Packit Service |
a1973e |
pll_type = PLL_NONE; /* known to differ from 7447A, no details yet */
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
default:
|
|
Packit Service |
a1973e |
return -ENODEV;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
perfctr_info.cpu_features = features;
|
|
Packit Service |
a1973e |
perfctr_info.cpu_type = 0; /* user-space should inspect PVR */
|
|
Packit Service |
a1973e |
perfctr_cpu_name = known_name;
|
|
Packit Service |
a1973e |
perfctr_info.cpu_khz = detect_cpu_khz(pll_type, tb_to_bus);
|
|
Packit Service |
a1973e |
perfctr_ppc_init_tests(have_mmcr1);
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int __init unknown_init(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
static char unknown_name[] __initdata = "Generic PowerPC with TB";
|
|
Packit Service |
a1973e |
unsigned int khz;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
khz = detect_cpu_khz(PLL_NONE, 0);
|
|
Packit Service |
a1973e |
if (!khz)
|
|
Packit Service |
a1973e |
return -ENODEV;
|
|
Packit Service |
a1973e |
perfctr_info.cpu_features = PERFCTR_FEATURE_RDTSC;
|
|
Packit Service |
a1973e |
perfctr_info.cpu_type = 0;
|
|
Packit Service |
a1973e |
perfctr_cpu_name = unknown_name;
|
|
Packit Service |
a1973e |
perfctr_info.cpu_khz = khz;
|
|
Packit Service |
a1973e |
pm_type = PM_NONE;
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void perfctr_cpu_clear_one(void *ignore)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
/* PREEMPT note: when called via on_each_cpu(),
|
|
Packit Service |
a1973e |
this is in IRQ context with preemption disabled. */
|
|
Packit Service |
a1973e |
perfctr_cpu_clear_counters();
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void perfctr_cpu_reset(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
on_each_cpu(perfctr_cpu_clear_one, NULL, 1);
|
|
Packit Service |
a1973e |
perfctr_cpu_set_ihandler(NULL);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
int __init perfctr_cpu_init(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
int err;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
perfctr_info.cpu_features = 0;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
err = known_init();
|
|
Packit Service |
a1973e |
if (err) {
|
|
Packit Service |
a1973e |
err = unknown_init();
|
|
Packit Service |
a1973e |
if (err)
|
|
Packit Service |
a1973e |
goto out;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
out:
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
void __exit perfctr_cpu_exit(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Hardware reservation. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static DEFINE_MUTEX(mutex);
|
|
Packit Service |
a1973e |
static const char *current_service = 0;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
const char *perfctr_cpu_reserve(const char *service)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
const char *ret;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
mutex_lock(&mutex);
|
|
Packit Service |
a1973e |
ret = current_service;
|
|
Packit Service |
a1973e |
if (ret)
|
|
Packit Service |
a1973e |
goto out_unlock;
|
|
Packit Service |
a1973e |
ret = "unknown driver (oprofile?)";
|
|
Packit Service |
a1973e |
if (perfctr_reserve_pmc_hardware() < 0)
|
|
Packit Service |
a1973e |
goto out_unlock;
|
|
Packit Service |
a1973e |
current_service = service;
|
|
Packit Service |
a1973e |
__module_get(THIS_MODULE);
|
|
Packit Service |
a1973e |
perfctr_cpu_reset();
|
|
Packit Service |
a1973e |
ret = NULL;
|
|
Packit Service |
a1973e |
out_unlock:
|
|
Packit Service |
a1973e |
mutex_unlock(&mutex);
|
|
Packit Service |
a1973e |
return ret;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
void perfctr_cpu_release(const char *service)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
mutex_lock(&mutex);
|
|
Packit Service |
a1973e |
if (service != current_service) {
|
|
Packit Service |
a1973e |
printk(KERN_ERR "%s: attempt by %s to release while reserved by %s\n",
|
|
Packit Service |
a1973e |
__FUNCTION__, service, current_service);
|
|
Packit Service |
a1973e |
} else {
|
|
Packit Service |
a1973e |
/* power down the counters */
|
|
Packit Service |
a1973e |
perfctr_cpu_reset();
|
|
Packit Service |
a1973e |
current_service = 0;
|
|
Packit Service |
a1973e |
perfctr_release_pmc_hardware();
|
|
Packit Service |
a1973e |
module_put(THIS_MODULE);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
mutex_unlock(&mutex);
|
|
Packit Service |
a1973e |
}
|