xref: /illumos-gate/usr/src/uts/sun4u/pcbe/opl_pcbe.c (revision c7a079a8)
125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2125cf1a30Sjl /*
2231632b73Swh  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2325cf1a30Sjl  * Use is subject to license terms.
2425cf1a30Sjl  */
2525cf1a30Sjl 
2625cf1a30Sjl /*
27*c7a079a8SJonathan Haslam  * This file contains preset event names from the Performance Application
28*c7a079a8SJonathan Haslam  * Programming Interface v3.5 which included the following notice:
29*c7a079a8SJonathan Haslam  *
30*c7a079a8SJonathan Haslam  *                             Copyright (c) 2005,6
31*c7a079a8SJonathan Haslam  *                           Innovative Computing Labs
32*c7a079a8SJonathan Haslam  *                         Computer Science Department,
33*c7a079a8SJonathan Haslam  *                            University of Tennessee,
34*c7a079a8SJonathan Haslam  *                                 Knoxville, TN.
35*c7a079a8SJonathan Haslam  *                              All Rights Reserved.
36*c7a079a8SJonathan Haslam  *
37*c7a079a8SJonathan Haslam  *
38*c7a079a8SJonathan Haslam  * Redistribution and use in source and binary forms, with or without
39*c7a079a8SJonathan Haslam  * modification, are permitted provided that the following conditions are met:
40*c7a079a8SJonathan Haslam  *
41*c7a079a8SJonathan Haslam  *    * Redistributions of source code must retain the above copyright notice,
42*c7a079a8SJonathan Haslam  *      this list of conditions and the following disclaimer.
43*c7a079a8SJonathan Haslam  *    * Redistributions in binary form must reproduce the above copyright
44*c7a079a8SJonathan Haslam  *      notice, this list of conditions and the following disclaimer in the
45*c7a079a8SJonathan Haslam  *      documentation and/or other materials provided with the distribution.
46*c7a079a8SJonathan Haslam  *    * Neither the name of the University of Tennessee nor the names of its
47*c7a079a8SJonathan Haslam  *      contributors may be used to endorse or promote products derived from
48*c7a079a8SJonathan Haslam  *      this software without specific prior written permission.
49*c7a079a8SJonathan Haslam  *
50*c7a079a8SJonathan Haslam  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
51*c7a079a8SJonathan Haslam  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52*c7a079a8SJonathan Haslam  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53*c7a079a8SJonathan Haslam  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
54*c7a079a8SJonathan Haslam  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55*c7a079a8SJonathan Haslam  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56*c7a079a8SJonathan Haslam  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57*c7a079a8SJonathan Haslam  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58*c7a079a8SJonathan Haslam  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59*c7a079a8SJonathan Haslam  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60*c7a079a8SJonathan Haslam  * POSSIBILITY OF SUCH DAMAGE.
61*c7a079a8SJonathan Haslam  *
62*c7a079a8SJonathan Haslam  *
63*c7a079a8SJonathan Haslam  * This open source software license conforms to the BSD License template.
6425cf1a30Sjl  */
6525cf1a30Sjl 
66*c7a079a8SJonathan Haslam /*
67*c7a079a8SJonathan Haslam  * SPARC64 VI & VII Performance Counter Backend
68*c7a079a8SJonathan Haslam  */
6925cf1a30Sjl 
7025cf1a30Sjl #include <sys/cpuvar.h>
7125cf1a30Sjl #include <sys/systm.h>
7225cf1a30Sjl #include <sys/cmn_err.h>
7325cf1a30Sjl #include <sys/cpc_impl.h>
7425cf1a30Sjl #include <sys/cpc_pcbe.h>
7525cf1a30Sjl #include <sys/modctl.h>
7625cf1a30Sjl #include <sys/machsystm.h>
7725cf1a30Sjl #include <sys/sdt.h>
7825cf1a30Sjl #include <sys/cpu_impl.h>
7925cf1a30Sjl 
8025cf1a30Sjl static int opl_pcbe_init(void);
8125cf1a30Sjl static uint_t opl_pcbe_ncounters(void);
8225cf1a30Sjl static const char *opl_pcbe_impl_name(void);
8325cf1a30Sjl static const char *opl_pcbe_cpuref(void);
8425cf1a30Sjl static char *opl_pcbe_list_events(uint_t picnum);
8525cf1a30Sjl static char *opl_pcbe_list_attrs(void);
8625cf1a30Sjl static uint64_t opl_pcbe_event_coverage(char *event);
8725cf1a30Sjl static uint64_t opl_pcbe_overflow_bitmap(void);
8825cf1a30Sjl static int opl_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
8925cf1a30Sjl     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
9025cf1a30Sjl     void *token);
9125cf1a30Sjl static void opl_pcbe_program(void *token);
9225cf1a30Sjl static void opl_pcbe_allstop(void);
9325cf1a30Sjl static void opl_pcbe_sample(void *token);
9425cf1a30Sjl static void opl_pcbe_free(void *config);
9525cf1a30Sjl 
9625cf1a30Sjl extern void ultra_setpcr(uint64_t);
9725cf1a30Sjl extern uint64_t ultra_getpcr(void);
9825cf1a30Sjl extern void ultra_setpic(uint64_t);
9925cf1a30Sjl extern uint64_t ultra_getpic(void);
10025cf1a30Sjl extern uint64_t ultra_gettick(void);
10125cf1a30Sjl 
10225cf1a30Sjl pcbe_ops_t opl_pcbe_ops = {
10325cf1a30Sjl 	PCBE_VER_1,
10425cf1a30Sjl 	CPC_CAP_OVERFLOW_INTERRUPT,
10525cf1a30Sjl 	opl_pcbe_ncounters,
10625cf1a30Sjl 	opl_pcbe_impl_name,
10725cf1a30Sjl 	opl_pcbe_cpuref,
10825cf1a30Sjl 	opl_pcbe_list_events,
10925cf1a30Sjl 	opl_pcbe_list_attrs,
11025cf1a30Sjl 	opl_pcbe_event_coverage,
11125cf1a30Sjl 	opl_pcbe_overflow_bitmap,
11225cf1a30Sjl 	opl_pcbe_configure,
11325cf1a30Sjl 	opl_pcbe_program,
11425cf1a30Sjl 	opl_pcbe_allstop,
11525cf1a30Sjl 	opl_pcbe_sample,
11625cf1a30Sjl 	opl_pcbe_free
11725cf1a30Sjl };
11825cf1a30Sjl 
11925cf1a30Sjl typedef struct _opl_pcbe_config {
12025cf1a30Sjl 	uint8_t		opl_picno;	/* From 0 to 7 */
12125cf1a30Sjl 	uint32_t	opl_bits;	/* %pcr event code unshifted */
12225cf1a30Sjl 	uint32_t	opl_flags;	/* user/system/priv */
12325cf1a30Sjl 	uint32_t	opl_pic;	/* unshifted raw %pic value */
12425cf1a30Sjl } opl_pcbe_config_t;
12525cf1a30Sjl 
12625cf1a30Sjl struct nametable {
12725cf1a30Sjl 	const uint8_t	bits;
12825cf1a30Sjl 	const char	*name;
12925cf1a30Sjl };
13025cf1a30Sjl 
131*c7a079a8SJonathan Haslam typedef struct _opl_generic_event {
132*c7a079a8SJonathan Haslam 	char *name;
133*c7a079a8SJonathan Haslam 	char *event;
134*c7a079a8SJonathan Haslam } opl_generic_event_t;
135*c7a079a8SJonathan Haslam 
136d1e4508aSsubhan /*
137d1e4508aSsubhan  * Performance Control Register (PCR)
138d1e4508aSsubhan  *
139d1e4508aSsubhan  * +----------+-----+-----+------+----+
140d1e4508aSsubhan  * |      0   | OVF |  0  | OVR0 | 0  |
141d1e4508aSsubhan  * +----------+-----+-----+------+----+
142d1e4508aSsubhan  * 63     48  47:32  31:27   26    25
143d1e4508aSsubhan  *
144d1e4508aSsubhan  * +----+----+--- -+----+-----+---+-----+-----+----+----+----+
145d1e4508aSsubhan  * | NC |  0 | SC  | 0  | SU  | 0 | SL  |ULRO | UT | ST |PRIV|
146d1e4508aSsubhan  * +----+----+-----+----+-----+---+-----+-----+----+----+----+
147d1e4508aSsubhan  * 24:22  21  20:18  17  16:11 10  9:4     3    2    1    0
148d1e4508aSsubhan  *
1495a1b32ccSsubhan  * ULRO and OVRO bits should be on upon accessing pcr unless
1505a1b32ccSsubhan  * those fields need to be updated.
1515a1b32ccSsubhan  * Turn off these bits when updating SU/SL or OVF field
1525a1b32ccSsubhan  * (during initialization, etc.).
1535a1b32ccSsubhan  *
154d1e4508aSsubhan  *
155d1e4508aSsubhan  * Performance Instrumentation Counter (PIC)
156e98fafb9Sjl  * Four PICs are implemented in SPARC64 VI and VII,
157d1e4508aSsubhan  * each PIC is accessed using PCR.SC as a select field.
158d1e4508aSsubhan  *
159d1e4508aSsubhan  * +------------------------+--------------------------+
160d1e4508aSsubhan  * |         PICU	    |		PICL	       |
161d1e4508aSsubhan  * +------------------------+--------------------------+
162d1e4508aSsubhan  *  63			 32  31			      0
163d1e4508aSsubhan  */
164d1e4508aSsubhan 
16525cf1a30Sjl #define	PIC_MASK (((uint64_t)1 << 32) - 1)
16625cf1a30Sjl 
167d1e4508aSsubhan #define	SPARC64_VI_PCR_PRIVPIC  UINT64_C(0)
16825cf1a30Sjl 
16925cf1a30Sjl #define	CPC_SPARC64_VI_PCR_SYS_SHIFT	1
170d1e4508aSsubhan #define	CPC_SPARC64_VI_PCR_USR_SHIFT	2
17125cf1a30Sjl 
17225cf1a30Sjl #define	CPC_SPARC64_VI_PCR_PICL_SHIFT	4
17325cf1a30Sjl #define	CPC_SPARC64_VI_PCR_PICU_SHIFT	11
174d1e4508aSsubhan #define	CPC_SPARC64_VI_PCR_PIC_MASK	UINT64_C(0x3F)
17525cf1a30Sjl 
17625cf1a30Sjl #define	CPC_SPARC64_VI_NPIC		8
17725cf1a30Sjl 
17825cf1a30Sjl #define	CPC_SPARC64_VI_PCR_ULRO_SHIFT	3
17925cf1a30Sjl #define	CPC_SPARC64_VI_PCR_SC_SHIFT	18
18025cf1a30Sjl #define	CPC_SPARC64_VI_PCR_SC_MASK	UINT64_C(0x7)
18125cf1a30Sjl #define	CPC_SPARC64_VI_PCR_NC_SHIFT	22
18225cf1a30Sjl #define	CPC_SPARC64_VI_PCR_NC_MASK	UINT64_C(0x7)
18325cf1a30Sjl #define	CPC_SPARC64_VI_PCR_OVRO_SHIFT	26
18425cf1a30Sjl #define	CPC_SPARC64_VI_PCR_OVF_SHIFT	32
18525cf1a30Sjl #define	CPC_SPARC64_VI_PCR_OVF_MASK	UINT64_C(0xffff)
18625cf1a30Sjl 
18725cf1a30Sjl #define	SPARC64_VI_PCR_SYS	(UINT64_C(1) << CPC_SPARC64_VI_PCR_SYS_SHIFT)
18825cf1a30Sjl #define	SPARC64_VI_PCR_USR	(UINT64_C(1) << CPC_SPARC64_VI_PCR_USR_SHIFT)
18925cf1a30Sjl #define	SPARC64_VI_PCR_ULRO	(UINT64_C(1) << CPC_SPARC64_VI_PCR_ULRO_SHIFT)
19025cf1a30Sjl #define	SPARC64_VI_PCR_OVRO	(UINT64_C(1) << CPC_SPARC64_VI_PCR_OVRO_SHIFT)
19125cf1a30Sjl #define	SPARC64_VI_PCR_OVF	(CPC_SPARC64_VI_PCR_OVF_MASK << \
19225cf1a30Sjl 					CPC_SPARC64_VI_PCR_OVF_SHIFT)
19325cf1a30Sjl 
19425cf1a30Sjl #define	SPARC64_VI_NUM_PIC_PAIRS	4
19525cf1a30Sjl 
19625cf1a30Sjl #define	SPARC64_VI_PCR_SEL_PIC(pcr, picno) {				\
19725cf1a30Sjl 	pcr &= ~((CPC_SPARC64_VI_PCR_SC_MASK				\
19825cf1a30Sjl 		<< CPC_SPARC64_VI_PCR_SC_SHIFT));			\
19925cf1a30Sjl 									\
20025cf1a30Sjl 	pcr |= (((picno) & CPC_SPARC64_VI_PCR_SC_MASK)			\
20125cf1a30Sjl 		<< CPC_SPARC64_VI_PCR_SC_SHIFT);			\
20225cf1a30Sjl }
20325cf1a30Sjl 
20425cf1a30Sjl #define	SPARC64_VI_PCR_SEL_EVENT(pcr, sl, su) {				\
20525cf1a30Sjl 	pcr &= ~((CPC_SPARC64_VI_PCR_PIC_MASK				\
20625cf1a30Sjl 		<< CPC_SPARC64_VI_PCR_PICL_SHIFT)			\
20725cf1a30Sjl 	    | (CPC_SPARC64_VI_PCR_PIC_MASK				\
20825cf1a30Sjl 		<< CPC_SPARC64_VI_PCR_PICU_SHIFT));			\
20925cf1a30Sjl 									\
21025cf1a30Sjl 	pcr |= (((sl) & CPC_SPARC64_VI_PCR_PIC_MASK)			\
21125cf1a30Sjl 		<< CPC_SPARC64_VI_PCR_PICL_SHIFT);			\
21225cf1a30Sjl 	pcr |= (((su) & CPC_SPARC64_VI_PCR_PIC_MASK)			\
21325cf1a30Sjl 		<< CPC_SPARC64_VI_PCR_PICU_SHIFT);			\
21425cf1a30Sjl }
21525cf1a30Sjl 
2165a1b32ccSsubhan #define	SPARC64_VI_CHK_OVF(pcr, picno)					\
2175a1b32ccSsubhan 	((pcr) & (UINT64_C(1) << (CPC_SPARC64_VI_PCR_OVF_SHIFT + picno)))
2185a1b32ccSsubhan 
2195a1b32ccSsubhan #define	SPARC64_VI_CLR_OVF(pcr, picno) {				\
2205a1b32ccSsubhan 	pcr &= ~(UINT64_C(1) << (CPC_SPARC64_VI_PCR_OVF_SHIFT + picno)); \
2215a1b32ccSsubhan }
2225a1b32ccSsubhan 
22325cf1a30Sjl #define	NT_END 0xFF
224*c7a079a8SJonathan Haslam #define	CPC_GEN_END { NULL, NULL }
22525cf1a30Sjl 
22625cf1a30Sjl static const uint64_t   allstopped = SPARC64_VI_PCR_PRIVPIC |
22725cf1a30Sjl 	SPARC64_VI_PCR_ULRO | SPARC64_VI_PCR_OVRO;
22825cf1a30Sjl 
2295a1b32ccSsubhan #define	SPARC64_VI_EVENTS_comm_0		\
23025cf1a30Sjl 	{0x0,	"cycle_counts"},		\
2315a1b32ccSsubhan 	{0x1,	"instruction_counts"}
2325a1b32ccSsubhan 
2335a1b32ccSsubhan #define	SPARC64_VI_EVENTS_comm_1		\
2345a1b32ccSsubhan 	{0x5,	"op_stv_wait"},			\
23525cf1a30Sjl 	{0x8,	"load_store_instructions"},	\
23625cf1a30Sjl 	{0x9,	"branch_instructions"},		\
23725cf1a30Sjl 	{0xa,	"floating_instructions"},	\
23825cf1a30Sjl 	{0xb,	"impdep2_instructions"},	\
23925cf1a30Sjl 	{0xc,	"prefetch_instructions"}
24025cf1a30Sjl 
2415a1b32ccSsubhan #define	SPARC64_VI_EVENTS_comm_2		\
2425a1b32ccSsubhan 	{0x1a,	"active_cycle_count"}
2435a1b32ccSsubhan 
24425cf1a30Sjl static const struct nametable SPARC64_VI_names_l0[] = {
2455a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_0,
246e98fafb9Sjl 	{0x2,	"only_this_thread_active"},
247e98fafb9Sjl 	{0x3,	"w_cse_window_empty"},
248e98fafb9Sjl 	{0x4,	"w_op_stv_wait_nc_pend"},
2495a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_1,
2505a1b32ccSsubhan 	{0x12,	"flush_rs"},
2515a1b32ccSsubhan 	{0x13,	"2iid_use"},
252caf02165Ssubhan 	{0x15,	"toq_rsbr_phantom"},
25325cf1a30Sjl 	{0x16,	"trap_int_vector"},
2545a1b32ccSsubhan 	{0x18,	"ts_by_sxmiss"},
255e98fafb9Sjl 	{0x18,	"both_threads_active"},
2565a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_2,
2575a1b32ccSsubhan 	{0x1d,	"op_stv_wait_sxmiss"},
258caf02165Ssubhan 	{0x1e,	"eu_comp_wait"},
259e98fafb9Sjl 	{0x23,	"op_l1_thrashing"},
2605a1b32ccSsubhan 	{0x24,	"swpf_fail_all"},
26125cf1a30Sjl 	{0x30,	"sx_miss_wait_pf"},
26225cf1a30Sjl 	{0x31,	"jbus_cpi_count"},
2635a1b32ccSsubhan 	{0x36,	"jbus_reqbus1_busy"},
26425cf1a30Sjl 	{NT_END, ""}
26525cf1a30Sjl };
26625cf1a30Sjl 
26725cf1a30Sjl static const struct nametable SPARC64_VI_names_u0[] = {
2685a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_0,
2695a1b32ccSsubhan 	{0x2,	"instruction_flow_counts"},
2705a1b32ccSsubhan 	{0x3,	"iwr_empty"},
2715a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_1,
2725a1b32ccSsubhan 	{0x12,	"rs1"},
2735a1b32ccSsubhan 	{0x13,	"1iid_use"},
27425cf1a30Sjl 	{0x16,	"trap_all"},
2755a1b32ccSsubhan 	{0x18,	"thread_switch_all"},
276e98fafb9Sjl 	{0x18,	"only_this_thread_active"},
2775a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_2,
27831632b73Swh 	{0x1b,	"rsf_pmmi"},
2795a1b32ccSsubhan 	{0x1d,	"act_thread_suspend"},
2805a1b32ccSsubhan 	{0x1e,	"cse_window_empty"},
2815a1b32ccSsubhan 	{0x1f,	"inh_cmit_gpr_2write"},
282e98fafb9Sjl 	{0x23,	"if_l1_thrashing"},
2835a1b32ccSsubhan 	{0x24,	"swpf_success_all"},
28425cf1a30Sjl 	{0x30,	"sx_miss_wait_dm"},
28525cf1a30Sjl 	{0x31,	"jbus_bi_count"},
2865a1b32ccSsubhan 	{0x34,	"lost_softpf_pfp_full"},
2875a1b32ccSsubhan 	{0x36,	"jbus_reqbus0_busy"},
28825cf1a30Sjl 	{NT_END, ""}
28925cf1a30Sjl };
29025cf1a30Sjl 
29125cf1a30Sjl static const struct nametable SPARC64_VI_names_l1[] = {
2925a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_0,
293e98fafb9Sjl 	{0x2,	"single_mode_instructions"},
294e98fafb9Sjl 	{0x3,	"w_branch_comp_wait"},
295e98fafb9Sjl 	{0x4,	"w_op_stv_wait_sxmiss_ex"},
2965a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_1,
2975a1b32ccSsubhan 	{0x13,	"4iid_use"},
2985a1b32ccSsubhan 	{0x15,	"flush_rs"},
29925cf1a30Sjl 	{0x16,	"trap_spill"},
3005a1b32ccSsubhan 	{0x18,	"ts_by_timer"},
3015a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_2,
3025a1b32ccSsubhan 	{0x1b,	"0iid_use"},
3035a1b32ccSsubhan 	{0x1d,	"op_stv_wait_nc_pend"},
3045a1b32ccSsubhan 	{0x1e,	"0endop"},
30525cf1a30Sjl 	{0x20,	"write_op_uTLB"},
30625cf1a30Sjl 	{0x30,	"sx_miss_count_pf"},
30725cf1a30Sjl 	{0x31,	"jbus_cpd_count"},
3085a1b32ccSsubhan 	{0x32,	"snres_64"},
3095a1b32ccSsubhan 	{0x36,	"jbus_reqbus3_busy"},
31025cf1a30Sjl 	{NT_END, ""}
31125cf1a30Sjl };
31225cf1a30Sjl 
31325cf1a30Sjl static const struct nametable SPARC64_VI_names_u1[] = {
3145a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_0,
315e98fafb9Sjl 	{0x2,	"single_mode_cycle_counts"},
316e98fafb9Sjl 	{0x3,	"w_eu_comp_wait"},
317e98fafb9Sjl 	{0x4,	"w_op_stv_wait_sxmiss"},
3185a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_1,
3195a1b32ccSsubhan 	{0x13,	"3iid_use"},
32025cf1a30Sjl 	{0x16,	"trap_int_level"},
3215a1b32ccSsubhan 	{0x18,	"ts_by_data_arrive"},
322e98fafb9Sjl 	{0x18,	"both_threads_empty"},
3235a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_2,
3245a1b32ccSsubhan 	{0x1b,	"op_stv_wait_nc_pend"},
3255a1b32ccSsubhan 	{0x1d,	"op_stv_wait_sxmiss_ex"},
326caf02165Ssubhan 	{0x1e,	"branch_comp_wait"},
32725cf1a30Sjl 	{0x20,	"write_if_uTLB"},
32825cf1a30Sjl 	{0x30,	"sx_miss_count_dm"},
32925cf1a30Sjl 	{0x31,	"jbus_cpb_count"},
3305a1b32ccSsubhan 	{0x32,	"snres_256"},
3315a1b32ccSsubhan 	{0x34,	"lost_softpf_by_abort"},
3325a1b32ccSsubhan 	{0x36,	"jbus_reqbus2_busy"},
33325cf1a30Sjl 	{NT_END, ""}
33425cf1a30Sjl };
33525cf1a30Sjl 
33625cf1a30Sjl static const struct nametable SPARC64_VI_names_l2[] = {
3375a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_0,
338e98fafb9Sjl 	{0x2,	"d_move_wait"},
339e98fafb9Sjl 	{0x3,	"w_op_stv_wait"},
340e98fafb9Sjl 	{0x4,	"w_fl_comp_wait"},
3415a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_1,
3425a1b32ccSsubhan 	{0x13,	"sync_intlk"},
34325cf1a30Sjl 	{0x16,	"trap_trap_inst"},
3445a1b32ccSsubhan 	{0x18,	"ts_by_if"},
3455a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_2,
3465a1b32ccSsubhan 	{0x1e,	"fl_comp_wait"},
34725cf1a30Sjl 	{0x20,	"op_r_iu_req_mi_go"},
34825cf1a30Sjl 	{0x30,	"sx_read_count_pf"},
349caf02165Ssubhan 	{0x31,	"jbus_odrbus_busy"},
3505a1b32ccSsubhan 	{0x33,	"sx_miss_count_dm_if"},
3515a1b32ccSsubhan 	{0x36,	"jbus_odrbus1_busy"},
35225cf1a30Sjl 	{NT_END, ""}
35325cf1a30Sjl };
35425cf1a30Sjl 
35525cf1a30Sjl static const struct nametable SPARC64_VI_names_u2[] = {
3565a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_0,
3575a1b32ccSsubhan 	{0x2,	"instruction_flow_counts"},
3585a1b32ccSsubhan 	{0x3,	"iwr_empty"},
3595a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_1,
36025cf1a30Sjl 	{0x16,	"trap_fill"},
3615a1b32ccSsubhan 	{0x18,	"ts_by_intr"},
3625a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_2,
3635a1b32ccSsubhan 	{0x1b,	"flush_rs"},
3645a1b32ccSsubhan 	{0x1d,	"cse_window_empty_sp_full"},
3655a1b32ccSsubhan 	{0x1e,	"op_stv_wait_ex"},
3665a1b32ccSsubhan 	{0x1f,	"3endop"},
36725cf1a30Sjl 	{0x20,	"if_r_iu_req_mi_go"},
3685a1b32ccSsubhan 	{0x24,	"swpf_lbs_hit"},
36925cf1a30Sjl 	{0x30,	"sx_read_count_dm"},
3705a1b32ccSsubhan 	{0x31,	"jbus_reqbus_busy"},
3715a1b32ccSsubhan 	{0x33,	"sx_btc_count"},
3725a1b32ccSsubhan 	{0x36,	"jbus_odrbus0_busy"},
37325cf1a30Sjl 	{NT_END, ""}
37425cf1a30Sjl };
37525cf1a30Sjl 
37625cf1a30Sjl static const struct nametable SPARC64_VI_names_l3[] = {
3775a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_0,
378e98fafb9Sjl 	{0x2,	"xma_inst"},
379e98fafb9Sjl 	{0x3,	"w_0endop"},
380e98fafb9Sjl 	{0x4,	"w_op_stv_wait_ex"},
3815a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_1,
38225cf1a30Sjl 	{0x16,	"trap_DMMU_miss"},
3835a1b32ccSsubhan 	{0x18,	"ts_by_suspend"},
3845a1b32ccSsubhan 	{0x19,	"ts_by_other"},
3855a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_2,
3865a1b32ccSsubhan 	{0x1b,	"decall_intlk"},
3875a1b32ccSsubhan 	{0x1e,	"2endop"},
3885a1b32ccSsubhan 	{0x1f,	"op_stv_wait_sxmiss"},
38925cf1a30Sjl 	{0x20,	"op_wait_all"},
39025cf1a30Sjl 	{0x30,	"dvp_count_pf"},
3915a1b32ccSsubhan 	{0x33,	"sx_miss_count_dm_opex"},
3925a1b32ccSsubhan 	{0x36,	"jbus_odrbus3_busy"},
39325cf1a30Sjl 	{NT_END, ""}
39425cf1a30Sjl };
39525cf1a30Sjl 
39625cf1a30Sjl static const struct nametable SPARC64_VI_names_u3[] = {
3975a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_0,
398e98fafb9Sjl 	{0x2,	"cse_priority_wait"},
399e98fafb9Sjl 	{0x3,	"w_d_move"},
400e98fafb9Sjl 	{0x4,	"w_cse_window_empty_sp_full"},
4015a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_1,
4025a1b32ccSsubhan 	{0x13,	"regwin_intlk"},
4035a1b32ccSsubhan 	{0x15,	"rs1"},
40425cf1a30Sjl 	{0x16,	"trap_IMMU_miss"},
4055a1b32ccSsubhan 	SPARC64_VI_EVENTS_comm_2,
406e98fafb9Sjl 	{0x1d,	"both_threads_suspended"},
4075a1b32ccSsubhan 	{0x1e,	"1endop"},
4085a1b32ccSsubhan 	{0x1f,	"op_stv_wait_sxmiss_ex"},
40925cf1a30Sjl 	{0x20,	"if_wait_all"},
41025cf1a30Sjl 	{0x30,	"dvp_count_dm"},
4115a1b32ccSsubhan 	{0x33,	"sx_miss_count_dm_opsh"},
4125a1b32ccSsubhan 	{0x36,	"jbus_odrbus2_busy"},
41325cf1a30Sjl 	{NT_END, ""}
41425cf1a30Sjl };
41525cf1a30Sjl 
4165a1b32ccSsubhan #undef	SPARC64_VI_EVENTS_comm_0
4175a1b32ccSsubhan #undef	SPARC64_VI_EVENTS_comm_1
4185a1b32ccSsubhan #undef	SPARC64_VI_EVENTS_comm_2
41925cf1a30Sjl 
42025cf1a30Sjl static const struct nametable *SPARC64_VI_names[CPC_SPARC64_VI_NPIC] = {
42125cf1a30Sjl 	SPARC64_VI_names_l0,
42225cf1a30Sjl 	SPARC64_VI_names_u0,
42325cf1a30Sjl 	SPARC64_VI_names_l1,
42425cf1a30Sjl 	SPARC64_VI_names_u1,
42525cf1a30Sjl 	SPARC64_VI_names_l2,
42625cf1a30Sjl 	SPARC64_VI_names_u2,
42725cf1a30Sjl 	SPARC64_VI_names_l3,
42825cf1a30Sjl 	SPARC64_VI_names_u3
42925cf1a30Sjl };
43025cf1a30Sjl 
43125cf1a30Sjl opl_pcbe_config_t nullpic[CPC_SPARC64_VI_NPIC] = {
43225cf1a30Sjl 	{0, 0x3f, 0, 0},
43325cf1a30Sjl 	{1, 0x3f, 0, 0},
43425cf1a30Sjl 	{2, 0x3f, 0, 0},
43525cf1a30Sjl 	{3, 0x3f, 0, 0},
43625cf1a30Sjl 	{4, 0x3f, 0, 0},
43725cf1a30Sjl 	{5, 0x3f, 0, 0},
43825cf1a30Sjl 	{6, 0x3f, 0, 0},
43925cf1a30Sjl 	{7, 0x3f, 0, 0}
44025cf1a30Sjl };
44125cf1a30Sjl 
442*c7a079a8SJonathan Haslam #define	SPARC64_VI_GENERIC_EVENTS_comm				\
443*c7a079a8SJonathan Haslam 	{ "PAPI_tot_cyc",	"cycle_counts" },		\
444*c7a079a8SJonathan Haslam 	{ "PAPI_tot_ins",	"instruction_counts" },		\
445*c7a079a8SJonathan Haslam 	{ "PAPI_br_tkn",	"branch_instructions" },	\
446*c7a079a8SJonathan Haslam 	{ "PAPI_fp_ops",	"floating_instructions" },	\
447*c7a079a8SJonathan Haslam 	{ "PAPI_fma_ins",	"impdep2_instructions" }
448*c7a079a8SJonathan Haslam 
449*c7a079a8SJonathan Haslam static const opl_generic_event_t SPARC64_VI_generic_names_l0[] = {
450*c7a079a8SJonathan Haslam 	SPARC64_VI_GENERIC_EVENTS_comm,
451*c7a079a8SJonathan Haslam 	CPC_GEN_END
452*c7a079a8SJonathan Haslam };
453*c7a079a8SJonathan Haslam 
454*c7a079a8SJonathan Haslam static const opl_generic_event_t SPARC64_VI_generic_names_u0[] = {
455*c7a079a8SJonathan Haslam 	SPARC64_VI_GENERIC_EVENTS_comm,
456*c7a079a8SJonathan Haslam 	CPC_GEN_END
457*c7a079a8SJonathan Haslam };
458*c7a079a8SJonathan Haslam 
459*c7a079a8SJonathan Haslam static const opl_generic_event_t SPARC64_VI_generic_names_l1[] = {
460*c7a079a8SJonathan Haslam 	SPARC64_VI_GENERIC_EVENTS_comm,
461*c7a079a8SJonathan Haslam 	CPC_GEN_END
462*c7a079a8SJonathan Haslam };
463*c7a079a8SJonathan Haslam 
464*c7a079a8SJonathan Haslam static const opl_generic_event_t SPARC64_VI_generic_names_u1[] = {
465*c7a079a8SJonathan Haslam 	SPARC64_VI_GENERIC_EVENTS_comm,
466*c7a079a8SJonathan Haslam 	CPC_GEN_END
467*c7a079a8SJonathan Haslam };
468*c7a079a8SJonathan Haslam 
469*c7a079a8SJonathan Haslam static const opl_generic_event_t SPARC64_VI_generic_names_l2[] = {
470*c7a079a8SJonathan Haslam 	SPARC64_VI_GENERIC_EVENTS_comm,
471*c7a079a8SJonathan Haslam 	{ "PAPI_l1_dcm",	"op_r_iu_req_mi_go" },
472*c7a079a8SJonathan Haslam 	CPC_GEN_END
473*c7a079a8SJonathan Haslam };
474*c7a079a8SJonathan Haslam 
475*c7a079a8SJonathan Haslam static const opl_generic_event_t SPARC64_VI_generic_names_u2[] = {
476*c7a079a8SJonathan Haslam 	SPARC64_VI_GENERIC_EVENTS_comm,
477*c7a079a8SJonathan Haslam 	{ "PAPI_l1_icm",	"if_r_iu_req_mi_go" },
478*c7a079a8SJonathan Haslam 	CPC_GEN_END
479*c7a079a8SJonathan Haslam };
480*c7a079a8SJonathan Haslam 
481*c7a079a8SJonathan Haslam static const opl_generic_event_t SPARC64_VI_generic_names_l3[] = {
482*c7a079a8SJonathan Haslam 	SPARC64_VI_GENERIC_EVENTS_comm,
483*c7a079a8SJonathan Haslam 	{ "PAPI_tlb_dm",	"trap_DMMU_miss" },
484*c7a079a8SJonathan Haslam 	CPC_GEN_END
485*c7a079a8SJonathan Haslam };
486*c7a079a8SJonathan Haslam 
487*c7a079a8SJonathan Haslam static const opl_generic_event_t SPARC64_VI_generic_names_u3[] = {
488*c7a079a8SJonathan Haslam 	SPARC64_VI_GENERIC_EVENTS_comm,
489*c7a079a8SJonathan Haslam 	{ "PAPI_tlb_im",	"trap_IMMU_miss" },
490*c7a079a8SJonathan Haslam 	CPC_GEN_END
491*c7a079a8SJonathan Haslam };
492*c7a079a8SJonathan Haslam 
493*c7a079a8SJonathan Haslam static const opl_generic_event_t
494*c7a079a8SJonathan Haslam 	*SPARC64_VI_generic_names[CPC_SPARC64_VI_NPIC] = {
495*c7a079a8SJonathan Haslam 	SPARC64_VI_generic_names_l0,
496*c7a079a8SJonathan Haslam 	SPARC64_VI_generic_names_u0,
497*c7a079a8SJonathan Haslam 	SPARC64_VI_generic_names_l1,
498*c7a079a8SJonathan Haslam 	SPARC64_VI_generic_names_u1,
499*c7a079a8SJonathan Haslam 	SPARC64_VI_generic_names_l2,
500*c7a079a8SJonathan Haslam 	SPARC64_VI_generic_names_u2,
501*c7a079a8SJonathan Haslam 	SPARC64_VI_generic_names_l3,
502*c7a079a8SJonathan Haslam 	SPARC64_VI_generic_names_u3
503*c7a079a8SJonathan Haslam };
504*c7a079a8SJonathan Haslam 
50525cf1a30Sjl static const struct nametable **events;
506*c7a079a8SJonathan Haslam static const opl_generic_event_t **generic_events;
50725cf1a30Sjl static const char *opl_impl_name;
50825cf1a30Sjl static const char *opl_cpuref;
50925cf1a30Sjl static char *pic_events[CPC_SPARC64_VI_NPIC];
51025cf1a30Sjl 
511e98fafb9Sjl static const char *sp_6_ref = "See the \"SPARC64 VI extensions\" and "
512e98fafb9Sjl 	"\"SPARC64 VII extensions\" for descriptions of these events.";
51325cf1a30Sjl 
51425cf1a30Sjl static int
opl_pcbe_init(void)51525cf1a30Sjl opl_pcbe_init(void)
51625cf1a30Sjl {
517*c7a079a8SJonathan Haslam 	const struct nametable		*n;
518*c7a079a8SJonathan Haslam 	const opl_generic_event_t	*gevp;
519*c7a079a8SJonathan Haslam 	int				i;
520*c7a079a8SJonathan Haslam 	size_t				size;
52125cf1a30Sjl 
52225cf1a30Sjl 	/*
52325cf1a30Sjl 	 * Discover type of CPU
52425cf1a30Sjl 	 *
52525cf1a30Sjl 	 * Point nametable to that CPU's table
52625cf1a30Sjl 	 */
52725cf1a30Sjl 	switch (ULTRA_VER_IMPL(ultra_getver())) {
52825cf1a30Sjl 	case OLYMPUS_C_IMPL:
529e98fafb9Sjl 	case JUPITER_IMPL:
53025cf1a30Sjl 		events = SPARC64_VI_names;
531*c7a079a8SJonathan Haslam 		generic_events = SPARC64_VI_generic_names;
532e98fafb9Sjl 		opl_impl_name = "SPARC64 VI & VII";
53325cf1a30Sjl 		opl_cpuref = sp_6_ref;
53425cf1a30Sjl 		break;
53525cf1a30Sjl 	default:
53625cf1a30Sjl 		return (-1);
53725cf1a30Sjl 	}
53825cf1a30Sjl 
53925cf1a30Sjl 	/*
54025cf1a30Sjl 	 * Initialize the list of events for each PIC.
54125cf1a30Sjl 	 * Do two passes: one to compute the size necessary and another
54225cf1a30Sjl 	 * to copy the strings. Need room for event, comma, and NULL terminator.
54325cf1a30Sjl 	 */
54425cf1a30Sjl 	for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) {
54525cf1a30Sjl 		size = 0;
54625cf1a30Sjl 		for (n = events[i]; n->bits != NT_END; n++)
54725cf1a30Sjl 			size += strlen(n->name) + 1;
548*c7a079a8SJonathan Haslam 		for (gevp = generic_events[i]; gevp->name != NULL; gevp++)
549*c7a079a8SJonathan Haslam 			size += strlen(gevp->name) + 1;
55025cf1a30Sjl 		pic_events[i] = kmem_alloc(size + 1, KM_SLEEP);
55125cf1a30Sjl 		*pic_events[i] = '\0';
55225cf1a30Sjl 		for (n = events[i]; n->bits != NT_END; n++) {
55325cf1a30Sjl 			(void) strcat(pic_events[i], n->name);
55425cf1a30Sjl 			(void) strcat(pic_events[i], ",");
55525cf1a30Sjl 		}
556*c7a079a8SJonathan Haslam 		for (gevp = generic_events[i]; gevp->name != NULL; gevp++) {
557*c7a079a8SJonathan Haslam 			(void) strcat(pic_events[i], gevp->name);
558*c7a079a8SJonathan Haslam 			(void) strcat(pic_events[i], ",");
559*c7a079a8SJonathan Haslam 		}
560*c7a079a8SJonathan Haslam 
56125cf1a30Sjl 		/*
56225cf1a30Sjl 		 * Remove trailing comma.
56325cf1a30Sjl 		 */
56425cf1a30Sjl 		pic_events[i][size - 1] = '\0';
56525cf1a30Sjl 	}
56625cf1a30Sjl 
56725cf1a30Sjl 	return (0);
56825cf1a30Sjl }
56925cf1a30Sjl 
57025cf1a30Sjl static uint_t
opl_pcbe_ncounters(void)57125cf1a30Sjl opl_pcbe_ncounters(void)
57225cf1a30Sjl {
57325cf1a30Sjl 	return (CPC_SPARC64_VI_NPIC);
57425cf1a30Sjl }
57525cf1a30Sjl 
57625cf1a30Sjl static const char *
opl_pcbe_impl_name(void)57725cf1a30Sjl opl_pcbe_impl_name(void)
57825cf1a30Sjl {
57925cf1a30Sjl 	return (opl_impl_name);
58025cf1a30Sjl }
58125cf1a30Sjl 
58225cf1a30Sjl static const char *
opl_pcbe_cpuref(void)58325cf1a30Sjl opl_pcbe_cpuref(void)
58425cf1a30Sjl {
58525cf1a30Sjl 	return (opl_cpuref);
58625cf1a30Sjl }
58725cf1a30Sjl 
58825cf1a30Sjl static char *
opl_pcbe_list_events(uint_t picnum)58925cf1a30Sjl opl_pcbe_list_events(uint_t picnum)
59025cf1a30Sjl {
59125cf1a30Sjl 	ASSERT(picnum >= 0 && picnum < cpc_ncounters);
59225cf1a30Sjl 
59325cf1a30Sjl 	return (pic_events[picnum]);
59425cf1a30Sjl }
59525cf1a30Sjl 
59625cf1a30Sjl static char *
opl_pcbe_list_attrs(void)59725cf1a30Sjl opl_pcbe_list_attrs(void)
59825cf1a30Sjl {
59925cf1a30Sjl 	return ("");
60025cf1a30Sjl }
60125cf1a30Sjl 
602*c7a079a8SJonathan Haslam static const opl_generic_event_t *
find_generic_event(int regno,char * name)603*c7a079a8SJonathan Haslam find_generic_event(int regno, char *name)
604*c7a079a8SJonathan Haslam {
605*c7a079a8SJonathan Haslam 	const opl_generic_event_t *gevp;
606*c7a079a8SJonathan Haslam 
607*c7a079a8SJonathan Haslam 	for (gevp = generic_events[regno]; gevp->name != NULL; gevp++)
608*c7a079a8SJonathan Haslam 		if (strcmp(name, gevp->name) == 0)
609*c7a079a8SJonathan Haslam 			return (gevp);
610*c7a079a8SJonathan Haslam 
611*c7a079a8SJonathan Haslam 	return (NULL);
612*c7a079a8SJonathan Haslam }
613*c7a079a8SJonathan Haslam 
61425cf1a30Sjl static const struct nametable *
find_event(int regno,char * name)61525cf1a30Sjl find_event(int regno, char *name)
61625cf1a30Sjl {
61725cf1a30Sjl 	const struct nametable *n;
61825cf1a30Sjl 
61925cf1a30Sjl 	n = events[regno];
62025cf1a30Sjl 
62125cf1a30Sjl 	for (; n->bits != NT_END; n++)
62225cf1a30Sjl 		if (strcmp(name, n->name) == 0)
62325cf1a30Sjl 			return (n);
62425cf1a30Sjl 
62525cf1a30Sjl 	return (NULL);
62625cf1a30Sjl }
62725cf1a30Sjl 
62825cf1a30Sjl static uint64_t
opl_pcbe_event_coverage(char * event)62925cf1a30Sjl opl_pcbe_event_coverage(char *event)
63025cf1a30Sjl {
63125cf1a30Sjl 	uint64_t bitmap = 0;
63225cf1a30Sjl 
63325cf1a30Sjl 	int	i;
63425cf1a30Sjl 	for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) {
635*c7a079a8SJonathan Haslam 		if ((find_event(i, event) != NULL) ||
636*c7a079a8SJonathan Haslam 		    (find_generic_event(i, event) != NULL))
63725cf1a30Sjl 			bitmap |= (1 << i);
63825cf1a30Sjl 	}
63925cf1a30Sjl 
64025cf1a30Sjl 	return (bitmap);
64125cf1a30Sjl }
64225cf1a30Sjl 
64325cf1a30Sjl /*
644d1e4508aSsubhan  * Check if counter overflow and clear it.
64525cf1a30Sjl  */
64625cf1a30Sjl static uint64_t
opl_pcbe_overflow_bitmap(void)64725cf1a30Sjl opl_pcbe_overflow_bitmap(void)
64825cf1a30Sjl {
6495a1b32ccSsubhan 	uint64_t	pcr;
65025cf1a30Sjl 
65125cf1a30Sjl 	pcr = ultra_getpcr();
652d1e4508aSsubhan 	DTRACE_PROBE1(sparc64__getpcr, uint64_t, pcr);
653d1e4508aSsubhan 
6545a1b32ccSsubhan 	return ((pcr & SPARC64_VI_PCR_OVF) >> CPC_SPARC64_VI_PCR_OVF_SHIFT);
65525cf1a30Sjl }
65625cf1a30Sjl 
65725cf1a30Sjl /*ARGSUSED*/
65825cf1a30Sjl static int
opl_pcbe_configure(uint_t picnum,char * event,uint64_t preset,uint32_t flags,uint_t nattrs,kcpc_attr_t * attrs,void ** data,void * token)65925cf1a30Sjl opl_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
66025cf1a30Sjl     uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
66125cf1a30Sjl {
662*c7a079a8SJonathan Haslam 	opl_pcbe_config_t		*conf;
663*c7a079a8SJonathan Haslam 	const struct nametable		*n;
664*c7a079a8SJonathan Haslam 	const opl_generic_event_t	*gevp;
665*c7a079a8SJonathan Haslam 	opl_pcbe_config_t		*other_config;
66625cf1a30Sjl 
66725cf1a30Sjl 	/*
66825cf1a30Sjl 	 * If we've been handed an existing configuration, we need only preset
66925cf1a30Sjl 	 * the counter value.
67025cf1a30Sjl 	 */
67125cf1a30Sjl 	if (*data != NULL) {
67225cf1a30Sjl 		conf = *data;
67325cf1a30Sjl 		conf->opl_pic = (uint32_t)preset;
67425cf1a30Sjl 		return (0);
67525cf1a30Sjl 	}
67625cf1a30Sjl 
67725cf1a30Sjl 	if (picnum < 0 || picnum >= CPC_SPARC64_VI_NPIC)
67825cf1a30Sjl 		return (CPC_INVALID_PICNUM);
67925cf1a30Sjl 
68025cf1a30Sjl 	if (nattrs != 0)
68125cf1a30Sjl 		return (CPC_INVALID_ATTRIBUTE);
68225cf1a30Sjl 
68325cf1a30Sjl 	/*
68425cf1a30Sjl 	 * Find other requests that will be programmed with this one, and ensure
68525cf1a30Sjl 	 * the flags don't conflict.
68625cf1a30Sjl 	 */
68725cf1a30Sjl 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
68825cf1a30Sjl 	    (other_config->opl_flags != flags))
68925cf1a30Sjl 		return (CPC_CONFLICTING_REQS);
69025cf1a30Sjl 
691*c7a079a8SJonathan Haslam 	if ((n = find_event(picnum, event)) == NULL) {
692*c7a079a8SJonathan Haslam 		if ((gevp = find_generic_event(picnum, event)) != NULL) {
693*c7a079a8SJonathan Haslam 			n = find_event(picnum, gevp->event);
694*c7a079a8SJonathan Haslam 			ASSERT(n != NULL);
695*c7a079a8SJonathan Haslam 		} else {
696*c7a079a8SJonathan Haslam 			return (CPC_INVALID_EVENT);
697*c7a079a8SJonathan Haslam 		}
698*c7a079a8SJonathan Haslam 	}
69925cf1a30Sjl 
70025cf1a30Sjl 	conf = kmem_alloc(sizeof (opl_pcbe_config_t), KM_SLEEP);
70125cf1a30Sjl 
70225cf1a30Sjl 	conf->opl_picno = picnum;
70325cf1a30Sjl 	conf->opl_bits = (uint32_t)n->bits;
70425cf1a30Sjl 	conf->opl_flags = flags;
70525cf1a30Sjl 	conf->opl_pic = (uint32_t)preset;
70625cf1a30Sjl 
70725cf1a30Sjl 	*data = conf;
70825cf1a30Sjl 	return (0);
70925cf1a30Sjl }
71025cf1a30Sjl 
71125cf1a30Sjl static void
opl_pcbe_program(void * token)71225cf1a30Sjl opl_pcbe_program(void *token)
71325cf1a30Sjl {
71425cf1a30Sjl 	opl_pcbe_config_t	*pic[CPC_SPARC64_VI_NPIC];
71525cf1a30Sjl 	opl_pcbe_config_t	*firstconfig;
71625cf1a30Sjl 	opl_pcbe_config_t	*tmp;
71725cf1a30Sjl 	uint64_t		pcr;
71825cf1a30Sjl 	uint64_t		curpic;
71925cf1a30Sjl 	uint8_t			bitmap = 0;	/* for used pic config */
72025cf1a30Sjl 	int			i;
72125cf1a30Sjl 	opl_pcbe_config_t	dummypic[CPC_SPARC64_VI_NPIC];
72225cf1a30Sjl 
72325cf1a30Sjl 	/* Get next pic config */
72425cf1a30Sjl 	firstconfig = tmp = kcpc_next_config(token, NULL, NULL);
72525cf1a30Sjl 
72625cf1a30Sjl 	while (tmp != NULL) {
72725cf1a30Sjl 		ASSERT(tmp->opl_picno < CPC_SPARC64_VI_NPIC);
72825cf1a30Sjl 		ASSERT(firstconfig->opl_flags == tmp->opl_flags);
72925cf1a30Sjl 		pic[tmp->opl_picno] = tmp;
73025cf1a30Sjl 		bitmap |= (uint8_t)(1 << tmp->opl_picno);
73125cf1a30Sjl 		tmp = kcpc_next_config(token, tmp, NULL);
73225cf1a30Sjl 	}
73325cf1a30Sjl 	if (bitmap == 0)
73425cf1a30Sjl 		panic("opl_pcbe: token %p has no configs", token);
73525cf1a30Sjl 
73625cf1a30Sjl 	/* Fill in unused pic config */
73725cf1a30Sjl 	for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) {
73825cf1a30Sjl 		if (bitmap & (1 << i))
73925cf1a30Sjl 			continue;
74025cf1a30Sjl 
74125cf1a30Sjl 		dummypic[i] = nullpic[i];
74225cf1a30Sjl 		dummypic[i].opl_flags = firstconfig->opl_flags;
743d1e4508aSsubhan 		pic[i] = &dummypic[i];
74425cf1a30Sjl 	}
74525cf1a30Sjl 
74625cf1a30Sjl 	/*
74725cf1a30Sjl 	 * For each counter pair, initialize event settings and
74825cf1a30Sjl 	 * counter values.
74925cf1a30Sjl 	 */
75025cf1a30Sjl 	ultra_setpcr(allstopped);
75125cf1a30Sjl 	pcr = allstopped;
75225cf1a30Sjl 	pcr &= ~SPARC64_VI_PCR_ULRO;
75325cf1a30Sjl 	for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) {
75425cf1a30Sjl 		SPARC64_VI_PCR_SEL_PIC(pcr, i);
75525cf1a30Sjl 		SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits,
75625cf1a30Sjl 		    pic[i*2 + 1]->opl_bits);
75725cf1a30Sjl 
75825cf1a30Sjl 		ultra_setpcr(pcr);
75925cf1a30Sjl 		curpic = (uint64_t)(pic[i*2]->opl_pic |
76025cf1a30Sjl 		    ((uint64_t)pic[i*2 + 1]->opl_pic << 32));
76125cf1a30Sjl 		ultra_setpic(curpic);
76225cf1a30Sjl 	}
76325cf1a30Sjl 
76425cf1a30Sjl 	/*
76525cf1a30Sjl 	 * For each counter pair, enable the trace flags to start
76625cf1a30Sjl 	 * counting. Re-read the counters to sample the counter value now
76725cf1a30Sjl 	 * and use that as the baseline for future samples.
76825cf1a30Sjl 	 */
76925cf1a30Sjl 
770d1e4508aSsubhan 	/* Get PCR */
77125cf1a30Sjl 	pcr = ultra_getpcr();
7725a1b32ccSsubhan 	pcr |= SPARC64_VI_PCR_ULRO;
7735a1b32ccSsubhan 	pcr &= ~(SPARC64_VI_PCR_OVRO | SPARC64_VI_PCR_OVF);
774d1e4508aSsubhan 
77525cf1a30Sjl 	if (pic[0]->opl_flags & CPC_COUNT_USER)
77625cf1a30Sjl 		pcr |= SPARC64_VI_PCR_USR;
77725cf1a30Sjl 	if (pic[0]->opl_flags & CPC_COUNT_SYSTEM)
77825cf1a30Sjl 		pcr |= SPARC64_VI_PCR_SYS;
77925cf1a30Sjl 
78025cf1a30Sjl 	/* Set counter values */
781d1e4508aSsubhan 
78225cf1a30Sjl 	for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) {
78325cf1a30Sjl 		SPARC64_VI_PCR_SEL_PIC(pcr, i);
78425cf1a30Sjl 		SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits,
78525cf1a30Sjl 		    pic[i*2 + 1]->opl_bits);
78625cf1a30Sjl 
78725cf1a30Sjl 		ultra_setpcr(pcr);
788d1e4508aSsubhan 		DTRACE_PROBE1(sparc64__setpcr, uint64_t, pcr);
789d1e4508aSsubhan 
79025cf1a30Sjl 		curpic = ultra_getpic();
791d1e4508aSsubhan 		DTRACE_PROBE1(sparc64__newpic, uint64_t, curpic);
79225cf1a30Sjl 		pic[i*2]->opl_pic = (uint32_t)(curpic & PIC_MASK);
79325cf1a30Sjl 		pic[i*2 + 1]->opl_pic = (uint32_t)(curpic >> 32);
79425cf1a30Sjl 	}
7955a1b32ccSsubhan 	pcr |= SPARC64_VI_PCR_OVRO;
7965a1b32ccSsubhan 	ultra_setpcr(pcr);
79725cf1a30Sjl }
79825cf1a30Sjl 
79925cf1a30Sjl static void
opl_pcbe_allstop(void)80025cf1a30Sjl opl_pcbe_allstop(void)
80125cf1a30Sjl {
80225cf1a30Sjl 	ultra_setpcr(allstopped);
80325cf1a30Sjl }
80425cf1a30Sjl 
80525cf1a30Sjl 
80625cf1a30Sjl static void
opl_pcbe_sample(void * token)80725cf1a30Sjl opl_pcbe_sample(void *token)
80825cf1a30Sjl {
80925cf1a30Sjl 	uint64_t		curpic;
81025cf1a30Sjl 	uint64_t		pcr;
8115a1b32ccSsubhan 	uint64_t		overflow;
81225cf1a30Sjl 	int64_t			diff;
81325cf1a30Sjl 	uint64_t		*pic_data[CPC_SPARC64_VI_NPIC];
81425cf1a30Sjl 	uint64_t		*dtmp;
81525cf1a30Sjl 	opl_pcbe_config_t	*pic[CPC_SPARC64_VI_NPIC];
81625cf1a30Sjl 	opl_pcbe_config_t	*ctmp;
81725cf1a30Sjl 	opl_pcbe_config_t	*firstconfig;
81825cf1a30Sjl 	uint8_t			bitmap = 0;	/* for used pic config */
81925cf1a30Sjl 	int			i;
82025cf1a30Sjl 	opl_pcbe_config_t dummypic[CPC_SPARC64_VI_NPIC];
82125cf1a30Sjl 	uint64_t dummypic_data[CPC_SPARC64_VI_NPIC];
82225cf1a30Sjl 
82325cf1a30Sjl 	/* Get next pic config */
82425cf1a30Sjl 	firstconfig = ctmp = kcpc_next_config(token, NULL, &dtmp);
82525cf1a30Sjl 
82625cf1a30Sjl 	while (ctmp != NULL) {
82725cf1a30Sjl 		ASSERT(ctmp->opl_picno < CPC_SPARC64_VI_NPIC);
82825cf1a30Sjl 		ASSERT(firstconfig->opl_flags == ctmp->opl_flags);
82925cf1a30Sjl 		pic[ctmp->opl_picno] = ctmp;
83025cf1a30Sjl 		pic_data[ctmp->opl_picno] = dtmp;
83125cf1a30Sjl 		bitmap |= (uint8_t)(1 << ctmp->opl_picno);
83225cf1a30Sjl 		ctmp = kcpc_next_config(token, ctmp, &dtmp);
83325cf1a30Sjl 	}
83425cf1a30Sjl 	if (bitmap == 0)
83525cf1a30Sjl 		panic("opl_pcbe: token %p has no configs", token);
83625cf1a30Sjl 
83725cf1a30Sjl 	/* Fill in unuse pic config */
83825cf1a30Sjl 	for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) {
83925cf1a30Sjl 		if (bitmap & (1 << i))
84025cf1a30Sjl 			continue;
84125cf1a30Sjl 
84225cf1a30Sjl 		dummypic[i] = nullpic[i];
84325cf1a30Sjl 		dummypic[i].opl_flags = firstconfig->opl_flags;
84425cf1a30Sjl 		pic[i] = &dummypic[i];
84525cf1a30Sjl 
84625cf1a30Sjl 		dummypic_data[i] = 0;
84725cf1a30Sjl 		pic_data[i] = &dummypic_data[i];
84825cf1a30Sjl 	}
84925cf1a30Sjl 
85025cf1a30Sjl 	pcr = ultra_getpcr();
8515a1b32ccSsubhan 	pcr &= ~SPARC64_VI_PCR_OVRO;
8525a1b32ccSsubhan 	pcr |= SPARC64_VI_PCR_ULRO;
85325cf1a30Sjl 
85425cf1a30Sjl 	for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) {
85525cf1a30Sjl 		SPARC64_VI_PCR_SEL_PIC(pcr, i);
85625cf1a30Sjl 		SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits,
85725cf1a30Sjl 		    pic[i*2 + 1]->opl_bits);
85825cf1a30Sjl 
85925cf1a30Sjl 		ultra_setpcr(pcr);
860d1e4508aSsubhan 
86125cf1a30Sjl 		curpic = ultra_getpic();
862d1e4508aSsubhan 		DTRACE_PROBE1(sparc64__getpic, unit64_t, curpic);
86325cf1a30Sjl 
8645a1b32ccSsubhan 		diff = (curpic & PIC_MASK) - (uint64_t)pic[i*2]->opl_pic;
8655a1b32ccSsubhan 		overflow = SPARC64_VI_CHK_OVF(pcr, i*2);
8665a1b32ccSsubhan 		if (overflow || (diff < 0)) {
8675a1b32ccSsubhan 			SPARC64_VI_CLR_OVF(pcr, i*2);
8685a1b32ccSsubhan 			ultra_setpcr(pcr);
86925cf1a30Sjl 			diff += (1ll << 32);
8705a1b32ccSsubhan 		}
87125cf1a30Sjl 		*pic_data[i*2] += diff;
87225cf1a30Sjl 
8735a1b32ccSsubhan 		diff = (curpic >> 32) - (uint64_t)pic[i*2 + 1]->opl_pic;
8745a1b32ccSsubhan 		overflow = SPARC64_VI_CHK_OVF(pcr, i*2 + 1);
8755a1b32ccSsubhan 		if (overflow || (diff < 0)) {
8765a1b32ccSsubhan 			SPARC64_VI_CLR_OVF(pcr, i*2 + 1);
8775a1b32ccSsubhan 			ultra_setpcr(pcr);
87825cf1a30Sjl 			diff += (1ll << 32);
8795a1b32ccSsubhan 		}
88025cf1a30Sjl 		*pic_data[i*2 + 1] += diff;
88125cf1a30Sjl 
88225cf1a30Sjl 		pic[i*2]->opl_pic = (uint32_t)(curpic & PIC_MASK);
88325cf1a30Sjl 		pic[i*2 + 1]->opl_pic = (uint32_t)(curpic >> 32);
88425cf1a30Sjl 	}
8855a1b32ccSsubhan 	pcr = ultra_getpcr();
8865a1b32ccSsubhan 	pcr |= SPARC64_VI_PCR_OVRO;
8875a1b32ccSsubhan 	ultra_setpcr(pcr);
88825cf1a30Sjl }
88925cf1a30Sjl 
89025cf1a30Sjl static void
opl_pcbe_free(void * config)89125cf1a30Sjl opl_pcbe_free(void *config)
89225cf1a30Sjl {
89325cf1a30Sjl 	kmem_free(config, sizeof (opl_pcbe_config_t));
89425cf1a30Sjl }
89525cf1a30Sjl 
89625cf1a30Sjl 
89725cf1a30Sjl static struct modlpcbe modlpcbe = {
89825cf1a30Sjl 	&mod_pcbeops,
899820c9f58Skk 	"SPARC64 VI&VII Perf Cntrs",
90025cf1a30Sjl 	&opl_pcbe_ops
90125cf1a30Sjl };
90225cf1a30Sjl 
90325cf1a30Sjl static struct modlinkage modl = {
90425cf1a30Sjl 	MODREV_1,
90525cf1a30Sjl 	&modlpcbe,
90625cf1a30Sjl };
90725cf1a30Sjl 
90825cf1a30Sjl int
_init(void)90925cf1a30Sjl _init(void)
91025cf1a30Sjl {
91125cf1a30Sjl 	if (opl_pcbe_init() != 0)
91225cf1a30Sjl 		return (ENOTSUP);
91325cf1a30Sjl 	return (mod_install(&modl));
91425cf1a30Sjl }
91525cf1a30Sjl 
91625cf1a30Sjl int
_fini(void)91725cf1a30Sjl _fini(void)
91825cf1a30Sjl {
91925cf1a30Sjl 	return (mod_remove(&modl));
92025cf1a30Sjl }
92125cf1a30Sjl 
92225cf1a30Sjl int
_info(struct modinfo * mi)92325cf1a30Sjl _info(struct modinfo *mi)
92425cf1a30Sjl {
92525cf1a30Sjl 	return (mod_info(&modl, mi));
92625cf1a30Sjl }
927