linux/arch/powerpc/oprofile/op_model_pa6t.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006-2007 PA Semi, Inc
   3 *
   4 * Author: Shashi Rao, PA Semi
   5 *
   6 * Maintained by: Olof Johansson <olof@lixom.net>
   7 *
   8 * Based on arch/powerpc/oprofile/op_model_power4.c
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  22 */
  23
  24#include <linux/oprofile.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>
  31
  32static unsigned char oprofile_running;
  33
  34/* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */
  35static u64 mmcr0_val;
  36static u64 mmcr1_val;
  37
  38/* inited in pa6t_reg_setup */
  39static u64 reset_value[OP_MAX_COUNTER];
  40
  41static inline u64 ctr_read(unsigned int i)
  42{
  43        switch (i) {
  44        case 0:
  45                return mfspr(SPRN_PA6T_PMC0);
  46        case 1:
  47                return mfspr(SPRN_PA6T_PMC1);
  48        case 2:
  49                return mfspr(SPRN_PA6T_PMC2);
  50        case 3:
  51                return mfspr(SPRN_PA6T_PMC3);
  52        case 4:
  53                return mfspr(SPRN_PA6T_PMC4);
  54        case 5:
  55                return mfspr(SPRN_PA6T_PMC5);
  56        default:
  57                printk(KERN_ERR "ctr_read called with bad arg %u\n", i);
  58                return 0;
  59        }
  60}
  61
  62static inline void ctr_write(unsigned int i, u64 val)
  63{
  64        switch (i) {
  65        case 0:
  66                mtspr(SPRN_PA6T_PMC0, val);
  67                break;
  68        case 1:
  69                mtspr(SPRN_PA6T_PMC1, val);
  70                break;
  71        case 2:
  72                mtspr(SPRN_PA6T_PMC2, val);
  73                break;
  74        case 3:
  75                mtspr(SPRN_PA6T_PMC3, val);
  76                break;
  77        case 4:
  78                mtspr(SPRN_PA6T_PMC4, val);
  79                break;
  80        case 5:
  81                mtspr(SPRN_PA6T_PMC5, val);
  82                break;
  83        default:
  84                printk(KERN_ERR "ctr_write called with bad arg %u\n", i);
  85                break;
  86        }
  87}
  88
  89
  90/* precompute the values to stuff in the hardware registers */
  91static int pa6t_reg_setup(struct op_counter_config *ctr,
  92                           struct op_system_config *sys,
  93                           int num_ctrs)
  94{
  95        int pmc;
  96
  97        /*
  98         * adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the
  99         * event_mappings file by turning off the counters that the user doesn't
 100         * care about
 101         *
 102         * setup user and kernel profiling
 103         */
 104        for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++)
 105                if (!ctr[pmc].enabled) {
 106                        sys->mmcr0 &= ~(0x1UL << pmc);
 107                        sys->mmcr0 &= ~(0x1UL << (pmc+12));
 108                        pr_debug("turned off counter %u\n", pmc);
 109                }
 110
 111        if (sys->enable_kernel)
 112                sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN;
 113        else
 114                sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN);
 115
 116        if (sys->enable_user)
 117                sys->mmcr0 |= PA6T_MMCR0_PREN;
 118        else
 119                sys->mmcr0 &= ~PA6T_MMCR0_PREN;
 120
 121        /*
 122         * The performance counter event settings are given in the mmcr0 and
 123         * mmcr1 values passed from the user in the op_system_config
 124         * structure (sys variable).
 125         */
 126        mmcr0_val = sys->mmcr0;
 127        mmcr1_val = sys->mmcr1;
 128        pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0);
 129        pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1);
 130
 131        for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) {
 132                /* counters are 40 bit. Move to cputable at some point? */
 133                reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count;
 134                pr_debug("reset_value for pmc%u inited to 0x%llx\n",
 135                                 pmc, reset_value[pmc]);
 136        }
 137
 138        return 0;
 139}
 140
 141/* configure registers on this cpu */
 142static int pa6t_cpu_setup(struct op_counter_config *ctr)
 143{
 144        u64 mmcr0 = mmcr0_val;
 145        u64 mmcr1 = mmcr1_val;
 146
 147        /* Default is all PMCs off */
 148        mmcr0 &= ~(0x3FUL);
 149        mtspr(SPRN_PA6T_MMCR0, mmcr0);
 150
 151        /* program selected programmable events in */
 152        mtspr(SPRN_PA6T_MMCR1, mmcr1);
 153
 154        pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(),
 155                mfspr(SPRN_PA6T_MMCR0));
 156        pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(),
 157                mfspr(SPRN_PA6T_MMCR1));
 158
 159        return 0;
 160}
 161
 162static int pa6t_start(struct op_counter_config *ctr)
 163{
 164        int i;
 165
 166        /* Hold off event counting until rfid */
 167        u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS;
 168
 169        for (i = 0; i < cur_cpu_spec->num_pmcs; i++)
 170                if (ctr[i].enabled)
 171                        ctr_write(i, reset_value[i]);
 172                else
 173                        ctr_write(i, 0UL);
 174
 175        mtspr(SPRN_PA6T_MMCR0, mmcr0);
 176
 177        oprofile_running = 1;
 178
 179        pr_debug("start on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0);
 180
 181        return 0;
 182}
 183
 184static void pa6t_stop(void)
 185{
 186        u64 mmcr0;
 187
 188        /* freeze counters */
 189        mmcr0 = mfspr(SPRN_PA6T_MMCR0);
 190        mmcr0 |= PA6T_MMCR0_FCM0;
 191        mtspr(SPRN_PA6T_MMCR0, mmcr0);
 192
 193        oprofile_running = 0;
 194
 195        pr_debug("stop on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0);
 196}
 197
 198/* handle the perfmon overflow vector */
 199static void pa6t_handle_interrupt(struct pt_regs *regs,
 200                                  struct op_counter_config *ctr)
 201{
 202        unsigned long pc = mfspr(SPRN_PA6T_SIAR);
 203        int is_kernel = is_kernel_addr(pc);
 204        u64 val;
 205        int i;
 206        u64 mmcr0;
 207
 208        /* disable perfmon counting until rfid */
 209        mmcr0 = mfspr(SPRN_PA6T_MMCR0);
 210        mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS);
 211
 212        /* Record samples. We've got one global bit for whether a sample
 213         * was taken, so add it for any counter that triggered overflow.
 214         */
 215        for (i = 0; i < cur_cpu_spec->num_pmcs; i++) {
 216                val = ctr_read(i);
 217                if (val & (0x1UL << 39)) { /* Overflow bit set */
 218                        if (oprofile_running && ctr[i].enabled) {
 219                                if (mmcr0 & PA6T_MMCR0_SIARLOG)
 220                                        oprofile_add_ext_sample(pc, regs, i, is_kernel);
 221                                ctr_write(i, reset_value[i]);
 222                        } else {
 223                                ctr_write(i, 0UL);
 224                        }
 225                }
 226        }
 227
 228        /* Restore mmcr0 to a good known value since the PMI changes it */
 229        mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS;
 230        mtspr(SPRN_PA6T_MMCR0, mmcr0);
 231}
 232
 233struct 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};
 240