735 lines
16 KiB
C
735 lines
16 KiB
C
/* $NetBSD: xscale_pmc.c,v 1.12 2007/10/17 19:53:45 garbled Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2002 Wasabi Systems, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Written by Allen Briggs for Wasabi Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed for the NetBSD Project by
|
|
* Wasabi Systems, Inc.
|
|
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
|
|
* or promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: xscale_pmc.c,v 1.12 2007/10/17 19:53:45 garbled Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/pmc.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <machine/pmc.h>
|
|
|
|
#include <arm/xscale/xscalereg.h>
|
|
#include <arm/xscale/xscalevar.h>
|
|
|
|
extern int profsrc;
|
|
|
|
struct xscale_pmc_state {
|
|
uint32_t pmnc; /* performance monitor ctrl */
|
|
uint32_t pmcr[3]; /* array of counter reset values */
|
|
uint64_t pmcv[3]; /* array of counter values */
|
|
uint64_t pmc_accv[3]; /* accumulated ctr values of children */
|
|
};
|
|
|
|
#define __PMC_CCNT_I (0)
|
|
#define __PMC0_I (1)
|
|
#define __PMC1_I (2)
|
|
#define __PMC_CCNT (1 << __PMC_CCNT_I)
|
|
#define __PMC0 (1 << __PMC0_I)
|
|
#define __PMC1 (1 << __PMC1_I)
|
|
#define __PMC_NCTRS 3
|
|
|
|
uint32_t pmc_kernel_mask = 0;
|
|
uint32_t pmc_kernel_bits = 0;
|
|
uint32_t pmc_kernel_enabled = 0;
|
|
uint32_t pmc_profiling_enabled = 0;
|
|
uint32_t pmc_reset_vals[3] = {0x80000000, 0x80000000, 0x80000000};
|
|
|
|
int pmc_usecount[3] = {0, 0, 0};
|
|
|
|
static inline uint32_t
|
|
xscale_pmnc_read(void)
|
|
{
|
|
uint32_t pmnc;
|
|
|
|
__asm volatile("mrc p14, 0, %0, c0, c0, 0"
|
|
: "=r" (pmnc));
|
|
|
|
return pmnc;
|
|
}
|
|
|
|
static inline void
|
|
xscale_pmnc_write(uint32_t val)
|
|
{
|
|
|
|
__asm volatile("mcr p14, 0, %0, c0, c0, 0"
|
|
: : "r" (val));
|
|
}
|
|
|
|
int
|
|
xscale_pmc_dispatch(void *arg)
|
|
{
|
|
struct clockframe *frame = arg;
|
|
struct xscale_pmc_state *pmcs;
|
|
struct proc *p;
|
|
uint32_t pmnc;
|
|
int s;
|
|
|
|
s = splhigh();
|
|
|
|
pmnc = xscale_pmnc_read() & ~(PMNC_C | PMNC_P);
|
|
|
|
/*
|
|
* Disable interrupts -- ensure we don't reset anything.
|
|
*/
|
|
xscale_pmnc_write(pmnc &
|
|
~(PMNC_PMN0_IF | PMNC_PMN1_IF | PMNC_CC_IF | PMNC_E));
|
|
|
|
/*
|
|
* If we have recorded a clock overflow...
|
|
*/
|
|
if (pmnc & PMNC_CC_IF) {
|
|
if (pmc_profiling_enabled & __PMC_CCNT) {
|
|
proftick(frame);
|
|
__asm volatile("mcr p14, 0, %0, c1, c0, 0"
|
|
: : "r" (pmc_reset_vals[__PMC_CCNT_I]));
|
|
} else if ((p = curproc) != NULL &&
|
|
(p->p_md.pmc_enabled & __PMC_CCNT) != 0) {
|
|
/*
|
|
* XXX - It's not quite clear that this is the proper
|
|
* way to handle this case (here or in the other
|
|
* counters below).
|
|
*/
|
|
pmcs = p->p_md.pmc_state;
|
|
pmcs->pmcv[__PMC_CCNT_I] += 0x100000000ULL;
|
|
__asm volatile("mcr p14, 0, %0, c1, c0, 0"
|
|
: : "r" (pmcs->pmcr[__PMC_CCNT_I]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we have recorded an PMC0 overflow...
|
|
*/
|
|
if (pmnc & PMNC_PMN0_IF) {
|
|
if (pmc_profiling_enabled & __PMC0) {
|
|
proftick(frame);
|
|
__asm volatile("mcr p14, 0, %0, c2, c0, 0"
|
|
: : "r" (pmc_reset_vals[__PMC0_I]));
|
|
} else if ((p = curproc) != NULL &&
|
|
(p->p_md.pmc_enabled & __PMC0) != 0) {
|
|
/*
|
|
* XXX - should handle wrapping the counter.
|
|
*/
|
|
pmcs = p->p_md.pmc_state;
|
|
pmcs->pmcv[__PMC0_I] += 0x100000000ULL;
|
|
__asm volatile("mcr p14, 0, %0, c2, c0, 0"
|
|
: : "r" (pmcs->pmcr[__PMC0_I]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we have recorded an PMC1 overflow...
|
|
*/
|
|
if (pmnc & PMNC_PMN1_IF) {
|
|
if (pmc_profiling_enabled & __PMC1) {
|
|
proftick(frame);
|
|
__asm volatile("mcr p14, 0, %0, c3, c0, 0"
|
|
: : "r" (pmc_reset_vals[__PMC1_I]));
|
|
} else if ((p = curproc) != NULL &&
|
|
(p->p_md.pmc_enabled & __PMC1) != 0) {
|
|
pmcs = p->p_md.pmc_state;
|
|
pmcs->pmcv[__PMC1_I] += 0x100000000ULL;
|
|
__asm volatile("mcr p14, 0, %0, c3, c0, 0"
|
|
: : "r" (pmcs->pmcr[__PMC1_I]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If any overflows were set, this will clear them.
|
|
* It will also re-enable the counters.
|
|
*/
|
|
xscale_pmnc_write(pmnc);
|
|
|
|
splx(s);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
xscale_fork(struct proc *p1, struct proc *p2)
|
|
{
|
|
struct xscale_pmc_state *pmcs_p1, *pmcs_p2;
|
|
|
|
p2->p_md.pmc_enabled = p1->p_md.pmc_enabled;
|
|
p2->p_md.pmc_state = malloc(sizeof(struct xscale_pmc_state),
|
|
M_PROC, M_NOWAIT);
|
|
if (p2->p_md.pmc_state == NULL) {
|
|
/* XXX
|
|
* Can't return failure at this point, so just disable
|
|
* PMC for new process.
|
|
*/
|
|
p2->p_md.pmc_enabled = 0;
|
|
return;
|
|
}
|
|
pmcs_p1 = p1->p_md.pmc_state;
|
|
pmcs_p2 = p2->p_md.pmc_state;
|
|
pmcs_p2->pmnc = pmcs_p1->pmnc;
|
|
pmcs_p2->pmcv[0] = 0;
|
|
pmcs_p2->pmcv[1] = 0;
|
|
pmcs_p2->pmcv[2] = 0;
|
|
pmcs_p2->pmc_accv[0] = 0;
|
|
pmcs_p2->pmc_accv[1] = 0;
|
|
pmcs_p2->pmc_accv[2] = 0;
|
|
if (p2->p_md.pmc_enabled & __PMC_CCNT)
|
|
pmc_usecount[__PMC_CCNT_I]++;
|
|
if (p2->p_md.pmc_enabled & __PMC0)
|
|
pmc_usecount[__PMC0_I]++;
|
|
if (p2->p_md.pmc_enabled & __PMC1)
|
|
pmc_usecount[__PMC1_I]++;
|
|
}
|
|
|
|
static int
|
|
xscale_num_counters(void)
|
|
{
|
|
return __PMC_NCTRS;
|
|
}
|
|
|
|
static int
|
|
xscale_counter_type(int ctr)
|
|
{
|
|
int ret;
|
|
|
|
switch (ctr) {
|
|
case __PMC_CCNT_I:
|
|
ret = PMC_TYPE_I80200_CCNT;
|
|
break;
|
|
case __PMC0_I:
|
|
case __PMC1_I:
|
|
ret = PMC_TYPE_I80200_PMCx;
|
|
break;
|
|
case -1:
|
|
ret = PMC_CLASS_I80200;
|
|
break;
|
|
default:
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
xscale_save_context(struct proc *p)
|
|
{
|
|
struct xscale_pmc_state *pmcs;
|
|
uint32_t pmnc, val;
|
|
|
|
if (p && p->p_md.pmc_state) {
|
|
pmcs = (struct xscale_pmc_state *) p->p_md.pmc_state;
|
|
|
|
/* disable counters */
|
|
pmnc = xscale_pmnc_read() & ~PMNC_E;
|
|
xscale_pmnc_write(pmnc);
|
|
|
|
/* do not save pmnc */
|
|
|
|
if (p->p_md.pmc_enabled & __PMC_CCNT) {
|
|
/* save ccnt */
|
|
__asm volatile("mrc p14, 0, %0, c1, c0, 0"
|
|
: "=r" (val));
|
|
pmcs->pmcv[__PMC_CCNT_I] &= ~0xffffffffULL;
|
|
pmcs->pmcv[__PMC_CCNT_I] |= val;
|
|
}
|
|
|
|
if (p->p_md.pmc_enabled & __PMC0) {
|
|
/* save pmc0 */
|
|
__asm volatile("mrc p14, 0, %0, c2, c0, 0"
|
|
: "=r" (val));
|
|
pmcs->pmcv[__PMC0_I] &= ~0xffffffffULL;
|
|
pmcs->pmcv[__PMC0_I] |= val;
|
|
}
|
|
|
|
if (p->p_md.pmc_enabled & __PMC1) {
|
|
/* save pmc1 */
|
|
__asm volatile("mrc p14, 0, %0, c3, c0, 0"
|
|
: "=r" (val));
|
|
pmcs->pmcv[__PMC1_I] &= ~0xffffffffULL;
|
|
pmcs->pmcv[__PMC1_I] |= val;
|
|
}
|
|
|
|
if (pmc_kernel_bits) {
|
|
__asm volatile("mcr p14, 0, %0, c0, c0, 0"
|
|
: : "r" (pmc_kernel_bits | PMNC_E));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
xscale_restore_context(struct proc *p)
|
|
{
|
|
struct xscale_pmc_state *pmcs;
|
|
register_t r = 0;
|
|
uint32_t val;
|
|
|
|
if (p && p->p_md.pmc_state) {
|
|
pmcs = (struct xscale_pmc_state *) p->p_md.pmc_state;
|
|
|
|
if (p->p_md.pmc_enabled & __PMC1) {
|
|
/* restore pmc1 */
|
|
val = pmcs->pmcv[__PMC1_I] & 0xffffffffULL;
|
|
__asm volatile("mcr p14, 0, %0, c3, c0, 0" : :
|
|
"r" (val));
|
|
}
|
|
|
|
if (p->p_md.pmc_enabled & __PMC0) {
|
|
/* restore pmc0 */
|
|
val = pmcs->pmcv[__PMC0_I] & 0xffffffffULL;
|
|
__asm volatile("mcr p14, 0, %0, c2, c0, 0" : :
|
|
"r" (val));
|
|
}
|
|
|
|
if (p->p_md.pmc_enabled & __PMC_CCNT) {
|
|
/* restore ccnt */
|
|
val = pmcs->pmcv[__PMC_CCNT_I] & 0xffffffffULL;
|
|
__asm volatile("mcr p14, 0, %0, c1, c0, 0" : :
|
|
"r" (val));
|
|
}
|
|
|
|
if (p->p_md.pmc_enabled)
|
|
r = pmcs->pmnc;
|
|
}
|
|
|
|
if (r | pmc_kernel_bits) {
|
|
/* restore pmnc & enable counters */
|
|
__asm volatile("mcr p14, 0, %0, c0, c0, 0"
|
|
: : "r" (r | pmc_kernel_bits | PMNC_E));
|
|
}
|
|
}
|
|
|
|
static void
|
|
xscale_accumulate(struct proc *parent, struct proc *child)
|
|
{
|
|
struct xscale_pmc_state *pmcs_parent, *pmcs_child;
|
|
|
|
pmcs_parent = parent->p_md.pmc_state;
|
|
pmcs_child = child->p_md.pmc_state;
|
|
if (pmcs_parent && pmcs_child) {
|
|
pmcs_parent->pmc_accv[__PMC_CCNT_I] +=
|
|
pmcs_child->pmcv[__PMC_CCNT_I];
|
|
pmcs_parent->pmc_accv[__PMC0_I] += pmcs_child->pmcv[__PMC0_I];
|
|
pmcs_parent->pmc_accv[__PMC1_I] += pmcs_child->pmcv[__PMC1_I];
|
|
}
|
|
}
|
|
|
|
static void
|
|
xscale_process_exit(struct proc *p)
|
|
{
|
|
int i;
|
|
|
|
if (!p)
|
|
return;
|
|
|
|
for (i=0 ; i<__PMC_NCTRS ; i++) {
|
|
if (p->p_md.pmc_enabled & (1 << i)) {
|
|
pmc_usecount[i]--;
|
|
p->p_md.pmc_enabled &= ~(1 << i);
|
|
}
|
|
}
|
|
if (p->p_md.pmc_state)
|
|
free(p->p_md.pmc_state, M_PROC);
|
|
p->p_md.pmc_state = NULL;
|
|
p->p_md.pmc_enabled = 0;
|
|
}
|
|
|
|
static void
|
|
xscale_enable_counter(struct proc *p, int ctr)
|
|
{
|
|
int current = (p == curproc);
|
|
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS || !p)
|
|
return;
|
|
|
|
if (current)
|
|
pmc_save_context(p);
|
|
|
|
if ((p->p_md.pmc_enabled & (1 << ctr)) == 0) {
|
|
pmc_usecount[ctr]++;
|
|
p->p_md.pmc_enabled |= (1 << ctr);
|
|
}
|
|
|
|
if (current)
|
|
pmc_restore_context(p);
|
|
}
|
|
|
|
static void
|
|
xscale_disable_counter(struct proc *p, int ctr)
|
|
{
|
|
int current = (p == curproc);
|
|
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS || !p)
|
|
return;
|
|
|
|
if (current)
|
|
pmc_save_context(p);
|
|
|
|
if (p->p_md.pmc_enabled & (1 << ctr)) {
|
|
pmc_usecount[ctr]--;
|
|
p->p_md.pmc_enabled &= ~(1 << ctr);
|
|
}
|
|
|
|
if (current)
|
|
pmc_restore_context(p);
|
|
}
|
|
|
|
static int
|
|
xscale_counter_isrunning(struct proc *p, int ctr)
|
|
{
|
|
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS)
|
|
return -1;
|
|
|
|
return ((pmc_kernel_enabled | p->p_md.pmc_enabled) & (1 << ctr));
|
|
}
|
|
|
|
static int
|
|
xscale_counter_isconfigured(struct proc *p, int ctr)
|
|
{
|
|
|
|
return ((ctr >= 0) && (ctr < __PMC_NCTRS));
|
|
}
|
|
|
|
static int
|
|
xscale_configure_counter(struct proc *p, int ctr, struct pmc_counter_cfg *cfg)
|
|
{
|
|
struct xscale_pmc_state *pmcs;
|
|
int current = (p == curproc);
|
|
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS || !p)
|
|
return EINVAL;
|
|
|
|
if (pmc_kernel_enabled & (1 << ctr))
|
|
return EBUSY;
|
|
|
|
if (ctr) {
|
|
if ((cfg->event_id > 0x16) || ((cfg->event_id & 0xe) == 0xe)
|
|
|| (cfg->event_id == 0x13))
|
|
return ENODEV;
|
|
} else {
|
|
if (cfg->event_id != 0x100 && cfg->event_id != 0x101)
|
|
return ENODEV;
|
|
}
|
|
|
|
if (current)
|
|
pmc_save_context(p);
|
|
|
|
if (p->p_md.pmc_state == NULL) {
|
|
p->p_md.pmc_state = malloc(sizeof(struct xscale_pmc_state),
|
|
M_PROC, M_WAITOK);
|
|
if (!p->p_md.pmc_state)
|
|
return ENOMEM;
|
|
}
|
|
pmcs = p->p_md.pmc_state;
|
|
|
|
switch (ctr) {
|
|
case __PMC_CCNT_I:
|
|
pmcs->pmnc &= ~PMNC_D;
|
|
pmcs->pmnc |= (PMNC_CC_IF | PMNC_CC_IE);
|
|
if (cfg->event_id == 0x101)
|
|
pmcs->pmnc |= PMNC_D;
|
|
break;
|
|
case __PMC0_I:
|
|
pmcs->pmnc &= ~PMNC_EVCNT0_MASK;
|
|
pmcs->pmnc |= (cfg->event_id << PMNC_EVCNT0_SHIFT)
|
|
| (PMNC_PMN0_IF | PMNC_PMN0_IE);
|
|
break;
|
|
case __PMC1_I:
|
|
pmcs->pmnc &= ~PMNC_EVCNT1_MASK;
|
|
pmcs->pmnc |= (cfg->event_id << PMNC_EVCNT1_SHIFT)
|
|
| (PMNC_PMN1_IF | PMNC_PMN1_IE);
|
|
break;
|
|
}
|
|
pmcs->pmcr[ctr] = (uint32_t) -((int32_t) cfg->reset_value);
|
|
pmcs->pmcv[ctr] = pmcs->pmcr[ctr];
|
|
|
|
if (current)
|
|
pmc_restore_context(p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xscale_get_counter_value(struct proc *p, int ctr, int flags, uint64_t *pval)
|
|
{
|
|
struct xscale_pmc_state *pmcs;
|
|
uint32_t val;
|
|
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS)
|
|
return EINVAL;
|
|
|
|
if (p) {
|
|
pmcs = p->p_md.pmc_state;
|
|
|
|
if (flags & PMC_VALUE_FLAGS_CHILDREN) {
|
|
*pval = pmcs->pmc_accv[ctr];
|
|
return 0;
|
|
}
|
|
if (p != curproc) {
|
|
*pval = pmcs->pmcv[ctr];
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
switch (ctr) {
|
|
case __PMC_CCNT_I:
|
|
__asm volatile("mrc p14, 0, %0, c1, c0, 0" : "=r" (val));
|
|
break;
|
|
case __PMC0_I:
|
|
__asm volatile("mrc p14, 0, %0, c2, c0, 0" : "=r" (val));
|
|
break;
|
|
case __PMC1_I:
|
|
__asm volatile("mrc p14, 0, %0, c3, c0, 0" : "=r" (val));
|
|
break;
|
|
default:
|
|
val = 0;
|
|
break;
|
|
}
|
|
|
|
*pval = val;
|
|
if (p) {
|
|
pmcs = p->p_md.pmc_state;
|
|
*pval += pmcs->pmcv[ctr];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xscale_start_profiling(int ctr, struct pmc_counter_cfg *cfg)
|
|
{
|
|
struct proc *p = curproc;
|
|
int s;
|
|
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS)
|
|
return EINVAL;
|
|
|
|
if ((pmc_usecount[ctr] > 0) || (pmc_kernel_enabled & (1 << ctr)))
|
|
return EBUSY;
|
|
|
|
if (ctr) {
|
|
if ((cfg->event_id > 0x16) || ((cfg->event_id & 0xe) == 0xe)
|
|
|| (cfg->event_id == 0x13))
|
|
return ENODEV;
|
|
} else {
|
|
if (cfg->event_id != 0x100 && cfg->event_id != 0x101)
|
|
return ENODEV;
|
|
}
|
|
|
|
pmc_save_context(p);
|
|
|
|
pmc_reset_vals[ctr] = (uint32_t) -((int32_t) cfg->reset_value);
|
|
|
|
s = splhigh();
|
|
|
|
switch (ctr) {
|
|
case __PMC_CCNT_I:
|
|
pmc_kernel_bits &= PMNC_D;
|
|
pmc_kernel_bits |= PMNC_CC_IE;
|
|
if (cfg->event_id)
|
|
pmc_kernel_bits |= PMNC_D;
|
|
__asm volatile("mcr p14, 0, %0, c1, c0, 0" : :
|
|
"r" (pmc_reset_vals[__PMC_CCNT_I]));
|
|
__asm volatile("mcr p14, 0, %0, c0, c0, 0" : :
|
|
"r" (PMNC_CC_IF));
|
|
break;
|
|
case __PMC0_I:
|
|
pmc_kernel_bits &= ~PMNC_EVCNT0_MASK;
|
|
pmc_kernel_bits |= (cfg->event_id << PMNC_EVCNT0_SHIFT)
|
|
| PMNC_PMN0_IE;
|
|
__asm volatile("mcr p14, 0, %0, c2, c0, 0" : :
|
|
"r" (pmc_reset_vals[__PMC0_I]));
|
|
__asm volatile("mcr p14, 0, %0, c0, c0, 0" : :
|
|
"r" (PMNC_PMN0_IF));
|
|
break;
|
|
case __PMC1_I:
|
|
pmc_kernel_bits &= ~PMNC_EVCNT1_MASK;
|
|
pmc_kernel_bits |= (cfg->event_id << PMNC_EVCNT1_SHIFT)
|
|
| PMNC_PMN1_IE;
|
|
__asm volatile("mcr p14, 0, %0, c3, c0, 0" : :
|
|
"r" (pmc_reset_vals[__PMC1_I]));
|
|
__asm volatile("mcr p14, 0, %0, c0, c0, 0" : :
|
|
"r" (PMNC_PMN1_IF));
|
|
break;
|
|
}
|
|
|
|
profsrc |= 1;
|
|
pmc_kernel_enabled |= (1 << ctr);
|
|
pmc_profiling_enabled |= (1 << ctr);
|
|
|
|
pmc_restore_context(p);
|
|
|
|
splx(s);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xscale_stop_profiling(int ctr)
|
|
{
|
|
struct proc *p = curproc;
|
|
uint32_t save;
|
|
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS)
|
|
return EINVAL;
|
|
|
|
if (!(pmc_kernel_enabled & (1 << ctr)))
|
|
return 0;
|
|
|
|
save = pmc_kernel_bits;
|
|
pmc_kernel_bits = 0;
|
|
pmc_save_context(p);
|
|
pmc_kernel_bits = save;
|
|
|
|
switch (ctr) {
|
|
case __PMC_CCNT_I:
|
|
pmc_kernel_bits &= (PMNC_D | PMNC_CC_IE);
|
|
break;
|
|
case __PMC0_I:
|
|
pmc_kernel_bits &= ~(PMNC_EVCNT0_MASK | PMNC_PMN0_IE);
|
|
break;
|
|
case __PMC1_I:
|
|
pmc_kernel_bits &= ~(PMNC_EVCNT1_MASK | PMNC_PMN1_IE);
|
|
break;
|
|
}
|
|
|
|
pmc_kernel_enabled &= ~(1 << ctr);
|
|
pmc_profiling_enabled &= ~(1 << ctr);
|
|
|
|
if (pmc_profiling_enabled == 0)
|
|
profsrc &= ~1;
|
|
|
|
pmc_restore_context(p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xscale_alloc_kernel_counter(int ctr, struct pmc_counter_cfg *cfg)
|
|
{
|
|
struct proc *p = curproc;
|
|
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS)
|
|
return EINVAL;
|
|
|
|
if ((pmc_usecount[ctr] > 0) || (pmc_kernel_enabled & (1 << ctr)))
|
|
return EBUSY;
|
|
|
|
if (ctr) {
|
|
if ((cfg->event_id > 0x16) || ((cfg->event_id & 0xe) == 0xe)
|
|
|| (cfg->event_id == 0x13))
|
|
return ENODEV;
|
|
} else {
|
|
if (cfg->event_id != 0x100 && cfg->event_id != 0x101)
|
|
return ENODEV;
|
|
}
|
|
|
|
pmc_save_context(p);
|
|
|
|
pmc_reset_vals[ctr] = (uint32_t) -((int32_t) cfg->reset_value);
|
|
|
|
switch (ctr) {
|
|
case __PMC_CCNT_I:
|
|
pmc_kernel_bits &= PMNC_D;
|
|
if (cfg->event_id)
|
|
pmc_kernel_bits |= PMNC_D;
|
|
__asm volatile("mcr p14, 0, %0, c1, c0, 0" : :
|
|
"r" (pmc_reset_vals[__PMC_CCNT_I]));
|
|
break;
|
|
case __PMC0_I:
|
|
pmc_kernel_bits &= ~PMNC_EVCNT0_MASK;
|
|
pmc_kernel_bits |= (cfg->event_id << PMNC_EVCNT0_SHIFT);
|
|
__asm volatile("mcr p14, 0, %0, c2, c0, 0" : :
|
|
"r" (pmc_reset_vals[__PMC0_I]));
|
|
break;
|
|
case __PMC1_I:
|
|
pmc_kernel_bits &= ~PMNC_EVCNT1_MASK;
|
|
pmc_kernel_bits |= (cfg->event_id << PMNC_EVCNT1_SHIFT);
|
|
__asm volatile("mcr p14, 0, %0, c3, c0, 0" : :
|
|
"r" (pmc_reset_vals[__PMC1_I]));
|
|
break;
|
|
}
|
|
|
|
pmc_kernel_enabled |= (1 << ctr);
|
|
|
|
pmc_restore_context(p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xscale_free_kernel_counter(int ctr)
|
|
{
|
|
if (ctr < 0 || ctr >= __PMC_NCTRS)
|
|
return EINVAL;
|
|
|
|
if (!(pmc_kernel_enabled & (1 << ctr)))
|
|
return 0;
|
|
|
|
pmc_kernel_enabled &= ~(1 << ctr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct arm_pmc_funcs xscale_pmc_funcs = {
|
|
xscale_fork,
|
|
xscale_num_counters,
|
|
xscale_counter_type,
|
|
xscale_save_context,
|
|
xscale_restore_context,
|
|
xscale_enable_counter,
|
|
xscale_disable_counter,
|
|
xscale_accumulate,
|
|
xscale_process_exit,
|
|
xscale_configure_counter,
|
|
xscale_get_counter_value,
|
|
xscale_counter_isconfigured,
|
|
xscale_counter_isrunning,
|
|
xscale_start_profiling,
|
|
xscale_stop_profiling,
|
|
xscale_alloc_kernel_counter,
|
|
xscale_free_kernel_counter
|
|
};
|
|
|
|
void
|
|
xscale_pmu_init(void)
|
|
{
|
|
arm_pmc = &xscale_pmc_funcs;
|
|
}
|