aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWu Zhangjin <wuzj@lemote.com>2009-07-02 11:25:46 -0400
committerRalf Baechle <ralf@linux-mips.org>2009-09-17 14:07:47 -0400
commit67b35e5d01aba7a83f2161b0c90acb08afa01e3e (patch)
treeb85665bc0611c07d26299c20424332cc8899133d
parent92d1b63dbae0865bee5317ca6b65e0b304dc590d (diff)
MIPS: Loongson: Add oprofile support
This kernel support is needed by the user-space tool:oprofile to profile linux kernel or applications via loongson2 performance counters. you can enable this driver via CONFIG_OPROFILE = y or m. On Loongson2 there are two performance counters, each one can count 16 events respectively. when anyone of the performance counter overflows, an interrupt will be generated and is routed to the IRQ MIPS_CPU_IRQ_BASE + 6. Signed-off-by: Yanhua <yanh@lemote.com> Signed-off-by: Wu Zhangjin <wuzj@lemote.com> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--arch/mips/include/asm/mach-lemote/loongson.h3
-rw-r--r--arch/mips/lemote/lm2e/irq.c2
-rw-r--r--arch/mips/oprofile/Makefile1
-rw-r--r--arch/mips/oprofile/common.c4
-rw-r--r--arch/mips/oprofile/op_model_loongson2.c177
5 files changed, 187 insertions, 0 deletions
diff --git a/arch/mips/include/asm/mach-lemote/loongson.h b/arch/mips/include/asm/mach-lemote/loongson.h
index 916eacec94ec..95ee4c8f2d56 100644
--- a/arch/mips/include/asm/mach-lemote/loongson.h
+++ b/arch/mips/include/asm/mach-lemote/loongson.h
@@ -50,4 +50,7 @@ extern void __init prom_init_env(void);
50#define LOONGSON_PXARB_CFG BONITO(BONITO_REGBASE + 0x68) 50#define LOONGSON_PXARB_CFG BONITO(BONITO_REGBASE + 0x68)
51#define LOONGSON_PXARB_STATUS BONITO(BONITO_REGBASE + 0x6c) 51#define LOONGSON_PXARB_STATUS BONITO(BONITO_REGBASE + 0x6c)
52 52
53/* loongson2-specific perf counter IRQ */
54#define LOONGSON2_PERFCNT_IRQ (MIPS_CPU_IRQ_BASE + 6)
55
53#endif /* __ASM_MACH_LOONGSON_LOONGSON_H */ 56#endif /* __ASM_MACH_LOONGSON_LOONGSON_H */
diff --git a/arch/mips/lemote/lm2e/irq.c b/arch/mips/lemote/lm2e/irq.c
index fb7643a25618..9585f5aa7cce 100644
--- a/arch/mips/lemote/lm2e/irq.c
+++ b/arch/mips/lemote/lm2e/irq.c
@@ -58,6 +58,8 @@ asmlinkage void plat_irq_dispatch(void)
58 58
59 if (pending & CAUSEF_IP7) 59 if (pending & CAUSEF_IP7)
60 do_IRQ(MIPS_CPU_IRQ_BASE + 7); 60 do_IRQ(MIPS_CPU_IRQ_BASE + 7);
61 else if (pending & CAUSEF_IP6) /* perf counter loverflow */
62 do_IRQ(LOONGSON2_PERFCNT_IRQ);
61 else if (pending & CAUSEF_IP5) 63 else if (pending & CAUSEF_IP5)
62 i8259_irqdispatch(); 64 i8259_irqdispatch();
63 else if (pending & CAUSEF_IP2) 65 else if (pending & CAUSEF_IP2)
diff --git a/arch/mips/oprofile/Makefile b/arch/mips/oprofile/Makefile
index bf3be6fcf7ff..02cc65e52d11 100644
--- a/arch/mips/oprofile/Makefile
+++ b/arch/mips/oprofile/Makefile
@@ -15,3 +15,4 @@ oprofile-$(CONFIG_CPU_MIPS64) += op_model_mipsxx.o
15oprofile-$(CONFIG_CPU_R10000) += op_model_mipsxx.o 15oprofile-$(CONFIG_CPU_R10000) += op_model_mipsxx.o
16oprofile-$(CONFIG_CPU_SB1) += op_model_mipsxx.o 16oprofile-$(CONFIG_CPU_SB1) += op_model_mipsxx.o
17oprofile-$(CONFIG_CPU_RM9000) += op_model_rm9000.o 17oprofile-$(CONFIG_CPU_RM9000) += op_model_rm9000.o
18oprofile-$(CONFIG_CPU_LOONGSON2) += op_model_loongson2.o
diff --git a/arch/mips/oprofile/common.c b/arch/mips/oprofile/common.c
index 3bf3354547f6..7832ad257a14 100644
--- a/arch/mips/oprofile/common.c
+++ b/arch/mips/oprofile/common.c
@@ -16,6 +16,7 @@
16 16
17extern struct op_mips_model op_model_mipsxx_ops __attribute__((weak)); 17extern struct op_mips_model op_model_mipsxx_ops __attribute__((weak));
18extern struct op_mips_model op_model_rm9000_ops __attribute__((weak)); 18extern struct op_mips_model op_model_rm9000_ops __attribute__((weak));
19extern struct op_mips_model op_model_loongson2_ops __attribute__((weak));
19 20
20static struct op_mips_model *model; 21static struct op_mips_model *model;
21 22
@@ -93,6 +94,9 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
93 case CPU_RM9000: 94 case CPU_RM9000:
94 lmodel = &op_model_rm9000_ops; 95 lmodel = &op_model_rm9000_ops;
95 break; 96 break;
97 case CPU_LOONGSON2:
98 lmodel = &op_model_loongson2_ops;
99 break;
96 }; 100 };
97 101
98 if (!lmodel) 102 if (!lmodel)
diff --git a/arch/mips/oprofile/op_model_loongson2.c b/arch/mips/oprofile/op_model_loongson2.c
new file mode 100644
index 000000000000..655cb8dec340
--- /dev/null
+++ b/arch/mips/oprofile/op_model_loongson2.c
@@ -0,0 +1,177 @@
1/*
2 * Loongson2 performance counter driver for oprofile
3 *
4 * Copyright (C) 2009 Lemote Inc. & Insititute of Computing Technology
5 * Author: Yanhua <yanh@lemote.com>
6 * Author: Wu Zhangjin <wuzj@lemote.com>
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 *
12 */
13#include <linux/init.h>
14#include <linux/oprofile.h>
15#include <linux/interrupt.h>
16
17#include <loongson.h> /* LOONGSON2_PERFCNT_IRQ */
18#include "op_impl.h"
19
20/*
21 * a patch should be sent to oprofile with the loongson-specific support.
22 * otherwise, the oprofile tool will not recognize this and complain about
23 * "cpu_type 'unset' is not valid".
24 */
25#define LOONGSON2_CPU_TYPE "mips/godson2"
26
27#define LOONGSON2_COUNTER1_EVENT(event) ((event & 0x0f) << 5)
28#define LOONGSON2_COUNTER2_EVENT(event) ((event & 0x0f) << 9)
29
30#define LOONGSON2_PERFCNT_EXL (1UL << 0)
31#define LOONGSON2_PERFCNT_KERNEL (1UL << 1)
32#define LOONGSON2_PERFCNT_SUPERVISOR (1UL << 2)
33#define LOONGSON2_PERFCNT_USER (1UL << 3)
34#define LOONGSON2_PERFCNT_INT_EN (1UL << 4)
35#define LOONGSON2_PERFCNT_OVERFLOW (1ULL << 31)
36
37/* Loongson2 performance counter register */
38#define read_c0_perfctrl() __read_64bit_c0_register($24, 0)
39#define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val)
40#define read_c0_perfcnt() __read_64bit_c0_register($25, 0)
41#define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val)
42
43static struct loongson2_register_config {
44 unsigned int ctrl;
45 unsigned long long reset_counter1;
46 unsigned long long reset_counter2;
47 int cnt1_enalbed, cnt2_enalbed;
48} reg;
49
50DEFINE_SPINLOCK(sample_lock);
51
52static char *oprofid = "LoongsonPerf";
53static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id);
54/* Compute all of the registers in preparation for enabling profiling. */
55
56static void loongson2_reg_setup(struct op_counter_config *cfg)
57{
58 unsigned int ctrl = 0;
59
60 reg.reset_counter1 = 0;
61 reg.reset_counter2 = 0;
62 /* Compute the performance counter ctrl word. */
63 /* For now count kernel and user mode */
64 if (cfg[0].enabled) {
65 ctrl |= LOONGSON2_COUNTER1_EVENT(cfg[0].event);
66 reg.reset_counter1 = 0x80000000ULL - cfg[0].count;
67 }
68
69 if (cfg[1].enabled) {
70 ctrl |= LOONGSON2_COUNTER2_EVENT(cfg[1].event);
71 reg.reset_counter2 = (0x80000000ULL - cfg[1].count);
72 }
73
74 if (cfg[0].enabled || cfg[1].enabled) {
75 ctrl |= LOONGSON2_PERFCNT_EXL | LOONGSON2_PERFCNT_INT_EN;
76 if (cfg[0].kernel || cfg[1].kernel)
77 ctrl |= LOONGSON2_PERFCNT_KERNEL;
78 if (cfg[0].user || cfg[1].user)
79 ctrl |= LOONGSON2_PERFCNT_USER;
80 }
81
82 reg.ctrl = ctrl;
83
84 reg.cnt1_enalbed = cfg[0].enabled;
85 reg.cnt2_enalbed = cfg[1].enabled;
86
87}
88
89/* Program all of the registers in preparation for enabling profiling. */
90
91static void loongson2_cpu_setup(void *args)
92{
93 uint64_t perfcount;
94
95 perfcount = (reg.reset_counter2 << 32) | reg.reset_counter1;
96 write_c0_perfcnt(perfcount);
97}
98
99static void loongson2_cpu_start(void *args)
100{
101 /* Start all counters on current CPU */
102 if (reg.cnt1_enalbed || reg.cnt2_enalbed)
103 write_c0_perfctrl(reg.ctrl);
104}
105
106static void loongson2_cpu_stop(void *args)
107{
108 /* Stop all counters on current CPU */
109 write_c0_perfctrl(0);
110 memset(&reg, 0, sizeof(reg));
111}
112
113static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id)
114{
115 uint64_t counter, counter1, counter2;
116 struct pt_regs *regs = get_irq_regs();
117 int enabled;
118 unsigned long flags;
119
120 /*
121 * LOONGSON2 defines two 32-bit performance counters.
122 * To avoid a race updating the registers we need to stop the counters
123 * while we're messing with
124 * them ...
125 */
126
127 /* Check whether the irq belongs to me */
128 enabled = reg.cnt1_enalbed | reg.cnt2_enalbed;
129 if (!enabled)
130 return IRQ_NONE;
131
132 counter = read_c0_perfcnt();
133 counter1 = counter & 0xffffffff;
134 counter2 = counter >> 32;
135
136 spin_lock_irqsave(&sample_lock, flags);
137
138 if (counter1 & LOONGSON2_PERFCNT_OVERFLOW) {
139 if (reg.cnt1_enalbed)
140 oprofile_add_sample(regs, 0);
141 counter1 = reg.reset_counter1;
142 }
143 if (counter2 & LOONGSON2_PERFCNT_OVERFLOW) {
144 if (reg.cnt2_enalbed)
145 oprofile_add_sample(regs, 1);
146 counter2 = reg.reset_counter2;
147 }
148
149 spin_unlock_irqrestore(&sample_lock, flags);
150
151 write_c0_perfcnt((counter2 << 32) | counter1);
152
153 return IRQ_HANDLED;
154}
155
156static int __init loongson2_init(void)
157{
158 return request_irq(LOONGSON2_PERFCNT_IRQ, loongson2_perfcount_handler,
159 IRQF_SHARED, "Perfcounter", oprofid);
160}
161
162static void loongson2_exit(void)
163{
164 write_c0_perfctrl(0);
165 free_irq(LOONGSON2_PERFCNT_IRQ, oprofid);
166}
167
168struct op_mips_model op_model_loongson2_ops = {
169 .reg_setup = loongson2_reg_setup,
170 .cpu_setup = loongson2_cpu_setup,
171 .init = loongson2_init,
172 .exit = loongson2_exit,
173 .cpu_start = loongson2_cpu_start,
174 .cpu_stop = loongson2_cpu_stop,
175 .cpu_type = LOONGSON2_CPU_TYPE,
176 .num_counters = 2
177};