1145256Sjkoshy/*-
2183266Sjkoshy * Copyright (c) 2003-2005,2008 Joseph Koshy
3174395Sjkoshy * Copyright (c) 2007 The FreeBSD Foundation
4145256Sjkoshy * All rights reserved.
5145256Sjkoshy *
6174395Sjkoshy * Portions of this software were developed by A. Joseph Koshy under
7174395Sjkoshy * sponsorship from the FreeBSD Foundation and Google, Inc.
8174395Sjkoshy *
9145256Sjkoshy * Redistribution and use in source and binary forms, with or without
10145256Sjkoshy * modification, are permitted provided that the following conditions
11145256Sjkoshy * are met:
12145256Sjkoshy * 1. Redistributions of source code must retain the above copyright
13145256Sjkoshy *    notice, this list of conditions and the following disclaimer.
14145256Sjkoshy * 2. Redistributions in binary form must reproduce the above copyright
15145256Sjkoshy *    notice, this list of conditions and the following disclaimer in the
16145256Sjkoshy *    documentation and/or other materials provided with the distribution.
17145256Sjkoshy *
18145256Sjkoshy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19145256Sjkoshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20145256Sjkoshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21145256Sjkoshy * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22145256Sjkoshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23145256Sjkoshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24145256Sjkoshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25145256Sjkoshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26145256Sjkoshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27145256Sjkoshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28145256Sjkoshy * SUCH DAMAGE.
29145256Sjkoshy */
30145256Sjkoshy
31145256Sjkoshy#include <sys/cdefs.h>
32145256Sjkoshy__FBSDID("$FreeBSD$");
33145256Sjkoshy
34145256Sjkoshy#include <sys/param.h>
35196224Sjhb#include <sys/bus.h>
36145256Sjkoshy#include <sys/lock.h>
37145256Sjkoshy#include <sys/mutex.h>
38145338Smarcel#include <sys/pmc.h>
39145256Sjkoshy#include <sys/pmckern.h>
40145256Sjkoshy#include <sys/smp.h>
41145256Sjkoshy#include <sys/systm.h>
42145256Sjkoshy
43196224Sjhb#include <machine/intr_machdep.h>
44196224Sjhb#include <machine/apicvar.h>
45174395Sjkoshy#include <machine/cpu.h>
46147191Sjkoshy#include <machine/cpufunc.h>
47185341Sjkim#include <machine/cputypes.h>
48145256Sjkoshy#include <machine/md_var.h>
49147191Sjkoshy#include <machine/pmc_mdep.h>
50147191Sjkoshy#include <machine/specialreg.h>
51145256Sjkoshy
52145256Sjkoshy/*
53145256Sjkoshy * PENTIUM PRO SUPPORT
54147191Sjkoshy *
55147191Sjkoshy * Quirks:
56147191Sjkoshy *
57147191Sjkoshy * - Both PMCs are enabled by a single bit P6_EVSEL_EN in performance
58147191Sjkoshy *   counter '0'.  This bit needs to be '1' if any of the two
59147191Sjkoshy *   performance counters are in use.  Perf counters can also be
60147191Sjkoshy *   switched off by writing zeros to their EVSEL register.
61147191Sjkoshy *
62147191Sjkoshy * - While the width of these counters is 40 bits, we do not appear to
63147191Sjkoshy *   have a way of writing 40 bits to the counter MSRs.  A WRMSR
64147191Sjkoshy *   instruction will sign extend bit 31 of the value being written to
65147191Sjkoshy *   the perf counter -- a value of 0x80000000 written to an perf
66147191Sjkoshy *   counter register will be sign extended to 0xFF80000000.
67147191Sjkoshy *
68147191Sjkoshy *   This quirk primarily affects thread-mode PMCs in counting mode, as
69147191Sjkoshy *   these PMCs read and write PMC registers at every context switch.
70145256Sjkoshy */
71145256Sjkoshy
72145256Sjkoshystruct p6pmc_descr {
73145256Sjkoshy	struct pmc_descr pm_descr; /* common information */
74145256Sjkoshy	uint32_t	pm_pmc_msr;
75145256Sjkoshy	uint32_t	pm_evsel_msr;
76145256Sjkoshy};
77145256Sjkoshy
78145256Sjkoshystatic struct p6pmc_descr p6_pmcdesc[P6_NPMCS] = {
79145256Sjkoshy
80145256Sjkoshy#define	P6_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | PMC_CAP_SYSTEM | \
81145256Sjkoshy    PMC_CAP_EDGE | PMC_CAP_THRESHOLD | PMC_CAP_READ | PMC_CAP_WRITE |	 \
82145256Sjkoshy    PMC_CAP_INVERT | PMC_CAP_QUALIFIER)
83145256Sjkoshy
84145256Sjkoshy	/* PMC 0 */
85145256Sjkoshy	{
86145256Sjkoshy		.pm_descr =
87145256Sjkoshy		{
88145256Sjkoshy			.pd_name  ="P6-0",
89145256Sjkoshy			.pd_class = PMC_CLASS_P6,
90145256Sjkoshy			.pd_caps  = P6_PMC_CAPS,
91145256Sjkoshy			.pd_width = 40
92145256Sjkoshy		},
93145256Sjkoshy		.pm_pmc_msr   = P6_MSR_PERFCTR0,
94145256Sjkoshy		.pm_evsel_msr = P6_MSR_EVSEL0
95145256Sjkoshy	},
96145256Sjkoshy
97145256Sjkoshy	/* PMC 1 */
98145256Sjkoshy	{
99145256Sjkoshy		.pm_descr =
100145256Sjkoshy		{
101145256Sjkoshy			.pd_name  ="P6-1",
102145256Sjkoshy			.pd_class = PMC_CLASS_P6,
103145256Sjkoshy			.pd_caps  = P6_PMC_CAPS,
104145256Sjkoshy			.pd_width = 40
105145256Sjkoshy		},
106145256Sjkoshy		.pm_pmc_msr   = P6_MSR_PERFCTR1,
107145256Sjkoshy		.pm_evsel_msr = P6_MSR_EVSEL1
108145256Sjkoshy	}
109145256Sjkoshy};
110145256Sjkoshy
111145256Sjkoshystatic enum pmc_cputype p6_cputype;
112145256Sjkoshy
113145256Sjkoshy/*
114145256Sjkoshy * P6 Event descriptor
115183717Sjkoshy *
116183717Sjkoshy * The 'pm_flags' field has the following structure:
117183717Sjkoshy * - The upper 4 bits are used to track which counter an event is valid on.
118183717Sjkoshy * - The lower bits form a bitmask of flags indicating support for the event
119183717Sjkoshy *   on a given CPU.
120145256Sjkoshy */
121145256Sjkoshy
122145256Sjkoshystruct p6_event_descr {
123145256Sjkoshy	const enum pmc_event pm_event;
124145256Sjkoshy	uint32_t	     pm_evsel;
125145256Sjkoshy	uint32_t	     pm_flags;
126145256Sjkoshy	uint32_t	     pm_unitmask;
127145256Sjkoshy};
128145256Sjkoshy
129183717Sjkoshy#define	P6F_CTR(C)	(1 << (28 + (C)))
130183717Sjkoshy#define	P6F_CTR0	P6F_CTR(0)
131183717Sjkoshy#define	P6F_CTR1	P6F_CTR(1)
132183717Sjkoshy#define	P6F(CPU)	(1 << ((CPU) - PMC_CPU_INTEL_P6))
133183717Sjkoshy#define	_P6F(C)		P6F(PMC_CPU_INTEL_##C)
134183717Sjkoshy#define	P6F_P6		_P6F(P6)
135183717Sjkoshy#define	P6F_CL		_P6F(CL)
136183717Sjkoshy#define	P6F_PII		_P6F(PII)
137183717Sjkoshy#define	P6F_PIII	_P6F(PIII)
138183717Sjkoshy#define	P6F_PM		_P6F(PM)
139183717Sjkoshy#define	P6F_ALL_CPUS	(P6F_P6 | P6F_PII | P6F_CL | P6F_PIII | P6F_PM)
140183717Sjkoshy#define	P6F_ALL_CTRS	(P6F_CTR0 | P6F_CTR1)
141183717Sjkoshy#define	P6F_ALL		(P6F_ALL_CPUS | P6F_ALL_CTRS)
142183717Sjkoshy
143183717Sjkoshy#define	P6_EVENT_VALID_FOR_CPU(P,CPU)	((P)->pm_flags & P6F(CPU))
144183717Sjkoshy#define	P6_EVENT_VALID_FOR_CTR(P,CTR)	((P)->pm_flags & P6F_CTR(CTR))
145183717Sjkoshy
146145256Sjkoshystatic const struct p6_event_descr p6_events[] = {
147145256Sjkoshy
148145256Sjkoshy#define	P6_EVDESCR(NAME, EVSEL, FLAGS, UMASK)	\
149145256Sjkoshy	{					\
150145256Sjkoshy		.pm_event = PMC_EV_P6_##NAME,	\
151145256Sjkoshy		.pm_evsel = (EVSEL),		\
152145256Sjkoshy		.pm_flags = (FLAGS),		\
153145256Sjkoshy		.pm_unitmask = (UMASK)		\
154145256Sjkoshy	}
155145256Sjkoshy
156145256SjkoshyP6_EVDESCR(DATA_MEM_REFS,		0x43, P6F_ALL, 0x00),
157145256SjkoshyP6_EVDESCR(DCU_LINES_IN,		0x45, P6F_ALL, 0x00),
158145256SjkoshyP6_EVDESCR(DCU_M_LINES_IN,		0x46, P6F_ALL, 0x00),
159145256SjkoshyP6_EVDESCR(DCU_M_LINES_OUT,		0x47, P6F_ALL, 0x00),
160145256SjkoshyP6_EVDESCR(DCU_MISS_OUTSTANDING,	0x47, P6F_ALL, 0x00),
161145256SjkoshyP6_EVDESCR(IFU_FETCH,			0x80, P6F_ALL, 0x00),
162145256SjkoshyP6_EVDESCR(IFU_FETCH_MISS,		0x81, P6F_ALL, 0x00),
163145256SjkoshyP6_EVDESCR(ITLB_MISS,			0x85, P6F_ALL, 0x00),
164145256SjkoshyP6_EVDESCR(IFU_MEM_STALL,		0x86, P6F_ALL, 0x00),
165145256SjkoshyP6_EVDESCR(ILD_STALL,			0x87, P6F_ALL, 0x00),
166145256SjkoshyP6_EVDESCR(L2_IFETCH,			0x28, P6F_ALL, 0x0F),
167145256SjkoshyP6_EVDESCR(L2_LD,			0x29, P6F_ALL, 0x0F),
168145256SjkoshyP6_EVDESCR(L2_ST,			0x2A, P6F_ALL, 0x0F),
169145256SjkoshyP6_EVDESCR(L2_LINES_IN,			0x24, P6F_ALL, 0x0F),
170145256SjkoshyP6_EVDESCR(L2_LINES_OUT,		0x26, P6F_ALL, 0x0F),
171145256SjkoshyP6_EVDESCR(L2_M_LINES_INM,		0x25, P6F_ALL, 0x00),
172145256SjkoshyP6_EVDESCR(L2_M_LINES_OUTM,		0x27, P6F_ALL, 0x0F),
173145256SjkoshyP6_EVDESCR(L2_RQSTS,			0x2E, P6F_ALL, 0x0F),
174145256SjkoshyP6_EVDESCR(L2_ADS,			0x21, P6F_ALL, 0x00),
175145256SjkoshyP6_EVDESCR(L2_DBUS_BUSY,		0x22, P6F_ALL, 0x00),
176145256SjkoshyP6_EVDESCR(L2_DBUS_BUSY_RD,		0x23, P6F_ALL, 0x00),
177145256SjkoshyP6_EVDESCR(BUS_DRDY_CLOCKS,		0x62, P6F_ALL, 0x20),
178145256SjkoshyP6_EVDESCR(BUS_LOCK_CLOCKS,		0x63, P6F_ALL, 0x20),
179145256SjkoshyP6_EVDESCR(BUS_REQ_OUTSTANDING,		0x60, P6F_ALL, 0x00),
180145256SjkoshyP6_EVDESCR(BUS_TRAN_BRD,		0x65, P6F_ALL, 0x20),
181145256SjkoshyP6_EVDESCR(BUS_TRAN_RFO,		0x66, P6F_ALL, 0x20),
182145256SjkoshyP6_EVDESCR(BUS_TRANS_WB,		0x67, P6F_ALL, 0x20),
183145256SjkoshyP6_EVDESCR(BUS_TRAN_IFETCH,		0x68, P6F_ALL, 0x20),
184145256SjkoshyP6_EVDESCR(BUS_TRAN_INVAL,		0x69, P6F_ALL, 0x20),
185145256SjkoshyP6_EVDESCR(BUS_TRAN_PWR,		0x6A, P6F_ALL, 0x20),
186145256SjkoshyP6_EVDESCR(BUS_TRANS_P,			0x6B, P6F_ALL, 0x20),
187145256SjkoshyP6_EVDESCR(BUS_TRANS_IO,		0x6C, P6F_ALL, 0x20),
188145256SjkoshyP6_EVDESCR(BUS_TRAN_DEF,		0x6D, P6F_ALL, 0x20),
189145256SjkoshyP6_EVDESCR(BUS_TRAN_BURST,		0x6E, P6F_ALL, 0x20),
190145256SjkoshyP6_EVDESCR(BUS_TRAN_ANY,		0x70, P6F_ALL, 0x20),
191145256SjkoshyP6_EVDESCR(BUS_TRAN_MEM,		0x6F, P6F_ALL, 0x20),
192145256SjkoshyP6_EVDESCR(BUS_DATA_RCV,		0x64, P6F_ALL, 0x00),
193145256SjkoshyP6_EVDESCR(BUS_BNR_DRV,			0x61, P6F_ALL, 0x00),
194145256SjkoshyP6_EVDESCR(BUS_HIT_DRV,			0x7A, P6F_ALL, 0x00),
195145256SjkoshyP6_EVDESCR(BUS_HITM_DRV,		0x7B, P6F_ALL, 0x00),
196145256SjkoshyP6_EVDESCR(BUS_SNOOP_STALL,		0x7E, P6F_ALL, 0x00),
197145256SjkoshyP6_EVDESCR(FLOPS,			0xC1, P6F_ALL_CPUS | P6F_CTR0, 0x00),
198145256SjkoshyP6_EVDESCR(FP_COMPS_OPS_EXE,		0x10, P6F_ALL_CPUS | P6F_CTR0, 0x00),
199145256SjkoshyP6_EVDESCR(FP_ASSIST,			0x11, P6F_ALL_CPUS | P6F_CTR1, 0x00),
200145256SjkoshyP6_EVDESCR(MUL,				0x12, P6F_ALL_CPUS | P6F_CTR1, 0x00),
201145256SjkoshyP6_EVDESCR(DIV,				0x13, P6F_ALL_CPUS | P6F_CTR1, 0x00),
202145256SjkoshyP6_EVDESCR(CYCLES_DIV_BUSY,		0x14, P6F_ALL_CPUS | P6F_CTR0, 0x00),
203145256SjkoshyP6_EVDESCR(LD_BLOCKS,			0x03, P6F_ALL, 0x00),
204145256SjkoshyP6_EVDESCR(SB_DRAINS,			0x04, P6F_ALL, 0x00),
205145256SjkoshyP6_EVDESCR(MISALIGN_MEM_REF,		0x05, P6F_ALL, 0x00),
206145256SjkoshyP6_EVDESCR(EMON_KNI_PREF_DISPATCHED,	0x07, P6F_PIII | P6F_ALL_CTRS, 0x03),
207145256SjkoshyP6_EVDESCR(EMON_KNI_PREF_MISS,		0x4B, P6F_PIII | P6F_ALL_CTRS, 0x03),
208145256SjkoshyP6_EVDESCR(INST_RETIRED,		0xC0, P6F_ALL, 0x00),
209145256SjkoshyP6_EVDESCR(UOPS_RETIRED,		0xC2, P6F_ALL, 0x00),
210145256SjkoshyP6_EVDESCR(INST_DECODED,		0xD0, P6F_ALL, 0x00),
211145256SjkoshyP6_EVDESCR(EMON_KNI_INST_RETIRED,	0xD8, P6F_PIII | P6F_ALL_CTRS, 0x01),
212145256SjkoshyP6_EVDESCR(EMON_KNI_COMP_INST_RET,	0xD9, P6F_PIII | P6F_ALL_CTRS, 0x01),
213145256SjkoshyP6_EVDESCR(HW_INT_RX,			0xC8, P6F_ALL, 0x00),
214145256SjkoshyP6_EVDESCR(CYCLES_INT_MASKED,		0xC6, P6F_ALL, 0x00),
215145256SjkoshyP6_EVDESCR(CYCLES_INT_PENDING_AND_MASKED, 0xC7, P6F_ALL, 0x00),
216145256SjkoshyP6_EVDESCR(BR_INST_RETIRED,		0xC4, P6F_ALL, 0x00),
217145256SjkoshyP6_EVDESCR(BR_MISS_PRED_RETIRED,	0xC5, P6F_ALL, 0x00),
218145256SjkoshyP6_EVDESCR(BR_TAKEN_RETIRED,		0xC9, P6F_ALL, 0x00),
219145256SjkoshyP6_EVDESCR(BR_MISS_PRED_TAKEN_RET, 	0xCA, P6F_ALL, 0x00),
220145256SjkoshyP6_EVDESCR(BR_INST_DECODED,		0xE0, P6F_ALL, 0x00),
221145256SjkoshyP6_EVDESCR(BTB_MISSES,			0xE2, P6F_ALL, 0x00),
222145256SjkoshyP6_EVDESCR(BR_BOGUS,			0xE4, P6F_ALL, 0x00),
223145256SjkoshyP6_EVDESCR(BACLEARS,			0xE6, P6F_ALL, 0x00),
224145256SjkoshyP6_EVDESCR(RESOURCE_STALLS,		0xA2, P6F_ALL, 0x00),
225145256SjkoshyP6_EVDESCR(PARTIAL_RAT_STALLS,		0xD2, P6F_ALL, 0x00),
226145256SjkoshyP6_EVDESCR(SEGMENT_REG_LOADS,		0x06, P6F_ALL, 0x00),
227145256SjkoshyP6_EVDESCR(CPU_CLK_UNHALTED,		0x79, P6F_ALL, 0x00),
228145256SjkoshyP6_EVDESCR(MMX_INSTR_EXEC,		0xB0,
229145256Sjkoshy			P6F_ALL_CTRS | P6F_CL | P6F_PII, 0x00),
230145256SjkoshyP6_EVDESCR(MMX_SAT_INSTR_EXEC,		0xB1,
231145256Sjkoshy			P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00),
232145256SjkoshyP6_EVDESCR(MMX_UOPS_EXEC,		0xB2,
233145256Sjkoshy			P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F),
234145256SjkoshyP6_EVDESCR(MMX_INSTR_TYPE_EXEC,		0xB3,
235145256Sjkoshy			P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x3F),
236145256SjkoshyP6_EVDESCR(FP_MMX_TRANS,		0xCC,
237145256Sjkoshy			P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x01),
238145256SjkoshyP6_EVDESCR(MMX_ASSIST,			0xCD,
239145256Sjkoshy			P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00),
240145256SjkoshyP6_EVDESCR(MMX_INSTR_RET,		0xCE, P6F_ALL_CTRS | P6F_PII, 0x00),
241145256SjkoshyP6_EVDESCR(SEG_RENAME_STALLS,		0xD4,
242145256Sjkoshy			P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F),
243145256SjkoshyP6_EVDESCR(SEG_REG_RENAMES,		0xD5,
244145256Sjkoshy			P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F),
245145256SjkoshyP6_EVDESCR(RET_SEG_RENAMES,		0xD6,
246145256Sjkoshy			P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00),
247145256SjkoshyP6_EVDESCR(EMON_EST_TRANS,		0x58, P6F_ALL_CTRS | P6F_PM, 0x02),
248145256SjkoshyP6_EVDESCR(EMON_THERMAL_TRIP,		0x59, P6F_ALL_CTRS | P6F_PM, 0x00),
249145256SjkoshyP6_EVDESCR(BR_INST_EXEC,		0x88, P6F_ALL_CTRS | P6F_PM, 0x00),
250145256SjkoshyP6_EVDESCR(BR_MISSP_EXEC,		0x89, P6F_ALL_CTRS | P6F_PM, 0x00),
251145256SjkoshyP6_EVDESCR(BR_BAC_MISSP_EXEC,		0x8A, P6F_ALL_CTRS | P6F_PM, 0x00),
252145256SjkoshyP6_EVDESCR(BR_CND_EXEC,			0x8B, P6F_ALL_CTRS | P6F_PM, 0x00),
253145256SjkoshyP6_EVDESCR(BR_CND_MISSP_EXEC,		0x8C, P6F_ALL_CTRS | P6F_PM, 0x00),
254145256SjkoshyP6_EVDESCR(BR_IND_EXEC,			0x8D, P6F_ALL_CTRS | P6F_PM, 0x00),
255145256SjkoshyP6_EVDESCR(BR_IND_MISSP_EXEC,		0x8E, P6F_ALL_CTRS | P6F_PM, 0x00),
256145256SjkoshyP6_EVDESCR(BR_RET_EXEC,			0x8F, P6F_ALL_CTRS | P6F_PM, 0x00),
257145256SjkoshyP6_EVDESCR(BR_RET_MISSP_EXEC,		0x90, P6F_ALL_CTRS | P6F_PM, 0x00),
258145256SjkoshyP6_EVDESCR(BR_RET_BAC_MISSP_EXEC,	0x91, P6F_ALL_CTRS | P6F_PM, 0x00),
259145256SjkoshyP6_EVDESCR(BR_CALL_EXEC,		0x92, P6F_ALL_CTRS | P6F_PM, 0x00),
260145256SjkoshyP6_EVDESCR(BR_CALL_MISSP_EXEC,		0x93, P6F_ALL_CTRS | P6F_PM, 0x00),
261145256SjkoshyP6_EVDESCR(BR_IND_CALL_EXEC,		0x94, P6F_ALL_CTRS | P6F_PM, 0x00),
262145256SjkoshyP6_EVDESCR(EMON_SIMD_INSTR_RETIRED,	0xCE, P6F_ALL_CTRS | P6F_PM, 0x00),
263145256SjkoshyP6_EVDESCR(EMON_SYNCH_UOPS,		0xD3, P6F_ALL_CTRS | P6F_PM, 0x00),
264145256SjkoshyP6_EVDESCR(EMON_ESP_UOPS,		0xD7, P6F_ALL_CTRS | P6F_PM, 0x00),
265145256SjkoshyP6_EVDESCR(EMON_FUSED_UOPS_RET,		0xDA, P6F_ALL_CTRS | P6F_PM, 0x03),
266145256SjkoshyP6_EVDESCR(EMON_UNFUSION,		0xDB, P6F_ALL_CTRS | P6F_PM, 0x00),
267145256SjkoshyP6_EVDESCR(EMON_PREF_RQSTS_UP,		0xF0, P6F_ALL_CTRS | P6F_PM, 0x00),
268145256SjkoshyP6_EVDESCR(EMON_PREF_RQSTS_DN,		0xD8, P6F_ALL_CTRS | P6F_PM, 0x00),
269145256SjkoshyP6_EVDESCR(EMON_SSE_SSE2_INST_RETIRED,	0xD8, P6F_ALL_CTRS | P6F_PM, 0x03),
270145256SjkoshyP6_EVDESCR(EMON_SSE_SSE2_COMP_INST_RETIRED, 0xD9, P6F_ALL_CTRS | P6F_PM, 0x03)
271145256Sjkoshy
272145256Sjkoshy#undef	P6_EVDESCR
273145256Sjkoshy};
274145256Sjkoshy
275145256Sjkoshy#define	P6_NEVENTS	(PMC_EV_P6_LAST - PMC_EV_P6_FIRST + 1)
276145256Sjkoshy
277145256Sjkoshystatic const struct p6_event_descr *
278145256Sjkoshyp6_find_event(enum pmc_event ev)
279145256Sjkoshy{
280145256Sjkoshy	int n;
281145256Sjkoshy
282145256Sjkoshy	for (n = 0; n < P6_NEVENTS; n++)
283145256Sjkoshy		if (p6_events[n].pm_event == ev)
284145256Sjkoshy			break;
285145256Sjkoshy	if (n == P6_NEVENTS)
286145256Sjkoshy		return NULL;
287145256Sjkoshy	return &p6_events[n];
288145256Sjkoshy}
289145256Sjkoshy
290145256Sjkoshy/*
291145256Sjkoshy * Per-CPU data structure for P6 class CPUs
292145256Sjkoshy *
293145256Sjkoshy * [common stuff]
294147191Sjkoshy * [flags for maintaining PMC start/stop state]
295145256Sjkoshy * [3 struct pmc_hw pointers]
296145256Sjkoshy * [3 struct pmc_hw structures]
297145256Sjkoshy */
298145256Sjkoshy
299145256Sjkoshystruct p6_cpu {
300145256Sjkoshy	struct pmc_hw	pc_p6pmcs[P6_NPMCS];
301147989Sjkoshy	uint32_t	pc_state;
302145256Sjkoshy};
303145256Sjkoshy
304184802Sjkoshystatic struct p6_cpu **p6_pcpu;
305184802Sjkoshy
306147191Sjkoshy/*
307147191Sjkoshy * If CTR1 is active, we need to keep the 'EN' bit if CTR0 set,
308147191Sjkoshy * with the rest of CTR0 being zero'ed out.
309147191Sjkoshy */
310147191Sjkoshy#define	P6_SYNC_CTR_STATE(PC) do {				\
311147191Sjkoshy		uint32_t _config, _enable;			\
312147191Sjkoshy		_enable = 0;					\
313147191Sjkoshy		if ((PC)->pc_state & 0x02)			\
314147191Sjkoshy			_enable |= P6_EVSEL_EN;			\
315147191Sjkoshy		if ((PC)->pc_state & 0x01)			\
316147191Sjkoshy			_config = rdmsr(P6_MSR_EVSEL0) | 	\
317147191Sjkoshy			    P6_EVSEL_EN;			\
318147191Sjkoshy		else						\
319147191Sjkoshy			_config = 0;				\
320147191Sjkoshy		wrmsr(P6_MSR_EVSEL0, _config | _enable);	\
321147191Sjkoshy	} while (0)
322147191Sjkoshy
323147191Sjkoshy#define	P6_MARK_STARTED(PC,RI) do {				\
324147191Sjkoshy		(PC)->pc_state |= (1 << ((RI)-1));		\
325147191Sjkoshy	} while (0)
326147191Sjkoshy
327147191Sjkoshy#define	P6_MARK_STOPPED(PC,RI) do {				\
328147191Sjkoshy		(PC)->pc_state &= ~(1<< ((RI)-1));		\
329147191Sjkoshy	} while (0)
330147191Sjkoshy
331145256Sjkoshystatic int
332184802Sjkoshyp6_pcpu_init(struct pmc_mdep *md, int cpu)
333145256Sjkoshy{
334184802Sjkoshy	int first_ri, n;
335184802Sjkoshy	struct p6_cpu *p6c;
336184802Sjkoshy	struct pmc_cpu *pc;
337145256Sjkoshy	struct pmc_hw *phw;
338145256Sjkoshy
339183266Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
340145256Sjkoshy	    ("[p6,%d] bad cpu %d", __LINE__, cpu));
341145256Sjkoshy
342145256Sjkoshy	PMCDBG(MDP,INI,0,"p6-init cpu=%d", cpu);
343145256Sjkoshy
344184802Sjkoshy	p6c = malloc(sizeof (struct p6_cpu), M_PMC, M_WAITOK|M_ZERO);
345184802Sjkoshy	pc = pmc_pcpu[cpu];
346145256Sjkoshy
347184802Sjkoshy	KASSERT(pc != NULL, ("[p6,%d] cpu %d null per-cpu", __LINE__, cpu));
348145256Sjkoshy
349184802Sjkoshy	phw = p6c->pc_p6pmcs;
350184802Sjkoshy	p6_pcpu[cpu] = p6c;
351184802Sjkoshy
352184802Sjkoshy	first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6].pcd_ri;
353184802Sjkoshy
354145256Sjkoshy	for (n = 0; n < P6_NPMCS; n++, phw++) {
355145256Sjkoshy		phw->phw_state   = PMC_PHW_FLAG_IS_ENABLED |
356145256Sjkoshy		    PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n);
357145256Sjkoshy		phw->phw_pmc     = NULL;
358184802Sjkoshy		pc->pc_hwpmcs[n + first_ri] = phw;
359145256Sjkoshy	}
360145256Sjkoshy
361184802Sjkoshy	return (0);
362145256Sjkoshy}
363145256Sjkoshy
364145256Sjkoshystatic int
365184802Sjkoshyp6_pcpu_fini(struct pmc_mdep *md, int cpu)
366145256Sjkoshy{
367184802Sjkoshy	int first_ri, n;
368184802Sjkoshy	struct p6_cpu *p6c;
369184802Sjkoshy	struct pmc_cpu *pc;
370145256Sjkoshy
371183266Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
372145256Sjkoshy	    ("[p6,%d] bad cpu %d", __LINE__, cpu));
373145256Sjkoshy
374145256Sjkoshy	PMCDBG(MDP,INI,0,"p6-cleanup cpu=%d", cpu);
375145256Sjkoshy
376184802Sjkoshy	p6c = p6_pcpu[cpu];
377184802Sjkoshy	p6_pcpu[cpu] = NULL;
378145256Sjkoshy
379184802Sjkoshy	KASSERT(p6c != NULL, ("[p6,%d] null pcpu", __LINE__));
380145256Sjkoshy
381184802Sjkoshy	free(p6c, M_PMC);
382145615Sjkoshy
383184802Sjkoshy	first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6].pcd_ri;
384184802Sjkoshy	pc = pmc_pcpu[cpu];
385184802Sjkoshy	for (n = 0; n < P6_NPMCS; n++)
386184802Sjkoshy		pc->pc_hwpmcs[n + first_ri] = NULL;
387145774Sjkoshy
388184802Sjkoshy	return (0);
389145256Sjkoshy}
390145256Sjkoshy
391145256Sjkoshystatic int
392145256Sjkoshyp6_read_pmc(int cpu, int ri, pmc_value_t *v)
393145256Sjkoshy{
394145256Sjkoshy	struct pmc *pm;
395145256Sjkoshy	struct p6pmc_descr *pd;
396145256Sjkoshy	pmc_value_t tmp;
397145256Sjkoshy
398184802Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
399184802Sjkoshy	    ("[p6,%d] illegal cpu value %d", __LINE__, cpu));
400184802Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
401184802Sjkoshy	    ("[p6,%d] illegal row-index %d", __LINE__, ri));
402145256Sjkoshy
403184802Sjkoshy	pm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc;
404184802Sjkoshy	pd = &p6_pmcdesc[ri];
405184802Sjkoshy
406145256Sjkoshy	KASSERT(pm,
407145256Sjkoshy	    ("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
408145256Sjkoshy
409147191Sjkoshy	tmp = rdmsr(pd->pm_pmc_msr) & P6_PERFCTR_READ_MASK;
410145774Sjkoshy	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
411147191Sjkoshy		*v = P6_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp);
412145256Sjkoshy	else
413145256Sjkoshy		*v = tmp;
414145256Sjkoshy
415145256Sjkoshy	PMCDBG(MDP,REA,1, "p6-read cpu=%d ri=%d msr=0x%x -> v=%jx", cpu, ri,
416145256Sjkoshy	    pd->pm_pmc_msr, *v);
417145256Sjkoshy
418184802Sjkoshy	return (0);
419145256Sjkoshy}
420145256Sjkoshy
421145256Sjkoshystatic int
422145256Sjkoshyp6_write_pmc(int cpu, int ri, pmc_value_t v)
423145256Sjkoshy{
424145256Sjkoshy	struct pmc *pm;
425145256Sjkoshy	struct p6pmc_descr *pd;
426145256Sjkoshy
427184802Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
428184802Sjkoshy	    ("[p6,%d] illegal cpu value %d", __LINE__, cpu));
429184802Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
430184802Sjkoshy	    ("[p6,%d] illegal row-index %d", __LINE__, ri));
431145256Sjkoshy
432184802Sjkoshy	pm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc;
433184802Sjkoshy	pd = &p6_pmcdesc[ri];
434184802Sjkoshy
435145256Sjkoshy	KASSERT(pm,
436145256Sjkoshy	    ("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
437145256Sjkoshy
438145256Sjkoshy	PMCDBG(MDP,WRI,1, "p6-write cpu=%d ri=%d msr=0x%x v=%jx", cpu, ri,
439145256Sjkoshy	    pd->pm_pmc_msr, v);
440145256Sjkoshy
441145774Sjkoshy	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
442147191Sjkoshy		v = P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v);
443145256Sjkoshy
444147191Sjkoshy	wrmsr(pd->pm_pmc_msr, v & P6_PERFCTR_WRITE_MASK);
445145256Sjkoshy
446184802Sjkoshy	return (0);
447145256Sjkoshy}
448145256Sjkoshy
449145256Sjkoshystatic int
450145256Sjkoshyp6_config_pmc(int cpu, int ri, struct pmc *pm)
451145256Sjkoshy{
452184802Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
453184802Sjkoshy	    ("[p6,%d] illegal CPU %d", __LINE__, cpu));
454145256Sjkoshy
455184802Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
456184802Sjkoshy	    ("[p6,%d] illegal row-index %d", __LINE__, ri));
457184802Sjkoshy
458145256Sjkoshy	PMCDBG(MDP,CFG,1, "p6-config cpu=%d ri=%d pm=%p", cpu, ri, pm);
459145256Sjkoshy
460184802Sjkoshy	KASSERT(p6_pcpu[cpu] != NULL, ("[p6,%d] null per-cpu %d", __LINE__,
461184802Sjkoshy	    cpu));
462145256Sjkoshy
463184802Sjkoshy	p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc = pm;
464184802Sjkoshy
465184802Sjkoshy	return (0);
466145256Sjkoshy}
467145256Sjkoshy
468145256Sjkoshy/*
469145774Sjkoshy * Retrieve a configured PMC pointer from hardware state.
470145774Sjkoshy */
471145774Sjkoshy
472145774Sjkoshystatic int
473145774Sjkoshyp6_get_config(int cpu, int ri, struct pmc **ppm)
474145774Sjkoshy{
475145774Sjkoshy
476184802Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
477184802Sjkoshy	    ("[p6,%d] illegal CPU %d", __LINE__, cpu));
478184802Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
479184802Sjkoshy	    ("[p6,%d] illegal row-index %d", __LINE__, ri));
480184802Sjkoshy
481184802Sjkoshy	*ppm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc;
482184802Sjkoshy
483184802Sjkoshy	return (0);
484145774Sjkoshy}
485145774Sjkoshy
486145774Sjkoshy
487145774Sjkoshy/*
488145256Sjkoshy * A pmc may be allocated to a given row index if:
489145256Sjkoshy * - the event is valid for this CPU
490145256Sjkoshy * - the event is valid for this counter index
491145256Sjkoshy */
492145256Sjkoshy
493145256Sjkoshystatic int
494145256Sjkoshyp6_allocate_pmc(int cpu, int ri, struct pmc *pm,
495145256Sjkoshy    const struct pmc_op_pmcallocate *a)
496145256Sjkoshy{
497145256Sjkoshy	uint32_t allowed_unitmask, caps, config, unitmask;
498145256Sjkoshy	const struct p6pmc_descr *pd;
499145256Sjkoshy	const struct p6_event_descr *pevent;
500145256Sjkoshy	enum pmc_event ev;
501145256Sjkoshy
502145256Sjkoshy	(void) cpu;
503145256Sjkoshy
504183266Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
505184802Sjkoshy	    ("[p6,%d] illegal CPU %d", __LINE__, cpu));
506145256Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
507184802Sjkoshy	    ("[p6,%d] illegal row-index value %d", __LINE__, ri));
508145256Sjkoshy
509145256Sjkoshy	pd = &p6_pmcdesc[ri];
510145256Sjkoshy
511145256Sjkoshy	PMCDBG(MDP,ALL,1, "p6-allocate ri=%d class=%d pmccaps=0x%x "
512145256Sjkoshy	    "reqcaps=0x%x", ri, pd->pm_descr.pd_class, pd->pm_descr.pd_caps,
513145256Sjkoshy	    pm->pm_caps);
514145256Sjkoshy
515145256Sjkoshy	/* check class */
516145774Sjkoshy	if (pd->pm_descr.pd_class != a->pm_class)
517184802Sjkoshy		return (EINVAL);
518145256Sjkoshy
519145256Sjkoshy	/* check requested capabilities */
520145256Sjkoshy	caps = a->pm_caps;
521145256Sjkoshy	if ((pd->pm_descr.pd_caps & caps) != caps)
522184802Sjkoshy		return (EPERM);
523145256Sjkoshy
524145256Sjkoshy	ev = pm->pm_event;
525145256Sjkoshy
526145256Sjkoshy	if (ev < PMC_EV_P6_FIRST || ev > PMC_EV_P6_LAST)
527184802Sjkoshy		return (EINVAL);
528145256Sjkoshy
529145256Sjkoshy	if ((pevent = p6_find_event(ev)) == NULL)
530184802Sjkoshy		return (ESRCH);
531145256Sjkoshy
532145256Sjkoshy	if (!P6_EVENT_VALID_FOR_CPU(pevent, p6_cputype) ||
533145256Sjkoshy	    !P6_EVENT_VALID_FOR_CTR(pevent, (ri-1)))
534184802Sjkoshy		return (EINVAL);
535145256Sjkoshy
536145256Sjkoshy	/* For certain events, Pentium M differs from the stock P6 */
537145256Sjkoshy	allowed_unitmask = 0;
538145256Sjkoshy	if (p6_cputype == PMC_CPU_INTEL_PM) {
539145256Sjkoshy		if (ev == PMC_EV_P6_L2_LD || ev == PMC_EV_P6_L2_LINES_IN ||
540145256Sjkoshy		    ev == PMC_EV_P6_L2_LINES_OUT)
541145256Sjkoshy			allowed_unitmask = P6_EVSEL_TO_UMASK(0x3F);
542145256Sjkoshy		else if (ev == PMC_EV_P6_L2_M_LINES_OUTM)
543145256Sjkoshy			allowed_unitmask = P6_EVSEL_TO_UMASK(0x30);
544145256Sjkoshy	} else
545145256Sjkoshy		allowed_unitmask = P6_EVSEL_TO_UMASK(pevent->pm_unitmask);
546145256Sjkoshy
547147191Sjkoshy	unitmask = a->pm_md.pm_ppro.pm_ppro_config & P6_EVSEL_UMASK_MASK;
548145256Sjkoshy	if (unitmask & ~allowed_unitmask) /* disallow reserved bits */
549184802Sjkoshy		return (EINVAL);
550145256Sjkoshy
551145256Sjkoshy	if (ev == PMC_EV_P6_MMX_UOPS_EXEC) /* hardcoded mask */
552145256Sjkoshy		unitmask = P6_EVSEL_TO_UMASK(0x0F);
553145256Sjkoshy
554145256Sjkoshy	config = 0;
555145256Sjkoshy
556145256Sjkoshy	config |= P6_EVSEL_EVENT_SELECT(pevent->pm_evsel);
557145256Sjkoshy
558145256Sjkoshy	if (unitmask & (caps & PMC_CAP_QUALIFIER))
559145256Sjkoshy		config |= unitmask;
560145256Sjkoshy
561145256Sjkoshy	if (caps & PMC_CAP_THRESHOLD)
562147191Sjkoshy		config |= a->pm_md.pm_ppro.pm_ppro_config &
563147191Sjkoshy		    P6_EVSEL_CMASK_MASK;
564145256Sjkoshy
565145256Sjkoshy	/* set at least one of the 'usr' or 'os' caps */
566145256Sjkoshy	if (caps & PMC_CAP_USER)
567145256Sjkoshy		config |= P6_EVSEL_USR;
568145256Sjkoshy	if (caps & PMC_CAP_SYSTEM)
569145256Sjkoshy		config |= P6_EVSEL_OS;
570145256Sjkoshy	if ((caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0)
571145256Sjkoshy		config |= (P6_EVSEL_USR|P6_EVSEL_OS);
572145256Sjkoshy
573145256Sjkoshy	if (caps & PMC_CAP_EDGE)
574145256Sjkoshy		config |= P6_EVSEL_E;
575145256Sjkoshy	if (caps & PMC_CAP_INVERT)
576145256Sjkoshy		config |= P6_EVSEL_INV;
577145256Sjkoshy	if (caps & PMC_CAP_INTERRUPT)
578145256Sjkoshy		config |= P6_EVSEL_INT;
579145256Sjkoshy
580147191Sjkoshy	pm->pm_md.pm_ppro.pm_ppro_evsel = config;
581145256Sjkoshy
582145256Sjkoshy	PMCDBG(MDP,ALL,2, "p6-allocate config=0x%x", config);
583145256Sjkoshy
584184802Sjkoshy	return (0);
585145256Sjkoshy}
586145256Sjkoshy
587145256Sjkoshystatic int
588145256Sjkoshyp6_release_pmc(int cpu, int ri, struct pmc *pm)
589145256Sjkoshy{
590145256Sjkoshy	(void) pm;
591145256Sjkoshy
592145256Sjkoshy	PMCDBG(MDP,REL,1, "p6-release cpu=%d ri=%d pm=%p", cpu, ri, pm);
593145256Sjkoshy
594183266Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
595145256Sjkoshy	    ("[p6,%d] illegal CPU value %d", __LINE__, cpu));
596145256Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
597145256Sjkoshy	    ("[p6,%d] illegal row-index %d", __LINE__, ri));
598145256Sjkoshy
599184802Sjkoshy	KASSERT(p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc == NULL,
600184802Sjkoshy	    ("[p6,%d] PHW pmc non-NULL", __LINE__));
601145256Sjkoshy
602184802Sjkoshy	return (0);
603145256Sjkoshy}
604145256Sjkoshy
605145256Sjkoshystatic int
606145256Sjkoshyp6_start_pmc(int cpu, int ri)
607145256Sjkoshy{
608145256Sjkoshy	uint32_t config;
609145256Sjkoshy	struct pmc *pm;
610147191Sjkoshy	struct p6_cpu *pc;
611145256Sjkoshy	const struct p6pmc_descr *pd;
612145256Sjkoshy
613183266Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
614145256Sjkoshy	    ("[p6,%d] illegal CPU value %d", __LINE__, cpu));
615145256Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
616145256Sjkoshy	    ("[p6,%d] illegal row-index %d", __LINE__, ri));
617145256Sjkoshy
618184802Sjkoshy	pc = p6_pcpu[cpu];
619184802Sjkoshy	pm = pc->pc_p6pmcs[ri].phw_pmc;
620184802Sjkoshy	pd = &p6_pmcdesc[ri];
621145256Sjkoshy
622145256Sjkoshy	KASSERT(pm,
623145256Sjkoshy	    ("[p6,%d] starting cpu%d,ri%d with no pmc configured",
624145256Sjkoshy		__LINE__, cpu, ri));
625145256Sjkoshy
626145256Sjkoshy	PMCDBG(MDP,STA,1, "p6-start cpu=%d ri=%d", cpu, ri);
627145256Sjkoshy
628147191Sjkoshy	config = pm->pm_md.pm_ppro.pm_ppro_evsel;
629145256Sjkoshy
630145256Sjkoshy	PMCDBG(MDP,STA,2, "p6-start/2 cpu=%d ri=%d evselmsr=0x%x config=0x%x",
631145256Sjkoshy	    cpu, ri, pd->pm_evsel_msr, config);
632145256Sjkoshy
633147191Sjkoshy	P6_MARK_STARTED(pc, ri);
634147191Sjkoshy	wrmsr(pd->pm_evsel_msr, config);
635147191Sjkoshy
636147191Sjkoshy	P6_SYNC_CTR_STATE(pc);
637147191Sjkoshy
638184802Sjkoshy	return (0);
639145256Sjkoshy}
640145256Sjkoshy
641145256Sjkoshystatic int
642145256Sjkoshyp6_stop_pmc(int cpu, int ri)
643145256Sjkoshy{
644145256Sjkoshy	struct pmc *pm;
645147191Sjkoshy	struct p6_cpu *pc;
646145256Sjkoshy	struct p6pmc_descr *pd;
647145256Sjkoshy
648183266Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
649145256Sjkoshy	    ("[p6,%d] illegal cpu value %d", __LINE__, cpu));
650145256Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
651145256Sjkoshy	    ("[p6,%d] illegal row index %d", __LINE__, ri));
652145256Sjkoshy
653184802Sjkoshy	pc = p6_pcpu[cpu];
654184802Sjkoshy	pm = pc->pc_p6pmcs[ri].phw_pmc;
655184802Sjkoshy	pd = &p6_pmcdesc[ri];
656145256Sjkoshy
657145256Sjkoshy	KASSERT(pm,
658145256Sjkoshy	    ("[p6,%d] cpu%d ri%d no configured PMC to stop", __LINE__,
659145256Sjkoshy		cpu, ri));
660145256Sjkoshy
661145256Sjkoshy	PMCDBG(MDP,STO,1, "p6-stop cpu=%d ri=%d", cpu, ri);
662145256Sjkoshy
663147191Sjkoshy	wrmsr(pd->pm_evsel_msr, 0);	/* stop hw */
664147191Sjkoshy	P6_MARK_STOPPED(pc, ri);	/* update software state */
665145256Sjkoshy
666147191Sjkoshy	P6_SYNC_CTR_STATE(pc);		/* restart CTR1 if need be */
667145256Sjkoshy
668147191Sjkoshy	PMCDBG(MDP,STO,2, "p6-stop/2 cpu=%d ri=%d", cpu, ri);
669184802Sjkoshy
670184802Sjkoshy	return (0);
671145256Sjkoshy}
672145256Sjkoshy
673145256Sjkoshystatic int
674174395Sjkoshyp6_intr(int cpu, struct trapframe *tf)
675145256Sjkoshy{
676184802Sjkoshy	int error, retval, ri;
677147191Sjkoshy	uint32_t perf0cfg;
678147191Sjkoshy	struct pmc *pm;
679147191Sjkoshy	struct p6_cpu *pc;
680147191Sjkoshy	pmc_value_t v;
681147191Sjkoshy
682183266Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
683147191Sjkoshy	    ("[p6,%d] CPU %d out of range", __LINE__, cpu));
684147191Sjkoshy
685147191Sjkoshy	retval = 0;
686184802Sjkoshy	pc = p6_pcpu[cpu];
687147191Sjkoshy
688147191Sjkoshy	/* stop both PMCs */
689147191Sjkoshy	perf0cfg = rdmsr(P6_MSR_EVSEL0);
690147191Sjkoshy	wrmsr(P6_MSR_EVSEL0, perf0cfg & ~P6_EVSEL_EN);
691147191Sjkoshy
692184802Sjkoshy	for (ri = 0; ri < P6_NPMCS; ri++) {
693147191Sjkoshy
694184802Sjkoshy		if ((pm = pc->pc_p6pmcs[ri].phw_pmc) == NULL ||
695147191Sjkoshy		    !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
696147191Sjkoshy			continue;
697147191Sjkoshy		}
698147191Sjkoshy
699185555Sjkoshy		if (!P6_PMC_HAS_OVERFLOWED(ri))
700185555Sjkoshy			continue;
701185555Sjkoshy
702147191Sjkoshy		retval = 1;
703147191Sjkoshy
704185555Sjkoshy		if (pm->pm_state != PMC_STATE_RUNNING)
705185555Sjkoshy			continue;
706185555Sjkoshy
707236238Sfabient		error = pmc_process_interrupt(cpu, PMC_HR, pm, tf,
708174395Sjkoshy		    TRAPF_USERMODE(tf));
709147191Sjkoshy		if (error)
710147191Sjkoshy			P6_MARK_STOPPED(pc,ri);
711147191Sjkoshy
712147191Sjkoshy		/* reload sampling count */
713147191Sjkoshy		v = pm->pm_sc.pm_reloadcount;
714184802Sjkoshy		wrmsr(P6_MSR_PERFCTR0 + ri,
715147191Sjkoshy		    P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v));
716147191Sjkoshy
717147191Sjkoshy	}
718147191Sjkoshy
719147191Sjkoshy	/*
720147191Sjkoshy	 * On P6 processors, the LAPIC needs to have its PMC interrupt
721147191Sjkoshy	 * unmasked after a PMC interrupt.
722147191Sjkoshy	 */
723147191Sjkoshy	if (retval)
724196224Sjhb		lapic_reenable_pmc();
725147191Sjkoshy
726147867Sjkoshy	atomic_add_int(retval ? &pmc_stats.pm_intr_processed :
727147867Sjkoshy	    &pmc_stats.pm_intr_ignored, 1);
728147867Sjkoshy
729147191Sjkoshy	/* restart counters that can be restarted */
730147191Sjkoshy	P6_SYNC_CTR_STATE(pc);
731147191Sjkoshy
732184802Sjkoshy	return (retval);
733145256Sjkoshy}
734145256Sjkoshy
735145256Sjkoshystatic int
736145256Sjkoshyp6_describe(int cpu, int ri, struct pmc_info *pi,
737145256Sjkoshy    struct pmc **ppmc)
738145256Sjkoshy{
739145256Sjkoshy	int error;
740145256Sjkoshy	size_t copied;
741145256Sjkoshy	struct pmc_hw *phw;
742145256Sjkoshy	struct p6pmc_descr *pd;
743145256Sjkoshy
744184802Sjkoshy	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
745184802Sjkoshy	    ("[p6,%d] illegal CPU %d", __LINE__, cpu));
746184802Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
747184802Sjkoshy	    ("[p6,%d] row-index %d out of range", __LINE__, ri));
748184802Sjkoshy
749145256Sjkoshy	phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
750145256Sjkoshy	pd  = &p6_pmcdesc[ri];
751145256Sjkoshy
752184802Sjkoshy	KASSERT(phw == &p6_pcpu[cpu]->pc_p6pmcs[ri],
753184802Sjkoshy	    ("[p6,%d] phw mismatch", __LINE__));
754184802Sjkoshy
755145256Sjkoshy	if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name,
756145256Sjkoshy		 PMC_NAME_MAX, &copied)) != 0)
757184802Sjkoshy		return (error);
758145256Sjkoshy
759145256Sjkoshy	pi->pm_class = pd->pm_descr.pd_class;
760145256Sjkoshy
761145256Sjkoshy	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
762145256Sjkoshy		pi->pm_enabled = TRUE;
763145256Sjkoshy		*ppmc          = phw->phw_pmc;
764145256Sjkoshy	} else {
765145256Sjkoshy		pi->pm_enabled = FALSE;
766145256Sjkoshy		*ppmc          = NULL;
767145256Sjkoshy	}
768145256Sjkoshy
769184802Sjkoshy	return (0);
770145256Sjkoshy}
771145256Sjkoshy
772145256Sjkoshystatic int
773145256Sjkoshyp6_get_msr(int ri, uint32_t *msr)
774145256Sjkoshy{
775145256Sjkoshy	KASSERT(ri >= 0 && ri < P6_NPMCS,
776145256Sjkoshy	    ("[p6,%d ri %d out of range", __LINE__, ri));
777145256Sjkoshy
778145774Sjkoshy	*msr = p6_pmcdesc[ri].pm_pmc_msr - P6_MSR_PERFCTR0;
779184802Sjkoshy
780184802Sjkoshy	return (0);
781145256Sjkoshy}
782145256Sjkoshy
783145256Sjkoshyint
784184802Sjkoshypmc_p6_initialize(struct pmc_mdep *md, int ncpus)
785145256Sjkoshy{
786184802Sjkoshy	struct pmc_classdep *pcd;
787184802Sjkoshy
788185341Sjkim	KASSERT(cpu_vendor_id == CPU_VENDOR_INTEL,
789145256Sjkoshy	    ("[p6,%d] Initializing non-intel processor", __LINE__));
790145256Sjkoshy
791145256Sjkoshy	PMCDBG(MDP,INI,1, "%s", "p6-initialize");
792145256Sjkoshy
793184802Sjkoshy	/* Allocate space for pointers to per-cpu descriptors. */
794184802Sjkoshy	p6_pcpu = malloc(sizeof(struct p6_cpu **) * ncpus, M_PMC,
795184802Sjkoshy	    M_ZERO|M_WAITOK);
796145256Sjkoshy
797184802Sjkoshy	/* Fill in the class dependent descriptor. */
798184802Sjkoshy	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6];
799184802Sjkoshy
800184802Sjkoshy	switch (md->pmd_cputype) {
801184802Sjkoshy
802145256Sjkoshy		/*
803145256Sjkoshy		 * P6 Family Processors
804145256Sjkoshy		 */
805145256Sjkoshy	case PMC_CPU_INTEL_P6:
806145256Sjkoshy	case PMC_CPU_INTEL_CL:
807145256Sjkoshy	case PMC_CPU_INTEL_PII:
808145256Sjkoshy	case PMC_CPU_INTEL_PIII:
809145256Sjkoshy	case PMC_CPU_INTEL_PM:
810145256Sjkoshy
811184802Sjkoshy		p6_cputype = md->pmd_cputype;
812145256Sjkoshy
813184802Sjkoshy		pcd->pcd_caps		= P6_PMC_CAPS;
814184802Sjkoshy		pcd->pcd_class		= PMC_CLASS_P6;
815184802Sjkoshy		pcd->pcd_num		= P6_NPMCS;
816184802Sjkoshy		pcd->pcd_ri		= md->pmd_npmc;
817184802Sjkoshy		pcd->pcd_width		= 40;
818145256Sjkoshy
819184802Sjkoshy		pcd->pcd_allocate_pmc	= p6_allocate_pmc;
820184802Sjkoshy		pcd->pcd_config_pmc	= p6_config_pmc;
821184802Sjkoshy		pcd->pcd_describe	= p6_describe;
822184802Sjkoshy		pcd->pcd_get_config	= p6_get_config;
823184802Sjkoshy		pcd->pcd_get_msr	= p6_get_msr;
824184802Sjkoshy		pcd->pcd_pcpu_fini	= p6_pcpu_fini;
825184802Sjkoshy		pcd->pcd_pcpu_init	= p6_pcpu_init;
826184802Sjkoshy		pcd->pcd_read_pmc	= p6_read_pmc;
827184802Sjkoshy		pcd->pcd_release_pmc	= p6_release_pmc;
828184802Sjkoshy		pcd->pcd_start_pmc	= p6_start_pmc;
829184802Sjkoshy		pcd->pcd_stop_pmc	= p6_stop_pmc;
830184802Sjkoshy		pcd->pcd_write_pmc	= p6_write_pmc;
831145256Sjkoshy
832184802Sjkoshy		md->pmd_pcpu_fini	= NULL;
833184802Sjkoshy		md->pmd_pcpu_init	= NULL;
834184802Sjkoshy		md->pmd_intr		= p6_intr;
835184802Sjkoshy
836184802Sjkoshy		md->pmd_npmc	       += P6_NPMCS;
837184802Sjkoshy
838145256Sjkoshy		break;
839184802Sjkoshy
840145256Sjkoshy	default:
841145256Sjkoshy		KASSERT(0,("[p6,%d] Unknown CPU type", __LINE__));
842145256Sjkoshy		return ENOSYS;
843145256Sjkoshy	}
844145256Sjkoshy
845184802Sjkoshy	return (0);
846145256Sjkoshy}
847184802Sjkoshy
848184802Sjkoshyvoid
849184802Sjkoshypmc_p6_finalize(struct pmc_mdep *md)
850184802Sjkoshy{
851184802Sjkoshy#if	defined(INVARIANTS)
852184802Sjkoshy	int i, ncpus;
853184802Sjkoshy#endif
854184802Sjkoshy
855184802Sjkoshy	KASSERT(p6_pcpu != NULL, ("[p6,%d] NULL p6_pcpu", __LINE__));
856184802Sjkoshy
857184802Sjkoshy#if	defined(INVARIANTS)
858184802Sjkoshy	ncpus = pmc_cpu_max();
859184802Sjkoshy	for (i = 0; i < ncpus; i++)
860184802Sjkoshy		KASSERT(p6_pcpu[i] == NULL, ("[p6,%d] non-null pcpu %d",
861184802Sjkoshy		    __LINE__, i));
862184802Sjkoshy#endif
863184802Sjkoshy
864184802Sjkoshy	free(p6_pcpu, M_PMC);
865184802Sjkoshy	p6_pcpu = NULL;
866184802Sjkoshy}
867