1200928Srpaulo/*-
2200928Srpaulo * Copyright (c) 2009 Rui Paulo <rpaulo@FreeBSD.org>
3200928Srpaulo * All rights reserved.
4200928Srpaulo *
5200928Srpaulo * Redistribution and use in source and binary forms, with or without
6200928Srpaulo * modification, are permitted provided that the following conditions
7200928Srpaulo * are met:
8200928Srpaulo * 1. Redistributions of source code must retain the above copyright
9200928Srpaulo *    notice, this list of conditions and the following disclaimer.
10200928Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11200928Srpaulo *    notice, this list of conditions and the following disclaimer in the
12200928Srpaulo *    documentation and/or other materials provided with the distribution.
13200928Srpaulo *
14200928Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15200928Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16200928Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17200928Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18200928Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19200928Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20200928Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21200928Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22200928Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23200928Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24200928Srpaulo * SUCH DAMAGE.
25200928Srpaulo *
26200928Srpaulo */
27200928Srpaulo
28200928Srpaulo#include <sys/cdefs.h>
29200928Srpaulo__FBSDID("$FreeBSD: releng/11.0/sys/dev/hwpmc/hwpmc_xscale.c 298411 2016-04-21 15:38:28Z pfg $");
30200928Srpaulo
31200928Srpaulo#include <sys/param.h>
32200928Srpaulo#include <sys/systm.h>
33200928Srpaulo#include <sys/pmc.h>
34200928Srpaulo#include <sys/pmckern.h>
35200928Srpaulo
36200928Srpaulo#include <machine/pmc_mdep.h>
37200928Srpaulo/*
38200928Srpaulo * Support for the Intel XScale network processors
39200928Srpaulo *
40200928Srpaulo * XScale processors have up to now three generations.
41200928Srpaulo *
42200928Srpaulo * The first generation has two PMC; the event selection, interrupt config
43200928Srpaulo * and overflow flag setup are done by writing to the PMNC register.
44200928Srpaulo * It also has less monitoring events than the latter generations.
45200928Srpaulo *
46200928Srpaulo * The second and third generatiosn have four PMCs, one register for the event
47200928Srpaulo * selection, one register for the interrupt config and one register for
48200928Srpaulo * the overflow flags.
49200928Srpaulo */
50200928Srpaulostatic int xscale_npmcs;
51200928Srpaulostatic int xscale_gen;	/* XScale Core generation */
52200928Srpaulo
53200928Srpaulostruct xscale_event_code_map {
54200928Srpaulo	enum pmc_event	pe_ev;
55200928Srpaulo	uint8_t		pe_code;
56200928Srpaulo};
57200928Srpaulo
58200928Srpauloconst struct xscale_event_code_map xscale_event_codes[] = {
59200928Srpaulo	/* 1st and 2nd Generation XScale cores */
60200928Srpaulo	{ PMC_EV_XSCALE_IC_FETCH,		0x00 },
61200928Srpaulo	{ PMC_EV_XSCALE_IC_MISS,		0x01 },
62200928Srpaulo	{ PMC_EV_XSCALE_DATA_DEPENDENCY_STALLED,0x02 },
63200928Srpaulo	{ PMC_EV_XSCALE_ITLB_MISS,		0x03 },
64200928Srpaulo	{ PMC_EV_XSCALE_DTLB_MISS,		0x04 },
65200928Srpaulo	{ PMC_EV_XSCALE_BRANCH_RETIRED,		0x05 },
66200928Srpaulo	{ PMC_EV_XSCALE_BRANCH_MISPRED,		0x06 },
67200928Srpaulo	{ PMC_EV_XSCALE_INSTR_RETIRED,		0x07 },
68200928Srpaulo	{ PMC_EV_XSCALE_DC_FULL_CYCLE,		0x08 },
69200928Srpaulo	{ PMC_EV_XSCALE_DC_FULL_CONTIG, 	0x09 },
70200928Srpaulo	{ PMC_EV_XSCALE_DC_ACCESS,		0x0a },
71200928Srpaulo	{ PMC_EV_XSCALE_DC_MISS,		0x0b },
72200928Srpaulo	{ PMC_EV_XSCALE_DC_WRITEBACK,		0x0c },
73200928Srpaulo	{ PMC_EV_XSCALE_PC_CHANGE,		0x0d },
74200928Srpaulo	/* 3rd Generation XScale cores */
75200928Srpaulo	{ PMC_EV_XSCALE_BRANCH_RETIRED_ALL,	0x0e },
76200928Srpaulo	{ PMC_EV_XSCALE_INSTR_CYCLE,		0x0f },
77200928Srpaulo	{ PMC_EV_XSCALE_CP_STALL,		0x17 },
78200928Srpaulo	{ PMC_EV_XSCALE_PC_CHANGE_ALL,		0x18 },
79200928Srpaulo	{ PMC_EV_XSCALE_PIPELINE_FLUSH,		0x19 },
80200928Srpaulo	{ PMC_EV_XSCALE_BACKEND_STALL,		0x1a },
81200928Srpaulo	{ PMC_EV_XSCALE_MULTIPLIER_USE,		0x1b },
82200928Srpaulo	{ PMC_EV_XSCALE_MULTIPLIER_STALLED,	0x1c },
83200928Srpaulo	{ PMC_EV_XSCALE_DATA_CACHE_STALLED,	0x1e },
84200928Srpaulo	{ PMC_EV_XSCALE_L2_CACHE_REQ,		0x20 },
85200928Srpaulo	{ PMC_EV_XSCALE_L2_CACHE_MISS,		0x23 },
86200928Srpaulo	{ PMC_EV_XSCALE_ADDRESS_BUS_TRANS,	0x40 },
87200928Srpaulo	{ PMC_EV_XSCALE_SELF_ADDRESS_BUS_TRANS,	0x41 },
88200928Srpaulo	{ PMC_EV_XSCALE_DATA_BUS_TRANS,		0x48 },
89200928Srpaulo};
90200928Srpaulo
91200928Srpaulo/*
92200928Srpaulo * Per-processor information.
93200928Srpaulo */
94200928Srpaulostruct xscale_cpu {
95200928Srpaulo	struct pmc_hw   *pc_xscalepmcs;
96200928Srpaulo};
97200928Srpaulo
98200928Srpaulostatic struct xscale_cpu **xscale_pcpu;
99200928Srpaulo
100200928Srpaulo/*
101200928Srpaulo * Performance Monitor Control Register
102200928Srpaulo */
103200928Srpaulostatic __inline uint32_t
104200928Srpauloxscale_pmnc_read(void)
105200928Srpaulo{
106200928Srpaulo	uint32_t reg;
107200928Srpaulo
108200928Srpaulo	__asm __volatile("mrc p14, 0, %0, c0, c1, 0" : "=r" (reg));
109200928Srpaulo
110200928Srpaulo	return (reg);
111200928Srpaulo}
112200928Srpaulo
113200928Srpaulostatic __inline void
114200928Srpauloxscale_pmnc_write(uint32_t reg)
115200928Srpaulo{
116200928Srpaulo	__asm __volatile("mcr p14, 0, %0, c0, c1, 0" : : "r" (reg));
117200928Srpaulo}
118200928Srpaulo
119200928Srpaulo/*
120200928Srpaulo * Clock Counter Register
121200928Srpaulo */
122200928Srpaulostatic __inline uint32_t
123200928Srpauloxscale_ccnt_read(void)
124200928Srpaulo{
125200928Srpaulo	uint32_t reg;
126200928Srpaulo
127200928Srpaulo	__asm __volatile("mrc p14, 0, %0, c1, c1, 0" : "=r" (reg));
128200928Srpaulo
129200928Srpaulo	return (reg);
130200928Srpaulo}
131200928Srpaulo
132200928Srpaulostatic __inline void
133200928Srpauloxscale_ccnt_write(uint32_t reg)
134200928Srpaulo{
135200928Srpaulo	__asm __volatile("mcr p14, 0, %0, c1, c1, 0" : : "r" (reg));
136200928Srpaulo}
137200928Srpaulo
138200928Srpaulo/*
139200928Srpaulo * Interrupt Enable Register
140200928Srpaulo */
141200928Srpaulostatic __inline uint32_t
142200928Srpauloxscale_inten_read(void)
143200928Srpaulo{
144200928Srpaulo	uint32_t reg;
145200928Srpaulo
146200928Srpaulo	__asm __volatile("mrc p14, 0, %0, c4, c1, 0" : "=r" (reg));
147200928Srpaulo
148200928Srpaulo	return (reg);
149200928Srpaulo}
150200928Srpaulo
151200928Srpaulostatic __inline void
152200928Srpauloxscale_inten_write(uint32_t reg)
153200928Srpaulo{
154200928Srpaulo	__asm __volatile("mcr p14, 0, %0, c4, c1, 0" : : "r" (reg));
155200928Srpaulo}
156200928Srpaulo
157200928Srpaulo/*
158200928Srpaulo * Overflow Flag Register
159200928Srpaulo */
160200928Srpaulostatic __inline uint32_t
161200928Srpauloxscale_flag_read(void)
162200928Srpaulo{
163200928Srpaulo	uint32_t reg;
164200928Srpaulo
165200928Srpaulo	__asm __volatile("mrc p14, 0, %0, c5, c1, 0" : "=r" (reg));
166200928Srpaulo
167200928Srpaulo	return (reg);
168200928Srpaulo}
169200928Srpaulo
170200928Srpaulostatic __inline void
171200928Srpauloxscale_flag_write(uint32_t reg)
172200928Srpaulo{
173200928Srpaulo	__asm __volatile("mcr p14, 0, %0, c5, c1, 0" : : "r" (reg));
174200928Srpaulo}
175200928Srpaulo
176200928Srpaulo/*
177200928Srpaulo * Event Selection Register
178200928Srpaulo */
179200928Srpaulostatic __inline uint32_t
180200928Srpauloxscale_evtsel_read(void)
181200928Srpaulo{
182200928Srpaulo	uint32_t reg;
183200928Srpaulo
184200928Srpaulo	__asm __volatile("mrc p14, 0, %0, c8, c1, 0" : "=r" (reg));
185200928Srpaulo
186200928Srpaulo	return (reg);
187200928Srpaulo}
188200928Srpaulo
189200928Srpaulostatic __inline void
190200928Srpauloxscale_evtsel_write(uint32_t reg)
191200928Srpaulo{
192200928Srpaulo	__asm __volatile("mcr p14, 0, %0, c8, c1, 0" : : "r" (reg));
193200928Srpaulo}
194200928Srpaulo
195200928Srpaulo/*
196200928Srpaulo * Performance Count Register N
197200928Srpaulo */
198200928Srpaulostatic uint32_t
199200928Srpauloxscale_pmcn_read(unsigned int pmc)
200200928Srpaulo{
201200928Srpaulo	uint32_t reg = 0;
202200928Srpaulo
203200928Srpaulo	KASSERT(pmc < 4, ("[xscale,%d] illegal PMC number %d", __LINE__, pmc));
204200928Srpaulo
205200928Srpaulo	switch (pmc) {
206200928Srpaulo	case 0:
207200928Srpaulo		__asm __volatile("mrc p14, 0, %0, c0, c2, 0" : "=r" (reg));
208200928Srpaulo		break;
209200928Srpaulo	case 1:
210200928Srpaulo		__asm __volatile("mrc p14, 0, %0, c1, c2, 0" : "=r" (reg));
211200928Srpaulo		break;
212200928Srpaulo	case 2:
213200928Srpaulo		__asm __volatile("mrc p14, 0, %0, c2, c2, 0" : "=r" (reg));
214200928Srpaulo		break;
215200928Srpaulo	case 3:
216200928Srpaulo		__asm __volatile("mrc p14, 0, %0, c3, c2, 0" : "=r" (reg));
217200928Srpaulo		break;
218200928Srpaulo	}
219200928Srpaulo
220200928Srpaulo	return (reg);
221200928Srpaulo}
222200928Srpaulo
223200928Srpaulostatic uint32_t
224200928Srpauloxscale_pmcn_write(unsigned int pmc, uint32_t reg)
225200928Srpaulo{
226200928Srpaulo
227200928Srpaulo	KASSERT(pmc < 4, ("[xscale,%d] illegal PMC number %d", __LINE__, pmc));
228200928Srpaulo
229200928Srpaulo	switch (pmc) {
230200928Srpaulo	case 0:
231200928Srpaulo		__asm __volatile("mcr p14, 0, %0, c0, c2, 0" : : "r" (reg));
232200928Srpaulo		break;
233200928Srpaulo	case 1:
234200928Srpaulo		__asm __volatile("mcr p14, 0, %0, c1, c2, 0" : : "r" (reg));
235200928Srpaulo		break;
236200928Srpaulo	case 2:
237200928Srpaulo		__asm __volatile("mcr p14, 0, %0, c2, c2, 0" : : "r" (reg));
238200928Srpaulo		break;
239200928Srpaulo	case 3:
240200928Srpaulo		__asm __volatile("mcr p14, 0, %0, c3, c2, 0" : : "r" (reg));
241200928Srpaulo		break;
242200928Srpaulo	}
243200928Srpaulo
244200928Srpaulo	return (reg);
245200928Srpaulo}
246200928Srpaulo
247200928Srpaulostatic int
248200928Srpauloxscale_allocate_pmc(int cpu, int ri, struct pmc *pm,
249200928Srpaulo  const struct pmc_op_pmcallocate *a)
250200928Srpaulo{
251200928Srpaulo	enum pmc_event pe;
252200928Srpaulo	uint32_t caps, config;
253200928Srpaulo	int i;
254200928Srpaulo
255200928Srpaulo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
256200928Srpaulo	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
257200928Srpaulo	KASSERT(ri >= 0 && ri < xscale_npmcs,
258200928Srpaulo	    ("[xscale,%d] illegal row index %d", __LINE__, ri));
259200928Srpaulo
260200928Srpaulo	caps = a->pm_caps;
261200928Srpaulo	if (a->pm_class != PMC_CLASS_XSCALE)
262200928Srpaulo		return (EINVAL);
263200928Srpaulo	pe = a->pm_ev;
264298411Spfg	for (i = 0; i < nitems(xscale_event_codes); i++) {
265200928Srpaulo		if (xscale_event_codes[i].pe_ev == pe) {
266200928Srpaulo			config = xscale_event_codes[i].pe_code;
267200928Srpaulo			break;
268200928Srpaulo		}
269200928Srpaulo	}
270298411Spfg	if (i == nitems(xscale_event_codes))
271200928Srpaulo		return EINVAL;
272200928Srpaulo	/* Generation 1 has fewer events */
273200928Srpaulo	if (xscale_gen == 1 && i > PMC_EV_XSCALE_PC_CHANGE)
274200928Srpaulo		return EINVAL;
275200928Srpaulo	pm->pm_md.pm_xscale.pm_xscale_evsel = config;
276200928Srpaulo
277282658Sjhb	PMCDBG2(MDP,ALL,2,"xscale-allocate ri=%d -> config=0x%x", ri, config);
278200928Srpaulo
279200928Srpaulo	return 0;
280200928Srpaulo}
281200928Srpaulo
282200928Srpaulo
283200928Srpaulostatic int
284200928Srpauloxscale_read_pmc(int cpu, int ri, pmc_value_t *v)
285200928Srpaulo{
286200928Srpaulo	struct pmc *pm;
287200928Srpaulo	pmc_value_t tmp;
288200928Srpaulo
289200928Srpaulo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
290200928Srpaulo	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
291200928Srpaulo	KASSERT(ri >= 0 && ri < xscale_npmcs,
292200928Srpaulo	    ("[xscale,%d] illegal row index %d", __LINE__, ri));
293200928Srpaulo
294200928Srpaulo	pm  = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc;
295200928Srpaulo	tmp = xscale_pmcn_read(ri);
296282658Sjhb	PMCDBG2(MDP,REA,2,"xscale-read id=%d -> %jd", ri, tmp);
297200928Srpaulo	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
298200928Srpaulo		*v = XSCALE_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp);
299200928Srpaulo	else
300200928Srpaulo		*v = tmp;
301200928Srpaulo
302200928Srpaulo	return 0;
303200928Srpaulo}
304200928Srpaulo
305200928Srpaulostatic int
306200928Srpauloxscale_write_pmc(int cpu, int ri, pmc_value_t v)
307200928Srpaulo{
308200928Srpaulo	struct pmc *pm;
309200928Srpaulo
310200928Srpaulo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
311200928Srpaulo	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
312200928Srpaulo	KASSERT(ri >= 0 && ri < xscale_npmcs,
313200928Srpaulo	    ("[xscale,%d] illegal row-index %d", __LINE__, ri));
314200928Srpaulo
315200928Srpaulo	pm  = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc;
316200928Srpaulo
317200928Srpaulo	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
318200928Srpaulo		v = XSCALE_RELOAD_COUNT_TO_PERFCTR_VALUE(v);
319200928Srpaulo
320282658Sjhb	PMCDBG3(MDP,WRI,1,"xscale-write cpu=%d ri=%d v=%jx", cpu, ri, v);
321200928Srpaulo
322200928Srpaulo	xscale_pmcn_write(ri, v);
323200928Srpaulo
324200928Srpaulo	return 0;
325200928Srpaulo}
326200928Srpaulo
327200928Srpaulostatic int
328200928Srpauloxscale_config_pmc(int cpu, int ri, struct pmc *pm)
329200928Srpaulo{
330200928Srpaulo	struct pmc_hw *phw;
331200928Srpaulo
332282658Sjhb	PMCDBG3(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
333200928Srpaulo
334200928Srpaulo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
335200928Srpaulo	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
336200928Srpaulo	KASSERT(ri >= 0 && ri < xscale_npmcs,
337200928Srpaulo	    ("[xscale,%d] illegal row-index %d", __LINE__, ri));
338200928Srpaulo
339200928Srpaulo	phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
340200928Srpaulo
341200928Srpaulo	KASSERT(pm == NULL || phw->phw_pmc == NULL,
342200928Srpaulo	    ("[xscale,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
343200928Srpaulo	    __LINE__, pm, phw->phw_pmc));
344200928Srpaulo
345200928Srpaulo	phw->phw_pmc = pm;
346200928Srpaulo
347200928Srpaulo	return 0;
348200928Srpaulo}
349200928Srpaulo
350200928Srpaulostatic int
351200928Srpauloxscale_start_pmc(int cpu, int ri)
352200928Srpaulo{
353200928Srpaulo	uint32_t pmnc, config, evtsel;
354200928Srpaulo        struct pmc *pm;
355200928Srpaulo        struct pmc_hw *phw;
356200928Srpaulo
357200928Srpaulo	phw    = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
358200928Srpaulo	pm     = phw->phw_pmc;
359200928Srpaulo	config = pm->pm_md.pm_xscale.pm_xscale_evsel;
360200928Srpaulo
361200928Srpaulo	/*
362200928Srpaulo	 * Configure the event selection.
363200928Srpaulo	 *
364200928Srpaulo	 * On the XScale 2nd Generation there's no EVTSEL register.
365200928Srpaulo	 */
366200928Srpaulo	if (xscale_npmcs == 2) {
367200928Srpaulo		pmnc = xscale_pmnc_read();
368200928Srpaulo		switch (ri) {
369200928Srpaulo		case 0:
370200928Srpaulo			pmnc &= ~XSCALE_PMNC_EVT0_MASK;
371200928Srpaulo			pmnc |= (config << 12) & XSCALE_PMNC_EVT0_MASK;
372200928Srpaulo			break;
373200928Srpaulo		case 1:
374200928Srpaulo			pmnc &= ~XSCALE_PMNC_EVT1_MASK;
375200928Srpaulo			pmnc |= (config << 20) & XSCALE_PMNC_EVT1_MASK;
376200928Srpaulo			break;
377200928Srpaulo		default:
378200928Srpaulo			/* XXX */
379200928Srpaulo			break;
380200928Srpaulo		}
381200928Srpaulo		xscale_pmnc_write(pmnc);
382200928Srpaulo	} else {
383200928Srpaulo		evtsel = xscale_evtsel_read();
384200928Srpaulo		switch (ri) {
385200928Srpaulo		case 0:
386200928Srpaulo			evtsel &= ~XSCALE_EVTSEL_EVT0_MASK;
387200928Srpaulo			evtsel |= config & XSCALE_EVTSEL_EVT0_MASK;
388200928Srpaulo			break;
389200928Srpaulo		case 1:
390200928Srpaulo			evtsel &= ~XSCALE_EVTSEL_EVT1_MASK;
391200928Srpaulo			evtsel |= (config << 8) & XSCALE_EVTSEL_EVT1_MASK;
392200928Srpaulo			break;
393200928Srpaulo		case 2:
394200928Srpaulo			evtsel &= ~XSCALE_EVTSEL_EVT2_MASK;
395200928Srpaulo			evtsel |= (config << 16) & XSCALE_EVTSEL_EVT2_MASK;
396200928Srpaulo			break;
397200928Srpaulo		case 3:
398200928Srpaulo			evtsel &= ~XSCALE_EVTSEL_EVT3_MASK;
399200928Srpaulo			evtsel |= (config << 24) & XSCALE_EVTSEL_EVT3_MASK;
400200928Srpaulo			break;
401200928Srpaulo		default:
402200928Srpaulo			/* XXX */
403200928Srpaulo			break;
404200928Srpaulo		}
405200928Srpaulo		xscale_evtsel_write(evtsel);
406200928Srpaulo	}
407200928Srpaulo	/*
408200928Srpaulo	 * Enable the PMC.
409200928Srpaulo	 *
410200928Srpaulo	 * Note that XScale provides only one bit to enable/disable _all_
411200928Srpaulo	 * performance monitoring units.
412200928Srpaulo	 */
413200928Srpaulo	pmnc = xscale_pmnc_read();
414200928Srpaulo	pmnc |= XSCALE_PMNC_ENABLE;
415200928Srpaulo	xscale_pmnc_write(pmnc);
416200928Srpaulo
417200928Srpaulo	return 0;
418200928Srpaulo}
419200928Srpaulo
420200928Srpaulostatic int
421200928Srpauloxscale_stop_pmc(int cpu, int ri)
422200928Srpaulo{
423200928Srpaulo	uint32_t pmnc, evtsel;
424200928Srpaulo        struct pmc *pm;
425200928Srpaulo        struct pmc_hw *phw;
426200928Srpaulo
427200928Srpaulo	phw    = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
428200928Srpaulo	pm     = phw->phw_pmc;
429200928Srpaulo
430200928Srpaulo	/*
431200928Srpaulo	 * Disable the PMCs.
432200928Srpaulo	 *
433200928Srpaulo	 * Note that XScale provides only one bit to enable/disable _all_
434200928Srpaulo	 * performance monitoring units.
435200928Srpaulo	 */
436200928Srpaulo	pmnc = xscale_pmnc_read();
437200928Srpaulo	pmnc &= ~XSCALE_PMNC_ENABLE;
438200928Srpaulo	xscale_pmnc_write(pmnc);
439200928Srpaulo	/*
440200928Srpaulo	 * A value of 0xff makes the corresponding PMU go into
441200928Srpaulo	 * power saving mode.
442200928Srpaulo	 */
443200928Srpaulo	if (xscale_npmcs == 2) {
444200928Srpaulo		pmnc = xscale_pmnc_read();
445200928Srpaulo		switch (ri) {
446200928Srpaulo		case 0:
447200928Srpaulo			pmnc |= XSCALE_PMNC_EVT0_MASK;
448200928Srpaulo			break;
449200928Srpaulo		case 1:
450200928Srpaulo			pmnc |= XSCALE_PMNC_EVT1_MASK;
451200928Srpaulo			break;
452200928Srpaulo		default:
453200928Srpaulo			/* XXX */
454200928Srpaulo			break;
455200928Srpaulo		}
456200928Srpaulo		xscale_pmnc_write(pmnc);
457200928Srpaulo	} else {
458200928Srpaulo		evtsel = xscale_evtsel_read();
459200928Srpaulo		switch (ri) {
460200928Srpaulo		case 0:
461200928Srpaulo			evtsel |= XSCALE_EVTSEL_EVT0_MASK;
462200928Srpaulo			break;
463200928Srpaulo		case 1:
464200928Srpaulo			evtsel |= XSCALE_EVTSEL_EVT1_MASK;
465200928Srpaulo			break;
466200928Srpaulo		case 2:
467200928Srpaulo			evtsel |= XSCALE_EVTSEL_EVT2_MASK;
468200928Srpaulo			break;
469200928Srpaulo		case 3:
470200928Srpaulo			evtsel |= XSCALE_EVTSEL_EVT3_MASK;
471200928Srpaulo			break;
472200928Srpaulo		default:
473200928Srpaulo			/* XXX */
474200928Srpaulo			break;
475200928Srpaulo		}
476200928Srpaulo		xscale_evtsel_write(evtsel);
477200928Srpaulo	}
478200928Srpaulo
479200928Srpaulo	return 0;
480200928Srpaulo}
481200928Srpaulo
482200928Srpaulostatic int
483200928Srpauloxscale_release_pmc(int cpu, int ri, struct pmc *pmc)
484200928Srpaulo{
485200928Srpaulo	struct pmc_hw *phw;
486200928Srpaulo
487200928Srpaulo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
488200928Srpaulo	    ("[xscale,%d] illegal CPU value %d", __LINE__, cpu));
489200928Srpaulo	KASSERT(ri >= 0 && ri < xscale_npmcs,
490200928Srpaulo	    ("[xscale,%d] illegal row-index %d", __LINE__, ri));
491200928Srpaulo
492200928Srpaulo	phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
493200928Srpaulo	KASSERT(phw->phw_pmc == NULL,
494200928Srpaulo	    ("[xscale,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
495200928Srpaulo
496200928Srpaulo	return 0;
497200928Srpaulo}
498200928Srpaulo
499200928Srpaulostatic int
500200928Srpauloxscale_intr(int cpu, struct trapframe *tf)
501200928Srpaulo{
502200928Srpaulo	printf("intr\n");
503200928Srpaulo	return 0;
504200928Srpaulo}
505200928Srpaulo
506200928Srpaulostatic int
507200928Srpauloxscale_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
508200928Srpaulo{
509200928Srpaulo	int error;
510200928Srpaulo	struct pmc_hw *phw;
511200928Srpaulo	char xscale_name[PMC_NAME_MAX];
512200928Srpaulo
513200928Srpaulo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
514200928Srpaulo	    ("[xscale,%d], illegal CPU %d", __LINE__, cpu));
515200928Srpaulo	KASSERT(ri >= 0 && ri < xscale_npmcs,
516200928Srpaulo	    ("[xscale,%d] row-index %d out of range", __LINE__, ri));
517200928Srpaulo
518200928Srpaulo	phw = &xscale_pcpu[cpu]->pc_xscalepmcs[ri];
519200928Srpaulo	snprintf(xscale_name, sizeof(xscale_name), "XSCALE-%d", ri);
520200928Srpaulo	if ((error = copystr(xscale_name, pi->pm_name, PMC_NAME_MAX,
521200928Srpaulo	    NULL)) != 0)
522200928Srpaulo		return error;
523200928Srpaulo	pi->pm_class = PMC_CLASS_XSCALE;
524200928Srpaulo	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
525200928Srpaulo		pi->pm_enabled = TRUE;
526200928Srpaulo		*ppmc          = phw->phw_pmc;
527200928Srpaulo	} else {
528200928Srpaulo		pi->pm_enabled = FALSE;
529200928Srpaulo		*ppmc	       = NULL;
530200928Srpaulo	}
531200928Srpaulo
532200928Srpaulo	return (0);
533200928Srpaulo}
534200928Srpaulo
535200928Srpaulostatic int
536200928Srpauloxscale_get_config(int cpu, int ri, struct pmc **ppm)
537200928Srpaulo{
538200928Srpaulo	*ppm = xscale_pcpu[cpu]->pc_xscalepmcs[ri].phw_pmc;
539200928Srpaulo
540200928Srpaulo	return 0;
541200928Srpaulo}
542200928Srpaulo
543200928Srpaulo/*
544200928Srpaulo * XXX don't know what we should do here.
545200928Srpaulo */
546200928Srpaulostatic int
547200928Srpauloxscale_switch_in(struct pmc_cpu *pc, struct pmc_process *pp)
548200928Srpaulo{
549200928Srpaulo	return 0;
550200928Srpaulo}
551200928Srpaulo
552200928Srpaulostatic int
553200928Srpauloxscale_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
554200928Srpaulo{
555200928Srpaulo	return 0;
556200928Srpaulo}
557200928Srpaulo
558200928Srpaulostatic int
559200928Srpauloxscale_pcpu_init(struct pmc_mdep *md, int cpu)
560200928Srpaulo{
561200928Srpaulo	int first_ri, i;
562200928Srpaulo	struct pmc_cpu *pc;
563200928Srpaulo	struct xscale_cpu *pac;
564200928Srpaulo	struct pmc_hw  *phw;
565200928Srpaulo
566200928Srpaulo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
567200928Srpaulo	    ("[xscale,%d] wrong cpu number %d", __LINE__, cpu));
568282658Sjhb	PMCDBG1(MDP,INI,1,"xscale-init cpu=%d", cpu);
569200928Srpaulo
570200928Srpaulo	xscale_pcpu[cpu] = pac = malloc(sizeof(struct xscale_cpu), M_PMC,
571200928Srpaulo	    M_WAITOK|M_ZERO);
572200928Srpaulo	pac->pc_xscalepmcs = malloc(sizeof(struct pmc_hw) * xscale_npmcs,
573200928Srpaulo	    M_PMC, M_WAITOK|M_ZERO);
574200928Srpaulo	pc = pmc_pcpu[cpu];
575200928Srpaulo	first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_XSCALE].pcd_ri;
576200928Srpaulo	KASSERT(pc != NULL, ("[xscale,%d] NULL per-cpu pointer", __LINE__));
577200928Srpaulo
578200928Srpaulo	for (i = 0, phw = pac->pc_xscalepmcs; i < xscale_npmcs; i++, phw++) {
579200928Srpaulo		phw->phw_state    = PMC_PHW_FLAG_IS_ENABLED |
580200928Srpaulo		    PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i);
581200928Srpaulo		phw->phw_pmc      = NULL;
582200928Srpaulo		pc->pc_hwpmcs[i + first_ri] = phw;
583200928Srpaulo	}
584200928Srpaulo
585200928Srpaulo	/*
586200928Srpaulo	 * Disable and put the PMUs into power save mode.
587200928Srpaulo	 */
588200928Srpaulo	if (xscale_npmcs == 2) {
589200928Srpaulo		xscale_pmnc_write(XSCALE_PMNC_EVT1_MASK |
590200928Srpaulo		    XSCALE_PMNC_EVT0_MASK);
591200928Srpaulo	} else {
592200928Srpaulo		xscale_evtsel_write(XSCALE_EVTSEL_EVT3_MASK |
593200928Srpaulo		    XSCALE_EVTSEL_EVT2_MASK | XSCALE_EVTSEL_EVT1_MASK |
594200928Srpaulo		    XSCALE_EVTSEL_EVT0_MASK);
595200928Srpaulo	}
596200928Srpaulo
597200928Srpaulo	return 0;
598200928Srpaulo}
599200928Srpaulo
600200928Srpaulostatic int
601200928Srpauloxscale_pcpu_fini(struct pmc_mdep *md, int cpu)
602200928Srpaulo{
603200928Srpaulo	return 0;
604200928Srpaulo}
605200928Srpaulo
606200928Srpaulostruct pmc_mdep *
607200928Srpaulopmc_xscale_initialize()
608200928Srpaulo{
609200928Srpaulo	struct pmc_mdep *pmc_mdep;
610200928Srpaulo	struct pmc_classdep *pcd;
611200928Srpaulo	uint32_t idreg;
612200928Srpaulo
613200928Srpaulo	/* Get the Core Generation from CP15 */
614200928Srpaulo	__asm __volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (idreg));
615200928Srpaulo	xscale_gen = (idreg >> 13) & 0x3;
616200928Srpaulo	switch (xscale_gen) {
617200928Srpaulo	case 1:
618200928Srpaulo		xscale_npmcs = 2;
619200928Srpaulo		break;
620200928Srpaulo	case 2:
621200928Srpaulo	case 3:
622200928Srpaulo		xscale_npmcs = 4;
623200928Srpaulo		break;
624200928Srpaulo	default:
625200928Srpaulo		printf("%s: unknown XScale core generation\n", __func__);
626200928Srpaulo		return (NULL);
627200928Srpaulo	}
628282658Sjhb	PMCDBG1(MDP,INI,1,"xscale-init npmcs=%d", xscale_npmcs);
629200928Srpaulo
630200928Srpaulo	/*
631200928Srpaulo	 * Allocate space for pointers to PMC HW descriptors and for
632200928Srpaulo	 * the MDEP structure used by MI code.
633200928Srpaulo	 */
634200928Srpaulo	xscale_pcpu = malloc(sizeof(struct xscale_cpu *) * pmc_cpu_max(), M_PMC,
635200928Srpaulo            M_WAITOK|M_ZERO);
636200928Srpaulo
637200928Srpaulo	/* Just one class */
638233628Sfabient	pmc_mdep = pmc_mdep_alloc(1);
639200928Srpaulo
640200928Srpaulo	pmc_mdep->pmd_cputype = PMC_CPU_INTEL_XSCALE;
641200928Srpaulo
642200928Srpaulo	pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_XSCALE];
643200928Srpaulo	pcd->pcd_caps  = XSCALE_PMC_CAPS;
644200928Srpaulo	pcd->pcd_class = PMC_CLASS_XSCALE;
645200928Srpaulo	pcd->pcd_num   = xscale_npmcs;
646200928Srpaulo	pcd->pcd_ri    = pmc_mdep->pmd_npmc;
647200928Srpaulo	pcd->pcd_width = 32;
648200928Srpaulo
649200928Srpaulo	pcd->pcd_allocate_pmc   = xscale_allocate_pmc;
650200928Srpaulo	pcd->pcd_config_pmc     = xscale_config_pmc;
651200928Srpaulo	pcd->pcd_pcpu_fini      = xscale_pcpu_fini;
652200928Srpaulo	pcd->pcd_pcpu_init      = xscale_pcpu_init;
653200928Srpaulo	pcd->pcd_describe       = xscale_describe;
654200928Srpaulo	pcd->pcd_get_config	= xscale_get_config;
655200928Srpaulo	pcd->pcd_read_pmc       = xscale_read_pmc;
656200928Srpaulo	pcd->pcd_release_pmc    = xscale_release_pmc;
657200928Srpaulo	pcd->pcd_start_pmc      = xscale_start_pmc;
658200928Srpaulo	pcd->pcd_stop_pmc       = xscale_stop_pmc;
659200928Srpaulo	pcd->pcd_write_pmc      = xscale_write_pmc;
660200928Srpaulo
661200928Srpaulo	pmc_mdep->pmd_intr       = xscale_intr;
662200928Srpaulo	pmc_mdep->pmd_switch_in  = xscale_switch_in;
663200928Srpaulo	pmc_mdep->pmd_switch_out = xscale_switch_out;
664200928Srpaulo
665200928Srpaulo	pmc_mdep->pmd_npmc   += xscale_npmcs;
666200928Srpaulo
667200928Srpaulo	return (pmc_mdep);
668200928Srpaulo}
669200928Srpaulo
670200928Srpaulovoid
671200928Srpaulopmc_xscale_finalize(struct pmc_mdep *md)
672200928Srpaulo{
673200928Srpaulo}
674