1 /*- 2 * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com> 3 * All rights reserved. 4 * 5 * This software was developed by SRI International and the University of 6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7 * ("CTSRD"), as part of the DARPA CRASH research programme. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * Performance Monitoring Unit 33 */ 34 35 #include <sys/cdefs.h> 36 #include "opt_hwpmc_hooks.h" 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/bus.h> 41 #include <sys/kernel.h> 42 #include <sys/module.h> 43 #include <sys/malloc.h> 44 #include <sys/rman.h> 45 #include <sys/timeet.h> 46 #include <sys/timetc.h> 47 #include <sys/pmc.h> 48 #include <sys/pmckern.h> 49 #include <sys/proc.h> 50 51 #include <machine/bus.h> 52 #include <machine/cpu.h> 53 #include <machine/intr.h> 54 55 #include "pmu.h" 56 57 /* CCNT */ 58 #if defined(__arm__) 59 int pmu_attched = 0; 60 uint32_t ccnt_hi[MAXCPU]; 61 #endif 62 63 #define PMU_OVSR_C 0x80000000 /* Cycle Counter */ 64 #define PMU_IESR_C 0x80000000 /* Cycle Counter */ 65 66 static int 67 pmu_intr(void *arg) 68 { 69 uint32_t r; 70 #if defined(__arm__) 71 u_int cpu; 72 73 cpu = PCPU_GET(cpuid); 74 75 r = cp15_pmovsr_get(); 76 if (r & PMU_OVSR_C) { 77 atomic_add_32(&ccnt_hi[cpu], 1); 78 /* Clear the event. */ 79 r &= ~PMU_OVSR_C; 80 cp15_pmovsr_set(PMU_OVSR_C); 81 } 82 #else 83 r = 1; 84 #endif 85 86 #ifdef HWPMC_HOOKS 87 /* Only call into the HWPMC framework if we know there is work. */ 88 if (r != 0 && pmc_intr) 89 (*pmc_intr)(curthread->td_intr_frame); 90 #endif 91 92 return (FILTER_HANDLED); 93 } 94 95 int 96 pmu_attach(device_t dev) 97 { 98 struct pmu_softc *sc; 99 #if defined(__arm__) 100 uint32_t iesr; 101 #endif 102 int err, i; 103 104 sc = device_get_softc(dev); 105 sc->dev = dev; 106 107 for (i = 0; i < MAX_RLEN; i++) { 108 if (sc->irq[i].res == NULL) 109 break; 110 err = bus_setup_intr(dev, sc->irq[i].res, 111 INTR_MPSAFE | INTR_TYPE_MISC, pmu_intr, NULL, NULL, 112 &sc->irq[i].ih); 113 if (err != 0) { 114 device_printf(dev, 115 "Unable to setup interrupt handler.\n"); 116 goto fail; 117 } 118 if (sc->irq[i].cpuid != -1) { 119 err = bus_bind_intr(dev, sc->irq[i].res, 120 sc->irq[i].cpuid); 121 if (err != 0) { 122 device_printf(sc->dev, 123 "Unable to bind interrupt.\n"); 124 goto fail; 125 } 126 } 127 } 128 129 #if defined(__arm__) 130 /* Initialize to 0. */ 131 for (i = 0; i < MAXCPU; i++) 132 ccnt_hi[i] = 0; 133 134 /* Enable the interrupt to fire on overflow. */ 135 iesr = cp15_pminten_get(); 136 iesr |= PMU_IESR_C; 137 cp15_pminten_set(iesr); 138 139 /* Need this for getcyclecount() fast path. */ 140 pmu_attched |= 1; 141 #endif 142 143 return (0); 144 145 fail: 146 for (i = 1; i < MAX_RLEN; i++) { 147 if (sc->irq[i].ih != NULL) 148 bus_teardown_intr(dev, sc->irq[i].res, sc->irq[i].ih); 149 if (sc->irq[i].res != NULL) 150 bus_release_resource(dev, SYS_RES_IRQ, i, 151 sc->irq[i].res); 152 } 153 return(err); 154 } 155 156