1/*-
2 * Copyright (c) 2009 Rui Paulo <rpaulo@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/pmc.h>
34#include <sys/pmckern.h>
35
36#include <machine/pmc_mdep.h>
37/*
38 * Support for the Intel XScale network processors
39 *
40 * XScale processors have up to now three generations.
41 *
42 * The first generation has two PMC; the event selection, interrupt config
43 * and overflow flag setup are done by writing to the PMNC register.
44 * It also has less monitoring events than the latter generations.
45 *
46 * The second and third generatiosn have four PMCs, one register for the event
47 * selection, one register for the interrupt config and one register for
48 * the overflow flags.
49 */
50static int xscale_npmcs;
51static int xscale_gen;	/* XScale Core generation */
52
53struct xscale_event_code_map {
54	enum pmc_event	pe_ev;
55	uint8_t		pe_code;
56};
57
58const struct xscale_event_code_map xscale_event_codes[] = {
59	/* 1st and 2nd Generation XScale cores */
60	{ PMC_EV_XSCALE_IC_FETCH,		0x00 },
61	{ PMC_EV_XSCALE_IC_MISS,		0x01 },
62	{ PMC_EV_XSCALE_DATA_DEPENDENCY_STALLED,0x02 },
63	{ PMC_EV_XSCALE_ITLB_MISS,		0x03 },
64	{ PMC_EV_XSCALE_DTLB_MISS,		0x04 },
65	{ PMC_EV_XSCALE_BRANCH_RETIRED,		0x05 },
66	{ PMC_EV_XSCALE_BRANCH_MISPRED,		0x06 },
67	{ PMC_EV_XSCALE_INSTR_RETIRED,		0x07 },
68	{ PMC_EV_XSCALE_DC_FULL_CYCLE,		0x08 },
69	{ PMC_EV_XSCALE_DC_FULL_CONTIG, 	0x09 },
70	{ PMC_EV_XSCALE_DC_ACCESS,		0x0a },
71	{ PMC_EV_XSCALE_DC_MISS,		0x0b },
72	{ PMC_EV_XSCALE_DC_WRITEBACK,		0x0c },
73	{ PMC_EV_XSCALE_PC_CHANGE,		0x0d },
74	/* 3rd Generation XScale cores */
75	{ PMC_EV_XSCALE_BRANCH_RETIRED_ALL,	0x0e },
76	{ PMC_EV_XSCALE_INSTR_CYCLE,		0x0f },
77	{ PMC_EV_XSCALE_CP_STALL,		0x17 },
78	{ PMC_EV_XSCALE_PC_CHANGE_ALL,		0x18 },
79	{ PMC_EV_XSCALE_PIPELINE_FLUSH,		0x19 },
80	{ PMC_EV_XSCALE_BACKEND_STALL,		0x1a },
81	{ PMC_EV_XSCALE_MULTIPLIER_USE,		0x1b },
82	{ PMC_EV_XSCALE_MULTIPLIER_STALLED,	0x1c },
83	{ PMC_EV_XSCALE_DATA_CACHE_STALLED,	0x1e },
84	{ PMC_EV_XSCALE_L2_CACHE_REQ,		0x20 },
85	{ PMC_EV_XSCALE_L2_CACHE_MISS,		0x23 },
86	{ PMC_EV_XSCALE_ADDRESS_BUS_TRANS,	0x40 },
87	{ PMC_EV_XSCALE_SELF_ADDRESS_BUS_TRANS,	0x41 },
88	{ PMC_EV_XSCALE_DATA_BUS_TRANS,		0x48 },
89};
90
91/*
92 * Per-processor information.
93 */
94struct xscale_cpu {
95	struct pmc_hw   *pc_xscalepmcs;
96};
97
98static struct xscale_cpu **xscale_pcpu;
99
100/*
101 * Performance Monitor Control Register
102 */
103static __inline uint32_t
104xscale_pmnc_read(void)
105{
106	uint32_t reg;
107
108	__asm __volatile("mrc p14, 0, %0, c0, c1, 0" : "=r" (reg));
109
110	return (reg);
111}
112
113static __inline void
114xscale_pmnc_write(uint32_t reg)
115{
116	__asm __volatile("mcr p14, 0, %0, c0, c1, 0" : : "r" (reg));
117}
118
119/*
120 * Clock Counter Register
121 */
122static __inline uint32_t
123xscale_ccnt_read(void)
124{
125	uint32_t reg;
126
127	__asm __volatile("mrc p14, 0, %0, c1, c1, 0" : "=r" (reg));
128
129	return (reg);
130}
131
132static __inline void
133xscale_ccnt_write(uint32_t reg)
134{
135	__asm __volatile("mcr p14, 0, %0, c1, c1, 0" : : "r" (reg));
136}
137
138/*
139 * Interrupt Enable Register
140 */
141static __inline uint32_t
142xscale_inten_read(void)
143{
144	uint32_t reg;
145
146	__asm __volatile("mrc p14, 0, %0, c4, c1, 0" : "=r" (reg));
147
148	return (reg);
149}
150
151static __inline void
152xscale_inten_write(uint32_t reg)
153{
154	__asm __volatile("mcr p14, 0, %0, c4, c1, 0" : : "r" (reg));
155}
156
157/*
158 * Overflow Flag Register
159 */
160static __inline uint32_t
161xscale_flag_read(void)
162{
163	uint32_t reg;
164
165	__asm __volatile("mrc p14, 0, %0, c5, c1, 0" : "=r" (reg));
166
167	return (reg);
168}
169
170static __inline void
171xscale_flag_write(uint32_t reg)
172{
173	__asm __volatile("mcr p14, 0, %0, c5, c1, 0" : : "r" (reg));
174}
175
176/*
177 * Event Selection Register
178 */
179static __inline uint32_t
180xscale_evtsel_read(void)
181{
182	uint32_t reg;
183
184	__asm __volatile("mrc p14, 0, %0, c8, c1, 0" : "=r" (reg));
185
186	return (reg);
187}
188
189static __inline void
190xscale_evtsel_write(uint32_t reg)
191{
192	__asm __volatile("mcr p14, 0, %0, c8, c1, 0" : : "r" (reg));
193}
194
195/*
196 * Performance Count Register N
197 */
198static uint32_t
199xscale_pmcn_read(unsigned int pmc)
200{
201	uint32_t reg = 0;
202
203	KASSERT(pmc < 4, ("[xscale,%d] illegal PMC number %d", __LINE__, pmc));
204
205	switch (pmc) {
206	case 0:
207		__asm __volatile("mrc p14, 0, %0, c0, c2, 0" : "=r" (reg));
208		break;
209	case 1:
210		__asm __volatile("mrc p14, 0, %0, c1, c2, 0" : "=r" (reg));
211		break;
212	case 2:
213		__asm __volatile("mrc p14, 0, %0, c2, c2, 0" : "=r" (reg));
214		break;
215	case 3:
216		__asm __volatile("mrc p14, 0, %0, c3, c2, 0" : "=r" (reg));
217		break;
218	}
219
220	return (reg);
221}
222
223static uint32_t
224xscale_pmcn_write(unsigned int pmc, uint32_t reg)
225{
226
227	KASSERT(pmc < 4, ("[xscale,%d] illegal PMC number %d", __LINE__, pmc));
228
229	switch (pmc) {
230	case 0:
231		__asm __volatile("mcr p14, 0, %0, c0, c2, 0" : : "r" (reg));
232		break;
233	case 1:
234		__asm __volatile("mcr p14, 0, %0, c1, c2, 0" : : "r" (reg));
235		break;
236	case 2:
237		__asm __volatile("mcr p14, 0, %0, c2, c2, 0" : : "r" (reg));
238		break;
239	case 3:
240		__asm __volatile("mcr p14, 0, %0, c3, c2, 0" : : "r" (reg));
241		break;
242	}
243
244	return (reg);
245}
246
247static int
248xscale_allocate_pmc(int cpu, int ri, struct pmc *pm,
249  const struct pmc_op_pmcallocate *a)
250{
251	enum pmc_event pe;
252	uint32_t caps, config;
253	int i;
254
255	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
256	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
257	KASSERT(ri >= 0 && ri < xscale_npmcs,
258	    ("[xscale,%d] illegal row index %d", __LINE__, ri));
259
260	caps = a->pm_caps;
261	if (a->pm_class != PMC_CLASS_XSCALE)
262		return (EINVAL);
263	pe = a->pm_ev;
264	for (i = 0; i < nitems(xscale_event_codes); i++) {
265		if (xscale_event_codes[i].pe_ev == pe) {
266			config = xscale_event_codes[i].pe_code;
267			break;
268		}
269	}
270	if (i == nitems(xscale_event_codes))
271		return EINVAL;
272	/* Generation 1 has fewer events */
273	if (xscale_gen == 1 && i > PMC_EV_XSCALE_PC_CHANGE)
274		return EINVAL;
275	pm->pm_md.pm_xscale.pm_xscale_evsel = config;
276
277	PMCDBG2(MDP,ALL,2,"xscale-allocate ri=%d -> config=0x%x", ri, config);
278
279	return 0;
280}
281
282
283static int
284xscale_read_pmc(int cpu, int ri, pmc_value_t *v)
285{
286	struct pmc *pm;
287	pmc_value_t tmp;
288
289	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
290	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
291	KASSERT(ri >= 0 && ri < xscale_npmcs,
292	    ("[xscale,%d] illegal row index %d", __LINE__, ri));
293
294	pm  = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc;
295	tmp = xscale_pmcn_read(ri);
296	PMCDBG2(MDP,REA,2,"xscale-read id=%d -> %jd", ri, tmp);
297	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
298		*v = XSCALE_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp);
299	else
300		*v = tmp;
301
302	return 0;
303}
304
305static int
306xscale_write_pmc(int cpu, int ri, pmc_value_t v)
307{
308	struct pmc *pm;
309
310	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
311	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
312	KASSERT(ri >= 0 && ri < xscale_npmcs,
313	    ("[xscale,%d] illegal row-index %d", __LINE__, ri));
314
315	pm  = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc;
316
317	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
318		v = XSCALE_RELOAD_COUNT_TO_PERFCTR_VALUE(v);
319
320	PMCDBG3(MDP,WRI,1,"xscale-write cpu=%d ri=%d v=%jx", cpu, ri, v);
321
322	xscale_pmcn_write(ri, v);
323
324	return 0;
325}
326
327static int
328xscale_config_pmc(int cpu, int ri, struct pmc *pm)
329{
330	struct pmc_hw *phw;
331
332	PMCDBG3(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
333
334	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
335	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
336	KASSERT(ri >= 0 && ri < xscale_npmcs,
337	    ("[xscale,%d] illegal row-index %d", __LINE__, ri));
338
339	phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
340
341	KASSERT(pm == NULL || phw->phw_pmc == NULL,
342	    ("[xscale,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
343	    __LINE__, pm, phw->phw_pmc));
344
345	phw->phw_pmc = pm;
346
347	return 0;
348}
349
350static int
351xscale_start_pmc(int cpu, int ri)
352{
353	uint32_t pmnc, config, evtsel;
354        struct pmc *pm;
355        struct pmc_hw *phw;
356
357	phw    = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
358	pm     = phw->phw_pmc;
359	config = pm->pm_md.pm_xscale.pm_xscale_evsel;
360
361	/*
362	 * Configure the event selection.
363	 *
364	 * On the XScale 2nd Generation there's no EVTSEL register.
365	 */
366	if (xscale_npmcs == 2) {
367		pmnc = xscale_pmnc_read();
368		switch (ri) {
369		case 0:
370			pmnc &= ~XSCALE_PMNC_EVT0_MASK;
371			pmnc |= (config << 12) & XSCALE_PMNC_EVT0_MASK;
372			break;
373		case 1:
374			pmnc &= ~XSCALE_PMNC_EVT1_MASK;
375			pmnc |= (config << 20) & XSCALE_PMNC_EVT1_MASK;
376			break;
377		default:
378			/* XXX */
379			break;
380		}
381		xscale_pmnc_write(pmnc);
382	} else {
383		evtsel = xscale_evtsel_read();
384		switch (ri) {
385		case 0:
386			evtsel &= ~XSCALE_EVTSEL_EVT0_MASK;
387			evtsel |= config & XSCALE_EVTSEL_EVT0_MASK;
388			break;
389		case 1:
390			evtsel &= ~XSCALE_EVTSEL_EVT1_MASK;
391			evtsel |= (config << 8) & XSCALE_EVTSEL_EVT1_MASK;
392			break;
393		case 2:
394			evtsel &= ~XSCALE_EVTSEL_EVT2_MASK;
395			evtsel |= (config << 16) & XSCALE_EVTSEL_EVT2_MASK;
396			break;
397		case 3:
398			evtsel &= ~XSCALE_EVTSEL_EVT3_MASK;
399			evtsel |= (config << 24) & XSCALE_EVTSEL_EVT3_MASK;
400			break;
401		default:
402			/* XXX */
403			break;
404		}
405		xscale_evtsel_write(evtsel);
406	}
407	/*
408	 * Enable the PMC.
409	 *
410	 * Note that XScale provides only one bit to enable/disable _all_
411	 * performance monitoring units.
412	 */
413	pmnc = xscale_pmnc_read();
414	pmnc |= XSCALE_PMNC_ENABLE;
415	xscale_pmnc_write(pmnc);
416
417	return 0;
418}
419
420static int
421xscale_stop_pmc(int cpu, int ri)
422{
423	uint32_t pmnc, evtsel;
424        struct pmc *pm;
425        struct pmc_hw *phw;
426
427	phw    = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
428	pm     = phw->phw_pmc;
429
430	/*
431	 * Disable the PMCs.
432	 *
433	 * Note that XScale provides only one bit to enable/disable _all_
434	 * performance monitoring units.
435	 */
436	pmnc = xscale_pmnc_read();
437	pmnc &= ~XSCALE_PMNC_ENABLE;
438	xscale_pmnc_write(pmnc);
439	/*
440	 * A value of 0xff makes the corresponding PMU go into
441	 * power saving mode.
442	 */
443	if (xscale_npmcs == 2) {
444		pmnc = xscale_pmnc_read();
445		switch (ri) {
446		case 0:
447			pmnc |= XSCALE_PMNC_EVT0_MASK;
448			break;
449		case 1:
450			pmnc |= XSCALE_PMNC_EVT1_MASK;
451			break;
452		default:
453			/* XXX */
454			break;
455		}
456		xscale_pmnc_write(pmnc);
457	} else {
458		evtsel = xscale_evtsel_read();
459		switch (ri) {
460		case 0:
461			evtsel |= XSCALE_EVTSEL_EVT0_MASK;
462			break;
463		case 1:
464			evtsel |= XSCALE_EVTSEL_EVT1_MASK;
465			break;
466		case 2:
467			evtsel |= XSCALE_EVTSEL_EVT2_MASK;
468			break;
469		case 3:
470			evtsel |= XSCALE_EVTSEL_EVT3_MASK;
471			break;
472		default:
473			/* XXX */
474			break;
475		}
476		xscale_evtsel_write(evtsel);
477	}
478
479	return 0;
480}
481
482static int
483xscale_release_pmc(int cpu, int ri, struct pmc *pmc)
484{
485	struct pmc_hw *phw;
486
487	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
488	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
489	KASSERT(ri >= 0 && ri < xscale_npmcs,
490	    ("[xscale,%d] illegal row-index %d", __LINE__, ri));
491
492	phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
493	KASSERT(phw->phw_pmc == NULL,
494	    ("[xscale,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
495
496	return 0;
497}
498
499static int
500xscale_intr(int cpu, struct trapframe *tf)
501{
502	printf("intr\n");
503	return 0;
504}
505
506static int
507xscale_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
508{
509	int error;
510	struct pmc_hw *phw;
511	char xscale_name[PMC_NAME_MAX];
512
513	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
514	    ("[xscale,%d], illegal CPU %d", __LINE__, cpu));
515	KASSERT(ri >= 0 && ri < xscale_npmcs,
516	    ("[xscale,%d] row-index %d out of range", __LINE__, ri));
517
518	phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
519	snprintf(xscale_name, sizeof(xscale_name), "XSCALE-%d", ri);
520	if ((error = copystr(xscale_name, pi->pm_name, PMC_NAME_MAX,
521	    NULL)) != 0)
522		return error;
523	pi->pm_class = PMC_CLASS_XSCALE;
524	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
525		pi->pm_enabled = TRUE;
526		*ppmc          = phw->phw_pmc;
527	} else {
528		pi->pm_enabled = FALSE;
529		*ppmc	       = NULL;
530	}
531
532	return (0);
533}
534
535static int
536xscale_get_config(int cpu, int ri, struct pmc **ppm)
537{
538	*ppm = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc;
539
540	return 0;
541}
542
543/*
544 * XXX don't know what we should do here.
545 */
546static int
547xscale_switch_in(struct pmc_cpu *pc, struct pmc_process *pp)
548{
549	return 0;
550}
551
552static int
553xscale_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
554{
555	return 0;
556}
557
558static int
559xscale_pcpu_init(struct pmc_mdep *md, int cpu)
560{
561	int first_ri, i;
562	struct pmc_cpu *pc;
563	struct xscale_cpu *pac;
564	struct pmc_hw  *phw;
565
566	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
567	    ("[xscale,%d] wrong cpu number %d", __LINE__, cpu));
568	PMCDBG1(MDP,INI,1,"xscale-init cpu=%d", cpu);
569
570	xscale_pcpu[cpu] = pac = malloc(sizeof(struct xscale_cpu), M_PMC,
571	    M_WAITOK|M_ZERO);
572	pac->pc_xscalepmcs = malloc(sizeof(struct pmc_hw) * xscale_npmcs,
573	    M_PMC, M_WAITOK|M_ZERO);
574	pc = pmc_pcpu[cpu];
575	first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_XSCALE].pcd_ri;
576	KASSERT(pc != NULL, ("[xscale,%d] NULL per-cpu pointer", __LINE__));
577
578	for (i = 0, phw = pac->pc_xscalepmcs; i < xscale_npmcs; i++, phw++) {
579		phw->phw_state    = PMC_PHW_FLAG_IS_ENABLED |
580		    PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i);
581		phw->phw_pmc      = NULL;
582		pc->pc_hwpmcs[i + first_ri] = phw;
583	}
584
585	/*
586	 * Disable and put the PMUs into power save mode.
587	 */
588	if (xscale_npmcs == 2) {
589		xscale_pmnc_write(XSCALE_PMNC_EVT1_MASK |
590		    XSCALE_PMNC_EVT0_MASK);
591	} else {
592		xscale_evtsel_write(XSCALE_EVTSEL_EVT3_MASK |
593		    XSCALE_EVTSEL_EVT2_MASK | XSCALE_EVTSEL_EVT1_MASK |
594		    XSCALE_EVTSEL_EVT0_MASK);
595	}
596
597	return 0;
598}
599
600static int
601xscale_pcpu_fini(struct pmc_mdep *md, int cpu)
602{
603	return 0;
604}
605
606struct pmc_mdep *
607pmc_xscale_initialize()
608{
609	struct pmc_mdep *pmc_mdep;
610	struct pmc_classdep *pcd;
611	uint32_t idreg;
612
613	/* Get the Core Generation from CP15 */
614	__asm __volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (idreg));
615	xscale_gen = (idreg >> 13) & 0x3;
616	switch (xscale_gen) {
617	case 1:
618		xscale_npmcs = 2;
619		break;
620	case 2:
621	case 3:
622		xscale_npmcs = 4;
623		break;
624	default:
625		printf("%s: unknown XScale core generation\n", __func__);
626		return (NULL);
627	}
628	PMCDBG1(MDP,INI,1,"xscale-init npmcs=%d", xscale_npmcs);
629
630	/*
631	 * Allocate space for pointers to PMC HW descriptors and for
632	 * the MDEP structure used by MI code.
633	 */
634	xscale_pcpu = malloc(sizeof(struct xscale_cpu *) * pmc_cpu_max(), M_PMC,
635            M_WAITOK|M_ZERO);
636
637	/* Just one class */
638	pmc_mdep = pmc_mdep_alloc(1);
639
640	pmc_mdep->pmd_cputype = PMC_CPU_INTEL_XSCALE;
641
642	pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_XSCALE];
643	pcd->pcd_caps  = XSCALE_PMC_CAPS;
644	pcd->pcd_class = PMC_CLASS_XSCALE;
645	pcd->pcd_num   = xscale_npmcs;
646	pcd->pcd_ri    = pmc_mdep->pmd_npmc;
647	pcd->pcd_width = 32;
648
649	pcd->pcd_allocate_pmc   = xscale_allocate_pmc;
650	pcd->pcd_config_pmc     = xscale_config_pmc;
651	pcd->pcd_pcpu_fini      = xscale_pcpu_fini;
652	pcd->pcd_pcpu_init      = xscale_pcpu_init;
653	pcd->pcd_describe       = xscale_describe;
654	pcd->pcd_get_config	= xscale_get_config;
655	pcd->pcd_read_pmc       = xscale_read_pmc;
656	pcd->pcd_release_pmc    = xscale_release_pmc;
657	pcd->pcd_start_pmc      = xscale_start_pmc;
658	pcd->pcd_stop_pmc       = xscale_stop_pmc;
659	pcd->pcd_write_pmc      = xscale_write_pmc;
660
661	pmc_mdep->pmd_intr       = xscale_intr;
662	pmc_mdep->pmd_switch_in  = xscale_switch_in;
663	pmc_mdep->pmd_switch_out = xscale_switch_out;
664
665	pmc_mdep->pmd_npmc   += xscale_npmcs;
666
667	return (pmc_mdep);
668}
669
670void
671pmc_xscale_finalize(struct pmc_mdep *md)
672{
673}
674