hwpmc_mips24k.c revision 227395
1204635Sgnn/*- 2204635Sgnn * Copyright (c) 2010 George V. Neville-Neil <gnn@freebsd.org> 3204635Sgnn * All rights reserved. 4204635Sgnn * 5204635Sgnn * Redistribution and use in source and binary forms, with or without 6204635Sgnn * modification, are permitted provided that the following conditions 7204635Sgnn * are met: 8204635Sgnn * 1. Redistributions of source code must retain the above copyright 9204635Sgnn * notice, this list of conditions and the following disclaimer. 10204635Sgnn * 2. Redistributions in binary form must reproduce the above copyright 11204635Sgnn * notice, this list of conditions and the following disclaimer in the 12204635Sgnn * documentation and/or other materials provided with the distribution. 13204635Sgnn * 14204635Sgnn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15204635Sgnn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16204635Sgnn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17204635Sgnn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18204635Sgnn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19204635Sgnn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20204635Sgnn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21204635Sgnn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22204635Sgnn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23204635Sgnn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24204635Sgnn * SUCH DAMAGE. 25204635Sgnn * 26204635Sgnn */ 27204635Sgnn 28204635Sgnn#include <sys/cdefs.h> 29204635Sgnn__FBSDID("$FreeBSD: head/sys/dev/hwpmc/hwpmc_mips24k.c 227395 2011-11-09 17:38:27Z adrian $"); 30204635Sgnn 31204635Sgnn#include <sys/param.h> 32204635Sgnn#include <sys/systm.h> 33204635Sgnn#include <sys/pmc.h> 34204635Sgnn#include <sys/pmckern.h> 35204635Sgnn 36204635Sgnn#include <machine/cpu.h> 37204635Sgnn#include <machine/cpufunc.h> 38204635Sgnn#include <machine/cputypes.h> 39204635Sgnn#include <machine/pmc_mdep.h> 40204635Sgnn 41204635Sgnn/* 42204635Sgnn * Support for MIPS CPUs 43204635Sgnn * 44204635Sgnn */ 45204635Sgnnstatic int mips24k_npmcs; 46204635Sgnn 47204635Sgnnstruct mips24k_event_code_map { 48204635Sgnn enum pmc_event pe_ev; /* enum value */ 49204635Sgnn uint8_t pe_counter; /* Which counter this can be counted in. */ 50204635Sgnn uint8_t pe_code; /* numeric code */ 51204635Sgnn}; 52204635Sgnn 53204635Sgnn/* 54204635Sgnn * MIPS event codes are encoded with a select bit. The 55204635Sgnn * select bit is used when writing to CP0 so that we 56204635Sgnn * can select either counter 0/2 or 1/3. The cycle 57204635Sgnn * and instruction counters are special in that they 58204635Sgnn * can be counted on either 0/2 or 1/3. 59204635Sgnn */ 60204635Sgnn 61204635Sgnn#define MIPS24K_ALL 255 /* Count events in any counter. */ 62204635Sgnn#define MIPS24K_CTR_0 0 /* Counter 0 Event */ 63204635Sgnn#define MIPS24K_CTR_1 1 /* Counter 1 Event */ 64204635Sgnn 65204635Sgnnconst struct mips24k_event_code_map mips24k_event_codes[] = { 66204635Sgnn { PMC_EV_MIPS24K_CYCLE, MIPS24K_ALL, 0}, 67204635Sgnn { PMC_EV_MIPS24K_INSTR_EXECUTED, MIPS24K_ALL, 1}, 68204635Sgnn { PMC_EV_MIPS24K_BRANCH_COMPLETED, MIPS24K_CTR_0, 2}, 69204635Sgnn { PMC_EV_MIPS24K_BRANCH_MISPRED, MIPS24K_CTR_1, 2}, 70204635Sgnn { PMC_EV_MIPS24K_RETURN, MIPS24K_CTR_0, 3}, 71204635Sgnn { PMC_EV_MIPS24K_RETURN_MISPRED, MIPS24K_CTR_1, 3}, 72204635Sgnn { PMC_EV_MIPS24K_RETURN_NOT_31, MIPS24K_CTR_0, 4}, 73204635Sgnn { PMC_EV_MIPS24K_RETURN_NOTPRED, MIPS24K_CTR_1, 4}, 74204635Sgnn { PMC_EV_MIPS24K_ITLB_ACCESS, MIPS24K_CTR_0, 5}, 75204635Sgnn { PMC_EV_MIPS24K_ITLB_MISS, MIPS24K_CTR_1, 5}, 76204635Sgnn { PMC_EV_MIPS24K_DTLB_ACCESS, MIPS24K_CTR_0, 6}, 77204635Sgnn { PMC_EV_MIPS24K_DTLB_MISS, MIPS24K_CTR_1, 6}, 78204635Sgnn { PMC_EV_MIPS24K_JTLB_IACCESS, MIPS24K_CTR_0, 7}, 79204635Sgnn { PMC_EV_MIPS24K_JTLB_IMISS, MIPS24K_CTR_1, 7}, 80204635Sgnn { PMC_EV_MIPS24K_JTLB_DACCESS, MIPS24K_CTR_0, 8}, 81204635Sgnn { PMC_EV_MIPS24K_JTLB_DMISS, MIPS24K_CTR_1, 8}, 82204635Sgnn { PMC_EV_MIPS24K_IC_FETCH, MIPS24K_CTR_0, 9}, 83204635Sgnn { PMC_EV_MIPS24K_IC_MISS, MIPS24K_CTR_1, 9}, 84204635Sgnn { PMC_EV_MIPS24K_DC_LOADSTORE, MIPS24K_CTR_0, 10}, 85204635Sgnn { PMC_EV_MIPS24K_DC_WRITEBACK, MIPS24K_CTR_1, 10}, 86204635Sgnn { PMC_EV_MIPS24K_DC_MISS, MIPS24K_ALL, 11}, 87204635Sgnn /* 12 reserved */ 88204635Sgnn { PMC_EV_MIPS24K_STORE_MISS, MIPS24K_CTR_0, 13}, 89204635Sgnn { PMC_EV_MIPS24K_LOAD_MISS, MIPS24K_CTR_1, 13}, 90204635Sgnn { PMC_EV_MIPS24K_INTEGER_COMPLETED, MIPS24K_CTR_0, 14}, 91204635Sgnn { PMC_EV_MIPS24K_FP_COMPLETED, MIPS24K_CTR_1, 14}, 92204635Sgnn { PMC_EV_MIPS24K_LOAD_COMPLETED, MIPS24K_CTR_0, 15}, 93204635Sgnn { PMC_EV_MIPS24K_STORE_COMPLETED, MIPS24K_CTR_1, 15}, 94204635Sgnn { PMC_EV_MIPS24K_BARRIER_COMPLETED, MIPS24K_CTR_0, 16}, 95204635Sgnn { PMC_EV_MIPS24K_MIPS16_COMPLETED, MIPS24K_CTR_1, 16}, 96204635Sgnn { PMC_EV_MIPS24K_NOP_COMPLETED, MIPS24K_CTR_0, 17}, 97204635Sgnn { PMC_EV_MIPS24K_INTEGER_MULDIV_COMPLETED, MIPS24K_CTR_1, 17}, 98204635Sgnn { PMC_EV_MIPS24K_RF_STALL, MIPS24K_CTR_0, 18}, 99204635Sgnn { PMC_EV_MIPS24K_INSTR_REFETCH, MIPS24K_CTR_1, 18}, 100204635Sgnn { PMC_EV_MIPS24K_STORE_COND_COMPLETED, MIPS24K_CTR_0, 19}, 101204635Sgnn { PMC_EV_MIPS24K_STORE_COND_FAILED, MIPS24K_CTR_1, 19}, 102204635Sgnn { PMC_EV_MIPS24K_ICACHE_REQUESTS, MIPS24K_CTR_0, 20}, 103204635Sgnn { PMC_EV_MIPS24K_ICACHE_HIT, MIPS24K_CTR_1, 20}, 104204635Sgnn { PMC_EV_MIPS24K_L2_WRITEBACK, MIPS24K_CTR_0, 21}, 105204635Sgnn { PMC_EV_MIPS24K_L2_ACCESS, MIPS24K_CTR_1, 21}, 106204635Sgnn { PMC_EV_MIPS24K_L2_MISS, MIPS24K_CTR_0, 22}, 107204635Sgnn { PMC_EV_MIPS24K_L2_ERR_CORRECTED, MIPS24K_CTR_1, 22}, 108204635Sgnn { PMC_EV_MIPS24K_EXCEPTIONS, MIPS24K_CTR_0, 23}, 109204635Sgnn /* Event 23 on COP0 1/3 is undefined */ 110204635Sgnn { PMC_EV_MIPS24K_RF_CYCLES_STALLED, MIPS24K_CTR_0, 24}, 111204635Sgnn { PMC_EV_MIPS24K_IFU_CYCLES_STALLED, MIPS24K_CTR_0, 25}, 112204635Sgnn { PMC_EV_MIPS24K_ALU_CYCLES_STALLED, MIPS24K_CTR_1, 25}, 113204635Sgnn /* Events 26 through 32 undefined or reserved to customers */ 114204635Sgnn { PMC_EV_MIPS24K_UNCACHED_LOAD, MIPS24K_CTR_0, 33}, 115204635Sgnn { PMC_EV_MIPS24K_UNCACHED_STORE, MIPS24K_CTR_1, 33}, 116204635Sgnn { PMC_EV_MIPS24K_CP2_REG_TO_REG_COMPLETED, MIPS24K_CTR_0, 35}, 117204635Sgnn { PMC_EV_MIPS24K_MFTC_COMPLETED, MIPS24K_CTR_1, 35}, 118204635Sgnn /* Event 36 reserved */ 119204635Sgnn { PMC_EV_MIPS24K_IC_BLOCKED_CYCLES, MIPS24K_CTR_0, 37}, 120204635Sgnn { PMC_EV_MIPS24K_DC_BLOCKED_CYCLES, MIPS24K_CTR_1, 37}, 121204635Sgnn { PMC_EV_MIPS24K_L2_IMISS_STALL_CYCLES, MIPS24K_CTR_0, 38}, 122204635Sgnn { PMC_EV_MIPS24K_L2_DMISS_STALL_CYCLES, MIPS24K_CTR_1, 38}, 123204635Sgnn { PMC_EV_MIPS24K_DMISS_CYCLES, MIPS24K_CTR_0, 39}, 124204635Sgnn { PMC_EV_MIPS24K_L2_MISS_CYCLES, MIPS24K_CTR_1, 39}, 125204635Sgnn { PMC_EV_MIPS24K_UNCACHED_BLOCK_CYCLES, MIPS24K_CTR_0, 40}, 126204635Sgnn { PMC_EV_MIPS24K_MDU_STALL_CYCLES, MIPS24K_CTR_0, 41}, 127204635Sgnn { PMC_EV_MIPS24K_FPU_STALL_CYCLES, MIPS24K_CTR_1, 41}, 128204635Sgnn { PMC_EV_MIPS24K_CP2_STALL_CYCLES, MIPS24K_CTR_0, 42}, 129204635Sgnn { PMC_EV_MIPS24K_COREXTEND_STALL_CYCLES, MIPS24K_CTR_1, 42}, 130204635Sgnn { PMC_EV_MIPS24K_ISPRAM_STALL_CYCLES, MIPS24K_CTR_0, 43}, 131204635Sgnn { PMC_EV_MIPS24K_DSPRAM_STALL_CYCLES, MIPS24K_CTR_1, 43}, 132204635Sgnn { PMC_EV_MIPS24K_CACHE_STALL_CYCLES, MIPS24K_CTR_0, 44}, 133204635Sgnn /* Event 44 undefined on 1/3 */ 134204635Sgnn { PMC_EV_MIPS24K_LOAD_TO_USE_STALLS, MIPS24K_CTR_0, 45}, 135204635Sgnn { PMC_EV_MIPS24K_BASE_MISPRED_STALLS, MIPS24K_CTR_1, 45}, 136204635Sgnn { PMC_EV_MIPS24K_CPO_READ_STALLS, MIPS24K_CTR_0, 46}, 137204635Sgnn { PMC_EV_MIPS24K_BRANCH_MISPRED_CYCLES, MIPS24K_CTR_1, 46}, 138204635Sgnn /* Event 47 reserved */ 139204635Sgnn { PMC_EV_MIPS24K_IFETCH_BUFFER_FULL, MIPS24K_CTR_0, 48}, 140204635Sgnn { PMC_EV_MIPS24K_FETCH_BUFFER_ALLOCATED, MIPS24K_CTR_1, 48}, 141204635Sgnn { PMC_EV_MIPS24K_EJTAG_ITRIGGER, MIPS24K_CTR_0, 49}, 142204635Sgnn { PMC_EV_MIPS24K_EJTAG_DTRIGGER, MIPS24K_CTR_1, 49}, 143204635Sgnn { PMC_EV_MIPS24K_FSB_LT_QUARTER, MIPS24K_CTR_0, 50}, 144204635Sgnn { PMC_EV_MIPS24K_FSB_QUARTER_TO_HALF, MIPS24K_CTR_1, 50}, 145204635Sgnn { PMC_EV_MIPS24K_FSB_GT_HALF, MIPS24K_CTR_0, 51}, 146204635Sgnn { PMC_EV_MIPS24K_FSB_FULL_PIPELINE_STALLS, MIPS24K_CTR_1, 51}, 147204635Sgnn { PMC_EV_MIPS24K_LDQ_LT_QUARTER, MIPS24K_CTR_0, 52}, 148204635Sgnn { PMC_EV_MIPS24K_LDQ_QUARTER_TO_HALF, MIPS24K_CTR_1, 52}, 149204635Sgnn { PMC_EV_MIPS24K_LDQ_GT_HALF, MIPS24K_CTR_0, 53}, 150204635Sgnn { PMC_EV_MIPS24K_LDQ_FULL_PIPELINE_STALLS, MIPS24K_CTR_1, 53}, 151204635Sgnn { PMC_EV_MIPS24K_WBB_LT_QUARTER, MIPS24K_CTR_0, 54}, 152204635Sgnn { PMC_EV_MIPS24K_WBB_QUARTER_TO_HALF, MIPS24K_CTR_1, 54}, 153204635Sgnn { PMC_EV_MIPS24K_WBB_GT_HALF, MIPS24K_CTR_0, 55}, 154204635Sgnn { PMC_EV_MIPS24K_WBB_FULL_PIPELINE_STALLS, MIPS24K_CTR_1, 55}, 155204635Sgnn /* Events 56-63 reserved */ 156204635Sgnn { PMC_EV_MIPS24K_REQUEST_LATENCY, MIPS24K_CTR_0, 61}, 157204635Sgnn { PMC_EV_MIPS24K_REQUEST_COUNT, MIPS24K_CTR_1, 61} 158204635Sgnn 159204635Sgnn}; 160204635Sgnn 161204635Sgnnconst int mips24k_event_codes_size = 162204635Sgnn sizeof(mips24k_event_codes) / sizeof(mips24k_event_codes[0]); 163204635Sgnn 164204635Sgnn/* 165204635Sgnn * Per-processor information. 166204635Sgnn */ 167204635Sgnnstruct mips24k_cpu { 168204635Sgnn struct pmc_hw *pc_mipspmcs; 169204635Sgnn}; 170204635Sgnn 171204635Sgnnstatic struct mips24k_cpu **mips24k_pcpu; 172204635Sgnn 173204635Sgnn/* 174204635Sgnn * Performance Count Register N 175204635Sgnn */ 176204635Sgnnstatic uint32_t 177204635Sgnnmips24k_pmcn_read(unsigned int pmc) 178204635Sgnn{ 179204635Sgnn uint32_t reg = 0; 180204635Sgnn 181204635Sgnn KASSERT(pmc < mips24k_npmcs, ("[mips,%d] illegal PMC number %d", 182204635Sgnn __LINE__, pmc)); 183204635Sgnn 184204635Sgnn /* The counter value is the next value after the control register. */ 185204635Sgnn switch (pmc) { 186204635Sgnn case 0: 187204635Sgnn reg = mips_rd_perfcnt1(); 188204635Sgnn break; 189204635Sgnn case 1: 190204635Sgnn reg = mips_rd_perfcnt3(); 191204635Sgnn break; 192204635Sgnn default: 193204635Sgnn return 0; 194204635Sgnn } 195204635Sgnn return (reg); 196204635Sgnn} 197204635Sgnn 198204635Sgnnstatic uint32_t 199204635Sgnnmips24k_pmcn_write(unsigned int pmc, uint32_t reg) 200204635Sgnn{ 201204635Sgnn 202204635Sgnn KASSERT(pmc < mips24k_npmcs, ("[mips,%d] illegal PMC number %d", 203204635Sgnn __LINE__, pmc)); 204204635Sgnn 205204635Sgnn switch (pmc) { 206204635Sgnn case 0: 207204635Sgnn mips_wr_perfcnt1(reg); 208204635Sgnn break; 209204635Sgnn case 1: 210204635Sgnn mips_wr_perfcnt3(reg); 211204635Sgnn break; 212204635Sgnn default: 213204635Sgnn return 0; 214204635Sgnn } 215204635Sgnn return (reg); 216204635Sgnn} 217204635Sgnn 218204635Sgnnstatic int 219204635Sgnnmips24k_allocate_pmc(int cpu, int ri, struct pmc *pm, 220204635Sgnn const struct pmc_op_pmcallocate *a) 221204635Sgnn{ 222204635Sgnn enum pmc_event pe; 223204635Sgnn uint32_t caps, config, counter; 224204635Sgnn int i; 225204635Sgnn 226204635Sgnn KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 227204635Sgnn ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 228204635Sgnn KASSERT(ri >= 0 && ri < mips24k_npmcs, 229204635Sgnn ("[mips,%d] illegal row index %d", __LINE__, ri)); 230204635Sgnn 231204635Sgnn caps = a->pm_caps; 232204635Sgnn if (a->pm_class != PMC_CLASS_MIPS24K) 233204635Sgnn return (EINVAL); 234204635Sgnn pe = a->pm_ev; 235204635Sgnn for (i = 0; i < mips24k_event_codes_size; i++) { 236204635Sgnn if (mips24k_event_codes[i].pe_ev == pe) { 237204635Sgnn config = mips24k_event_codes[i].pe_code; 238204635Sgnn counter = mips24k_event_codes[i].pe_counter; 239204635Sgnn break; 240204635Sgnn } 241204635Sgnn } 242204635Sgnn if (i == mips24k_event_codes_size) 243204635Sgnn return (EINVAL); 244204635Sgnn 245204635Sgnn if ((counter != MIPS24K_ALL) && (counter != ri)) 246204635Sgnn return (EINVAL); 247204635Sgnn 248204635Sgnn config <<= MIPS24K_PMC_SELECT; 249204635Sgnn 250204635Sgnn if (caps & PMC_CAP_SYSTEM) 251204635Sgnn config |= (MIPS24K_PMC_SUPER_ENABLE | 252204635Sgnn MIPS24K_PMC_KERNEL_ENABLE); 253204635Sgnn if (caps & PMC_CAP_USER) 254204635Sgnn config |= MIPS24K_PMC_USER_ENABLE; 255204635Sgnn if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0) 256204635Sgnn config |= MIPS24K_PMC_ENABLE; 257227395Sadrian if (caps & PMC_CAP_INTERRUPT) 258227395Sadrian config |= MIPS24K_PMC_INTERRUPT_ENABLE; 259204635Sgnn 260204635Sgnn pm->pm_md.pm_mips24k.pm_mips24k_evsel = config; 261204635Sgnn 262204635Sgnn PMCDBG(MDP,ALL,2,"mips-allocate ri=%d -> config=0x%x", ri, config); 263204635Sgnn 264204635Sgnn return 0; 265204635Sgnn} 266204635Sgnn 267204635Sgnn 268204635Sgnnstatic int 269204635Sgnnmips24k_read_pmc(int cpu, int ri, pmc_value_t *v) 270204635Sgnn{ 271204635Sgnn struct pmc *pm; 272204635Sgnn pmc_value_t tmp; 273204635Sgnn 274204635Sgnn KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 275204635Sgnn ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 276204635Sgnn KASSERT(ri >= 0 && ri < mips24k_npmcs, 277204635Sgnn ("[mips,%d] illegal row index %d", __LINE__, ri)); 278204635Sgnn 279204635Sgnn pm = mips24k_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 280204635Sgnn tmp = mips24k_pmcn_read(ri); 281204635Sgnn PMCDBG(MDP,REA,2,"mips-read id=%d -> %jd", ri, tmp); 282204635Sgnn if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 283204635Sgnn *v = MIPS24K_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); 284204635Sgnn else 285204635Sgnn *v = tmp; 286204635Sgnn 287204635Sgnn return 0; 288204635Sgnn} 289204635Sgnn 290204635Sgnnstatic int 291204635Sgnnmips24k_write_pmc(int cpu, int ri, pmc_value_t v) 292204635Sgnn{ 293204635Sgnn struct pmc *pm; 294204635Sgnn 295204635Sgnn KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 296204635Sgnn ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 297204635Sgnn KASSERT(ri >= 0 && ri < mips24k_npmcs, 298204635Sgnn ("[mips,%d] illegal row-index %d", __LINE__, ri)); 299204635Sgnn 300204635Sgnn pm = mips24k_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 301204635Sgnn 302204635Sgnn if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 303204635Sgnn v = MIPS24K_RELOAD_COUNT_TO_PERFCTR_VALUE(v); 304204635Sgnn 305204635Sgnn PMCDBG(MDP,WRI,1,"mips-write cpu=%d ri=%d v=%jx", cpu, ri, v); 306204635Sgnn 307204635Sgnn mips24k_pmcn_write(ri, v); 308204635Sgnn 309204635Sgnn return 0; 310204635Sgnn} 311204635Sgnn 312204635Sgnnstatic int 313204635Sgnnmips24k_config_pmc(int cpu, int ri, struct pmc *pm) 314204635Sgnn{ 315204635Sgnn struct pmc_hw *phw; 316204635Sgnn 317204635Sgnn PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 318204635Sgnn 319204635Sgnn KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 320204635Sgnn ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 321204635Sgnn KASSERT(ri >= 0 && ri < mips24k_npmcs, 322204635Sgnn ("[mips,%d] illegal row-index %d", __LINE__, ri)); 323204635Sgnn 324204635Sgnn phw = &mips24k_pcpu[cpu]->pc_mipspmcs[ri]; 325204635Sgnn 326204635Sgnn KASSERT(pm == NULL || phw->phw_pmc == NULL, 327204635Sgnn ("[mips,%d] pm=%p phw->pm=%p hwpmc not unconfigured", 328204635Sgnn __LINE__, pm, phw->phw_pmc)); 329204635Sgnn 330204635Sgnn phw->phw_pmc = pm; 331204635Sgnn 332204635Sgnn return 0; 333204635Sgnn} 334204635Sgnn 335204635Sgnnstatic int 336204635Sgnnmips24k_start_pmc(int cpu, int ri) 337204635Sgnn{ 338204635Sgnn uint32_t config; 339204635Sgnn struct pmc *pm; 340204635Sgnn struct pmc_hw *phw; 341204635Sgnn 342204635Sgnn phw = &mips24k_pcpu[cpu]->pc_mipspmcs[ri]; 343204635Sgnn pm = phw->phw_pmc; 344204635Sgnn config = pm->pm_md.pm_mips24k.pm_mips24k_evsel; 345204635Sgnn 346204635Sgnn /* Enable the PMC. */ 347204635Sgnn switch (ri) { 348204635Sgnn case 0: 349204635Sgnn mips_wr_perfcnt0(config); 350204635Sgnn break; 351204635Sgnn case 1: 352204635Sgnn mips_wr_perfcnt2(config); 353204635Sgnn break; 354204635Sgnn default: 355204635Sgnn break; 356204635Sgnn } 357204635Sgnn 358204635Sgnn return 0; 359204635Sgnn} 360204635Sgnn 361204635Sgnnstatic int 362204635Sgnnmips24k_stop_pmc(int cpu, int ri) 363204635Sgnn{ 364204635Sgnn struct pmc *pm; 365204635Sgnn struct pmc_hw *phw; 366204635Sgnn 367204635Sgnn phw = &mips24k_pcpu[cpu]->pc_mipspmcs[ri]; 368204635Sgnn pm = phw->phw_pmc; 369204635Sgnn 370204635Sgnn /* 371204635Sgnn * Disable the PMCs. 372204635Sgnn * 373204635Sgnn * Clearing the entire register turns the counter off as well 374204635Sgnn * as removes the previously sampled event. 375204635Sgnn */ 376204635Sgnn switch (ri) { 377204635Sgnn case 0: 378204635Sgnn mips_wr_perfcnt0(0); 379204635Sgnn break; 380204635Sgnn case 1: 381204635Sgnn mips_wr_perfcnt2(0); 382204635Sgnn break; 383204635Sgnn default: 384204635Sgnn break; 385204635Sgnn } 386204635Sgnn return 0; 387204635Sgnn} 388204635Sgnn 389204635Sgnnstatic int 390204635Sgnnmips24k_release_pmc(int cpu, int ri, struct pmc *pmc) 391204635Sgnn{ 392204635Sgnn struct pmc_hw *phw; 393204635Sgnn 394204635Sgnn KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 395204635Sgnn ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 396204635Sgnn KASSERT(ri >= 0 && ri < mips24k_npmcs, 397204635Sgnn ("[mips,%d] illegal row-index %d", __LINE__, ri)); 398204635Sgnn 399204635Sgnn phw = &mips24k_pcpu[cpu]->pc_mipspmcs[ri]; 400204635Sgnn KASSERT(phw->phw_pmc == NULL, 401204635Sgnn ("[mips,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 402204635Sgnn 403204635Sgnn return 0; 404204635Sgnn} 405204635Sgnn 406204635Sgnnstatic int 407204635Sgnnmips24k_intr(int cpu, struct trapframe *tf) 408204635Sgnn{ 409227395Sadrian int error; 410227395Sadrian int retval, ri; 411227395Sadrian struct pmc *pm; 412227395Sadrian struct mips24k_cpu *pc; 413227395Sadrian uint32_t r, r0, r2; 414227395Sadrian 415227395Sadrian KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 416227395Sadrian ("[mips24k,%d] CPU %d out of range", __LINE__, cpu)); 417227395Sadrian 418227395Sadrian retval = 0; 419227395Sadrian pc = mips24k_pcpu[cpu]; 420227395Sadrian 421227395Sadrian /* Stop PMCs without clearing the counter */ 422227395Sadrian r0 = mips_rd_perfcnt0(); 423227395Sadrian mips_wr_perfcnt0(r0 & ~(0x1f)); 424227395Sadrian r2 = mips_rd_perfcnt2(); 425227395Sadrian mips_wr_perfcnt2(r2 & ~(0x1f)); 426227395Sadrian 427227395Sadrian for (ri = 0; ri < mips24k_npmcs; ri++) { 428227395Sadrian pm = mips24k_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 429227395Sadrian if (pm == NULL) 430227395Sadrian continue; 431227395Sadrian if (! PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 432227395Sadrian continue; 433227395Sadrian 434227395Sadrian r = mips24k_pmcn_read(ri); 435227395Sadrian 436227395Sadrian /* If bit 31 is set, the counter has overflowed */ 437227395Sadrian if ((r & 0x80000000) == 0) 438227395Sadrian continue; 439227395Sadrian 440227395Sadrian retval = 1; 441227395Sadrian if (pm->pm_state != PMC_STATE_RUNNING) 442227395Sadrian continue; 443227395Sadrian error = pmc_process_interrupt(cpu, pm, tf, 444227395Sadrian TRAPF_USERMODE(tf)); 445227395Sadrian if (error) { 446227395Sadrian /* Clear/disable the relevant counter */ 447227395Sadrian if (ri == 0) 448227395Sadrian r0 = 0; 449227395Sadrian else if (ri == 1) 450227395Sadrian r2 = 0; 451227395Sadrian mips24k_stop_pmc(cpu, ri); 452227395Sadrian } 453227395Sadrian 454227395Sadrian /* Reload sampling count */ 455227395Sadrian mips24k_write_pmc(cpu, ri, pm->pm_sc.pm_reloadcount); 456227395Sadrian } 457227395Sadrian 458227395Sadrian /* 459227395Sadrian * Re-enable the PMC counters where they left off. 460227395Sadrian * 461227395Sadrian * Any counter which overflowed will have its sample count 462227395Sadrian * reloaded in the loop above. 463227395Sadrian */ 464227395Sadrian mips_wr_perfcnt0(r0); 465227395Sadrian mips_wr_perfcnt2(r2); 466227395Sadrian 467227395Sadrian return retval; 468204635Sgnn} 469204635Sgnn 470204635Sgnnstatic int 471204635Sgnnmips24k_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 472204635Sgnn{ 473204635Sgnn int error; 474204635Sgnn struct pmc_hw *phw; 475204635Sgnn char mips24k_name[PMC_NAME_MAX]; 476204635Sgnn 477204635Sgnn KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 478204635Sgnn ("[mips,%d], illegal CPU %d", __LINE__, cpu)); 479204635Sgnn KASSERT(ri >= 0 && ri < mips24k_npmcs, 480204635Sgnn ("[mips,%d] row-index %d out of range", __LINE__, ri)); 481204635Sgnn 482204635Sgnn phw = &mips24k_pcpu[cpu]->pc_mipspmcs[ri]; 483204635Sgnn snprintf(mips24k_name, sizeof(mips24k_name), "MIPS-%d", ri); 484204635Sgnn if ((error = copystr(mips24k_name, pi->pm_name, PMC_NAME_MAX, 485204635Sgnn NULL)) != 0) 486204635Sgnn return error; 487204635Sgnn pi->pm_class = PMC_CLASS_MIPS24K; 488204635Sgnn if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 489204635Sgnn pi->pm_enabled = TRUE; 490204635Sgnn *ppmc = phw->phw_pmc; 491204635Sgnn } else { 492204635Sgnn pi->pm_enabled = FALSE; 493204635Sgnn *ppmc = NULL; 494204635Sgnn } 495204635Sgnn 496204635Sgnn return (0); 497204635Sgnn} 498204635Sgnn 499204635Sgnnstatic int 500204635Sgnnmips24k_get_config(int cpu, int ri, struct pmc **ppm) 501204635Sgnn{ 502204635Sgnn *ppm = mips24k_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 503204635Sgnn 504204635Sgnn return 0; 505204635Sgnn} 506204635Sgnn 507204635Sgnn/* 508204635Sgnn * XXX don't know what we should do here. 509204635Sgnn */ 510204635Sgnnstatic int 511204635Sgnnmips24k_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) 512204635Sgnn{ 513204635Sgnn return 0; 514204635Sgnn} 515204635Sgnn 516204635Sgnnstatic int 517204635Sgnnmips24k_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) 518204635Sgnn{ 519204635Sgnn return 0; 520204635Sgnn} 521204635Sgnn 522204635Sgnnstatic int 523204635Sgnnmips24k_pcpu_init(struct pmc_mdep *md, int cpu) 524204635Sgnn{ 525204635Sgnn int first_ri, i; 526204635Sgnn struct pmc_cpu *pc; 527204635Sgnn struct mips24k_cpu *pac; 528204635Sgnn struct pmc_hw *phw; 529204635Sgnn 530204635Sgnn KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 531204635Sgnn ("[mips,%d] wrong cpu number %d", __LINE__, cpu)); 532204635Sgnn PMCDBG(MDP,INI,1,"mips-init cpu=%d", cpu); 533204635Sgnn 534204635Sgnn mips24k_pcpu[cpu] = pac = malloc(sizeof(struct mips24k_cpu), M_PMC, 535204635Sgnn M_WAITOK|M_ZERO); 536204635Sgnn pac->pc_mipspmcs = malloc(sizeof(struct pmc_hw) * mips24k_npmcs, 537204635Sgnn M_PMC, M_WAITOK|M_ZERO); 538204635Sgnn pc = pmc_pcpu[cpu]; 539204635Sgnn first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS24K].pcd_ri; 540204635Sgnn KASSERT(pc != NULL, ("[mips,%d] NULL per-cpu pointer", __LINE__)); 541204635Sgnn 542204635Sgnn for (i = 0, phw = pac->pc_mipspmcs; i < mips24k_npmcs; i++, phw++) { 543204635Sgnn phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 544204635Sgnn PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); 545204635Sgnn phw->phw_pmc = NULL; 546204635Sgnn pc->pc_hwpmcs[i + first_ri] = phw; 547204635Sgnn } 548204635Sgnn 549204635Sgnn /* 550204635Sgnn * Clear the counter control register which has the effect 551204635Sgnn * of disabling counting. 552204635Sgnn */ 553204635Sgnn for (i = 0; i < mips24k_npmcs; i++) 554204635Sgnn mips24k_pmcn_write(i, 0); 555204635Sgnn 556204635Sgnn return 0; 557204635Sgnn} 558204635Sgnn 559204635Sgnnstatic int 560204635Sgnnmips24k_pcpu_fini(struct pmc_mdep *md, int cpu) 561204635Sgnn{ 562204635Sgnn return 0; 563204635Sgnn} 564204635Sgnn 565204635Sgnnstruct pmc_mdep * 566204635Sgnnpmc_mips24k_initialize() 567204635Sgnn{ 568204635Sgnn struct pmc_mdep *pmc_mdep; 569204635Sgnn struct pmc_classdep *pcd; 570204635Sgnn 571204635Sgnn /* 572204635Sgnn * Read the counter control registers from CP0 573204635Sgnn * to determine the number of available PMCs. 574204635Sgnn * The control registers use bit 31 as a "more" bit. 575204635Sgnn * 576204635Sgnn * XXX: With the current macros it is hard to read the 577204635Sgnn * CP0 registers in any varied way. 578204635Sgnn */ 579204635Sgnn mips24k_npmcs = 2; 580204635Sgnn 581204635Sgnn PMCDBG(MDP,INI,1,"mips-init npmcs=%d", mips24k_npmcs); 582204635Sgnn 583204635Sgnn /* 584204635Sgnn * Allocate space for pointers to PMC HW descriptors and for 585204635Sgnn * the MDEP structure used by MI code. 586204635Sgnn */ 587204635Sgnn mips24k_pcpu = malloc(sizeof(struct mips24k_cpu *) * pmc_cpu_max(), M_PMC, 588204635Sgnn M_WAITOK|M_ZERO); 589204635Sgnn 590204635Sgnn /* Just one class */ 591204635Sgnn pmc_mdep = malloc(sizeof(struct pmc_mdep) + sizeof(struct pmc_classdep), 592204635Sgnn M_PMC, M_WAITOK|M_ZERO); 593204635Sgnn 594204635Sgnn pmc_mdep->pmd_cputype = PMC_CPU_MIPS_24K; 595204635Sgnn pmc_mdep->pmd_nclass = 1; 596204635Sgnn 597204635Sgnn pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS24K]; 598204635Sgnn pcd->pcd_caps = MIPS24K_PMC_CAPS; 599204635Sgnn pcd->pcd_class = PMC_CLASS_MIPS24K; 600204635Sgnn pcd->pcd_num = mips24k_npmcs; 601204635Sgnn pcd->pcd_ri = pmc_mdep->pmd_npmc; 602204635Sgnn pcd->pcd_width = 32; /* XXX: Fix for 64 bit MIPS */ 603204635Sgnn 604204635Sgnn pcd->pcd_allocate_pmc = mips24k_allocate_pmc; 605204635Sgnn pcd->pcd_config_pmc = mips24k_config_pmc; 606204635Sgnn pcd->pcd_pcpu_fini = mips24k_pcpu_fini; 607204635Sgnn pcd->pcd_pcpu_init = mips24k_pcpu_init; 608204635Sgnn pcd->pcd_describe = mips24k_describe; 609204635Sgnn pcd->pcd_get_config = mips24k_get_config; 610204635Sgnn pcd->pcd_read_pmc = mips24k_read_pmc; 611204635Sgnn pcd->pcd_release_pmc = mips24k_release_pmc; 612204635Sgnn pcd->pcd_start_pmc = mips24k_start_pmc; 613204635Sgnn pcd->pcd_stop_pmc = mips24k_stop_pmc; 614204635Sgnn pcd->pcd_write_pmc = mips24k_write_pmc; 615204635Sgnn 616204635Sgnn pmc_mdep->pmd_intr = mips24k_intr; 617204635Sgnn pmc_mdep->pmd_switch_in = mips24k_switch_in; 618204635Sgnn pmc_mdep->pmd_switch_out = mips24k_switch_out; 619204635Sgnn 620204635Sgnn pmc_mdep->pmd_npmc += mips24k_npmcs; 621204635Sgnn 622204635Sgnn return (pmc_mdep); 623204635Sgnn} 624204635Sgnn 625204635Sgnnvoid 626204635Sgnnpmc_mips24k_finalize(struct pmc_mdep *md) 627204635Sgnn{ 628204635Sgnn (void) md; 629204635Sgnn} 630204635Sgnn 631