Back to home page

LXR

 
 

    


0001 /*
0002  * This file is subject to the terms and conditions of the GNU General Public
0003  * License.  See the file "COPYING" in the main directory of this archive
0004  * for more details.
0005  *
0006  */
0007 #include <linux/init.h>
0008 #include <linux/cpu.h>
0009 #include <linux/smp.h>
0010 #include <linux/proc_fs.h>
0011 #include <linux/oprofile.h>
0012 #include <linux/spinlock.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/uaccess.h>
0015 #include <irq.h>
0016 #include <loongson.h>
0017 #include "op_impl.h"
0018 
0019 #define LOONGSON3_PERFCNT_OVERFLOW  (1ULL << 63)
0020 
0021 #define LOONGSON3_PERFCTRL_EXL      (1UL << 0)
0022 #define LOONGSON3_PERFCTRL_KERNEL   (1UL << 1)
0023 #define LOONGSON3_PERFCTRL_SUPERVISOR   (1UL << 2)
0024 #define LOONGSON3_PERFCTRL_USER     (1UL << 3)
0025 #define LOONGSON3_PERFCTRL_ENABLE   (1UL << 4)
0026 #define LOONGSON3_PERFCTRL_W        (1UL << 30)
0027 #define LOONGSON3_PERFCTRL_M        (1UL << 31)
0028 #define LOONGSON3_PERFCTRL_EVENT(idx, event) \
0029     (((event) & (idx ? 0x0f : 0x3f)) << 5)
0030 
0031 /* Loongson-3 PerfCount performance counter1 register */
0032 #define read_c0_perflo1() __read_64bit_c0_register($25, 0)
0033 #define write_c0_perflo1(val) __write_64bit_c0_register($25, 0, val)
0034 #define read_c0_perfhi1() __read_64bit_c0_register($25, 1)
0035 #define write_c0_perfhi1(val) __write_64bit_c0_register($25, 1, val)
0036 
0037 /* Loongson-3 PerfCount performance counter2 register */
0038 #define read_c0_perflo2() __read_64bit_c0_register($25, 2)
0039 #define write_c0_perflo2(val) __write_64bit_c0_register($25, 2, val)
0040 #define read_c0_perfhi2() __read_64bit_c0_register($25, 3)
0041 #define write_c0_perfhi2(val) __write_64bit_c0_register($25, 3, val)
0042 
0043 static int (*save_perf_irq)(void);
0044 
0045 static struct loongson3_register_config {
0046     unsigned int control1;
0047     unsigned int control2;
0048     unsigned long long reset_counter1;
0049     unsigned long long reset_counter2;
0050     int ctr1_enable, ctr2_enable;
0051 } reg;
0052 
0053 static void reset_counters(void *arg)
0054 {
0055     write_c0_perfhi1(0);
0056     write_c0_perfhi2(0);
0057     write_c0_perflo1(0xc0000000);
0058     write_c0_perflo2(0x40000000);
0059 }
0060 
0061 /* Compute all of the registers in preparation for enabling profiling. */
0062 static void loongson3_reg_setup(struct op_counter_config *ctr)
0063 {
0064     unsigned int control1 = 0;
0065     unsigned int control2 = 0;
0066 
0067     reg.reset_counter1 = 0;
0068     reg.reset_counter2 = 0;
0069     /* Compute the performance counter control word. */
0070     /* For now count kernel and user mode */
0071     if (ctr[0].enabled) {
0072         control1 |= LOONGSON3_PERFCTRL_EVENT(0, ctr[0].event) |
0073                     LOONGSON3_PERFCTRL_ENABLE;
0074         if (ctr[0].kernel)
0075             control1 |= LOONGSON3_PERFCTRL_KERNEL;
0076         if (ctr[0].user)
0077             control1 |= LOONGSON3_PERFCTRL_USER;
0078         reg.reset_counter1 = 0x8000000000000000ULL - ctr[0].count;
0079     }
0080 
0081     if (ctr[1].enabled) {
0082         control2 |= LOONGSON3_PERFCTRL_EVENT(1, ctr[1].event) |
0083                     LOONGSON3_PERFCTRL_ENABLE;
0084         if (ctr[1].kernel)
0085             control2 |= LOONGSON3_PERFCTRL_KERNEL;
0086         if (ctr[1].user)
0087             control2 |= LOONGSON3_PERFCTRL_USER;
0088         reg.reset_counter2 = 0x8000000000000000ULL - ctr[1].count;
0089     }
0090 
0091     if (ctr[0].enabled)
0092         control1 |= LOONGSON3_PERFCTRL_EXL;
0093     if (ctr[1].enabled)
0094         control2 |= LOONGSON3_PERFCTRL_EXL;
0095 
0096     reg.control1 = control1;
0097     reg.control2 = control2;
0098     reg.ctr1_enable = ctr[0].enabled;
0099     reg.ctr2_enable = ctr[1].enabled;
0100 }
0101 
0102 /* Program all of the registers in preparation for enabling profiling. */
0103 static void loongson3_cpu_setup(void *args)
0104 {
0105     uint64_t perfcount1, perfcount2;
0106 
0107     perfcount1 = reg.reset_counter1;
0108     perfcount2 = reg.reset_counter2;
0109     write_c0_perfhi1(perfcount1);
0110     write_c0_perfhi2(perfcount2);
0111 }
0112 
0113 static void loongson3_cpu_start(void *args)
0114 {
0115     /* Start all counters on current CPU */
0116     reg.control1 |= (LOONGSON3_PERFCTRL_W|LOONGSON3_PERFCTRL_M);
0117     reg.control2 |= (LOONGSON3_PERFCTRL_W|LOONGSON3_PERFCTRL_M);
0118 
0119     if (reg.ctr1_enable)
0120         write_c0_perflo1(reg.control1);
0121     if (reg.ctr2_enable)
0122         write_c0_perflo2(reg.control2);
0123 }
0124 
0125 static void loongson3_cpu_stop(void *args)
0126 {
0127     /* Stop all counters on current CPU */
0128     write_c0_perflo1(0xc0000000);
0129     write_c0_perflo2(0x40000000);
0130     memset(&reg, 0, sizeof(reg));
0131 }
0132 
0133 static int loongson3_perfcount_handler(void)
0134 {
0135     unsigned long flags;
0136     uint64_t counter1, counter2;
0137     uint32_t cause, handled = IRQ_NONE;
0138     struct pt_regs *regs = get_irq_regs();
0139 
0140     cause = read_c0_cause();
0141     if (!(cause & CAUSEF_PCI))
0142         return handled;
0143 
0144     counter1 = read_c0_perfhi1();
0145     counter2 = read_c0_perfhi2();
0146 
0147     local_irq_save(flags);
0148 
0149     if (counter1 & LOONGSON3_PERFCNT_OVERFLOW) {
0150         if (reg.ctr1_enable)
0151             oprofile_add_sample(regs, 0);
0152         counter1 = reg.reset_counter1;
0153     }
0154     if (counter2 & LOONGSON3_PERFCNT_OVERFLOW) {
0155         if (reg.ctr2_enable)
0156             oprofile_add_sample(regs, 1);
0157         counter2 = reg.reset_counter2;
0158     }
0159 
0160     local_irq_restore(flags);
0161 
0162     write_c0_perfhi1(counter1);
0163     write_c0_perfhi2(counter2);
0164 
0165     if (!(cause & CAUSEF_TI))
0166         handled = IRQ_HANDLED;
0167 
0168     return handled;
0169 }
0170 
0171 static int loongson3_starting_cpu(unsigned int cpu)
0172 {
0173     write_c0_perflo1(reg.control1);
0174     write_c0_perflo2(reg.control2);
0175     return 0;
0176 }
0177 
0178 static int loongson3_dying_cpu(unsigned int cpu)
0179 {
0180     write_c0_perflo1(0xc0000000);
0181     write_c0_perflo2(0x40000000);
0182     return 0;
0183 }
0184 
0185 static int __init loongson3_init(void)
0186 {
0187     on_each_cpu(reset_counters, NULL, 1);
0188     cpuhp_setup_state_nocalls(CPUHP_AP_MIPS_OP_LOONGSON3_STARTING,
0189                   "mips/oprofile/loongson3:starting",
0190                   loongson3_starting_cpu, loongson3_dying_cpu);
0191     save_perf_irq = perf_irq;
0192     perf_irq = loongson3_perfcount_handler;
0193 
0194     return 0;
0195 }
0196 
0197 static void loongson3_exit(void)
0198 {
0199     on_each_cpu(reset_counters, NULL, 1);
0200     cpuhp_remove_state_nocalls(CPUHP_AP_MIPS_OP_LOONGSON3_STARTING);
0201     perf_irq = save_perf_irq;
0202 }
0203 
0204 struct op_mips_model op_model_loongson3_ops = {
0205     .reg_setup  = loongson3_reg_setup,
0206     .cpu_setup  = loongson3_cpu_setup,
0207     .init       = loongson3_init,
0208     .exit       = loongson3_exit,
0209     .cpu_start  = loongson3_cpu_start,
0210     .cpu_stop   = loongson3_cpu_stop,
0211     .cpu_type   = "mips/loongson3",
0212     .num_counters   = 2
0213 };