xref: /freebsd/sys/dev/hwpmc/hwpmc_uncore.c (revision fdafd315)
11fa7f10bSFabien Thomas /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
41fa7f10bSFabien Thomas  * Copyright (c) 2010 Fabien Thomas
51fa7f10bSFabien Thomas  * All rights reserved.
61fa7f10bSFabien Thomas  *
71fa7f10bSFabien Thomas  * Redistribution and use in source and binary forms, with or without
81fa7f10bSFabien Thomas  * modification, are permitted provided that the following conditions
91fa7f10bSFabien Thomas  * are met:
101fa7f10bSFabien Thomas  * 1. Redistributions of source code must retain the above copyright
111fa7f10bSFabien Thomas  *    notice, this list of conditions and the following disclaimer.
121fa7f10bSFabien Thomas  * 2. Redistributions in binary form must reproduce the above copyright
131fa7f10bSFabien Thomas  *    notice, this list of conditions and the following disclaimer in the
141fa7f10bSFabien Thomas  *    documentation and/or other materials provided with the distribution.
151fa7f10bSFabien Thomas  *
161fa7f10bSFabien Thomas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171fa7f10bSFabien Thomas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181fa7f10bSFabien Thomas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191fa7f10bSFabien Thomas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201fa7f10bSFabien Thomas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211fa7f10bSFabien Thomas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221fa7f10bSFabien Thomas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231fa7f10bSFabien Thomas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241fa7f10bSFabien Thomas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251fa7f10bSFabien Thomas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261fa7f10bSFabien Thomas  * SUCH DAMAGE.
271fa7f10bSFabien Thomas  */
281fa7f10bSFabien Thomas 
291fa7f10bSFabien Thomas /*
301fa7f10bSFabien Thomas  * Intel Uncore PMCs.
311fa7f10bSFabien Thomas  */
321fa7f10bSFabien Thomas 
331fa7f10bSFabien Thomas #include <sys/param.h>
341fa7f10bSFabien Thomas #include <sys/bus.h>
351fa7f10bSFabien Thomas #include <sys/pmc.h>
361fa7f10bSFabien Thomas #include <sys/pmckern.h>
371fa7f10bSFabien Thomas #include <sys/systm.h>
381fa7f10bSFabien Thomas 
391fa7f10bSFabien Thomas #include <machine/intr_machdep.h>
40e07ef9b0SJohn Baldwin #include <x86/apicvar.h>
411fa7f10bSFabien Thomas #include <machine/cpu.h>
421fa7f10bSFabien Thomas #include <machine/cpufunc.h>
431fa7f10bSFabien Thomas #include <machine/specialreg.h>
441fa7f10bSFabien Thomas 
451fa7f10bSFabien Thomas #define	UCF_PMC_CAPS \
461fa7f10bSFabien Thomas 	(PMC_CAP_READ | PMC_CAP_WRITE)
471fa7f10bSFabien Thomas 
481fa7f10bSFabien Thomas #define	UCP_PMC_CAPS \
491fa7f10bSFabien Thomas     (PMC_CAP_EDGE | PMC_CAP_THRESHOLD | PMC_CAP_READ | PMC_CAP_WRITE | \
501fa7f10bSFabien Thomas     PMC_CAP_INVERT | PMC_CAP_QUALIFIER | PMC_CAP_PRECISE)
511fa7f10bSFabien Thomas 
5278d763a2SDavide Italiano #define	SELECTSEL(x) \
53cc0c1555SSean Bruno 	(((x) == PMC_CPU_INTEL_SANDYBRIDGE || (x) == PMC_CPU_INTEL_HASWELL) ? \
54cc0c1555SSean Bruno 	UCP_CB0_EVSEL0 : UCP_EVSEL0)
5578d763a2SDavide Italiano 
5678d763a2SDavide Italiano #define SELECTOFF(x) \
57cc0c1555SSean Bruno 	(((x) == PMC_CPU_INTEL_SANDYBRIDGE || (x) == PMC_CPU_INTEL_HASWELL) ? \
58cc0c1555SSean Bruno 	UCF_OFFSET_SB : UCF_OFFSET)
5978d763a2SDavide Italiano 
601fa7f10bSFabien Thomas static enum pmc_cputype	uncore_cputype;
611fa7f10bSFabien Thomas 
621fa7f10bSFabien Thomas struct uncore_cpu {
631fa7f10bSFabien Thomas 	volatile uint32_t	pc_ucfctrl;	/* Fixed function control. */
641fa7f10bSFabien Thomas 	volatile uint64_t	pc_globalctrl;	/* Global control register. */
651fa7f10bSFabien Thomas 	struct pmc_hw		pc_uncorepmcs[];
661fa7f10bSFabien Thomas };
671fa7f10bSFabien Thomas 
681fa7f10bSFabien Thomas static struct uncore_cpu **uncore_pcpu;
691fa7f10bSFabien Thomas 
701fa7f10bSFabien Thomas static uint64_t uncore_pmcmask;
711fa7f10bSFabien Thomas 
721fa7f10bSFabien Thomas static int uncore_ucf_ri;		/* relative index of fixed counters */
731fa7f10bSFabien Thomas static int uncore_ucf_width;
741fa7f10bSFabien Thomas static int uncore_ucf_npmc;
751fa7f10bSFabien Thomas 
761fa7f10bSFabien Thomas static int uncore_ucp_width;
771fa7f10bSFabien Thomas static int uncore_ucp_npmc;
781fa7f10bSFabien Thomas 
791fa7f10bSFabien Thomas static int
uncore_pcpu_noop(struct pmc_mdep * md,int cpu)801fa7f10bSFabien Thomas uncore_pcpu_noop(struct pmc_mdep *md, int cpu)
811fa7f10bSFabien Thomas {
821fa7f10bSFabien Thomas 	(void) md;
831fa7f10bSFabien Thomas 	(void) cpu;
841fa7f10bSFabien Thomas 	return (0);
851fa7f10bSFabien Thomas }
861fa7f10bSFabien Thomas 
871fa7f10bSFabien Thomas static int
uncore_pcpu_init(struct pmc_mdep * md,int cpu)881fa7f10bSFabien Thomas uncore_pcpu_init(struct pmc_mdep *md, int cpu)
891fa7f10bSFabien Thomas {
901fa7f10bSFabien Thomas 	struct pmc_cpu *pc;
911fa7f10bSFabien Thomas 	struct uncore_cpu *cc;
921fa7f10bSFabien Thomas 	struct pmc_hw *phw;
931fa7f10bSFabien Thomas 	int uncore_ri, n, npmc;
941fa7f10bSFabien Thomas 
951fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
961fa7f10bSFabien Thomas 	    ("[ucf,%d] insane cpu number %d", __LINE__, cpu));
971fa7f10bSFabien Thomas 
984a3690dfSJohn Baldwin 	PMCDBG1(MDP,INI,1,"uncore-init cpu=%d", cpu);
991fa7f10bSFabien Thomas 
1001fa7f10bSFabien Thomas 	uncore_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP].pcd_ri;
1011fa7f10bSFabien Thomas 	npmc = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP].pcd_num;
1021fa7f10bSFabien Thomas 	npmc += md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCF].pcd_num;
1031fa7f10bSFabien Thomas 
1041fa7f10bSFabien Thomas 	cc = malloc(sizeof(struct uncore_cpu) + npmc * sizeof(struct pmc_hw),
1051fa7f10bSFabien Thomas 	    M_PMC, M_WAITOK | M_ZERO);
1061fa7f10bSFabien Thomas 
1071fa7f10bSFabien Thomas 	uncore_pcpu[cpu] = cc;
1081fa7f10bSFabien Thomas 	pc = pmc_pcpu[cpu];
1091fa7f10bSFabien Thomas 
1101fa7f10bSFabien Thomas 	KASSERT(pc != NULL && cc != NULL,
1111fa7f10bSFabien Thomas 	    ("[uncore,%d] NULL per-cpu structures cpu=%d", __LINE__, cpu));
1121fa7f10bSFabien Thomas 
1131fa7f10bSFabien Thomas 	for (n = 0, phw = cc->pc_uncorepmcs; n < npmc; n++, phw++) {
1141fa7f10bSFabien Thomas 		phw->phw_state 	  = PMC_PHW_FLAG_IS_ENABLED |
1151fa7f10bSFabien Thomas 		    PMC_PHW_CPU_TO_STATE(cpu) |
1161fa7f10bSFabien Thomas 		    PMC_PHW_INDEX_TO_STATE(n + uncore_ri);
1171fa7f10bSFabien Thomas 		phw->phw_pmc	  = NULL;
1181fa7f10bSFabien Thomas 		pc->pc_hwpmcs[n + uncore_ri]  = phw;
1191fa7f10bSFabien Thomas 	}
1201fa7f10bSFabien Thomas 
1211fa7f10bSFabien Thomas 	return (0);
1221fa7f10bSFabien Thomas }
1231fa7f10bSFabien Thomas 
1241fa7f10bSFabien Thomas static int
uncore_pcpu_fini(struct pmc_mdep * md,int cpu)1251fa7f10bSFabien Thomas uncore_pcpu_fini(struct pmc_mdep *md, int cpu)
1261fa7f10bSFabien Thomas {
1271fa7f10bSFabien Thomas 	int uncore_ri, n, npmc;
1281fa7f10bSFabien Thomas 	struct pmc_cpu *pc;
1291fa7f10bSFabien Thomas 	struct uncore_cpu *cc;
1301fa7f10bSFabien Thomas 
1311fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
1321fa7f10bSFabien Thomas 	    ("[uncore,%d] insane cpu number (%d)", __LINE__, cpu));
1331fa7f10bSFabien Thomas 
1344a3690dfSJohn Baldwin 	PMCDBG1(MDP,INI,1,"uncore-pcpu-fini cpu=%d", cpu);
1351fa7f10bSFabien Thomas 
1361fa7f10bSFabien Thomas 	if ((cc = uncore_pcpu[cpu]) == NULL)
1371fa7f10bSFabien Thomas 		return (0);
1381fa7f10bSFabien Thomas 
1391fa7f10bSFabien Thomas 	uncore_pcpu[cpu] = NULL;
1401fa7f10bSFabien Thomas 
1411fa7f10bSFabien Thomas 	pc = pmc_pcpu[cpu];
1421fa7f10bSFabien Thomas 
1431fa7f10bSFabien Thomas 	KASSERT(pc != NULL, ("[uncore,%d] NULL per-cpu %d state", __LINE__,
1441fa7f10bSFabien Thomas 		cpu));
1451fa7f10bSFabien Thomas 
1461fa7f10bSFabien Thomas 	npmc = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP].pcd_num;
1471fa7f10bSFabien Thomas 	uncore_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP].pcd_ri;
1481fa7f10bSFabien Thomas 
1491fa7f10bSFabien Thomas 	for (n = 0; n < npmc; n++)
15078d763a2SDavide Italiano 		wrmsr(SELECTSEL(uncore_cputype) + n, 0);
1511fa7f10bSFabien Thomas 
1521fa7f10bSFabien Thomas 	wrmsr(UCF_CTRL, 0);
1531fa7f10bSFabien Thomas 	npmc += md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCF].pcd_num;
1541fa7f10bSFabien Thomas 
1551fa7f10bSFabien Thomas 	for (n = 0; n < npmc; n++)
1561fa7f10bSFabien Thomas 		pc->pc_hwpmcs[n + uncore_ri] = NULL;
1571fa7f10bSFabien Thomas 
1581fa7f10bSFabien Thomas 	free(cc, M_PMC);
1591fa7f10bSFabien Thomas 
1601fa7f10bSFabien Thomas 	return (0);
1611fa7f10bSFabien Thomas }
1621fa7f10bSFabien Thomas 
1631fa7f10bSFabien Thomas /*
1641fa7f10bSFabien Thomas  * Fixed function counters.
1651fa7f10bSFabien Thomas  */
1661fa7f10bSFabien Thomas 
1671fa7f10bSFabien Thomas static pmc_value_t
ucf_perfctr_value_to_reload_count(pmc_value_t v)1681fa7f10bSFabien Thomas ucf_perfctr_value_to_reload_count(pmc_value_t v)
1691fa7f10bSFabien Thomas {
170e74c7ffcSJessica Clarke 
171e74c7ffcSJessica Clarke 	/* If the PMC has overflowed, return a reload count of zero. */
172e74c7ffcSJessica Clarke 	if ((v & (1ULL << (uncore_ucf_width - 1))) == 0)
173e74c7ffcSJessica Clarke 		return (0);
1741fa7f10bSFabien Thomas 	v &= (1ULL << uncore_ucf_width) - 1;
1751fa7f10bSFabien Thomas 	return (1ULL << uncore_ucf_width) - v;
1761fa7f10bSFabien Thomas }
1771fa7f10bSFabien Thomas 
1781fa7f10bSFabien Thomas static pmc_value_t
ucf_reload_count_to_perfctr_value(pmc_value_t rlc)1791fa7f10bSFabien Thomas ucf_reload_count_to_perfctr_value(pmc_value_t rlc)
1801fa7f10bSFabien Thomas {
1811fa7f10bSFabien Thomas 	return (1ULL << uncore_ucf_width) - rlc;
1821fa7f10bSFabien Thomas }
1831fa7f10bSFabien Thomas 
1841fa7f10bSFabien Thomas static int
ucf_allocate_pmc(int cpu,int ri,struct pmc * pm,const struct pmc_op_pmcallocate * a)1851fa7f10bSFabien Thomas ucf_allocate_pmc(int cpu, int ri, struct pmc *pm,
1861fa7f10bSFabien Thomas     const struct pmc_op_pmcallocate *a)
1871fa7f10bSFabien Thomas {
1880e78510bSMitchell Horne 	uint32_t flags;
1891fa7f10bSFabien Thomas 
1901fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
1911fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU %d", __LINE__, cpu));
1921fa7f10bSFabien Thomas 
1934a3690dfSJohn Baldwin 	PMCDBG2(MDP,ALL,1, "ucf-allocate ri=%d reqcaps=0x%x", ri, pm->pm_caps);
1941fa7f10bSFabien Thomas 
1951fa7f10bSFabien Thomas 	if (ri < 0 || ri > uncore_ucf_npmc)
1961fa7f10bSFabien Thomas 		return (EINVAL);
1971fa7f10bSFabien Thomas 
1980e78510bSMitchell Horne 	if (a->pm_class != PMC_CLASS_UCF)
1991fa7f10bSFabien Thomas 		return (EINVAL);
2001fa7f10bSFabien Thomas 
201c190fb35SMitchell Horne 	if ((a->pm_flags & PMC_F_EV_PMU) == 0)
202c190fb35SMitchell Horne 		return (EINVAL);
203c190fb35SMitchell Horne 
2041fa7f10bSFabien Thomas 	flags = UCF_EN;
2051fa7f10bSFabien Thomas 
2061fa7f10bSFabien Thomas 	pm->pm_md.pm_ucf.pm_ucf_ctrl = (flags << (ri * 4));
2071fa7f10bSFabien Thomas 
2084a3690dfSJohn Baldwin 	PMCDBG1(MDP,ALL,2, "ucf-allocate config=0x%jx",
2091fa7f10bSFabien Thomas 	    (uintmax_t) pm->pm_md.pm_ucf.pm_ucf_ctrl);
2101fa7f10bSFabien Thomas 
2111fa7f10bSFabien Thomas 	return (0);
2121fa7f10bSFabien Thomas }
2131fa7f10bSFabien Thomas 
2141fa7f10bSFabien Thomas static int
ucf_config_pmc(int cpu,int ri,struct pmc * pm)2151fa7f10bSFabien Thomas ucf_config_pmc(int cpu, int ri, struct pmc *pm)
2161fa7f10bSFabien Thomas {
2171fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
2181fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU %d", __LINE__, cpu));
2191fa7f10bSFabien Thomas 
2201fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucf_npmc,
2211fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
2221fa7f10bSFabien Thomas 
2234a3690dfSJohn Baldwin 	PMCDBG3(MDP,CFG,1, "ucf-config cpu=%d ri=%d pm=%p", cpu, ri, pm);
2241fa7f10bSFabien Thomas 
2251fa7f10bSFabien Thomas 	KASSERT(uncore_pcpu[cpu] != NULL, ("[uncore,%d] null per-cpu %d", __LINE__,
2261fa7f10bSFabien Thomas 	    cpu));
2271fa7f10bSFabien Thomas 
2281fa7f10bSFabien Thomas 	uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc = pm;
2291fa7f10bSFabien Thomas 
2301fa7f10bSFabien Thomas 	return (0);
2311fa7f10bSFabien Thomas }
2321fa7f10bSFabien Thomas 
2331fa7f10bSFabien Thomas static int
ucf_describe(int cpu,int ri,struct pmc_info * pi,struct pmc ** ppmc)2341fa7f10bSFabien Thomas ucf_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
2351fa7f10bSFabien Thomas {
2361fa7f10bSFabien Thomas 	struct pmc_hw *phw;
2371fa7f10bSFabien Thomas 
2381fa7f10bSFabien Thomas 	phw = &uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri];
2391fa7f10bSFabien Thomas 
24031610e34SMitchell Horne 	snprintf(pi->pm_name, sizeof(pi->pm_name), "UCF-%d", ri);
2411fa7f10bSFabien Thomas 	pi->pm_class = PMC_CLASS_UCF;
2421fa7f10bSFabien Thomas 
2431fa7f10bSFabien Thomas 	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
2441fa7f10bSFabien Thomas 		pi->pm_enabled = TRUE;
2451fa7f10bSFabien Thomas 		*ppmc          = phw->phw_pmc;
2461fa7f10bSFabien Thomas 	} else {
2471fa7f10bSFabien Thomas 		pi->pm_enabled = FALSE;
2481fa7f10bSFabien Thomas 		*ppmc          = NULL;
2491fa7f10bSFabien Thomas 	}
2501fa7f10bSFabien Thomas 
2511fa7f10bSFabien Thomas 	return (0);
2521fa7f10bSFabien Thomas }
2531fa7f10bSFabien Thomas 
2541fa7f10bSFabien Thomas static int
ucf_get_config(int cpu,int ri,struct pmc ** ppm)2551fa7f10bSFabien Thomas ucf_get_config(int cpu, int ri, struct pmc **ppm)
2561fa7f10bSFabien Thomas {
2571fa7f10bSFabien Thomas 	*ppm = uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc;
2581fa7f10bSFabien Thomas 
2591fa7f10bSFabien Thomas 	return (0);
2601fa7f10bSFabien Thomas }
2611fa7f10bSFabien Thomas 
2621fa7f10bSFabien Thomas static int
ucf_read_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t * v)26339f92a76SMitchell Horne ucf_read_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t *v)
2641fa7f10bSFabien Thomas {
2651fa7f10bSFabien Thomas 	pmc_value_t tmp;
2661fa7f10bSFabien Thomas 
2671fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
2681fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal cpu value %d", __LINE__, cpu));
2691fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucf_npmc,
2701fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
2711fa7f10bSFabien Thomas 
2721fa7f10bSFabien Thomas 	tmp = rdmsr(UCF_CTR0 + ri);
2731fa7f10bSFabien Thomas 
2741fa7f10bSFabien Thomas 	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
2751fa7f10bSFabien Thomas 		*v = ucf_perfctr_value_to_reload_count(tmp);
2761fa7f10bSFabien Thomas 	else
2771fa7f10bSFabien Thomas 		*v = tmp;
2781fa7f10bSFabien Thomas 
2794a3690dfSJohn Baldwin 	PMCDBG3(MDP,REA,1, "ucf-read cpu=%d ri=%d -> v=%jx", cpu, ri, *v);
2801fa7f10bSFabien Thomas 
2811fa7f10bSFabien Thomas 	return (0);
2821fa7f10bSFabien Thomas }
2831fa7f10bSFabien Thomas 
2841fa7f10bSFabien Thomas static int
ucf_release_pmc(int cpu,int ri,struct pmc * pmc)2851fa7f10bSFabien Thomas ucf_release_pmc(int cpu, int ri, struct pmc *pmc)
2861fa7f10bSFabien Thomas {
2874a3690dfSJohn Baldwin 	PMCDBG3(MDP,REL,1, "ucf-release cpu=%d ri=%d pm=%p", cpu, ri, pmc);
2881fa7f10bSFabien Thomas 
2891fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
2901fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU value %d", __LINE__, cpu));
2911fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucf_npmc,
2921fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
2931fa7f10bSFabien Thomas 
2941fa7f10bSFabien Thomas 	KASSERT(uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc == NULL,
2951fa7f10bSFabien Thomas 	    ("[uncore,%d] PHW pmc non-NULL", __LINE__));
2961fa7f10bSFabien Thomas 
2971fa7f10bSFabien Thomas 	return (0);
2981fa7f10bSFabien Thomas }
2991fa7f10bSFabien Thomas 
3001fa7f10bSFabien Thomas static int
ucf_start_pmc(int cpu,int ri,struct pmc * pm)30139f92a76SMitchell Horne ucf_start_pmc(int cpu, int ri, struct pmc *pm)
3021fa7f10bSFabien Thomas {
3031fa7f10bSFabien Thomas 	struct uncore_cpu *ucfc;
3041fa7f10bSFabien Thomas 
3051fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
3061fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU value %d", __LINE__, cpu));
3071fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucf_npmc,
3081fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
3091fa7f10bSFabien Thomas 
3104a3690dfSJohn Baldwin 	PMCDBG2(MDP,STA,1,"ucf-start cpu=%d ri=%d", cpu, ri);
3111fa7f10bSFabien Thomas 
3121fa7f10bSFabien Thomas 	ucfc = uncore_pcpu[cpu];
3131fa7f10bSFabien Thomas 	ucfc->pc_ucfctrl |= pm->pm_md.pm_ucf.pm_ucf_ctrl;
3141fa7f10bSFabien Thomas 
3151fa7f10bSFabien Thomas 	wrmsr(UCF_CTRL, ucfc->pc_ucfctrl);
3161fa7f10bSFabien Thomas 
31778d763a2SDavide Italiano 	ucfc->pc_globalctrl |= (1ULL << (ri + SELECTOFF(uncore_cputype)));
3181fa7f10bSFabien Thomas 	wrmsr(UC_GLOBAL_CTRL, ucfc->pc_globalctrl);
3191fa7f10bSFabien Thomas 
3204a3690dfSJohn Baldwin 	PMCDBG4(MDP,STA,1,"ucfctrl=%x(%x) globalctrl=%jx(%jx)",
3211fa7f10bSFabien Thomas 	    ucfc->pc_ucfctrl, (uint32_t) rdmsr(UCF_CTRL),
3221fa7f10bSFabien Thomas 	    ucfc->pc_globalctrl, rdmsr(UC_GLOBAL_CTRL));
3231fa7f10bSFabien Thomas 
3241fa7f10bSFabien Thomas 	return (0);
3251fa7f10bSFabien Thomas }
3261fa7f10bSFabien Thomas 
3271fa7f10bSFabien Thomas static int
ucf_stop_pmc(int cpu,int ri,struct pmc * pm __unused)32839f92a76SMitchell Horne ucf_stop_pmc(int cpu, int ri, struct pmc *pm __unused)
3291fa7f10bSFabien Thomas {
3301fa7f10bSFabien Thomas 	uint32_t fc;
3311fa7f10bSFabien Thomas 	struct uncore_cpu *ucfc;
3321fa7f10bSFabien Thomas 
3334a3690dfSJohn Baldwin 	PMCDBG2(MDP,STO,1,"ucf-stop cpu=%d ri=%d", cpu, ri);
3341fa7f10bSFabien Thomas 
3351fa7f10bSFabien Thomas 	ucfc = uncore_pcpu[cpu];
3361fa7f10bSFabien Thomas 
3371fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
3381fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU value %d", __LINE__, cpu));
3391fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucf_npmc,
3401fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
3411fa7f10bSFabien Thomas 
3421fa7f10bSFabien Thomas 	fc = (UCF_MASK << (ri * 4));
3431fa7f10bSFabien Thomas 
3441fa7f10bSFabien Thomas 	ucfc->pc_ucfctrl &= ~fc;
3451fa7f10bSFabien Thomas 
3464a3690dfSJohn Baldwin 	PMCDBG1(MDP,STO,1,"ucf-stop ucfctrl=%x", ucfc->pc_ucfctrl);
3471fa7f10bSFabien Thomas 	wrmsr(UCF_CTRL, ucfc->pc_ucfctrl);
3481fa7f10bSFabien Thomas 
349326a8d3eSAlexander Motin 	/* Don't need to write UC_GLOBAL_CTRL, one disable is enough. */
3501fa7f10bSFabien Thomas 
3514a3690dfSJohn Baldwin 	PMCDBG4(MDP,STO,1,"ucfctrl=%x(%x) globalctrl=%jx(%jx)",
3521fa7f10bSFabien Thomas 	    ucfc->pc_ucfctrl, (uint32_t) rdmsr(UCF_CTRL),
3531fa7f10bSFabien Thomas 	    ucfc->pc_globalctrl, rdmsr(UC_GLOBAL_CTRL));
3541fa7f10bSFabien Thomas 
3551fa7f10bSFabien Thomas 	return (0);
3561fa7f10bSFabien Thomas }
3571fa7f10bSFabien Thomas 
3581fa7f10bSFabien Thomas static int
ucf_write_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t v)35939f92a76SMitchell Horne ucf_write_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t v)
3601fa7f10bSFabien Thomas {
3611fa7f10bSFabien Thomas 	struct uncore_cpu *cc;
3621fa7f10bSFabien Thomas 
3631fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
3641fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal cpu value %d", __LINE__, cpu));
3651fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucf_npmc,
3661fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
3671fa7f10bSFabien Thomas 
3681fa7f10bSFabien Thomas 	cc = uncore_pcpu[cpu];
3691fa7f10bSFabien Thomas 
3701fa7f10bSFabien Thomas 	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
3711fa7f10bSFabien Thomas 		v = ucf_reload_count_to_perfctr_value(v);
3721fa7f10bSFabien Thomas 
3731fa7f10bSFabien Thomas 	wrmsr(UCF_CTRL, 0);	/* Turn off fixed counters */
3741fa7f10bSFabien Thomas 	wrmsr(UCF_CTR0 + ri, v);
3751fa7f10bSFabien Thomas 	wrmsr(UCF_CTRL, cc->pc_ucfctrl);
3761fa7f10bSFabien Thomas 
3774a3690dfSJohn Baldwin 	PMCDBG4(MDP,WRI,1, "ucf-write cpu=%d ri=%d v=%jx ucfctrl=%jx ",
3781fa7f10bSFabien Thomas 	    cpu, ri, v, (uintmax_t) rdmsr(UCF_CTRL));
3791fa7f10bSFabien Thomas 
3801fa7f10bSFabien Thomas 	return (0);
3811fa7f10bSFabien Thomas }
3821fa7f10bSFabien Thomas 
3831fa7f10bSFabien Thomas 
3841fa7f10bSFabien Thomas static void
ucf_initialize(struct pmc_mdep * md,int maxcpu,int npmc,int pmcwidth)3851fa7f10bSFabien Thomas ucf_initialize(struct pmc_mdep *md, int maxcpu, int npmc, int pmcwidth)
3861fa7f10bSFabien Thomas {
3871fa7f10bSFabien Thomas 	struct pmc_classdep *pcd;
3881fa7f10bSFabien Thomas 
3891fa7f10bSFabien Thomas 	KASSERT(md != NULL, ("[ucf,%d] md is NULL", __LINE__));
3901fa7f10bSFabien Thomas 
3914a3690dfSJohn Baldwin 	PMCDBG0(MDP,INI,1, "ucf-initialize");
3921fa7f10bSFabien Thomas 
3931fa7f10bSFabien Thomas 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCF];
3941fa7f10bSFabien Thomas 
3951fa7f10bSFabien Thomas 	pcd->pcd_caps	= UCF_PMC_CAPS;
3961fa7f10bSFabien Thomas 	pcd->pcd_class	= PMC_CLASS_UCF;
3971fa7f10bSFabien Thomas 	pcd->pcd_num	= npmc;
3981fa7f10bSFabien Thomas 	pcd->pcd_ri	= md->pmd_npmc;
3991fa7f10bSFabien Thomas 	pcd->pcd_width	= pmcwidth;
4001fa7f10bSFabien Thomas 
4011fa7f10bSFabien Thomas 	pcd->pcd_allocate_pmc	= ucf_allocate_pmc;
4021fa7f10bSFabien Thomas 	pcd->pcd_config_pmc	= ucf_config_pmc;
4031fa7f10bSFabien Thomas 	pcd->pcd_describe	= ucf_describe;
4041fa7f10bSFabien Thomas 	pcd->pcd_get_config	= ucf_get_config;
4051fa7f10bSFabien Thomas 	pcd->pcd_get_msr	= NULL;
4061fa7f10bSFabien Thomas 	pcd->pcd_pcpu_fini	= uncore_pcpu_noop;
4071fa7f10bSFabien Thomas 	pcd->pcd_pcpu_init	= uncore_pcpu_noop;
4081fa7f10bSFabien Thomas 	pcd->pcd_read_pmc	= ucf_read_pmc;
4091fa7f10bSFabien Thomas 	pcd->pcd_release_pmc	= ucf_release_pmc;
4101fa7f10bSFabien Thomas 	pcd->pcd_start_pmc	= ucf_start_pmc;
4111fa7f10bSFabien Thomas 	pcd->pcd_stop_pmc	= ucf_stop_pmc;
4121fa7f10bSFabien Thomas 	pcd->pcd_write_pmc	= ucf_write_pmc;
4131fa7f10bSFabien Thomas 
4141fa7f10bSFabien Thomas 	md->pmd_npmc	       += npmc;
4151fa7f10bSFabien Thomas }
4161fa7f10bSFabien Thomas 
4171fa7f10bSFabien Thomas /*
4181fa7f10bSFabien Thomas  * Intel programmable PMCs.
4191fa7f10bSFabien Thomas  */
4201fa7f10bSFabien Thomas 
4211fa7f10bSFabien Thomas /*
4221fa7f10bSFabien Thomas  * Event descriptor tables.
4231fa7f10bSFabien Thomas  *
4241fa7f10bSFabien Thomas  * For each event id, we track:
4251fa7f10bSFabien Thomas  *
4261fa7f10bSFabien Thomas  * 1. The CPUs that the event is valid for.
4271fa7f10bSFabien Thomas  *
4281fa7f10bSFabien Thomas  * 2. If the event uses a fixed UMASK, the value of the umask field.
4291fa7f10bSFabien Thomas  *    If the event doesn't use a fixed UMASK, a mask of legal bits
4301fa7f10bSFabien Thomas  *    to check against.
4311fa7f10bSFabien Thomas  */
4321fa7f10bSFabien Thomas 
4331fa7f10bSFabien Thomas struct ucp_event_descr {
4341fa7f10bSFabien Thomas 	enum pmc_event	ucp_ev;
4351fa7f10bSFabien Thomas 	unsigned char	ucp_evcode;
4361fa7f10bSFabien Thomas 	unsigned char	ucp_umask;
4371fa7f10bSFabien Thomas 	unsigned char	ucp_flags;
4381fa7f10bSFabien Thomas };
4391fa7f10bSFabien Thomas 
4401fa7f10bSFabien Thomas #define	UCP_F_I7	(1 << 0)	/* CPU: Core i7 */
4411fa7f10bSFabien Thomas #define	UCP_F_WM	(1 << 1)	/* CPU: Westmere */
44278d763a2SDavide Italiano #define	UCP_F_SB	(1 << 2)	/* CPU: Sandy Bridge */
443cc0c1555SSean Bruno #define	UCP_F_HW	(1 << 3)	/* CPU: Haswell */
444cc0c1555SSean Bruno #define	UCP_F_FM	(1 << 4)	/* Fixed mask */
4451fa7f10bSFabien Thomas 
4461fa7f10bSFabien Thomas #define	UCP_F_ALLCPUS					\
4471fa7f10bSFabien Thomas     (UCP_F_I7 | UCP_F_WM)
4481fa7f10bSFabien Thomas 
4491fa7f10bSFabien Thomas #define	UCP_F_CMASK		0xFF000000
4501fa7f10bSFabien Thomas 
4511fa7f10bSFabien Thomas static pmc_value_t
ucp_perfctr_value_to_reload_count(pmc_value_t v)4521fa7f10bSFabien Thomas ucp_perfctr_value_to_reload_count(pmc_value_t v)
4531fa7f10bSFabien Thomas {
4541fa7f10bSFabien Thomas 	v &= (1ULL << uncore_ucp_width) - 1;
4551fa7f10bSFabien Thomas 	return (1ULL << uncore_ucp_width) - v;
4561fa7f10bSFabien Thomas }
4571fa7f10bSFabien Thomas 
4581fa7f10bSFabien Thomas static pmc_value_t
ucp_reload_count_to_perfctr_value(pmc_value_t rlc)4591fa7f10bSFabien Thomas ucp_reload_count_to_perfctr_value(pmc_value_t rlc)
4601fa7f10bSFabien Thomas {
4611fa7f10bSFabien Thomas 	return (1ULL << uncore_ucp_width) - rlc;
4621fa7f10bSFabien Thomas }
4631fa7f10bSFabien Thomas 
464cc0c1555SSean Bruno /*
465cc0c1555SSean Bruno  * Counter specific event information for Sandybridge and Haswell
466cc0c1555SSean Bruno  */
4671fa7f10bSFabien Thomas static int
ucp_event_sb_hw_ok_on_counter(uint8_t ev,int ri)468e92a1350SMatt Macy ucp_event_sb_hw_ok_on_counter(uint8_t ev, int ri)
46978d763a2SDavide Italiano {
47078d763a2SDavide Italiano 	uint32_t mask;
47178d763a2SDavide Italiano 
472e92a1350SMatt Macy 	switch (ev) {
47378d763a2SDavide Italiano 		/*
47478d763a2SDavide Italiano 		 * Events valid only on counter 0.
47578d763a2SDavide Italiano 		 */
476e92a1350SMatt Macy 		case 0x80:
477e92a1350SMatt Macy 		case 0x83:
47878d763a2SDavide Italiano 		mask = (1 << 0);
47978d763a2SDavide Italiano 		break;
48078d763a2SDavide Italiano 
48178d763a2SDavide Italiano 	default:
48278d763a2SDavide Italiano 		mask = ~0;	/* Any row index is ok. */
48378d763a2SDavide Italiano 	}
48478d763a2SDavide Italiano 
48578d763a2SDavide Italiano 	return (mask & (1 << ri));
48678d763a2SDavide Italiano }
48778d763a2SDavide Italiano 
48878d763a2SDavide Italiano static int
ucp_allocate_pmc(int cpu,int ri,struct pmc * pm,const struct pmc_op_pmcallocate * a)4891fa7f10bSFabien Thomas ucp_allocate_pmc(int cpu, int ri, struct pmc *pm,
4901fa7f10bSFabien Thomas     const struct pmc_op_pmcallocate *a)
4911fa7f10bSFabien Thomas {
492e92a1350SMatt Macy 	uint8_t ev;
493e92a1350SMatt Macy 	const struct pmc_md_ucp_op_pmcallocate *ucp;
4941fa7f10bSFabien Thomas 
4951fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
4961fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU %d", __LINE__, cpu));
4971fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucp_npmc,
4981fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index value %d", __LINE__, ri));
4991fa7f10bSFabien Thomas 
500315cd55dSMitchell Horne 	if (a->pm_class != PMC_CLASS_UCP)
501315cd55dSMitchell Horne 		return (EINVAL);
502315cd55dSMitchell Horne 
503c190fb35SMitchell Horne 	if ((a->pm_flags & PMC_F_EV_PMU) == 0)
504c190fb35SMitchell Horne 		return (EINVAL);
505c190fb35SMitchell Horne 
506e92a1350SMatt Macy 	ucp = &a->pm_md.pm_ucp;
507e92a1350SMatt Macy 	ev = UCP_EVSEL(ucp->pm_ucp_config);
50878d763a2SDavide Italiano 	switch (uncore_cputype) {
509cc0c1555SSean Bruno 	case PMC_CPU_INTEL_HASWELL:
51078d763a2SDavide Italiano 	case PMC_CPU_INTEL_SANDYBRIDGE:
511cc0c1555SSean Bruno 		if (ucp_event_sb_hw_ok_on_counter(ev, ri) == 0)
51278d763a2SDavide Italiano 			return (EINVAL);
51378d763a2SDavide Italiano 		break;
51478d763a2SDavide Italiano 	default:
51578d763a2SDavide Italiano 		break;
51678d763a2SDavide Italiano 	}
51778d763a2SDavide Italiano 
518e92a1350SMatt Macy 	pm->pm_md.pm_ucp.pm_ucp_evsel = ucp->pm_ucp_config | UCP_EN;
5191fa7f10bSFabien Thomas 
5201fa7f10bSFabien Thomas 	return (0);
5211fa7f10bSFabien Thomas }
5221fa7f10bSFabien Thomas 
5231fa7f10bSFabien Thomas static int
ucp_config_pmc(int cpu,int ri,struct pmc * pm)5241fa7f10bSFabien Thomas ucp_config_pmc(int cpu, int ri, struct pmc *pm)
5251fa7f10bSFabien Thomas {
5261fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
5271fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU %d", __LINE__, cpu));
5281fa7f10bSFabien Thomas 
5291fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucp_npmc,
5301fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
5311fa7f10bSFabien Thomas 
5324a3690dfSJohn Baldwin 	PMCDBG3(MDP,CFG,1, "ucp-config cpu=%d ri=%d pm=%p", cpu, ri, pm);
5331fa7f10bSFabien Thomas 
5341fa7f10bSFabien Thomas 	KASSERT(uncore_pcpu[cpu] != NULL, ("[uncore,%d] null per-cpu %d", __LINE__,
5351fa7f10bSFabien Thomas 	    cpu));
5361fa7f10bSFabien Thomas 
5371fa7f10bSFabien Thomas 	uncore_pcpu[cpu]->pc_uncorepmcs[ri].phw_pmc = pm;
5381fa7f10bSFabien Thomas 
5391fa7f10bSFabien Thomas 	return (0);
5401fa7f10bSFabien Thomas }
5411fa7f10bSFabien Thomas 
5421fa7f10bSFabien Thomas static int
ucp_describe(int cpu,int ri,struct pmc_info * pi,struct pmc ** ppmc)5431fa7f10bSFabien Thomas ucp_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
5441fa7f10bSFabien Thomas {
5451fa7f10bSFabien Thomas 	struct pmc_hw *phw;
5461fa7f10bSFabien Thomas 
5471fa7f10bSFabien Thomas 	phw = &uncore_pcpu[cpu]->pc_uncorepmcs[ri];
5481fa7f10bSFabien Thomas 
54931610e34SMitchell Horne 	snprintf(pi->pm_name, sizeof(pi->pm_name), "UCP-%d", ri);
5501fa7f10bSFabien Thomas 	pi->pm_class = PMC_CLASS_UCP;
5511fa7f10bSFabien Thomas 
5521fa7f10bSFabien Thomas 	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
5531fa7f10bSFabien Thomas 		pi->pm_enabled = TRUE;
5541fa7f10bSFabien Thomas 		*ppmc          = phw->phw_pmc;
5551fa7f10bSFabien Thomas 	} else {
5561fa7f10bSFabien Thomas 		pi->pm_enabled = FALSE;
5571fa7f10bSFabien Thomas 		*ppmc          = NULL;
5581fa7f10bSFabien Thomas 	}
5591fa7f10bSFabien Thomas 
5601fa7f10bSFabien Thomas 	return (0);
5611fa7f10bSFabien Thomas }
5621fa7f10bSFabien Thomas 
5631fa7f10bSFabien Thomas static int
ucp_get_config(int cpu,int ri,struct pmc ** ppm)5641fa7f10bSFabien Thomas ucp_get_config(int cpu, int ri, struct pmc **ppm)
5651fa7f10bSFabien Thomas {
5661fa7f10bSFabien Thomas 	*ppm = uncore_pcpu[cpu]->pc_uncorepmcs[ri].phw_pmc;
5671fa7f10bSFabien Thomas 
5681fa7f10bSFabien Thomas 	return (0);
5691fa7f10bSFabien Thomas }
5701fa7f10bSFabien Thomas 
5711fa7f10bSFabien Thomas static int
ucp_read_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t * v)57239f92a76SMitchell Horne ucp_read_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t *v)
5731fa7f10bSFabien Thomas {
5741fa7f10bSFabien Thomas 	pmc_value_t tmp;
5751fa7f10bSFabien Thomas 
5761fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
5771fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal cpu value %d", __LINE__, cpu));
5781fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucp_npmc,
5791fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
5801fa7f10bSFabien Thomas 
5811fa7f10bSFabien Thomas 	tmp = rdmsr(UCP_PMC0 + ri);
5821fa7f10bSFabien Thomas 	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
5831fa7f10bSFabien Thomas 		*v = ucp_perfctr_value_to_reload_count(tmp);
5841fa7f10bSFabien Thomas 	else
5851fa7f10bSFabien Thomas 		*v = tmp;
5861fa7f10bSFabien Thomas 
5874a3690dfSJohn Baldwin 	PMCDBG4(MDP,REA,1, "ucp-read cpu=%d ri=%d msr=0x%x -> v=%jx", cpu, ri,
5881fa7f10bSFabien Thomas 	    ri, *v);
5891fa7f10bSFabien Thomas 
5901fa7f10bSFabien Thomas 	return (0);
5911fa7f10bSFabien Thomas }
5921fa7f10bSFabien Thomas 
5931fa7f10bSFabien Thomas static int
ucp_release_pmc(int cpu,int ri,struct pmc * pm)5941fa7f10bSFabien Thomas ucp_release_pmc(int cpu, int ri, struct pmc *pm)
5951fa7f10bSFabien Thomas {
5961fa7f10bSFabien Thomas 	(void) pm;
5971fa7f10bSFabien Thomas 
5984a3690dfSJohn Baldwin 	PMCDBG3(MDP,REL,1, "ucp-release cpu=%d ri=%d pm=%p", cpu, ri,
5991fa7f10bSFabien Thomas 	    pm);
6001fa7f10bSFabien Thomas 
6011fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
6021fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU value %d", __LINE__, cpu));
6031fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucp_npmc,
6041fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
6051fa7f10bSFabien Thomas 
6061fa7f10bSFabien Thomas 	KASSERT(uncore_pcpu[cpu]->pc_uncorepmcs[ri].phw_pmc
6071fa7f10bSFabien Thomas 	    == NULL, ("[uncore,%d] PHW pmc non-NULL", __LINE__));
6081fa7f10bSFabien Thomas 
6091fa7f10bSFabien Thomas 	return (0);
6101fa7f10bSFabien Thomas }
6111fa7f10bSFabien Thomas 
6121fa7f10bSFabien Thomas static int
ucp_start_pmc(int cpu,int ri,struct pmc * pm)61339f92a76SMitchell Horne ucp_start_pmc(int cpu, int ri, struct pmc *pm)
6141fa7f10bSFabien Thomas {
6151a4614a5SAlexander Motin 	uint64_t evsel;
6161fa7f10bSFabien Thomas 	struct uncore_cpu *cc;
6171fa7f10bSFabien Thomas 
6181fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
6191fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal CPU value %d", __LINE__, cpu));
6201fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucp_npmc,
6211fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row-index %d", __LINE__, ri));
6221fa7f10bSFabien Thomas 
6231fa7f10bSFabien Thomas 	cc = uncore_pcpu[cpu];
6241fa7f10bSFabien Thomas 
6254a3690dfSJohn Baldwin 	PMCDBG2(MDP,STA,1, "ucp-start cpu=%d ri=%d", cpu, ri);
6261fa7f10bSFabien Thomas 
6271fa7f10bSFabien Thomas 	evsel = pm->pm_md.pm_ucp.pm_ucp_evsel;
6281fa7f10bSFabien Thomas 
6294a3690dfSJohn Baldwin 	PMCDBG4(MDP,STA,2,
63078d763a2SDavide Italiano 	    "ucp-start/2 cpu=%d ri=%d evselmsr=0x%x evsel=0x%x",
63178d763a2SDavide Italiano 	    cpu, ri, SELECTSEL(uncore_cputype) + ri, evsel);
6321fa7f10bSFabien Thomas 
63378d763a2SDavide Italiano 	wrmsr(SELECTSEL(uncore_cputype) + ri, evsel);
6341fa7f10bSFabien Thomas 
6351fa7f10bSFabien Thomas 	cc->pc_globalctrl |= (1ULL << ri);
6361fa7f10bSFabien Thomas 	wrmsr(UC_GLOBAL_CTRL, cc->pc_globalctrl);
6371fa7f10bSFabien Thomas 
6381fa7f10bSFabien Thomas 	return (0);
6391fa7f10bSFabien Thomas }
6401fa7f10bSFabien Thomas 
6411fa7f10bSFabien Thomas static int
ucp_stop_pmc(int cpu,int ri,struct pmc * pm __unused)64239f92a76SMitchell Horne ucp_stop_pmc(int cpu, int ri, struct pmc *pm __unused)
6431fa7f10bSFabien Thomas {
6441fa7f10bSFabien Thomas 
6451fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
6461fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal cpu value %d", __LINE__, cpu));
6471fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucp_npmc,
6481fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row index %d", __LINE__, ri));
6491fa7f10bSFabien Thomas 
6504a3690dfSJohn Baldwin 	PMCDBG2(MDP,STO,1, "ucp-stop cpu=%d ri=%d", cpu, ri);
6511fa7f10bSFabien Thomas 
65278d763a2SDavide Italiano 	/* stop hw. */
65378d763a2SDavide Italiano 	wrmsr(SELECTSEL(uncore_cputype) + ri, 0);
6541fa7f10bSFabien Thomas 
655326a8d3eSAlexander Motin 	/* Don't need to write UC_GLOBAL_CTRL, one disable is enough. */
6561fa7f10bSFabien Thomas 
6571fa7f10bSFabien Thomas 	return (0);
6581fa7f10bSFabien Thomas }
6591fa7f10bSFabien Thomas 
6601fa7f10bSFabien Thomas static int
ucp_write_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t v)66139f92a76SMitchell Horne ucp_write_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t v)
6621fa7f10bSFabien Thomas {
6631fa7f10bSFabien Thomas 
6641fa7f10bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
6651fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal cpu value %d", __LINE__, cpu));
6661fa7f10bSFabien Thomas 	KASSERT(ri >= 0 && ri < uncore_ucp_npmc,
6671fa7f10bSFabien Thomas 	    ("[uncore,%d] illegal row index %d", __LINE__, ri));
6681fa7f10bSFabien Thomas 
6694a3690dfSJohn Baldwin 	PMCDBG4(MDP,WRI,1, "ucp-write cpu=%d ri=%d msr=0x%x v=%jx", cpu, ri,
6701fa7f10bSFabien Thomas 	    UCP_PMC0 + ri, v);
6711fa7f10bSFabien Thomas 
6721fa7f10bSFabien Thomas 	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
6731fa7f10bSFabien Thomas 		v = ucp_reload_count_to_perfctr_value(v);
6741fa7f10bSFabien Thomas 
6751fa7f10bSFabien Thomas 	/*
6761fa7f10bSFabien Thomas 	 * Write the new value to the counter.  The counter will be in
6771fa7f10bSFabien Thomas 	 * a stopped state when the pcd_write() entry point is called.
6781fa7f10bSFabien Thomas 	 */
6791fa7f10bSFabien Thomas 
6801fa7f10bSFabien Thomas 	wrmsr(UCP_PMC0 + ri, v);
6811fa7f10bSFabien Thomas 
6821fa7f10bSFabien Thomas 	return (0);
6831fa7f10bSFabien Thomas }
6841fa7f10bSFabien Thomas 
6851fa7f10bSFabien Thomas 
6861fa7f10bSFabien Thomas static void
ucp_initialize(struct pmc_mdep * md,int maxcpu,int npmc,int pmcwidth)6871fa7f10bSFabien Thomas ucp_initialize(struct pmc_mdep *md, int maxcpu, int npmc, int pmcwidth)
6881fa7f10bSFabien Thomas {
6891fa7f10bSFabien Thomas 	struct pmc_classdep *pcd;
6901fa7f10bSFabien Thomas 
6911fa7f10bSFabien Thomas 	KASSERT(md != NULL, ("[ucp,%d] md is NULL", __LINE__));
6921fa7f10bSFabien Thomas 
6934a3690dfSJohn Baldwin 	PMCDBG0(MDP,INI,1, "ucp-initialize");
6941fa7f10bSFabien Thomas 
6951fa7f10bSFabien Thomas 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP];
6961fa7f10bSFabien Thomas 
6971fa7f10bSFabien Thomas 	pcd->pcd_caps	= UCP_PMC_CAPS;
6981fa7f10bSFabien Thomas 	pcd->pcd_class	= PMC_CLASS_UCP;
6991fa7f10bSFabien Thomas 	pcd->pcd_num	= npmc;
7001fa7f10bSFabien Thomas 	pcd->pcd_ri	= md->pmd_npmc;
7011fa7f10bSFabien Thomas 	pcd->pcd_width	= pmcwidth;
7021fa7f10bSFabien Thomas 
7031fa7f10bSFabien Thomas 	pcd->pcd_allocate_pmc	= ucp_allocate_pmc;
7041fa7f10bSFabien Thomas 	pcd->pcd_config_pmc	= ucp_config_pmc;
7051fa7f10bSFabien Thomas 	pcd->pcd_describe	= ucp_describe;
7061fa7f10bSFabien Thomas 	pcd->pcd_get_config	= ucp_get_config;
7071fa7f10bSFabien Thomas 	pcd->pcd_get_msr	= NULL;
7081fa7f10bSFabien Thomas 	pcd->pcd_pcpu_fini	= uncore_pcpu_fini;
7091fa7f10bSFabien Thomas 	pcd->pcd_pcpu_init	= uncore_pcpu_init;
7101fa7f10bSFabien Thomas 	pcd->pcd_read_pmc	= ucp_read_pmc;
7111fa7f10bSFabien Thomas 	pcd->pcd_release_pmc	= ucp_release_pmc;
7121fa7f10bSFabien Thomas 	pcd->pcd_start_pmc	= ucp_start_pmc;
7131fa7f10bSFabien Thomas 	pcd->pcd_stop_pmc	= ucp_stop_pmc;
7141fa7f10bSFabien Thomas 	pcd->pcd_write_pmc	= ucp_write_pmc;
7151fa7f10bSFabien Thomas 
7161fa7f10bSFabien Thomas 	md->pmd_npmc	       += npmc;
7171fa7f10bSFabien Thomas }
7181fa7f10bSFabien Thomas 
7191fa7f10bSFabien Thomas int
pmc_uncore_initialize(struct pmc_mdep * md,int maxcpu)7201fa7f10bSFabien Thomas pmc_uncore_initialize(struct pmc_mdep *md, int maxcpu)
7211fa7f10bSFabien Thomas {
7221fa7f10bSFabien Thomas 	uncore_cputype = md->pmd_cputype;
7231fa7f10bSFabien Thomas 	uncore_pmcmask = 0;
7241fa7f10bSFabien Thomas 
7251fa7f10bSFabien Thomas 	/*
7261fa7f10bSFabien Thomas 	 * Initialize programmable counters.
7271fa7f10bSFabien Thomas 	 */
7281fa7f10bSFabien Thomas 
7291fa7f10bSFabien Thomas 	uncore_ucp_npmc  = 8;
7301fa7f10bSFabien Thomas 	uncore_ucp_width = 48;
7311fa7f10bSFabien Thomas 
7321fa7f10bSFabien Thomas 	uncore_pmcmask |= ((1ULL << uncore_ucp_npmc) - 1);
7331fa7f10bSFabien Thomas 
7341fa7f10bSFabien Thomas 	ucp_initialize(md, maxcpu, uncore_ucp_npmc, uncore_ucp_width);
7351fa7f10bSFabien Thomas 
7361fa7f10bSFabien Thomas 	/*
7371fa7f10bSFabien Thomas 	 * Initialize fixed function counters, if present.
7381fa7f10bSFabien Thomas 	 */
7391fa7f10bSFabien Thomas 	uncore_ucf_ri = uncore_ucp_npmc;
7401fa7f10bSFabien Thomas 	uncore_ucf_npmc  = 1;
7411fa7f10bSFabien Thomas 	uncore_ucf_width = 48;
7421fa7f10bSFabien Thomas 
7431fa7f10bSFabien Thomas 	ucf_initialize(md, maxcpu, uncore_ucf_npmc, uncore_ucf_width);
74478d763a2SDavide Italiano 	uncore_pmcmask |= ((1ULL << uncore_ucf_npmc) - 1) << SELECTOFF(uncore_cputype);
7451fa7f10bSFabien Thomas 
7464a3690dfSJohn Baldwin 	PMCDBG2(MDP,INI,1,"uncore-init pmcmask=0x%jx ucfri=%d", uncore_pmcmask,
7471fa7f10bSFabien Thomas 	    uncore_ucf_ri);
7481fa7f10bSFabien Thomas 
749c08da142SEitan Adler 	uncore_pcpu = malloc(sizeof(*uncore_pcpu) * maxcpu, M_PMC,
7501fa7f10bSFabien Thomas 	    M_ZERO | M_WAITOK);
7511fa7f10bSFabien Thomas 
7521fa7f10bSFabien Thomas 	return (0);
7531fa7f10bSFabien Thomas }
7541fa7f10bSFabien Thomas 
7551fa7f10bSFabien Thomas void
pmc_uncore_finalize(struct pmc_mdep * md)7561fa7f10bSFabien Thomas pmc_uncore_finalize(struct pmc_mdep *md)
7571fa7f10bSFabien Thomas {
7584a3690dfSJohn Baldwin 	PMCDBG0(MDP,INI,1, "uncore-finalize");
7591fa7f10bSFabien Thomas 
760*90a6ea5cSMitchell Horne 	for (int i = 0; i < pmc_cpu_max(); i++)
761*90a6ea5cSMitchell Horne 		KASSERT(uncore_pcpu[i] == NULL,
762*90a6ea5cSMitchell Horne 		    ("[uncore,%d] non-null pcpu cpu %d", __LINE__, i));
763*90a6ea5cSMitchell Horne 
7641fa7f10bSFabien Thomas 	free(uncore_pcpu, M_PMC);
7651fa7f10bSFabien Thomas 	uncore_pcpu = NULL;
7661fa7f10bSFabien Thomas }
767