|
Packit |
577717 |
/* $Id: ppc.c,v 1.43 2007/10/06 13:02:07 mikpe Exp $
|
|
Packit |
577717 |
* PPC32 performance-monitoring counters driver.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Copyright (C) 2004-2007 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 |
#include <linux/init.h>
|
|
Packit |
577717 |
#include <linux/sched.h>
|
|
Packit |
577717 |
#include <linux/fs.h>
|
|
Packit |
577717 |
#include <linux/perfctr.h>
|
|
Packit |
577717 |
#include <asm/prom.h>
|
|
Packit |
577717 |
#include <asm/time.h> /* tb_ticks_per_jiffy, get_tbl() */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include "ppc_tests.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Support for lazy evntsel and perfctr SPR updates. */
|
|
Packit |
577717 |
struct per_cpu_cache { /* roughly a subset of perfctr_cpu_state */
|
|
Packit |
577717 |
unsigned int id; /* cache owner id */
|
|
Packit |
577717 |
/* Physically indexed cache of the MMCRs. */
|
|
Packit |
577717 |
unsigned int ppc_mmcr[3];
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
static DEFINE_PER_CPU(struct per_cpu_cache, per_cpu_cache);
|
|
Packit |
577717 |
#define __get_cpu_cache(cpu) (&per_cpu(per_cpu_cache, cpu))
|
|
Packit |
577717 |
#define get_cpu_cache() (&__get_cpu_var(per_cpu_cache))
|
|
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[6];
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
enum pm_type {
|
|
Packit |
577717 |
PM_NONE,
|
|
Packit |
577717 |
PM_604,
|
|
Packit |
577717 |
PM_604e,
|
|
Packit |
577717 |
PM_750, /* XXX: Minor event set diffs between IBM and Moto. */
|
|
Packit |
577717 |
PM_7400,
|
|
Packit |
577717 |
PM_7450,
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
static enum pm_type pm_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 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
static void perfctr_default_ihandler(unsigned long pc)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static perfctr_ihandler_t perfctr_ihandler = perfctr_default_ihandler;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void do_perfctr_interrupt(struct pt_regs *regs)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
preempt_disable();
|
|
Packit |
577717 |
(*perfctr_ihandler)(instruction_pointer(regs));
|
|
Packit |
577717 |
preempt_enable_no_resched();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline int perfctr_reserve_pmc_hardware(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return reserve_pmc_hardware(do_perfctr_interrupt);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_set_ihandler(perfctr_ihandler_t ihandler)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
perfctr_ihandler = ihandler ? ihandler : perfctr_default_ihandler;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static inline int perfctr_reserve_pmc_hardware(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return reserve_pmc_hardware(NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#define perfctr_cstatus_has_ictrs(cstatus) 0
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void perfctr_release_pmc_hardware(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
release_pmc_hardware();
|
|
Packit |
577717 |
}
|
|
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->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->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->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 |
/* The ppc driver internally uses cstatus & (1<<30) to record that
|
|
Packit |
577717 |
a context has an asynchronously changing MMCR0. */
|
|
Packit |
577717 |
static inline unsigned int perfctr_cstatus_set_mmcr0_quirk(unsigned int cstatus)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return cstatus | (1 << 30);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline int perfctr_cstatus_has_mmcr0_quirk(unsigned int cstatus)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return cstatus & (1 << 30);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Driver procedures. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* The PowerPC 604/750/74xx family.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Common features
|
|
Packit |
577717 |
* ---------------
|
|
Packit |
577717 |
* - Per counter event selection data in subfields of control registers.
|
|
Packit |
577717 |
* MMCR0 contains both global control and PMC1/PMC2 event selectors.
|
|
Packit |
577717 |
* - Overflow interrupt support is present in all processors, but an
|
|
Packit |
577717 |
* erratum makes it difficult to use in 750/7400/7410 processors.
|
|
Packit |
577717 |
* - There is no concept of per-counter qualifiers:
|
|
Packit |
577717 |
* - User-mode/supervisor-mode restrictions are global.
|
|
Packit |
577717 |
* - Two groups of counters, PMC1 and PMC2-PMC<highest>. Each group
|
|
Packit |
577717 |
* has a single overflow interrupt/event enable/disable flag.
|
|
Packit |
577717 |
* - The instructions used to read (mfspr) and write (mtspr) the control
|
|
Packit |
577717 |
* and counter registers (SPRs) only support hardcoded register numbers.
|
|
Packit |
577717 |
* There is no support for accessing an SPR via a runtime value.
|
|
Packit |
577717 |
* - Each counter supports its own unique set of events. However, events
|
|
Packit |
577717 |
* 0-1 are common for PMC1-PMC4, and events 2-4 are common for PMC1-PMC4.
|
|
Packit |
577717 |
* - There is no separate high-resolution core clock counter.
|
|
Packit |
577717 |
* The time-base counter is available, but it typically runs an order of
|
|
Packit |
577717 |
* magnitude slower than the core clock.
|
|
Packit |
577717 |
* Any performance counter can be programmed to count core clocks, but
|
|
Packit |
577717 |
* doing this (a) reserves one PMC, and (b) needs indirect accesses
|
|
Packit |
577717 |
* since the SPR number in general isn't known at compile-time.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 604
|
|
Packit |
577717 |
* ---
|
|
Packit |
577717 |
* 604 has MMCR0, PMC1, PMC2, SIA, and SDA.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* MMCR0[THRESHOLD] is not automatically multiplied.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* On the 604, software must always reset MMCR0[ENINT] after
|
|
Packit |
577717 |
* taking a PMI. This is not the case for the 604e.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 604e
|
|
Packit |
577717 |
* ----
|
|
Packit |
577717 |
* 604e adds MMCR1, PMC3, and PMC4.
|
|
Packit |
577717 |
* Bus-to-core multiplier is available via HID1[PLL_CFG].
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* MMCR0[THRESHOLD] is automatically multiplied by 4.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* When the 604e vectors to the PMI handler, it automatically
|
|
Packit |
577717 |
* clears any pending PMIs. Unlike the 604, the 604e does not
|
|
Packit |
577717 |
* require MMCR0[ENINT] to be cleared (and possibly reset)
|
|
Packit |
577717 |
* before external interrupts can be re-enabled.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 750
|
|
Packit |
577717 |
* ---
|
|
Packit |
577717 |
* 750 adds user-readable MMCRn/PMCn/SIA registers, and removes SDA.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* MMCR0[THRESHOLD] is not automatically multiplied.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Motorola MPC750UM.pdf, page C-78, states: "The performance monitor
|
|
Packit |
577717 |
* of the MPC755 functions the same as that of the MPC750, (...), except
|
|
Packit |
577717 |
* that for both the MPC750 and MPC755, no combination of the thermal
|
|
Packit |
577717 |
* assist unit, the decrementer register, and the performance monitor
|
|
Packit |
577717 |
* can be used at any one time. If exceptions for any two of these
|
|
Packit |
577717 |
* functional blocks are enabled together, multiple exceptions caused
|
|
Packit |
577717 |
* by any of these three blocks cause unpredictable results."
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* IBM 750CXe_Err_DD2X.pdf, Erratum #13, states that a PMI which
|
|
Packit |
577717 |
* occurs immediately after a delayed decrementer exception can
|
|
Packit |
577717 |
* corrupt SRR0, causing the processor to hang. It also states that
|
|
Packit |
577717 |
* PMIs via TB bit transitions can be used to simulate the decrementer.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 750FX adds dual-PLL support and programmable core frequency switching.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 750FX DD2.3 fixed the DEC/PMI SRR0 corruption erratum.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 74xx
|
|
Packit |
577717 |
* ----
|
|
Packit |
577717 |
* 7400 adds MMCR2 and BAMR.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* MMCR0[THRESHOLD] is multiplied by 2 or 32, as specified
|
|
Packit |
577717 |
* by MMCR2[THRESHMULT].
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 74xx changes the semantics of several MMCR0 control bits,
|
|
Packit |
577717 |
* compared to 604/750.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* PPC7410 Erratum No. 10: Like the MPC750 TAU/DECR/PMI erratum.
|
|
Packit |
577717 |
* Erratum No. 14 marks TAU as unsupported in 7410, but this leaves
|
|
Packit |
577717 |
* perfmon and decrementer interrupts as being mutually exclusive.
|
|
Packit |
577717 |
* Affects PPC7410 1.0-1.2 (PVR 0x800C1100-0x800C1102). 1.3 and up
|
|
Packit |
577717 |
* (PVR 0x800C1103 up) are Ok.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 7450 adds PMC5 and PMC6.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* 7455/7445 V3.3 (PVR 80010303) and later use the 7457 PLL table,
|
|
Packit |
577717 |
* earlier revisions use the 7450 PLL table
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline unsigned int read_pmc(unsigned int pmc)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (pmc) {
|
|
Packit |
577717 |
default: /* impossible, but silences gcc warning */
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC1);
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC2);
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC3);
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC4);
|
|
Packit |
577717 |
case 4:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC5);
|
|
Packit |
577717 |
case 5:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC6);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void ppc_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->user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
ctrs->tsc = get_tbl();
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nrctrs; ++i) {
|
|
Packit |
577717 |
unsigned int pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
ctrs->pmc[i] = read_pmc(pmc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int pmc_max_event(unsigned int pmc)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (pmc) {
|
|
Packit |
577717 |
default: /* impossible, but silences gcc warning */
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
return 127;
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
return 63;
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
return 31;
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
return 31;
|
|
Packit |
577717 |
case 4:
|
|
Packit |
577717 |
return 31;
|
|
Packit |
577717 |
case 5:
|
|
Packit |
577717 |
return 63;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int get_nr_pmcs(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (pm_type) {
|
|
Packit |
577717 |
case PM_7450:
|
|
Packit |
577717 |
return 6;
|
|
Packit |
577717 |
case PM_7400:
|
|
Packit |
577717 |
case PM_750:
|
|
Packit |
577717 |
case PM_604e:
|
|
Packit |
577717 |
return 4;
|
|
Packit |
577717 |
case PM_604:
|
|
Packit |
577717 |
return 2;
|
|
Packit |
577717 |
default: /* PM_NONE, but silences gcc warning */
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int ppc_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[6];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
nr_pmcs = get_nr_pmcs();
|
|
Packit |
577717 |
nractrs = state->control.header.nractrs;
|
|
Packit |
577717 |
nrctrs = nractrs + state->control.header.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 |
evntsel[1-1] = (state->control.mmcr0 >> (31-25)) & 0x7F;
|
|
Packit |
577717 |
evntsel[2-1] = (state->control.mmcr0 >> (31-31)) & 0x3F;
|
|
Packit |
577717 |
evntsel[3-1] = (state->control.mmcr1 >> (31- 4)) & 0x1F;
|
|
Packit |
577717 |
evntsel[4-1] = (state->control.mmcr1 >> (31- 9)) & 0x1F;
|
|
Packit |
577717 |
evntsel[5-1] = (state->control.mmcr1 >> (31-14)) & 0x1F;
|
|
Packit |
577717 |
evntsel[6-1] = (state->control.mmcr1 >> (31-20)) & 0x3F;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i = 0; i < nrctrs; ++i) {
|
|
Packit |
577717 |
pmc = state->control.pmc_map[i];
|
|
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 |
if (evntsel[pmc] > pmc_max_event(pmc))
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* unused event selectors must be zero */
|
|
Packit |
577717 |
for(i = 0; i < ARRAY_SIZE(evntsel); ++i)
|
|
Packit |
577717 |
if (!(pmc_mask & (1<
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* XXX: temporary limitation */
|
|
Packit |
577717 |
if ((pmi_mask & ~1) && (pmi_mask & ~1) != (pmc_mask & ~1))
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch (pm_type) {
|
|
Packit |
577717 |
case PM_7450:
|
|
Packit |
577717 |
case PM_7400:
|
|
Packit |
577717 |
if (state->control.mmcr2 & MMCR2_RESERVED)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
if (state->control.mmcr2)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* check MMCR1; non-existent event selectors are taken care of
|
|
Packit |
577717 |
by the "unused event selectors must be zero" check above */
|
|
Packit |
577717 |
if (state->control.mmcr1 & MMCR1__RESERVED)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* We do not yet handle TBEE as the only exception cause,
|
|
Packit |
577717 |
so PMXE requires at least one interrupt-mode counter. */
|
|
Packit |
577717 |
if ((state->control.mmcr0 & MMCR0_PMXE) && !state->control.header.nrictrs)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
state->id = new_id();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* MMCR0[FC] and MMCR0[TRIGGER] may change on 74xx if FCECE or
|
|
Packit |
577717 |
* TRIGGER is set. At suspends we must read MMCR0 back into
|
|
Packit |
577717 |
* the state and the cache and then freeze the counters, and
|
|
Packit |
577717 |
* at resumes we must unfreeze the counters and reload MMCR0.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
switch (pm_type) {
|
|
Packit |
577717 |
case PM_7450:
|
|
Packit |
577717 |
case PM_7400:
|
|
Packit |
577717 |
if (state->control.mmcr0 & (MMCR0_FCECE | MMCR0_TRIGGER))
|
|
Packit |
577717 |
state->user.cstatus = perfctr_cstatus_set_mmcr0_quirk(state->user.cstatus);
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* The MMCR0 handling for FCECE and TRIGGER is also needed for PMXE. */
|
|
Packit |
577717 |
if (state->control.mmcr0 & (MMCR0_PMXE | MMCR0_FCECE | MMCR0_TRIGGER))
|
|
Packit |
577717 |
state->user.cstatus = perfctr_cstatus_set_mmcr0_quirk(state->user.cstatus);
|
|
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 */
|
|
Packit |
577717 |
static void ppc_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->user.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->control.pmc_map[i];
|
|
Packit |
577717 |
unsigned int now = read_pmc(pmc);
|
|
Packit |
577717 |
state->user.pmc[i].sum += now - state->user.pmc[i].start;
|
|
Packit |
577717 |
state->user.pmc[i].start = now;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/* cache->id is still == state->id */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void ppc_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[6];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpu = smp_processor_id();
|
|
Packit |
577717 |
cache = __get_cpu_cache(cpu);
|
|
Packit |
577717 |
if (cache->id == state->id) {
|
|
Packit |
577717 |
/* Clearing cache->id to force write_control()
|
|
Packit |
577717 |
to unfreeze MMCR0 would be done here, but it
|
|
Packit |
577717 |
is subsumed by resume()'s MMCR0 reload logic. */
|
|
Packit |
577717 |
if (is_isuspend_cpu(state, cpu))
|
|
Packit |
577717 |
return; /* skip reload of PMCs */
|
|
Packit |
577717 |
}
|
|
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->user.cstatus;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i)
|
|
Packit |
577717 |
pmc[state->control.pmc_map[i]] = state->user.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->id remains != state->id */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void ppc_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->id == state->id)
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Order matters here: update threshmult and event
|
|
Packit |
577717 |
* selectors before updating global control, which
|
|
Packit |
577717 |
* potentially enables PMIs.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Since mtspr doesn't accept a runtime value for the
|
|
Packit |
577717 |
* SPR number, unroll the loop so each mtspr targets
|
|
Packit |
577717 |
* a constant SPR.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* For processors without MMCR2, we ensure that the
|
|
Packit |
577717 |
* cache and the state indicate the same value for it,
|
|
Packit |
577717 |
* preventing any actual mtspr to it. Ditto for MMCR1.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
value = state->control.mmcr2;
|
|
Packit |
577717 |
if (value != cache->ppc_mmcr[2]) {
|
|
Packit |
577717 |
cache->ppc_mmcr[2] = value;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR2, value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
value = state->control.mmcr1;
|
|
Packit |
577717 |
if (value != cache->ppc_mmcr[1]) {
|
|
Packit |
577717 |
cache->ppc_mmcr[1] = value;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR1, value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
value = state->control.mmcr0;
|
|
Packit |
577717 |
if (value != cache->ppc_mmcr[0]) {
|
|
Packit |
577717 |
cache->ppc_mmcr[0] = value;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
cache->id = state->id;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void ppc_clear_counters(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (pm_type) {
|
|
Packit |
577717 |
case PM_7450:
|
|
Packit |
577717 |
case PM_7400:
|
|
Packit |
577717 |
mtspr(SPRN_MMCR2, 0);
|
|
Packit |
577717 |
mtspr(SPRN_BAMR, 0);
|
|
Packit |
577717 |
case PM_750:
|
|
Packit |
577717 |
case PM_604e:
|
|
Packit |
577717 |
mtspr(SPRN_MMCR1, 0);
|
|
Packit |
577717 |
case PM_604:
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, 0);
|
|
Packit |
577717 |
case PM_NONE:
|
|
Packit |
577717 |
;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
switch (pm_type) {
|
|
Packit |
577717 |
case PM_7450:
|
|
Packit |
577717 |
mtspr(SPRN_PMC6, 0);
|
|
Packit |
577717 |
mtspr(SPRN_PMC5, 0);
|
|
Packit |
577717 |
case PM_7400:
|
|
Packit |
577717 |
case PM_750:
|
|
Packit |
577717 |
case PM_604e:
|
|
Packit |
577717 |
mtspr(SPRN_PMC4, 0);
|
|
Packit |
577717 |
mtspr(SPRN_PMC3, 0);
|
|
Packit |
577717 |
case PM_604:
|
|
Packit |
577717 |
mtspr(SPRN_PMC2, 0);
|
|
Packit |
577717 |
mtspr(SPRN_PMC1, 0);
|
|
Packit |
577717 |
case PM_NONE:
|
|
Packit |
577717 |
;
|
|
Packit |
577717 |
}
|
|
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 ppc_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 ppc_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 ppc_isuspend(state);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_iresume(const struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return ppc_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->control.mmcr0 |= MMCR0_PMXE;
|
|
Packit |
577717 |
#ifdef CONFIG_SMP
|
|
Packit |
577717 |
clear_isuspend_cpu(state);
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
get_cpu_cache()->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 |
unsigned int cstatus, nrctrs, i, pmc_mask;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
pmc_mask = 0;
|
|
Packit |
577717 |
for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) {
|
|
Packit |
577717 |
if ((int)state->user.pmc[i].start < 0) { /* PPC-specific */
|
|
Packit |
577717 |
unsigned int pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
/* XXX: "+=" to correct for overshots */
|
|
Packit |
577717 |
state->user.pmc[i].start = state->control.ireset[pmc];
|
|
Packit |
577717 |
pmc_mask |= (1 << i);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (!pmc_mask && (state->control.mmcr0 & MMCR0_TBEE))
|
|
Packit |
577717 |
pmc_mask = (1<<8); /* fake TB bit flip indicator */
|
|
Packit |
577717 |
return pmc_mask;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline int check_ireset(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int nrctrs, i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
i = state->control.header.nractrs;
|
|
Packit |
577717 |
nrctrs = i + state->control.header.nrictrs;
|
|
Packit |
577717 |
for(; i < nrctrs; ++i) {
|
|
Packit |
577717 |
unsigned int pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
if ((int)state->control.ireset[pmc] < 0) /* PPC-specific */
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
state->user.pmc[i].start = state->control.ireset[pmc];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return 0;
|
|
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(struct perfctr_cpu_state *state) { return 0; }
|
|
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 ppc_check_control(state);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int perfctr_cpu_update_control(struct perfctr_cpu_state *state, int is_global)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
clear_isuspend_cpu(state);
|
|
Packit |
577717 |
state->user.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.header.nrictrs)
|
|
Packit |
577717 |
return -EPERM;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
err = check_control(state); /* may initialise state->cstatus */
|
|
Packit |
577717 |
if (err < 0)
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
err = check_ireset(state);
|
|
Packit |
577717 |
if (err < 0) {
|
|
Packit |
577717 |
state->user.cstatus = 0;
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
state->user.cstatus |= perfctr_mk_cstatus(state->control.header.tsc_on,
|
|
Packit |
577717 |
state->control.header.nractrs,
|
|
Packit |
577717 |
state->control.header.nrictrs);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* get_reg_offset() maps SPR numbers to offsets into struct perfctr_cpu_control,
|
|
Packit |
577717 |
* suitable for accessing control data of type unsigned int.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static const struct {
|
|
Packit |
577717 |
unsigned int spr;
|
|
Packit |
577717 |
unsigned int offset;
|
|
Packit |
577717 |
} reg_offsets[] = {
|
|
Packit |
577717 |
{ SPRN_MMCR0, offsetof(struct perfctr_cpu_control, mmcr0) },
|
|
Packit |
577717 |
{ SPRN_MMCR1, offsetof(struct perfctr_cpu_control, mmcr1) },
|
|
Packit |
577717 |
{ SPRN_MMCR2, offsetof(struct perfctr_cpu_control, mmcr2) },
|
|
Packit |
577717 |
{ SPRN_PMC1, offsetof(struct perfctr_cpu_control, ireset[1-1]) },
|
|
Packit |
577717 |
{ SPRN_PMC2, offsetof(struct perfctr_cpu_control, ireset[2-1]) },
|
|
Packit |
577717 |
{ SPRN_PMC3, offsetof(struct perfctr_cpu_control, ireset[3-1]) },
|
|
Packit |
577717 |
{ SPRN_PMC4, offsetof(struct perfctr_cpu_control, ireset[4-1]) },
|
|
Packit |
577717 |
{ SPRN_PMC5, offsetof(struct perfctr_cpu_control, ireset[5-1]) },
|
|
Packit |
577717 |
{ SPRN_PMC6, offsetof(struct perfctr_cpu_control, ireset[6-1]) },
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int get_reg_offset(unsigned int spr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i = 0; i < ARRAY_SIZE(reg_offsets); ++i)
|
|
Packit |
577717 |
if (spr == reg_offsets[i].spr)
|
|
Packit |
577717 |
return reg_offsets[i].offset;
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int access_regs(struct perfctr_cpu_control *control,
|
|
Packit |
577717 |
void *argp, unsigned int argbytes, int do_write)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_cpu_reg *regs;
|
|
Packit |
577717 |
unsigned int i, nr_regs, *where;
|
|
Packit |
577717 |
int offset;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
nr_regs = argbytes / sizeof(struct perfctr_cpu_reg);
|
|
Packit |
577717 |
if (nr_regs * sizeof(struct perfctr_cpu_reg) != argbytes)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
regs = (struct perfctr_cpu_reg*)argp;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i = 0; i < nr_regs; ++i) {
|
|
Packit |
577717 |
offset = get_reg_offset(regs[i].nr);
|
|
Packit |
577717 |
if (offset < 0)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
where = (unsigned int*)((char*)control + offset);
|
|
Packit |
577717 |
if (do_write)
|
|
Packit |
577717 |
*where = regs[i].value;
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
regs[i].value = *where;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return argbytes;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int perfctr_cpu_control_write(struct perfctr_cpu_control *control, unsigned int domain,
|
|
Packit |
577717 |
const void *srcp, unsigned int srcbytes)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (domain != PERFCTR_DOMAIN_CPU_REGS)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
return access_regs(control, (void*)srcp, srcbytes, 1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int perfctr_cpu_control_read(const struct perfctr_cpu_control *control, unsigned int domain,
|
|
Packit |
577717 |
void *dstp, unsigned int dstbytes)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (domain != PERFCTR_DOMAIN_CPU_REGS)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
return access_regs((struct perfctr_cpu_control*)control, dstp, dstbytes, 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 |
write_perfseq_begin(&state->user.sequence);
|
|
Packit |
577717 |
if (perfctr_cstatus_has_mmcr0_quirk(state->user.cstatus)) {
|
|
Packit |
577717 |
unsigned int mmcr0 = mfspr(SPRN_MMCR0);
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, mmcr0 | MMCR0_FC);
|
|
Packit |
577717 |
get_cpu_cache()->ppc_mmcr[0] = mmcr0 | MMCR0_FC;
|
|
Packit |
577717 |
state->control.mmcr0 = mmcr0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (perfctr_cstatus_has_ictrs(state->user.cstatus))
|
|
Packit |
577717 |
perfctr_cpu_isuspend(state);
|
|
Packit |
577717 |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
state->user.tsc_sum += now.tsc - state->user.tsc_start;
|
|
Packit |
577717 |
nractrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nractrs; ++i)
|
|
Packit |
577717 |
state->user.pmc[i].sum += now.pmc[i] - state->user.pmc[i].start;
|
|
Packit |
577717 |
write_perfseq_end(&state->user.sequence);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_resume(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
write_perfseq_begin(&state->user.sequence);
|
|
Packit |
577717 |
if (perfctr_cstatus_has_ictrs(state->user.cstatus))
|
|
Packit |
577717 |
perfctr_cpu_iresume(state);
|
|
Packit |
577717 |
if (perfctr_cstatus_has_mmcr0_quirk(state->user.cstatus))
|
|
Packit |
577717 |
get_cpu_cache()->id = 0; /* force reload of MMCR0 */
|
|
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->user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
state->user.tsc_start = now.tsc;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nrctrs; ++i)
|
|
Packit |
577717 |
state->user.pmc[i].start = now.pmc[i];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
write_perfseq_end(&state->user.sequence);
|
|
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 |
write_perfseq_begin(&state->user.sequence);
|
|
Packit |
577717 |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus)) {
|
|
Packit |
577717 |
state->user.tsc_sum += now.tsc - state->user.tsc_start;
|
|
Packit |
577717 |
state->user.tsc_start = now.tsc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
nractrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nractrs; ++i) {
|
|
Packit |
577717 |
state->user.pmc[i].sum += now.pmc[i] - state->user.pmc[i].start;
|
|
Packit |
577717 |
state->user.pmc[i].start = now.pmc[i];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
write_perfseq_end(&state->user.sequence);
|
|
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->id = -1;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ppc_clear_counters();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Processor detection and initialisation procedures. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Derive CPU core frequency from TB frequency and PLL_CFG. */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
enum pll_type {
|
|
Packit |
577717 |
PLL_NONE, /* for e.g. 604 which has no HID1[PLL_CFG] */
|
|
Packit |
577717 |
PLL_604e,
|
|
Packit |
577717 |
PLL_750,
|
|
Packit |
577717 |
PLL_750FX,
|
|
Packit |
577717 |
PLL_7400,
|
|
Packit |
577717 |
PLL_7450,
|
|
Packit |
577717 |
PLL_7457,
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* These are the known bus-to-core ratios, indexed by PLL_CFG.
|
|
Packit |
577717 |
Multiplied by 2 since half-multiplier steps are present. */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned char cfg_ratio_604e[16] __initdata = { // *2
|
|
Packit |
577717 |
2, 2, 14, 2, 4, 13, 5, 9,
|
|
Packit |
577717 |
6, 11, 8, 10, 3, 12, 7, 0
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned char cfg_ratio_750[16] __initdata = { // *2
|
|
Packit |
577717 |
5, 15, 14, 2, 4, 13, 20, 9, // 0b0110 is 18 if L1_TSTCLK=0, but that is abnormal
|
|
Packit |
577717 |
6, 11, 8, 10, 16, 12, 7, 0
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned char cfg_ratio_750FX[32] __initdata = { // *2
|
|
Packit |
577717 |
0, 0, 2, 2, 4, 5, 6, 7,
|
|
Packit |
577717 |
8, 9, 10, 11, 12, 13, 14, 15,
|
|
Packit |
577717 |
16, 17, 18, 19, 20, 22, 24, 26,
|
|
Packit |
577717 |
28, 30, 32, 34, 36, 38, 40, 0
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned char cfg_ratio_7400[16] __initdata = { // *2
|
|
Packit |
577717 |
18, 15, 14, 2, 4, 13, 5, 9,
|
|
Packit |
577717 |
6, 11, 8, 10, 16, 12, 7, 0
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned char cfg_ratio_7450[32] __initdata = { // *2
|
|
Packit |
577717 |
1, 0, 15, 30, 14, 0, 2, 0,
|
|
Packit |
577717 |
4, 0, 13, 26, 5, 0, 9, 18,
|
|
Packit |
577717 |
6, 0, 11, 22, 8, 20, 10, 24,
|
|
Packit |
577717 |
16, 28, 12, 32, 7, 0, 0, 0
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned char cfg_ratio_7457[32] __initdata = { // *2
|
|
Packit |
577717 |
23, 34, 15, 30, 14, 36, 2, 40,
|
|
Packit |
577717 |
4, 42, 13, 26, 17, 48, 19, 18,
|
|
Packit |
577717 |
6, 21, 11, 22, 8, 20, 10, 24,
|
|
Packit |
577717 |
16, 28, 12, 32, 27, 56, 0, 25
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int __init tb_to_core_ratio(enum pll_type pll_type)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned char *cfg_ratio;
|
|
Packit |
577717 |
unsigned int shift = 28, mask = 0xF, hid1, pll_cfg, ratio;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch (pll_type) {
|
|
Packit |
577717 |
case PLL_604e:
|
|
Packit |
577717 |
cfg_ratio = cfg_ratio_604e;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PLL_750:
|
|
Packit |
577717 |
cfg_ratio = cfg_ratio_750;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PLL_750FX:
|
|
Packit |
577717 |
cfg_ratio = cfg_ratio_750FX;
|
|
Packit |
577717 |
hid1 = mfspr(SPRN_HID1);
|
|
Packit |
577717 |
switch ((hid1 >> 16) & 0x3) { /* HID1[PI0,PS] */
|
|
Packit |
577717 |
case 0: /* PLL0 with external config */
|
|
Packit |
577717 |
shift = 31-4; /* access HID1[PCE] */
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 2: /* PLL0 with internal config */
|
|
Packit |
577717 |
shift = 31-20; /* access HID1[PC0] */
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 1: case 3: /* PLL1 */
|
|
Packit |
577717 |
shift = 31-28; /* access HID1[PC1] */
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
mask = 0x1F;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PLL_7400:
|
|
Packit |
577717 |
cfg_ratio = cfg_ratio_7400;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PLL_7450:
|
|
Packit |
577717 |
cfg_ratio = cfg_ratio_7450;
|
|
Packit |
577717 |
shift = 12;
|
|
Packit |
577717 |
mask = 0x1F;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PLL_7457:
|
|
Packit |
577717 |
cfg_ratio = cfg_ratio_7457;
|
|
Packit |
577717 |
shift = 12;
|
|
Packit |
577717 |
mask = 0x1F;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
hid1 = mfspr(SPRN_HID1);
|
|
Packit |
577717 |
pll_cfg = (hid1 >> shift) & mask;
|
|
Packit |
577717 |
ratio = cfg_ratio[pll_cfg];
|
|
Packit |
577717 |
if (!ratio)
|
|
Packit |
577717 |
printk(KERN_WARNING "perfctr: unknown PLL_CFG 0x%x\n", pll_cfg);
|
|
Packit |
577717 |
return (4/2) * ratio;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int __init pll_to_core_khz(enum pll_type pll_type)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int tb_to_core = tb_to_core_ratio(pll_type);
|
|
Packit |
577717 |
perfctr_info.tsc_to_cpu_mult = tb_to_core;
|
|
Packit |
577717 |
return tb_ticks_per_jiffy * tb_to_core * (HZ/10) / (1000/10);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Extract core and timebase frequencies from Open Firmware. */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PPC_OF
|
|
Packit |
577717 |
static unsigned int __init of_to_core_khz(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct device_node *cpu;
|
|
Packit |
577717 |
unsigned int *fp, core, tb;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpu = find_type_devices("cpu");
|
|
Packit |
577717 |
if (!cpu)
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
fp = (unsigned int*)get_property(cpu, "clock-frequency", NULL);
|
|
Packit |
577717 |
if (!fp || !(core = *fp))
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
fp = (unsigned int*)get_property(cpu, "timebase-frequency", NULL);
|
|
Packit |
577717 |
if (!fp || !(tb = *fp))
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
perfctr_info.tsc_to_cpu_mult = core / tb;
|
|
Packit |
577717 |
return core / 1000;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static inline unsigned int of_to_core_khz(void) { return 0; }
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int __init detect_cpu_khz(enum pll_type pll_type)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int khz;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
khz = pll_to_core_khz(pll_type);
|
|
Packit |
577717 |
if (khz)
|
|
Packit |
577717 |
return khz;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
khz = of_to_core_khz();
|
|
Packit |
577717 |
if (khz)
|
|
Packit |
577717 |
return khz;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printk(KERN_WARNING "perfctr: unable to determine CPU speed\n");
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int __init known_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
static char known_name[] __initdata = "PowerPC 60x/7xx/74xx";
|
|
Packit |
577717 |
unsigned int features;
|
|
Packit |
577717 |
enum pll_type pll_type;
|
|
Packit |
577717 |
unsigned int pvr;
|
|
Packit |
577717 |
int have_mmcr1;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
features = PERFCTR_FEATURE_RDTSC | PERFCTR_FEATURE_RDPMC;
|
|
Packit |
577717 |
have_mmcr1 = 1;
|
|
Packit |
577717 |
pvr = mfspr(SPRN_PVR);
|
|
Packit |
577717 |
switch (PVR_VER(pvr)) {
|
|
Packit |
577717 |
case 0x0004: /* 604 */
|
|
Packit |
577717 |
pm_type = PM_604;
|
|
Packit |
577717 |
pll_type = PLL_NONE;
|
|
Packit |
577717 |
features = PERFCTR_FEATURE_RDTSC;
|
|
Packit |
577717 |
have_mmcr1 = 0;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x0009: /* 604e; */
|
|
Packit |
577717 |
case 0x000A: /* 604ev */
|
|
Packit |
577717 |
pm_type = PM_604e;
|
|
Packit |
577717 |
pll_type = PLL_604e;
|
|
Packit |
577717 |
features = PERFCTR_FEATURE_RDTSC;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x0008: /* 750/740 */
|
|
Packit |
577717 |
pm_type = PM_750;
|
|
Packit |
577717 |
pll_type = PLL_750;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x7000: case 0x7001: /* IBM750FX */
|
|
Packit |
577717 |
if ((pvr & 0xFF0F) >= 0x0203)
|
|
Packit |
577717 |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit |
577717 |
pm_type = PM_750;
|
|
Packit |
577717 |
pll_type = PLL_750FX;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x7002: /* IBM750GX */
|
|
Packit |
577717 |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit |
577717 |
pm_type = PM_750;
|
|
Packit |
577717 |
pll_type = PLL_750FX;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x000C: /* 7400 */
|
|
Packit |
577717 |
pm_type = PM_7400;
|
|
Packit |
577717 |
pll_type = PLL_7400;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x800C: /* 7410 */
|
|
Packit |
577717 |
if ((pvr & 0xFFFF) >= 0x1103)
|
|
Packit |
577717 |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit |
577717 |
pm_type = PM_7400;
|
|
Packit |
577717 |
pll_type = PLL_7400;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x8000: /* 7451/7441 */
|
|
Packit |
577717 |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit |
577717 |
pm_type = PM_7450;
|
|
Packit |
577717 |
pll_type = PLL_7450;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x8001: /* 7455/7445 */
|
|
Packit |
577717 |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit |
577717 |
pm_type = PM_7450;
|
|
Packit |
577717 |
pll_type = ((pvr & 0xFFFF) < 0x0303) ? PLL_7450 : PLL_7457;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x8002: /* 7457/7447 */
|
|
Packit |
577717 |
case 0x8003: /* 7447A */
|
|
Packit |
577717 |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit |
577717 |
pm_type = PM_7450;
|
|
Packit |
577717 |
pll_type = PLL_7457;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 0x8004: /* 7448 */
|
|
Packit |
577717 |
features |= PERFCTR_FEATURE_PCINT;
|
|
Packit |
577717 |
pm_type = PM_7450;
|
|
Packit |
577717 |
pll_type = PLL_NONE; /* known to differ from 7447A, no details yet */
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
return -ENODEV;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
perfctr_info.cpu_features = features;
|
|
Packit |
577717 |
perfctr_cpu_name = known_name;
|
|
Packit |
577717 |
perfctr_info.cpu_khz = detect_cpu_khz(pll_type);
|
|
Packit |
577717 |
perfctr_ppc_init_tests(have_mmcr1);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int __init unknown_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
static char unknown_name[] __initdata = "Generic PowerPC with TB";
|
|
Packit |
577717 |
unsigned int khz;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
khz = detect_cpu_khz(PLL_NONE);
|
|
Packit |
577717 |
if (!khz)
|
|
Packit |
577717 |
return -ENODEV;
|
|
Packit |
577717 |
perfctr_info.cpu_features = PERFCTR_FEATURE_RDTSC;
|
|
Packit |
577717 |
perfctr_cpu_name = unknown_name;
|
|
Packit |
577717 |
perfctr_info.cpu_khz = khz;
|
|
Packit |
577717 |
pm_type = PM_NONE;
|
|
Packit |
577717 |
return 0;
|
|
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, 1);
|
|
Packit |
577717 |
perfctr_cpu_set_ihandler(NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int init_done;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int __init perfctr_cpu_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_info.cpu_features = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
err = known_init();
|
|
Packit |
577717 |
if (err) {
|
|
Packit |
577717 |
err = unknown_init();
|
|
Packit |
577717 |
if (err)
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
init_done = 1;
|
|
Packit |
577717 |
out:
|
|
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 |
if (!init_done)
|
|
Packit |
577717 |
return "unsupported hardware";
|
|
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?)";
|
|
Packit |
577717 |
if (perfctr_reserve_pmc_hardware() < 0)
|
|
Packit |
577717 |
goto out_unlock;
|
|
Packit |
577717 |
current_service = service;
|
|
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 |
} else {
|
|
Packit |
577717 |
/* power down the counters */
|
|
Packit |
577717 |
perfctr_cpu_reset();
|
|
Packit |
577717 |
current_service = 0;
|
|
Packit |
577717 |
perfctr_release_pmc_hardware();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
mutex_unlock(&mutex);
|
|
Packit |
577717 |
}
|