xref: /freebsd/sys/dev/hwpmc/hwpmc_cmn600.c (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2003-2008 Joseph Koshy
5  * Copyright (c) 2007 The FreeBSD Foundation
6  * Copyright (c) 2021-2022 ARM Ltd
7  *
8  * Portions of this software were developed by A. Joseph Koshy under
9  * sponsorship from the FreeBSD Foundation and Google, Inc.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /* Arm CoreLink CMN-600 Coherent Mesh Network PMU Driver */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include "opt_acpi.h"
39 
40 /*
41  * This depends on ACPI, but is built unconditionally in the hwpmc module.
42  */
43 #ifdef DEV_ACPI
44 #include <sys/param.h>
45 #include <sys/lock.h>
46 #include <sys/malloc.h>
47 #include <sys/module.h>
48 #include <sys/mutex.h>
49 #include <sys/pmc.h>
50 #include <sys/pmckern.h>
51 #include <sys/systm.h>
52 
53 #include <machine/cmn600_reg.h>
54 
55 struct cmn600_descr {
56 	struct pmc_descr pd_descr;  /* "base class" */
57 	void		*pd_rw_arg; /* Argument to use with read/write */
58 	struct pmc	*pd_pmc;
59 	struct pmc_hw	*pd_phw;
60 	uint32_t	 pd_nodeid;
61 	int32_t		 pd_node_type;
62 	int		 pd_local_counter;
63 
64 };
65 
66 static struct cmn600_descr **cmn600_pmcdesc;
67 
68 static struct cmn600_pmc cmn600_pmcs[CMN600_UNIT_MAX];
69 static int cmn600_units = 0;
70 
71 static inline struct cmn600_descr *
72 cmn600desc(int ri)
73 {
74 
75 	return (cmn600_pmcdesc[ri]);
76 }
77 
78 static inline int
79 class_ri2unit(int ri)
80 {
81 
82 	return (ri / CMN600_COUNTERS_N);
83 }
84 
85 #define	EVENCNTR(x)	(((x) >> POR_DT_PMEVCNT_EVENCNT_SHIFT) << \
86     POR_DTM_PMEVCNT_CNTR_WIDTH)
87 #define	ODDCNTR(x)	(((x) >> POR_DT_PMEVCNT_ODDCNT_SHIFT) << \
88     POR_DTM_PMEVCNT_CNTR_WIDTH)
89 
90 static uint64_t
91 cmn600_pmu_readcntr(void *arg, u_int nodeid, u_int xpcntr, u_int dtccntr,
92     u_int width)
93 {
94 	uint64_t dtcval, xpval;
95 
96 	KASSERT(xpcntr < 4, ("[cmn600,%d] XP counter number %d is too big."
97 	    " Max: 3", __LINE__, xpcntr));
98 	KASSERT(dtccntr < 8, ("[cmn600,%d] Global counter number %d is too"
99 	    " big. Max: 7", __LINE__, dtccntr));
100 
101 	dtcval = pmu_cmn600_rd8(arg, nodeid, NODE_TYPE_DTC,
102 	    POR_DT_PMEVCNT(dtccntr >> 1));
103 	if (width == 4) {
104 		dtcval = (dtccntr & 1) ? ODDCNTR(dtcval) : EVENCNTR(dtcval);
105 		dtcval &= 0xffffffff0000UL;
106 	} else
107 		dtcval <<= POR_DTM_PMEVCNT_CNTR_WIDTH;
108 
109 	xpval = pmu_cmn600_rd8(arg, nodeid, NODE_TYPE_XP, POR_DTM_PMEVCNT);
110 	xpval >>= xpcntr * POR_DTM_PMEVCNT_CNTR_WIDTH;
111 	xpval &= 0xffffUL;
112 	return (dtcval | xpval);
113 }
114 
115 static void
116 cmn600_pmu_writecntr(void *arg, u_int nodeid, u_int xpcntr, u_int dtccntr,
117     u_int width, uint64_t val)
118 {
119 	int shift;
120 
121 	KASSERT(xpcntr < 4, ("[cmn600,%d] XP counter number %d is too big."
122 	    " Max: 3", __LINE__, xpcntr));
123 	KASSERT(dtccntr < 8, ("[cmn600,%d] Global counter number %d is too"
124 	    " big. Max: 7", __LINE__, dtccntr));
125 
126 	if (width == 4) {
127 		shift = (dtccntr & 1) ? POR_DT_PMEVCNT_ODDCNT_SHIFT :
128 		    POR_DT_PMEVCNT_EVENCNT_SHIFT;
129 		pmu_cmn600_md8(arg, nodeid, NODE_TYPE_DTC,
130 		    POR_DT_PMEVCNT(dtccntr >> 1), 0xffffffffUL << shift,
131 		    ((val >> POR_DTM_PMEVCNT_CNTR_WIDTH) & 0xffffffff) << shift);
132 	} else
133 		pmu_cmn600_wr8(arg, nodeid, NODE_TYPE_DTC,
134 		    POR_DT_PMEVCNT(dtccntr & ~0x1), val >>
135 		    POR_DTM_PMEVCNT_CNTR_WIDTH);
136 
137 	shift = xpcntr * POR_DTM_PMEVCNT_CNTR_WIDTH;
138 	val &= 0xffffUL;
139 	pmu_cmn600_md8(arg, nodeid, NODE_TYPE_XP, POR_DTM_PMEVCNT,
140 	    0xffffUL << shift, val << shift);
141 }
142 
143 #undef	EVENCNTR
144 #undef	ODDCNTR
145 
146 /*
147  * read a pmc register
148  */
149 static int
150 cmn600_read_pmc(int cpu, int ri, pmc_value_t *v)
151 {
152 	int counter, local_counter, nodeid;
153 	struct cmn600_descr *desc;
154 	struct pmc *pm;
155 	void *arg;
156 
157 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
158 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
159 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
160 	    ri));
161 
162 	counter = ri % CMN600_COUNTERS_N;
163 	desc = cmn600desc(ri);
164 	pm = desc->pd_phw->phw_pmc;
165 	arg = desc->pd_rw_arg;
166 	nodeid = pm->pm_md.pm_cmn600.pm_cmn600_nodeid;
167 	local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter;
168 
169 	KASSERT(pm != NULL,
170 	    ("[cmn600,%d] No owner for HWPMC [cpu%d,pmc%d]", __LINE__,
171 		cpu, ri));
172 
173 	*v = cmn600_pmu_readcntr(arg, nodeid, local_counter, counter, 4);
174 	PMCDBG3(MDP, REA, 2, "%s id=%d -> %jd", __func__, ri, *v);
175 
176 	return (0);
177 }
178 
179 /*
180  * Write a pmc register.
181  */
182 static int
183 cmn600_write_pmc(int cpu, int ri, pmc_value_t v)
184 {
185 	int counter, local_counter, nodeid;
186 	struct cmn600_descr *desc;
187 	struct pmc *pm;
188 	void *arg;
189 
190 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
191 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
192 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
193 	    ri));
194 
195 	counter = ri % CMN600_COUNTERS_N;
196 	desc = cmn600desc(ri);
197 	pm = desc->pd_phw->phw_pmc;
198 	arg = desc->pd_rw_arg;
199 	nodeid = pm->pm_md.pm_cmn600.pm_cmn600_nodeid;
200 	local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter;
201 
202 	KASSERT(pm != NULL,
203 	    ("[cmn600,%d] PMC not owned (cpu%d,pmc%d)", __LINE__,
204 		cpu, ri));
205 
206 	PMCDBG4(MDP, WRI, 1, "%s cpu=%d ri=%d v=%jx", __func__, cpu, ri, v);
207 
208 	cmn600_pmu_writecntr(arg, nodeid, local_counter, counter, 4, v);
209 	return (0);
210 }
211 
212 /*
213  * configure hardware pmc according to the configuration recorded in
214  * pmc 'pm'.
215  */
216 static int
217 cmn600_config_pmc(int cpu, int ri, struct pmc *pm)
218 {
219 	struct pmc_hw *phw;
220 
221 	PMCDBG4(MDP, CFG, 1, "%s cpu=%d ri=%d pm=%p", __func__, cpu, ri, pm);
222 
223 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
224 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
225 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
226 	    ri));
227 
228 	phw = cmn600desc(ri)->pd_phw;
229 
230 	KASSERT(pm == NULL || phw->phw_pmc == NULL,
231 	    ("[cmn600,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
232 		__LINE__, pm, phw->phw_pmc));
233 
234 	phw->phw_pmc = pm;
235 	return (0);
236 }
237 
238 /*
239  * Retrieve a configured PMC pointer from hardware state.
240  */
241 static int
242 cmn600_get_config(int cpu, int ri, struct pmc **ppm)
243 {
244 
245 	*ppm = cmn600desc(ri)->pd_phw->phw_pmc;
246 
247 	return (0);
248 }
249 
250 #define	CASE_DN_VER_EVT(n, id) case PMC_EV_CMN600_PMU_ ## n: { *event = id; \
251 	return (0); }
252 static int
253 cmn600_map_ev2event(int ev, int rev, int *node_type, uint8_t *event)
254 {
255 	if (ev < PMC_EV_CMN600_PMU_dn_rxreq_dvmop ||
256 	    ev > PMC_EV_CMN600_PMU_rni_rdb_ord)
257 		return (EINVAL);
258 	if (ev <= PMC_EV_CMN600_PMU_dn_rxreq_trk_full) {
259 		*node_type = NODE_TYPE_DVM;
260 		if (rev < 0x200) {
261 			switch (ev) {
262 			CASE_DN_VER_EVT(dn_rxreq_dvmop, 1);
263 			CASE_DN_VER_EVT(dn_rxreq_dvmsync, 2);
264 			CASE_DN_VER_EVT(dn_rxreq_dvmop_vmid_filtered, 3);
265 			CASE_DN_VER_EVT(dn_rxreq_retried, 4);
266 			CASE_DN_VER_EVT(dn_rxreq_trk_occupancy, 5);
267 			}
268 		} else {
269 			switch (ev) {
270 			CASE_DN_VER_EVT(dn_rxreq_tlbi_dvmop, 0x01);
271 			CASE_DN_VER_EVT(dn_rxreq_bpi_dvmop, 0x02);
272 			CASE_DN_VER_EVT(dn_rxreq_pici_dvmop, 0x03);
273 			CASE_DN_VER_EVT(dn_rxreq_vivi_dvmop, 0x04);
274 			CASE_DN_VER_EVT(dn_rxreq_dvmsync, 0x05);
275 			CASE_DN_VER_EVT(dn_rxreq_dvmop_vmid_filtered, 0x06);
276 			CASE_DN_VER_EVT(dn_rxreq_dvmop_other_filtered, 0x07);
277 			CASE_DN_VER_EVT(dn_rxreq_retried, 0x08);
278 			CASE_DN_VER_EVT(dn_rxreq_snp_sent, 0x09);
279 			CASE_DN_VER_EVT(dn_rxreq_snp_stalled, 0x0a);
280 			CASE_DN_VER_EVT(dn_rxreq_trk_full, 0x0b);
281 			CASE_DN_VER_EVT(dn_rxreq_trk_occupancy, 0x0c);
282 			}
283 		}
284 		return (EINVAL);
285 	} else if (ev <= PMC_EV_CMN600_PMU_hnf_snp_fwded) {
286 		*node_type = NODE_TYPE_HN_F;
287 		*event = ev - PMC_EV_CMN600_PMU_hnf_cache_miss;
288 		return (0);
289 	} else if (ev <= PMC_EV_CMN600_PMU_hni_pcie_serialization) {
290 		*node_type = NODE_TYPE_HN_I;
291 		*event = ev - PMC_EV_CMN600_PMU_hni_rrt_rd_occ_cnt_ovfl;
292 		return (0);
293 	} else if (ev <= PMC_EV_CMN600_PMU_xp_partial_dat_flit) {
294 		*node_type = NODE_TYPE_XP;
295 		*event = ev - PMC_EV_CMN600_PMU_xp_txflit_valid;
296 		return (0);
297 	} else if (ev <= PMC_EV_CMN600_PMU_sbsx_txrsp_stall) {
298 		*node_type = NODE_TYPE_SBSX;
299 		*event = ev - PMC_EV_CMN600_PMU_sbsx_rd_req;
300 		return (0);
301 	} else if (ev <= PMC_EV_CMN600_PMU_rnd_rdb_ord) {
302 		*node_type = NODE_TYPE_RN_D;
303 		*event = ev - PMC_EV_CMN600_PMU_rnd_s0_rdata_beats;
304 		return (0);
305 	} else if (ev <= PMC_EV_CMN600_PMU_rni_rdb_ord) {
306 		*node_type = NODE_TYPE_RN_I;
307 		*event = ev - PMC_EV_CMN600_PMU_rni_s0_rdata_beats;
308 		return (0);
309 	} else if (ev <= PMC_EV_CMN600_PMU_cxha_snphaz_occ) {
310 		*node_type = NODE_TYPE_CXHA;
311 		*event = ev - PMC_EV_CMN600_PMU_cxha_rddatbyp;
312 		return (0);
313 	} else if (ev <= PMC_EV_CMN600_PMU_cxra_ext_dat_stall) {
314 		*node_type = NODE_TYPE_CXRA;
315 		*event = ev - PMC_EV_CMN600_PMU_cxra_req_trk_occ;
316 		return (0);
317 	} else if (ev <= PMC_EV_CMN600_PMU_cxla_avg_latency_form_tx_tlp) {
318 		*node_type = NODE_TYPE_CXLA;
319 		*event = ev - PMC_EV_CMN600_PMU_cxla_rx_tlp_link0;
320 		return (0);
321 	}
322 	return (EINVAL);
323 }
324 
325 /*
326  * Check if a given allocation is feasible.
327  */
328 
329 static int
330 cmn600_allocate_pmc(int cpu, int ri, struct pmc *pm,
331     const struct pmc_op_pmcallocate *a)
332 {
333 	struct cmn600_descr *desc;
334 	const struct pmc_descr *pd;
335 	uint64_t caps __unused;
336 	int local_counter, node_type;
337 	enum pmc_event pe;
338 	void *arg;
339 	uint8_t e;
340 	int err;
341 
342 	(void) cpu;
343 
344 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
345 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
346 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
347 	    ri));
348 
349 	desc = cmn600desc(ri);
350 	arg = desc->pd_rw_arg;
351 	pd = &desc->pd_descr;
352 	if (cmn600_pmcs[class_ri2unit(ri)].domain != pcpu_find(cpu)->pc_domain)
353 		return (EINVAL);
354 
355 	/* check class match */
356 	if (pd->pd_class != a->pm_class)
357 		return (EINVAL);
358 
359 	caps = pm->pm_caps;
360 
361 	PMCDBG3(MDP, ALL, 1, "%s ri=%d caps=0x%x", __func__, ri, caps);
362 
363 	pe = a->pm_ev;
364 	err = cmn600_map_ev2event(pe, pmu_cmn600_rev(arg), &node_type, &e);
365 	if (err != 0)
366 		return (err);
367 	err = pmu_cmn600_alloc_localpmc(arg,
368 	    a->pm_md.pm_cmn600.pma_cmn600_nodeid, node_type, &local_counter);
369 	if (err != 0)
370 		return (err);
371 
372 	pm->pm_md.pm_cmn600.pm_cmn600_config =
373 	    a->pm_md.pm_cmn600.pma_cmn600_config;
374 	pm->pm_md.pm_cmn600.pm_cmn600_occupancy =
375 	    a->pm_md.pm_cmn600.pma_cmn600_occupancy;
376 	desc->pd_nodeid = pm->pm_md.pm_cmn600.pm_cmn600_nodeid =
377 	    a->pm_md.pm_cmn600.pma_cmn600_nodeid;
378 	desc->pd_node_type = pm->pm_md.pm_cmn600.pm_cmn600_node_type =
379 	    node_type;
380 	pm->pm_md.pm_cmn600.pm_cmn600_event = e;
381 	desc->pd_local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter =
382 	    local_counter;
383 
384 	return (0);
385 }
386 
387 /* Release machine dependent state associated with a PMC. */
388 
389 static int
390 cmn600_release_pmc(int cpu, int ri, struct pmc *pmc)
391 {
392 	struct cmn600_descr *desc;
393 	struct pmc_hw *phw;
394 	struct pmc *pm __diagused;
395 	int err;
396 
397 	(void) pmc;
398 
399 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
400 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
401 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
402 	    ri));
403 
404 	desc = cmn600desc(ri);
405 	phw = desc->pd_phw;
406 	pm  = phw->phw_pmc;
407 	err = pmu_cmn600_free_localpmc(desc->pd_rw_arg, desc->pd_nodeid,
408 	    desc->pd_node_type, desc->pd_local_counter);
409 	if (err != 0)
410 		return (err);
411 
412 	KASSERT(pm == NULL, ("[cmn600,%d] PHW pmc %p non-NULL", __LINE__, pm));
413 
414 	return (0);
415 }
416 
417 static inline uint64_t
418 cmn600_encode_source(int node_type, int counter, int port, int sub)
419 {
420 
421 	/* Calculate pmevcnt0_input_sel based on list in Table 3-794. */
422 	if (node_type == NODE_TYPE_XP)
423 		return (0x4 | counter);
424 
425 	return (((port + 1) << 4) | (sub << 2) | counter);
426 }
427 
428 /*
429  * start a PMC.
430  */
431 
432 static int
433 cmn600_start_pmc(int cpu, int ri)
434 {
435 	int counter, local_counter, node_type, shift;
436 	uint64_t config, occupancy, source, xp_pmucfg;
437 	struct cmn600_descr *desc;
438 	struct pmc_hw *phw;
439 	struct pmc *pm;
440 	uint8_t event, port, sub;
441 	uint16_t nodeid;
442 	void *arg;
443 
444 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
445 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
446 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
447 	    ri));
448 
449 	counter = ri % CMN600_COUNTERS_N;
450 	desc = cmn600desc(ri);
451 	phw = desc->pd_phw;
452 	pm  = phw->phw_pmc;
453 	arg = desc->pd_rw_arg;
454 
455 	KASSERT(pm != NULL,
456 	    ("[cmn600,%d] starting cpu%d,pmc%d with null pmc record", __LINE__,
457 		cpu, ri));
458 
459 	PMCDBG3(MDP, STA, 1, "%s cpu=%d ri=%d", __func__, cpu, ri);
460 
461 	config = pm->pm_md.pm_cmn600.pm_cmn600_config;
462 	occupancy = pm->pm_md.pm_cmn600.pm_cmn600_occupancy;
463 	node_type = pm->pm_md.pm_cmn600.pm_cmn600_node_type;
464 	event = pm->pm_md.pm_cmn600.pm_cmn600_event;
465 	nodeid = pm->pm_md.pm_cmn600.pm_cmn600_nodeid;
466 	local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter;
467 	port = (nodeid >> 2) & 1;
468 	sub = nodeid & 3;
469 
470 	switch (node_type) {
471 	case NODE_TYPE_DVM:
472 	case NODE_TYPE_HN_F:
473 	case NODE_TYPE_CXHA:
474 	case NODE_TYPE_CXRA:
475 		pmu_cmn600_md8(arg, nodeid, node_type,
476 		    CMN600_COMMON_PMU_EVENT_SEL,
477 		    CMN600_COMMON_PMU_EVENT_SEL_OCC_MASK,
478 		    occupancy << CMN600_COMMON_PMU_EVENT_SEL_OCC_SHIFT);
479 		break;
480 	case NODE_TYPE_XP:
481 		/* Set PC and Interface.*/
482 		event |= config;
483 	}
484 
485 	/*
486 	 * 5.5.1 Set up PMU counters
487 	 * 1. Ensure that the NIDEN input is asserted. HW side. */
488 	/* 2. Select event of target node for one of four outputs. */
489 	pmu_cmn600_md8(arg, nodeid, node_type, CMN600_COMMON_PMU_EVENT_SEL,
490 	    0xff << (local_counter * 8),
491 	    event << (local_counter * 8));
492 
493 	xp_pmucfg = pmu_cmn600_rd8(arg, nodeid, NODE_TYPE_XP,
494 	    POR_DTM_PMU_CONFIG);
495 	/*
496 	 * 3. configure XP to connect one of four target node outputs to local
497 	 * counter.
498 	 */
499 	source = cmn600_encode_source(node_type, local_counter, port, sub);
500 	shift = (local_counter * POR_DTM_PMU_CONFIG_VCNT_INPUT_SEL_WIDTH) +
501 	    POR_DTM_PMU_CONFIG_VCNT_INPUT_SEL_SHIFT;
502 	xp_pmucfg &= ~(0xffUL << shift);
503 	xp_pmucfg |= source << shift;
504 
505 	/* 4. Pair with global counters A, B, C, ..., H. */
506 	shift = (local_counter * 4) + 16;
507 	xp_pmucfg &= ~(0xfUL << shift);
508 	xp_pmucfg |= counter << shift;
509 	/* Enable pairing.*/
510 	xp_pmucfg |= 1 << (local_counter + 4);
511 
512 	/* 5. Combine local counters 0 with 1, 2 with 3 or all four. */
513 	xp_pmucfg &= ~0xeUL;
514 
515 	/* 6. Enable XP's PMU function. */
516 	xp_pmucfg |= POR_DTM_PMU_CONFIG_PMU_EN;
517 	pmu_cmn600_wr8(arg, nodeid, NODE_TYPE_XP, POR_DTM_PMU_CONFIG, xp_pmucfg);
518 	if (node_type == NODE_TYPE_CXLA)
519 		pmu_cmn600_set8(arg, nodeid, NODE_TYPE_CXLA,
520 		    POR_CXG_RA_CFG_CTL, EN_CXLA_PMUCMD_PROP);
521 
522 	/* 7. Enable DTM. */
523 	pmu_cmn600_set8(arg, nodeid, NODE_TYPE_XP, POR_DTM_CONTROL,
524 	    POR_DTM_CONTROL_DTM_ENABLE);
525 
526 	/* 8. Reset grouping of global counters. Use 32 bits. */
527 	pmu_cmn600_clr8(arg, nodeid, NODE_TYPE_DTC, POR_DT_PMCR,
528 	    POR_DT_PMCR_CNTCFG_MASK);
529 
530 	/* 9. Enable DTC. */
531 	pmu_cmn600_set8(arg, nodeid, NODE_TYPE_DTC, POR_DT_DTC_CTL,
532 	    POR_DT_DTC_CTL_DT_EN);
533 
534 	/* 10. Enable Overflow Interrupt. */
535 	pmu_cmn600_set8(arg, nodeid, NODE_TYPE_DTC, POR_DT_PMCR,
536 	    POR_DT_PMCR_OVFL_INTR_EN);
537 
538 	/* 11. Run PMC. */
539 	pmu_cmn600_set8(arg, nodeid, NODE_TYPE_DTC, POR_DT_PMCR,
540 	    POR_DT_PMCR_PMU_EN);
541 
542 	return (0);
543 }
544 
545 /*
546  * Stop a PMC.
547  */
548 
549 static int
550 cmn600_stop_pmc(int cpu, int ri)
551 {
552 	struct cmn600_descr *desc;
553 	struct pmc_hw *phw;
554 	struct pmc *pm;
555 	int local_counter;
556 	uint64_t val;
557 
558 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
559 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
560 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
561 	    ri));
562 
563 	desc = cmn600desc(ri);
564 	phw = desc->pd_phw;
565 	pm  = phw->phw_pmc;
566 
567 	KASSERT(pm != NULL,
568 	    ("[cmn600,%d] cpu%d,pmc%d no PMC to stop", __LINE__,
569 		cpu, ri));
570 
571 	PMCDBG2(MDP, STO, 1, "%s ri=%d", __func__, ri);
572 
573 	/* Disable pairing. */
574 	local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter;
575 	pmu_cmn600_clr8(desc->pd_rw_arg, pm->pm_md.pm_cmn600.pm_cmn600_nodeid,
576 	    NODE_TYPE_XP, POR_DTM_PMU_CONFIG, (1 << (local_counter + 4)));
577 
578 	/* Shutdown XP's DTM function if no paired counters. */
579 	val = pmu_cmn600_rd8(desc->pd_rw_arg,
580 	    pm->pm_md.pm_cmn600.pm_cmn600_nodeid, NODE_TYPE_XP,
581 	    POR_DTM_PMU_CONFIG);
582 	if ((val & 0xf0) == 0)
583 		pmu_cmn600_clr8(desc->pd_rw_arg,
584 		    pm->pm_md.pm_cmn600.pm_cmn600_nodeid, NODE_TYPE_XP,
585 		    POR_DTM_PMU_CONFIG, POR_DTM_CONTROL_DTM_ENABLE);
586 
587 	return (0);
588 }
589 
590 /*
591  * describe a PMC
592  */
593 static int
594 cmn600_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
595 {
596 	struct pmc_hw *phw;
597 	size_t copied;
598 	int error;
599 
600 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
601 	    ("[cmn600,%d] illegal CPU %d", __LINE__, cpu));
602 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
603 	    ri));
604 
605 	phw = cmn600desc(ri)->pd_phw;
606 
607 	if ((error = copystr(cmn600desc(ri)->pd_descr.pd_name,
608 	    pi->pm_name, PMC_NAME_MAX, &copied)) != 0)
609 		return (error);
610 
611 	pi->pm_class = cmn600desc(ri)->pd_descr.pd_class;
612 
613 	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
614 		pi->pm_enabled = TRUE;
615 		*ppmc          = phw->phw_pmc;
616 	} else {
617 		pi->pm_enabled = FALSE;
618 		*ppmc          = NULL;
619 	}
620 
621 	return (0);
622 }
623 
624 /*
625  * processor dependent initialization.
626  */
627 
628 static int
629 cmn600_pcpu_init(struct pmc_mdep *md, int cpu)
630 {
631 	int first_ri, n, npmc;
632 	struct pmc_hw  *phw;
633 	struct pmc_cpu *pc;
634 	int mdep_class;
635 
636 	mdep_class = PMC_MDEP_CLASS_INDEX_CMN600;
637 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
638 	    ("[cmn600,%d] insane cpu number %d", __LINE__, cpu));
639 
640 	PMCDBG1(MDP, INI, 1, "cmn600-init cpu=%d", cpu);
641 
642 	/*
643 	 * Set the content of the hardware descriptors to a known
644 	 * state and initialize pointers in the MI per-cpu descriptor.
645 	 */
646 
647 	pc = pmc_pcpu[cpu];
648 	first_ri = md->pmd_classdep[mdep_class].pcd_ri;
649 	npmc = md->pmd_classdep[mdep_class].pcd_num;
650 
651 	for (n = 0; n < npmc; n++, phw++) {
652 		phw = cmn600desc(n)->pd_phw;
653 		phw->phw_state = PMC_PHW_CPU_TO_STATE(cpu) |
654 		    PMC_PHW_INDEX_TO_STATE(n);
655 		/* Set enabled only if unit present. */
656 		if (cmn600_pmcs[class_ri2unit(n)].arg != NULL)
657 			phw->phw_state |= PMC_PHW_FLAG_IS_ENABLED;
658 		phw->phw_pmc = NULL;
659 		pc->pc_hwpmcs[n + first_ri] = phw;
660 	}
661 	return (0);
662 }
663 
664 /*
665  * processor dependent cleanup prior to the KLD
666  * being unloaded
667  */
668 
669 static int
670 cmn600_pcpu_fini(struct pmc_mdep *md, int cpu)
671 {
672 
673 	return (0);
674 }
675 
676 static int
677 cmn600_pmu_intr(struct trapframe *tf, int unit, int i)
678 {
679 	struct pmc_cpu *pc __diagused;
680 	struct pmc_hw *phw;
681 	struct pmc *pm;
682 	int error, cpu, ri;
683 
684 	ri = i + unit * CMN600_COUNTERS_N;
685 	cpu = curcpu;
686 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
687 	    ("[cmn600,%d] CPU %d out of range", __LINE__, cpu));
688 	pc = pmc_pcpu[cpu];
689 	KASSERT(pc != NULL, ("pc != NULL"));
690 
691 	phw = cmn600desc(ri)->pd_phw;
692 	KASSERT(phw != NULL, ("phw != NULL"));
693 	pm  = phw->phw_pmc;
694 	if (pm == NULL)
695 		return (0);
696 
697 	if (!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
698 		/* Always CPU0. */
699 		pm->pm_pcpu_state[0].pps_overflowcnt += 1;
700 		return (0);
701 	}
702 
703 	if (pm->pm_state != PMC_STATE_RUNNING)
704 		return (0);
705 
706 	error = pmc_process_interrupt(PMC_HR, pm, tf);
707 	if (error)
708 		cmn600_stop_pmc(cpu, ri);
709 
710 	/* Reload sampling count */
711 	cmn600_write_pmc(cpu, ri, pm->pm_sc.pm_reloadcount);
712 
713 	return (0);
714 }
715 
716 /*
717  * Initialize ourselves.
718  */
719 static int
720 cmn600_init_pmc_units(void)
721 {
722 	int i;
723 
724 	if (cmn600_units > 0) { /* Already initialized. */
725 		return (0);
726 	}
727 
728 	cmn600_units = cmn600_pmc_nunits();
729 	if (cmn600_units == 0)
730 		return (ENOENT);
731 
732 	for (i = 0; i < cmn600_units; i++) {
733 		if (cmn600_pmc_getunit(i, &cmn600_pmcs[i].arg,
734 		    &cmn600_pmcs[i].domain) != 0)
735 			cmn600_pmcs[i].arg = NULL;
736 	}
737 	return (0);
738 }
739 
740 int
741 pmc_cmn600_nclasses(void)
742 {
743 
744 	if (cmn600_pmc_nunits() > 0)
745 		return (1);
746 	return (0);
747 }
748 
749 int
750 pmc_cmn600_initialize(struct pmc_mdep *md)
751 {
752 	struct pmc_classdep *pcd;
753 	int i, npmc, unit;
754 
755 	cmn600_init_pmc_units();
756 	KASSERT(md != NULL, ("[cmn600,%d] md is NULL", __LINE__));
757 	KASSERT(cmn600_units < CMN600_UNIT_MAX,
758 	    ("[cmn600,%d] cmn600_units too big", __LINE__));
759 
760 	PMCDBG0(MDP,INI,1, "cmn600-initialize");
761 
762 	npmc = CMN600_COUNTERS_N * cmn600_units;
763 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_CMN600];
764 
765 	pcd->pcd_caps		= PMC_CAP_SYSTEM | PMC_CAP_READ |
766 	    PMC_CAP_WRITE | PMC_CAP_QUALIFIER | PMC_CAP_INTERRUPT |
767 	    PMC_CAP_DOMWIDE;
768 	pcd->pcd_class	= PMC_CLASS_CMN600_PMU;
769 	pcd->pcd_num	= npmc;
770 	pcd->pcd_ri	= md->pmd_npmc;
771 	pcd->pcd_width	= 48;
772 
773 	pcd->pcd_allocate_pmc	= cmn600_allocate_pmc;
774 	pcd->pcd_config_pmc	= cmn600_config_pmc;
775 	pcd->pcd_describe	= cmn600_describe;
776 	pcd->pcd_get_config	= cmn600_get_config;
777 	pcd->pcd_get_msr	= NULL;
778 	pcd->pcd_pcpu_fini	= cmn600_pcpu_fini;
779 	pcd->pcd_pcpu_init	= cmn600_pcpu_init;
780 	pcd->pcd_read_pmc	= cmn600_read_pmc;
781 	pcd->pcd_release_pmc	= cmn600_release_pmc;
782 	pcd->pcd_start_pmc	= cmn600_start_pmc;
783 	pcd->pcd_stop_pmc	= cmn600_stop_pmc;
784 	pcd->pcd_write_pmc	= cmn600_write_pmc;
785 
786 	md->pmd_npmc	       += npmc;
787 	cmn600_pmcdesc = malloc(sizeof(struct cmn600_descr *) * npmc *
788 	    CMN600_PMU_DEFAULT_UNITS_N, M_PMC, M_WAITOK|M_ZERO);
789 	for (i = 0; i < npmc; i++) {
790 		cmn600_pmcdesc[i] = malloc(sizeof(struct cmn600_descr), M_PMC,
791 		    M_WAITOK|M_ZERO);
792 
793 		unit = i / CMN600_COUNTERS_N;
794 		KASSERT(unit >= 0, ("unit >= 0"));
795 		KASSERT(cmn600_pmcs[unit].arg != NULL, ("arg != NULL"));
796 
797 		cmn600_pmcdesc[i]->pd_rw_arg = cmn600_pmcs[unit].arg;
798 		cmn600_pmcdesc[i]->pd_descr.pd_class =
799 		    PMC_CLASS_CMN600_PMU;
800 		cmn600_pmcdesc[i]->pd_descr.pd_caps = pcd->pcd_caps;
801 		cmn600_pmcdesc[i]->pd_phw = (struct pmc_hw *)malloc(
802 		    sizeof(struct pmc_hw), M_PMC, M_WAITOK|M_ZERO);
803 		snprintf(cmn600_pmcdesc[i]->pd_descr.pd_name, 63,
804 		    "CMN600_%d", i);
805 		cmn600_pmu_intr_cb(cmn600_pmcs[unit].arg, cmn600_pmu_intr);
806 	}
807 
808 	return (0);
809 }
810 
811 void
812 pmc_cmn600_finalize(struct pmc_mdep *md)
813 {
814 	struct pmc_classdep *pcd;
815 	int i, npmc;
816 
817 	KASSERT(md->pmd_classdep[PMC_MDEP_CLASS_INDEX_CMN600].pcd_class ==
818 	    PMC_CLASS_CMN600_PMU, ("[cmn600,%d] pmc class mismatch",
819 	    __LINE__));
820 
821 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_CMN600];
822 
823 	npmc = pcd->pcd_num;
824 	for (i = 0; i < npmc; i++) {
825 		free(cmn600_pmcdesc[i]->pd_phw, M_PMC);
826 		free(cmn600_pmcdesc[i], M_PMC);
827 	}
828 	free(cmn600_pmcdesc, M_PMC);
829 	cmn600_pmcdesc = NULL;
830 }
831 
832 MODULE_DEPEND(pmc, cmn600, 1, 1, 1);
833 #endif /* DEV_ACPI */
834