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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.34 173 lines 4.8 kB view raw
1/* 2 * Loongson2 performance counter driver for oprofile 3 * 4 * Copyright (C) 2009 Lemote Inc. 5 * Author: Yanhua <yanh@lemote.com> 6 * Author: Wu Zhangjin <wuzhangjin@gmail.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/loongson2" 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_enabled, cnt2_enabled; 48} reg; 49 50static char *oprofid = "LoongsonPerf"; 51static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id); 52/* Compute all of the registers in preparation for enabling profiling. */ 53 54static void loongson2_reg_setup(struct op_counter_config *cfg) 55{ 56 unsigned int ctrl = 0; 57 58 reg.reset_counter1 = 0; 59 reg.reset_counter2 = 0; 60 /* Compute the performance counter ctrl word. */ 61 /* For now count kernel and user mode */ 62 if (cfg[0].enabled) { 63 ctrl |= LOONGSON2_COUNTER1_EVENT(cfg[0].event); 64 reg.reset_counter1 = 0x80000000ULL - cfg[0].count; 65 } 66 67 if (cfg[1].enabled) { 68 ctrl |= LOONGSON2_COUNTER2_EVENT(cfg[1].event); 69 reg.reset_counter2 = (0x80000000ULL - cfg[1].count); 70 } 71 72 if (cfg[0].enabled || cfg[1].enabled) { 73 ctrl |= LOONGSON2_PERFCNT_EXL | LOONGSON2_PERFCNT_INT_EN; 74 if (cfg[0].kernel || cfg[1].kernel) 75 ctrl |= LOONGSON2_PERFCNT_KERNEL; 76 if (cfg[0].user || cfg[1].user) 77 ctrl |= LOONGSON2_PERFCNT_USER; 78 } 79 80 reg.ctrl = ctrl; 81 82 reg.cnt1_enabled = cfg[0].enabled; 83 reg.cnt2_enabled = cfg[1].enabled; 84 85} 86 87/* Program all of the registers in preparation for enabling profiling. */ 88 89static void loongson2_cpu_setup(void *args) 90{ 91 uint64_t perfcount; 92 93 perfcount = (reg.reset_counter2 << 32) | reg.reset_counter1; 94 write_c0_perfcnt(perfcount); 95} 96 97static void loongson2_cpu_start(void *args) 98{ 99 /* Start all counters on current CPU */ 100 if (reg.cnt1_enabled || reg.cnt2_enabled) 101 write_c0_perfctrl(reg.ctrl); 102} 103 104static void loongson2_cpu_stop(void *args) 105{ 106 /* Stop all counters on current CPU */ 107 write_c0_perfctrl(0); 108 memset(&reg, 0, sizeof(reg)); 109} 110 111static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id) 112{ 113 uint64_t counter, counter1, counter2; 114 struct pt_regs *regs = get_irq_regs(); 115 int enabled; 116 117 /* 118 * LOONGSON2 defines two 32-bit performance counters. 119 * To avoid a race updating the registers we need to stop the counters 120 * while we're messing with 121 * them ... 122 */ 123 124 /* Check whether the irq belongs to me */ 125 enabled = read_c0_perfctrl() & LOONGSON2_PERFCNT_INT_EN; 126 if (!enabled) 127 return IRQ_NONE; 128 enabled = reg.cnt1_enabled | reg.cnt2_enabled; 129 if (!enabled) 130 return IRQ_NONE; 131 132 counter = read_c0_perfcnt(); 133 counter1 = counter & 0xffffffff; 134 counter2 = counter >> 32; 135 136 if (counter1 & LOONGSON2_PERFCNT_OVERFLOW) { 137 if (reg.cnt1_enabled) 138 oprofile_add_sample(regs, 0); 139 counter1 = reg.reset_counter1; 140 } 141 if (counter2 & LOONGSON2_PERFCNT_OVERFLOW) { 142 if (reg.cnt2_enabled) 143 oprofile_add_sample(regs, 1); 144 counter2 = reg.reset_counter2; 145 } 146 147 write_c0_perfcnt((counter2 << 32) | counter1); 148 149 return IRQ_HANDLED; 150} 151 152static int __init loongson2_init(void) 153{ 154 return request_irq(LOONGSON2_PERFCNT_IRQ, loongson2_perfcount_handler, 155 IRQF_SHARED, "Perfcounter", oprofid); 156} 157 158static void loongson2_exit(void) 159{ 160 write_c0_perfctrl(0); 161 free_irq(LOONGSON2_PERFCNT_IRQ, oprofid); 162} 163 164struct op_mips_model op_model_loongson2_ops = { 165 .reg_setup = loongson2_reg_setup, 166 .cpu_setup = loongson2_cpu_setup, 167 .init = loongson2_init, 168 .exit = loongson2_exit, 169 .cpu_start = loongson2_cpu_start, 170 .cpu_stop = loongson2_cpu_stop, 171 .cpu_type = LOONGSON2_CPU_TYPE, 172 .num_counters = 2 173};