aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c')
-rw-r--r--target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c b/target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c
new file mode 100644
index 000000000..aeac3c666
--- /dev/null
+++ b/target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c
@@ -0,0 +1,221 @@
+/*
+ * arch/ubicom32/oprofile/profile.c
+ * Oprofile support for arch Ubicom32
+ *
+ * (C) Copyright 2009, Ubicom, Inc.
+ *
+ * This file is part of the Ubicom32 Linux Kernel Port.
+ *
+ * The Ubicom32 Linux Kernel Port is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 2 of the
+ * License, or (at your option)
+ * any later version.
+ *
+ * The Ubicom32 Linux Kernel Port is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the Ubicom32 Linux Kernel Port. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Ubicom32 implementation derived from (with many thanks):
+ * arch/m68knommu
+ * arch/blackfin
+ * arch/parisc
+ */
+
+/**
+ * @file profile.c
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Hunyue Yau <hy@hy-research.com>
+ */
+
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <asm/devtree.h>
+#include <asm/thread.h>
+
+/* For identifying userland vs kernel address */
+#include <asm/stacktrace.h>
+#include "ipProf.h"
+
+/* For communications with the backend */
+static struct profilenode *profile_node;
+
+/* Bitmask containing all Linux threads - as seen by the ROSR reg */
+static unsigned long th_all_mask;
+
+/* Lookup table to translate a hardware thread into a CPU identifier
+ * Table is indexed by the ROSR value which is assumed to be
+ * relatively small (0...15).
+ */
+unsigned int cpu_map[THREAD_ARCHITECTURAL_MAX];
+
+static struct pt_regs regs;
+
+/*
+ * For each sample returned, checked to see if they are relevant to
+ * us. This is necessary as the ubicom32 architecture has other software
+ * running outside of Linux. Only then, put the sample into the relevant
+ * cpu bins.
+ *
+ * To minimize overhead, a global mask with all possible threads of in
+ * interest to us is used as a first check. Then a second mask identifying
+ * the thread is used to obtain an identifier for that "CPU".
+ */
+
+/*
+ * ubicom32_build_cpu_th_mask()
+ *
+ * Build a lookup table for translation between hardware thread
+ * "ROSR" values and Linux CPU ids
+ *
+ * *** This gets executed on all CPUs at once! ***
+ */
+static void ubicom32_build_cpu_th_mask(void *mask)
+{
+ thread_t self = thread_get_self();
+ unsigned long *th_m = mask;
+
+ BUG_ON(self <= 0 || self >= THREAD_ARCHITECTURAL_MAX);
+ cpu_map[self] = smp_processor_id();
+
+ set_bit(self, th_m);
+}
+
+/*
+ * profile_interrupt()
+ *
+ * Process samples returned from the profiler backend. The backend
+ * may return samples that are irrelevant to us or may even return
+ * multiple samples for the same CPU. Note that the sames may be
+ * for ANY cpu. At this time, this is unique and to support this requires
+ * Oprofile to expose an interface to accept the CPU that the same came
+ * frome.
+ */
+static irqreturn_t profile_interrupt(int irq, void *arg)
+{
+ int i, buf_entry;
+ int is_kernel;
+ unsigned int bit_th;
+ unsigned int th;
+
+ if (!(profile_node->enabled) || profile_node->count < 0) {
+ printk(KERN_WARNING
+ "Unexpected interrupt, no samples or not enabled!\n");
+ return IRQ_HANDLED;
+ }
+
+ profile_node->busy = 1; /* Keep backend out */
+
+ for (i = 0; i < profile_node->count; i++) {
+ buf_entry = profile_node->tail;
+ profile_node->tail++;
+ profile_node->tail %= IPPROFILETIO_MAX_SAMPLES;
+
+ /* Note - the "thread" ID is only the lower 4 bits */
+ th = (0x0f & profile_node->samples[buf_entry].thread);
+ bit_th = (1 << th);
+
+ if ((bit_th & th_all_mask) == 0)
+ continue;
+
+ regs.pc = profile_node->samples[buf_entry].pc;
+
+ is_kernel = ubicom32_is_kernel(regs.pc);
+
+ oprofile_add_ext_sample_cpu(regs.pc, &regs, 0, is_kernel,
+ cpu_map[th]);
+ }
+ profile_node->count = 0;
+ profile_node->busy = 0;
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * profile_start()
+ *
+ * Notification from oprofile to start the profiler
+ */
+static int profile_start(void)
+{
+ if (!profile_node)
+ return -1;
+
+ profile_node->enabled = 1;
+
+ return 0;
+}
+
+/*
+ * profile_stop()
+ *
+ * Notification from oprofile to stop the profiler
+ */
+static void profile_stop(void)
+{
+ if (profile_node)
+ profile_node->enabled = 0;
+}
+
+/*
+ * oprofile_arch_init()
+ *
+ * Attach to Oprofile after qualify the availability of the backend
+ * profiler support.
+ */
+int __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+ int r = -ENODEV;
+
+ profile_node = (struct profilenode *)devtree_find_node("profiler");
+
+ if (profile_node == NULL) {
+ printk(KERN_WARNING "Cannot find profiler node\n");
+ return r;
+ }
+
+ r = request_irq(profile_node->dn.recvirq, profile_interrupt,
+ IRQF_DISABLED, "profiler", NULL);
+
+ if (r < 0) {
+ profile_node = NULL;
+ printk(KERN_WARNING "Cannot get profiler IRQ\n");
+ return r;
+ }
+
+ ops->start = profile_start;
+ ops->stop = profile_stop;
+ ops->cpu_type = "timer";
+
+ memset(cpu_map, 0, sizeof(cpu_map));
+
+ on_each_cpu(ubicom32_build_cpu_th_mask, &th_all_mask, 1);
+
+ memset(&regs, 0, sizeof(regs));
+
+ return r;
+}
+
+/*
+ * oprofile_arch_exit()
+ *
+ * External call to take outselves out.
+ * Make sure backend is not running.
+ */
+void oprofile_arch_exit(void)
+{
+ BUG_ON(profile_node->enabled);
+}