1277745Sbr/*- 2277745Sbr * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com> 3277745Sbr * All rights reserved. 4277745Sbr * 5277745Sbr * This software was developed by SRI International and the University of 6277745Sbr * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7277745Sbr * ("CTSRD"), as part of the DARPA CRASH research programme. 8277745Sbr * 9277745Sbr * Redistribution and use in source and binary forms, with or without 10277745Sbr * modification, are permitted provided that the following conditions 11277745Sbr * are met: 12277745Sbr * 1. Redistributions of source code must retain the above copyright 13277745Sbr * notice, this list of conditions and the following disclaimer. 14277745Sbr * 2. Redistributions in binary form must reproduce the above copyright 15277745Sbr * notice, this list of conditions and the following disclaimer in the 16277745Sbr * documentation and/or other materials provided with the distribution. 17277745Sbr * 18277745Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19277745Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20277745Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21277745Sbr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22277745Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23277745Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24277745Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25277745Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26277745Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27277745Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28277745Sbr * SUCH DAMAGE. 29277745Sbr */ 30277745Sbr 31277745Sbr/* 32277745Sbr * Performance Monitoring Unit 33277745Sbr */ 34277745Sbr 35277745Sbr#include <sys/cdefs.h> 36277745Sbr__FBSDID("$FreeBSD$"); 37277745Sbr 38277745Sbr#include "opt_hwpmc_hooks.h" 39277745Sbr 40277745Sbr#include <sys/param.h> 41277745Sbr#include <sys/systm.h> 42277745Sbr#include <sys/bus.h> 43277745Sbr#include <sys/kernel.h> 44277745Sbr#include <sys/module.h> 45277745Sbr#include <sys/malloc.h> 46277745Sbr#include <sys/rman.h> 47277745Sbr#include <sys/timeet.h> 48277745Sbr#include <sys/timetc.h> 49277745Sbr#include <sys/pmc.h> 50277745Sbr#include <sys/pmckern.h> 51277745Sbr 52277745Sbr#include <dev/fdt/fdt_common.h> 53277745Sbr#include <dev/ofw/openfirm.h> 54277745Sbr#include <dev/ofw/ofw_bus.h> 55277745Sbr#include <dev/ofw/ofw_bus_subr.h> 56277745Sbr 57277745Sbr#include <machine/bus.h> 58277745Sbr#include <machine/cpu.h> 59277745Sbr#include <machine/intr.h> 60277745Sbr 61291210Sandrew#ifdef notyet 62283112Sbr#define MAX_RLEN 8 63291210Sandrew#else 64291210Sandrew#define MAX_RLEN 1 65291210Sandrew#endif 66283112Sbr 67277745Sbrstruct pmu_softc { 68283112Sbr struct resource *res[MAX_RLEN]; 69277745Sbr device_t dev; 70283112Sbr void *ih[MAX_RLEN]; 71277745Sbr}; 72277745Sbr 73277745Sbrstatic struct ofw_compat_data compat_data[] = { 74283112Sbr {"arm,armv8-pmuv3", 1}, 75277745Sbr {"arm,cortex-a17-pmu", 1}, 76277745Sbr {"arm,cortex-a15-pmu", 1}, 77277745Sbr {"arm,cortex-a12-pmu", 1}, 78277745Sbr {"arm,cortex-a9-pmu", 1}, 79277745Sbr {"arm,cortex-a8-pmu", 1}, 80277745Sbr {"arm,cortex-a7-pmu", 1}, 81277745Sbr {"arm,cortex-a5-pmu", 1}, 82277745Sbr {"arm,arm11mpcore-pmu", 1}, 83277745Sbr {"arm,arm1176-pmu", 1}, 84277745Sbr {"arm,arm1136-pmu", 1}, 85277745Sbr {"qcom,krait-pmu", 1}, 86277745Sbr {NULL, 0} 87277745Sbr}; 88277745Sbr 89277745Sbrstatic struct resource_spec pmu_spec[] = { 90277745Sbr { SYS_RES_IRQ, 0, RF_ACTIVE }, 91291210Sandrew /* We don't currently handle pmu events, other than on cpu 0 */ 92291216Sandrew#ifdef notyet 93283112Sbr { SYS_RES_IRQ, 1, RF_ACTIVE | RF_OPTIONAL }, 94283112Sbr { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL }, 95283112Sbr { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL }, 96283112Sbr { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL }, 97283112Sbr { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL }, 98283112Sbr { SYS_RES_IRQ, 6, RF_ACTIVE | RF_OPTIONAL }, 99283112Sbr { SYS_RES_IRQ, 7, RF_ACTIVE | RF_OPTIONAL }, 100291210Sandrew#endif 101277745Sbr { -1, 0 } 102277745Sbr}; 103277745Sbr 104290614Sbz/* CCNT */ 105290614Sbz#if __ARM_ARCH > 6 106290614Sbzint pmu_attched = 0; 107290614Sbzuint32_t ccnt_hi[MAXCPU]; 108290614Sbz#endif 109290614Sbz 110290614Sbz#define PMU_OVSR_C 0x80000000 /* Cycle Counter */ 111290614Sbz#define PMU_IESR_C 0x80000000 /* Cycle Counter */ 112290614Sbz 113277745Sbrstatic int 114277745Sbrpmu_intr(void *arg) 115277745Sbr{ 116290614Sbz#ifdef HWPMC_HOOKS 117277745Sbr struct trapframe *tf; 118290614Sbz#endif 119290614Sbz uint32_t r; 120290614Sbz#if defined(__arm__) && (__ARM_ARCH > 6) 121290614Sbz u_int cpu; 122277745Sbr 123290614Sbz cpu = PCPU_GET(cpuid); 124277745Sbr 125290614Sbz r = cp15_pmovsr_get(); 126290614Sbz if (r & PMU_OVSR_C) { 127290614Sbz atomic_add_32(&ccnt_hi[cpu], 1); 128290614Sbz /* Clear the event. */ 129290614Sbz r &= ~PMU_OVSR_C; 130290614Sbz cp15_pmovsr_set(PMU_OVSR_C); 131290614Sbz } 132290614Sbz#else 133290614Sbz r = 1; 134290614Sbz#endif 135290614Sbz 136277745Sbr#ifdef HWPMC_HOOKS 137290614Sbz /* Only call into the HWPMC framework if we know there is work. */ 138290614Sbz if (r != 0 && pmc_intr) { 139290614Sbz tf = arg; 140277745Sbr (*pmc_intr)(PCPU_GET(cpuid), tf); 141290614Sbz } 142277745Sbr#endif 143277745Sbr 144277745Sbr return (FILTER_HANDLED); 145277745Sbr} 146277745Sbr 147277745Sbrstatic int 148277745Sbrpmu_probe(device_t dev) 149277745Sbr{ 150277745Sbr 151277745Sbr if (!ofw_bus_status_okay(dev)) 152277745Sbr return (ENXIO); 153277745Sbr 154277745Sbr if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { 155277745Sbr device_set_desc(dev, "Performance Monitoring Unit"); 156277745Sbr return (BUS_PROBE_DEFAULT); 157277745Sbr } 158277745Sbr 159277745Sbr return (ENXIO); 160277745Sbr} 161277745Sbr 162277745Sbrstatic int 163277745Sbrpmu_attach(device_t dev) 164277745Sbr{ 165277745Sbr struct pmu_softc *sc; 166290614Sbz#if defined(__arm__) && (__ARM_ARCH > 6) 167290614Sbz uint32_t iesr; 168290614Sbz#endif 169277745Sbr int err; 170283112Sbr int i; 171277745Sbr 172277745Sbr sc = device_get_softc(dev); 173277745Sbr sc->dev = dev; 174277745Sbr 175277745Sbr if (bus_alloc_resources(dev, pmu_spec, sc->res)) { 176277745Sbr device_printf(dev, "could not allocate resources\n"); 177277745Sbr return (ENXIO); 178277745Sbr } 179277745Sbr 180277745Sbr /* Setup interrupt handler */ 181283112Sbr for (i = 0; i < MAX_RLEN; i++) { 182283112Sbr if (sc->res[i] == NULL) 183283112Sbr break; 184283112Sbr 185283112Sbr err = bus_setup_intr(dev, sc->res[i], INTR_MPSAFE | INTR_TYPE_MISC, 186283112Sbr pmu_intr, NULL, NULL, &sc->ih[i]); 187283112Sbr if (err) { 188283112Sbr device_printf(dev, "Unable to setup interrupt handler.\n"); 189283112Sbr return (ENXIO); 190283112Sbr } 191277745Sbr } 192277745Sbr 193290614Sbz#if defined(__arm__) && (__ARM_ARCH > 6) 194290614Sbz /* Initialize to 0. */ 195290614Sbz for (i = 0; i < MAXCPU; i++) 196290614Sbz ccnt_hi[i] = 0; 197290614Sbz 198290614Sbz /* Enable the interrupt to fire on overflow. */ 199290614Sbz iesr = cp15_pminten_get(); 200290614Sbz iesr |= PMU_IESR_C; 201290614Sbz cp15_pminten_set(iesr); 202290614Sbz 203290614Sbz /* Need this for getcyclecount() fast path. */ 204290614Sbz pmu_attched |= 1; 205290614Sbz#endif 206290614Sbz 207277745Sbr return (0); 208277745Sbr} 209277745Sbr 210277745Sbrstatic device_method_t pmu_methods[] = { 211277745Sbr DEVMETHOD(device_probe, pmu_probe), 212277745Sbr DEVMETHOD(device_attach, pmu_attach), 213277745Sbr { 0, 0 } 214277745Sbr}; 215277745Sbr 216277745Sbrstatic driver_t pmu_driver = { 217277745Sbr "pmu", 218277745Sbr pmu_methods, 219277745Sbr sizeof(struct pmu_softc), 220277745Sbr}; 221277745Sbr 222277745Sbrstatic devclass_t pmu_devclass; 223277745Sbr 224277745SbrDRIVER_MODULE(pmu, simplebus, pmu_driver, pmu_devclass, 0, 0); 225