lkml.org 
[lkml]   [2009]   [Jul]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v4 11/16] [loongson] add oprofile support
    Date
    From: Wu Zhangjin <wuzj@lemote.com>

    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.

    In loongson2, there are two performance counters, each one can count 16 events
    respectively. when anyone of the performance counter overflows, an interrupt
    will generate and goes to the interrupt line: MIPS_CPU_IRQ_BASE + 6

    Signed-off-by: Yanhua <yanh@lemote.com>
    Signed-off-by: Wu Zhangjin <wuzj@lemote.com>
    ---
    arch/mips/include/asm/mach-lemote/loongson.h | 3 +
    arch/mips/lemote/lm2e/irq.c | 2 +
    arch/mips/oprofile/Makefile | 1 +
    arch/mips/oprofile/common.c | 4 +
    arch/mips/oprofile/op_model_loongson2.c | 177 ++++++++++++++++++++++++++
    5 files changed, 187 insertions(+), 0 deletions(-)
    create mode 100644 arch/mips/oprofile/op_model_loongson2.c
    diff --git a/arch/mips/include/asm/mach-lemote/loongson.h b/arch/mips/include/asm/mach-lemote/loongson.h
    index 916eace..95ee4c8 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);
    #define LOONGSON_PXARB_CFG BONITO(BONITO_REGBASE + 0x68)
    #define LOONGSON_PXARB_STATUS BONITO(BONITO_REGBASE + 0x6c)

    +/* loongson2-specific perf counter IRQ */
    +#define LOONGSON2_PERFCNT_IRQ (MIPS_CPU_IRQ_BASE + 6)
    +
    #endif /* __ASM_MACH_LOONGSON_LOONGSON_H */
    diff --git a/arch/mips/lemote/lm2e/irq.c b/arch/mips/lemote/lm2e/irq.c
    index fb7643a..9585f5a 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)

    if (pending & CAUSEF_IP7)
    do_IRQ(MIPS_CPU_IRQ_BASE + 7);
    + else if (pending & CAUSEF_IP6) /* perf counter loverflow */
    + do_IRQ(LOONGSON2_PERFCNT_IRQ);
    else if (pending & CAUSEF_IP5)
    i8259_irqdispatch();
    else if (pending & CAUSEF_IP2)
    diff --git a/arch/mips/oprofile/Makefile b/arch/mips/oprofile/Makefile
    index bf3be6f..02cc65e 100644
    --- a/arch/mips/oprofile/Makefile
    +++ b/arch/mips/oprofile/Makefile
    @@ -15,3 +15,4 @@ oprofile-$(CONFIG_CPU_MIPS64) += op_model_mipsxx.o
    oprofile-$(CONFIG_CPU_R10000) += op_model_mipsxx.o
    oprofile-$(CONFIG_CPU_SB1) += op_model_mipsxx.o
    oprofile-$(CONFIG_CPU_RM9000) += op_model_rm9000.o
    +oprofile-$(CONFIG_CPU_LOONGSON2) += op_model_loongson2.o
    diff --git a/arch/mips/oprofile/common.c b/arch/mips/oprofile/common.c
    index 3bf3354..7832ad2 100644
    --- a/arch/mips/oprofile/common.c
    +++ b/arch/mips/oprofile/common.c
    @@ -16,6 +16,7 @@

    extern struct op_mips_model op_model_mipsxx_ops __attribute__((weak));
    extern struct op_mips_model op_model_rm9000_ops __attribute__((weak));
    +extern struct op_mips_model op_model_loongson2_ops __attribute__((weak));

    static struct op_mips_model *model;

    @@ -93,6 +94,9 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
    case CPU_RM9000:
    lmodel = &op_model_rm9000_ops;
    break;
    + case CPU_LOONGSON2:
    + lmodel = &op_model_loongson2_ops;
    + break;
    };

    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 0000000..655cb8d
    --- /dev/null
    +++ b/arch/mips/oprofile/op_model_loongson2.c
    @@ -0,0 +1,177 @@
    +/*
    + * Loongson2 performance counter driver for oprofile
    + *
    + * Copyright (C) 2009 Lemote Inc. & Insititute of Computing Technology
    + * Author: Yanhua <yanh@lemote.com>
    + * Author: Wu Zhangjin <wuzj@lemote.com>
    + *
    + * This file is subject to the terms and conditions of the GNU General Public
    + * License. See the file "COPYING" in the main directory of this archive
    + * for more details.
    + *
    + */
    +#include <linux/init.h>
    +#include <linux/oprofile.h>
    +#include <linux/interrupt.h>
    +
    +#include <loongson.h> /* LOONGSON2_PERFCNT_IRQ */
    +#include "op_impl.h"
    +
    +/*
    + * a patch should be sent to oprofile with the loongson-specific support.
    + * otherwise, the oprofile tool will not recognize this and complain about
    + * "cpu_type 'unset' is not valid".
    + */
    +#define LOONGSON2_CPU_TYPE "mips/godson2"
    +
    +#define LOONGSON2_COUNTER1_EVENT(event) ((event & 0x0f) << 5)
    +#define LOONGSON2_COUNTER2_EVENT(event) ((event & 0x0f) << 9)
    +
    +#define LOONGSON2_PERFCNT_EXL (1UL << 0)
    +#define LOONGSON2_PERFCNT_KERNEL (1UL << 1)
    +#define LOONGSON2_PERFCNT_SUPERVISOR (1UL << 2)
    +#define LOONGSON2_PERFCNT_USER (1UL << 3)
    +#define LOONGSON2_PERFCNT_INT_EN (1UL << 4)
    +#define LOONGSON2_PERFCNT_OVERFLOW (1ULL << 31)
    +
    +/* Loongson2 performance counter register */
    +#define read_c0_perfctrl() __read_64bit_c0_register($24, 0)
    +#define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val)
    +#define read_c0_perfcnt() __read_64bit_c0_register($25, 0)
    +#define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val)
    +
    +static struct loongson2_register_config {
    + unsigned int ctrl;
    + unsigned long long reset_counter1;
    + unsigned long long reset_counter2;
    + int cnt1_enalbed, cnt2_enalbed;
    +} reg;
    +
    +DEFINE_SPINLOCK(sample_lock);
    +
    +static char *oprofid = "LoongsonPerf";
    +static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id);
    +/* Compute all of the registers in preparation for enabling profiling. */
    +
    +static void loongson2_reg_setup(struct op_counter_config *cfg)
    +{
    + unsigned int ctrl = 0;
    +
    + reg.reset_counter1 = 0;
    + reg.reset_counter2 = 0;
    + /* Compute the performance counter ctrl word. */
    + /* For now count kernel and user mode */
    + if (cfg[0].enabled) {
    + ctrl |= LOONGSON2_COUNTER1_EVENT(cfg[0].event);
    + reg.reset_counter1 = 0x80000000ULL - cfg[0].count;
    + }
    +
    + if (cfg[1].enabled) {
    + ctrl |= LOONGSON2_COUNTER2_EVENT(cfg[1].event);
    + reg.reset_counter2 = (0x80000000ULL - cfg[1].count);
    + }
    +
    + if (cfg[0].enabled || cfg[1].enabled) {
    + ctrl |= LOONGSON2_PERFCNT_EXL | LOONGSON2_PERFCNT_INT_EN;
    + if (cfg[0].kernel || cfg[1].kernel)
    + ctrl |= LOONGSON2_PERFCNT_KERNEL;
    + if (cfg[0].user || cfg[1].user)
    + ctrl |= LOONGSON2_PERFCNT_USER;
    + }
    +
    + reg.ctrl = ctrl;
    +
    + reg.cnt1_enalbed = cfg[0].enabled;
    + reg.cnt2_enalbed = cfg[1].enabled;
    +
    +}
    +
    +/* Program all of the registers in preparation for enabling profiling. */
    +
    +static void loongson2_cpu_setup(void *args)
    +{
    + uint64_t perfcount;
    +
    + perfcount = (reg.reset_counter2 << 32) | reg.reset_counter1;
    + write_c0_perfcnt(perfcount);
    +}
    +
    +static void loongson2_cpu_start(void *args)
    +{
    + /* Start all counters on current CPU */
    + if (reg.cnt1_enalbed || reg.cnt2_enalbed)
    + write_c0_perfctrl(reg.ctrl);
    +}
    +
    +static void loongson2_cpu_stop(void *args)
    +{
    + /* Stop all counters on current CPU */
    + write_c0_perfctrl(0);
    + memset(&reg, 0, sizeof(reg));
    +}
    +
    +static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id)
    +{
    + uint64_t counter, counter1, counter2;
    + struct pt_regs *regs = get_irq_regs();
    + int enabled;
    + unsigned long flags;
    +
    + /*
    + * LOONGSON2 defines two 32-bit performance counters.
    + * To avoid a race updating the registers we need to stop the counters
    + * while we're messing with
    + * them ...
    + */
    +
    + /* Check whether the irq belongs to me */
    + enabled = reg.cnt1_enalbed | reg.cnt2_enalbed;
    + if (!enabled)
    + return IRQ_NONE;
    +
    + counter = read_c0_perfcnt();
    + counter1 = counter & 0xffffffff;
    + counter2 = counter >> 32;
    +
    + spin_lock_irqsave(&sample_lock, flags);
    +
    + if (counter1 & LOONGSON2_PERFCNT_OVERFLOW) {
    + if (reg.cnt1_enalbed)
    + oprofile_add_sample(regs, 0);
    + counter1 = reg.reset_counter1;
    + }
    + if (counter2 & LOONGSON2_PERFCNT_OVERFLOW) {
    + if (reg.cnt2_enalbed)
    + oprofile_add_sample(regs, 1);
    + counter2 = reg.reset_counter2;
    + }
    +
    + spin_unlock_irqrestore(&sample_lock, flags);
    +
    + write_c0_perfcnt((counter2 << 32) | counter1);
    +
    + return IRQ_HANDLED;
    +}
    +
    +static int __init loongson2_init(void)
    +{
    + return request_irq(LOONGSON2_PERFCNT_IRQ, loongson2_perfcount_handler,
    + IRQF_SHARED, "Perfcounter", oprofid);
    +}
    +
    +static void loongson2_exit(void)
    +{
    + write_c0_perfctrl(0);
    + free_irq(LOONGSON2_PERFCNT_IRQ, oprofid);
    +}
    +
    +struct op_mips_model op_model_loongson2_ops = {
    + .reg_setup = loongson2_reg_setup,
    + .cpu_setup = loongson2_cpu_setup,
    + .init = loongson2_init,
    + .exit = loongson2_exit,
    + .cpu_start = loongson2_cpu_start,
    + .cpu_stop = loongson2_cpu_stop,
    + .cpu_type = LOONGSON2_CPU_TYPE,
    + .num_counters = 2
    +};
    --
    1.6.2.1


    \
     
     \ /
      Last update: 2009-07-02 17:29    [from the cache]
    ©2003-2011 Jasper Spaans