16411d14dSRuslan Bukin /*- 26411d14dSRuslan Bukin * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com> 36411d14dSRuslan Bukin * All rights reserved. 46411d14dSRuslan Bukin * 56411d14dSRuslan Bukin * This software was developed by SRI International and the University of 66411d14dSRuslan Bukin * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 76411d14dSRuslan Bukin * ("CTSRD"), as part of the DARPA CRASH research programme. 86411d14dSRuslan Bukin * 96411d14dSRuslan Bukin * Redistribution and use in source and binary forms, with or without 106411d14dSRuslan Bukin * modification, are permitted provided that the following conditions 116411d14dSRuslan Bukin * are met: 126411d14dSRuslan Bukin * 1. Redistributions of source code must retain the above copyright 136411d14dSRuslan Bukin * notice, this list of conditions and the following disclaimer. 146411d14dSRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 156411d14dSRuslan Bukin * notice, this list of conditions and the following disclaimer in the 166411d14dSRuslan Bukin * documentation and/or other materials provided with the distribution. 176411d14dSRuslan Bukin * 186411d14dSRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 196411d14dSRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 206411d14dSRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 216411d14dSRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 226411d14dSRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 236411d14dSRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 246411d14dSRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 256411d14dSRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 266411d14dSRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 276411d14dSRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 286411d14dSRuslan Bukin * SUCH DAMAGE. 296411d14dSRuslan Bukin */ 306411d14dSRuslan Bukin 316411d14dSRuslan Bukin #include <sys/cdefs.h> 326411d14dSRuslan Bukin __FBSDID("$FreeBSD$"); 336411d14dSRuslan Bukin 346411d14dSRuslan Bukin #include <sys/param.h> 356411d14dSRuslan Bukin #include <sys/systm.h> 366411d14dSRuslan Bukin #include <sys/pmc.h> 376411d14dSRuslan Bukin #include <sys/pmckern.h> 386411d14dSRuslan Bukin 396411d14dSRuslan Bukin #include <machine/pmc_mdep.h> 406411d14dSRuslan Bukin #include <machine/cpu.h> 416411d14dSRuslan Bukin 426411d14dSRuslan Bukin static int armv7_npmcs; 436411d14dSRuslan Bukin 446411d14dSRuslan Bukin struct armv7_event_code_map { 456411d14dSRuslan Bukin enum pmc_event pe_ev; 466411d14dSRuslan Bukin uint8_t pe_code; 476411d14dSRuslan Bukin }; 486411d14dSRuslan Bukin 49ab632d96SZbigniew Bodek #define PMC_EV_CPU_CYCLES 0xFF 50ab632d96SZbigniew Bodek 516411d14dSRuslan Bukin /* 526411d14dSRuslan Bukin * Per-processor information. 536411d14dSRuslan Bukin */ 546411d14dSRuslan Bukin struct armv7_cpu { 556411d14dSRuslan Bukin struct pmc_hw *pc_armv7pmcs; 566411d14dSRuslan Bukin }; 576411d14dSRuslan Bukin 586411d14dSRuslan Bukin static struct armv7_cpu **armv7_pcpu; 596411d14dSRuslan Bukin 606411d14dSRuslan Bukin /* 616411d14dSRuslan Bukin * Interrupt Enable Set Register 626411d14dSRuslan Bukin */ 636411d14dSRuslan Bukin static __inline void 646411d14dSRuslan Bukin armv7_interrupt_enable(uint32_t pmc) 656411d14dSRuslan Bukin { 666411d14dSRuslan Bukin uint32_t reg; 676411d14dSRuslan Bukin 686411d14dSRuslan Bukin reg = (1 << pmc); 6954384e56SBjoern A. Zeeb cp15_pminten_set(reg); 706411d14dSRuslan Bukin } 716411d14dSRuslan Bukin 726411d14dSRuslan Bukin /* 736411d14dSRuslan Bukin * Interrupt Clear Set Register 746411d14dSRuslan Bukin */ 756411d14dSRuslan Bukin static __inline void 766411d14dSRuslan Bukin armv7_interrupt_disable(uint32_t pmc) 776411d14dSRuslan Bukin { 786411d14dSRuslan Bukin uint32_t reg; 796411d14dSRuslan Bukin 806411d14dSRuslan Bukin reg = (1 << pmc); 8154384e56SBjoern A. Zeeb cp15_pminten_clr(reg); 826411d14dSRuslan Bukin } 836411d14dSRuslan Bukin 846411d14dSRuslan Bukin /* 856411d14dSRuslan Bukin * Counter Set Enable Register 866411d14dSRuslan Bukin */ 876411d14dSRuslan Bukin static __inline void 886411d14dSRuslan Bukin armv7_counter_enable(unsigned int pmc) 896411d14dSRuslan Bukin { 906411d14dSRuslan Bukin uint32_t reg; 916411d14dSRuslan Bukin 926411d14dSRuslan Bukin reg = (1 << pmc); 9354384e56SBjoern A. Zeeb cp15_pmcnten_set(reg); 946411d14dSRuslan Bukin } 956411d14dSRuslan Bukin 966411d14dSRuslan Bukin /* 976411d14dSRuslan Bukin * Counter Clear Enable Register 986411d14dSRuslan Bukin */ 996411d14dSRuslan Bukin static __inline void 1006411d14dSRuslan Bukin armv7_counter_disable(unsigned int pmc) 1016411d14dSRuslan Bukin { 1026411d14dSRuslan Bukin uint32_t reg; 1036411d14dSRuslan Bukin 1046411d14dSRuslan Bukin reg = (1 << pmc); 10554384e56SBjoern A. Zeeb cp15_pmcnten_clr(reg); 1066411d14dSRuslan Bukin } 1076411d14dSRuslan Bukin 1086411d14dSRuslan Bukin /* 1096411d14dSRuslan Bukin * Performance Count Register N 1106411d14dSRuslan Bukin */ 1116411d14dSRuslan Bukin static uint32_t 11290a6e9efSAndrew Turner armv7_pmcn_read(unsigned int pmc, uint32_t evsel) 1136411d14dSRuslan Bukin { 1146411d14dSRuslan Bukin 11590a6e9efSAndrew Turner if (evsel == PMC_EV_CPU_CYCLES) { 11690a6e9efSAndrew Turner return ((uint32_t)cp15_pmccntr_get()); 11790a6e9efSAndrew Turner } 11890a6e9efSAndrew Turner 11954384e56SBjoern A. Zeeb KASSERT(pmc < armv7_npmcs, ("%s: illegal PMC number %d", __func__, pmc)); 1206411d14dSRuslan Bukin 12154384e56SBjoern A. Zeeb cp15_pmselr_set(pmc); 12254384e56SBjoern A. Zeeb return (cp15_pmxevcntr_get()); 1236411d14dSRuslan Bukin } 1246411d14dSRuslan Bukin 1256411d14dSRuslan Bukin static uint32_t 1266411d14dSRuslan Bukin armv7_pmcn_write(unsigned int pmc, uint32_t reg) 1276411d14dSRuslan Bukin { 1286411d14dSRuslan Bukin 12954384e56SBjoern A. Zeeb KASSERT(pmc < armv7_npmcs, ("%s: illegal PMC number %d", __func__, pmc)); 1306411d14dSRuslan Bukin 13154384e56SBjoern A. Zeeb cp15_pmselr_set(pmc); 13254384e56SBjoern A. Zeeb cp15_pmxevcntr_set(reg); 1336411d14dSRuslan Bukin 1346411d14dSRuslan Bukin return (reg); 1356411d14dSRuslan Bukin } 1366411d14dSRuslan Bukin 1376411d14dSRuslan Bukin static int 1386411d14dSRuslan Bukin armv7_allocate_pmc(int cpu, int ri, struct pmc *pm, 1396411d14dSRuslan Bukin const struct pmc_op_pmcallocate *a) 1406411d14dSRuslan Bukin { 1416411d14dSRuslan Bukin enum pmc_event pe; 1423e0bfdd8SRuslan Bukin uint32_t config; 1436411d14dSRuslan Bukin 1446411d14dSRuslan Bukin KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 1456411d14dSRuslan Bukin ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 1466411d14dSRuslan Bukin KASSERT(ri >= 0 && ri < armv7_npmcs, 1476411d14dSRuslan Bukin ("[armv7,%d] illegal row index %d", __LINE__, ri)); 1486411d14dSRuslan Bukin 1496411d14dSRuslan Bukin if (a->pm_class != PMC_CLASS_ARMV7) 1506411d14dSRuslan Bukin return (EINVAL); 1516411d14dSRuslan Bukin pe = a->pm_ev; 1526411d14dSRuslan Bukin 1533e0bfdd8SRuslan Bukin config = (pe & EVENT_ID_MASK); 1546411d14dSRuslan Bukin pm->pm_md.pm_armv7.pm_armv7_evsel = config; 1556411d14dSRuslan Bukin 15662699f34SBjoern A. Zeeb PMCDBG2(MDP, ALL, 2, "armv7-allocate ri=%d -> config=0x%x", ri, config); 1576411d14dSRuslan Bukin 1586411d14dSRuslan Bukin return 0; 1596411d14dSRuslan Bukin } 1606411d14dSRuslan Bukin 1616411d14dSRuslan Bukin 1626411d14dSRuslan Bukin static int 16339f92a76SMitchell Horne armv7_read_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t *v) 1646411d14dSRuslan Bukin { 1656411d14dSRuslan Bukin pmc_value_t tmp; 16690a6e9efSAndrew Turner register_t s; 16790a6e9efSAndrew Turner u_int reg; 1686411d14dSRuslan Bukin 1696411d14dSRuslan Bukin KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 1706411d14dSRuslan Bukin ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 1716411d14dSRuslan Bukin KASSERT(ri >= 0 && ri < armv7_npmcs, 1726411d14dSRuslan Bukin ("[armv7,%d] illegal row index %d", __LINE__, ri)); 1736411d14dSRuslan Bukin 17490a6e9efSAndrew Turner s = intr_disable(); 17590a6e9efSAndrew Turner tmp = armv7_pmcn_read(ri, pm->pm_md.pm_armv7.pm_armv7_evsel); 17690a6e9efSAndrew Turner 17790a6e9efSAndrew Turner /* Check if counter has overflowed */ 178ab632d96SZbigniew Bodek if (pm->pm_md.pm_armv7.pm_armv7_evsel == PMC_EV_CPU_CYCLES) 17990a6e9efSAndrew Turner reg = (1u << 31); 1806411d14dSRuslan Bukin else 18190a6e9efSAndrew Turner reg = (1u << ri); 18290a6e9efSAndrew Turner 18390a6e9efSAndrew Turner if ((cp15_pmovsr_get() & reg) != 0) { 18490a6e9efSAndrew Turner /* Clear Overflow Flag */ 18590a6e9efSAndrew Turner cp15_pmovsr_set(reg); 1866815909aSAndrew Turner pm->pm_pcpu_state[cpu].pps_overflowcnt++; 18790a6e9efSAndrew Turner 18890a6e9efSAndrew Turner /* Reread counter in case we raced. */ 18990a6e9efSAndrew Turner tmp = armv7_pmcn_read(ri, pm->pm_md.pm_armv7.pm_armv7_evsel); 19090a6e9efSAndrew Turner } 1916815909aSAndrew Turner tmp += 0x100000000llu * pm->pm_pcpu_state[cpu].pps_overflowcnt; 19290a6e9efSAndrew Turner intr_restore(s); 1936411d14dSRuslan Bukin 19462699f34SBjoern A. Zeeb PMCDBG2(MDP, REA, 2, "armv7-read id=%d -> %jd", ri, tmp); 195e74c7ffcSJessica Clarke if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { 196e74c7ffcSJessica Clarke /* 197e74c7ffcSJessica Clarke * Clamp value to 0 if the counter just overflowed, 198e74c7ffcSJessica Clarke * otherwise the returned reload count would wrap to a 199e74c7ffcSJessica Clarke * huge value. 200e74c7ffcSJessica Clarke */ 201e74c7ffcSJessica Clarke if ((tmp & (1ull << 63)) == 0) 202e74c7ffcSJessica Clarke tmp = 0; 2036411d14dSRuslan Bukin else 204e74c7ffcSJessica Clarke tmp = ARMV7_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); 205e74c7ffcSJessica Clarke } 2066411d14dSRuslan Bukin *v = tmp; 2076411d14dSRuslan Bukin 2086411d14dSRuslan Bukin return 0; 2096411d14dSRuslan Bukin } 2106411d14dSRuslan Bukin 2116411d14dSRuslan Bukin static int 21239f92a76SMitchell Horne armv7_write_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t v) 2136411d14dSRuslan Bukin { 2146411d14dSRuslan Bukin 2156411d14dSRuslan Bukin KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 2166411d14dSRuslan Bukin ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 2176411d14dSRuslan Bukin KASSERT(ri >= 0 && ri < armv7_npmcs, 2186411d14dSRuslan Bukin ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 2196411d14dSRuslan Bukin 2206411d14dSRuslan Bukin if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 2216411d14dSRuslan Bukin v = ARMV7_RELOAD_COUNT_TO_PERFCTR_VALUE(v); 2226411d14dSRuslan Bukin 22362699f34SBjoern A. Zeeb PMCDBG3(MDP, WRI, 1, "armv7-write cpu=%d ri=%d v=%jx", cpu, ri, v); 2246411d14dSRuslan Bukin 2256815909aSAndrew Turner pm->pm_pcpu_state[cpu].pps_overflowcnt = v >> 32; 226ab632d96SZbigniew Bodek if (pm->pm_md.pm_armv7.pm_armv7_evsel == PMC_EV_CPU_CYCLES) 22754384e56SBjoern A. Zeeb cp15_pmccntr_set(v); 2286411d14dSRuslan Bukin else 2296411d14dSRuslan Bukin armv7_pmcn_write(ri, v); 2306411d14dSRuslan Bukin 2316411d14dSRuslan Bukin return 0; 2326411d14dSRuslan Bukin } 2336411d14dSRuslan Bukin 2346411d14dSRuslan Bukin static int 2356411d14dSRuslan Bukin armv7_config_pmc(int cpu, int ri, struct pmc *pm) 2366411d14dSRuslan Bukin { 2376411d14dSRuslan Bukin struct pmc_hw *phw; 2386411d14dSRuslan Bukin 23962699f34SBjoern A. Zeeb PMCDBG3(MDP, CFG, 1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 2406411d14dSRuslan Bukin 2416411d14dSRuslan Bukin KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 2426411d14dSRuslan Bukin ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 2436411d14dSRuslan Bukin KASSERT(ri >= 0 && ri < armv7_npmcs, 2446411d14dSRuslan Bukin ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 2456411d14dSRuslan Bukin 2466411d14dSRuslan Bukin phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 2476411d14dSRuslan Bukin 2486411d14dSRuslan Bukin KASSERT(pm == NULL || phw->phw_pmc == NULL, 2496411d14dSRuslan Bukin ("[armv7,%d] pm=%p phw->pm=%p hwpmc not unconfigured", 2506411d14dSRuslan Bukin __LINE__, pm, phw->phw_pmc)); 2516411d14dSRuslan Bukin 2526411d14dSRuslan Bukin phw->phw_pmc = pm; 2536411d14dSRuslan Bukin 2546411d14dSRuslan Bukin return 0; 2556411d14dSRuslan Bukin } 2566411d14dSRuslan Bukin 2576411d14dSRuslan Bukin static int 25839f92a76SMitchell Horne armv7_start_pmc(int cpu, int ri, struct pmc *pm) 2596411d14dSRuslan Bukin { 2606411d14dSRuslan Bukin uint32_t config; 2616411d14dSRuslan Bukin 2626411d14dSRuslan Bukin config = pm->pm_md.pm_armv7.pm_armv7_evsel; 2636411d14dSRuslan Bukin 2646411d14dSRuslan Bukin /* 2656411d14dSRuslan Bukin * Configure the event selection. 2666411d14dSRuslan Bukin */ 267ab632d96SZbigniew Bodek if (config != PMC_EV_CPU_CYCLES) { 26854384e56SBjoern A. Zeeb cp15_pmselr_set(ri); 26954384e56SBjoern A. Zeeb cp15_pmxevtyper_set(config); 270ab632d96SZbigniew Bodek } else 271ab632d96SZbigniew Bodek ri = 31; 2726411d14dSRuslan Bukin 2736411d14dSRuslan Bukin /* 2746411d14dSRuslan Bukin * Enable the PMC. 2756411d14dSRuslan Bukin */ 2766411d14dSRuslan Bukin armv7_interrupt_enable(ri); 2776411d14dSRuslan Bukin armv7_counter_enable(ri); 2786411d14dSRuslan Bukin 2796411d14dSRuslan Bukin return 0; 2806411d14dSRuslan Bukin } 2816411d14dSRuslan Bukin 2826411d14dSRuslan Bukin static int 28339f92a76SMitchell Horne armv7_stop_pmc(int cpu, int ri, struct pmc *pm) 2846411d14dSRuslan Bukin { 285ab632d96SZbigniew Bodek uint32_t config; 2866411d14dSRuslan Bukin 287ab632d96SZbigniew Bodek config = pm->pm_md.pm_armv7.pm_armv7_evsel; 288ab632d96SZbigniew Bodek if (config == PMC_EV_CPU_CYCLES) 289ab632d96SZbigniew Bodek ri = 31; 2906411d14dSRuslan Bukin 2916411d14dSRuslan Bukin /* 2926411d14dSRuslan Bukin * Disable the PMCs. 2936411d14dSRuslan Bukin */ 2946411d14dSRuslan Bukin armv7_counter_disable(ri); 2956411d14dSRuslan Bukin armv7_interrupt_disable(ri); 2966411d14dSRuslan Bukin 2976411d14dSRuslan Bukin return 0; 2986411d14dSRuslan Bukin } 2996411d14dSRuslan Bukin 3006411d14dSRuslan Bukin static int 3016411d14dSRuslan Bukin armv7_release_pmc(int cpu, int ri, struct pmc *pmc) 3026411d14dSRuslan Bukin { 303e1988353SJohn Baldwin struct pmc_hw *phw __diagused; 3046411d14dSRuslan Bukin 3056411d14dSRuslan Bukin KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 3066411d14dSRuslan Bukin ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 3076411d14dSRuslan Bukin KASSERT(ri >= 0 && ri < armv7_npmcs, 3086411d14dSRuslan Bukin ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 3096411d14dSRuslan Bukin 3106411d14dSRuslan Bukin phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 3116411d14dSRuslan Bukin KASSERT(phw->phw_pmc == NULL, 3126411d14dSRuslan Bukin ("[armv7,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 3136411d14dSRuslan Bukin 3146411d14dSRuslan Bukin return 0; 3156411d14dSRuslan Bukin } 3166411d14dSRuslan Bukin 3176411d14dSRuslan Bukin static int 318eb7c9019SMatt Macy armv7_intr(struct trapframe *tf) 3196411d14dSRuslan Bukin { 3206411d14dSRuslan Bukin int retval, ri; 3216411d14dSRuslan Bukin struct pmc *pm; 3226411d14dSRuslan Bukin int error; 323eb7c9019SMatt Macy int reg, cpu; 3246411d14dSRuslan Bukin 325fbc27301SIan Lepore cpu = curcpu; 3266411d14dSRuslan Bukin KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 3276411d14dSRuslan Bukin ("[armv7,%d] CPU %d out of range", __LINE__, cpu)); 3286411d14dSRuslan Bukin 3296411d14dSRuslan Bukin retval = 0; 3306411d14dSRuslan Bukin 3316411d14dSRuslan Bukin for (ri = 0; ri < armv7_npmcs; ri++) { 3326411d14dSRuslan Bukin pm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 3336411d14dSRuslan Bukin if (pm == NULL) 3346411d14dSRuslan Bukin continue; 3356411d14dSRuslan Bukin 3366411d14dSRuslan Bukin /* Check if counter has overflowed */ 337ab632d96SZbigniew Bodek if (pm->pm_md.pm_armv7.pm_armv7_evsel == PMC_EV_CPU_CYCLES) 33890a6e9efSAndrew Turner reg = (1u << 31); 3396411d14dSRuslan Bukin else 34090a6e9efSAndrew Turner reg = (1u << ri); 3416411d14dSRuslan Bukin 34254384e56SBjoern A. Zeeb if ((cp15_pmovsr_get() & reg) == 0) { 3436411d14dSRuslan Bukin continue; 3446411d14dSRuslan Bukin } 3456411d14dSRuslan Bukin 3466411d14dSRuslan Bukin /* Clear Overflow Flag */ 34754384e56SBjoern A. Zeeb cp15_pmovsr_set(reg); 3486411d14dSRuslan Bukin 3496411d14dSRuslan Bukin retval = 1; /* Found an interrupting PMC. */ 350ab632d96SZbigniew Bodek 3516815909aSAndrew Turner pm->pm_pcpu_state[cpu].pps_overflowcnt += 1; 352e74c7ffcSJessica Clarke 353e74c7ffcSJessica Clarke if (!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 354ab632d96SZbigniew Bodek continue; 355e74c7ffcSJessica Clarke 3566411d14dSRuslan Bukin if (pm->pm_state != PMC_STATE_RUNNING) 3576411d14dSRuslan Bukin continue; 3586411d14dSRuslan Bukin 359eb7c9019SMatt Macy error = pmc_process_interrupt(PMC_HR, pm, tf); 3606411d14dSRuslan Bukin if (error) 36139f92a76SMitchell Horne armv7_stop_pmc(cpu, ri, pm); 3626411d14dSRuslan Bukin 3636411d14dSRuslan Bukin /* Reload sampling count */ 36439f92a76SMitchell Horne armv7_write_pmc(cpu, ri, pm, pm->pm_sc.pm_reloadcount); 3656411d14dSRuslan Bukin } 3666411d14dSRuslan Bukin 3676411d14dSRuslan Bukin return (retval); 3686411d14dSRuslan Bukin } 3696411d14dSRuslan Bukin 3706411d14dSRuslan Bukin static int 3716411d14dSRuslan Bukin armv7_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 3726411d14dSRuslan Bukin { 3736411d14dSRuslan Bukin struct pmc_hw *phw; 3746411d14dSRuslan Bukin 3756411d14dSRuslan Bukin KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 3766411d14dSRuslan Bukin ("[armv7,%d], illegal CPU %d", __LINE__, cpu)); 3776411d14dSRuslan Bukin KASSERT(ri >= 0 && ri < armv7_npmcs, 3786411d14dSRuslan Bukin ("[armv7,%d] row-index %d out of range", __LINE__, ri)); 3796411d14dSRuslan Bukin 3806411d14dSRuslan Bukin phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 38131610e34SMitchell Horne 38231610e34SMitchell Horne snprintf(pi->pm_name, sizeof(pi->pm_name), "ARMV7-%d", ri); 3836411d14dSRuslan Bukin pi->pm_class = PMC_CLASS_ARMV7; 38431610e34SMitchell Horne 3856411d14dSRuslan Bukin if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 3866411d14dSRuslan Bukin pi->pm_enabled = TRUE; 3876411d14dSRuslan Bukin *ppmc = phw->phw_pmc; 3886411d14dSRuslan Bukin } else { 3896411d14dSRuslan Bukin pi->pm_enabled = FALSE; 3906411d14dSRuslan Bukin *ppmc = NULL; 3916411d14dSRuslan Bukin } 3926411d14dSRuslan Bukin 3936411d14dSRuslan Bukin return (0); 3946411d14dSRuslan Bukin } 3956411d14dSRuslan Bukin 3966411d14dSRuslan Bukin static int 3976411d14dSRuslan Bukin armv7_get_config(int cpu, int ri, struct pmc **ppm) 3986411d14dSRuslan Bukin { 3996411d14dSRuslan Bukin 4006411d14dSRuslan Bukin *ppm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 4016411d14dSRuslan Bukin 4026411d14dSRuslan Bukin return 0; 4036411d14dSRuslan Bukin } 4046411d14dSRuslan Bukin 4056411d14dSRuslan Bukin static int 4066411d14dSRuslan Bukin armv7_pcpu_init(struct pmc_mdep *md, int cpu) 4076411d14dSRuslan Bukin { 4086411d14dSRuslan Bukin struct armv7_cpu *pac; 4096411d14dSRuslan Bukin struct pmc_hw *phw; 4106411d14dSRuslan Bukin struct pmc_cpu *pc; 4116411d14dSRuslan Bukin uint32_t pmnc; 4126411d14dSRuslan Bukin int first_ri; 4136411d14dSRuslan Bukin int i; 4146411d14dSRuslan Bukin 4156411d14dSRuslan Bukin KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 4166411d14dSRuslan Bukin ("[armv7,%d] wrong cpu number %d", __LINE__, cpu)); 4179c0a2d52SMitchell Horne PMCDBG0(MDP, INI, 1, "armv7-pcpu-init"); 4186411d14dSRuslan Bukin 4196411d14dSRuslan Bukin armv7_pcpu[cpu] = pac = malloc(sizeof(struct armv7_cpu), M_PMC, 4206411d14dSRuslan Bukin M_WAITOK|M_ZERO); 4216411d14dSRuslan Bukin 4226411d14dSRuslan Bukin pac->pc_armv7pmcs = malloc(sizeof(struct pmc_hw) * armv7_npmcs, 4236411d14dSRuslan Bukin M_PMC, M_WAITOK|M_ZERO); 4246411d14dSRuslan Bukin pc = pmc_pcpu[cpu]; 4256411d14dSRuslan Bukin first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_ARMV7].pcd_ri; 4266411d14dSRuslan Bukin KASSERT(pc != NULL, ("[armv7,%d] NULL per-cpu pointer", __LINE__)); 4276411d14dSRuslan Bukin 4286411d14dSRuslan Bukin for (i = 0, phw = pac->pc_armv7pmcs; i < armv7_npmcs; i++, phw++) { 4296411d14dSRuslan Bukin phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 4306411d14dSRuslan Bukin PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); 4316411d14dSRuslan Bukin phw->phw_pmc = NULL; 4326411d14dSRuslan Bukin pc->pc_hwpmcs[i + first_ri] = phw; 4336411d14dSRuslan Bukin } 4346411d14dSRuslan Bukin 435ab632d96SZbigniew Bodek pmnc = 0xffffffff; 436ab632d96SZbigniew Bodek cp15_pmcnten_clr(pmnc); 437ab632d96SZbigniew Bodek cp15_pminten_clr(pmnc); 438ab632d96SZbigniew Bodek cp15_pmovsr_set(pmnc); 439ab632d96SZbigniew Bodek 4406411d14dSRuslan Bukin /* Enable unit */ 44154384e56SBjoern A. Zeeb pmnc = cp15_pmcr_get(); 4426411d14dSRuslan Bukin pmnc |= ARMV7_PMNC_ENABLE; 44354384e56SBjoern A. Zeeb cp15_pmcr_set(pmnc); 4446411d14dSRuslan Bukin 4456411d14dSRuslan Bukin return 0; 4466411d14dSRuslan Bukin } 4476411d14dSRuslan Bukin 4486411d14dSRuslan Bukin static int 4496411d14dSRuslan Bukin armv7_pcpu_fini(struct pmc_mdep *md, int cpu) 4506411d14dSRuslan Bukin { 4516411d14dSRuslan Bukin uint32_t pmnc; 4526411d14dSRuslan Bukin 4539c0a2d52SMitchell Horne PMCDBG0(MDP, INI, 1, "armv7-pcpu-fini"); 4549c0a2d52SMitchell Horne 45554384e56SBjoern A. Zeeb pmnc = cp15_pmcr_get(); 4566411d14dSRuslan Bukin pmnc &= ~ARMV7_PMNC_ENABLE; 45754384e56SBjoern A. Zeeb cp15_pmcr_set(pmnc); 4586411d14dSRuslan Bukin 459ab632d96SZbigniew Bodek pmnc = 0xffffffff; 460ab632d96SZbigniew Bodek cp15_pmcnten_clr(pmnc); 461ab632d96SZbigniew Bodek cp15_pminten_clr(pmnc); 462ab632d96SZbigniew Bodek cp15_pmovsr_set(pmnc); 463ab632d96SZbigniew Bodek 4649c0a2d52SMitchell Horne free(armv7_pcpu[cpu]->pc_armv7pmcs, M_PMC); 4659c0a2d52SMitchell Horne free(armv7_pcpu[cpu], M_PMC); 4669c0a2d52SMitchell Horne armv7_pcpu[cpu] = NULL; 4679c0a2d52SMitchell Horne 4686411d14dSRuslan Bukin return 0; 4696411d14dSRuslan Bukin } 4706411d14dSRuslan Bukin 4716411d14dSRuslan Bukin struct pmc_mdep * 47205cef747SAndrew Turner pmc_armv7_initialize(void) 4736411d14dSRuslan Bukin { 4746411d14dSRuslan Bukin struct pmc_mdep *pmc_mdep; 4756411d14dSRuslan Bukin struct pmc_classdep *pcd; 4763e0bfdd8SRuslan Bukin int idcode; 4776411d14dSRuslan Bukin int reg; 4786411d14dSRuslan Bukin 47954384e56SBjoern A. Zeeb reg = cp15_pmcr_get(); 4806411d14dSRuslan Bukin armv7_npmcs = (reg >> ARMV7_PMNC_N_SHIFT) & \ 4816411d14dSRuslan Bukin ARMV7_PMNC_N_MASK; 4823e0bfdd8SRuslan Bukin idcode = (reg & ARMV7_IDCODE_MASK) >> ARMV7_IDCODE_SHIFT; 4836411d14dSRuslan Bukin 48462699f34SBjoern A. Zeeb PMCDBG1(MDP, INI, 1, "armv7-init npmcs=%d", armv7_npmcs); 4856411d14dSRuslan Bukin 4866411d14dSRuslan Bukin /* 4876411d14dSRuslan Bukin * Allocate space for pointers to PMC HW descriptors and for 4886411d14dSRuslan Bukin * the MDEP structure used by MI code. 4896411d14dSRuslan Bukin */ 4906411d14dSRuslan Bukin armv7_pcpu = malloc(sizeof(struct armv7_cpu *) * pmc_cpu_max(), 4916411d14dSRuslan Bukin M_PMC, M_WAITOK | M_ZERO); 4926411d14dSRuslan Bukin 4936411d14dSRuslan Bukin /* Just one class */ 4946411d14dSRuslan Bukin pmc_mdep = pmc_mdep_alloc(1); 4953e0bfdd8SRuslan Bukin 4963e0bfdd8SRuslan Bukin switch (idcode) { 4973e0bfdd8SRuslan Bukin case ARMV7_IDCODE_CORTEX_A9: 4983e0bfdd8SRuslan Bukin pmc_mdep->pmd_cputype = PMC_CPU_ARMV7_CORTEX_A9; 4993e0bfdd8SRuslan Bukin break; 5003e0bfdd8SRuslan Bukin default: 5013e0bfdd8SRuslan Bukin case ARMV7_IDCODE_CORTEX_A8: 5023e0bfdd8SRuslan Bukin /* 5033e0bfdd8SRuslan Bukin * On A8 we implemented common events only, 5043e0bfdd8SRuslan Bukin * so use it for the rest of machines. 5053e0bfdd8SRuslan Bukin */ 5063e0bfdd8SRuslan Bukin pmc_mdep->pmd_cputype = PMC_CPU_ARMV7_CORTEX_A8; 5073e0bfdd8SRuslan Bukin break; 5083e0bfdd8SRuslan Bukin } 5096411d14dSRuslan Bukin 5106411d14dSRuslan Bukin pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_ARMV7]; 5116411d14dSRuslan Bukin pcd->pcd_caps = ARMV7_PMC_CAPS; 5126411d14dSRuslan Bukin pcd->pcd_class = PMC_CLASS_ARMV7; 5136411d14dSRuslan Bukin pcd->pcd_num = armv7_npmcs; 5146411d14dSRuslan Bukin pcd->pcd_ri = pmc_mdep->pmd_npmc; 5156411d14dSRuslan Bukin pcd->pcd_width = 32; 5166411d14dSRuslan Bukin 5176411d14dSRuslan Bukin pcd->pcd_allocate_pmc = armv7_allocate_pmc; 5186411d14dSRuslan Bukin pcd->pcd_config_pmc = armv7_config_pmc; 5196411d14dSRuslan Bukin pcd->pcd_pcpu_fini = armv7_pcpu_fini; 5206411d14dSRuslan Bukin pcd->pcd_pcpu_init = armv7_pcpu_init; 5216411d14dSRuslan Bukin pcd->pcd_describe = armv7_describe; 5226411d14dSRuslan Bukin pcd->pcd_get_config = armv7_get_config; 5236411d14dSRuslan Bukin pcd->pcd_read_pmc = armv7_read_pmc; 5246411d14dSRuslan Bukin pcd->pcd_release_pmc = armv7_release_pmc; 5256411d14dSRuslan Bukin pcd->pcd_start_pmc = armv7_start_pmc; 5266411d14dSRuslan Bukin pcd->pcd_stop_pmc = armv7_stop_pmc; 5276411d14dSRuslan Bukin pcd->pcd_write_pmc = armv7_write_pmc; 5286411d14dSRuslan Bukin 5296411d14dSRuslan Bukin pmc_mdep->pmd_intr = armv7_intr; 5306411d14dSRuslan Bukin pmc_mdep->pmd_npmc += armv7_npmcs; 5316411d14dSRuslan Bukin 5326411d14dSRuslan Bukin return (pmc_mdep); 5336411d14dSRuslan Bukin } 5346411d14dSRuslan Bukin 5356411d14dSRuslan Bukin void 5366411d14dSRuslan Bukin pmc_armv7_finalize(struct pmc_mdep *md) 5376411d14dSRuslan Bukin { 5389c0a2d52SMitchell Horne PMCDBG0(MDP, INI, 1, "armv7-finalize"); 5396411d14dSRuslan Bukin 5409c0a2d52SMitchell Horne free(armv7_pcpu, M_PMC); 5416411d14dSRuslan Bukin } 542