#include <sys/param.h>
#include <sys/pmc.h>
#include <sys/pmckern.h>
#include <sys/systm.h>
#include <machine/pmc_mdep.h>
#include <machine/spr.h>
#include <machine/cpu.h>
#include "hwpmc_powerpc.h"
#define POWER8_MAX_PMCS 6
#define PM_EVENT_CODE(pe) (pe & 0xffff)
#define PM_EVENT_COUNTER(pe) ((pe >> 16) & 0xffff)
#define PM_CYC 0x1e
#define PM_INST_CMPL 0x02
static void
power8_set_pmc(int cpu, int ri, int config)
{
register_t mmcr;
switch (ri) {
case 0:
case 1:
case 2:
case 3:
mmcr = mfspr(SPR_MMCR1);
mmcr &= ~SPR_MMCR1_P8_PMCNSEL_MASK(ri);
mmcr |= SPR_MMCR1_P8_PMCNSEL(ri, config & ~POWERPC_PMC_ENABLE);
mtspr(SPR_MMCR1, mmcr);
break;
}
mmcr = mfspr(SPR_MMCR2) | SPR_MMCR2_FCNHSP(ri);
if (config != PMCN_NONE) {
if (config & POWERPC_PMC_USER_ENABLE)
mmcr &= ~(SPR_MMCR2_FCNP0(ri) |
SPR_MMCR2_FCNP1(ri));
if (config & POWERPC_PMC_KERNEL_ENABLE)
mmcr &= ~(SPR_MMCR2_FCNH(ri) |
SPR_MMCR2_FCNS(ri));
}
mtspr(SPR_MMCR2, mmcr);
}
static int
power8_pcpu_init(struct pmc_mdep *md, int cpu)
{
register_t mmcr0;
int i;
powerpc_pcpu_init(md, cpu);
mmcr0 = mfspr(SPR_MMCR0) | SPR_MMCR0_FC;
mtspr(SPR_MMCR0, mmcr0);
mmcr0 &= ~(SPR_MMCR0_PMAO | SPR_MMCR0_FCPC | SPR_MMCR0_FCP);
mmcr0 |= SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE;
mtspr(SPR_MMCR0, mmcr0);
for (i = 0; i < POWER8_MAX_PMCS; i++)
powerpc_pmcn_write(i, 0);
mtspr(SPR_MMCR1, mfspr(SPR_MMCR1) & ~SPR_MMCR1_P8_PMCSEL_ALL);
mtspr(SPR_MMCR2, mfspr(SPR_MMCR2) |
SPR_MMCR2_FCNHSP(0) | SPR_MMCR2_FCNHSP(1) | SPR_MMCR2_FCNHSP(2) |
SPR_MMCR2_FCNHSP(3) | SPR_MMCR2_FCNHSP(4) | SPR_MMCR2_FCNHSP(5));
mmcr0 &= ~SPR_MMCR0_FC;
mmcr0 |= SPR_MMCR0_PMAE;
mtspr(SPR_MMCR0, mmcr0);
return (0);
}
static int
power8_pcpu_fini(struct pmc_mdep *md, int cpu)
{
register_t mmcr0;
mmcr0 = mfspr(SPR_MMCR0);
mmcr0 &= ~SPR_MMCR0_PMAE;
mmcr0 |= SPR_MMCR0_FC;
mtspr(SPR_MMCR0, mmcr0);
return (powerpc_pcpu_fini(md, cpu));
}
static void
power8_resume_pmc(bool ie)
{
register_t mmcr0;
mmcr0 = mfspr(SPR_MMCR0);
mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMAO | SPR_MMCR0_PMAE);
if (ie)
mmcr0 |= SPR_MMCR0_PMAE;
mtspr(SPR_MMCR0, mmcr0);
}
static int
power8_allocate_pmc(int cpu, int ri, struct pmc *pm,
const struct pmc_op_pmcallocate *a)
{
uint32_t caps, config, counter, pe;
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
("[powerpc,%d] illegal CPU value %d", __LINE__, cpu));
KASSERT(ri >= 0 && ri < ppc_max_pmcs,
("[powerpc,%d] illegal row index %d", __LINE__, ri));
pe = a->pm_md.pm_event;
counter = PM_EVENT_COUNTER(pe);
config = PM_EVENT_CODE(pe);
if (a->pm_class != PMC_CLASS_POWER8)
return (EINVAL);
if ((a->pm_flags & PMC_F_EV_PMU) == 0)
return (EINVAL);
if (counter == 0 && config == PM_INST_CMPL)
counter = 5;
else if (counter == 0 && config == PM_CYC)
counter = 6;
else if (counter > 4)
return (EINVAL);
if (counter != 0 && counter != ri + 1)
return (EINVAL);
caps = a->pm_caps;
if (caps & PMC_CAP_SYSTEM)
config |= POWERPC_PMC_KERNEL_ENABLE;
if (caps & PMC_CAP_USER)
config |= POWERPC_PMC_USER_ENABLE;
if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0)
config |= POWERPC_PMC_ENABLE;
pm->pm_md.pm_powerpc.pm_powerpc_evsel = config;
PMCDBG3(MDP,ALL,1,"powerpc-allocate cpu=%d ri=%d -> config=0x%x",
cpu, ri, config);
return (0);
}
int
pmc_power8_initialize(struct pmc_mdep *pmc_mdep)
{
struct pmc_classdep *pcd;
pmc_mdep->pmd_cputype = PMC_CPU_PPC_POWER8;
pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC];
pcd->pcd_caps = POWERPC_PMC_CAPS;
pcd->pcd_class = PMC_CLASS_POWER8;
pcd->pcd_num = POWER8_MAX_PMCS;
pcd->pcd_ri = pmc_mdep->pmd_npmc;
pcd->pcd_width = 32;
pcd->pcd_pcpu_init = power8_pcpu_init;
pcd->pcd_pcpu_fini = power8_pcpu_fini;
pcd->pcd_allocate_pmc = power8_allocate_pmc;
pcd->pcd_release_pmc = powerpc_release_pmc;
pcd->pcd_start_pmc = powerpc_start_pmc;
pcd->pcd_stop_pmc = powerpc_stop_pmc;
pcd->pcd_get_config = powerpc_get_config;
pcd->pcd_config_pmc = powerpc_config_pmc;
pcd->pcd_describe = powerpc_describe;
pcd->pcd_read_pmc = powerpc_read_pmc;
pcd->pcd_write_pmc = powerpc_write_pmc;
pmc_mdep->pmd_npmc += POWER8_MAX_PMCS;
pmc_mdep->pmd_intr = powerpc_pmc_intr;
ppc_max_pmcs = POWER8_MAX_PMCS;
powerpc_set_pmc = power8_set_pmc;
powerpc_pmcn_read = powerpc_pmcn_read_default;
powerpc_pmcn_write = powerpc_pmcn_write_default;
powerpc_resume_pmc = power8_resume_pmc;
return (0);
}