Path: blob/master/arch/powerpc/oprofile/op_model_pa6t.c
10817 views
/*1* Copyright (C) 2006-2007 PA Semi, Inc2*3* Author: Shashi Rao, PA Semi4*5* Maintained by: Olof Johansson <[email protected]>6*7* Based on arch/powerpc/oprofile/op_model_power4.c8*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License version 2 as11* published by the Free Software Foundation.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA21*/2223#include <linux/oprofile.h>24#include <linux/init.h>25#include <linux/smp.h>26#include <linux/percpu.h>27#include <asm/processor.h>28#include <asm/cputable.h>29#include <asm/oprofile_impl.h>30#include <asm/reg.h>3132static unsigned char oprofile_running;3334/* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */35static u64 mmcr0_val;36static u64 mmcr1_val;3738/* inited in pa6t_reg_setup */39static u64 reset_value[OP_MAX_COUNTER];4041static inline u64 ctr_read(unsigned int i)42{43switch (i) {44case 0:45return mfspr(SPRN_PA6T_PMC0);46case 1:47return mfspr(SPRN_PA6T_PMC1);48case 2:49return mfspr(SPRN_PA6T_PMC2);50case 3:51return mfspr(SPRN_PA6T_PMC3);52case 4:53return mfspr(SPRN_PA6T_PMC4);54case 5:55return mfspr(SPRN_PA6T_PMC5);56default:57printk(KERN_ERR "ctr_read called with bad arg %u\n", i);58return 0;59}60}6162static inline void ctr_write(unsigned int i, u64 val)63{64switch (i) {65case 0:66mtspr(SPRN_PA6T_PMC0, val);67break;68case 1:69mtspr(SPRN_PA6T_PMC1, val);70break;71case 2:72mtspr(SPRN_PA6T_PMC2, val);73break;74case 3:75mtspr(SPRN_PA6T_PMC3, val);76break;77case 4:78mtspr(SPRN_PA6T_PMC4, val);79break;80case 5:81mtspr(SPRN_PA6T_PMC5, val);82break;83default:84printk(KERN_ERR "ctr_write called with bad arg %u\n", i);85break;86}87}888990/* precompute the values to stuff in the hardware registers */91static int pa6t_reg_setup(struct op_counter_config *ctr,92struct op_system_config *sys,93int num_ctrs)94{95int pmc;9697/*98* adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the99* event_mappings file by turning off the counters that the user doesn't100* care about101*102* setup user and kernel profiling103*/104for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++)105if (!ctr[pmc].enabled) {106sys->mmcr0 &= ~(0x1UL << pmc);107sys->mmcr0 &= ~(0x1UL << (pmc+12));108pr_debug("turned off counter %u\n", pmc);109}110111if (sys->enable_kernel)112sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN;113else114sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN);115116if (sys->enable_user)117sys->mmcr0 |= PA6T_MMCR0_PREN;118else119sys->mmcr0 &= ~PA6T_MMCR0_PREN;120121/*122* The performance counter event settings are given in the mmcr0 and123* mmcr1 values passed from the user in the op_system_config124* structure (sys variable).125*/126mmcr0_val = sys->mmcr0;127mmcr1_val = sys->mmcr1;128pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0);129pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1);130131for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) {132/* counters are 40 bit. Move to cputable at some point? */133reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count;134pr_debug("reset_value for pmc%u inited to 0x%llx\n",135pmc, reset_value[pmc]);136}137138return 0;139}140141/* configure registers on this cpu */142static int pa6t_cpu_setup(struct op_counter_config *ctr)143{144u64 mmcr0 = mmcr0_val;145u64 mmcr1 = mmcr1_val;146147/* Default is all PMCs off */148mmcr0 &= ~(0x3FUL);149mtspr(SPRN_PA6T_MMCR0, mmcr0);150151/* program selected programmable events in */152mtspr(SPRN_PA6T_MMCR1, mmcr1);153154pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(),155mfspr(SPRN_PA6T_MMCR0));156pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(),157mfspr(SPRN_PA6T_MMCR1));158159return 0;160}161162static int pa6t_start(struct op_counter_config *ctr)163{164int i;165166/* Hold off event counting until rfid */167u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS;168169for (i = 0; i < cur_cpu_spec->num_pmcs; i++)170if (ctr[i].enabled)171ctr_write(i, reset_value[i]);172else173ctr_write(i, 0UL);174175mtspr(SPRN_PA6T_MMCR0, mmcr0);176177oprofile_running = 1;178179pr_debug("start on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0);180181return 0;182}183184static void pa6t_stop(void)185{186u64 mmcr0;187188/* freeze counters */189mmcr0 = mfspr(SPRN_PA6T_MMCR0);190mmcr0 |= PA6T_MMCR0_FCM0;191mtspr(SPRN_PA6T_MMCR0, mmcr0);192193oprofile_running = 0;194195pr_debug("stop on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0);196}197198/* handle the perfmon overflow vector */199static void pa6t_handle_interrupt(struct pt_regs *regs,200struct op_counter_config *ctr)201{202unsigned long pc = mfspr(SPRN_PA6T_SIAR);203int is_kernel = is_kernel_addr(pc);204u64 val;205int i;206u64 mmcr0;207208/* disable perfmon counting until rfid */209mmcr0 = mfspr(SPRN_PA6T_MMCR0);210mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS);211212/* Record samples. We've got one global bit for whether a sample213* was taken, so add it for any counter that triggered overflow.214*/215for (i = 0; i < cur_cpu_spec->num_pmcs; i++) {216val = ctr_read(i);217if (val & (0x1UL << 39)) { /* Overflow bit set */218if (oprofile_running && ctr[i].enabled) {219if (mmcr0 & PA6T_MMCR0_SIARLOG)220oprofile_add_ext_sample(pc, regs, i, is_kernel);221ctr_write(i, reset_value[i]);222} else {223ctr_write(i, 0UL);224}225}226}227228/* Restore mmcr0 to a good known value since the PMI changes it */229mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS;230mtspr(SPRN_PA6T_MMCR0, mmcr0);231}232233struct op_powerpc_model op_model_pa6t = {234.reg_setup = pa6t_reg_setup,235.cpu_setup = pa6t_cpu_setup,236.start = pa6t_start,237.stop = pa6t_stop,238.handle_interrupt = pa6t_handle_interrupt,239};240241242