Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: Loongson-3: Add oprofile support

Loongson-3 has two groups of performance counters, they are 4 sub-
registers of CP0's REG25. This patch add oprofile support.

REG25, sel 0: Perf Control of group 0;
REG25, sel 1: Perf Counter of group 0;
REG25, sel 2: Perf Control of group 1;
REG25, sel 3: Perf Counter of group 1.

Signed-off-by: Huacai Chen <chenhc@lemote.com>
Cc: John Crispin <john@phrozen.org>
Cc: Steven J. Hill <Steven.Hill@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: Fuxin Zhang <zhangfx@lemote.com>
Cc: Zhangjin Wu <wuzhangjin@gmail.com>
Patchwork: https://patchwork.linux-mips.org/patch/8328/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Huacai Chen and committed by
Ralf Baechle
89467e73 3adeb256

+225
+1
arch/mips/oprofile/Makefile
··· 14 14 oprofile-$(CONFIG_CPU_SB1) += op_model_mipsxx.o 15 15 oprofile-$(CONFIG_CPU_XLR) += op_model_mipsxx.o 16 16 oprofile-$(CONFIG_CPU_LOONGSON2) += op_model_loongson2.o 17 + oprofile-$(CONFIG_CPU_LOONGSON3) += op_model_loongson3.o
+4
arch/mips/oprofile/common.c
··· 18 18 19 19 extern struct op_mips_model op_model_mipsxx_ops __weak; 20 20 extern struct op_mips_model op_model_loongson2_ops __weak; 21 + extern struct op_mips_model op_model_loongson3_ops __weak; 21 22 22 23 static struct op_mips_model *model; 23 24 ··· 104 103 105 104 case CPU_LOONGSON2: 106 105 lmodel = &op_model_loongson2_ops; 106 + break; 107 + case CPU_LOONGSON3: 108 + lmodel = &op_model_loongson3_ops; 107 109 break; 108 110 }; 109 111
+220
arch/mips/oprofile/op_model_loongson3.c
··· 1 + /* 2 + * This file is subject to the terms and conditions of the GNU General Public 3 + * License. See the file "COPYING" in the main directory of this archive 4 + * for more details. 5 + * 6 + */ 7 + #include <linux/init.h> 8 + #include <linux/cpu.h> 9 + #include <linux/smp.h> 10 + #include <linux/proc_fs.h> 11 + #include <linux/oprofile.h> 12 + #include <linux/spinlock.h> 13 + #include <linux/interrupt.h> 14 + #include <asm/uaccess.h> 15 + #include <irq.h> 16 + #include <loongson.h> 17 + #include "op_impl.h" 18 + 19 + #define LOONGSON3_PERFCNT_OVERFLOW (1ULL << 63) 20 + 21 + #define LOONGSON3_PERFCTRL_EXL (1UL << 0) 22 + #define LOONGSON3_PERFCTRL_KERNEL (1UL << 1) 23 + #define LOONGSON3_PERFCTRL_SUPERVISOR (1UL << 2) 24 + #define LOONGSON3_PERFCTRL_USER (1UL << 3) 25 + #define LOONGSON3_PERFCTRL_ENABLE (1UL << 4) 26 + #define LOONGSON3_PERFCTRL_W (1UL << 30) 27 + #define LOONGSON3_PERFCTRL_M (1UL << 31) 28 + #define LOONGSON3_PERFCTRL_EVENT(idx, event) \ 29 + (((event) & (idx ? 0x0f : 0x3f)) << 5) 30 + 31 + /* Loongson-3 PerfCount performance counter1 register */ 32 + #define read_c0_perflo1() __read_64bit_c0_register($25, 0) 33 + #define write_c0_perflo1(val) __write_64bit_c0_register($25, 0, val) 34 + #define read_c0_perfhi1() __read_64bit_c0_register($25, 1) 35 + #define write_c0_perfhi1(val) __write_64bit_c0_register($25, 1, val) 36 + 37 + /* Loongson-3 PerfCount performance counter2 register */ 38 + #define read_c0_perflo2() __read_64bit_c0_register($25, 2) 39 + #define write_c0_perflo2(val) __write_64bit_c0_register($25, 2, val) 40 + #define read_c0_perfhi2() __read_64bit_c0_register($25, 3) 41 + #define write_c0_perfhi2(val) __write_64bit_c0_register($25, 3, val) 42 + 43 + static int (*save_perf_irq)(void); 44 + 45 + static struct loongson3_register_config { 46 + unsigned int control1; 47 + unsigned int control2; 48 + unsigned long long reset_counter1; 49 + unsigned long long reset_counter2; 50 + int ctr1_enable, ctr2_enable; 51 + } reg; 52 + 53 + static void reset_counters(void *arg) 54 + { 55 + write_c0_perfhi1(0); 56 + write_c0_perfhi2(0); 57 + write_c0_perflo1(0xc0000000); 58 + write_c0_perflo2(0x40000000); 59 + } 60 + 61 + /* Compute all of the registers in preparation for enabling profiling. */ 62 + static void loongson3_reg_setup(struct op_counter_config *ctr) 63 + { 64 + unsigned int control1 = 0; 65 + unsigned int control2 = 0; 66 + 67 + reg.reset_counter1 = 0; 68 + reg.reset_counter2 = 0; 69 + /* Compute the performance counter control word. */ 70 + /* For now count kernel and user mode */ 71 + if (ctr[0].enabled) { 72 + control1 |= LOONGSON3_PERFCTRL_EVENT(0, ctr[0].event) | 73 + LOONGSON3_PERFCTRL_ENABLE; 74 + if (ctr[0].kernel) 75 + control1 |= LOONGSON3_PERFCTRL_KERNEL; 76 + if (ctr[0].user) 77 + control1 |= LOONGSON3_PERFCTRL_USER; 78 + reg.reset_counter1 = 0x8000000000000000ULL - ctr[0].count; 79 + } 80 + 81 + if (ctr[1].enabled) { 82 + control2 |= LOONGSON3_PERFCTRL_EVENT(1, ctr[1].event) | 83 + LOONGSON3_PERFCTRL_ENABLE; 84 + if (ctr[1].kernel) 85 + control2 |= LOONGSON3_PERFCTRL_KERNEL; 86 + if (ctr[1].user) 87 + control2 |= LOONGSON3_PERFCTRL_USER; 88 + reg.reset_counter2 = 0x8000000000000000ULL - ctr[1].count; 89 + } 90 + 91 + if (ctr[0].enabled) 92 + control1 |= LOONGSON3_PERFCTRL_EXL; 93 + if (ctr[1].enabled) 94 + control2 |= LOONGSON3_PERFCTRL_EXL; 95 + 96 + reg.control1 = control1; 97 + reg.control2 = control2; 98 + reg.ctr1_enable = ctr[0].enabled; 99 + reg.ctr2_enable = ctr[1].enabled; 100 + } 101 + 102 + /* Program all of the registers in preparation for enabling profiling. */ 103 + static void loongson3_cpu_setup(void *args) 104 + { 105 + uint64_t perfcount1, perfcount2; 106 + 107 + perfcount1 = reg.reset_counter1; 108 + perfcount2 = reg.reset_counter2; 109 + write_c0_perfhi1(perfcount1); 110 + write_c0_perfhi2(perfcount2); 111 + } 112 + 113 + static void loongson3_cpu_start(void *args) 114 + { 115 + /* Start all counters on current CPU */ 116 + reg.control1 |= (LOONGSON3_PERFCTRL_W|LOONGSON3_PERFCTRL_M); 117 + reg.control2 |= (LOONGSON3_PERFCTRL_W|LOONGSON3_PERFCTRL_M); 118 + 119 + if (reg.ctr1_enable) 120 + write_c0_perflo1(reg.control1); 121 + if (reg.ctr2_enable) 122 + write_c0_perflo2(reg.control2); 123 + } 124 + 125 + static void loongson3_cpu_stop(void *args) 126 + { 127 + /* Stop all counters on current CPU */ 128 + write_c0_perflo1(0xc0000000); 129 + write_c0_perflo2(0x40000000); 130 + memset(&reg, 0, sizeof(reg)); 131 + } 132 + 133 + static int loongson3_perfcount_handler(void) 134 + { 135 + unsigned long flags; 136 + uint64_t counter1, counter2; 137 + uint32_t cause, handled = IRQ_NONE; 138 + struct pt_regs *regs = get_irq_regs(); 139 + 140 + cause = read_c0_cause(); 141 + if (!(cause & CAUSEF_PCI)) 142 + return handled; 143 + 144 + counter1 = read_c0_perfhi1(); 145 + counter2 = read_c0_perfhi2(); 146 + 147 + local_irq_save(flags); 148 + 149 + if (counter1 & LOONGSON3_PERFCNT_OVERFLOW) { 150 + if (reg.ctr1_enable) 151 + oprofile_add_sample(regs, 0); 152 + counter1 = reg.reset_counter1; 153 + } 154 + if (counter2 & LOONGSON3_PERFCNT_OVERFLOW) { 155 + if (reg.ctr2_enable) 156 + oprofile_add_sample(regs, 1); 157 + counter2 = reg.reset_counter2; 158 + } 159 + 160 + local_irq_restore(flags); 161 + 162 + write_c0_perfhi1(counter1); 163 + write_c0_perfhi2(counter2); 164 + 165 + if (!(cause & CAUSEF_TI)) 166 + handled = IRQ_HANDLED; 167 + 168 + return handled; 169 + } 170 + 171 + static int loongson3_cpu_callback(struct notifier_block *nfb, 172 + unsigned long action, void *hcpu) 173 + { 174 + switch (action) { 175 + case CPU_STARTING: 176 + case CPU_STARTING_FROZEN: 177 + write_c0_perflo1(reg.control1); 178 + write_c0_perflo2(reg.control2); 179 + break; 180 + case CPU_DYING: 181 + case CPU_DYING_FROZEN: 182 + write_c0_perflo1(0xc0000000); 183 + write_c0_perflo2(0x40000000); 184 + break; 185 + } 186 + 187 + return NOTIFY_OK; 188 + } 189 + 190 + static struct notifier_block loongson3_notifier_block = { 191 + .notifier_call = loongson3_cpu_callback 192 + }; 193 + 194 + static int __init loongson3_init(void) 195 + { 196 + on_each_cpu(reset_counters, NULL, 1); 197 + register_hotcpu_notifier(&loongson3_notifier_block); 198 + save_perf_irq = perf_irq; 199 + perf_irq = loongson3_perfcount_handler; 200 + 201 + return 0; 202 + } 203 + 204 + static void loongson3_exit(void) 205 + { 206 + on_each_cpu(reset_counters, NULL, 1); 207 + unregister_hotcpu_notifier(&loongson3_notifier_block); 208 + perf_irq = save_perf_irq; 209 + } 210 + 211 + struct op_mips_model op_model_loongson3_ops = { 212 + .reg_setup = loongson3_reg_setup, 213 + .cpu_setup = loongson3_cpu_setup, 214 + .init = loongson3_init, 215 + .exit = loongson3_exit, 216 + .cpu_start = loongson3_cpu_start, 217 + .cpu_stop = loongson3_cpu_stop, 218 + .cpu_type = "mips/loongson3", 219 + .num_counters = 2 220 + };