1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
31#include <sys/pmc.h>
32#include <sys/pmckern.h>
33#include <sys/systm.h>
34
35#include <machine/pmc_mdep.h>
36#include <machine/spr.h>
37#include <machine/cpu.h>
38
39#include "hwpmc_powerpc.h"
40
41#define	POWER8_MAX_PMCS		6
42
43#define PM_EVENT_CODE(pe)	(pe & 0xffff)
44#define PM_EVENT_COUNTER(pe)	((pe >> 16) & 0xffff)
45
46#define PM_CYC			0x1e
47#define PM_INST_CMPL		0x02
48
49static void
50power8_set_pmc(int cpu, int ri, int config)
51{
52	register_t mmcr;
53
54	/* Select event */
55	switch (ri) {
56	case 0:
57	case 1:
58	case 2:
59	case 3:
60		mmcr = mfspr(SPR_MMCR1);
61		mmcr &= ~SPR_MMCR1_P8_PMCNSEL_MASK(ri);
62		mmcr |= SPR_MMCR1_P8_PMCNSEL(ri, config & ~POWERPC_PMC_ENABLE);
63		mtspr(SPR_MMCR1, mmcr);
64		break;
65	}
66
67	/*
68	 * By default, freeze counter in all states.
69	 * If counter is being started, unfreeze it in selected states.
70	 */
71	mmcr = mfspr(SPR_MMCR2) | SPR_MMCR2_FCNHSP(ri);
72	if (config != PMCN_NONE) {
73		if (config & POWERPC_PMC_USER_ENABLE)
74			mmcr &= ~(SPR_MMCR2_FCNP0(ri) |
75			    SPR_MMCR2_FCNP1(ri));
76		if (config & POWERPC_PMC_KERNEL_ENABLE)
77			mmcr &= ~(SPR_MMCR2_FCNH(ri) |
78			    SPR_MMCR2_FCNS(ri));
79	}
80	mtspr(SPR_MMCR2, mmcr);
81}
82
83static int
84power8_pcpu_init(struct pmc_mdep *md, int cpu)
85{
86	register_t mmcr0;
87	int i;
88
89	powerpc_pcpu_init(md, cpu);
90
91	/* Freeze all counters before modifying PMC registers */
92	mmcr0 = mfspr(SPR_MMCR0) | SPR_MMCR0_FC;
93	mtspr(SPR_MMCR0, mmcr0);
94
95	/*
96	 * Now setup MMCR0:
97	 *  - PMAO=0: clear alerts
98	 *  - FCPC=0, FCP=0: don't freeze counters in problem state
99	 *  - FCECE: Freeze Counters on Enabled Condition or Event
100	 *  - PMC1CE/PMCNCE: PMC1/N Condition Enable
101	 */
102	mmcr0 &= ~(SPR_MMCR0_PMAO | SPR_MMCR0_FCPC | SPR_MMCR0_FCP);
103	mmcr0 |= SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE;
104	mtspr(SPR_MMCR0, mmcr0);
105
106	/* Clear all PMCs to prevent enabled condition interrupts */
107	for (i = 0; i < POWER8_MAX_PMCS; i++)
108		powerpc_pmcn_write(i, 0);
109
110	/* Disable events in PMCs 1-4 */
111	mtspr(SPR_MMCR1, mfspr(SPR_MMCR1) & ~SPR_MMCR1_P8_PMCSEL_ALL);
112
113	/* Freeze each counter, in all states */
114	mtspr(SPR_MMCR2, mfspr(SPR_MMCR2) |
115	    SPR_MMCR2_FCNHSP(0) | SPR_MMCR2_FCNHSP(1) | SPR_MMCR2_FCNHSP(2) |
116	    SPR_MMCR2_FCNHSP(3) | SPR_MMCR2_FCNHSP(4) | SPR_MMCR2_FCNHSP(5));
117
118	/* Enable interrupts, unset global freeze */
119	mmcr0 &= ~SPR_MMCR0_FC;
120	mmcr0 |= SPR_MMCR0_PMAE;
121	mtspr(SPR_MMCR0, mmcr0);
122	return (0);
123}
124
125static int
126power8_pcpu_fini(struct pmc_mdep *md, int cpu)
127{
128	register_t mmcr0;
129
130	/* Freeze counters, disable interrupts */
131	mmcr0 = mfspr(SPR_MMCR0);
132	mmcr0 &= ~SPR_MMCR0_PMAE;
133	mmcr0 |= SPR_MMCR0_FC;
134	mtspr(SPR_MMCR0, mmcr0);
135
136	return (powerpc_pcpu_fini(md, cpu));
137}
138
139static void
140power8_resume_pmc(bool ie)
141{
142	register_t mmcr0;
143
144	/* Unfreeze counters and re-enable PERF exceptions if requested. */
145	mmcr0 = mfspr(SPR_MMCR0);
146	mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMAO | SPR_MMCR0_PMAE);
147	if (ie)
148		mmcr0 |= SPR_MMCR0_PMAE;
149	mtspr(SPR_MMCR0, mmcr0);
150}
151
152static int
153power8_allocate_pmc(int cpu, int ri, struct pmc *pm,
154	const struct pmc_op_pmcallocate *a)
155{
156	uint32_t caps, config, counter, pe;
157
158	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
159	    ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu));
160	KASSERT(ri >= 0 && ri < ppc_max_pmcs,
161	    ("[powerpc,%d] illegal row index %d", __LINE__, ri));
162
163	pe = a->pm_md.pm_event;
164	counter = PM_EVENT_COUNTER(pe);
165	config = PM_EVENT_CODE(pe);
166
167	if (a->pm_class != PMC_CLASS_POWER8)
168		return (EINVAL);
169
170	if ((a->pm_flags & PMC_F_EV_PMU) == 0)
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
206int
207pmc_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