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