aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/hwpmc/hwpmc_arm64.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/hwpmc/hwpmc_arm64.c')
-rw-r--r--sys/dev/hwpmc/hwpmc_arm64.c72
1 files changed, 53 insertions, 19 deletions
diff --git a/sys/dev/hwpmc/hwpmc_arm64.c b/sys/dev/hwpmc/hwpmc_arm64.c
index af8d25b098c4..310e43065716 100644
--- a/sys/dev/hwpmc/hwpmc_arm64.c
+++ b/sys/dev/hwpmc/hwpmc_arm64.c
@@ -34,10 +34,12 @@
#include <machine/pmc_mdep.h>
#include <machine/cpu.h>
+#include <machine/machdep.h>
#include "opt_acpi.h"
static int arm64_npmcs;
+static bool arm64_64bit_events __read_mostly = false;
struct arm64_event_code_map {
enum pmc_event pe_ev;
@@ -112,7 +114,7 @@ arm64_counter_disable(unsigned int pmc)
/*
* Performance Monitors Control Register
*/
-static uint32_t
+static uint64_t
arm64_pmcr_read(void)
{
uint32_t reg;
@@ -123,7 +125,7 @@ arm64_pmcr_read(void)
}
static void
-arm64_pmcr_write(uint32_t reg)
+arm64_pmcr_write(uint64_t reg)
{
WRITE_SPECIALREG(pmcr_el0, reg);
@@ -134,7 +136,7 @@ arm64_pmcr_write(uint32_t reg)
/*
* Performance Count Register N
*/
-static uint32_t
+static uint64_t
arm64_pmcn_read(unsigned int pmc)
{
@@ -148,7 +150,7 @@ arm64_pmcn_read(unsigned int pmc)
}
static void
-arm64_pmcn_write(unsigned int pmc, uint32_t reg)
+arm64_pmcn_write(unsigned int pmc, uint64_t reg)
{
KASSERT(pmc < arm64_npmcs, ("%s: illegal PMC number %d", __func__, pmc));
@@ -163,7 +165,7 @@ static int
arm64_allocate_pmc(int cpu, int ri, struct pmc *pm,
const struct pmc_op_pmcallocate *a)
{
- uint32_t config;
+ uint64_t config;
enum pmc_event pe;
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
@@ -186,10 +188,18 @@ arm64_allocate_pmc(int cpu, int ri, struct pmc *pm,
switch (a->pm_caps & (PMC_CAP_SYSTEM | PMC_CAP_USER)) {
case PMC_CAP_SYSTEM:
+ /* Exclude EL0 */
config |= PMEVTYPER_U;
+ if (in_vhe()) {
+ /* If in VHE we need to include EL2 and exclude EL1 */
+ config |= PMEVTYPER_NSH | PMEVTYPER_P;
+ }
break;
case PMC_CAP_USER:
+ /* Exclude EL1 */
config |= PMEVTYPER_P;
+ /* Exclude EL2 */
+ config &= ~PMEVTYPER_NSH;
break;
default:
/*
@@ -197,11 +207,16 @@ arm64_allocate_pmc(int cpu, int ri, struct pmc *pm,
* (default setting) or if both flags are specified
* (user explicitly requested both qualifiers).
*/
+ if (in_vhe()) {
+ /* If in VHE we need to include EL2 */
+ config |= PMEVTYPER_NSH;
+ }
break;
}
pm->pm_md.pm_arm64.pm_arm64_evsel = config;
- PMCDBG2(MDP, ALL, 2, "arm64-allocate ri=%d -> config=0x%x", ri, config);
+ PMCDBG2(MDP, ALL, 2, "arm64-allocate ri=%d -> config=0x%lx", ri,
+ config);
return (0);
}
@@ -233,7 +248,15 @@ arm64_read_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t *v)
/* Reread counter in case we raced. */
tmp = arm64_pmcn_read(ri);
}
- tmp += 0x100000000llu * pm->pm_pcpu_state[cpu].pps_overflowcnt;
+ /*
+ * If the counter is 32-bit increment the upper bits of the counter.
+ * It it is 64-bit then there is nothing we can do as tmp is already
+ * 64-bit.
+ */
+ if (!arm64_64bit_events) {
+ tmp &= 0xffffffffu;
+ tmp += (uint64_t)pm->pm_pcpu_state[cpu].pps_overflowcnt << 32;
+ }
intr_restore(s);
PMCDBG2(MDP, REA, 2, "arm64-read id=%d -> %jd", ri, tmp);
@@ -267,7 +290,10 @@ arm64_write_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t v)
PMCDBG3(MDP, WRI, 1, "arm64-write cpu=%d ri=%d v=%jx", cpu, ri, v);
- pm->pm_pcpu_state[cpu].pps_overflowcnt = v >> 32;
+ if (!arm64_64bit_events) {
+ pm->pm_pcpu_state[cpu].pps_overflowcnt = v >> 32;
+ v &= 0xffffffffu;
+ }
arm64_pmcn_write(ri, v);
return (0);
@@ -299,7 +325,7 @@ arm64_config_pmc(int cpu, int ri, struct pmc *pm)
static int
arm64_start_pmc(int cpu, int ri, struct pmc *pm)
{
- uint32_t config;
+ uint64_t config;
config = pm->pm_md.pm_arm64.pm_arm64_evsel;
@@ -475,9 +501,10 @@ arm64_pcpu_init(struct pmc_mdep *md, int cpu)
WRITE_SPECIALREG(pmcntenclr_el0, 0xffffffff);
WRITE_SPECIALREG(pmintenclr_el1, 0xffffffff);
- /* Enable unit */
- pmcr = arm64_pmcr_read();
- pmcr |= PMCR_E;
+ /* Enable unit with a useful default state */
+ pmcr = PMCR_LC | PMCR_C | PMCR_P | PMCR_E;
+ if (arm64_64bit_events)
+ pmcr |= PMCR_LP;
arm64_pmcr_write(pmcr);
return (0);
@@ -486,7 +513,7 @@ arm64_pcpu_init(struct pmc_mdep *md, int cpu)
static int
arm64_pcpu_fini(struct pmc_mdep *md, int cpu)
{
- uint32_t pmcr;
+ uint64_t pmcr;
PMCDBG0(MDP, INI, 1, "arm64-pcpu-fini");
@@ -507,13 +534,14 @@ pmc_arm64_initialize(void)
struct pmc_mdep *pmc_mdep;
struct pmc_classdep *pcd;
int classes, idcode, impcode;
- int reg;
+ uint64_t dfr;
+ uint64_t pmcr;
uint64_t midr;
- reg = arm64_pmcr_read();
- arm64_npmcs = (reg & PMCR_N_MASK) >> PMCR_N_SHIFT;
- impcode = (reg & PMCR_IMP_MASK) >> PMCR_IMP_SHIFT;
- idcode = (reg & PMCR_IDCODE_MASK) >> PMCR_IDCODE_SHIFT;
+ pmcr = arm64_pmcr_read();
+ arm64_npmcs = (pmcr & PMCR_N_MASK) >> PMCR_N_SHIFT;
+ impcode = (pmcr & PMCR_IMP_MASK) >> PMCR_IMP_SHIFT;
+ idcode = (pmcr & PMCR_IDCODE_MASK) >> PMCR_IDCODE_SHIFT;
PMCDBG1(MDP, INI, 1, "arm64-init npmcs=%d", arm64_npmcs);
@@ -529,6 +557,12 @@ pmc_arm64_initialize(void)
midr &= ~(CPU_VAR_MASK | CPU_REV_MASK);
snprintf(pmc_cpuid, sizeof(pmc_cpuid), "0x%016lx", midr);
+ /* Check if we have 64-bit counters */
+ if (get_kernel_reg(ID_AA64DFR0_EL1, &dfr)) {
+ if (ID_AA64DFR0_PMUVer_VAL(dfr) >= ID_AA64DFR0_PMUVer_3_5)
+ arm64_64bit_events = true;
+ }
+
/*
* Allocate space for pointers to PMC HW descriptors and for
* the MDEP structure used by MI code.
@@ -576,7 +610,7 @@ pmc_arm64_initialize(void)
pcd->pcd_class = PMC_CLASS_ARMV8;
pcd->pcd_num = arm64_npmcs;
pcd->pcd_ri = pmc_mdep->pmd_npmc;
- pcd->pcd_width = 32;
+ pcd->pcd_width = 64;
pcd->pcd_allocate_pmc = arm64_allocate_pmc;
pcd->pcd_config_pmc = arm64_config_pmc;