summaryrefslogtreecommitdiff
path: root/arch/mips/oprofile
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/oprofile')
-rw-r--r--arch/mips/oprofile/Kconfig23
-rw-r--r--arch/mips/oprofile/Makefile15
-rw-r--r--arch/mips/oprofile/common.c106
-rw-r--r--arch/mips/oprofile/op_impl.h37
-rw-r--r--arch/mips/oprofile/op_model_rm9000.c137
5 files changed, 318 insertions, 0 deletions
diff --git a/arch/mips/oprofile/Kconfig b/arch/mips/oprofile/Kconfig
new file mode 100644
index 000000000000..19d37730b664
--- /dev/null
+++ b/arch/mips/oprofile/Kconfig
@@ -0,0 +1,23 @@
+
+menu "Profiling support"
+ depends on EXPERIMENTAL
+
+config PROFILING
+ bool "Profiling support (EXPERIMENTAL)"
+ help
+ Say Y here to enable the extended profiling support mechanisms used
+ by profilers such as OProfile.
+
+
+config OPROFILE
+ tristate "OProfile system profiling (EXPERIMENTAL)"
+ depends on PROFILING
+ help
+ OProfile is a profiling system capable of profiling the
+ whole system, include the kernel, kernel modules, libraries,
+ and applications.
+
+ If unsure, say N.
+
+endmenu
+
diff --git a/arch/mips/oprofile/Makefile b/arch/mips/oprofile/Makefile
new file mode 100644
index 000000000000..354261d37d62
--- /dev/null
+++ b/arch/mips/oprofile/Makefile
@@ -0,0 +1,15 @@
+EXTRA_CFLAGS := -Werror
+
+obj-$(CONFIG_OPROFILE) += oprofile.o
+
+DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
+ oprof.o cpu_buffer.o buffer_sync.o \
+ event_buffer.o oprofile_files.o \
+ oprofilefs.o oprofile_stats.o \
+ timer_int.o )
+
+oprofile-y := $(DRIVER_OBJS) common.o
+
+oprofile-$(CONFIG_CPU_MIPS32) += op_model_mipsxx.o
+oprofile-$(CONFIG_CPU_MIPS64) += op_model_mipsxx.o
+oprofile-$(CONFIG_CPU_RM9000) += op_model_rm9000.o
diff --git a/arch/mips/oprofile/common.c b/arch/mips/oprofile/common.c
new file mode 100644
index 000000000000..ab65ce3d471a
--- /dev/null
+++ b/arch/mips/oprofile/common.c
@@ -0,0 +1,106 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 by Ralf Baechle
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/oprofile.h>
+#include <linux/smp.h>
+#include <asm/cpu-info.h>
+
+#include "op_impl.h"
+
+extern struct op_mips_model op_model_mipsxx __attribute__((weak));
+extern struct op_mips_model op_model_rm9000 __attribute__((weak));
+
+static struct op_mips_model *model;
+
+static struct op_counter_config ctr[20];
+
+static int op_mips_setup(void)
+{
+ /* Pre-compute the values to stuff in the hardware registers. */
+ model->reg_setup(ctr);
+
+ /* Configure the registers on all cpus. */
+ on_each_cpu(model->cpu_setup, 0, 0, 1);
+
+ return 0;
+}
+
+static int op_mips_create_files(struct super_block * sb, struct dentry * root)
+{
+ int i;
+
+ for (i = 0; i < model->num_counters; ++i) {
+ struct dentry *dir;
+ char buf[3];
+
+ snprintf(buf, sizeof buf, "%d", i);
+ dir = oprofilefs_mkdir(sb, root, buf);
+
+ oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+ oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+ oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+ /* Dummies. */
+ oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+ oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+ oprofilefs_create_ulong(sb, dir, "exl", &ctr[i].exl);
+ oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+ }
+
+ return 0;
+}
+
+static int op_mips_start(void)
+{
+ on_each_cpu(model->cpu_start, NULL, 0, 1);
+
+ return 0;
+}
+
+static void op_mips_stop(void)
+{
+ /* Disable performance monitoring for all counters. */
+ on_each_cpu(model->cpu_stop, NULL, 0, 1);
+}
+
+void __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+ struct op_mips_model *lmodel = NULL;
+
+ switch (current_cpu_data.cputype) {
+ case CPU_24K:
+ lmodel = &op_model_mipsxx;
+ break;
+
+ case CPU_RM9000:
+ lmodel = &op_model_rm9000;
+ break;
+ };
+
+ if (!lmodel)
+ return;
+
+ if (lmodel->init())
+ return;
+
+ model = lmodel;
+
+ ops->create_files = op_mips_create_files;
+ ops->setup = op_mips_setup;
+ ops->start = op_mips_start;
+ ops->stop = op_mips_stop;
+ ops->cpu_type = lmodel->cpu_type;
+
+ printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
+ lmodel->cpu_type);
+}
+
+void oprofile_arch_exit(void)
+{
+ model->exit();
+}
diff --git a/arch/mips/oprofile/op_impl.h b/arch/mips/oprofile/op_impl.h
new file mode 100644
index 000000000000..9f5cdff041be
--- /dev/null
+++ b/arch/mips/oprofile/op_impl.h
@@ -0,0 +1,37 @@
+/**
+ * @file arch/alpha/oprofile/op_impl.h
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Richard Henderson <rth@twiddle.net>
+ */
+
+#ifndef OP_IMPL_H
+#define OP_IMPL_H 1
+
+/* Per-counter configuration as set via oprofilefs. */
+struct op_counter_config {
+ unsigned long enabled;
+ unsigned long event;
+ unsigned long count;
+ /* Dummies because I am too lazy to hack the userspace tools. */
+ unsigned long kernel;
+ unsigned long user;
+ unsigned long exl;
+ unsigned long unit_mask;
+};
+
+/* Per-architecture configury and hooks. */
+struct op_mips_model {
+ void (*reg_setup) (struct op_counter_config *);
+ void (*cpu_setup) (void * dummy);
+ int (*init)(void);
+ void (*exit)(void);
+ void (*cpu_start)(void *args);
+ void (*cpu_stop)(void *args);
+ char *cpu_type;
+ unsigned char num_counters;
+};
+
+#endif
diff --git a/arch/mips/oprofile/op_model_rm9000.c b/arch/mips/oprofile/op_model_rm9000.c
new file mode 100644
index 000000000000..bee47793cb1a
--- /dev/null
+++ b/arch/mips/oprofile/op_model_rm9000.c
@@ -0,0 +1,137 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 by Ralf Baechle
+ */
+#include <linux/oprofile.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+
+#include "op_impl.h"
+
+#define RM9K_COUNTER1_EVENT(event) ((event) << 0)
+#define RM9K_COUNTER1_SUPERVISOR (1ULL << 7)
+#define RM9K_COUNTER1_KERNEL (1ULL << 8)
+#define RM9K_COUNTER1_USER (1ULL << 9)
+#define RM9K_COUNTER1_ENABLE (1ULL << 10)
+#define RM9K_COUNTER1_OVERFLOW (1ULL << 15)
+
+#define RM9K_COUNTER2_EVENT(event) ((event) << 16)
+#define RM9K_COUNTER2_SUPERVISOR (1ULL << 23)
+#define RM9K_COUNTER2_KERNEL (1ULL << 24)
+#define RM9K_COUNTER2_USER (1ULL << 25)
+#define RM9K_COUNTER2_ENABLE (1ULL << 26)
+#define RM9K_COUNTER2_OVERFLOW (1ULL << 31)
+
+extern unsigned int rm9000_perfcount_irq;
+
+static struct rm9k_register_config {
+ unsigned int control;
+ unsigned int reset_counter1;
+ unsigned int reset_counter2;
+} reg;
+
+/* Compute all of the registers in preparation for enabling profiling. */
+
+static void rm9000_reg_setup(struct op_counter_config *ctr)
+{
+ unsigned int control = 0;
+
+ /* Compute the performance counter control word. */
+ /* For now count kernel and user mode */
+ if (ctr[0].enabled)
+ control |= RM9K_COUNTER1_EVENT(ctr[0].event) |
+ RM9K_COUNTER1_KERNEL |
+ RM9K_COUNTER1_USER |
+ RM9K_COUNTER1_ENABLE;
+ if (ctr[1].enabled)
+ control |= RM9K_COUNTER2_EVENT(ctr[1].event) |
+ RM9K_COUNTER2_KERNEL |
+ RM9K_COUNTER2_USER |
+ RM9K_COUNTER2_ENABLE;
+ reg.control = control;
+
+ reg.reset_counter1 = 0x80000000 - ctr[0].count;
+ reg.reset_counter2 = 0x80000000 - ctr[1].count;
+}
+
+/* Program all of the registers in preparation for enabling profiling. */
+
+static void rm9000_cpu_setup (void *args)
+{
+ uint64_t perfcount;
+
+ perfcount = ((uint64_t) reg.reset_counter2 << 32) | reg.reset_counter1;
+ write_c0_perfcount(perfcount);
+}
+
+static void rm9000_cpu_start(void *args)
+{
+ /* Start all counters on current CPU */
+ write_c0_perfcontrol(reg.control);
+}
+
+static void rm9000_cpu_stop(void *args)
+{
+ /* Stop all counters on current CPU */
+ write_c0_perfcontrol(0);
+}
+
+static irqreturn_t rm9000_perfcount_handler(int irq, void * dev_id,
+ struct pt_regs *regs)
+{
+ unsigned int control = read_c0_perfcontrol();
+ uint32_t counter1, counter2;
+ uint64_t counters;
+
+ /*
+ * RM9000 combines two 32-bit performance counters into a single
+ * 64-bit coprocessor zero register. To avoid a race updating the
+ * registers we need to stop the counters while we're messing with
+ * them ...
+ */
+ write_c0_perfcontrol(0);
+
+ counters = read_c0_perfcount();
+ counter1 = counters;
+ counter2 = counters >> 32;
+
+ if (control & RM9K_COUNTER1_OVERFLOW) {
+ oprofile_add_sample(regs, 0);
+ counter1 = reg.reset_counter1;
+ }
+ if (control & RM9K_COUNTER2_OVERFLOW) {
+ oprofile_add_sample(regs, 1);
+ counter2 = reg.reset_counter2;
+ }
+
+ counters = ((uint64_t)counter2 << 32) | counter1;
+ write_c0_perfcount(counters);
+ write_c0_perfcontrol(reg.control);
+
+ return IRQ_HANDLED;
+}
+
+static int rm9000_init(void)
+{
+ return request_irq(rm9000_perfcount_irq, rm9000_perfcount_handler,
+ 0, "Perfcounter", NULL);
+}
+
+static void rm9000_exit(void)
+{
+ free_irq(rm9000_perfcount_irq, NULL);
+}
+
+struct op_mips_model op_model_rm9000 = {
+ .reg_setup = rm9000_reg_setup,
+ .cpu_setup = rm9000_cpu_setup,
+ .init = rm9000_init,
+ .exit = rm9000_exit,
+ .cpu_start = rm9000_cpu_start,
+ .cpu_stop = rm9000_cpu_stop,
+ .cpu_type = "mips/rm9000",
+ .num_counters = 2
+};