xref: /titanic_51/usr/src/uts/sun4v/pcbe/niagara2_pcbe.c (revision c4c6ba57afa115b6a7e20afa6a2da6c99f521616)
144961713Sgirish /*
244961713Sgirish  * CDDL HEADER START
344961713Sgirish  *
444961713Sgirish  * The contents of this file are subject to the terms of the
544961713Sgirish  * Common Development and Distribution License (the "License").
644961713Sgirish  * You may not use this file except in compliance with the License.
744961713Sgirish  *
844961713Sgirish  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
944961713Sgirish  * or http://www.opensolaris.org/os/licensing.
1044961713Sgirish  * See the License for the specific language governing permissions
1144961713Sgirish  * and limitations under the License.
1244961713Sgirish  *
1344961713Sgirish  * When distributing Covered Code, include this CDDL HEADER in each
1444961713Sgirish  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1544961713Sgirish  * If applicable, add the following below this CDDL HEADER, with the
1644961713Sgirish  * fields enclosed by brackets "[]" replaced with your own identifying
1744961713Sgirish  * information: Portions Copyright [yyyy] [name of copyright owner]
1844961713Sgirish  *
1944961713Sgirish  * CDDL HEADER END
2044961713Sgirish  */
2144961713Sgirish /*
22b80c1b63SSree Vemuri  * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
2344961713Sgirish  */
2444961713Sgirish 
2544961713Sgirish /*
26c7a079a8SJonathan Haslam  * This file contains preset event names from the Performance Application
27c7a079a8SJonathan Haslam  * Programming Interface v3.5 which included the following notice:
28c7a079a8SJonathan Haslam  *
29c7a079a8SJonathan Haslam  *                             Copyright (c) 2005,6
30c7a079a8SJonathan Haslam  *                           Innovative Computing Labs
31c7a079a8SJonathan Haslam  *                         Computer Science Department,
32c7a079a8SJonathan Haslam  *                            University of Tennessee,
33c7a079a8SJonathan Haslam  *                                 Knoxville, TN.
34c7a079a8SJonathan Haslam  *                              All Rights Reserved.
35c7a079a8SJonathan Haslam  *
36c7a079a8SJonathan Haslam  *
37c7a079a8SJonathan Haslam  * Redistribution and use in source and binary forms, with or without
38c7a079a8SJonathan Haslam  * modification, are permitted provided that the following conditions are met:
39c7a079a8SJonathan Haslam  *
40c7a079a8SJonathan Haslam  *    * Redistributions of source code must retain the above copyright notice,
41c7a079a8SJonathan Haslam  *      this list of conditions and the following disclaimer.
42c7a079a8SJonathan Haslam  *    * Redistributions in binary form must reproduce the above copyright
43c7a079a8SJonathan Haslam  *      notice, this list of conditions and the following disclaimer in the
44c7a079a8SJonathan Haslam  *      documentation and/or other materials provided with the distribution.
45c7a079a8SJonathan Haslam  *    * Neither the name of the University of Tennessee nor the names of its
46c7a079a8SJonathan Haslam  *      contributors may be used to endorse or promote products derived from
47c7a079a8SJonathan Haslam  *      this software without specific prior written permission.
48c7a079a8SJonathan Haslam  *
49c7a079a8SJonathan Haslam  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
50c7a079a8SJonathan Haslam  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51c7a079a8SJonathan Haslam  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52c7a079a8SJonathan Haslam  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
53c7a079a8SJonathan Haslam  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54c7a079a8SJonathan Haslam  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55c7a079a8SJonathan Haslam  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56c7a079a8SJonathan Haslam  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57c7a079a8SJonathan Haslam  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58c7a079a8SJonathan Haslam  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59c7a079a8SJonathan Haslam  * POSSIBILITY OF SUCH DAMAGE.
60c7a079a8SJonathan Haslam  *
61c7a079a8SJonathan Haslam  *
62c7a079a8SJonathan Haslam  * This open source software license conforms to the BSD License template.
63c7a079a8SJonathan Haslam  */
64c7a079a8SJonathan Haslam 
65c7a079a8SJonathan Haslam /*
6644961713Sgirish  * Niagara2 Performance Counter Backend
6744961713Sgirish  */
6844961713Sgirish 
6944961713Sgirish #include <sys/cpuvar.h>
7044961713Sgirish #include <sys/systm.h>
7144961713Sgirish #include <sys/archsystm.h>
7244961713Sgirish #include <sys/cmn_err.h>
7344961713Sgirish #include <sys/cpc_impl.h>
7444961713Sgirish #include <sys/cpc_pcbe.h>
7544961713Sgirish #include <sys/modctl.h>
7644961713Sgirish #include <sys/machsystm.h>
7744961713Sgirish #include <sys/sdt.h>
7844961713Sgirish #include <sys/niagara2regs.h>
7944961713Sgirish #include <sys/hsvc.h>
808d4e547dSae112802 #include <sys/hypervisor_api.h>
818d4e547dSae112802 #include <sys/disp.h>
8244961713Sgirish 
8359ac0c16Sdavemq /*LINTLIBRARY*/
8444961713Sgirish static int ni2_pcbe_init(void);
8544961713Sgirish static uint_t ni2_pcbe_ncounters(void);
8644961713Sgirish static const char *ni2_pcbe_impl_name(void);
8744961713Sgirish static const char *ni2_pcbe_cpuref(void);
8844961713Sgirish static char *ni2_pcbe_list_events(uint_t picnum);
8944961713Sgirish static char *ni2_pcbe_list_attrs(void);
9044961713Sgirish static uint64_t ni2_pcbe_event_coverage(char *event);
9144961713Sgirish static uint64_t ni2_pcbe_overflow_bitmap(void);
9244961713Sgirish static int ni2_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
9344961713Sgirish     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
9444961713Sgirish     void *token);
9544961713Sgirish static void ni2_pcbe_program(void *token);
9644961713Sgirish static void ni2_pcbe_allstop(void);
9744961713Sgirish static void ni2_pcbe_sample(void *token);
9844961713Sgirish static void ni2_pcbe_free(void *config);
9944961713Sgirish 
10044961713Sgirish extern void ultra_setpcr(uint64_t);
10144961713Sgirish extern uint64_t ultra_getpcr(void);
10244961713Sgirish extern void ultra_setpic(uint64_t);
10344961713Sgirish extern uint64_t ultra_getpic(void);
10444961713Sgirish extern uint64_t ultra_gettick(void);
10544961713Sgirish extern char cpu_module_name[];
10644961713Sgirish 
1071e81ac6eSSree Vemuri #define	L2_dmiss_ld_event	0x320
1081e81ac6eSSree Vemuri int pcbe_l2dmiss_ovtrap_enable = 0;
1091e81ac6eSSree Vemuri 
11044961713Sgirish pcbe_ops_t ni2_pcbe_ops = {
11144961713Sgirish 	PCBE_VER_1,
11244961713Sgirish 	CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE,
11344961713Sgirish 	ni2_pcbe_ncounters,
11444961713Sgirish 	ni2_pcbe_impl_name,
11544961713Sgirish 	ni2_pcbe_cpuref,
11644961713Sgirish 	ni2_pcbe_list_events,
11744961713Sgirish 	ni2_pcbe_list_attrs,
11844961713Sgirish 	ni2_pcbe_event_coverage,
11944961713Sgirish 	ni2_pcbe_overflow_bitmap,
12044961713Sgirish 	ni2_pcbe_configure,
12144961713Sgirish 	ni2_pcbe_program,
12244961713Sgirish 	ni2_pcbe_allstop,
12344961713Sgirish 	ni2_pcbe_sample,
12444961713Sgirish 	ni2_pcbe_free
12544961713Sgirish };
12644961713Sgirish 
12744961713Sgirish typedef struct _ni2_pcbe_config {
12844961713Sgirish 	uint_t		pcbe_picno;	/* 0 for pic0 or 1 for pic1 */
12944961713Sgirish 	uint32_t	pcbe_evsel;	/* %pcr event code unshifted */
13044961713Sgirish 	uint32_t	pcbe_flags;	/* hpriv/user/system/priv */
13144961713Sgirish 	uint32_t	pcbe_pic;	/* unshifted raw %pic value */
13244961713Sgirish } ni2_pcbe_config_t;
13344961713Sgirish 
13444961713Sgirish typedef struct _ni2_event {
13544961713Sgirish 	const char	*name;
13644961713Sgirish 	const uint32_t	emask;
13744961713Sgirish 	const uint32_t	emask_valid;	/* Mask of unreserved MASK bits */
13844961713Sgirish } ni2_event_t;
13944961713Sgirish 
140c7a079a8SJonathan Haslam typedef struct _ni2_generic_event {
141c7a079a8SJonathan Haslam 	char *name;
142c7a079a8SJonathan Haslam 	char *event;
143c7a079a8SJonathan Haslam } ni2_generic_event_t;
144c7a079a8SJonathan Haslam 
1454df55fdeSJanie Lu #define	ULTRA_PCR_PRIVPIC	(UINT64_C(1) << CPC_PCR_PRIV_SHIFT)
14644961713Sgirish #define	EV_END {NULL, 0, 0}
147c7a079a8SJonathan Haslam #define	GEN_EV_END {NULL, NULL}
14844961713Sgirish 
1498d4e547dSae112802 static const uint64_t	allstopped = (ULTRA_PCR_PRIVPIC |
1504df55fdeSJanie Lu 	CPC_PCR_HOLDOV0 | CPC_PCR_HOLDOV1);
15144961713Sgirish 
1528d4e547dSae112802 /*
1538d4e547dSae112802  * We update this array in the program and allstop routine. The array
1548d4e547dSae112802  * is checked in the sample routine to allow us to only perform the
1558d4e547dSae112802  * PCR.ht bit check when counting is in progress.
1568d4e547dSae112802  */
1578d4e547dSae112802 static boolean_t ni2_cpc_counting[NCPU];
1588d4e547dSae112802 
159bb4f5042Sha137994 static ni2_event_t ni2_events[] = {
1608d4e547dSae112802 	{ "Idle_strands",			0x000, 0x00 },
1618d4e547dSae112802 	{ "Br_completed",			0x201, 0xff },
1628d4e547dSae112802 	{ "Br_taken",				0x202, 0xff },
1638d4e547dSae112802 	{ "Instr_FGU_arithmetic",		0x204, 0xff },
1648d4e547dSae112802 	{ "Instr_ld",				0x208, 0xff },
1658d4e547dSae112802 	{ "Instr_st",				0x210, 0xff },
1668d4e547dSae112802 	{ "Instr_sw",				0x220, 0xff },
1678d4e547dSae112802 	{ "Instr_other",			0x240, 0xff },
1688d4e547dSae112802 	{ "Atomics",				0x280, 0xff },
1698d4e547dSae112802 	{ "Instr_cnt",				0x2fd, 0xff },
1708d4e547dSae112802 	{ "IC_miss",				0x301, 0x33 },
1718d4e547dSae112802 	{ "DC_miss",				0x302, 0x33 },
1728d4e547dSae112802 	{ "L2_imiss",				0x310, 0x33 },
1738d4e547dSae112802 	{ "L2_dmiss_ld",			0x320, 0x33 },
1748d4e547dSae112802 	{ "ITLB_HWTW_ref_L2",			0x404, 0x3c },
1758d4e547dSae112802 	{ "DTLB_HWTW_ref_L2",			0x408, 0x3c },
1768d4e547dSae112802 	{ "ITLB_HWTW_miss_L2",			0x410, 0x3c },
1778d4e547dSae112802 	{ "DTLB_HWTW_miss_L2",			0x420, 0x3c },
1788d4e547dSae112802 	{ "Stream_ld_to_PCX",			0x501, 0x3f },
1798d4e547dSae112802 	{ "Stream_st_to_PCX",			0x502, 0x3f },
1808d4e547dSae112802 	{ "CPU_ld_to_PCX",			0x504, 0x3f },
1818d4e547dSae112802 	{ "CPU_ifetch_to_PCX",			0x508, 0x3f },
1828d4e547dSae112802 	{ "CPU_st_to_PCX",			0x510, 0x3f },
1838d4e547dSae112802 	{ "MMU_ld_to_PCX",			0x520, 0x3f },
1844df55fdeSJanie Lu #ifdef KT_IMPL
1854df55fdeSJanie Lu 	{ "DES_3DES_op",			0x601, 0xff },
1864df55fdeSJanie Lu 	{ "AES_op",				0x602, 0xff },
1874df55fdeSJanie Lu 	{ "Kasumi_op",				0x604, 0xff },
1884df55fdeSJanie Lu 	{ "MD5_SHA-1_SHA-256_op",		0x608, 0xff },
1894df55fdeSJanie Lu 	{ "MA_op",				0x610, 0xff },
1904df55fdeSJanie Lu 	{ "CRC_TCPIP_cksum",			0x620, 0xff },
1914df55fdeSJanie Lu 	{ "DES_3DES_busy_cycle",		0x701, 0xff },
1924df55fdeSJanie Lu 	{ "AES_busy_cycle",			0x702, 0xff },
1934df55fdeSJanie Lu 	{ "Kasumi_busy_cycle",			0x704, 0xff },
1944df55fdeSJanie Lu 	{ "MD5_SHA-1_SHA-256_busy_cycle",	0x708, 0xff },
1954df55fdeSJanie Lu 	{ "MA_busy_cycle",			0x710, 0xff },
1964df55fdeSJanie Lu 	{ "CRC_MPA_cksum",			0x720, 0xff },
1974df55fdeSJanie Lu #else
1988d4e547dSae112802 	{ "DES_3DES_op",			0x601, 0x3f },
1998d4e547dSae112802 	{ "AES_op",				0x602, 0x3f },
2008d4e547dSae112802 	{ "RC4_op",				0x604, 0x3f },
2018d4e547dSae112802 	{ "MD5_SHA-1_SHA-256_op",		0x608, 0x3f },
2028d4e547dSae112802 	{ "MA_op",				0x610, 0x3f },
2038d4e547dSae112802 	{ "CRC_TCPIP_cksum",			0x620, 0x3f },
2048d4e547dSae112802 	{ "DES_3DES_busy_cycle",		0x701, 0x3f },
2058d4e547dSae112802 	{ "AES_busy_cycle",			0x702, 0x3f },
2068d4e547dSae112802 	{ "RC4_busy_cycle",			0x704, 0x3f },
2078d4e547dSae112802 	{ "MD5_SHA-1_SHA-256_busy_cycle",	0x708, 0x3f },
2088d4e547dSae112802 	{ "MA_busy_cycle",			0x710, 0x3f },
2098d4e547dSae112802 	{ "CRC_MPA_cksum",			0x720, 0x3f },
2104df55fdeSJanie Lu #endif
2118d4e547dSae112802 	{ "ITLB_miss",				0xb04, 0x0c },
2128d4e547dSae112802 	{ "DTLB_miss",				0xb08, 0x0c },
2138d4e547dSae112802 	{ "TLB_miss",				0xb0c, 0x0c },
2148d4e547dSae112802 	EV_END
2158d4e547dSae112802 };
2168d4e547dSae112802 
217c7a079a8SJonathan Haslam static ni2_generic_event_t ni2_generic_events[] = {
218c7a079a8SJonathan Haslam 	{ "PAPI_tot_ins",	"Instr_cnt" },
219c7a079a8SJonathan Haslam 	{ "PAPI_l1_dcm",	"DC_miss" },
220c7a079a8SJonathan Haslam 	{ "PAPI_l1_icm",	"IC_miss" },
221c7a079a8SJonathan Haslam 	{ "PAPI_l2_icm",	"L2_imiss" },
222c7a079a8SJonathan Haslam 	{ "PAPI_l2_ldm",	"L2_dmiss_ld" },
223c7a079a8SJonathan Haslam 	{ "PAPI_tlb_dm",	"DTLB_miss" },
224c7a079a8SJonathan Haslam 	{ "PAPI_tlb_im",	"ITLB_miss" },
225c7a079a8SJonathan Haslam 	{ "PAPI_tlb_tm",	"TLB_miss" },
226c7a079a8SJonathan Haslam 	{ "PAPI_br_tkn",	"Br_taken" },
227c7a079a8SJonathan Haslam 	{ "PAPI_br_ins",	"Br_completed" },
228c7a079a8SJonathan Haslam 	{ "PAPI_ld_ins",	"Instr_ld" },
229c7a079a8SJonathan Haslam 	{ "PAPI_sr_ins",	"Instr_st" },
23067b48423SJonathan Haslam 	{ "PAPI_fp_ops",	"Instr_FGU_arithmetic" },
23167b48423SJonathan Haslam 	{ "PAPI_fp_ins",	"Instr_FGU_arithmetic" },
232c7a079a8SJonathan Haslam 	GEN_EV_END
233c7a079a8SJonathan Haslam };
234c7a079a8SJonathan Haslam 
23544961713Sgirish static char		*evlist;
23644961713Sgirish static size_t		evlist_sz;
23744961713Sgirish static uint16_t 	pcr_pic0_mask;
23844961713Sgirish static uint16_t 	pcr_pic1_mask;
23944961713Sgirish 
24044961713Sgirish #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
24144961713Sgirish 			"http://www.sun.com/processors/manuals"
24244961713Sgirish 
24359ac0c16Sdavemq #if defined(NIAGARA2_IMPL)
24459ac0c16Sdavemq static const char	*cpu_impl_name = "UltraSPARC T2";
24559ac0c16Sdavemq static const char *cpu_pcbe_ref = "See the \"UltraSPARC T2 User's Manual\" "
24644961713Sgirish 			"for descriptions of these events." CPU_REF_URL;
24759ac0c16Sdavemq #elif defined(VFALLS_IMPL)
24859ac0c16Sdavemq static const char	*cpu_impl_name = "UltraSPARC T2+";
24959ac0c16Sdavemq static const char *cpu_pcbe_ref = "See the \"UltraSPARC T2+ User's Manual\" "
25059ac0c16Sdavemq 			"for descriptions of these events." CPU_REF_URL;
2514df55fdeSJanie Lu #elif defined(KT_IMPL)
252*c4c6ba57SVivek Gavaskar static const char	*cpu_impl_name = "SPARC T3";
253*c4c6ba57SVivek Gavaskar static const char *cpu_pcbe_ref = "See the \"SPARC T3 User's Manual\" "
2544df55fdeSJanie Lu 			"for descriptions of these events." CPU_REF_URL;
25559ac0c16Sdavemq #endif
25644961713Sgirish 
25759ac0c16Sdavemq static boolean_t cpu_hsvc_available = B_TRUE;
25844961713Sgirish 
25944961713Sgirish static int
26044961713Sgirish ni2_pcbe_init(void)
26144961713Sgirish {
26244961713Sgirish 	ni2_event_t		*evp;
263c7a079a8SJonathan Haslam 	ni2_generic_event_t	*gevp;
26444961713Sgirish 	int			status;
26559ac0c16Sdavemq 	uint64_t		cpu_hsvc_major;
26659ac0c16Sdavemq 	uint64_t		cpu_hsvc_minor;
26759ac0c16Sdavemq #if defined(NIAGARA2_IMPL)
26859ac0c16Sdavemq 	uint64_t		hsvc_cpu_group = HSVC_GROUP_NIAGARA2_CPU;
26959ac0c16Sdavemq 	uint64_t		hsvc_cpu_major = NIAGARA2_HSVC_MAJOR;
27059ac0c16Sdavemq #elif defined(VFALLS_IMPL)
27159ac0c16Sdavemq 	uint64_t		hsvc_cpu_group = HSVC_GROUP_VFALLS_CPU;
27259ac0c16Sdavemq 	uint64_t		hsvc_cpu_major = VFALLS_HSVC_MAJOR;
2734df55fdeSJanie Lu #elif defined(KT_IMPL)
2744df55fdeSJanie Lu 	uint64_t	hsvc_cpu_group = HSVC_GROUP_KT_CPU;
2754df55fdeSJanie Lu 	uint64_t	hsvc_cpu_major = KT_HSVC_MAJOR;
27659ac0c16Sdavemq #endif
27744961713Sgirish 
2784df55fdeSJanie Lu 	pcr_pic0_mask = CPC_PCR_PIC0_MASK;
2794df55fdeSJanie Lu 	pcr_pic1_mask = CPC_PCR_PIC1_MASK;
28044961713Sgirish 
28144961713Sgirish 	/*
28244961713Sgirish 	 * Validate API version for Niagara2 specific hypervisor services
28344961713Sgirish 	 */
28459ac0c16Sdavemq 	status = hsvc_version(hsvc_cpu_group, &cpu_hsvc_major,
28559ac0c16Sdavemq 	    &cpu_hsvc_minor);
28659ac0c16Sdavemq 	if ((status != 0) || (cpu_hsvc_major != hsvc_cpu_major)) {
28744961713Sgirish 		cmn_err(CE_WARN, "hypervisor services not negotiated "
28859ac0c16Sdavemq 		    "or unsupported major number: group: 0x%lx major: 0x%lx "
28959ac0c16Sdavemq 		    "minor: 0x%lx errno: %d", hsvc_cpu_group,
29059ac0c16Sdavemq 		    cpu_hsvc_major, cpu_hsvc_minor, status);
29159ac0c16Sdavemq 		cpu_hsvc_available = B_FALSE;
29244961713Sgirish 	}
293bb4f5042Sha137994 
29444961713Sgirish 	/*
29544961713Sgirish 	 * Construct event list.
29644961713Sgirish 	 *
29744961713Sgirish 	 * First pass:  Calculate size needed. We'll need an additional byte
29844961713Sgirish 	 *		for the NULL pointer during the last strcat.
29944961713Sgirish 	 *
30044961713Sgirish 	 * Second pass: Copy strings.
30144961713Sgirish 	 */
30244961713Sgirish 	for (evp = ni2_events; evp->name != NULL; evp++)
30344961713Sgirish 		evlist_sz += strlen(evp->name) + 1;
30444961713Sgirish 
305c7a079a8SJonathan Haslam 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++)
306c7a079a8SJonathan Haslam 		evlist_sz += strlen(gevp->name) + 1;
307c7a079a8SJonathan Haslam 
30844961713Sgirish 	evlist = kmem_alloc(evlist_sz + 1, KM_SLEEP);
30944961713Sgirish 	evlist[0] = '\0';
31044961713Sgirish 
31144961713Sgirish 	for (evp = ni2_events; evp->name != NULL; evp++) {
31244961713Sgirish 		(void) strcat(evlist, evp->name);
31344961713Sgirish 		(void) strcat(evlist, ",");
31444961713Sgirish 	}
315c7a079a8SJonathan Haslam 
316c7a079a8SJonathan Haslam 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++) {
317c7a079a8SJonathan Haslam 		(void) strcat(evlist, gevp->name);
318c7a079a8SJonathan Haslam 		(void) strcat(evlist, ",");
319c7a079a8SJonathan Haslam 	}
320c7a079a8SJonathan Haslam 
32144961713Sgirish 	/*
32244961713Sgirish 	 * Remove trailing comma.
32344961713Sgirish 	 */
32444961713Sgirish 	evlist[evlist_sz - 1] = '\0';
32544961713Sgirish 
32644961713Sgirish 	return (0);
32744961713Sgirish }
32844961713Sgirish 
32944961713Sgirish static uint_t
33044961713Sgirish ni2_pcbe_ncounters(void)
33144961713Sgirish {
33244961713Sgirish 	return (2);
33344961713Sgirish }
33444961713Sgirish 
33544961713Sgirish static const char *
33644961713Sgirish ni2_pcbe_impl_name(void)
33744961713Sgirish {
33859ac0c16Sdavemq 	return (cpu_impl_name);
33944961713Sgirish }
34044961713Sgirish 
34144961713Sgirish static const char *
34244961713Sgirish ni2_pcbe_cpuref(void)
34344961713Sgirish {
34459ac0c16Sdavemq 	return (cpu_pcbe_ref);
34544961713Sgirish }
34644961713Sgirish 
34744961713Sgirish static char *
34844961713Sgirish ni2_pcbe_list_events(uint_t picnum)
34944961713Sgirish {
35044961713Sgirish 	ASSERT(picnum < cpc_ncounters);
35144961713Sgirish 
35244961713Sgirish 	return (evlist);
35344961713Sgirish }
35444961713Sgirish 
35544961713Sgirish static char *
35644961713Sgirish ni2_pcbe_list_attrs(void)
35744961713Sgirish {
35859ac0c16Sdavemq 	if (cpu_hsvc_available == B_TRUE)
35959ac0c16Sdavemq #if defined(NIAGARA2_IMPL)
36044961713Sgirish 		return ("hpriv,emask");
36159ac0c16Sdavemq #elif defined(VFALLS_IMPL)
36259ac0c16Sdavemq 		return ("hpriv,l2ctl,emask");
3634df55fdeSJanie Lu #elif defined(KT_IMPL)
3644df55fdeSJanie Lu 		return ("hpriv,l2ctl,emask,sample");
36559ac0c16Sdavemq #endif
36644961713Sgirish 	else
3674df55fdeSJanie Lu #if defined(KT_IMPL)
3684df55fdeSJanie Lu 		return ("emask,sample");
3694df55fdeSJanie Lu #else
37044961713Sgirish 		return ("emask");
3714df55fdeSJanie Lu #endif
37244961713Sgirish }
37344961713Sgirish 
374c7a079a8SJonathan Haslam static ni2_generic_event_t *
375c7a079a8SJonathan Haslam find_generic_event(char *name)
376c7a079a8SJonathan Haslam {
377c7a079a8SJonathan Haslam 	ni2_generic_event_t	*gevp;
378c7a079a8SJonathan Haslam 
379c7a079a8SJonathan Haslam 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++) {
380c7a079a8SJonathan Haslam 		if (strcmp(name, gevp->name) == 0)
381c7a079a8SJonathan Haslam 			return (gevp);
382c7a079a8SJonathan Haslam 	}
383c7a079a8SJonathan Haslam 
384c7a079a8SJonathan Haslam 	return (NULL);
385c7a079a8SJonathan Haslam }
386c7a079a8SJonathan Haslam 
38744961713Sgirish static ni2_event_t *
38844961713Sgirish find_event(char *name)
38944961713Sgirish {
39044961713Sgirish 	ni2_event_t		*evp;
39144961713Sgirish 
39244961713Sgirish 	for (evp = ni2_events; evp->name != NULL; evp++)
39344961713Sgirish 		if (strcmp(name, evp->name) == 0)
39444961713Sgirish 			return (evp);
39544961713Sgirish 
39644961713Sgirish 	return (NULL);
39744961713Sgirish }
39844961713Sgirish 
39944961713Sgirish /*ARGSUSED*/
40044961713Sgirish static uint64_t
40144961713Sgirish ni2_pcbe_event_coverage(char *event)
40244961713Sgirish {
40344961713Sgirish 	/*
404b885580bSAlexander Kolbasov 	 * Check whether counter event is supported
405b885580bSAlexander Kolbasov 	 */
406b885580bSAlexander Kolbasov 	if (find_event(event) == NULL && find_generic_event(event) == NULL)
407b885580bSAlexander Kolbasov 		return (0);
408b885580bSAlexander Kolbasov 
409b885580bSAlexander Kolbasov 	/*
41044961713Sgirish 	 * Fortunately, both pic0 and pic1 can count all events.
41144961713Sgirish 	 */
41244961713Sgirish 	return (0x3);
41344961713Sgirish }
41444961713Sgirish 
41544961713Sgirish static uint64_t
41644961713Sgirish ni2_pcbe_overflow_bitmap(void)
41744961713Sgirish {
41844961713Sgirish 	uint64_t	pcr, overflow;
41944961713Sgirish 	uint64_t	pic;
42044961713Sgirish 	uint32_t	pic0, pic1;
42144961713Sgirish 	boolean_t	update_pic = B_FALSE;
4224df55fdeSJanie Lu 	boolean_t	pic_inrange = B_FALSE;
42344961713Sgirish 
42444961713Sgirish 	ASSERT(getpil() >= DISP_LEVEL);
42544961713Sgirish 	pcr = ultra_getpcr();
42644961713Sgirish 	DTRACE_PROBE1(niagara2__getpcr, uint64_t, pcr);
4274df55fdeSJanie Lu 	overflow =  (pcr & CPC_PCR_OV0_MASK) >>
4284df55fdeSJanie Lu 	    CPC_PCR_OV0_SHIFT;
4294df55fdeSJanie Lu 	overflow |=  (pcr & CPC_PCR_OV1_MASK) >>
4304df55fdeSJanie Lu 	    CPC_PCR_OV1_SHIFT;
4318d4e547dSae112802 
4328d4e547dSae112802 	pic = ultra_getpic();
4338d4e547dSae112802 	pic0 = (uint32_t)(pic & PIC0_MASK);
4348d4e547dSae112802 	pic1 = (uint32_t)((pic >> PIC1_SHIFT) & PIC0_MASK);
4358d4e547dSae112802 
4364df55fdeSJanie Lu 	pcr |= (CPC_PCR_HOLDOV0 | CPC_PCR_HOLDOV1);
4378d4e547dSae112802 
4388d4e547dSae112802 	if (overflow & 0x1) {
4394df55fdeSJanie Lu 		pcr &= ~(CPC_PCR_OV0_MASK |
4404df55fdeSJanie Lu 		    CPC_PCR_HOLDOV0);
4414df55fdeSJanie Lu 		pic_inrange = PIC_IN_OV_RANGE(pic0);
4424df55fdeSJanie Lu #if defined(KT_IMPL)
4434df55fdeSJanie Lu 		if (pcr & CPC_PCR_SAMPLE_MODE_MASK)
4444df55fdeSJanie Lu 			pic_inrange = SAMPLE_PIC_IN_OV_RANGE(pic0);
4454df55fdeSJanie Lu #endif
4464df55fdeSJanie Lu 		if (pic_inrange) {
44744961713Sgirish 			pic0 = 0;
44844961713Sgirish 			update_pic = B_TRUE;
44944961713Sgirish 		}
4508d4e547dSae112802 	}
4518d4e547dSae112802 
4528d4e547dSae112802 	if (overflow & 0x2) {
4534df55fdeSJanie Lu 		pcr &= ~(CPC_PCR_OV1_MASK |
4544df55fdeSJanie Lu 		    CPC_PCR_HOLDOV1);
4554df55fdeSJanie Lu 		pic_inrange = PIC_IN_OV_RANGE(pic1);
4564df55fdeSJanie Lu #if defined(KT_IMPL)
4574df55fdeSJanie Lu 		if (pcr & CPC_PCR_SAMPLE_MODE_MASK)
4584df55fdeSJanie Lu 			pic_inrange = SAMPLE_PIC_IN_OV_RANGE(pic1);
4594df55fdeSJanie Lu #endif
4604df55fdeSJanie Lu 		if (pic_inrange) {
46144961713Sgirish 			pic1 = 0;
46244961713Sgirish 			update_pic = B_TRUE;
46344961713Sgirish 		}
4648d4e547dSae112802 	}
46544961713Sgirish 
46644961713Sgirish 	if (update_pic)
46744961713Sgirish 		ultra_setpic(((uint64_t)pic1 << PIC1_SHIFT) | pic0);
46844961713Sgirish 
4698d4e547dSae112802 	/*
4708d4e547dSae112802 	 * The HV interface does not need to be used here because we are
4718d4e547dSae112802 	 * only resetting the OV bits and do not need to set the HT bit.
4728d4e547dSae112802 	 */
4738d4e547dSae112802 	DTRACE_PROBE1(niagara2__setpcr, uint64_t, pcr);
4748d4e547dSae112802 	ultra_setpcr(pcr);
4758d4e547dSae112802 
47644961713Sgirish 	return (overflow);
47744961713Sgirish }
47844961713Sgirish 
47944961713Sgirish /*ARGSUSED*/
48044961713Sgirish static int
48144961713Sgirish ni2_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
48244961713Sgirish     uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
48344961713Sgirish {
48444961713Sgirish 	ni2_pcbe_config_t	*cfg;
48544961713Sgirish 	ni2_pcbe_config_t	*other_config;
48644961713Sgirish 	ni2_event_t		*evp;
487c7a079a8SJonathan Haslam 	ni2_generic_event_t	*gevp;
48844961713Sgirish 	int			i;
48944961713Sgirish 	uint32_t		evsel;
4904df55fdeSJanie Lu #if defined(VFALLS_IMPL) || defined(KT_IMPL)
491a588362cSSree Vemuri 	uint64_t		l2ctl = 0;
492a588362cSSree Vemuri #endif
49344961713Sgirish 
49444961713Sgirish 	/*
49544961713Sgirish 	 * If we've been handed an existing configuration, we need only preset
49644961713Sgirish 	 * the counter value.
49744961713Sgirish 	 */
49844961713Sgirish 	if (*data != NULL) {
49944961713Sgirish 		cfg = *data;
50044961713Sgirish 		cfg->pcbe_pic = (uint32_t)preset;
50144961713Sgirish 		return (0);
50244961713Sgirish 	}
50344961713Sgirish 
50444961713Sgirish 	if (picnum > 1)
50544961713Sgirish 		return (CPC_INVALID_PICNUM);
50644961713Sgirish 
507c7a079a8SJonathan Haslam 
508c7a079a8SJonathan Haslam 	if ((evp = find_event(event)) == NULL) {
509c7a079a8SJonathan Haslam 		if ((gevp = find_generic_event(event)) != NULL) {
510c7a079a8SJonathan Haslam 			evp = find_event(gevp->event);
511c7a079a8SJonathan Haslam 			ASSERT(evp != NULL);
512c7a079a8SJonathan Haslam 
513c7a079a8SJonathan Haslam 			if (nattrs > 0)
514c7a079a8SJonathan Haslam 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
515c7a079a8SJonathan Haslam 		} else {
51644961713Sgirish 			return (CPC_INVALID_EVENT);
517c7a079a8SJonathan Haslam 		}
518c7a079a8SJonathan Haslam 	}
51944961713Sgirish 
52044961713Sgirish 	evsel = evp->emask;
52144961713Sgirish 
52244961713Sgirish 	for (i = 0; i < nattrs; i++) {
52344961713Sgirish 		if (strcmp(attrs[i].ka_name, "hpriv") == 0) {
52444961713Sgirish 			if (attrs[i].ka_val != 0)
5258d4e547dSae112802 				flags |= CPC_COUNT_HV;
52644961713Sgirish 		} else if (strcmp(attrs[i].ka_name, "emask") == 0) {
52744961713Sgirish 			if ((attrs[i].ka_val | evp->emask_valid) !=
52844961713Sgirish 			    evp->emask_valid)
52944961713Sgirish 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
53044961713Sgirish 			evsel |= attrs[i].ka_val;
5314df55fdeSJanie Lu #if defined(VFALLS_IMPL) || defined(KT_IMPL)
53259ac0c16Sdavemq 		} else if (strcmp(attrs[i].ka_name, "l2ctl") == 0) {
5334df55fdeSJanie Lu 			if ((attrs[i].ka_val | L2_CTL_MASK) !=
5344df55fdeSJanie Lu 			    L2_CTL_MASK)
53559ac0c16Sdavemq 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
536a588362cSSree Vemuri 			else
537a588362cSSree Vemuri 				l2ctl = attrs[i].ka_val;
53859ac0c16Sdavemq #endif
5394df55fdeSJanie Lu #if defined(KT_IMPL)
5404df55fdeSJanie Lu 		} else if (strcmp(attrs[i].ka_name, "sample") == 0) {
5414df55fdeSJanie Lu 			if (attrs[i].ka_val != 0)
5424df55fdeSJanie Lu 				flags |= CPC_COUNT_SAMPLE_MODE;
5434df55fdeSJanie Lu #endif
54444961713Sgirish 		} else
54544961713Sgirish 			return (CPC_INVALID_ATTRIBUTE);
54644961713Sgirish 	}
54744961713Sgirish 
5484df55fdeSJanie Lu #if defined(VFALLS_IMPL) || defined(KT_IMPL)
549a588362cSSree Vemuri 	/*
550a588362cSSree Vemuri 	 * Set PERF_CONTROL bits in L2_CONTROL_REG only when events have
551a588362cSSree Vemuri 	 * SL bits equal to 3.
552a588362cSSree Vemuri 	 */
553b80c1b63SSree Vemuri 	if ((evsel & SL_MASK) == SL3_MASK) {
5544df55fdeSJanie Lu 		if ((hv_niagara_setperf(HV_L2_CTL, l2ctl)) != 0)
555a588362cSSree Vemuri 			return (CPC_HV_NO_ACCESS);
556a588362cSSree Vemuri 	}
557a588362cSSree Vemuri #endif
558a588362cSSree Vemuri 
55944961713Sgirish 	/*
56044961713Sgirish 	 * Find other requests that will be programmed with this one, and ensure
56144961713Sgirish 	 * the flags don't conflict.
56244961713Sgirish 	 */
56344961713Sgirish 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
56444961713Sgirish 	    (other_config->pcbe_flags != flags))
56544961713Sgirish 		return (CPC_CONFLICTING_REQS);
56644961713Sgirish 
5678d4e547dSae112802 	/*
5688d4e547dSae112802 	 * If the hpriv attribute is present, make sure we have
5698d4e547dSae112802 	 * access to hyperprivileged events before continuing with
5708d4e547dSae112802 	 * this configuration. If we can set the ht bit in the PCR
5718d4e547dSae112802 	 * successfully, we must have access to hyperprivileged
5728d4e547dSae112802 	 * events.
5738d4e547dSae112802 	 *
5748d4e547dSae112802 	 * If this is a static per-CPU configuration, the CPC
5758d4e547dSae112802 	 * driver ensures there can not be more than one for this
5768d4e547dSae112802 	 * CPU. If this is a per-LWP configuration, the driver
5778d4e547dSae112802 	 * ensures no static per-CPU counting is ongoing and that
5788d4e547dSae112802 	 * the target LWP is not already being monitored.
5798d4e547dSae112802 	 */
5808d4e547dSae112802 	if (flags & CPC_COUNT_HV) {
5818d4e547dSae112802 		kpreempt_disable();
5828d4e547dSae112802 
5838d4e547dSae112802 		DTRACE_PROBE1(niagara2__setpcr, uint64_t,
5844df55fdeSJanie Lu 		    allstopped | CPC_PCR_HT);
5854df55fdeSJanie Lu 		if (hv_niagara_setperf(HV_SPARC_CTL,
5864df55fdeSJanie Lu 		    allstopped | CPC_PCR_HT) != H_EOK) {
5878d4e547dSae112802 			kpreempt_enable();
5888d4e547dSae112802 			return (CPC_HV_NO_ACCESS);
5898d4e547dSae112802 		}
5908d4e547dSae112802 
5918d4e547dSae112802 		DTRACE_PROBE1(niagara2__setpcr, uint64_t, allstopped);
5924df55fdeSJanie Lu 		(void) hv_niagara_setperf(HV_SPARC_CTL, allstopped);
5938d4e547dSae112802 
5948d4e547dSae112802 		kpreempt_enable();
5958d4e547dSae112802 	}
5968d4e547dSae112802 
59744961713Sgirish 	cfg = kmem_alloc(sizeof (*cfg), KM_SLEEP);
59844961713Sgirish 
59944961713Sgirish 	cfg->pcbe_picno = picnum;
60044961713Sgirish 	cfg->pcbe_evsel = evsel;
60144961713Sgirish 	cfg->pcbe_flags = flags;
60244961713Sgirish 	cfg->pcbe_pic = (uint32_t)preset;
60344961713Sgirish 
60444961713Sgirish 	*data = cfg;
60544961713Sgirish 	return (0);
60644961713Sgirish }
60744961713Sgirish 
60844961713Sgirish static void
60944961713Sgirish ni2_pcbe_program(void *token)
61044961713Sgirish {
61144961713Sgirish 	ni2_pcbe_config_t	*pic0;
61244961713Sgirish 	ni2_pcbe_config_t	*pic1;
61344961713Sgirish 	ni2_pcbe_config_t	*tmp;
61444961713Sgirish 	ni2_pcbe_config_t	nullcfg = { 1, 0, 0, 0 };
61544961713Sgirish 	uint64_t		pcr;
61644961713Sgirish 	uint64_t		curpic;
61744961713Sgirish 	uint64_t		toe;
61844961713Sgirish 
61944961713Sgirish 	/* enable trap-on-event for pic0 and pic1 */
6204df55fdeSJanie Lu 	toe = (CPC_PCR_TOE0 | CPC_PCR_TOE1);
62144961713Sgirish 
62244961713Sgirish 	if ((pic0 = (ni2_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
62344961713Sgirish 	    NULL)
62444961713Sgirish 		panic("ni2_pcbe: token %p has no configs", token);
62544961713Sgirish 
62644961713Sgirish 	if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
62744961713Sgirish 		pic1 = &nullcfg;
62844961713Sgirish 		nullcfg.pcbe_flags = pic0->pcbe_flags;
6294df55fdeSJanie Lu 		toe = CPC_PCR_TOE0; /* enable trap-on-event for pic0 */
63044961713Sgirish 	}
63144961713Sgirish 
63244961713Sgirish 	if (pic0->pcbe_picno != 0) {
63344961713Sgirish 		/*
63444961713Sgirish 		 * pic0 is counter 1, so if we need the null config it should
63544961713Sgirish 		 * be counter 0.
63644961713Sgirish 		 */
63744961713Sgirish 		nullcfg.pcbe_picno = 0;
63844961713Sgirish 		tmp = pic0;
63944961713Sgirish 		pic0 = pic1;
64044961713Sgirish 		pic1 = tmp;
6414df55fdeSJanie Lu 		toe = CPC_PCR_TOE1; /* enable trap-on-event for pic1 */
64244961713Sgirish 	}
64344961713Sgirish 
64444961713Sgirish 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
64559ac0c16Sdavemq 		panic("%s: bad config on token %p\n", cpu_impl_name, token);
64644961713Sgirish 
64744961713Sgirish 	/*
64844961713Sgirish 	 * UltraSPARC does not allow pic0 to be configured differently
64944961713Sgirish 	 * from pic1. If the flags on these two configurations are
65044961713Sgirish 	 * different, they are incompatible. This condition should be
65144961713Sgirish 	 * caught at configure time.
65244961713Sgirish 	 */
65344961713Sgirish 	ASSERT(pic0->pcbe_flags == pic1->pcbe_flags);
65444961713Sgirish 
6558d4e547dSae112802 	ni2_pcbe_allstop();
6568d4e547dSae112802 
65744961713Sgirish 	ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) |
65844961713Sgirish 	    (uint64_t)pic0->pcbe_pic);
65944961713Sgirish 
6604df55fdeSJanie Lu 	pcr = (pic0->pcbe_evsel & pcr_pic0_mask) << CPC_PCR_PIC0_SHIFT;
66144961713Sgirish 	pcr |= (pic1->pcbe_evsel & pcr_pic1_mask) <<
6624df55fdeSJanie Lu 	    CPC_PCR_PIC1_SHIFT;
66344961713Sgirish 
66444961713Sgirish 	if (pic0->pcbe_flags & CPC_COUNT_USER)
6654df55fdeSJanie Lu 		pcr |= (1ull << CPC_PCR_UT_SHIFT);
66644961713Sgirish 	if (pic0->pcbe_flags & CPC_COUNT_SYSTEM)
6674df55fdeSJanie Lu 		pcr |= (1ull << CPC_PCR_ST_SHIFT);
6688d4e547dSae112802 	if (pic0->pcbe_flags & CPC_COUNT_HV)
6694df55fdeSJanie Lu 		pcr |= (1ull << CPC_PCR_HT_SHIFT);
6704df55fdeSJanie Lu #if defined(KT_IMPL)
6714df55fdeSJanie Lu 	if (pic0->pcbe_flags & CPC_COUNT_SAMPLE_MODE)
6724df55fdeSJanie Lu 		pcr |= (1ull << CPC_PCR_SAMPLE_MODE_SHIFT);
6734df55fdeSJanie Lu #endif
6741e81ac6eSSree Vemuri 
6751e81ac6eSSree Vemuri 	if (pcbe_l2dmiss_ovtrap_enable == 0) {
6761e81ac6eSSree Vemuri 		/*
6771e81ac6eSSree Vemuri 		 * SW workaround for HW Erratum 108.
6781e81ac6eSSree Vemuri 		 * Disable overflow interrupts when L2_dmiss_ld event is
6791e81ac6eSSree Vemuri 		 * selected.
6801e81ac6eSSree Vemuri 		 */
6811e81ac6eSSree Vemuri 		if ((pic0->pcbe_evsel & L2_dmiss_ld_event) == L2_dmiss_ld_event)
6821e81ac6eSSree Vemuri 			toe &= ~CPC_PCR_TOE0;
6831e81ac6eSSree Vemuri 
6841e81ac6eSSree Vemuri 		if ((pic1->pcbe_evsel & L2_dmiss_ld_event) == L2_dmiss_ld_event)
6851e81ac6eSSree Vemuri 			toe &= ~CPC_PCR_TOE1;
6861e81ac6eSSree Vemuri 	}
68744961713Sgirish 	pcr |= toe;
68844961713Sgirish 
68944961713Sgirish 	DTRACE_PROBE1(niagara2__setpcr, uint64_t, pcr);
69044961713Sgirish 
6918d4e547dSae112802 	if (pic0->pcbe_flags & CPC_COUNT_HV) {
69244961713Sgirish 		/*
6938d4e547dSae112802 		 * The ht bit in the PCR is only writable in
6948d4e547dSae112802 		 * hyperprivileged mode. So if we are counting
6958d4e547dSae112802 		 * hpriv events, we must use the HV interface
6968d4e547dSae112802 		 * hv_niagara_setperf to set the PCR. If this
6978d4e547dSae112802 		 * fails, assume we no longer have access to
6988d4e547dSae112802 		 * hpriv events.
69944961713Sgirish 		 */
7004df55fdeSJanie Lu 		if (hv_niagara_setperf(HV_SPARC_CTL, pcr) != H_EOK) {
7018d4e547dSae112802 			kcpc_invalidate_config(token);
7028d4e547dSae112802 			return;
7038d4e547dSae112802 		}
70444961713Sgirish 	} else
7058d4e547dSae112802 		/* Set the PCR with no hpriv event counting enabled. */
70644961713Sgirish 		ultra_setpcr(pcr);
70744961713Sgirish 
7088d4e547dSae112802 	ni2_cpc_counting[CPU->cpu_id] = B_TRUE;
7098d4e547dSae112802 
71044961713Sgirish 	/*
71144961713Sgirish 	 * On UltraSPARC, only read-to-read counts are accurate. We cannot
71244961713Sgirish 	 * expect the value we wrote into the PIC, above, to be there after
71344961713Sgirish 	 * starting the counter. We must sample the counter value now and use
71444961713Sgirish 	 * that as the baseline for future samples.
71544961713Sgirish 	 */
71644961713Sgirish 	curpic = ultra_getpic();
71744961713Sgirish 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
71844961713Sgirish 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
71944961713Sgirish 
72044961713Sgirish 	DTRACE_PROBE1(niagara2__newpic, uint64_t, curpic);
72144961713Sgirish }
72244961713Sgirish 
72344961713Sgirish static void
72444961713Sgirish ni2_pcbe_allstop(void)
72544961713Sgirish {
7268d4e547dSae112802 	/*
7278d4e547dSae112802 	 * We use the HV interface here because if we were counting
7288d4e547dSae112802 	 * hyperprivileged events, we must reset the PCR.ht bit to stop
7298d4e547dSae112802 	 * the counting. In the event that this HV call fails, we fall
7308d4e547dSae112802 	 * back on ultra_setpcr which does not have write access to the
7318d4e547dSae112802 	 * ht bit.
7328d4e547dSae112802 	 */
7334df55fdeSJanie Lu 	if (hv_niagara_setperf(HV_SPARC_CTL, allstopped) != H_EOK)
73444961713Sgirish 		ultra_setpcr(allstopped);
7358d4e547dSae112802 
7368d4e547dSae112802 	ni2_cpc_counting[CPU->cpu_id] = B_FALSE;
73744961713Sgirish }
73844961713Sgirish 
73944961713Sgirish static void
74044961713Sgirish ni2_pcbe_sample(void *token)
74144961713Sgirish {
74244961713Sgirish 	uint64_t		curpic;
74344961713Sgirish 	int64_t			diff;
74444961713Sgirish 	uint64_t		*pic0_data;
74544961713Sgirish 	uint64_t		*pic1_data;
74644961713Sgirish 	uint64_t		*dtmp;
74744961713Sgirish 	uint64_t		tmp;
7488d4e547dSae112802 	uint64_t		pcr;
74944961713Sgirish 	ni2_pcbe_config_t	*pic0;
75044961713Sgirish 	ni2_pcbe_config_t	*pic1;
75144961713Sgirish 	ni2_pcbe_config_t	nullcfg = { 1, 0, 0, 0 };
75244961713Sgirish 	ni2_pcbe_config_t	*ctmp;
75344961713Sgirish 
75444961713Sgirish 	curpic = ultra_getpic();
75544961713Sgirish 	DTRACE_PROBE1(niagara2__getpic, uint64_t, curpic);
75644961713Sgirish 
75744961713Sgirish 	if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
75859ac0c16Sdavemq 		panic("%s: token %p has no configs", cpu_impl_name, token);
75944961713Sgirish 
76044961713Sgirish 	if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
76144961713Sgirish 		pic1 = &nullcfg;
76244961713Sgirish 		pic1_data = &tmp;
76344961713Sgirish 	}
76444961713Sgirish 
76544961713Sgirish 	if (pic0->pcbe_picno != 0) {
76644961713Sgirish 		nullcfg.pcbe_picno = 0;
76744961713Sgirish 		ctmp = pic0;
76844961713Sgirish 		pic0 = pic1;
76944961713Sgirish 		pic1 = ctmp;
77044961713Sgirish 		dtmp = pic0_data;
77144961713Sgirish 		pic0_data = pic1_data;
77244961713Sgirish 		pic1_data = dtmp;
77344961713Sgirish 	}
77444961713Sgirish 
77544961713Sgirish 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
77659ac0c16Sdavemq 		panic("%s: bad config on token %p\n", cpu_impl_name, token);
77744961713Sgirish 
7788d4e547dSae112802 
7798d4e547dSae112802 	if (pic0->pcbe_flags & CPC_COUNT_HV) {
7808d4e547dSae112802 		/*
7818d4e547dSae112802 		 * If the hpriv attribute is present, but the HT bit
7828d4e547dSae112802 		 * is not set in the PCR, access to hyperprivileged
7838d4e547dSae112802 		 * events must have been revoked. Only perform this
7848d4e547dSae112802 		 * check if counting is not stopped.
7858d4e547dSae112802 		 */
7868d4e547dSae112802 		pcr = ultra_getpcr();
7878d4e547dSae112802 		DTRACE_PROBE1(niagara2__getpcr, uint64_t, pcr);
7888d4e547dSae112802 		if (ni2_cpc_counting[CPU->cpu_id] &&
7894df55fdeSJanie Lu 		    !(pcr & CPC_PCR_HT)) {
7908d4e547dSae112802 			kcpc_invalidate_config(token);
7918d4e547dSae112802 			return;
7928d4e547dSae112802 		}
7938d4e547dSae112802 	}
7948d4e547dSae112802 
79544961713Sgirish 	diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic;
79644961713Sgirish 	if (diff < 0)
79744961713Sgirish 		diff += (1ll << 32);
79844961713Sgirish 	*pic0_data += diff;
79944961713Sgirish 
80044961713Sgirish 	diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic;
80144961713Sgirish 	if (diff < 0)
80244961713Sgirish 		diff += (1ll << 32);
80344961713Sgirish 	*pic1_data += diff;
80444961713Sgirish 
80544961713Sgirish 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
80644961713Sgirish 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
80744961713Sgirish }
80844961713Sgirish 
80944961713Sgirish static void
81044961713Sgirish ni2_pcbe_free(void *config)
81144961713Sgirish {
81244961713Sgirish 	kmem_free(config, sizeof (ni2_pcbe_config_t));
81344961713Sgirish }
81444961713Sgirish 
81544961713Sgirish 
81644961713Sgirish static struct modlpcbe modlpcbe = {
81744961713Sgirish 	&mod_pcbeops,
81859ac0c16Sdavemq #if defined(NIAGARA2_IMPL)
819820c9f58Skk208521 	"UltraSPARC T2 Performance Counters",
82059ac0c16Sdavemq #elif defined(VFALLS_IMPL)
821820c9f58Skk208521 	"UltraSPARC T2+ Performance Counters",
8224df55fdeSJanie Lu #elif defined(KT_IMPL)
823*c4c6ba57SVivek Gavaskar 	"SPARC T3 Performance Counters",
82459ac0c16Sdavemq #endif
82544961713Sgirish 	&ni2_pcbe_ops
82644961713Sgirish };
82744961713Sgirish 
82844961713Sgirish static struct modlinkage modl = {
82944961713Sgirish 	MODREV_1,
83044961713Sgirish 	&modlpcbe,
83144961713Sgirish };
83244961713Sgirish 
83344961713Sgirish int
83444961713Sgirish _init(void)
83544961713Sgirish {
83644961713Sgirish 	if (ni2_pcbe_init() != 0)
83744961713Sgirish 		return (ENOTSUP);
83844961713Sgirish 	return (mod_install(&modl));
83944961713Sgirish }
84044961713Sgirish 
84144961713Sgirish int
84244961713Sgirish _fini(void)
84344961713Sgirish {
84444961713Sgirish 	return (mod_remove(&modl));
84544961713Sgirish }
84644961713Sgirish 
84744961713Sgirish int
84844961713Sgirish _info(struct modinfo *mi)
84944961713Sgirish {
85044961713Sgirish 	return (mod_info(&modl, mi));
85144961713Sgirish }
852