Path: blob/master/arch/mips/oprofile/op_model_mipsxx.c
10817 views
/*1* This file is subject to the terms and conditions of the GNU General Public2* License. See the file "COPYING" in the main directory of this archive3* for more details.4*5* Copyright (C) 2004, 05, 06 by Ralf Baechle6* Copyright (C) 2005 by MIPS Technologies, Inc.7*/8#include <linux/cpumask.h>9#include <linux/oprofile.h>10#include <linux/interrupt.h>11#include <linux/smp.h>12#include <asm/irq_regs.h>1314#include "op_impl.h"1516#define M_PERFCTL_EXL (1UL << 0)17#define M_PERFCTL_KERNEL (1UL << 1)18#define M_PERFCTL_SUPERVISOR (1UL << 2)19#define M_PERFCTL_USER (1UL << 3)20#define M_PERFCTL_INTERRUPT_ENABLE (1UL << 4)21#define M_PERFCTL_EVENT(event) (((event) & 0x3ff) << 5)22#define M_PERFCTL_VPEID(vpe) ((vpe) << 16)23#define M_PERFCTL_MT_EN(filter) ((filter) << 20)24#define M_TC_EN_ALL M_PERFCTL_MT_EN(0)25#define M_TC_EN_VPE M_PERFCTL_MT_EN(1)26#define M_TC_EN_TC M_PERFCTL_MT_EN(2)27#define M_PERFCTL_TCID(tcid) ((tcid) << 22)28#define M_PERFCTL_WIDE (1UL << 30)29#define M_PERFCTL_MORE (1UL << 31)3031#define M_COUNTER_OVERFLOW (1UL << 31)3233static int (*save_perf_irq)(void);3435#ifdef CONFIG_MIPS_MT_SMP36static int cpu_has_mipsmt_pertccounters;37#define WHAT (M_TC_EN_VPE | \38M_PERFCTL_VPEID(cpu_data[smp_processor_id()].vpe_id))39#define vpe_id() (cpu_has_mipsmt_pertccounters ? \400 : cpu_data[smp_processor_id()].vpe_id)4142/*43* The number of bits to shift to convert between counters per core and44* counters per VPE. There is no reasonable interface atm to obtain the45* number of VPEs used by Linux and in the 34K this number is fixed to two46* anyways so we hardcore a few things here for the moment. The way it's47* done here will ensure that oprofile VSMP kernel will run right on a lesser48* core like a 24K also or with maxcpus=1.49*/50static inline unsigned int vpe_shift(void)51{52if (num_possible_cpus() > 1)53return 1;5455return 0;56}5758#else5960#define WHAT 061#define vpe_id() 06263static inline unsigned int vpe_shift(void)64{65return 0;66}6768#endif6970static inline unsigned int counters_total_to_per_cpu(unsigned int counters)71{72return counters >> vpe_shift();73}7475static inline unsigned int counters_per_cpu_to_total(unsigned int counters)76{77return counters << vpe_shift();78}7980#define __define_perf_accessors(r, n, np) \81\82static inline unsigned int r_c0_ ## r ## n(void) \83{ \84unsigned int cpu = vpe_id(); \85\86switch (cpu) { \87case 0: \88return read_c0_ ## r ## n(); \89case 1: \90return read_c0_ ## r ## np(); \91default: \92BUG(); \93} \94return 0; \95} \96\97static inline void w_c0_ ## r ## n(unsigned int value) \98{ \99unsigned int cpu = vpe_id(); \100\101switch (cpu) { \102case 0: \103write_c0_ ## r ## n(value); \104return; \105case 1: \106write_c0_ ## r ## np(value); \107return; \108default: \109BUG(); \110} \111return; \112} \113114__define_perf_accessors(perfcntr, 0, 2)115__define_perf_accessors(perfcntr, 1, 3)116__define_perf_accessors(perfcntr, 2, 0)117__define_perf_accessors(perfcntr, 3, 1)118119__define_perf_accessors(perfctrl, 0, 2)120__define_perf_accessors(perfctrl, 1, 3)121__define_perf_accessors(perfctrl, 2, 0)122__define_perf_accessors(perfctrl, 3, 1)123124struct op_mips_model op_model_mipsxx_ops;125126static struct mipsxx_register_config {127unsigned int control[4];128unsigned int counter[4];129} reg;130131/* Compute all of the registers in preparation for enabling profiling. */132133static void mipsxx_reg_setup(struct op_counter_config *ctr)134{135unsigned int counters = op_model_mipsxx_ops.num_counters;136int i;137138/* Compute the performance counter control word. */139for (i = 0; i < counters; i++) {140reg.control[i] = 0;141reg.counter[i] = 0;142143if (!ctr[i].enabled)144continue;145146reg.control[i] = M_PERFCTL_EVENT(ctr[i].event) |147M_PERFCTL_INTERRUPT_ENABLE;148if (ctr[i].kernel)149reg.control[i] |= M_PERFCTL_KERNEL;150if (ctr[i].user)151reg.control[i] |= M_PERFCTL_USER;152if (ctr[i].exl)153reg.control[i] |= M_PERFCTL_EXL;154reg.counter[i] = 0x80000000 - ctr[i].count;155}156}157158/* Program all of the registers in preparation for enabling profiling. */159160static void mipsxx_cpu_setup(void *args)161{162unsigned int counters = op_model_mipsxx_ops.num_counters;163164switch (counters) {165case 4:166w_c0_perfctrl3(0);167w_c0_perfcntr3(reg.counter[3]);168case 3:169w_c0_perfctrl2(0);170w_c0_perfcntr2(reg.counter[2]);171case 2:172w_c0_perfctrl1(0);173w_c0_perfcntr1(reg.counter[1]);174case 1:175w_c0_perfctrl0(0);176w_c0_perfcntr0(reg.counter[0]);177}178}179180/* Start all counters on current CPU */181static void mipsxx_cpu_start(void *args)182{183unsigned int counters = op_model_mipsxx_ops.num_counters;184185switch (counters) {186case 4:187w_c0_perfctrl3(WHAT | reg.control[3]);188case 3:189w_c0_perfctrl2(WHAT | reg.control[2]);190case 2:191w_c0_perfctrl1(WHAT | reg.control[1]);192case 1:193w_c0_perfctrl0(WHAT | reg.control[0]);194}195}196197/* Stop all counters on current CPU */198static void mipsxx_cpu_stop(void *args)199{200unsigned int counters = op_model_mipsxx_ops.num_counters;201202switch (counters) {203case 4:204w_c0_perfctrl3(0);205case 3:206w_c0_perfctrl2(0);207case 2:208w_c0_perfctrl1(0);209case 1:210w_c0_perfctrl0(0);211}212}213214static int mipsxx_perfcount_handler(void)215{216unsigned int counters = op_model_mipsxx_ops.num_counters;217unsigned int control;218unsigned int counter;219int handled = IRQ_NONE;220221if (cpu_has_mips_r2 && !(read_c0_cause() & (1 << 26)))222return handled;223224switch (counters) {225#define HANDLE_COUNTER(n) \226case n + 1: \227control = r_c0_perfctrl ## n(); \228counter = r_c0_perfcntr ## n(); \229if ((control & M_PERFCTL_INTERRUPT_ENABLE) && \230(counter & M_COUNTER_OVERFLOW)) { \231oprofile_add_sample(get_irq_regs(), n); \232w_c0_perfcntr ## n(reg.counter[n]); \233handled = IRQ_HANDLED; \234}235HANDLE_COUNTER(3)236HANDLE_COUNTER(2)237HANDLE_COUNTER(1)238HANDLE_COUNTER(0)239}240241return handled;242}243244#define M_CONFIG1_PC (1 << 4)245246static inline int __n_counters(void)247{248if (!(read_c0_config1() & M_CONFIG1_PC))249return 0;250if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))251return 1;252if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))253return 2;254if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))255return 3;256257return 4;258}259260static inline int n_counters(void)261{262int counters;263264switch (current_cpu_type()) {265case CPU_R10000:266counters = 2;267break;268269case CPU_R12000:270case CPU_R14000:271counters = 4;272break;273274default:275counters = __n_counters();276}277278return counters;279}280281static void reset_counters(void *arg)282{283int counters = (int)(long)arg;284switch (counters) {285case 4:286w_c0_perfctrl3(0);287w_c0_perfcntr3(0);288case 3:289w_c0_perfctrl2(0);290w_c0_perfcntr2(0);291case 2:292w_c0_perfctrl1(0);293w_c0_perfcntr1(0);294case 1:295w_c0_perfctrl0(0);296w_c0_perfcntr0(0);297}298}299300static int __init mipsxx_init(void)301{302int counters;303304counters = n_counters();305if (counters == 0) {306printk(KERN_ERR "Oprofile: CPU has no performance counters\n");307return -ENODEV;308}309310#ifdef CONFIG_MIPS_MT_SMP311cpu_has_mipsmt_pertccounters = read_c0_config7() & (1<<19);312if (!cpu_has_mipsmt_pertccounters)313counters = counters_total_to_per_cpu(counters);314#endif315on_each_cpu(reset_counters, (void *)(long)counters, 1);316317op_model_mipsxx_ops.num_counters = counters;318switch (current_cpu_type()) {319case CPU_20KC:320op_model_mipsxx_ops.cpu_type = "mips/20K";321break;322323case CPU_24K:324op_model_mipsxx_ops.cpu_type = "mips/24K";325break;326327case CPU_25KF:328op_model_mipsxx_ops.cpu_type = "mips/25K";329break;330331case CPU_1004K:332#if 0333/* FIXME: report as 34K for now */334op_model_mipsxx_ops.cpu_type = "mips/1004K";335break;336#endif337338case CPU_34K:339op_model_mipsxx_ops.cpu_type = "mips/34K";340break;341342case CPU_74K:343op_model_mipsxx_ops.cpu_type = "mips/74K";344break;345346case CPU_5KC:347op_model_mipsxx_ops.cpu_type = "mips/5K";348break;349350case CPU_R10000:351if ((current_cpu_data.processor_id & 0xff) == 0x20)352op_model_mipsxx_ops.cpu_type = "mips/r10000-v2.x";353else354op_model_mipsxx_ops.cpu_type = "mips/r10000";355break;356357case CPU_R12000:358case CPU_R14000:359op_model_mipsxx_ops.cpu_type = "mips/r12000";360break;361362case CPU_SB1:363case CPU_SB1A:364op_model_mipsxx_ops.cpu_type = "mips/sb1";365break;366367default:368printk(KERN_ERR "Profiling unsupported for this CPU\n");369370return -ENODEV;371}372373save_perf_irq = perf_irq;374perf_irq = mipsxx_perfcount_handler;375376return 0;377}378379static void mipsxx_exit(void)380{381int counters = op_model_mipsxx_ops.num_counters;382383counters = counters_per_cpu_to_total(counters);384on_each_cpu(reset_counters, (void *)(long)counters, 1);385386perf_irq = save_perf_irq;387}388389struct op_mips_model op_model_mipsxx_ops = {390.reg_setup = mipsxx_reg_setup,391.cpu_setup = mipsxx_cpu_setup,392.init = mipsxx_init,393.exit = mipsxx_exit,394.cpu_start = mipsxx_cpu_start,395.cpu_stop = mipsxx_cpu_stop,396};397398399