linux/arch/mips/oprofile/op_model_loongson2.c
<<
>>
Prefs
   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#include <linux/init.h>
  13#include <linux/oprofile.h>
  14#include <linux/interrupt.h>
  15
  16#include <loongson.h>                   /* LOONGSON2_PERFCNT_IRQ */
  17#include "op_impl.h"
  18
  19#define LOONGSON2_CPU_TYPE      "mips/loongson2"
  20
  21#define LOONGSON2_PERFCNT_OVERFLOW              (1ULL   << 31)
  22
  23#define LOONGSON2_PERFCTRL_EXL                  (1UL    <<  0)
  24#define LOONGSON2_PERFCTRL_KERNEL               (1UL    <<  1)
  25#define LOONGSON2_PERFCTRL_SUPERVISOR           (1UL    <<  2)
  26#define LOONGSON2_PERFCTRL_USER                 (1UL    <<  3)
  27#define LOONGSON2_PERFCTRL_ENABLE               (1UL    <<  4)
  28#define LOONGSON2_PERFCTRL_EVENT(idx, event) \
  29        (((event) & 0x0f) << ((idx) ? 9 : 5))
  30
  31#define read_c0_perfctrl() __read_64bit_c0_register($24, 0)
  32#define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val)
  33#define read_c0_perfcnt() __read_64bit_c0_register($25, 0)
  34#define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val)
  35
  36static struct loongson2_register_config {
  37        unsigned int ctrl;
  38        unsigned long long reset_counter1;
  39        unsigned long long reset_counter2;
  40        int cnt1_enabled, cnt2_enabled;
  41} reg;
  42
  43static char *oprofid = "LoongsonPerf";
  44static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id);
  45
  46static void reset_counters(void *arg)
  47{
  48        write_c0_perfctrl(0);
  49        write_c0_perfcnt(0);
  50}
  51
  52static void loongson2_reg_setup(struct op_counter_config *cfg)
  53{
  54        unsigned int ctrl = 0;
  55
  56        reg.reset_counter1 = 0;
  57        reg.reset_counter2 = 0;
  58
  59        /*
  60         * Compute the performance counter ctrl word.
  61         * For now, count kernel and user mode.
  62         */
  63        if (cfg[0].enabled) {
  64                ctrl |= LOONGSON2_PERFCTRL_EVENT(0, cfg[0].event);
  65                reg.reset_counter1 = 0x80000000ULL - cfg[0].count;
  66        }
  67
  68        if (cfg[1].enabled) {
  69                ctrl |= LOONGSON2_PERFCTRL_EVENT(1, cfg[1].event);
  70                reg.reset_counter2 = 0x80000000ULL - cfg[1].count;
  71        }
  72
  73        if (cfg[0].enabled || cfg[1].enabled) {
  74                ctrl |= LOONGSON2_PERFCTRL_EXL | LOONGSON2_PERFCTRL_ENABLE;
  75                if (cfg[0].kernel || cfg[1].kernel)
  76                        ctrl |= LOONGSON2_PERFCTRL_KERNEL;
  77                if (cfg[0].user || cfg[1].user)
  78                        ctrl |= LOONGSON2_PERFCTRL_USER;
  79        }
  80
  81        reg.ctrl = ctrl;
  82
  83        reg.cnt1_enabled = cfg[0].enabled;
  84        reg.cnt2_enabled = cfg[1].enabled;
  85}
  86
  87static void loongson2_cpu_setup(void *args)
  88{
  89        write_c0_perfcnt((reg.reset_counter2 << 32) | reg.reset_counter1);
  90}
  91
  92static void loongson2_cpu_start(void *args)
  93{
  94        /* Start all counters on current CPU */
  95        if (reg.cnt1_enabled || reg.cnt2_enabled)
  96                write_c0_perfctrl(reg.ctrl);
  97}
  98
  99static void loongson2_cpu_stop(void *args)
 100{
 101        /* Stop all counters on current CPU */
 102        write_c0_perfctrl(0);
 103        memset(&reg, 0, sizeof(reg));
 104}
 105
 106static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id)
 107{
 108        uint64_t counter, counter1, counter2;
 109        struct pt_regs *regs = get_irq_regs();
 110        int enabled;
 111
 112        /* Check whether the irq belongs to me */
 113        enabled = read_c0_perfctrl() & LOONGSON2_PERFCTRL_ENABLE;
 114        if (!enabled)
 115                return IRQ_NONE;
 116        enabled = reg.cnt1_enabled | reg.cnt2_enabled;
 117        if (!enabled)
 118                return IRQ_NONE;
 119
 120        counter = read_c0_perfcnt();
 121        counter1 = counter & 0xffffffff;
 122        counter2 = counter >> 32;
 123
 124        if (counter1 & LOONGSON2_PERFCNT_OVERFLOW) {
 125                if (reg.cnt1_enabled)
 126                        oprofile_add_sample(regs, 0);
 127                counter1 = reg.reset_counter1;
 128        }
 129        if (counter2 & LOONGSON2_PERFCNT_OVERFLOW) {
 130                if (reg.cnt2_enabled)
 131                        oprofile_add_sample(regs, 1);
 132                counter2 = reg.reset_counter2;
 133        }
 134
 135        write_c0_perfcnt((counter2 << 32) | counter1);
 136
 137        return IRQ_HANDLED;
 138}
 139
 140static int __init loongson2_init(void)
 141{
 142        return request_irq(LOONGSON2_PERFCNT_IRQ, loongson2_perfcount_handler,
 143                           IRQF_SHARED, "Perfcounter", oprofid);
 144}
 145
 146static void loongson2_exit(void)
 147{
 148        reset_counters(NULL);
 149        free_irq(LOONGSON2_PERFCNT_IRQ, oprofid);
 150}
 151
 152struct op_mips_model op_model_loongson2_ops = {
 153        .reg_setup = loongson2_reg_setup,
 154        .cpu_setup = loongson2_cpu_setup,
 155        .init = loongson2_init,
 156        .exit = loongson2_exit,
 157        .cpu_start = loongson2_cpu_start,
 158        .cpu_stop = loongson2_cpu_stop,
 159        .cpu_type = LOONGSON2_CPU_TYPE,
 160        .num_counters = 2
 161};
 162