xref: /freebsd/sys/dev/hwpmc/hwpmc_dmc620.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
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 Ampere Computing LLC
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 /* Support for ARM DMC-620 Memory Controller PMU */
34 
35 #include <sys/cdefs.h>
36 #include <sys/param.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/pmc.h>
41 #include <sys/pmckern.h>
42 #include <sys/systm.h>
43 
44 #include <dev/hwpmc/pmu_dmc620_reg.h>
45 
46 #define	DMC620_TYPE_CLKDIV2	0
47 #define	DMC620_TYPE_CLK		1
48 #define CLASS2TYPE(c)	((c) - PMC_CLASS_DMC620_PMU_CD2)
49 
50 /* Create wrapper for each class. */
51 #define	CLASSDEP_FN2(fn, t1, a1, t2, a2)				\
52 	static int fn(int class, t1 a1, t2 a2);				\
53 	static int fn ## _cd2(t1 a1, t2 a2)				\
54 	{								\
55 		return (fn(PMC_CLASS_DMC620_PMU_CD2, a1, a2));		\
56 	}								\
57 	static int fn ## _c(t1 a1, t2 a2)				\
58 	{								\
59 		return (fn(PMC_CLASS_DMC620_PMU_C, a1, a2));		\
60 	}								\
61 	static int fn(int class, t1 a1, t2 a2)
62 
63 #define	CLASSDEP_FN3(fn, t1, a1, t2, a2, t3, a3)			\
64 	static int fn(int class, t1 a1, t2 a2, t3 a3);			\
65 	static int fn ## _cd2(t1 a1, t2 a2, t3 a3)			\
66 	{								\
67 		return (fn(PMC_CLASS_DMC620_PMU_CD2, a1, a2, a3));	\
68 	}								\
69 	static int fn ## _c(t1 a1, t2 a2, t3 a3)			\
70 	{								\
71 		return (fn(PMC_CLASS_DMC620_PMU_C, a1, a2, a3));	\
72 	}								\
73 	static int fn(int class, t1 a1, t2 a2, t3 a3)
74 
75 #define	CLASSDEP_FN4(fn, t1, a1, t2, a2, t3, a3, t4, a4)		\
76 	static int fn(int class, t1 a1, t2 a2, t3 a3, t4 a4);		\
77 	static int fn ## _cd2(t1 a1, t2 a2, t3 a3, t4 a4)		\
78 	{								\
79 		return (fn(PMC_CLASS_DMC620_PMU_CD2, a1, a2, a3, a4));	\
80 	}								\
81 	static int fn ## _c(t1 a1, t2 a2, t3 a3, t4 a4)			\
82 	{								\
83 		return (fn(PMC_CLASS_DMC620_PMU_C, a1, a2, a3, a4));	\
84 	}								\
85 	static int fn(int class, t1 a1, t2 a2, t3 a3, t4 a4)
86 
87 struct dmc620_pmc {
88 	void	*arg;
89 	int	domain;
90 };
91 
92 struct dmc620_descr {
93 	struct pmc_descr pd_descr;  /* "base class" */
94 	void		*pd_rw_arg; /* Argument to use with read/write */
95 	struct pmc	*pd_pmc;
96 	struct pmc_hw	*pd_phw;
97 	uint32_t	pd_config;
98 	uint32_t	pd_match;
99 	uint32_t	pd_mask;
100 	uint32_t	pd_evsel;   /* address of EVSEL register */
101 	uint32_t	pd_perfctr; /* address of PERFCTR register */
102 
103 };
104 
105 static struct dmc620_descr **dmc620_pmcdesc[2];
106 static struct dmc620_pmc dmc620_pmcs[DMC620_UNIT_MAX];
107 static int dmc620_npmcs = 0;
108 
109 void
110 dmc620_pmc_register(int unit, void *arg, int domain)
111 {
112 
113 	if (unit >= DMC620_UNIT_MAX) {
114 		/* TODO */
115 		return;
116 	}
117 
118 	dmc620_pmcs[unit].arg = arg;
119 	dmc620_pmcs[unit].domain = domain;
120 	dmc620_npmcs++;
121 }
122 
123 void
124 dmc620_pmc_unregister(int unit)
125 {
126 
127 	dmc620_pmcs[unit].arg = NULL;
128 	dmc620_npmcs--;
129 }
130 
131 int
132 pmc_dmc620_nclasses(void)
133 {
134 
135 	if (dmc620_npmcs > 0)
136 		return (2);
137 	return (0);
138 }
139 
140 static inline struct dmc620_descr *
141 dmc620desc(int class, int cpu, int ri)
142 {
143 	int c;
144 
145 	c = CLASS2TYPE(class);
146 	KASSERT((c & 0xfffffffe) == 0, ("[dmc620,%d] 'c' can only be 0 or 1. "
147 	    "now %d", __LINE__, c));
148 
149 	return (dmc620_pmcdesc[c][ri]);
150 }
151 
152 static inline int
153 cntr(int class, int ri)
154 {
155 	int c;
156 
157 	c = CLASS2TYPE(class);
158 	KASSERT((c & 0xfffffffe) == 0, ("[dmc620,%d] 'c' can only be 0 or 1. "
159 	    "now %d", __LINE__, c));
160 
161 	if (c == DMC620_TYPE_CLKDIV2)
162 		return (ri % DMC620_CLKDIV2_COUNTERS_N);
163 	return ((ri % DMC620_CLK_COUNTERS_N) + DMC620_CLKDIV2_COUNTERS_N);
164 }
165 
166 static inline int
167 class2mdep(int class)
168 {
169 
170 	switch (class) {
171 	case PMC_CLASS_DMC620_PMU_CD2:
172 		return (PMC_MDEP_CLASS_INDEX_DMC620_CD2);
173 	case PMC_CLASS_DMC620_PMU_C:
174 		return (PMC_MDEP_CLASS_INDEX_DMC620_C);
175 	}
176 	return (-1);
177 }
178 
179 static inline int
180 class_ri2unit(int class, int ri)
181 {
182 
183 	if (class == PMC_CLASS_DMC620_PMU_CD2)
184 		return (ri / DMC620_CLKDIV2_COUNTERS_N);
185 	else
186 		return (ri / DMC620_CLK_COUNTERS_N);
187 }
188 
189 /*
190  * read a pmc register
191  */
192 
193 CLASSDEP_FN4(dmc620_read_pmc, int, cpu, int, ri, struct pmc *, pm,
194     pmc_value_t *, v)
195 {
196 	struct dmc620_descr *desc;
197 
198 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
199 	    ("[dmc620,%d] illegal CPU value %d", __LINE__, cpu));
200 	KASSERT(ri >= 0, ("[dmc620,%d] row-index %d out of range", __LINE__,
201 	    ri));
202 
203 	desc = dmc620desc(class, cpu, ri);
204 
205 	PMCDBG3(MDP,REA,1,"%s id=%d class=%d", __func__, ri, class);
206 
207 	/*
208 	 * Should emulate 64bits, because 32 bits counter overflows faster than
209 	 * pmcstat default period.
210 	 */
211 	/* Always CPU0. Single controller for all CPUs. */
212 	*v = ((uint64_t)pm->pm_pcpu_state[0].pps_overflowcnt << 32) |
213 	    pmu_dmc620_rd4(desc->pd_rw_arg, cntr(class, ri),
214 	    DMC620_COUNTER_VALUE_LO);
215 
216 	PMCDBG3(MDP, REA, 2, "%s id=%d -> %jd", __func__, ri, *v);
217 
218 	return (0);
219 }
220 
221 /*
222  * Write a pmc register.
223  */
224 
225 CLASSDEP_FN4(dmc620_write_pmc, int, cpu, int, ri, struct pmc *, pm,
226     pmc_value_t, v)
227 {
228 	struct dmc620_descr *desc;
229 
230 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
231 	    ("[dmc620,%d] illegal CPU value %d", __LINE__, cpu));
232 	KASSERT(ri >= 0, ("[dmc620,%d] row-index %d out of range", __LINE__,
233 	    ri));
234 
235 	desc = dmc620desc(class, cpu, ri);
236 
237 	PMCDBG4(MDP, WRI, 1, "%s cpu=%d ri=%d v=%jx", __func__, cpu, ri, v);
238 
239 	pmu_dmc620_wr4(desc->pd_rw_arg, cntr(class, ri),
240 	    DMC620_COUNTER_VALUE_LO, v);
241 	return (0);
242 }
243 
244 /*
245  * configure hardware pmc according to the configuration recorded in
246  * pmc 'pm'.
247  */
248 
249 CLASSDEP_FN3(dmc620_config_pmc, int, cpu, int, ri, struct pmc *, pm)
250 {
251 	struct pmc_hw *phw;
252 
253 	PMCDBG4(MDP, CFG, 1, "%s cpu=%d ri=%d pm=%p", __func__, cpu, ri, pm);
254 
255 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
256 	    ("[dmc620,%d] illegal CPU value %d", __LINE__, cpu));
257 	KASSERT(ri >= 0, ("[dmc620,%d] row-index %d out of range", __LINE__,
258 	    ri));
259 
260 	phw = dmc620desc(class, cpu, ri)->pd_phw;
261 
262 	KASSERT(pm == NULL || phw->phw_pmc == NULL,
263 	    ("[dmc620,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
264 		__LINE__, pm, phw->phw_pmc));
265 
266 	phw->phw_pmc = pm;
267 	return (0);
268 }
269 
270 /*
271  * Retrieve a configured PMC pointer from hardware state.
272  */
273 
274 CLASSDEP_FN3(dmc620_get_config, int, cpu, int, ri, struct pmc **, ppm)
275 {
276 
277 	*ppm = dmc620desc(class, cpu, ri)->pd_phw->phw_pmc;
278 
279 	return (0);
280 }
281 
282 /*
283  * Check if a given allocation is feasible.
284  */
285 
286 CLASSDEP_FN4(dmc620_allocate_pmc, int, cpu, int, ri, struct pmc *,pm,
287     const struct pmc_op_pmcallocate *, a)
288 {
289 	const struct pmc_descr *pd;
290 	uint64_t caps, control;
291 	enum pmc_event pe;
292 	uint8_t e;
293 
294 	(void) cpu;
295 
296 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
297 	    ("[dmc620,%d] illegal CPU value %d", __LINE__, cpu));
298 	KASSERT(ri >= 0, ("[dmc620,%d] row-index %d out of range", __LINE__,
299 	    ri));
300 
301 	pd = &dmc620desc(class, cpu, ri)->pd_descr;
302 	if (dmc620_pmcs[class_ri2unit(class, ri)].domain !=
303 	    pcpu_find(cpu)->pc_domain)
304 		return (EINVAL);
305 
306 	/* check class match */
307 	if (pd->pd_class != a->pm_class)
308 		return (EINVAL);
309 
310 	caps = pm->pm_caps;
311 
312 	PMCDBG3(MDP, ALL, 1, "%s ri=%d caps=0x%x", __func__, ri, caps);
313 
314 	pe = a->pm_ev;
315 	if (class == PMC_CLASS_DMC620_PMU_CD2)
316 		e = pe - PMC_EV_DMC620_PMU_CD2_FIRST;
317 	else
318 		e = pe - PMC_EV_DMC620_PMU_C_FIRST;
319 
320 	control = (e << DMC620_COUNTER_CONTROL_EVENT_SHIFT) &
321 	    DMC620_COUNTER_CONTROL_EVENT_MASK;
322 
323 	if (caps & PMC_CAP_INVERT)
324 		control |= DMC620_COUNTER_CONTROL_INVERT;
325 
326 	pm->pm_md.pm_dmc620.pm_control = control;
327 	pm->pm_md.pm_dmc620.pm_match = a->pm_md.pm_dmc620.pm_dmc620_match;
328 	pm->pm_md.pm_dmc620.pm_mask = a->pm_md.pm_dmc620.pm_dmc620_mask;
329 
330 	PMCDBG3(MDP, ALL, 2, "%s ri=%d -> control=0x%x", __func__, ri, control);
331 
332 	return (0);
333 }
334 
335 /*
336  * Release machine dependent state associated with a PMC.  This is a
337  * no-op on this architecture.
338  *
339  */
340 
341 /* ARGSUSED0 */
342 CLASSDEP_FN3(dmc620_release_pmc, int, cpu, int, ri, struct pmc *, pmc)
343 {
344 	struct pmc_hw *phw __diagused;
345 
346 	(void) pmc;
347 
348 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
349 	    ("[dmc620,%d] illegal CPU value %d", __LINE__, cpu));
350 	KASSERT(ri >= 0, ("[dmc620,%d] row-index %d out of range", __LINE__,
351 	    ri));
352 
353 	phw = dmc620desc(class, cpu, ri)->pd_phw;
354 
355 	KASSERT(phw->phw_pmc == NULL,
356 	    ("[dmc620,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
357 
358 	return (0);
359 }
360 
361 /*
362  * start a PMC.
363  */
364 
365 CLASSDEP_FN3(dmc620_start_pmc, int, cpu, int, ri, struct pmc *, pm)
366 {
367 	struct dmc620_descr *desc;
368 	uint64_t control;
369 
370 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
371 	    ("[dmc620,%d] illegal CPU value %d", __LINE__, cpu));
372 	KASSERT(ri >= 0, ("[dmc620,%d] row-index %d out of range", __LINE__,
373 	    ri));
374 
375 	desc = dmc620desc(class, cpu, ri);
376 
377 	PMCDBG3(MDP, STA, 1, "%s cpu=%d ri=%d", __func__, cpu, ri);
378 
379 	pmu_dmc620_wr4(desc->pd_rw_arg, cntr(class, ri),
380 	    DMC620_COUNTER_MASK_LO, pm->pm_md.pm_dmc620.pm_mask & 0xffffffff);
381 	pmu_dmc620_wr4(desc->pd_rw_arg, cntr(class, ri),
382 	    DMC620_COUNTER_MASK_HI, pm->pm_md.pm_dmc620.pm_mask >> 32);
383 	pmu_dmc620_wr4(desc->pd_rw_arg, cntr(class, ri),
384 	    DMC620_COUNTER_MATCH_LO, pm->pm_md.pm_dmc620.pm_match & 0xffffffff);
385 	pmu_dmc620_wr4(desc->pd_rw_arg, cntr(class, ri),
386 	    DMC620_COUNTER_MATCH_HI, pm->pm_md.pm_dmc620.pm_match >> 32);
387 	/* turn on the PMC ENABLE bit */
388 	control = pm->pm_md.pm_dmc620.pm_control | DMC620_COUNTER_CONTROL_ENABLE;
389 
390 	PMCDBG2(MDP, STA, 2, "%s control=0x%x", __func__, control);
391 
392 	pmu_dmc620_wr4(desc->pd_rw_arg, cntr(class, ri),
393 	    DMC620_COUNTER_CONTROL, control);
394 	return (0);
395 }
396 
397 /*
398  * Stop a PMC.
399  */
400 
401 CLASSDEP_FN3(dmc620_stop_pmc, int, cpu, int, ri, struct pmc *, pm)
402 {
403 	struct dmc620_descr *desc;
404 	uint64_t control;
405 
406 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
407 	    ("[dmc620,%d] illegal CPU value %d", __LINE__, cpu));
408 	KASSERT(ri >= 0, ("[dmc620,%d] row-index %d out of range", __LINE__,
409 	    ri));
410 
411 	desc = dmc620desc(class, cpu, ri);
412 
413 	PMCDBG2(MDP, STO, 1, "%s ri=%d", __func__, ri);
414 
415 	/* turn off the PMC ENABLE bit */
416 	control = pm->pm_md.pm_dmc620.pm_control & ~DMC620_COUNTER_CONTROL_ENABLE;
417 	pmu_dmc620_wr4(desc->pd_rw_arg, cntr(class, ri),
418 	    DMC620_COUNTER_CONTROL, control);
419 
420 	return (0);
421 }
422 
423 /*
424  * describe a PMC
425  */
426 CLASSDEP_FN4(dmc620_describe, int, cpu, int, ri, struct pmc_info *, pi,
427     struct pmc **, ppmc)
428 {
429 	struct pmc_descr *pd;
430 	struct pmc_hw *phw;
431 
432 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
433 	    ("[dmc620,%d] illegal CPU %d", __LINE__, cpu));
434 	KASSERT(ri >= 0, ("[dmc620,%d] row-index %d out of range", __LINE__,
435 	    ri));
436 
437 	phw = dmc620desc(class, cpu, ri)->pd_phw;
438 	pd = &dmc620desc(class, cpu, ri)->pd_descr;
439 
440 	strlcpy(pi->pm_name, pd->pd_name, sizeof(pi->pm_name));
441 	pi->pm_class = pd->pd_class;
442 
443 	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
444 		pi->pm_enabled = TRUE;
445 		*ppmc          = phw->phw_pmc;
446 	} else {
447 		pi->pm_enabled = FALSE;
448 		*ppmc          = NULL;
449 	}
450 
451 	return (0);
452 }
453 
454 /*
455  * processor dependent initialization.
456  */
457 
458 CLASSDEP_FN2(dmc620_pcpu_init, struct pmc_mdep *, md, int, cpu)
459 {
460 	int first_ri, n, npmc;
461 	struct pmc_hw  *phw;
462 	struct pmc_cpu *pc;
463 	int mdep_class;
464 
465 	mdep_class = class2mdep(class);
466 	KASSERT(mdep_class != -1, ("[dmc620,%d] wrong class %d", __LINE__,
467 	    class));
468 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
469 	    ("[dmc620,%d] insane cpu number %d", __LINE__, cpu));
470 
471 	PMCDBG1(MDP, INI, 1, "dmc620-init cpu=%d", cpu);
472 
473 	/*
474 	 * Set the content of the hardware descriptors to a known
475 	 * state and initialize pointers in the MI per-cpu descriptor.
476 	 */
477 
478 	pc = pmc_pcpu[cpu];
479 	first_ri = md->pmd_classdep[mdep_class].pcd_ri;
480 	npmc = md->pmd_classdep[mdep_class].pcd_num;
481 
482 	for (n = 0; n < npmc; n++, phw++) {
483 		phw = dmc620desc(class, cpu, n)->pd_phw;
484 		phw->phw_state = PMC_PHW_CPU_TO_STATE(cpu) |
485 		    PMC_PHW_INDEX_TO_STATE(n);
486 		/* Set enabled only if unit present. */
487 		if (dmc620_pmcs[class_ri2unit(class, n)].arg != NULL)
488 			phw->phw_state |= PMC_PHW_FLAG_IS_ENABLED;
489 		phw->phw_pmc = NULL;
490 		pc->pc_hwpmcs[n + first_ri] = phw;
491 	}
492 	return (0);
493 }
494 
495 /*
496  * processor dependent cleanup prior to the KLD
497  * being unloaded
498  */
499 
500 CLASSDEP_FN2(dmc620_pcpu_fini, struct pmc_mdep *, md, int, cpu)
501 {
502 
503 	return (0);
504 }
505 
506 int
507 dmc620_intr(struct trapframe *tf, int class, int unit, int i)
508 {
509 	struct pmc_cpu *pc __diagused;
510 	struct pmc_hw *phw;
511 	struct pmc *pm;
512 	int error, cpu, ri;
513 
514 	ri = i + unit * ((class == PMC_CLASS_DMC620_PMU_CD2) ?
515 	    DMC620_CLKDIV2_COUNTERS_N : DMC620_CLK_COUNTERS_N);
516 	cpu = curcpu;
517 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
518 	    ("[dmc620,%d] CPU %d out of range", __LINE__, cpu));
519 	pc = pmc_pcpu[cpu];
520 	KASSERT(pc != NULL, ("pc != NULL"));
521 
522 	phw = dmc620desc(class, cpu, ri)->pd_phw;
523 	KASSERT(phw != NULL, ("phw != NULL"));
524 	pm  = phw->phw_pmc;
525 	if (pm == NULL)
526 		return (0);
527 
528 	if (!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
529 		/* Always CPU0. */
530 		pm->pm_pcpu_state[0].pps_overflowcnt += 1;
531 		return (0);
532 	}
533 
534 	if (pm->pm_state != PMC_STATE_RUNNING)
535 		return (0);
536 
537 	error = pmc_process_interrupt(PMC_HR, pm, tf);
538 	if (error)
539 		dmc620_stop_pmc(class, cpu, ri, pm);
540 
541 	/* Reload sampling count */
542 	dmc620_write_pmc(class, cpu, ri, pm, pm->pm_sc.pm_reloadcount);
543 
544 	return (0);
545 }
546 
547 /*
548  * Initialize ourselves.
549  */
550 
551 int
552 pmc_dmc620_initialize_cd2(struct pmc_mdep *md)
553 {
554 	struct pmc_classdep *pcd;
555 	int i, npmc, unit;
556 
557 	KASSERT(md != NULL, ("[dmc620,%d] md is NULL", __LINE__));
558 	KASSERT(dmc620_npmcs <= DMC620_UNIT_MAX,
559 	    ("[dmc620,%d] dmc620_npmcs too big", __LINE__));
560 
561 	PMCDBG0(MDP,INI,1, "dmc620-initialize");
562 
563 	npmc = DMC620_CLKDIV2_COUNTERS_N * dmc620_npmcs;
564 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_DMC620_CD2];
565 
566 	pcd->pcd_caps		= PMC_CAP_SYSTEM | PMC_CAP_READ |
567 	    PMC_CAP_WRITE | PMC_CAP_INVERT | PMC_CAP_QUALIFIER |
568 	    PMC_CAP_INTERRUPT | PMC_CAP_DOMWIDE;
569 	pcd->pcd_class	= PMC_CLASS_DMC620_PMU_CD2;
570 	pcd->pcd_num	= npmc;
571 	pcd->pcd_ri	= md->pmd_npmc;
572 	pcd->pcd_width	= 32;
573 
574 	pcd->pcd_allocate_pmc	= dmc620_allocate_pmc_cd2;
575 	pcd->pcd_config_pmc	= dmc620_config_pmc_cd2;
576 	pcd->pcd_describe	= dmc620_describe_cd2;
577 	pcd->pcd_get_config	= dmc620_get_config_cd2;
578 	pcd->pcd_get_msr	= NULL;
579 	pcd->pcd_pcpu_fini	= dmc620_pcpu_fini_cd2;
580 	pcd->pcd_pcpu_init	= dmc620_pcpu_init_cd2;
581 	pcd->pcd_read_pmc	= dmc620_read_pmc_cd2;
582 	pcd->pcd_release_pmc	= dmc620_release_pmc_cd2;
583 	pcd->pcd_start_pmc	= dmc620_start_pmc_cd2;
584 	pcd->pcd_stop_pmc	= dmc620_stop_pmc_cd2;
585 	pcd->pcd_write_pmc	= dmc620_write_pmc_cd2;
586 
587 	md->pmd_npmc	       += npmc;
588 	dmc620_pmcdesc[0] = malloc(sizeof(struct dmc620_descr *) * npmc *
589 	    DMC620_PMU_DEFAULT_UNITS_N, M_PMC, M_WAITOK|M_ZERO);
590 	for (i = 0; i < npmc; i++) {
591 		dmc620_pmcdesc[0][i] = malloc(sizeof(struct dmc620_descr),
592 		    M_PMC, M_WAITOK|M_ZERO);
593 
594 		unit = i / DMC620_CLKDIV2_COUNTERS_N;
595 		KASSERT(unit >= 0, ("unit >= 0"));
596 		KASSERT(dmc620_pmcs[unit].arg != NULL, ("arg != NULL"));
597 
598 		dmc620_pmcdesc[0][i]->pd_rw_arg = dmc620_pmcs[unit].arg;
599 		dmc620_pmcdesc[0][i]->pd_descr.pd_class =
600 		    PMC_CLASS_DMC620_PMU_CD2;
601 		dmc620_pmcdesc[0][i]->pd_descr.pd_caps = pcd->pcd_caps;
602 		dmc620_pmcdesc[0][i]->pd_phw = malloc(sizeof(struct pmc_hw),
603 		    M_PMC, M_WAITOK|M_ZERO);
604 		snprintf(dmc620_pmcdesc[0][i]->pd_descr.pd_name, 63,
605 		    "DMC620_CD2_%d", i);
606 	}
607 
608 	return (0);
609 }
610 
611 int
612 pmc_dmc620_initialize_c(struct pmc_mdep *md)
613 {
614 	struct pmc_classdep *pcd;
615 	int i, npmc, unit;
616 
617 	KASSERT(md != NULL, ("[dmc620,%d] md is NULL", __LINE__));
618 	KASSERT(dmc620_npmcs <= DMC620_UNIT_MAX,
619 	    ("[dmc620,%d] dmc620_npmcs too big", __LINE__));
620 
621 	PMCDBG0(MDP,INI,1, "dmc620-initialize");
622 
623 	npmc = DMC620_CLK_COUNTERS_N * dmc620_npmcs;
624 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_DMC620_C];
625 
626 	pcd->pcd_caps		= PMC_CAP_SYSTEM | PMC_CAP_READ |
627 	    PMC_CAP_WRITE | PMC_CAP_INVERT | PMC_CAP_QUALIFIER |
628 	    PMC_CAP_INTERRUPT | PMC_CAP_DOMWIDE;
629 	pcd->pcd_class	= PMC_CLASS_DMC620_PMU_C;
630 	pcd->pcd_num	= npmc;
631 	pcd->pcd_ri	= md->pmd_npmc;
632 	pcd->pcd_width	= 32;
633 
634 	pcd->pcd_allocate_pmc	= dmc620_allocate_pmc_c;
635 	pcd->pcd_config_pmc	= dmc620_config_pmc_c;
636 	pcd->pcd_describe	= dmc620_describe_c;
637 	pcd->pcd_get_config	= dmc620_get_config_c;
638 	pcd->pcd_get_msr	= NULL;
639 	pcd->pcd_pcpu_fini	= dmc620_pcpu_fini_c;
640 	pcd->pcd_pcpu_init	= dmc620_pcpu_init_c;
641 	pcd->pcd_read_pmc	= dmc620_read_pmc_c;
642 	pcd->pcd_release_pmc	= dmc620_release_pmc_c;
643 	pcd->pcd_start_pmc	= dmc620_start_pmc_c;
644 	pcd->pcd_stop_pmc	= dmc620_stop_pmc_c;
645 	pcd->pcd_write_pmc	= dmc620_write_pmc_c;
646 
647 	md->pmd_npmc	       += npmc;
648 	dmc620_pmcdesc[1] = malloc(sizeof(struct dmc620_descr *) * npmc *
649 	    DMC620_PMU_DEFAULT_UNITS_N, M_PMC, M_WAITOK|M_ZERO);
650 	for (i = 0; i < npmc; i++) {
651 		dmc620_pmcdesc[1][i] = malloc(sizeof(struct dmc620_descr),
652 		    M_PMC, M_WAITOK|M_ZERO);
653 
654 		unit = i / DMC620_CLK_COUNTERS_N;
655 		KASSERT(unit >= 0, ("unit >= 0"));
656 		KASSERT(dmc620_pmcs[unit].arg != NULL, ("arg != NULL"));
657 
658 		dmc620_pmcdesc[1][i]->pd_rw_arg = dmc620_pmcs[unit].arg;
659 		dmc620_pmcdesc[1][i]->pd_descr.pd_class = PMC_CLASS_DMC620_PMU_C;
660 		dmc620_pmcdesc[1][i]->pd_descr.pd_caps = pcd->pcd_caps;
661 		dmc620_pmcdesc[1][i]->pd_phw = malloc(sizeof(struct pmc_hw),
662 		    M_PMC, M_WAITOK|M_ZERO);
663 		snprintf(dmc620_pmcdesc[1][i]->pd_descr.pd_name, 63,
664 		    "DMC620_C_%d", i);
665 	}
666 
667 	return (0);
668 }
669 
670 void
671 pmc_dmc620_finalize_cd2(struct pmc_mdep *md)
672 {
673 	struct pmc_classdep *pcd;
674 	int i, npmc;
675 
676 	KASSERT(md->pmd_classdep[PMC_MDEP_CLASS_INDEX_DMC620_CD2].pcd_class ==
677 	    PMC_CLASS_DMC620_PMU_CD2, ("[dmc620,%d] pmc class mismatch",
678 	    __LINE__));
679 
680 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_DMC620_CD2];
681 
682 	npmc = pcd->pcd_num;
683 	for (i = 0; i < npmc; i++) {
684 		free(dmc620_pmcdesc[0][i]->pd_phw, M_PMC);
685 		free(dmc620_pmcdesc[0][i], M_PMC);
686 	}
687 	free(dmc620_pmcdesc[0], M_PMC);
688 	dmc620_pmcdesc[0] = NULL;
689 }
690 
691 void
692 pmc_dmc620_finalize_c(struct pmc_mdep *md)
693 {
694 	struct pmc_classdep *pcd;
695 	int i, npmc;
696 
697 	KASSERT(md->pmd_classdep[PMC_MDEP_CLASS_INDEX_DMC620_C].pcd_class ==
698 	    PMC_CLASS_DMC620_PMU_C, ("[dmc620,%d] pmc class mismatch",
699 	    __LINE__));
700 
701 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_DMC620_C];
702 
703 	npmc = pcd->pcd_num;
704 	for (i = 0; i < npmc; i++) {
705 		free(dmc620_pmcdesc[1][i]->pd_phw, M_PMC);
706 		free(dmc620_pmcdesc[1][i], M_PMC);
707 	}
708 	free(dmc620_pmcdesc[1], M_PMC);
709 	dmc620_pmcdesc[1] = NULL;
710 }
711