Index: sys/arm/arm/pmu.c =================================================================== --- sys/arm/arm/pmu.c +++ sys/arm/arm/pmu.c @@ -94,16 +94,38 @@ { -1, 0 } }; +/* CCNT */ +uint64_t ccnt_freq; +uint32_t ccnt_hi[MAXCPU]; + +#define PMU_OVSR_C 0x80000000 /* Cycle Counter */ +#define PMU_IESR_C 0x80000000 /* Cycle Counter */ + static int pmu_intr(void *arg) { +#ifdef HWPMC_HOOKS struct trapframe *tf; +#endif + uint32_t r; + u_int cpu; - tf = arg; + cpu = PCPU_GET(cpuid); + + r = cp15_pmovsr_get(); + if (r & PMU_OVSR_C) { + atomic_add_32(&ccnt_hi[cpu], 1); + /* Clear the event. */ + r &= ~PMU_OVSR_C; + cp15_pmovsr_set(PMU_OVSR_C); + } #ifdef HWPMC_HOOKS - if (pmc_intr) + /* Only call into the HWPMC framework if we know there is work. */ + if (r != 0 && pmc_intr) { + tf = arg; (*pmc_intr)(PCPU_GET(cpuid), tf); + } #endif return (FILTER_HANDLED); @@ -128,6 +150,7 @@ pmu_attach(device_t dev) { struct pmu_softc *sc; + uint32_t iesr; int err; int i; @@ -152,6 +175,15 @@ } } + /* Initialize to 0. */ + for (err = 0; err < MAXCPU; err++) + ccnt_hi[err] = 0; + + /* Enable the interrupt to fire on overflow. */ + iesr = cp15_pminten_get(); + iesr |= PMU_IESR_C; + cp15_pminten_set(iesr); + return (0); } @@ -170,3 +202,27 @@ static devclass_t pmu_devclass; DRIVER_MODULE(pmu, simplebus, pmu_driver, pmu_devclass, 0, 0); + + +static void +_calibrate_ccnt_freq(void *arg __unused) +{ + uint64_t ccnt1, ccnt2; + + /* + *"Calibrate" here and provide ccnt_freq. We have a ~4.2s window + * before the counter wraps around (we do not have interrupts to + * catch that yet, so make sure 2 > 1 :-). + */ + ccnt1 = cp15_pmccntr_get(); + DELAY(1000000); + ccnt2 = cp15_pmccntr_get(); + if (ccnt2 < ccnt1) + ccnt2 += 0x100000000; + ccnt_freq = ccnt2 - ccnt1; + if (bootverbose) + printf("CCNT clock: %ju Hz\n", (intmax_t)ccnt_freq); +} +/* SI_SUB_FIRST so it runs before DTrace skew measure. */ +SYSINIT(_calibrate_ccnt_freq, SI_SUB_SMP, SI_ORDER_FIRST, _calibrate_ccnt_freq, NULL); + Index: sys/arm/conf/BEAGLEBONE =================================================================== --- sys/arm/conf/BEAGLEBONE +++ sys/arm/conf/BEAGLEBONE @@ -101,6 +101,9 @@ # Mailbox support device ti_mbox +# PMU support (for CCNT). +device pmu + # USB support device usb options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. Index: sys/arm/include/clock.h =================================================================== --- sys/arm/include/clock.h +++ sys/arm/include/clock.h @@ -29,4 +29,8 @@ #ifndef _MACHINE_CLOCK_H_ #define _MACHINE_CLOCK_H_ +#ifdef DEV_PMU +extern uint64_t ccnt_freq; +#endif + #endif /* !_MACHINE_CLOCK_H_ */ Index: sys/arm/include/cpu.h =================================================================== --- sys/arm/include/cpu.h +++ sys/arm/include/cpu.h @@ -14,12 +14,36 @@ #ifdef _KERNEL #if __ARM_ARCH >= 6 #include -#endif +#ifdef DEV_PMU +#include +#define PMU_OVSR_C 0x80000000 /* Cycle Counter */ +extern uint32_t ccnt_hi[MAXCPU]; +#endif /* DEV_PMU */ +#endif /* __ARM_ARCH > 6 */ + static __inline uint64_t get_cyclecount(void) { -#if __ARM_ARCH >= 6 - return cp15_pmccntr_get(); +#if (__ARM_ARCH > 6) && defined(DEV_PMU) + u_int cpu; + uint64_t h, h2; + uint32_t l, r; + + cpu = PCPU_GET(cpuid); + h = (uint64_t)atomic_load_acq_32(&ccnt_hi[cpu]); + l = cp15_pmccntr_get(); + /* In case interrupts are disabled we need to check for overflow. */ + r = cp15_pmovsr_get(); + if (r & PMU_OVSR_C) { + atomic_add_32(&ccnt_hi[cpu], 1); + /* Clear the event. */ + cp15_pmovsr_set(PMU_OVSR_C); + } + /* Make sure there was no wrap-around while we read the lo half. */ + h2 = (uint64_t)atomic_load_acq_32(&ccnt_hi[cpu]); + if (h != h2) + l = cp15_pmccntr_get(); + return (h2 << 32 | l); #else /* No performance counters, so use binuptime(9). This is slooooow */ struct bintime bt; Index: sys/conf/options.arm =================================================================== --- sys/conf/options.arm +++ sys/conf/options.arm @@ -22,6 +22,7 @@ CPU_XSCALE_IXP435 opt_global.h CPU_XSCALE_PXA2X0 opt_global.h DEV_GIC opt_global.h +DEV_PMU opt_global.h EFI opt_platform.h FLASHADDR opt_global.h GIC_DEFAULT_ICFGR_INIT opt_global.h