xref: /freebsd/sys/dev/hwpmc/hwpmc_power8.c (revision 9768746b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 Justin Hibbits
5  * Copyright (c) 2020 Leandro Lupori
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/pmc.h>
35 #include <sys/pmckern.h>
36 #include <sys/systm.h>
37 
38 #include <machine/pmc_mdep.h>
39 #include <machine/spr.h>
40 #include <machine/cpu.h>
41 
42 #include "hwpmc_powerpc.h"
43 
44 #define	POWER8_MAX_PMCS		6
45 
46 #define PM_EVENT_CODE(pe)	(pe & 0xffff)
47 #define PM_EVENT_COUNTER(pe)	((pe >> 16) & 0xffff)
48 
49 #define PM_CYC			0x1e
50 #define PM_INST_CMPL		0x02
51 
52 static void
53 power8_set_pmc(int cpu, int ri, int config)
54 {
55 	register_t mmcr;
56 
57 	/* Select event */
58 	switch (ri) {
59 	case 0:
60 	case 1:
61 	case 2:
62 	case 3:
63 		mmcr = mfspr(SPR_MMCR1);
64 		mmcr &= ~SPR_MMCR1_P8_PMCNSEL_MASK(ri);
65 		mmcr |= SPR_MMCR1_P8_PMCNSEL(ri, config & ~POWERPC_PMC_ENABLE);
66 		mtspr(SPR_MMCR1, mmcr);
67 		break;
68 	}
69 
70 	/*
71 	 * By default, freeze counter in all states.
72 	 * If counter is being started, unfreeze it in selected states.
73 	 */
74 	mmcr = mfspr(SPR_MMCR2) | SPR_MMCR2_FCNHSP(ri);
75 	if (config != PMCN_NONE) {
76 		if (config & POWERPC_PMC_USER_ENABLE)
77 			mmcr &= ~(SPR_MMCR2_FCNP0(ri) |
78 			    SPR_MMCR2_FCNP1(ri));
79 		if (config & POWERPC_PMC_KERNEL_ENABLE)
80 			mmcr &= ~(SPR_MMCR2_FCNH(ri) |
81 			    SPR_MMCR2_FCNS(ri));
82 	}
83 	mtspr(SPR_MMCR2, mmcr);
84 }
85 
86 static int
87 power8_pcpu_init(struct pmc_mdep *md, int cpu)
88 {
89 	register_t mmcr0;
90 	int i;
91 
92 	powerpc_pcpu_init(md, cpu);
93 
94 	/* Freeze all counters before modifying PMC registers */
95 	mmcr0 = mfspr(SPR_MMCR0) | SPR_MMCR0_FC;
96 	mtspr(SPR_MMCR0, mmcr0);
97 
98 	/*
99 	 * Now setup MMCR0:
100 	 *  - PMAO=0: clear alerts
101 	 *  - FCPC=0, FCP=0: don't freeze counters in problem state
102 	 *  - FCECE: Freeze Counters on Enabled Condition or Event
103 	 *  - PMC1CE/PMCNCE: PMC1/N Condition Enable
104 	 */
105 	mmcr0 &= ~(SPR_MMCR0_PMAO | SPR_MMCR0_FCPC | SPR_MMCR0_FCP);
106 	mmcr0 |= SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE;
107 	mtspr(SPR_MMCR0, mmcr0);
108 
109 	/* Clear all PMCs to prevent enabled condition interrupts */
110 	for (i = 0; i < POWER8_MAX_PMCS; i++)
111 		powerpc_pmcn_write(i, 0);
112 
113 	/* Disable events in PMCs 1-4 */
114 	mtspr(SPR_MMCR1, mfspr(SPR_MMCR1) & ~SPR_MMCR1_P8_PMCSEL_ALL);
115 
116 	/* Freeze each counter, in all states */
117 	mtspr(SPR_MMCR2, mfspr(SPR_MMCR2) |
118 	    SPR_MMCR2_FCNHSP(0) | SPR_MMCR2_FCNHSP(1) | SPR_MMCR2_FCNHSP(2) |
119 	    SPR_MMCR2_FCNHSP(3) | SPR_MMCR2_FCNHSP(4) | SPR_MMCR2_FCNHSP(5));
120 
121 	/* Enable interrupts, unset global freeze */
122 	mmcr0 &= ~SPR_MMCR0_FC;
123 	mmcr0 |= SPR_MMCR0_PMAE;
124 	mtspr(SPR_MMCR0, mmcr0);
125 	return (0);
126 }
127 
128 static int
129 power8_pcpu_fini(struct pmc_mdep *md, int cpu)
130 {
131 	register_t mmcr0;
132 
133 	/* Freeze counters, disable interrupts */
134 	mmcr0 = mfspr(SPR_MMCR0);
135 	mmcr0 &= ~SPR_MMCR0_PMAE;
136 	mmcr0 |= SPR_MMCR0_FC;
137 	mtspr(SPR_MMCR0, mmcr0);
138 
139 	return (powerpc_pcpu_fini(md, cpu));
140 }
141 
142 static void
143 power8_resume_pmc(bool ie)
144 {
145 	register_t mmcr0;
146 
147 	/* Unfreeze counters and re-enable PERF exceptions if requested. */
148 	mmcr0 = mfspr(SPR_MMCR0);
149 	mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMAO | SPR_MMCR0_PMAE);
150 	if (ie)
151 		mmcr0 |= SPR_MMCR0_PMAE;
152 	mtspr(SPR_MMCR0, mmcr0);
153 }
154 
155 static int
156 power8_allocate_pmc(int cpu, int ri, struct pmc *pm,
157 	const struct pmc_op_pmcallocate *a)
158 {
159 	uint32_t caps, config, counter, pe;
160 
161 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
162 	    ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu));
163 	KASSERT(ri >= 0 && ri < ppc_max_pmcs,
164 	    ("[powerpc,%d] illegal row index %d", __LINE__, ri));
165 
166 	pe = a->pm_md.pm_event;
167 	counter = PM_EVENT_COUNTER(pe);
168 	config = PM_EVENT_CODE(pe);
169 
170 	if (a->pm_class != PMC_CLASS_POWER8)
171 		return (EINVAL);
172 
173 	/*
174 	 * PMC5 and PMC6 are not programmable and always count instructions
175 	 * completed and cycles, respectively.
176 	 *
177 	 * When counter is 0 any of the 4 programmable PMCs may be used for
178 	 * the specified event, otherwise it must match ri + 1.
179 	 */
180 	if (counter == 0 && config == PM_INST_CMPL)
181 		counter = 5;
182 	else if (counter == 0 && config == PM_CYC)
183 		counter = 6;
184 	else if (counter > 4)
185 		return (EINVAL);
186 
187 	if (counter != 0 && counter != ri + 1)
188 		return (EINVAL);
189 
190 	caps = a->pm_caps;
191 
192 	if (caps & PMC_CAP_SYSTEM)
193 		config |= POWERPC_PMC_KERNEL_ENABLE;
194 	if (caps & PMC_CAP_USER)
195 		config |= POWERPC_PMC_USER_ENABLE;
196 	if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0)
197 		config |= POWERPC_PMC_ENABLE;
198 
199 	pm->pm_md.pm_powerpc.pm_powerpc_evsel = config;
200 
201 	PMCDBG3(MDP,ALL,1,"powerpc-allocate cpu=%d ri=%d -> config=0x%x",
202 	    cpu, ri, config);
203 	return (0);
204 }
205 
206 int
207 pmc_power8_initialize(struct pmc_mdep *pmc_mdep)
208 {
209 	struct pmc_classdep *pcd;
210 
211 	pmc_mdep->pmd_cputype = PMC_CPU_PPC_POWER8;
212 
213 	pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC];
214 	pcd->pcd_caps  = POWERPC_PMC_CAPS;
215 	pcd->pcd_class = PMC_CLASS_POWER8;
216 	pcd->pcd_num   = POWER8_MAX_PMCS;
217 	pcd->pcd_ri    = pmc_mdep->pmd_npmc;
218 	pcd->pcd_width = 32;
219 
220 	pcd->pcd_pcpu_init      = power8_pcpu_init;
221 	pcd->pcd_pcpu_fini      = power8_pcpu_fini;
222 	pcd->pcd_allocate_pmc   = power8_allocate_pmc;
223 	pcd->pcd_release_pmc    = powerpc_release_pmc;
224 	pcd->pcd_start_pmc      = powerpc_start_pmc;
225 	pcd->pcd_stop_pmc       = powerpc_stop_pmc;
226 	pcd->pcd_get_config     = powerpc_get_config;
227 	pcd->pcd_config_pmc     = powerpc_config_pmc;
228 	pcd->pcd_describe       = powerpc_describe;
229 	pcd->pcd_read_pmc       = powerpc_read_pmc;
230 	pcd->pcd_write_pmc      = powerpc_write_pmc;
231 
232 	pmc_mdep->pmd_npmc     += POWER8_MAX_PMCS;
233 	pmc_mdep->pmd_intr      = powerpc_pmc_intr;
234 
235 	ppc_max_pmcs = POWER8_MAX_PMCS;
236 
237 	powerpc_set_pmc = power8_set_pmc;
238 	powerpc_pmcn_read = powerpc_pmcn_read_default;
239 	powerpc_pmcn_write = powerpc_pmcn_write_default;
240 	powerpc_resume_pmc = power8_resume_pmc;
241 
242 	return (0);
243 }
244