linux/arch/sparc/kernel/pcr.c
<<
>>
Prefs
   1/* pcr.c: Generic sparc64 performance counter infrastructure.
   2 *
   3 * Copyright (C) 2009 David S. Miller (davem@davemloft.net)
   4 */
   5#include <linux/kernel.h>
   6#include <linux/export.h>
   7#include <linux/init.h>
   8#include <linux/irq.h>
   9
  10#include <linux/irq_work.h>
  11#include <linux/ftrace.h>
  12
  13#include <asm/pil.h>
  14#include <asm/pcr.h>
  15#include <asm/nmi.h>
  16#include <asm/spitfire.h>
  17
  18/* This code is shared between various users of the performance
  19 * counters.  Users will be oprofile, pseudo-NMI watchdog, and the
  20 * perf_event support layer.
  21 */
  22
  23#define PCR_SUN4U_ENABLE        (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)
  24#define PCR_N2_ENABLE           (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \
  25                                 PCR_N2_TOE_OV1 | \
  26                                 (2 << PCR_N2_SL1_SHIFT) | \
  27                                 (0xff << PCR_N2_MASK1_SHIFT))
  28
  29u64 pcr_enable;
  30unsigned int picl_shift;
  31
  32/* Performance counter interrupts run unmasked at PIL level 15.
  33 * Therefore we can't do things like wakeups and other work
  34 * that expects IRQ disabling to be adhered to in locking etc.
  35 *
  36 * Therefore in such situations we defer the work by signalling
  37 * a lower level cpu IRQ.
  38 */
  39void __irq_entry deferred_pcr_work_irq(int irq, struct pt_regs *regs)
  40{
  41        struct pt_regs *old_regs;
  42
  43        clear_softint(1 << PIL_DEFERRED_PCR_WORK);
  44
  45        old_regs = set_irq_regs(regs);
  46        irq_enter();
  47#ifdef CONFIG_IRQ_WORK
  48        irq_work_run();
  49#endif
  50        irq_exit();
  51        set_irq_regs(old_regs);
  52}
  53
  54void arch_irq_work_raise(void)
  55{
  56        set_softint(1 << PIL_DEFERRED_PCR_WORK);
  57}
  58
  59const struct pcr_ops *pcr_ops;
  60EXPORT_SYMBOL_GPL(pcr_ops);
  61
  62static u64 direct_pcr_read(void)
  63{
  64        u64 val;
  65
  66        read_pcr(val);
  67        return val;
  68}
  69
  70static void direct_pcr_write(u64 val)
  71{
  72        write_pcr(val);
  73}
  74
  75static const struct pcr_ops direct_pcr_ops = {
  76        .read   = direct_pcr_read,
  77        .write  = direct_pcr_write,
  78};
  79
  80static void n2_pcr_write(u64 val)
  81{
  82        unsigned long ret;
  83
  84        if (val & PCR_N2_HTRACE) {
  85                ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val);
  86                if (ret != HV_EOK)
  87                        write_pcr(val);
  88        } else
  89                write_pcr(val);
  90}
  91
  92static const struct pcr_ops n2_pcr_ops = {
  93        .read   = direct_pcr_read,
  94        .write  = n2_pcr_write,
  95};
  96
  97static unsigned long perf_hsvc_group;
  98static unsigned long perf_hsvc_major;
  99static unsigned long perf_hsvc_minor;
 100
 101static int __init register_perf_hsvc(void)
 102{
 103        if (tlb_type == hypervisor) {
 104                switch (sun4v_chip_type) {
 105                case SUN4V_CHIP_NIAGARA1:
 106                        perf_hsvc_group = HV_GRP_NIAG_PERF;
 107                        break;
 108
 109                case SUN4V_CHIP_NIAGARA2:
 110                        perf_hsvc_group = HV_GRP_N2_CPU;
 111                        break;
 112
 113                case SUN4V_CHIP_NIAGARA3:
 114                        perf_hsvc_group = HV_GRP_KT_CPU;
 115                        break;
 116
 117                default:
 118                        return -ENODEV;
 119                }
 120
 121
 122                perf_hsvc_major = 1;
 123                perf_hsvc_minor = 0;
 124                if (sun4v_hvapi_register(perf_hsvc_group,
 125                                         perf_hsvc_major,
 126                                         &perf_hsvc_minor)) {
 127                        printk("perfmon: Could not register hvapi.\n");
 128                        return -ENODEV;
 129                }
 130        }
 131        return 0;
 132}
 133
 134static void __init unregister_perf_hsvc(void)
 135{
 136        if (tlb_type != hypervisor)
 137                return;
 138        sun4v_hvapi_unregister(perf_hsvc_group);
 139}
 140
 141int __init pcr_arch_init(void)
 142{
 143        int err = register_perf_hsvc();
 144
 145        if (err)
 146                return err;
 147
 148        switch (tlb_type) {
 149        case hypervisor:
 150                pcr_ops = &n2_pcr_ops;
 151                pcr_enable = PCR_N2_ENABLE;
 152                picl_shift = 2;
 153                break;
 154
 155        case cheetah:
 156        case cheetah_plus:
 157                pcr_ops = &direct_pcr_ops;
 158                pcr_enable = PCR_SUN4U_ENABLE;
 159                break;
 160
 161        case spitfire:
 162                /* UltraSPARC-I/II and derivatives lack a profile
 163                 * counter overflow interrupt so we can't make use of
 164                 * their hardware currently.
 165                 */
 166                /* fallthrough */
 167        default:
 168                err = -ENODEV;
 169                goto out_unregister;
 170        }
 171
 172        return nmi_init();
 173
 174out_unregister:
 175        unregister_perf_hsvc();
 176        return err;
 177}
 178