1200928Srpaulo/*- 2200928Srpaulo * Copyright (c) 2009 Rui Paulo <rpaulo@FreeBSD.org> 3200928Srpaulo * All rights reserved. 4200928Srpaulo * 5200928Srpaulo * Redistribution and use in source and binary forms, with or without 6200928Srpaulo * modification, are permitted provided that the following conditions 7200928Srpaulo * are met: 8200928Srpaulo * 1. Redistributions of source code must retain the above copyright 9200928Srpaulo * notice, this list of conditions and the following disclaimer. 10200928Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11200928Srpaulo * notice, this list of conditions and the following disclaimer in the 12200928Srpaulo * documentation and/or other materials provided with the distribution. 13200928Srpaulo * 14200928Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15200928Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16200928Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17200928Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18200928Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19200928Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20200928Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21200928Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22200928Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23200928Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24200928Srpaulo * SUCH DAMAGE. 25200928Srpaulo * 26200928Srpaulo */ 27200928Srpaulo 28200928Srpaulo#include <sys/cdefs.h> 29200928Srpaulo__FBSDID("$FreeBSD: releng/11.0/sys/dev/hwpmc/hwpmc_xscale.c 298411 2016-04-21 15:38:28Z pfg $"); 30200928Srpaulo 31200928Srpaulo#include <sys/param.h> 32200928Srpaulo#include <sys/systm.h> 33200928Srpaulo#include <sys/pmc.h> 34200928Srpaulo#include <sys/pmckern.h> 35200928Srpaulo 36200928Srpaulo#include <machine/pmc_mdep.h> 37200928Srpaulo/* 38200928Srpaulo * Support for the Intel XScale network processors 39200928Srpaulo * 40200928Srpaulo * XScale processors have up to now three generations. 41200928Srpaulo * 42200928Srpaulo * The first generation has two PMC; the event selection, interrupt config 43200928Srpaulo * and overflow flag setup are done by writing to the PMNC register. 44200928Srpaulo * It also has less monitoring events than the latter generations. 45200928Srpaulo * 46200928Srpaulo * The second and third generatiosn have four PMCs, one register for the event 47200928Srpaulo * selection, one register for the interrupt config and one register for 48200928Srpaulo * the overflow flags. 49200928Srpaulo */ 50200928Srpaulostatic int xscale_npmcs; 51200928Srpaulostatic int xscale_gen; /* XScale Core generation */ 52200928Srpaulo 53200928Srpaulostruct xscale_event_code_map { 54200928Srpaulo enum pmc_event pe_ev; 55200928Srpaulo uint8_t pe_code; 56200928Srpaulo}; 57200928Srpaulo 58200928Srpauloconst struct xscale_event_code_map xscale_event_codes[] = { 59200928Srpaulo /* 1st and 2nd Generation XScale cores */ 60200928Srpaulo { PMC_EV_XSCALE_IC_FETCH, 0x00 }, 61200928Srpaulo { PMC_EV_XSCALE_IC_MISS, 0x01 }, 62200928Srpaulo { PMC_EV_XSCALE_DATA_DEPENDENCY_STALLED,0x02 }, 63200928Srpaulo { PMC_EV_XSCALE_ITLB_MISS, 0x03 }, 64200928Srpaulo { PMC_EV_XSCALE_DTLB_MISS, 0x04 }, 65200928Srpaulo { PMC_EV_XSCALE_BRANCH_RETIRED, 0x05 }, 66200928Srpaulo { PMC_EV_XSCALE_BRANCH_MISPRED, 0x06 }, 67200928Srpaulo { PMC_EV_XSCALE_INSTR_RETIRED, 0x07 }, 68200928Srpaulo { PMC_EV_XSCALE_DC_FULL_CYCLE, 0x08 }, 69200928Srpaulo { PMC_EV_XSCALE_DC_FULL_CONTIG, 0x09 }, 70200928Srpaulo { PMC_EV_XSCALE_DC_ACCESS, 0x0a }, 71200928Srpaulo { PMC_EV_XSCALE_DC_MISS, 0x0b }, 72200928Srpaulo { PMC_EV_XSCALE_DC_WRITEBACK, 0x0c }, 73200928Srpaulo { PMC_EV_XSCALE_PC_CHANGE, 0x0d }, 74200928Srpaulo /* 3rd Generation XScale cores */ 75200928Srpaulo { PMC_EV_XSCALE_BRANCH_RETIRED_ALL, 0x0e }, 76200928Srpaulo { PMC_EV_XSCALE_INSTR_CYCLE, 0x0f }, 77200928Srpaulo { PMC_EV_XSCALE_CP_STALL, 0x17 }, 78200928Srpaulo { PMC_EV_XSCALE_PC_CHANGE_ALL, 0x18 }, 79200928Srpaulo { PMC_EV_XSCALE_PIPELINE_FLUSH, 0x19 }, 80200928Srpaulo { PMC_EV_XSCALE_BACKEND_STALL, 0x1a }, 81200928Srpaulo { PMC_EV_XSCALE_MULTIPLIER_USE, 0x1b }, 82200928Srpaulo { PMC_EV_XSCALE_MULTIPLIER_STALLED, 0x1c }, 83200928Srpaulo { PMC_EV_XSCALE_DATA_CACHE_STALLED, 0x1e }, 84200928Srpaulo { PMC_EV_XSCALE_L2_CACHE_REQ, 0x20 }, 85200928Srpaulo { PMC_EV_XSCALE_L2_CACHE_MISS, 0x23 }, 86200928Srpaulo { PMC_EV_XSCALE_ADDRESS_BUS_TRANS, 0x40 }, 87200928Srpaulo { PMC_EV_XSCALE_SELF_ADDRESS_BUS_TRANS, 0x41 }, 88200928Srpaulo { PMC_EV_XSCALE_DATA_BUS_TRANS, 0x48 }, 89200928Srpaulo}; 90200928Srpaulo 91200928Srpaulo/* 92200928Srpaulo * Per-processor information. 93200928Srpaulo */ 94200928Srpaulostruct xscale_cpu { 95200928Srpaulo struct pmc_hw *pc_xscalepmcs; 96200928Srpaulo}; 97200928Srpaulo 98200928Srpaulostatic struct xscale_cpu **xscale_pcpu; 99200928Srpaulo 100200928Srpaulo/* 101200928Srpaulo * Performance Monitor Control Register 102200928Srpaulo */ 103200928Srpaulostatic __inline uint32_t 104200928Srpauloxscale_pmnc_read(void) 105200928Srpaulo{ 106200928Srpaulo uint32_t reg; 107200928Srpaulo 108200928Srpaulo __asm __volatile("mrc p14, 0, %0, c0, c1, 0" : "=r" (reg)); 109200928Srpaulo 110200928Srpaulo return (reg); 111200928Srpaulo} 112200928Srpaulo 113200928Srpaulostatic __inline void 114200928Srpauloxscale_pmnc_write(uint32_t reg) 115200928Srpaulo{ 116200928Srpaulo __asm __volatile("mcr p14, 0, %0, c0, c1, 0" : : "r" (reg)); 117200928Srpaulo} 118200928Srpaulo 119200928Srpaulo/* 120200928Srpaulo * Clock Counter Register 121200928Srpaulo */ 122200928Srpaulostatic __inline uint32_t 123200928Srpauloxscale_ccnt_read(void) 124200928Srpaulo{ 125200928Srpaulo uint32_t reg; 126200928Srpaulo 127200928Srpaulo __asm __volatile("mrc p14, 0, %0, c1, c1, 0" : "=r" (reg)); 128200928Srpaulo 129200928Srpaulo return (reg); 130200928Srpaulo} 131200928Srpaulo 132200928Srpaulostatic __inline void 133200928Srpauloxscale_ccnt_write(uint32_t reg) 134200928Srpaulo{ 135200928Srpaulo __asm __volatile("mcr p14, 0, %0, c1, c1, 0" : : "r" (reg)); 136200928Srpaulo} 137200928Srpaulo 138200928Srpaulo/* 139200928Srpaulo * Interrupt Enable Register 140200928Srpaulo */ 141200928Srpaulostatic __inline uint32_t 142200928Srpauloxscale_inten_read(void) 143200928Srpaulo{ 144200928Srpaulo uint32_t reg; 145200928Srpaulo 146200928Srpaulo __asm __volatile("mrc p14, 0, %0, c4, c1, 0" : "=r" (reg)); 147200928Srpaulo 148200928Srpaulo return (reg); 149200928Srpaulo} 150200928Srpaulo 151200928Srpaulostatic __inline void 152200928Srpauloxscale_inten_write(uint32_t reg) 153200928Srpaulo{ 154200928Srpaulo __asm __volatile("mcr p14, 0, %0, c4, c1, 0" : : "r" (reg)); 155200928Srpaulo} 156200928Srpaulo 157200928Srpaulo/* 158200928Srpaulo * Overflow Flag Register 159200928Srpaulo */ 160200928Srpaulostatic __inline uint32_t 161200928Srpauloxscale_flag_read(void) 162200928Srpaulo{ 163200928Srpaulo uint32_t reg; 164200928Srpaulo 165200928Srpaulo __asm __volatile("mrc p14, 0, %0, c5, c1, 0" : "=r" (reg)); 166200928Srpaulo 167200928Srpaulo return (reg); 168200928Srpaulo} 169200928Srpaulo 170200928Srpaulostatic __inline void 171200928Srpauloxscale_flag_write(uint32_t reg) 172200928Srpaulo{ 173200928Srpaulo __asm __volatile("mcr p14, 0, %0, c5, c1, 0" : : "r" (reg)); 174200928Srpaulo} 175200928Srpaulo 176200928Srpaulo/* 177200928Srpaulo * Event Selection Register 178200928Srpaulo */ 179200928Srpaulostatic __inline uint32_t 180200928Srpauloxscale_evtsel_read(void) 181200928Srpaulo{ 182200928Srpaulo uint32_t reg; 183200928Srpaulo 184200928Srpaulo __asm __volatile("mrc p14, 0, %0, c8, c1, 0" : "=r" (reg)); 185200928Srpaulo 186200928Srpaulo return (reg); 187200928Srpaulo} 188200928Srpaulo 189200928Srpaulostatic __inline void 190200928Srpauloxscale_evtsel_write(uint32_t reg) 191200928Srpaulo{ 192200928Srpaulo __asm __volatile("mcr p14, 0, %0, c8, c1, 0" : : "r" (reg)); 193200928Srpaulo} 194200928Srpaulo 195200928Srpaulo/* 196200928Srpaulo * Performance Count Register N 197200928Srpaulo */ 198200928Srpaulostatic uint32_t 199200928Srpauloxscale_pmcn_read(unsigned int pmc) 200200928Srpaulo{ 201200928Srpaulo uint32_t reg = 0; 202200928Srpaulo 203200928Srpaulo KASSERT(pmc < 4, ("[xscale,%d] illegal PMC number %d", __LINE__, pmc)); 204200928Srpaulo 205200928Srpaulo switch (pmc) { 206200928Srpaulo case 0: 207200928Srpaulo __asm __volatile("mrc p14, 0, %0, c0, c2, 0" : "=r" (reg)); 208200928Srpaulo break; 209200928Srpaulo case 1: 210200928Srpaulo __asm __volatile("mrc p14, 0, %0, c1, c2, 0" : "=r" (reg)); 211200928Srpaulo break; 212200928Srpaulo case 2: 213200928Srpaulo __asm __volatile("mrc p14, 0, %0, c2, c2, 0" : "=r" (reg)); 214200928Srpaulo break; 215200928Srpaulo case 3: 216200928Srpaulo __asm __volatile("mrc p14, 0, %0, c3, c2, 0" : "=r" (reg)); 217200928Srpaulo break; 218200928Srpaulo } 219200928Srpaulo 220200928Srpaulo return (reg); 221200928Srpaulo} 222200928Srpaulo 223200928Srpaulostatic uint32_t 224200928Srpauloxscale_pmcn_write(unsigned int pmc, uint32_t reg) 225200928Srpaulo{ 226200928Srpaulo 227200928Srpaulo KASSERT(pmc < 4, ("[xscale,%d] illegal PMC number %d", __LINE__, pmc)); 228200928Srpaulo 229200928Srpaulo switch (pmc) { 230200928Srpaulo case 0: 231200928Srpaulo __asm __volatile("mcr p14, 0, %0, c0, c2, 0" : : "r" (reg)); 232200928Srpaulo break; 233200928Srpaulo case 1: 234200928Srpaulo __asm __volatile("mcr p14, 0, %0, c1, c2, 0" : : "r" (reg)); 235200928Srpaulo break; 236200928Srpaulo case 2: 237200928Srpaulo __asm __volatile("mcr p14, 0, %0, c2, c2, 0" : : "r" (reg)); 238200928Srpaulo break; 239200928Srpaulo case 3: 240200928Srpaulo __asm __volatile("mcr p14, 0, %0, c3, c2, 0" : : "r" (reg)); 241200928Srpaulo break; 242200928Srpaulo } 243200928Srpaulo 244200928Srpaulo return (reg); 245200928Srpaulo} 246200928Srpaulo 247200928Srpaulostatic int 248200928Srpauloxscale_allocate_pmc(int cpu, int ri, struct pmc *pm, 249200928Srpaulo const struct pmc_op_pmcallocate *a) 250200928Srpaulo{ 251200928Srpaulo enum pmc_event pe; 252200928Srpaulo uint32_t caps, config; 253200928Srpaulo int i; 254200928Srpaulo 255200928Srpaulo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 256200928Srpaulo ("[xscale,%d] illegal CPU value %d", __LINE__, cpu)); 257200928Srpaulo KASSERT(ri >= 0 && ri < xscale_npmcs, 258200928Srpaulo ("[xscale,%d] illegal row index %d", __LINE__, ri)); 259200928Srpaulo 260200928Srpaulo caps = a->pm_caps; 261200928Srpaulo if (a->pm_class != PMC_CLASS_XSCALE) 262200928Srpaulo return (EINVAL); 263200928Srpaulo pe = a->pm_ev; 264298411Spfg for (i = 0; i < nitems(xscale_event_codes); i++) { 265200928Srpaulo if (xscale_event_codes[i].pe_ev == pe) { 266200928Srpaulo config = xscale_event_codes[i].pe_code; 267200928Srpaulo break; 268200928Srpaulo } 269200928Srpaulo } 270298411Spfg if (i == nitems(xscale_event_codes)) 271200928Srpaulo return EINVAL; 272200928Srpaulo /* Generation 1 has fewer events */ 273200928Srpaulo if (xscale_gen == 1 && i > PMC_EV_XSCALE_PC_CHANGE) 274200928Srpaulo return EINVAL; 275200928Srpaulo pm->pm_md.pm_xscale.pm_xscale_evsel = config; 276200928Srpaulo 277282658Sjhb PMCDBG2(MDP,ALL,2,"xscale-allocate ri=%d -> config=0x%x", ri, config); 278200928Srpaulo 279200928Srpaulo return 0; 280200928Srpaulo} 281200928Srpaulo 282200928Srpaulo 283200928Srpaulostatic int 284200928Srpauloxscale_read_pmc(int cpu, int ri, pmc_value_t *v) 285200928Srpaulo{ 286200928Srpaulo struct pmc *pm; 287200928Srpaulo pmc_value_t tmp; 288200928Srpaulo 289200928Srpaulo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 290200928Srpaulo ("[xscale,%d] illegal CPU value %d", __LINE__, cpu)); 291200928Srpaulo KASSERT(ri >= 0 && ri < xscale_npmcs, 292200928Srpaulo ("[xscale,%d] illegal row index %d", __LINE__, ri)); 293200928Srpaulo 294200928Srpaulo pm = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc; 295200928Srpaulo tmp = xscale_pmcn_read(ri); 296282658Sjhb PMCDBG2(MDP,REA,2,"xscale-read id=%d -> %jd", ri, tmp); 297200928Srpaulo if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 298200928Srpaulo *v = XSCALE_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); 299200928Srpaulo else 300200928Srpaulo *v = tmp; 301200928Srpaulo 302200928Srpaulo return 0; 303200928Srpaulo} 304200928Srpaulo 305200928Srpaulostatic int 306200928Srpauloxscale_write_pmc(int cpu, int ri, pmc_value_t v) 307200928Srpaulo{ 308200928Srpaulo struct pmc *pm; 309200928Srpaulo 310200928Srpaulo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 311200928Srpaulo ("[xscale,%d] illegal CPU value %d", __LINE__, cpu)); 312200928Srpaulo KASSERT(ri >= 0 && ri < xscale_npmcs, 313200928Srpaulo ("[xscale,%d] illegal row-index %d", __LINE__, ri)); 314200928Srpaulo 315200928Srpaulo pm = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc; 316200928Srpaulo 317200928Srpaulo if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 318200928Srpaulo v = XSCALE_RELOAD_COUNT_TO_PERFCTR_VALUE(v); 319200928Srpaulo 320282658Sjhb PMCDBG3(MDP,WRI,1,"xscale-write cpu=%d ri=%d v=%jx", cpu, ri, v); 321200928Srpaulo 322200928Srpaulo xscale_pmcn_write(ri, v); 323200928Srpaulo 324200928Srpaulo return 0; 325200928Srpaulo} 326200928Srpaulo 327200928Srpaulostatic int 328200928Srpauloxscale_config_pmc(int cpu, int ri, struct pmc *pm) 329200928Srpaulo{ 330200928Srpaulo struct pmc_hw *phw; 331200928Srpaulo 332282658Sjhb PMCDBG3(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 333200928Srpaulo 334200928Srpaulo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 335200928Srpaulo ("[xscale,%d] illegal CPU value %d", __LINE__, cpu)); 336200928Srpaulo KASSERT(ri >= 0 && ri < xscale_npmcs, 337200928Srpaulo ("[xscale,%d] illegal row-index %d", __LINE__, ri)); 338200928Srpaulo 339200928Srpaulo phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri]; 340200928Srpaulo 341200928Srpaulo KASSERT(pm == NULL || phw->phw_pmc == NULL, 342200928Srpaulo ("[xscale,%d] pm=%p phw->pm=%p hwpmc not unconfigured", 343200928Srpaulo __LINE__, pm, phw->phw_pmc)); 344200928Srpaulo 345200928Srpaulo phw->phw_pmc = pm; 346200928Srpaulo 347200928Srpaulo return 0; 348200928Srpaulo} 349200928Srpaulo 350200928Srpaulostatic int 351200928Srpauloxscale_start_pmc(int cpu, int ri) 352200928Srpaulo{ 353200928Srpaulo uint32_t pmnc, config, evtsel; 354200928Srpaulo struct pmc *pm; 355200928Srpaulo struct pmc_hw *phw; 356200928Srpaulo 357200928Srpaulo phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri]; 358200928Srpaulo pm = phw->phw_pmc; 359200928Srpaulo config = pm->pm_md.pm_xscale.pm_xscale_evsel; 360200928Srpaulo 361200928Srpaulo /* 362200928Srpaulo * Configure the event selection. 363200928Srpaulo * 364200928Srpaulo * On the XScale 2nd Generation there's no EVTSEL register. 365200928Srpaulo */ 366200928Srpaulo if (xscale_npmcs == 2) { 367200928Srpaulo pmnc = xscale_pmnc_read(); 368200928Srpaulo switch (ri) { 369200928Srpaulo case 0: 370200928Srpaulo pmnc &= ~XSCALE_PMNC_EVT0_MASK; 371200928Srpaulo pmnc |= (config << 12) & XSCALE_PMNC_EVT0_MASK; 372200928Srpaulo break; 373200928Srpaulo case 1: 374200928Srpaulo pmnc &= ~XSCALE_PMNC_EVT1_MASK; 375200928Srpaulo pmnc |= (config << 20) & XSCALE_PMNC_EVT1_MASK; 376200928Srpaulo break; 377200928Srpaulo default: 378200928Srpaulo /* XXX */ 379200928Srpaulo break; 380200928Srpaulo } 381200928Srpaulo xscale_pmnc_write(pmnc); 382200928Srpaulo } else { 383200928Srpaulo evtsel = xscale_evtsel_read(); 384200928Srpaulo switch (ri) { 385200928Srpaulo case 0: 386200928Srpaulo evtsel &= ~XSCALE_EVTSEL_EVT0_MASK; 387200928Srpaulo evtsel |= config & XSCALE_EVTSEL_EVT0_MASK; 388200928Srpaulo break; 389200928Srpaulo case 1: 390200928Srpaulo evtsel &= ~XSCALE_EVTSEL_EVT1_MASK; 391200928Srpaulo evtsel |= (config << 8) & XSCALE_EVTSEL_EVT1_MASK; 392200928Srpaulo break; 393200928Srpaulo case 2: 394200928Srpaulo evtsel &= ~XSCALE_EVTSEL_EVT2_MASK; 395200928Srpaulo evtsel |= (config << 16) & XSCALE_EVTSEL_EVT2_MASK; 396200928Srpaulo break; 397200928Srpaulo case 3: 398200928Srpaulo evtsel &= ~XSCALE_EVTSEL_EVT3_MASK; 399200928Srpaulo evtsel |= (config << 24) & XSCALE_EVTSEL_EVT3_MASK; 400200928Srpaulo break; 401200928Srpaulo default: 402200928Srpaulo /* XXX */ 403200928Srpaulo break; 404200928Srpaulo } 405200928Srpaulo xscale_evtsel_write(evtsel); 406200928Srpaulo } 407200928Srpaulo /* 408200928Srpaulo * Enable the PMC. 409200928Srpaulo * 410200928Srpaulo * Note that XScale provides only one bit to enable/disable _all_ 411200928Srpaulo * performance monitoring units. 412200928Srpaulo */ 413200928Srpaulo pmnc = xscale_pmnc_read(); 414200928Srpaulo pmnc |= XSCALE_PMNC_ENABLE; 415200928Srpaulo xscale_pmnc_write(pmnc); 416200928Srpaulo 417200928Srpaulo return 0; 418200928Srpaulo} 419200928Srpaulo 420200928Srpaulostatic int 421200928Srpauloxscale_stop_pmc(int cpu, int ri) 422200928Srpaulo{ 423200928Srpaulo uint32_t pmnc, evtsel; 424200928Srpaulo struct pmc *pm; 425200928Srpaulo struct pmc_hw *phw; 426200928Srpaulo 427200928Srpaulo phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri]; 428200928Srpaulo pm = phw->phw_pmc; 429200928Srpaulo 430200928Srpaulo /* 431200928Srpaulo * Disable the PMCs. 432200928Srpaulo * 433200928Srpaulo * Note that XScale provides only one bit to enable/disable _all_ 434200928Srpaulo * performance monitoring units. 435200928Srpaulo */ 436200928Srpaulo pmnc = xscale_pmnc_read(); 437200928Srpaulo pmnc &= ~XSCALE_PMNC_ENABLE; 438200928Srpaulo xscale_pmnc_write(pmnc); 439200928Srpaulo /* 440200928Srpaulo * A value of 0xff makes the corresponding PMU go into 441200928Srpaulo * power saving mode. 442200928Srpaulo */ 443200928Srpaulo if (xscale_npmcs == 2) { 444200928Srpaulo pmnc = xscale_pmnc_read(); 445200928Srpaulo switch (ri) { 446200928Srpaulo case 0: 447200928Srpaulo pmnc |= XSCALE_PMNC_EVT0_MASK; 448200928Srpaulo break; 449200928Srpaulo case 1: 450200928Srpaulo pmnc |= XSCALE_PMNC_EVT1_MASK; 451200928Srpaulo break; 452200928Srpaulo default: 453200928Srpaulo /* XXX */ 454200928Srpaulo break; 455200928Srpaulo } 456200928Srpaulo xscale_pmnc_write(pmnc); 457200928Srpaulo } else { 458200928Srpaulo evtsel = xscale_evtsel_read(); 459200928Srpaulo switch (ri) { 460200928Srpaulo case 0: 461200928Srpaulo evtsel |= XSCALE_EVTSEL_EVT0_MASK; 462200928Srpaulo break; 463200928Srpaulo case 1: 464200928Srpaulo evtsel |= XSCALE_EVTSEL_EVT1_MASK; 465200928Srpaulo break; 466200928Srpaulo case 2: 467200928Srpaulo evtsel |= XSCALE_EVTSEL_EVT2_MASK; 468200928Srpaulo break; 469200928Srpaulo case 3: 470200928Srpaulo evtsel |= XSCALE_EVTSEL_EVT3_MASK; 471200928Srpaulo break; 472200928Srpaulo default: 473200928Srpaulo /* XXX */ 474200928Srpaulo break; 475200928Srpaulo } 476200928Srpaulo xscale_evtsel_write(evtsel); 477200928Srpaulo } 478200928Srpaulo 479200928Srpaulo return 0; 480200928Srpaulo} 481200928Srpaulo 482200928Srpaulostatic int 483200928Srpauloxscale_release_pmc(int cpu, int ri, struct pmc *pmc) 484200928Srpaulo{ 485200928Srpaulo struct pmc_hw *phw; 486200928Srpaulo 487200928Srpaulo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 488200928Srpaulo ("[xscale,%d] illegal CPU value %d", __LINE__, cpu)); 489200928Srpaulo KASSERT(ri >= 0 && ri < xscale_npmcs, 490200928Srpaulo ("[xscale,%d] illegal row-index %d", __LINE__, ri)); 491200928Srpaulo 492200928Srpaulo phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri]; 493200928Srpaulo KASSERT(phw->phw_pmc == NULL, 494200928Srpaulo ("[xscale,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 495200928Srpaulo 496200928Srpaulo return 0; 497200928Srpaulo} 498200928Srpaulo 499200928Srpaulostatic int 500200928Srpauloxscale_intr(int cpu, struct trapframe *tf) 501200928Srpaulo{ 502200928Srpaulo printf("intr\n"); 503200928Srpaulo return 0; 504200928Srpaulo} 505200928Srpaulo 506200928Srpaulostatic int 507200928Srpauloxscale_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 508200928Srpaulo{ 509200928Srpaulo int error; 510200928Srpaulo struct pmc_hw *phw; 511200928Srpaulo char xscale_name[PMC_NAME_MAX]; 512200928Srpaulo 513200928Srpaulo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 514200928Srpaulo ("[xscale,%d], illegal CPU %d", __LINE__, cpu)); 515200928Srpaulo KASSERT(ri >= 0 && ri < xscale_npmcs, 516200928Srpaulo ("[xscale,%d] row-index %d out of range", __LINE__, ri)); 517200928Srpaulo 518200928Srpaulo phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri]; 519200928Srpaulo snprintf(xscale_name, sizeof(xscale_name), "XSCALE-%d", ri); 520200928Srpaulo if ((error = copystr(xscale_name, pi->pm_name, PMC_NAME_MAX, 521200928Srpaulo NULL)) != 0) 522200928Srpaulo return error; 523200928Srpaulo pi->pm_class = PMC_CLASS_XSCALE; 524200928Srpaulo if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 525200928Srpaulo pi->pm_enabled = TRUE; 526200928Srpaulo *ppmc = phw->phw_pmc; 527200928Srpaulo } else { 528200928Srpaulo pi->pm_enabled = FALSE; 529200928Srpaulo *ppmc = NULL; 530200928Srpaulo } 531200928Srpaulo 532200928Srpaulo return (0); 533200928Srpaulo} 534200928Srpaulo 535200928Srpaulostatic int 536200928Srpauloxscale_get_config(int cpu, int ri, struct pmc **ppm) 537200928Srpaulo{ 538200928Srpaulo *ppm = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc; 539200928Srpaulo 540200928Srpaulo return 0; 541200928Srpaulo} 542200928Srpaulo 543200928Srpaulo/* 544200928Srpaulo * XXX don't know what we should do here. 545200928Srpaulo */ 546200928Srpaulostatic int 547200928Srpauloxscale_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) 548200928Srpaulo{ 549200928Srpaulo return 0; 550200928Srpaulo} 551200928Srpaulo 552200928Srpaulostatic int 553200928Srpauloxscale_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) 554200928Srpaulo{ 555200928Srpaulo return 0; 556200928Srpaulo} 557200928Srpaulo 558200928Srpaulostatic int 559200928Srpauloxscale_pcpu_init(struct pmc_mdep *md, int cpu) 560200928Srpaulo{ 561200928Srpaulo int first_ri, i; 562200928Srpaulo struct pmc_cpu *pc; 563200928Srpaulo struct xscale_cpu *pac; 564200928Srpaulo struct pmc_hw *phw; 565200928Srpaulo 566200928Srpaulo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 567200928Srpaulo ("[xscale,%d] wrong cpu number %d", __LINE__, cpu)); 568282658Sjhb PMCDBG1(MDP,INI,1,"xscale-init cpu=%d", cpu); 569200928Srpaulo 570200928Srpaulo xscale_pcpu[cpu] = pac = malloc(sizeof(struct xscale_cpu), M_PMC, 571200928Srpaulo M_WAITOK|M_ZERO); 572200928Srpaulo pac->pc_xscalepmcs = malloc(sizeof(struct pmc_hw) * xscale_npmcs, 573200928Srpaulo M_PMC, M_WAITOK|M_ZERO); 574200928Srpaulo pc = pmc_pcpu[cpu]; 575200928Srpaulo first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_XSCALE].pcd_ri; 576200928Srpaulo KASSERT(pc != NULL, ("[xscale,%d] NULL per-cpu pointer", __LINE__)); 577200928Srpaulo 578200928Srpaulo for (i = 0, phw = pac->pc_xscalepmcs; i < xscale_npmcs; i++, phw++) { 579200928Srpaulo phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 580200928Srpaulo PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); 581200928Srpaulo phw->phw_pmc = NULL; 582200928Srpaulo pc->pc_hwpmcs[i + first_ri] = phw; 583200928Srpaulo } 584200928Srpaulo 585200928Srpaulo /* 586200928Srpaulo * Disable and put the PMUs into power save mode. 587200928Srpaulo */ 588200928Srpaulo if (xscale_npmcs == 2) { 589200928Srpaulo xscale_pmnc_write(XSCALE_PMNC_EVT1_MASK | 590200928Srpaulo XSCALE_PMNC_EVT0_MASK); 591200928Srpaulo } else { 592200928Srpaulo xscale_evtsel_write(XSCALE_EVTSEL_EVT3_MASK | 593200928Srpaulo XSCALE_EVTSEL_EVT2_MASK | XSCALE_EVTSEL_EVT1_MASK | 594200928Srpaulo XSCALE_EVTSEL_EVT0_MASK); 595200928Srpaulo } 596200928Srpaulo 597200928Srpaulo return 0; 598200928Srpaulo} 599200928Srpaulo 600200928Srpaulostatic int 601200928Srpauloxscale_pcpu_fini(struct pmc_mdep *md, int cpu) 602200928Srpaulo{ 603200928Srpaulo return 0; 604200928Srpaulo} 605200928Srpaulo 606200928Srpaulostruct pmc_mdep * 607200928Srpaulopmc_xscale_initialize() 608200928Srpaulo{ 609200928Srpaulo struct pmc_mdep *pmc_mdep; 610200928Srpaulo struct pmc_classdep *pcd; 611200928Srpaulo uint32_t idreg; 612200928Srpaulo 613200928Srpaulo /* Get the Core Generation from CP15 */ 614200928Srpaulo __asm __volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (idreg)); 615200928Srpaulo xscale_gen = (idreg >> 13) & 0x3; 616200928Srpaulo switch (xscale_gen) { 617200928Srpaulo case 1: 618200928Srpaulo xscale_npmcs = 2; 619200928Srpaulo break; 620200928Srpaulo case 2: 621200928Srpaulo case 3: 622200928Srpaulo xscale_npmcs = 4; 623200928Srpaulo break; 624200928Srpaulo default: 625200928Srpaulo printf("%s: unknown XScale core generation\n", __func__); 626200928Srpaulo return (NULL); 627200928Srpaulo } 628282658Sjhb PMCDBG1(MDP,INI,1,"xscale-init npmcs=%d", xscale_npmcs); 629200928Srpaulo 630200928Srpaulo /* 631200928Srpaulo * Allocate space for pointers to PMC HW descriptors and for 632200928Srpaulo * the MDEP structure used by MI code. 633200928Srpaulo */ 634200928Srpaulo xscale_pcpu = malloc(sizeof(struct xscale_cpu *) * pmc_cpu_max(), M_PMC, 635200928Srpaulo M_WAITOK|M_ZERO); 636200928Srpaulo 637200928Srpaulo /* Just one class */ 638233628Sfabient pmc_mdep = pmc_mdep_alloc(1); 639200928Srpaulo 640200928Srpaulo pmc_mdep->pmd_cputype = PMC_CPU_INTEL_XSCALE; 641200928Srpaulo 642200928Srpaulo pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_XSCALE]; 643200928Srpaulo pcd->pcd_caps = XSCALE_PMC_CAPS; 644200928Srpaulo pcd->pcd_class = PMC_CLASS_XSCALE; 645200928Srpaulo pcd->pcd_num = xscale_npmcs; 646200928Srpaulo pcd->pcd_ri = pmc_mdep->pmd_npmc; 647200928Srpaulo pcd->pcd_width = 32; 648200928Srpaulo 649200928Srpaulo pcd->pcd_allocate_pmc = xscale_allocate_pmc; 650200928Srpaulo pcd->pcd_config_pmc = xscale_config_pmc; 651200928Srpaulo pcd->pcd_pcpu_fini = xscale_pcpu_fini; 652200928Srpaulo pcd->pcd_pcpu_init = xscale_pcpu_init; 653200928Srpaulo pcd->pcd_describe = xscale_describe; 654200928Srpaulo pcd->pcd_get_config = xscale_get_config; 655200928Srpaulo pcd->pcd_read_pmc = xscale_read_pmc; 656200928Srpaulo pcd->pcd_release_pmc = xscale_release_pmc; 657200928Srpaulo pcd->pcd_start_pmc = xscale_start_pmc; 658200928Srpaulo pcd->pcd_stop_pmc = xscale_stop_pmc; 659200928Srpaulo pcd->pcd_write_pmc = xscale_write_pmc; 660200928Srpaulo 661200928Srpaulo pmc_mdep->pmd_intr = xscale_intr; 662200928Srpaulo pmc_mdep->pmd_switch_in = xscale_switch_in; 663200928Srpaulo pmc_mdep->pmd_switch_out = xscale_switch_out; 664200928Srpaulo 665200928Srpaulo pmc_mdep->pmd_npmc += xscale_npmcs; 666200928Srpaulo 667200928Srpaulo return (pmc_mdep); 668200928Srpaulo} 669200928Srpaulo 670200928Srpaulovoid 671200928Srpaulopmc_xscale_finalize(struct pmc_mdep *md) 672200928Srpaulo{ 673200928Srpaulo} 674