summaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files/arch/rlx/oprofile
diff options
context:
space:
mode:
authorRoman Yeryomin <roman@advem.lv>2012-09-13 00:40:35 +0300
committerRoman Yeryomin <roman@advem.lv>2012-12-03 00:13:21 +0200
commit5deb3317cb51ac52de922bb55f8492624018906d (patch)
treec2fbe6346699d9bb0f2100490c3029519bb8fde8 /target/linux/realtek/files/arch/rlx/oprofile
parent0239d37124f9184b478a42de8a7fa1bc85a6a6fe (diff)
Add realtek target files
Signed-off-by: Roman Yeryomin <roman@advem.lv>
Diffstat (limited to 'target/linux/realtek/files/arch/rlx/oprofile')
-rw-r--r--target/linux/realtek/files/arch/rlx/oprofile/Makefile17
-rw-r--r--target/linux/realtek/files/arch/rlx/oprofile/common.c124
-rw-r--r--target/linux/realtek/files/arch/rlx/oprofile/op_impl.h39
-rw-r--r--target/linux/realtek/files/arch/rlx/oprofile/op_model_mipsxx.c397
-rw-r--r--target/linux/realtek/files/arch/rlx/oprofile/op_model_rm9000.c138
5 files changed, 715 insertions, 0 deletions
diff --git a/target/linux/realtek/files/arch/rlx/oprofile/Makefile b/target/linux/realtek/files/arch/rlx/oprofile/Makefile
new file mode 100644
index 000000000..bf3be6fcf
--- /dev/null
+++ b/target/linux/realtek/files/arch/rlx/oprofile/Makefile
@@ -0,0 +1,17 @@
+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_R10000) += op_model_mipsxx.o
+oprofile-$(CONFIG_CPU_SB1) += op_model_mipsxx.o
+oprofile-$(CONFIG_CPU_RM9000) += op_model_rm9000.o
diff --git a/target/linux/realtek/files/arch/rlx/oprofile/common.c b/target/linux/realtek/files/arch/rlx/oprofile/common.c
new file mode 100644
index 000000000..3bf335454
--- /dev/null
+++ b/target/linux/realtek/files/arch/rlx/oprofile/common.c
@@ -0,0 +1,124 @@
+/*
+ * 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, 2005 Ralf Baechle
+ * Copyright (C) 2005 MIPS Technologies, Inc.
+ */
+#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_ops __attribute__((weak));
+extern struct op_mips_model op_model_rm9000_ops __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, NULL, 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[4];
+
+ 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);
+ 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);
+ /* Dummy. */
+ 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, 1);
+
+ return 0;
+}
+
+static void op_mips_stop(void)
+{
+ /* Disable performance monitoring for all counters. */
+ on_each_cpu(model->cpu_stop, NULL, 1);
+}
+
+int __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+ struct op_mips_model *lmodel = NULL;
+ int res;
+
+ switch (current_cpu_type()) {
+ case CPU_5KC:
+ case CPU_20KC:
+ case CPU_24K:
+ case CPU_25KF:
+ case CPU_34K:
+ case CPU_1004K:
+ case CPU_74K:
+ case CPU_SB1:
+ case CPU_SB1A:
+ case CPU_R10000:
+ case CPU_R12000:
+ case CPU_R14000:
+ lmodel = &op_model_mipsxx_ops;
+ break;
+
+ case CPU_RM9000:
+ lmodel = &op_model_rm9000_ops;
+ break;
+ };
+
+ if (!lmodel)
+ return -ENODEV;
+
+ res = lmodel->init();
+ if (res)
+ return res;
+
+ model = lmodel;
+
+ ops->create_files = op_mips_create_files;
+ ops->setup = op_mips_setup;
+ //ops->shutdown = op_mips_shutdown;
+ 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);
+
+ return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+ if (model)
+ model->exit();
+}
diff --git a/target/linux/realtek/files/arch/rlx/oprofile/op_impl.h b/target/linux/realtek/files/arch/rlx/oprofile/op_impl.h
new file mode 100644
index 000000000..f04b54fb3
--- /dev/null
+++ b/target/linux/realtek/files/arch/rlx/oprofile/op_impl.h
@@ -0,0 +1,39 @@
+/**
+ * @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
+
+extern int (*perf_irq)(void);
+
+/* 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/target/linux/realtek/files/arch/rlx/oprofile/op_model_mipsxx.c b/target/linux/realtek/files/arch/rlx/oprofile/op_model_mipsxx.c
new file mode 100644
index 000000000..54759f166
--- /dev/null
+++ b/target/linux/realtek/files/arch/rlx/oprofile/op_model_mipsxx.c
@@ -0,0 +1,397 @@
+/*
+ * 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, 05, 06 by Ralf Baechle
+ * Copyright (C) 2005 by MIPS Technologies, Inc.
+ */
+#include <linux/cpumask.h>
+#include <linux/oprofile.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <asm/irq_regs.h>
+
+#include "op_impl.h"
+
+#define M_PERFCTL_EXL (1UL << 0)
+#define M_PERFCTL_KERNEL (1UL << 1)
+#define M_PERFCTL_SUPERVISOR (1UL << 2)
+#define M_PERFCTL_USER (1UL << 3)
+#define M_PERFCTL_INTERRUPT_ENABLE (1UL << 4)
+#define M_PERFCTL_EVENT(event) (((event) & 0x3ff) << 5)
+#define M_PERFCTL_VPEID(vpe) ((vpe) << 16)
+#define M_PERFCTL_MT_EN(filter) ((filter) << 20)
+#define M_TC_EN_ALL M_PERFCTL_MT_EN(0)
+#define M_TC_EN_VPE M_PERFCTL_MT_EN(1)
+#define M_TC_EN_TC M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid) ((tcid) << 22)
+#define M_PERFCTL_WIDE (1UL << 30)
+#define M_PERFCTL_MORE (1UL << 31)
+
+#define M_COUNTER_OVERFLOW (1UL << 31)
+
+static int (*save_perf_irq)(void);
+
+#ifdef CONFIG_MIPS_MT_SMP
+static int cpu_has_mipsmt_pertccounters;
+#define WHAT (M_TC_EN_VPE | \
+ M_PERFCTL_VPEID(cpu_data[smp_processor_id()].vpe_id))
+#define vpe_id() (cpu_has_mipsmt_pertccounters ? \
+ 0 : cpu_data[smp_processor_id()].vpe_id)
+
+/*
+ * The number of bits to shift to convert between counters per core and
+ * counters per VPE. There is no reasonable interface atm to obtain the
+ * number of VPEs used by Linux and in the 34K this number is fixed to two
+ * anyways so we hardcore a few things here for the moment. The way it's
+ * done here will ensure that oprofile VSMP kernel will run right on a lesser
+ * core like a 24K also or with maxcpus=1.
+ */
+static inline unsigned int vpe_shift(void)
+{
+ if (num_possible_cpus() > 1)
+ return 1;
+
+ return 0;
+}
+
+#else
+
+#define WHAT 0
+#define vpe_id() 0
+
+static inline unsigned int vpe_shift(void)
+{
+ return 0;
+}
+
+#endif
+
+static inline unsigned int counters_total_to_per_cpu(unsigned int counters)
+{
+ return counters >> vpe_shift();
+}
+
+static inline unsigned int counters_per_cpu_to_total(unsigned int counters)
+{
+ return counters << vpe_shift();
+}
+
+#define __define_perf_accessors(r, n, np) \
+ \
+static inline unsigned int r_c0_ ## r ## n(void) \
+{ \
+ unsigned int cpu = vpe_id(); \
+ \
+ switch (cpu) { \
+ case 0: \
+ return read_c0_ ## r ## n(); \
+ case 1: \
+ return read_c0_ ## r ## np(); \
+ default: \
+ BUG(); \
+ } \
+ return 0; \
+} \
+ \
+static inline void w_c0_ ## r ## n(unsigned int value) \
+{ \
+ unsigned int cpu = vpe_id(); \
+ \
+ switch (cpu) { \
+ case 0: \
+ write_c0_ ## r ## n(value); \
+ return; \
+ case 1: \
+ write_c0_ ## r ## np(value); \
+ return; \
+ default: \
+ BUG(); \
+ } \
+ return; \
+} \
+
+__define_perf_accessors(perfcntr, 0, 2)
+__define_perf_accessors(perfcntr, 1, 3)
+__define_perf_accessors(perfcntr, 2, 0)
+__define_perf_accessors(perfcntr, 3, 1)
+
+__define_perf_accessors(perfctrl, 0, 2)
+__define_perf_accessors(perfctrl, 1, 3)
+__define_perf_accessors(perfctrl, 2, 0)
+__define_perf_accessors(perfctrl, 3, 1)
+
+struct op_mips_model op_model_mipsxx_ops;
+
+static struct mipsxx_register_config {
+ unsigned int control[4];
+ unsigned int counter[4];
+} reg;
+
+/* Compute all of the registers in preparation for enabling profiling. */
+
+static void mipsxx_reg_setup(struct op_counter_config *ctr)
+{
+ unsigned int counters = op_model_mipsxx_ops.num_counters;
+ int i;
+
+ /* Compute the performance counter control word. */
+ for (i = 0; i < counters; i++) {
+ reg.control[i] = 0;
+ reg.counter[i] = 0;
+
+ if (!ctr[i].enabled)
+ continue;
+
+ reg.control[i] = M_PERFCTL_EVENT(ctr[i].event) |
+ M_PERFCTL_INTERRUPT_ENABLE;
+ if (ctr[i].kernel)
+ reg.control[i] |= M_PERFCTL_KERNEL;
+ if (ctr[i].user)
+ reg.control[i] |= M_PERFCTL_USER;
+ if (ctr[i].exl)
+ reg.control[i] |= M_PERFCTL_EXL;
+ reg.counter[i] = 0x80000000 - ctr[i].count;
+ }
+}
+
+/* Program all of the registers in preparation for enabling profiling. */
+
+static void mipsxx_cpu_setup(void *args)
+{
+ unsigned int counters = op_model_mipsxx_ops.num_counters;
+
+ switch (counters) {
+ case 4:
+ w_c0_perfctrl3(0);
+ w_c0_perfcntr3(reg.counter[3]);
+ case 3:
+ w_c0_perfctrl2(0);
+ w_c0_perfcntr2(reg.counter[2]);
+ case 2:
+ w_c0_perfctrl1(0);
+ w_c0_perfcntr1(reg.counter[1]);
+ case 1:
+ w_c0_perfctrl0(0);
+ w_c0_perfcntr0(reg.counter[0]);
+ }
+}
+
+/* Start all counters on current CPU */
+static void mipsxx_cpu_start(void *args)
+{
+ unsigned int counters = op_model_mipsxx_ops.num_counters;
+
+ switch (counters) {
+ case 4:
+ w_c0_perfctrl3(WHAT | reg.control[3]);
+ case 3:
+ w_c0_perfctrl2(WHAT | reg.control[2]);
+ case 2:
+ w_c0_perfctrl1(WHAT | reg.control[1]);
+ case 1:
+ w_c0_perfctrl0(WHAT | reg.control[0]);
+ }
+}
+
+/* Stop all counters on current CPU */
+static void mipsxx_cpu_stop(void *args)
+{
+ unsigned int counters = op_model_mipsxx_ops.num_counters;
+
+ switch (counters) {
+ case 4:
+ w_c0_perfctrl3(0);
+ case 3:
+ w_c0_perfctrl2(0);
+ case 2:
+ w_c0_perfctrl1(0);
+ case 1:
+ w_c0_perfctrl0(0);
+ }
+}
+
+static int mipsxx_perfcount_handler(void)
+{
+ unsigned int counters = op_model_mipsxx_ops.num_counters;
+ unsigned int control;
+ unsigned int counter;
+ int handled = IRQ_NONE;
+
+ if (cpu_has_mips_r2 && !(read_c0_cause() & (1 << 26)))
+ return handled;
+
+ switch (counters) {
+#define HANDLE_COUNTER(n) \
+ case n + 1: \
+ control = r_c0_perfctrl ## n(); \
+ counter = r_c0_perfcntr ## n(); \
+ if ((control & M_PERFCTL_INTERRUPT_ENABLE) && \
+ (counter & M_COUNTER_OVERFLOW)) { \
+ oprofile_add_sample(get_irq_regs(), n); \
+ w_c0_perfcntr ## n(reg.counter[n]); \
+ handled = IRQ_HANDLED; \
+ }
+ HANDLE_COUNTER(3)
+ HANDLE_COUNTER(2)
+ HANDLE_COUNTER(1)
+ HANDLE_COUNTER(0)
+ }
+
+ return handled;
+}
+
+#define M_CONFIG1_PC (1 << 4)
+
+static inline int __n_counters(void)
+{
+ if (!(read_c0_config1() & M_CONFIG1_PC))
+ return 0;
+ if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+ return 1;
+ if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+ return 2;
+ if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+ return 3;
+
+ return 4;
+}
+
+static inline int n_counters(void)
+{
+ int counters;
+
+ switch (current_cpu_type()) {
+ case CPU_R10000:
+ counters = 2;
+ break;
+
+ case CPU_R12000:
+ case CPU_R14000:
+ counters = 4;
+ break;
+
+ default:
+ counters = __n_counters();
+ }
+
+ return counters;
+}
+
+static void reset_counters(void *arg)
+{
+ int counters = (int)(long)arg;
+ switch (counters) {
+ case 4:
+ w_c0_perfctrl3(0);
+ w_c0_perfcntr3(0);
+ case 3:
+ w_c0_perfctrl2(0);
+ w_c0_perfcntr2(0);
+ case 2:
+ w_c0_perfctrl1(0);
+ w_c0_perfcntr1(0);
+ case 1:
+ w_c0_perfctrl0(0);
+ w_c0_perfcntr0(0);
+ }
+}
+
+static int __init mipsxx_init(void)
+{
+ int counters;
+
+ counters = n_counters();
+ if (counters == 0) {
+ printk(KERN_ERR "Oprofile: CPU has no performance counters\n");
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_MIPS_MT_SMP
+ cpu_has_mipsmt_pertccounters = read_c0_config7() & (1<<19);
+ if (!cpu_has_mipsmt_pertccounters)
+ counters = counters_total_to_per_cpu(counters);
+#endif
+ on_each_cpu(reset_counters, (void *)(long)counters, 1);
+
+ op_model_mipsxx_ops.num_counters = counters;
+ switch (current_cpu_type()) {
+ case CPU_20KC:
+ op_model_mipsxx_ops.cpu_type = "mips/20K";
+ break;
+
+ case CPU_24K:
+ op_model_mipsxx_ops.cpu_type = "mips/24K";
+ break;
+
+ case CPU_25KF:
+ op_model_mipsxx_ops.cpu_type = "mips/25K";
+ break;
+
+ case CPU_1004K:
+#if 0
+ /* FIXME: report as 34K for now */
+ op_model_mipsxx_ops.cpu_type = "mips/1004K";
+ break;
+#endif
+
+ case CPU_34K:
+ op_model_mipsxx_ops.cpu_type = "mips/34K";
+ break;
+
+ case CPU_74K:
+ op_model_mipsxx_ops.cpu_type = "mips/74K";
+ break;
+
+ case CPU_5KC:
+ op_model_mipsxx_ops.cpu_type = "mips/5K";
+ break;
+
+ case CPU_R10000:
+ if ((current_cpu_data.processor_id & 0xff) == 0x20)
+ op_model_mipsxx_ops.cpu_type = "mips/r10000-v2.x";
+ else
+ op_model_mipsxx_ops.cpu_type = "mips/r10000";
+ break;
+
+ case CPU_R12000:
+ case CPU_R14000:
+ op_model_mipsxx_ops.cpu_type = "mips/r12000";
+ break;
+
+ case CPU_SB1:
+ case CPU_SB1A:
+ op_model_mipsxx_ops.cpu_type = "mips/sb1";
+ break;
+
+ default:
+ printk(KERN_ERR "Profiling unsupported for this CPU\n");
+
+ return -ENODEV;
+ }
+
+ save_perf_irq = perf_irq;
+ perf_irq = mipsxx_perfcount_handler;
+
+ return 0;
+}
+
+static void mipsxx_exit(void)
+{
+ int counters = op_model_mipsxx_ops.num_counters;
+
+ counters = counters_per_cpu_to_total(counters);
+ on_each_cpu(reset_counters, (void *)(long)counters, 1);
+
+ perf_irq = save_perf_irq;
+}
+
+struct op_mips_model op_model_mipsxx_ops = {
+ .reg_setup = mipsxx_reg_setup,
+ .cpu_setup = mipsxx_cpu_setup,
+ .init = mipsxx_init,
+ .exit = mipsxx_exit,
+ .cpu_start = mipsxx_cpu_start,
+ .cpu_stop = mipsxx_cpu_stop,
+};
diff --git a/target/linux/realtek/files/arch/rlx/oprofile/op_model_rm9000.c b/target/linux/realtek/files/arch/rlx/oprofile/op_model_rm9000.c
new file mode 100644
index 000000000..3aa813849
--- /dev/null
+++ b/target/linux/realtek/files/arch/rlx/oprofile/op_model_rm9000.c
@@ -0,0 +1,138 @@
+/*
+ * 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/init.h>
+#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)
+{
+ unsigned int control = read_c0_perfcontrol();
+ struct pt_regs *regs = get_irq_regs();
+ 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 __init 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_ops = {
+ .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
+};