summaryrefslogtreecommitdiffstats
path: root/arch/mips/oprofile/op_model_loongson3.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-12-11 17:56:37 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-11 17:56:37 -0800
commitc0222ac086669a631814bbf857f8c8023452a4d7 (patch)
treebb1d9908031fcf69016eeefa7b35a4f68f414333 /arch/mips/oprofile/op_model_loongson3.c
parent140cd7fb04a4a2bc09a30980bc8104cc89e09330 (diff)
parente2965cd0003f222bd49f67907c2bc6ed691c6d20 (diff)
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS updates from Ralf Baechle: "This is an unusually large pull request for MIPS - in parts because lots of patches missed the 3.18 deadline but primarily because some folks opened the flood gates. - Retire the MIPS-specific phys_t with the generic phys_addr_t. - Improvments for the backtrace code used by oprofile. - Better backtraces on SMP systems. - Cleanups for the Octeon platform code. - Cleanups and fixes for the Loongson platform code. - Cleanups and fixes to the firmware library. - Switch ATH79 platform to use the firmware library. - Grand overhault to the SEAD3 and Malta interrupt code. - Move the GIC interrupt code to drivers/irqchip - Lots of GIC cleanups and updates to the GIC code to use modern IRQ infrastructures and features of the kernel. - OF documentation updates for the GIC bindings - Move GIC clocksource driver to drivers/clocksource - Merge GIC clocksource driver with clockevent driver. - Further updates to bring the GIC clocksource driver up to date. - R3000 TLB code cleanups - Improvments to the Loongson 3 platform code. - Convert pr_warning to pr_warn. - Merge a bunch of small lantiq and ralink fixes that have been staged/lingering inside the openwrt tree for a while. - Update archhelp for IP22/IP32 - Fix a number of issues for Loongson 1B. - New clocksource and clockevent driver for Loongson 1B. - Further work on clk handling for Loongson 1B. - Platform work for Broadcom BMIPS. - Error handling cleanups for TurboChannel. - Fixes and optimization to the microMIPS support. - Option to disable the FTLB. - Dump more relevant information on machine check exception - Change binfmt to allow arch to examine PT_*PROC headers - Support for new style FPU register model in O32 - VDSO randomization. - BCM47xx cleanups - BCM47xx reimplement the way the kernel accesses NVRAM information. - Random cleanups - Add support for ATH25 platforms - Remove pointless locking code in some PCI platforms. - Some improvments to EVA support - Minor Alchemy cleanup" * 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (185 commits) MIPS: Add MFHC0 and MTHC0 instructions to uasm. MIPS: Cosmetic cleanups of page table headers. MIPS: Add CP0 macros for extended EntryLo registers MIPS: Remove now unused definition of phys_t. MIPS: Replace use of phys_t with phys_addr_t. MIPS: Replace MIPS-specific 64BIT_PHYS_ADDR with generic PHYS_ADDR_T_64BIT PCMCIA: Alchemy Don't select 64BIT_PHYS_ADDR in Kconfig. MIPS: lib: memset: Clean up some MIPS{EL,EB} ifdefery MIPS: iomap: Use __mem_{read,write}{b,w,l} for MMIO MIPS: <asm/types.h> fix indentation. MAINTAINERS: Add entry for BMIPS multiplatform kernel MIPS: Enable VDSO randomization MIPS: Remove a temporary hack for debugging cache flushes in SMTC configuration MIPS: Remove declaration of obsolete arch_init_clk_ops() MIPS: atomic.h: Reformat to fit in 79 columns MIPS: Apply `.insn' to fixup labels throughout MIPS: Fix microMIPS LL/SC immediate offsets MIPS: Kconfig: Only allow 32-bit microMIPS builds MIPS: signal.c: Fix an invalid cast in ISA mode bit handling MIPS: mm: Only build one microassembler that is suitable ...
Diffstat (limited to 'arch/mips/oprofile/op_model_loongson3.c')
-rw-r--r--arch/mips/oprofile/op_model_loongson3.c220
1 files changed, 220 insertions, 0 deletions
diff --git a/arch/mips/oprofile/op_model_loongson3.c b/arch/mips/oprofile/op_model_loongson3.c
new file mode 100644
index 00000000000..8bcf7fc40f0
--- /dev/null
+++ b/arch/mips/oprofile/op_model_loongson3.c
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/proc_fs.h>
+#include <linux/oprofile.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <irq.h>
+#include <loongson.h>
+#include "op_impl.h"
+
+#define LOONGSON3_PERFCNT_OVERFLOW (1ULL << 63)
+
+#define LOONGSON3_PERFCTRL_EXL (1UL << 0)
+#define LOONGSON3_PERFCTRL_KERNEL (1UL << 1)
+#define LOONGSON3_PERFCTRL_SUPERVISOR (1UL << 2)
+#define LOONGSON3_PERFCTRL_USER (1UL << 3)
+#define LOONGSON3_PERFCTRL_ENABLE (1UL << 4)
+#define LOONGSON3_PERFCTRL_W (1UL << 30)
+#define LOONGSON3_PERFCTRL_M (1UL << 31)
+#define LOONGSON3_PERFCTRL_EVENT(idx, event) \
+ (((event) & (idx ? 0x0f : 0x3f)) << 5)
+
+/* Loongson-3 PerfCount performance counter1 register */
+#define read_c0_perflo1() __read_64bit_c0_register($25, 0)
+#define write_c0_perflo1(val) __write_64bit_c0_register($25, 0, val)
+#define read_c0_perfhi1() __read_64bit_c0_register($25, 1)
+#define write_c0_perfhi1(val) __write_64bit_c0_register($25, 1, val)
+
+/* Loongson-3 PerfCount performance counter2 register */
+#define read_c0_perflo2() __read_64bit_c0_register($25, 2)
+#define write_c0_perflo2(val) __write_64bit_c0_register($25, 2, val)
+#define read_c0_perfhi2() __read_64bit_c0_register($25, 3)
+#define write_c0_perfhi2(val) __write_64bit_c0_register($25, 3, val)
+
+static int (*save_perf_irq)(void);
+
+static struct loongson3_register_config {
+ unsigned int control1;
+ unsigned int control2;
+ unsigned long long reset_counter1;
+ unsigned long long reset_counter2;
+ int ctr1_enable, ctr2_enable;
+} reg;
+
+static void reset_counters(void *arg)
+{
+ write_c0_perfhi1(0);
+ write_c0_perfhi2(0);
+ write_c0_perflo1(0xc0000000);
+ write_c0_perflo2(0x40000000);
+}
+
+/* Compute all of the registers in preparation for enabling profiling. */
+static void loongson3_reg_setup(struct op_counter_config *ctr)
+{
+ unsigned int control1 = 0;
+ unsigned int control2 = 0;
+
+ reg.reset_counter1 = 0;
+ reg.reset_counter2 = 0;
+ /* Compute the performance counter control word. */
+ /* For now count kernel and user mode */
+ if (ctr[0].enabled) {
+ control1 |= LOONGSON3_PERFCTRL_EVENT(0, ctr[0].event) |
+ LOONGSON3_PERFCTRL_ENABLE;
+ if (ctr[0].kernel)
+ control1 |= LOONGSON3_PERFCTRL_KERNEL;
+ if (ctr[0].user)
+ control1 |= LOONGSON3_PERFCTRL_USER;
+ reg.reset_counter1 = 0x8000000000000000ULL - ctr[0].count;
+ }
+
+ if (ctr[1].enabled) {
+ control2 |= LOONGSON3_PERFCTRL_EVENT(1, ctr[1].event) |
+ LOONGSON3_PERFCTRL_ENABLE;
+ if (ctr[1].kernel)
+ control2 |= LOONGSON3_PERFCTRL_KERNEL;
+ if (ctr[1].user)
+ control2 |= LOONGSON3_PERFCTRL_USER;
+ reg.reset_counter2 = 0x8000000000000000ULL - ctr[1].count;
+ }
+
+ if (ctr[0].enabled)
+ control1 |= LOONGSON3_PERFCTRL_EXL;
+ if (ctr[1].enabled)
+ control2 |= LOONGSON3_PERFCTRL_EXL;
+
+ reg.control1 = control1;
+ reg.control2 = control2;
+ reg.ctr1_enable = ctr[0].enabled;
+ reg.ctr2_enable = ctr[1].enabled;
+}
+
+/* Program all of the registers in preparation for enabling profiling. */
+static void loongson3_cpu_setup(void *args)
+{
+ uint64_t perfcount1, perfcount2;
+
+ perfcount1 = reg.reset_counter1;
+ perfcount2 = reg.reset_counter2;
+ write_c0_perfhi1(perfcount1);
+ write_c0_perfhi2(perfcount2);
+}
+
+static void loongson3_cpu_start(void *args)
+{
+ /* Start all counters on current CPU */
+ reg.control1 |= (LOONGSON3_PERFCTRL_W|LOONGSON3_PERFCTRL_M);
+ reg.control2 |= (LOONGSON3_PERFCTRL_W|LOONGSON3_PERFCTRL_M);
+
+ if (reg.ctr1_enable)
+ write_c0_perflo1(reg.control1);
+ if (reg.ctr2_enable)
+ write_c0_perflo2(reg.control2);
+}
+
+static void loongson3_cpu_stop(void *args)
+{
+ /* Stop all counters on current CPU */
+ write_c0_perflo1(0xc0000000);
+ write_c0_perflo2(0x40000000);
+ memset(&reg, 0, sizeof(reg));
+}
+
+static int loongson3_perfcount_handler(void)
+{
+ unsigned long flags;
+ uint64_t counter1, counter2;
+ uint32_t cause, handled = IRQ_NONE;
+ struct pt_regs *regs = get_irq_regs();
+
+ cause = read_c0_cause();
+ if (!(cause & CAUSEF_PCI))
+ return handled;
+
+ counter1 = read_c0_perfhi1();
+ counter2 = read_c0_perfhi2();
+
+ local_irq_save(flags);
+
+ if (counter1 & LOONGSON3_PERFCNT_OVERFLOW) {
+ if (reg.ctr1_enable)
+ oprofile_add_sample(regs, 0);
+ counter1 = reg.reset_counter1;
+ }
+ if (counter2 & LOONGSON3_PERFCNT_OVERFLOW) {
+ if (reg.ctr2_enable)
+ oprofile_add_sample(regs, 1);
+ counter2 = reg.reset_counter2;
+ }
+
+ local_irq_restore(flags);
+
+ write_c0_perfhi1(counter1);
+ write_c0_perfhi2(counter2);
+
+ if (!(cause & CAUSEF_TI))
+ handled = IRQ_HANDLED;
+
+ return handled;
+}
+
+static int loongson3_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ switch (action) {
+ case CPU_STARTING:
+ case CPU_STARTING_FROZEN:
+ write_c0_perflo1(reg.control1);
+ write_c0_perflo2(reg.control2);
+ break;
+ case CPU_DYING:
+ case CPU_DYING_FROZEN:
+ write_c0_perflo1(0xc0000000);
+ write_c0_perflo2(0x40000000);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block loongson3_notifier_block = {
+ .notifier_call = loongson3_cpu_callback
+};
+
+static int __init loongson3_init(void)
+{
+ on_each_cpu(reset_counters, NULL, 1);
+ register_hotcpu_notifier(&loongson3_notifier_block);
+ save_perf_irq = perf_irq;
+ perf_irq = loongson3_perfcount_handler;
+
+ return 0;
+}
+
+static void loongson3_exit(void)
+{
+ on_each_cpu(reset_counters, NULL, 1);
+ unregister_hotcpu_notifier(&loongson3_notifier_block);
+ perf_irq = save_perf_irq;
+}
+
+struct op_mips_model op_model_loongson3_ops = {
+ .reg_setup = loongson3_reg_setup,
+ .cpu_setup = loongson3_cpu_setup,
+ .init = loongson3_init,
+ .exit = loongson3_exit,
+ .cpu_start = loongson3_cpu_start,
+ .cpu_stop = loongson3_cpu_stop,
+ .cpu_type = "mips/loongson3",
+ .num_counters = 2
+};