xref: /freebsd/sys/dev/hwpmc/hwpmc_power8.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
168dd7182SLeandro Lupori /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
368dd7182SLeandro Lupori  *
468dd7182SLeandro Lupori  * Copyright (c) 2013 Justin Hibbits
568dd7182SLeandro Lupori  * Copyright (c) 2020 Leandro Lupori
668dd7182SLeandro Lupori  * All rights reserved.
768dd7182SLeandro Lupori  *
868dd7182SLeandro Lupori  * Redistribution and use in source and binary forms, with or without
968dd7182SLeandro Lupori  * modification, are permitted provided that the following conditions
1068dd7182SLeandro Lupori  * are met:
1168dd7182SLeandro Lupori  * 1. Redistributions of source code must retain the above copyright
1268dd7182SLeandro Lupori  *    notice, this list of conditions and the following disclaimer.
1368dd7182SLeandro Lupori  * 2. Redistributions in binary form must reproduce the above copyright
1468dd7182SLeandro Lupori  *    notice, this list of conditions and the following disclaimer in the
1568dd7182SLeandro Lupori  *    documentation and/or other materials provided with the distribution.
1668dd7182SLeandro Lupori  *
1768dd7182SLeandro Lupori  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1868dd7182SLeandro Lupori  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1968dd7182SLeandro Lupori  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2068dd7182SLeandro Lupori  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2168dd7182SLeandro Lupori  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2268dd7182SLeandro Lupori  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2368dd7182SLeandro Lupori  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2468dd7182SLeandro Lupori  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2568dd7182SLeandro Lupori  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2668dd7182SLeandro Lupori  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2768dd7182SLeandro Lupori  * SUCH DAMAGE.
2868dd7182SLeandro Lupori  */
2968dd7182SLeandro Lupori 
3068dd7182SLeandro Lupori #include <sys/param.h>
3168dd7182SLeandro Lupori #include <sys/pmc.h>
3268dd7182SLeandro Lupori #include <sys/pmckern.h>
3368dd7182SLeandro Lupori #include <sys/systm.h>
3468dd7182SLeandro Lupori 
3568dd7182SLeandro Lupori #include <machine/pmc_mdep.h>
3668dd7182SLeandro Lupori #include <machine/spr.h>
3768dd7182SLeandro Lupori #include <machine/cpu.h>
3868dd7182SLeandro Lupori 
3968dd7182SLeandro Lupori #include "hwpmc_powerpc.h"
4068dd7182SLeandro Lupori 
4168dd7182SLeandro Lupori #define	POWER8_MAX_PMCS		6
4268dd7182SLeandro Lupori 
43b48a2770SLeandro Lupori #define PM_EVENT_CODE(pe)	(pe & 0xffff)
44b48a2770SLeandro Lupori #define PM_EVENT_COUNTER(pe)	((pe >> 16) & 0xffff)
45b48a2770SLeandro Lupori 
46b48a2770SLeandro Lupori #define PM_CYC			0x1e
47b48a2770SLeandro Lupori #define PM_INST_CMPL		0x02
48b48a2770SLeandro Lupori 
4968dd7182SLeandro Lupori static void
power8_set_pmc(int cpu,int ri,int config)5068dd7182SLeandro Lupori power8_set_pmc(int cpu, int ri, int config)
5168dd7182SLeandro Lupori {
5268dd7182SLeandro Lupori 	register_t mmcr;
5368dd7182SLeandro Lupori 
5468dd7182SLeandro Lupori 	/* Select event */
5568dd7182SLeandro Lupori 	switch (ri) {
5668dd7182SLeandro Lupori 	case 0:
5768dd7182SLeandro Lupori 	case 1:
5868dd7182SLeandro Lupori 	case 2:
5968dd7182SLeandro Lupori 	case 3:
6068dd7182SLeandro Lupori 		mmcr = mfspr(SPR_MMCR1);
6168dd7182SLeandro Lupori 		mmcr &= ~SPR_MMCR1_P8_PMCNSEL_MASK(ri);
6268dd7182SLeandro Lupori 		mmcr |= SPR_MMCR1_P8_PMCNSEL(ri, config & ~POWERPC_PMC_ENABLE);
6368dd7182SLeandro Lupori 		mtspr(SPR_MMCR1, mmcr);
6468dd7182SLeandro Lupori 		break;
6568dd7182SLeandro Lupori 	}
6668dd7182SLeandro Lupori 
6768dd7182SLeandro Lupori 	/*
6868dd7182SLeandro Lupori 	 * By default, freeze counter in all states.
6968dd7182SLeandro Lupori 	 * If counter is being started, unfreeze it in selected states.
7068dd7182SLeandro Lupori 	 */
7168dd7182SLeandro Lupori 	mmcr = mfspr(SPR_MMCR2) | SPR_MMCR2_FCNHSP(ri);
7268dd7182SLeandro Lupori 	if (config != PMCN_NONE) {
7368dd7182SLeandro Lupori 		if (config & POWERPC_PMC_USER_ENABLE)
7468dd7182SLeandro Lupori 			mmcr &= ~(SPR_MMCR2_FCNP0(ri) |
7568dd7182SLeandro Lupori 			    SPR_MMCR2_FCNP1(ri));
7668dd7182SLeandro Lupori 		if (config & POWERPC_PMC_KERNEL_ENABLE)
7768dd7182SLeandro Lupori 			mmcr &= ~(SPR_MMCR2_FCNH(ri) |
7868dd7182SLeandro Lupori 			    SPR_MMCR2_FCNS(ri));
7968dd7182SLeandro Lupori 	}
8068dd7182SLeandro Lupori 	mtspr(SPR_MMCR2, mmcr);
8168dd7182SLeandro Lupori }
8268dd7182SLeandro Lupori 
8368dd7182SLeandro Lupori static int
power8_pcpu_init(struct pmc_mdep * md,int cpu)8468dd7182SLeandro Lupori power8_pcpu_init(struct pmc_mdep *md, int cpu)
8568dd7182SLeandro Lupori {
8668dd7182SLeandro Lupori 	register_t mmcr0;
8768dd7182SLeandro Lupori 	int i;
8868dd7182SLeandro Lupori 
8968dd7182SLeandro Lupori 	powerpc_pcpu_init(md, cpu);
9068dd7182SLeandro Lupori 
9168dd7182SLeandro Lupori 	/* Freeze all counters before modifying PMC registers */
9268dd7182SLeandro Lupori 	mmcr0 = mfspr(SPR_MMCR0) | SPR_MMCR0_FC;
9368dd7182SLeandro Lupori 	mtspr(SPR_MMCR0, mmcr0);
9468dd7182SLeandro Lupori 
9568dd7182SLeandro Lupori 	/*
9668dd7182SLeandro Lupori 	 * Now setup MMCR0:
9768dd7182SLeandro Lupori 	 *  - PMAO=0: clear alerts
9868dd7182SLeandro Lupori 	 *  - FCPC=0, FCP=0: don't freeze counters in problem state
9968dd7182SLeandro Lupori 	 *  - FCECE: Freeze Counters on Enabled Condition or Event
10068dd7182SLeandro Lupori 	 *  - PMC1CE/PMCNCE: PMC1/N Condition Enable
10168dd7182SLeandro Lupori 	 */
10268dd7182SLeandro Lupori 	mmcr0 &= ~(SPR_MMCR0_PMAO | SPR_MMCR0_FCPC | SPR_MMCR0_FCP);
10368dd7182SLeandro Lupori 	mmcr0 |= SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE;
10468dd7182SLeandro Lupori 	mtspr(SPR_MMCR0, mmcr0);
10568dd7182SLeandro Lupori 
10668dd7182SLeandro Lupori 	/* Clear all PMCs to prevent enabled condition interrupts */
10768dd7182SLeandro Lupori 	for (i = 0; i < POWER8_MAX_PMCS; i++)
10868dd7182SLeandro Lupori 		powerpc_pmcn_write(i, 0);
10968dd7182SLeandro Lupori 
11068dd7182SLeandro Lupori 	/* Disable events in PMCs 1-4 */
11168dd7182SLeandro Lupori 	mtspr(SPR_MMCR1, mfspr(SPR_MMCR1) & ~SPR_MMCR1_P8_PMCSEL_ALL);
11268dd7182SLeandro Lupori 
11368dd7182SLeandro Lupori 	/* Freeze each counter, in all states */
11468dd7182SLeandro Lupori 	mtspr(SPR_MMCR2, mfspr(SPR_MMCR2) |
11568dd7182SLeandro Lupori 	    SPR_MMCR2_FCNHSP(0) | SPR_MMCR2_FCNHSP(1) | SPR_MMCR2_FCNHSP(2) |
11668dd7182SLeandro Lupori 	    SPR_MMCR2_FCNHSP(3) | SPR_MMCR2_FCNHSP(4) | SPR_MMCR2_FCNHSP(5));
11768dd7182SLeandro Lupori 
11868dd7182SLeandro Lupori 	/* Enable interrupts, unset global freeze */
11968dd7182SLeandro Lupori 	mmcr0 &= ~SPR_MMCR0_FC;
12068dd7182SLeandro Lupori 	mmcr0 |= SPR_MMCR0_PMAE;
12168dd7182SLeandro Lupori 	mtspr(SPR_MMCR0, mmcr0);
12268dd7182SLeandro Lupori 	return (0);
12368dd7182SLeandro Lupori }
12468dd7182SLeandro Lupori 
12568dd7182SLeandro Lupori static int
power8_pcpu_fini(struct pmc_mdep * md,int cpu)12668dd7182SLeandro Lupori power8_pcpu_fini(struct pmc_mdep *md, int cpu)
12768dd7182SLeandro Lupori {
12868dd7182SLeandro Lupori 	register_t mmcr0;
12968dd7182SLeandro Lupori 
13068dd7182SLeandro Lupori 	/* Freeze counters, disable interrupts */
13168dd7182SLeandro Lupori 	mmcr0 = mfspr(SPR_MMCR0);
13268dd7182SLeandro Lupori 	mmcr0 &= ~SPR_MMCR0_PMAE;
13368dd7182SLeandro Lupori 	mmcr0 |= SPR_MMCR0_FC;
13468dd7182SLeandro Lupori 	mtspr(SPR_MMCR0, mmcr0);
13568dd7182SLeandro Lupori 
13668dd7182SLeandro Lupori 	return (powerpc_pcpu_fini(md, cpu));
13768dd7182SLeandro Lupori }
13868dd7182SLeandro Lupori 
13968dd7182SLeandro Lupori static void
power8_resume_pmc(bool ie)14068dd7182SLeandro Lupori power8_resume_pmc(bool ie)
14168dd7182SLeandro Lupori {
14268dd7182SLeandro Lupori 	register_t mmcr0;
14368dd7182SLeandro Lupori 
14468dd7182SLeandro Lupori 	/* Unfreeze counters and re-enable PERF exceptions if requested. */
14568dd7182SLeandro Lupori 	mmcr0 = mfspr(SPR_MMCR0);
14668dd7182SLeandro Lupori 	mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMAO | SPR_MMCR0_PMAE);
14768dd7182SLeandro Lupori 	if (ie)
14868dd7182SLeandro Lupori 		mmcr0 |= SPR_MMCR0_PMAE;
14968dd7182SLeandro Lupori 	mtspr(SPR_MMCR0, mmcr0);
15068dd7182SLeandro Lupori }
15168dd7182SLeandro Lupori 
152b48a2770SLeandro Lupori static int
power8_allocate_pmc(int cpu,int ri,struct pmc * pm,const struct pmc_op_pmcallocate * a)153b48a2770SLeandro Lupori power8_allocate_pmc(int cpu, int ri, struct pmc *pm,
154b48a2770SLeandro Lupori 	const struct pmc_op_pmcallocate *a)
155b48a2770SLeandro Lupori {
156b48a2770SLeandro Lupori 	uint32_t caps, config, counter, pe;
157b48a2770SLeandro Lupori 
158b48a2770SLeandro Lupori 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
159b48a2770SLeandro Lupori 	    ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu));
160b48a2770SLeandro Lupori 	KASSERT(ri >= 0 && ri < ppc_max_pmcs,
161b48a2770SLeandro Lupori 	    ("[powerpc,%d] illegal row index %d", __LINE__, ri));
162b48a2770SLeandro Lupori 
163b48a2770SLeandro Lupori 	pe = a->pm_md.pm_event;
164b48a2770SLeandro Lupori 	counter = PM_EVENT_COUNTER(pe);
165b48a2770SLeandro Lupori 	config = PM_EVENT_CODE(pe);
166b48a2770SLeandro Lupori 
167315cd55dSMitchell Horne 	if (a->pm_class != PMC_CLASS_POWER8)
168315cd55dSMitchell Horne 		return (EINVAL);
169315cd55dSMitchell Horne 
170*c190fb35SMitchell Horne 	if ((a->pm_flags & PMC_F_EV_PMU) == 0)
171*c190fb35SMitchell Horne 		return (EINVAL);
172*c190fb35SMitchell Horne 
173b48a2770SLeandro Lupori 	/*
174b48a2770SLeandro Lupori 	 * PMC5 and PMC6 are not programmable and always count instructions
175b48a2770SLeandro Lupori 	 * completed and cycles, respectively.
176b48a2770SLeandro Lupori 	 *
177b48a2770SLeandro Lupori 	 * When counter is 0 any of the 4 programmable PMCs may be used for
178b48a2770SLeandro Lupori 	 * the specified event, otherwise it must match ri + 1.
179b48a2770SLeandro Lupori 	 */
180b48a2770SLeandro Lupori 	if (counter == 0 && config == PM_INST_CMPL)
181b48a2770SLeandro Lupori 		counter = 5;
182b48a2770SLeandro Lupori 	else if (counter == 0 && config == PM_CYC)
183b48a2770SLeandro Lupori 		counter = 6;
184b48a2770SLeandro Lupori 	else if (counter > 4)
185b48a2770SLeandro Lupori 		return (EINVAL);
186b48a2770SLeandro Lupori 
187b48a2770SLeandro Lupori 	if (counter != 0 && counter != ri + 1)
188b48a2770SLeandro Lupori 		return (EINVAL);
189b48a2770SLeandro Lupori 
190b48a2770SLeandro Lupori 	caps = a->pm_caps;
191b48a2770SLeandro Lupori 
192b48a2770SLeandro Lupori 	if (caps & PMC_CAP_SYSTEM)
193b48a2770SLeandro Lupori 		config |= POWERPC_PMC_KERNEL_ENABLE;
194b48a2770SLeandro Lupori 	if (caps & PMC_CAP_USER)
195b48a2770SLeandro Lupori 		config |= POWERPC_PMC_USER_ENABLE;
196b48a2770SLeandro Lupori 	if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0)
197b48a2770SLeandro Lupori 		config |= POWERPC_PMC_ENABLE;
198b48a2770SLeandro Lupori 
199b48a2770SLeandro Lupori 	pm->pm_md.pm_powerpc.pm_powerpc_evsel = config;
200b48a2770SLeandro Lupori 
201b48a2770SLeandro Lupori 	PMCDBG3(MDP,ALL,1,"powerpc-allocate cpu=%d ri=%d -> config=0x%x",
202b48a2770SLeandro Lupori 	    cpu, ri, config);
203b48a2770SLeandro Lupori 	return (0);
204b48a2770SLeandro Lupori }
205b48a2770SLeandro Lupori 
20668dd7182SLeandro Lupori int
pmc_power8_initialize(struct pmc_mdep * pmc_mdep)20768dd7182SLeandro Lupori pmc_power8_initialize(struct pmc_mdep *pmc_mdep)
20868dd7182SLeandro Lupori {
20968dd7182SLeandro Lupori 	struct pmc_classdep *pcd;
21068dd7182SLeandro Lupori 
21168dd7182SLeandro Lupori 	pmc_mdep->pmd_cputype = PMC_CPU_PPC_POWER8;
21268dd7182SLeandro Lupori 
21368dd7182SLeandro Lupori 	pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC];
21468dd7182SLeandro Lupori 	pcd->pcd_caps  = POWERPC_PMC_CAPS;
21568dd7182SLeandro Lupori 	pcd->pcd_class = PMC_CLASS_POWER8;
21668dd7182SLeandro Lupori 	pcd->pcd_num   = POWER8_MAX_PMCS;
21768dd7182SLeandro Lupori 	pcd->pcd_ri    = pmc_mdep->pmd_npmc;
21868dd7182SLeandro Lupori 	pcd->pcd_width = 32;
21968dd7182SLeandro Lupori 
22068dd7182SLeandro Lupori 	pcd->pcd_pcpu_init      = power8_pcpu_init;
22168dd7182SLeandro Lupori 	pcd->pcd_pcpu_fini      = power8_pcpu_fini;
222b48a2770SLeandro Lupori 	pcd->pcd_allocate_pmc   = power8_allocate_pmc;
22368dd7182SLeandro Lupori 	pcd->pcd_release_pmc    = powerpc_release_pmc;
22468dd7182SLeandro Lupori 	pcd->pcd_start_pmc      = powerpc_start_pmc;
22568dd7182SLeandro Lupori 	pcd->pcd_stop_pmc       = powerpc_stop_pmc;
22668dd7182SLeandro Lupori 	pcd->pcd_get_config     = powerpc_get_config;
22768dd7182SLeandro Lupori 	pcd->pcd_config_pmc     = powerpc_config_pmc;
22868dd7182SLeandro Lupori 	pcd->pcd_describe       = powerpc_describe;
22968dd7182SLeandro Lupori 	pcd->pcd_read_pmc       = powerpc_read_pmc;
23068dd7182SLeandro Lupori 	pcd->pcd_write_pmc      = powerpc_write_pmc;
23168dd7182SLeandro Lupori 
23268dd7182SLeandro Lupori 	pmc_mdep->pmd_npmc     += POWER8_MAX_PMCS;
23368dd7182SLeandro Lupori 	pmc_mdep->pmd_intr      = powerpc_pmc_intr;
23468dd7182SLeandro Lupori 
23568dd7182SLeandro Lupori 	ppc_max_pmcs = POWER8_MAX_PMCS;
23668dd7182SLeandro Lupori 
23768dd7182SLeandro Lupori 	powerpc_set_pmc = power8_set_pmc;
23868dd7182SLeandro Lupori 	powerpc_pmcn_read = powerpc_pmcn_read_default;
23968dd7182SLeandro Lupori 	powerpc_pmcn_write = powerpc_pmcn_write_default;
24068dd7182SLeandro Lupori 	powerpc_resume_pmc = power8_resume_pmc;
24168dd7182SLeandro Lupori 
24268dd7182SLeandro Lupori 	return (0);
24368dd7182SLeandro Lupori }
244