Commit 469e04de authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Oprofile: ARM/XScale PMU driver

From: Zwane Mwaikambo <zwane@linuxpower.ca>

The following patch adds support for the XScale performance monitoring unit
to OProfile.  It uses not only the performance monitoring counters, but
also the clock cycle counter (CCNT) allowing for upto 5 usable counters.

The code has been developed and tested on an IOP331 (hardware courtesy of
Intel) therefore i haven't been able to test it on XScale PMU1 systems. 
Testing on said systems would be appreciated, and if done, please uncomment
the #define DEBUG line at the top of op_model_xscale.c

OProfile userspace support has already been committed and should be
available via CVS.
parent 6451af4d
......@@ -7,3 +7,5 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
timer_int.o )
oprofile-y := $(DRIVER_OBJS) init.o
oprofile-$(CONFIG_CPU_XSCALE) += common.o op_model_xscale.o
/**
* @file common.c
*
* @remark Copyright 2004 Oprofile Authors
* @remark Read the file COPYING
*
* @author Zwane Mwaikambo
*/
#include <linux/init.h>
#include <linux/oprofile.h>
#include <linux/errno.h>
#include <asm/semaphore.h>
#include "op_counter.h"
#include "op_arm_model.h"
static struct op_arm_model_spec *pmu_model;
static int pmu_enabled;
static struct semaphore pmu_sem;
static int pmu_start(void);
static int pmu_setup(void);
static void pmu_stop(void);
static int pmu_create_files(struct super_block *, struct dentry *);
static struct oprofile_operations pmu_ops = {
.create_files = pmu_create_files,
.setup = pmu_setup,
.shutdown = pmu_stop,
.start = pmu_start,
.stop = pmu_stop,
};
#ifdef CONFIG_PM
static struct sys_device device_oprofile = {
.id = 0,
.cls = &oprofile_sysclass,
};
static int __init init_driverfs(void)
{
int ret;
if (!(ret = sysdev_class_register(&oprofile_sysclass)))
ret = sys_device_register(&device_oprofile);
return ret;
}
static void __exit exit_driverfs(void)
{
sys_device_unregister(&device_oprofile);
sysdev_class_unregister(&oprofile_sysclass);
}
#else
#define init_driverfs() do { } while (0)
#define exit_driverfs() do { } while (0)
#endif /* CONFIG_PM */
struct op_counter_config counter_config[OP_MAX_COUNTER];
static int pmu_create_files(struct super_block *sb, struct dentry *root)
{
unsigned int i;
for (i = 0; i < pmu_model->num_counters; i++) {
struct dentry *dir;
char buf[2];
snprintf(buf, sizeof buf, "%d", i);
dir = oprofilefs_mkdir(sb, root, buf);
oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled);
oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event);
oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count);
oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask);
oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel);
oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user);
}
return 0;
}
static int pmu_setup(void)
{
int ret;
spin_lock(&oprofilefs_lock);
ret = pmu_model->setup_ctrs();
spin_unlock(&oprofilefs_lock);
return ret;
}
static int pmu_start(void)
{
int ret = -EBUSY;
down(&pmu_sem);
if (!pmu_enabled) {
ret = pmu_model->start();
pmu_enabled = !ret;
}
up(&pmu_sem);
return ret;
}
static void pmu_stop(void)
{
down(&pmu_sem);
if (pmu_enabled)
pmu_model->stop();
pmu_enabled = 0;
up(&pmu_sem);
}
int __init pmu_init(struct oprofile_operations **ops, struct op_arm_model_spec *spec)
{
init_MUTEX(&pmu_sem);
if (spec->init() < 0)
return -ENODEV;
pmu_model = spec;
init_driverfs();
*ops = &pmu_ops;
pmu_ops.cpu_type = pmu_model->name;
printk(KERN_INFO "oprofile: using %s PMU\n", spec->name);
return 0;
}
void pmu_exit(void)
{
if (pmu_model) {
exit_driverfs();
pmu_model = NULL;
}
}
......@@ -2,6 +2,7 @@
* @file init.c
*
* @remark Copyright 2004 Oprofile Authors
* @remark Read the file COPYING
*
* @author Zwane Mwaikambo
*/
......@@ -9,14 +10,21 @@
#include <linux/oprofile.h>
#include <linux/init.h>
#include <linux/errno.h>
#include "op_arm_model.h"
int oprofile_arch_init(struct oprofile_operations **ops)
int __init oprofile_arch_init(struct oprofile_operations **ops)
{
int ret = -ENODEV;
#ifdef CONFIG_CPU_XSCALE
ret = pmu_init(ops, &op_xscale_spec);
#endif
return ret;
}
void oprofile_arch_exit(void)
{
#ifdef CONFIG_CPU_XSCALE
pmu_exit();
#endif
}
/**
* @file op_arm_model.h
* interface to ARM machine specific operations
*
* @remark Copyright 2004 Oprofile Authors
* @remark Read the file COPYING
*
* @author Zwane Mwaikambo
*/
#ifndef OP_ARM_MODEL_H
#define OP_ARM_MODEL_H
struct op_arm_model_spec {
int (*init)(void);
unsigned int num_counters;
int (*setup_ctrs)(void);
int (*start)(void);
void (*stop)(void);
char *name;
};
#ifdef CONFIG_CPU_XSCALE
extern struct op_arm_model_spec op_xscale_spec;
#endif
extern int pmu_init(struct oprofile_operations **ops, struct op_arm_model_spec *spec);
extern void pmu_exit(void);
#endif /* OP_ARM_MODEL_H */
/**
* @file op_counter.h
*
* @remark Copyright 2004 Oprofile Authors
* @remark Read the file COPYING
*
* @author Zwane Mwaikambo
*/
#ifndef OP_COUNTER_H
#define OP_COUNTER_H
#define OP_MAX_COUNTER 5
/* Per performance monitor configuration as set via
* oprofilefs.
*/
struct op_counter_config {
unsigned long count;
unsigned long enabled;
unsigned long event;
unsigned long unit_mask;
unsigned long kernel;
unsigned long user;
};
extern struct op_counter_config counter_config[];
#endif /* OP_COUNTER_H */
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment