14244065bSChristopher Covington /* 24244065bSChristopher Covington * Test the ARM Performance Monitors Unit (PMU). 34244065bSChristopher Covington * 44244065bSChristopher Covington * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. 54244065bSChristopher Covington * Copyright (C) 2016, Red Hat Inc, Wei Huang <wei@redhat.com> 64244065bSChristopher Covington * 74244065bSChristopher Covington * This program is free software; you can redistribute it and/or modify it 84244065bSChristopher Covington * under the terms of the GNU Lesser General Public License version 2.1 and 94244065bSChristopher Covington * only version 2.1 as published by the Free Software Foundation. 104244065bSChristopher Covington * 114244065bSChristopher Covington * This program is distributed in the hope that it will be useful, but WITHOUT 124244065bSChristopher Covington * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 134244065bSChristopher Covington * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 144244065bSChristopher Covington * for more details. 154244065bSChristopher Covington */ 164244065bSChristopher Covington #include "libcflat.h" 174244065bSChristopher Covington #include "asm/barrier.h" 184244065bSChristopher Covington #include "asm/sysreg.h" 194244065bSChristopher Covington #include "asm/processor.h" 204244065bSChristopher Covington 21*d81bb7a3SChristopher Covington #define PMU_PMCR_E (1 << 0) 22*d81bb7a3SChristopher Covington #define PMU_PMCR_C (1 << 2) 23*d81bb7a3SChristopher Covington #define PMU_PMCR_LC (1 << 6) 244244065bSChristopher Covington #define PMU_PMCR_N_SHIFT 11 254244065bSChristopher Covington #define PMU_PMCR_N_MASK 0x1f 264244065bSChristopher Covington #define PMU_PMCR_ID_SHIFT 16 274244065bSChristopher Covington #define PMU_PMCR_ID_MASK 0xff 284244065bSChristopher Covington #define PMU_PMCR_IMP_SHIFT 24 294244065bSChristopher Covington #define PMU_PMCR_IMP_MASK 0xff 304244065bSChristopher Covington 314244065bSChristopher Covington #define ID_DFR0_PERFMON_SHIFT 24 324244065bSChristopher Covington #define ID_DFR0_PERFMON_MASK 0xf 334244065bSChristopher Covington 34*d81bb7a3SChristopher Covington #define PMU_CYCLE_IDX 31 35*d81bb7a3SChristopher Covington 36*d81bb7a3SChristopher Covington #define NR_SAMPLES 10 37*d81bb7a3SChristopher Covington 384244065bSChristopher Covington static unsigned int pmu_version; 394244065bSChristopher Covington #if defined(__arm__) 404244065bSChristopher Covington #define PMCR __ACCESS_CP15(c9, 0, c12, 0) 414244065bSChristopher Covington #define ID_DFR0 __ACCESS_CP15(c0, 0, c1, 2) 42*d81bb7a3SChristopher Covington #define PMSELR __ACCESS_CP15(c9, 0, c12, 5) 43*d81bb7a3SChristopher Covington #define PMXEVTYPER __ACCESS_CP15(c9, 0, c13, 1) 44*d81bb7a3SChristopher Covington #define PMCNTENSET __ACCESS_CP15(c9, 0, c12, 1) 45*d81bb7a3SChristopher Covington #define PMCCNTR32 __ACCESS_CP15(c9, 0, c13, 0) 46*d81bb7a3SChristopher Covington #define PMCCNTR64 __ACCESS_CP15_64(0, c9) 474244065bSChristopher Covington 484244065bSChristopher Covington static inline uint32_t get_id_dfr0(void) { return read_sysreg(ID_DFR0); } 494244065bSChristopher Covington static inline uint32_t get_pmcr(void) { return read_sysreg(PMCR); } 50*d81bb7a3SChristopher Covington static inline void set_pmcr(uint32_t v) { write_sysreg(v, PMCR); } 51*d81bb7a3SChristopher Covington static inline void set_pmcntenset(uint32_t v) { write_sysreg(v, PMCNTENSET); } 52*d81bb7a3SChristopher Covington 53*d81bb7a3SChristopher Covington static inline uint64_t get_pmccntr(void) 54*d81bb7a3SChristopher Covington { 55*d81bb7a3SChristopher Covington if (pmu_version == 0x3) 56*d81bb7a3SChristopher Covington return read_sysreg(PMCCNTR64); 57*d81bb7a3SChristopher Covington else 58*d81bb7a3SChristopher Covington return read_sysreg(PMCCNTR32); 59*d81bb7a3SChristopher Covington } 60*d81bb7a3SChristopher Covington 61*d81bb7a3SChristopher Covington /* PMCCFILTR is an obsolete name for PMXEVTYPER31 in ARMv7 */ 62*d81bb7a3SChristopher Covington static inline void set_pmccfiltr(uint32_t value) 63*d81bb7a3SChristopher Covington { 64*d81bb7a3SChristopher Covington write_sysreg(PMU_CYCLE_IDX, PMSELR); 65*d81bb7a3SChristopher Covington write_sysreg(value, PMXEVTYPER); 66*d81bb7a3SChristopher Covington isb(); 67*d81bb7a3SChristopher Covington } 684244065bSChristopher Covington #elif defined(__aarch64__) 694244065bSChristopher Covington static inline uint32_t get_id_dfr0(void) { return read_sysreg(id_dfr0_el1); } 704244065bSChristopher Covington static inline uint32_t get_pmcr(void) { return read_sysreg(pmcr_el0); } 71*d81bb7a3SChristopher Covington static inline void set_pmcr(uint32_t v) { write_sysreg(v, pmcr_el0); } 72*d81bb7a3SChristopher Covington static inline uint64_t get_pmccntr(void) { return read_sysreg(pmccntr_el0); } 73*d81bb7a3SChristopher Covington static inline void set_pmcntenset(uint32_t v) { write_sysreg(v, pmcntenset_el0); } 74*d81bb7a3SChristopher Covington static inline void set_pmccfiltr(uint32_t v) { write_sysreg(v, pmccfiltr_el0); } 754244065bSChristopher Covington #endif 764244065bSChristopher Covington 774244065bSChristopher Covington /* 784244065bSChristopher Covington * As a simple sanity check on the PMCR_EL0, ensure the implementer field isn't 794244065bSChristopher Covington * null. Also print out a couple other interesting fields for diagnostic 804244065bSChristopher Covington * purposes. For example, as of fall 2016, QEMU TCG mode doesn't implement 814244065bSChristopher Covington * event counters and therefore reports zero event counters, but hopefully 824244065bSChristopher Covington * support for at least the instructions event will be added in the future and 834244065bSChristopher Covington * the reported number of event counters will become nonzero. 844244065bSChristopher Covington */ 854244065bSChristopher Covington static bool check_pmcr(void) 864244065bSChristopher Covington { 874244065bSChristopher Covington uint32_t pmcr; 884244065bSChristopher Covington 894244065bSChristopher Covington pmcr = get_pmcr(); 904244065bSChristopher Covington 914244065bSChristopher Covington report_info("PMU implementer/ID code/counters: 0x%x(\"%c\")/0x%x/%d", 924244065bSChristopher Covington (pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK, 934244065bSChristopher Covington ((pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK) ? : ' ', 944244065bSChristopher Covington (pmcr >> PMU_PMCR_ID_SHIFT) & PMU_PMCR_ID_MASK, 954244065bSChristopher Covington (pmcr >> PMU_PMCR_N_SHIFT) & PMU_PMCR_N_MASK); 964244065bSChristopher Covington 974244065bSChristopher Covington return ((pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK) != 0; 984244065bSChristopher Covington } 994244065bSChristopher Covington 100*d81bb7a3SChristopher Covington /* 101*d81bb7a3SChristopher Covington * Ensure that the cycle counter progresses between back-to-back reads. 102*d81bb7a3SChristopher Covington */ 103*d81bb7a3SChristopher Covington static bool check_cycles_increase(void) 104*d81bb7a3SChristopher Covington { 105*d81bb7a3SChristopher Covington bool success = true; 106*d81bb7a3SChristopher Covington 107*d81bb7a3SChristopher Covington /* init before event access, this test only cares about cycle count */ 108*d81bb7a3SChristopher Covington set_pmcntenset(1 << PMU_CYCLE_IDX); 109*d81bb7a3SChristopher Covington set_pmccfiltr(0); /* count cycles in EL0, EL1, but not EL2 */ 110*d81bb7a3SChristopher Covington 111*d81bb7a3SChristopher Covington set_pmcr(get_pmcr() | PMU_PMCR_LC | PMU_PMCR_C | PMU_PMCR_E); 112*d81bb7a3SChristopher Covington 113*d81bb7a3SChristopher Covington for (int i = 0; i < NR_SAMPLES; i++) { 114*d81bb7a3SChristopher Covington uint64_t a, b; 115*d81bb7a3SChristopher Covington 116*d81bb7a3SChristopher Covington a = get_pmccntr(); 117*d81bb7a3SChristopher Covington b = get_pmccntr(); 118*d81bb7a3SChristopher Covington 119*d81bb7a3SChristopher Covington if (a >= b) { 120*d81bb7a3SChristopher Covington printf("Read %"PRId64" then %"PRId64".\n", a, b); 121*d81bb7a3SChristopher Covington success = false; 122*d81bb7a3SChristopher Covington break; 123*d81bb7a3SChristopher Covington } 124*d81bb7a3SChristopher Covington } 125*d81bb7a3SChristopher Covington 126*d81bb7a3SChristopher Covington set_pmcr(get_pmcr() & ~PMU_PMCR_E); 127*d81bb7a3SChristopher Covington 128*d81bb7a3SChristopher Covington return success; 129*d81bb7a3SChristopher Covington } 130*d81bb7a3SChristopher Covington 1314244065bSChristopher Covington /* Return FALSE if no PMU found, otherwise return TRUE */ 1324244065bSChristopher Covington bool pmu_probe(void) 1334244065bSChristopher Covington { 1344244065bSChristopher Covington uint32_t dfr0; 1354244065bSChristopher Covington 1364244065bSChristopher Covington /* probe pmu version */ 1374244065bSChristopher Covington dfr0 = get_id_dfr0(); 1384244065bSChristopher Covington pmu_version = (dfr0 >> ID_DFR0_PERFMON_SHIFT) & ID_DFR0_PERFMON_MASK; 1394244065bSChristopher Covington 1404244065bSChristopher Covington if (pmu_version) 1414244065bSChristopher Covington report_info("PMU version: %d", pmu_version); 1424244065bSChristopher Covington 1434244065bSChristopher Covington return pmu_version; 1444244065bSChristopher Covington } 1454244065bSChristopher Covington 1464244065bSChristopher Covington int main(void) 1474244065bSChristopher Covington { 1484244065bSChristopher Covington if (!pmu_probe()) { 1494244065bSChristopher Covington printf("No PMU found, test skipped...\n"); 1504244065bSChristopher Covington return report_summary(); 1514244065bSChristopher Covington } 1524244065bSChristopher Covington 1534244065bSChristopher Covington report_prefix_push("pmu"); 1544244065bSChristopher Covington 1554244065bSChristopher Covington report("Control register", check_pmcr()); 156*d81bb7a3SChristopher Covington report("Monotonically increasing cycle count", check_cycles_increase()); 1574244065bSChristopher Covington 1584244065bSChristopher Covington return report_summary(); 1594244065bSChristopher Covington } 160