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