xref: /onnv-gate/usr/src/uts/sun4/io/fpc/fpc-kstats.c (revision 3800:3d29f956e474)
11370Sschwartz /*
21370Sschwartz  * CDDL HEADER START
31370Sschwartz  *
41370Sschwartz  * The contents of this file are subject to the terms of the
51370Sschwartz  * Common Development and Distribution License (the "License").
61370Sschwartz  * You may not use this file except in compliance with the License.
71370Sschwartz  *
81370Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91370Sschwartz  * or http://www.opensolaris.org/os/licensing.
101370Sschwartz  * See the License for the specific language governing permissions
111370Sschwartz  * and limitations under the License.
121370Sschwartz  *
131370Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
141370Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151370Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
161370Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
171370Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
181370Sschwartz  *
191370Sschwartz  * CDDL HEADER END
201370Sschwartz  */
211370Sschwartz 
221370Sschwartz /*
23*3800Skd93003  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
241370Sschwartz  * Use is subject to license terms.
251370Sschwartz  */
261370Sschwartz 
271370Sschwartz #pragma ident	"%Z%%M%	%I%	%E% SMI"
281370Sschwartz 
291370Sschwartz #include <sys/types.h>
301370Sschwartz #include <sys/sunddi.h>
311370Sschwartz #include <sys/sunndi.h>
321370Sschwartz #include <sys/kstat.h>
331370Sschwartz #include <fpc.h>
341370Sschwartz 
351370Sschwartz /*
361370Sschwartz  * CLEAR_PIC is needed by busstat to extract the current event type of a PIC.
371370Sschwartz  * There will be an entry for CLEAR_PIC in each fi_kev_mask_t table below, but
381370Sschwartz  * they are different from the other entries in that busstat won't show them to
391370Sschwartz  * the user.
401370Sschwartz  */
41*3800Skd93003 #define	DEVICE_NAME_LEN		4
421370Sschwartz #define	PIC_STR_LEN		12
431370Sschwartz 
441370Sschwartz /*
451370Sschwartz  * Data structure used to build array of event-names and pcr-mask values
461370Sschwartz  */
471370Sschwartz typedef struct fi_kev_mask {
481370Sschwartz 	char		*event_name;
491370Sschwartz 	uint64_t	pcr_mask;
501370Sschwartz } fi_kev_mask_t;
511370Sschwartz 
521370Sschwartz typedef struct fi_ksinfo {
531370Sschwartz 	uint32_t	pic_num_events;
541370Sschwartz 	uint32_t	pic_leaf_id;
551370Sschwartz 	uint8_t		pic_sel_shift[NUM_MAX_COUNTERS];
561370Sschwartz 	kstat_t		*pic_name_ksp[NUM_MAX_COUNTERS];
571370Sschwartz 	kstat_t		*cntr_ksp;
581370Sschwartz 	fire_perfcnt_t	pic_reg_group;
591370Sschwartz } fi_ksinfo_t;
601370Sschwartz 
611370Sschwartz static fi_ksinfo_t *fi_imu_kstats[NUM_LEAVES];
621370Sschwartz static fi_ksinfo_t *fi_mmu_kstats[NUM_LEAVES];
631370Sschwartz static fi_ksinfo_t *fi_tlu_kstats[NUM_LEAVES];
641370Sschwartz static fi_ksinfo_t *fi_lpu_kstats[NUM_LEAVES];
651370Sschwartz static fi_ksinfo_t *fi_jbc_kstat;
661370Sschwartz 
671370Sschwartz static int fpc_create_name_kstat(char *name, fi_ksinfo_t *pp, fi_kev_mask_t *ev,
681370Sschwartz     int base, int num_cntrs);
691370Sschwartz static void fpc_delete_name_kstat(fi_ksinfo_t *pp);
701370Sschwartz static kstat_t *fpc_create_cntr_kstat(char *name, int instance,
711370Sschwartz     int (*update)(kstat_t *, int), void *ksinfop, int num_pics);
721370Sschwartz static int fpc_cntr_kstat_update(kstat_t *ksp, int rw);
731370Sschwartz static int fpc_dev_kstat(fire_perfcnt_t reg_group, uint8_t num_inst);
741370Sschwartz static kstat_t *fpc_create_picN_kstat(char *mod_name, int pic,
751370Sschwartz     int pic_sel_shift, int num_ev, fi_kev_mask_t *ev_array);
761370Sschwartz /*
771370Sschwartz  * Below are event lists, which map an event name specified on the commandline
781370Sschwartz  * with a value to program the event register with.
791370Sschwartz  *
801370Sschwartz  * The last entry will be the mask of the entire event field for the PIC and
811370Sschwartz  * counter type.
821370Sschwartz  */
831370Sschwartz 
841370Sschwartz /*
851370Sschwartz  * JBC performance events.
861370Sschwartz  */
871370Sschwartz static fi_kev_mask_t
881370Sschwartz fire_jbc_events[] = {
891370Sschwartz 	{JBC01_S_EVT_NONE,		JBC01_EVT_NONE},
901370Sschwartz 	{JBC01_S_EVT_CLK,		JBC01_EVT_CLK},
911370Sschwartz 	{JBC01_S_EVT_IDLE,		JBC01_EVT_IDLE},
921370Sschwartz 	{JBC01_S_EVT_FIRE,		JBC01_EVT_FIRE},
931370Sschwartz 	{JBC01_S_EVT_READ_LATENCY,	JBC01_EVT_READ_LATENCY},
941370Sschwartz 	{JBC01_S_EVT_READ_SAMPLE,	JBC01_EVT_READ_SAMPLE},
951370Sschwartz 	{JBC01_S_EVT_I2C_PIO,		JBC01_EVT_I2C_PIO},
961370Sschwartz 	{JBC01_S_EVT_EBUS_PIO,		JBC01_EVT_EBUS_PIO},
971370Sschwartz 	{JBC01_S_EVT_RINGA_PIO,		JBC01_EVT_RINGA_PIO},
981370Sschwartz 	{JBC01_S_EVT_RINGB_PIO,		JBC01_EVT_RINGB_PIO},
991370Sschwartz 	{JBC01_S_EVT_PARTIAL_WR,	JBC01_EVT_PARTIAL_WR},
1001370Sschwartz 	{JBC01_S_EVT_TOTAL_WR,		JBC01_EVT_TOTAL_WR},
1011370Sschwartz 	{JBC01_S_EVT_TOTAL_RD,		JBC01_EVT_TOTAL_RD},
1021370Sschwartz 	{JBC01_S_EVT_AOKOFF,		JBC01_EVT_AOKOFF},
1031370Sschwartz 	{JBC01_S_EVT_DOKOFF,		JBC01_EVT_DOKOFF},
1041370Sschwartz 	{JBC01_S_EVT_DAOKOFF,		JBC01_EVT_DAOKOFF},
1051370Sschwartz 	{JBC01_S_EVT_JBUS_COH_XACT,	JBC01_EVT_JBUS_COH_XACT},
1061370Sschwartz 	{JBC01_S_EVT_FIRE_COH_XACT,	JBC01_EVT_FIRE_COH_XACT},
1071370Sschwartz 	{JBC01_S_EVT_JBUS_NCOH_XACT,	JBC01_EVT_JBUS_NCOH_XACT},
1081370Sschwartz 	{JBC01_S_EVT_FGN_IO_HIT,	JBC01_EVT_FGN_IO_HIT},
1091370Sschwartz 	{JBC01_S_EVT_FIRE_WBS,		JBC01_EVT_FIRE_WBS},
1101370Sschwartz 	{JBC01_S_EVT_PCIEA_PIO_WR,	JBC01_EVT_PCIEA_PIO_WR},
1111370Sschwartz 	{JBC01_S_EVT_PCIEA_PIO_RD,	JBC01_EVT_PCIEA_PIO_RD},
1121370Sschwartz 	{JBC01_S_EVT_PCIEB_PIO_WR,	JBC01_EVT_PCIEB_PIO_WR},
1131370Sschwartz 	{JBC01_S_EVT_PCIEB_PIO_RD,	JBC01_EVT_PCIEB_PIO_RD},
1141370Sschwartz 	{COMMON_S_CLEAR_PIC,		JBC01_EVT_MASK}
1151370Sschwartz };
1161370Sschwartz 
1171370Sschwartz /*
1181370Sschwartz  * IMU performance events
1191370Sschwartz  */
1201370Sschwartz static fi_kev_mask_t
1211370Sschwartz fire_imu_events[] = {
1221370Sschwartz 	{IMU01_S_EVT_NONE,		IMU01_EVT_NONE},
1231370Sschwartz 	{IMU01_S_EVT_CLK,		IMU01_EVT_CLK},
1241370Sschwartz 	{IMU01_S_EVT_MONDO,		IMU01_EVT_MONDO},
1251370Sschwartz 	{IMU01_S_EVT_MSI,		IMU01_EVT_MSI},
1261370Sschwartz 	{IMU01_S_EVT_MONDO_NAKS,	IMU01_EVT_MONDO_NAKS},
1271370Sschwartz 	{IMU01_S_EVT_EQ_WR,		IMU01_EVT_EQ_WR},
1281370Sschwartz 	{IMU01_S_EVT_EQ_MONDO,		IMU01_EVT_EQ_MONDO},
1291370Sschwartz 	{COMMON_S_CLEAR_PIC,		IMU01_EVT_MASK}
1301370Sschwartz };
1311370Sschwartz 
1321370Sschwartz /*
1331370Sschwartz  * MMU performance events
1341370Sschwartz  */
1351370Sschwartz static fi_kev_mask_t
1361370Sschwartz fire_mmu_events[] = {
1371370Sschwartz 	{MMU01_S_EVT_NONE,		MMU01_EVT_NONE},
1381370Sschwartz 	{MMU01_S_EVT_CLK,		MMU01_EVT_CLK},
1391370Sschwartz 	{MMU01_S_EVT_TRANS,		MMU01_EVT_TRANSL},
1401370Sschwartz 	{MMU01_S_EVT_STALL,		MMU01_EVT_STALL},
1411370Sschwartz 	{MMU01_S_EVT_TRANSL_MISS,	MMU01_EVT_TRANSL_MISS},
1421370Sschwartz 	{MMU01_S_EVT_TBLWLK_STALL,	MMU01_EVT_TBLWLK_STALL},
1431370Sschwartz 	{MMU01_S_EVT_BYPASS_TRANSL,	MMU01_EVT_BYPASS_TRANSL},
1441370Sschwartz 	{MMU01_S_EVT_TRANSL_TRANSL,	MMU01_EVT_TRANSL_TRANSL},
1451370Sschwartz 	{MMU01_S_EVT_FLOW_CNTL_STALL,	MMU01_EVT_FLOW_CNTL_STALL},
1461370Sschwartz 	{MMU01_S_EVT_FLUSH_CACHE_ENT,	MMU01_EVT_FLUSH_CACHE_ENT},
1471370Sschwartz 	{COMMON_S_CLEAR_PIC,		MMU01_EVT_MASK}
1481370Sschwartz };
1491370Sschwartz 
1501370Sschwartz /*
1511370Sschwartz  * TLU performance events for counters 0 and 1
1521370Sschwartz  */
1531370Sschwartz static fi_kev_mask_t
1541370Sschwartz fire_tlu_events[] = {
1551370Sschwartz 	{TLU01_S_EVT_NONE,			TLU01_EVT_NONE},
1561370Sschwartz 	{TLU01_S_EVT_CLK,			TLU01_EVT_CLK},
1571370Sschwartz 	{TLU01_S_EVT_COMPL,			TLU01_EVT_COMPL},
1581370Sschwartz 	{TLU01_S_EVT_XMT_POST_CR_UNAV,		TLU01_EVT_XMT_POST_CR_UNAV},
1591370Sschwartz 	{TLU01_S_EVT_XMT_NPOST_CR_UNAV,		TLU01_EVT_XMT_NPOST_CR_UNAV},
1601370Sschwartz 	{TLU01_S_EVT_XMT_CMPL_CR_UNAV,		TLU01_EVT_XMT_CMPL_CR_UNAV},
1611370Sschwartz 	{TLU01_S_EVT_XMT_ANY_CR_UNAV,		TLU01_EVT_XMT_ANY_CR_UNAV},
1621370Sschwartz 	{TLU01_S_EVT_RETRY_CR_UNAV,		TLU01_EVT_RETRY_CR_UNAV},
1631370Sschwartz 	{TLU01_S_EVT_MEMRD_PKT_RCVD,		TLU01_EVT_MEMRD_PKT_RCVD},
1641370Sschwartz 	{TLU01_S_EVT_MEMWR_PKT_RCVD,		TLU01_EVT_MEMWR_PKT_RCVD},
1651370Sschwartz 	{TLU01_S_EVT_RCV_CR_THRESH,		TLU01_EVT_RCV_CR_THRESH},
1661370Sschwartz 	{TLU01_S_EVT_RCV_PST_HDR_CR_EXH,	TLU01_EVT_RCV_PST_HDR_CR_EXH},
1671370Sschwartz 	{TLU01_S_EVT_RCV_PST_DA_CR_MPS,		TLU01_EVT_RCV_PST_DA_CR_MPS},
1681370Sschwartz 	{TLU01_S_EVT_RCV_NPST_HDR_CR_EXH,	TLU01_EVT_RCV_NPST_HDR_CR_EXH},
1691370Sschwartz 	{TLU01_S_EVT_RCVR_L0S,			TLU01_EVT_RCVR_L0S},
1701370Sschwartz 	{TLU01_S_EVT_RCVR_L0S_TRANS,		TLU01_EVT_RCVR_L0S_TRANS},
1711370Sschwartz 	{TLU01_S_EVT_XMTR_L0S,			TLU01_EVT_XMTR_L0S},
1721370Sschwartz 	{TLU01_S_EVT_XMTR_L0S_TRANS,		TLU01_EVT_XMTR_L0S_TRANS},
1731370Sschwartz 	{TLU01_S_EVT_RCVR_ERR,			TLU01_EVT_RCVR_ERR},
1741370Sschwartz 	{TLU01_S_EVT_BAD_TLP,			TLU01_EVT_BAD_TLP},
1751370Sschwartz 	{TLU01_S_EVT_BAD_DLLP,			TLU01_EVT_BAD_DLLP},
1761370Sschwartz 	{TLU01_S_EVT_REPLAY_ROLLOVER,		TLU01_EVT_REPLAY_ROLLOVER},
1771370Sschwartz 	{TLU01_S_EVT_REPLAY_TMO,		TLU01_EVT_REPLAY_TMO},
1781370Sschwartz 	{COMMON_S_CLEAR_PIC,			TLU01_EVT_MASK}
1791370Sschwartz };
1801370Sschwartz 
1811370Sschwartz /*
1821370Sschwartz  * TLU performance events for counter 2
1831370Sschwartz  */
1841370Sschwartz static fi_kev_mask_t
1851370Sschwartz fire_tlu2_events[] = {
1861370Sschwartz 	{TLU2_S_EVT_NONE,			TLU2_EVT_NONE},
1871370Sschwartz 	{TLU2_S_EVT_NON_POST_COMPL_TIME,	TLU2_EVT_NON_POST_COMPL_TIME},
1881370Sschwartz 	{TLU2_S_EVT_XMT_DATA_WORD,		TLU2_EVT_XMT_DATA_WORD},
1891370Sschwartz 	{TLU2_S_EVT_RCVD_DATA_WORD,		TLU2_EVT_RCVD_DATA_WORD},
1901370Sschwartz 	{COMMON_S_CLEAR_PIC,			TLU2_EVT_MASK}
1911370Sschwartz };
1921370Sschwartz 
1931370Sschwartz /*
1941370Sschwartz  * LPU performance events
1951370Sschwartz  */
1961370Sschwartz static fi_kev_mask_t
1971370Sschwartz fire_lpu_events[] = {
1981370Sschwartz 	{LPU12_S_EVT_RESET,		LPU12_EVT_RESET},
1991370Sschwartz 	{LPU12_S_EVT_TLP_RCVD,		LPU12_EVT_TLP_RCVD},
2001370Sschwartz 	{LPU12_S_EVT_DLLP_RCVD,		LPU12_EVT_DLLP_RCVD},
2011370Sschwartz 	{LPU12_S_EVT_ACK_DLLP_RCVD,	LPU12_EVT_ACK_DLLP_RCVD},
2021370Sschwartz 	{LPU12_S_EVT_NAK_DLLP_RCVD,	LPU12_EVT_NAK_DLLP_RCVD},
2031370Sschwartz 	{LPU12_S_EVT_RETRY_START,	LPU12_EVT_RETRY_START},
2041370Sschwartz 	{LPU12_S_EVT_REPLAY_TMO,	LPU12_EVT_REPLAY_TMO},
2051370Sschwartz 	{LPU12_S_EVT_ACK_NAK_LAT_TMO,	LPU12_EVT_ACK_NAK_LAT_TMO},
2061370Sschwartz 	{LPU12_S_EVT_BAD_DLLP,		LPU12_EVT_BAD_DLLP},
2071370Sschwartz 	{LPU12_S_EVT_BAD_TLP,		LPU12_EVT_BAD_TLP},
2081370Sschwartz 	{LPU12_S_EVT_NAK_DLLP_SENT,	LPU12_EVT_NAK_DLLP_SENT},
2091370Sschwartz 	{LPU12_S_EVT_ACK_DLLP_SENT,	LPU12_EVT_ACK_DLLP_SENT},
2101370Sschwartz 	{LPU12_S_EVT_RCVR_ERROR,	LPU12_EVT_RCVR_ERROR},
2111370Sschwartz 	{LPU12_S_EVT_LTSSM_RECOV_ENTRY,	LPU12_EVT_LTSSM_RECOV_ENTRY},
2121370Sschwartz 	{LPU12_S_EVT_REPLAY_IN_PROG,	LPU12_EVT_REPLAY_IN_PROG},
2131370Sschwartz 	{LPU12_S_EVT_TLP_XMT_IN_PROG,	LPU12_EVT_TLP_XMT_IN_PROG},
2141370Sschwartz 	{LPU12_S_EVT_CLK_CYC,		LPU12_EVT_CLK_CYC},
2151370Sschwartz 	{LPU12_S_EVT_TLP_DLLP_XMT_PROG,	LPU12_EVT_TLP_DLLP_XMT_PROG},
2161370Sschwartz 	{LPU12_S_EVT_TLP_DLLP_RCV_PROG,	LPU12_EVT_TLP_DLLP_RCV_PROG},
2171370Sschwartz 	{COMMON_S_CLEAR_PIC,		LPU12_EVT_MASK}
2181370Sschwartz };
2191370Sschwartz 
2201370Sschwartz int
fpc_kstat_init(dev_info_t * dip)2211370Sschwartz fpc_kstat_init(dev_info_t *dip)
2221370Sschwartz {
2231370Sschwartz 	fire_perfcnt_t i;
2241370Sschwartz 	int avail;
2251370Sschwartz 	uint8_t num_inst = 0;
2261370Sschwartz 
2271370Sschwartz 	if (fpc_perfcnt_module_init(dip, &avail) != DDI_SUCCESS)
2281370Sschwartz 		return (DDI_FAILURE);
2291370Sschwartz 
2301370Sschwartz 	if (avail & PCIE_A_REGS_AVAIL)
2311370Sschwartz 		num_inst++;
2321370Sschwartz 	if (avail & PCIE_B_REGS_AVAIL)
2331370Sschwartz 		num_inst++;
2341370Sschwartz 
2351370Sschwartz 	for (i = jbc; i < MAX_REG_TYPES; i++) {
2361370Sschwartz 		if (i == jbc) {
2371370Sschwartz 			if (avail & JBUS_REGS_AVAIL) {
2381370Sschwartz 				if (fpc_dev_kstat(i, 1) != SUCCESS)
2391370Sschwartz 					return (DDI_FAILURE);
2401370Sschwartz 			}
2411370Sschwartz 		} else {
2421370Sschwartz 			if (!num_inst)
2431370Sschwartz 				break;
2441370Sschwartz 			if (fpc_dev_kstat(i, num_inst) != SUCCESS)
2451370Sschwartz 				return (DDI_FAILURE);
2461370Sschwartz 		}
2471370Sschwartz 	}
2481370Sschwartz 
2491370Sschwartz 	return (DDI_SUCCESS);
2501370Sschwartz }
2511370Sschwartz 
2521370Sschwartz static int
fpc_dev_kstat(fire_perfcnt_t reg_group,uint8_t num_inst)2531370Sschwartz fpc_dev_kstat(fire_perfcnt_t reg_group, uint8_t num_inst)
2541370Sschwartz {
2551370Sschwartz 	int i, base_cntrid, num_cntrs;
2561370Sschwartz 	uint32_t num_events, num_events2;
2571370Sschwartz 	char dev_name[DEVICE_NAME_LEN];
2581370Sschwartz 	fi_ksinfo_t *ksinfop;
2591370Sschwartz 	fi_kev_mask_t *fire_events, *fire_events2;
2601370Sschwartz 
2611370Sschwartz 	switch (reg_group) {
2621370Sschwartz 	case imu:
263*3800Skd93003 		(void) strncpy(dev_name, "imu", sizeof (dev_name));
2641370Sschwartz 		num_events = sizeof (fire_imu_events) / sizeof (fi_kev_mask_t);
2651370Sschwartz 		fire_events = fire_imu_events;
2661370Sschwartz 		num_cntrs = NUM_IMU_COUNTERS;
2671370Sschwartz 		break;
2681370Sschwartz 	case mmu:
269*3800Skd93003 		(void) strncpy(dev_name, "mmu", sizeof (dev_name));
2701370Sschwartz 		num_events = sizeof (fire_mmu_events) / sizeof (fi_kev_mask_t);
2711370Sschwartz 		fire_events = fire_mmu_events;
2721370Sschwartz 		num_cntrs = NUM_MMU_COUNTERS;
2731370Sschwartz 		break;
2741370Sschwartz 	case lpu:
275*3800Skd93003 		(void) strncpy(dev_name, "lpu", sizeof (dev_name));
2761370Sschwartz 		num_events = sizeof (fire_lpu_events) / sizeof (fi_kev_mask_t);
2771370Sschwartz 		fire_events = fire_lpu_events;
2781370Sschwartz 		num_cntrs = NUM_LPU_COUNTERS;
2791370Sschwartz 		break;
2801370Sschwartz 	case tlu:
281*3800Skd93003 		(void) strncpy(dev_name, "tlu", sizeof (dev_name));
2821370Sschwartz 		num_events = sizeof (fire_tlu_events) / sizeof (fi_kev_mask_t);
2831370Sschwartz 		num_events2 = sizeof (fire_tlu2_events) /
2841370Sschwartz 					sizeof (fi_kev_mask_t);
2851370Sschwartz 		fire_events = fire_tlu_events;
2861370Sschwartz 		fire_events2 = fire_tlu2_events;
2871370Sschwartz 		num_cntrs = NUM_TLU_COUNTERS;
2881370Sschwartz 		break;
2891370Sschwartz 	case jbc:
2901370Sschwartz 		(void) strncpy(dev_name, "jbc", sizeof (dev_name));
2911370Sschwartz 		num_events = sizeof (fire_jbc_events) / sizeof (fi_kev_mask_t);
2921370Sschwartz 		fire_events = fire_jbc_events;
2931370Sschwartz 		num_cntrs = NUM_JBC_COUNTERS;
2941370Sschwartz 		break;
2951370Sschwartz 	default:
2961370Sschwartz 		return (FAILURE);
2971370Sschwartz 	}
2981370Sschwartz 
2991370Sschwartz 	for (i = 0; i < num_inst; i++) {
3001370Sschwartz 		ksinfop = (fi_ksinfo_t *)kmem_zalloc(sizeof (fi_ksinfo_t),
3011370Sschwartz 						KM_SLEEP);
3021370Sschwartz 
3031370Sschwartz 		ksinfop->pic_num_events = num_events;
3041370Sschwartz 		ksinfop->pic_reg_group = reg_group;
3051370Sschwartz 		ksinfop->pic_leaf_id = i;
3061370Sschwartz 		ksinfop->pic_sel_shift[0] = PIC0_EVT_SEL_SHIFT;
3071370Sschwartz 
3081370Sschwartz 		if (reg_group == lpu)
3091370Sschwartz 			ksinfop->pic_sel_shift[1] = PIC2_EVT_SEL_SHIFT;
3101370Sschwartz 		else
3111370Sschwartz 			ksinfop->pic_sel_shift[1] = PIC1_EVT_SEL_SHIFT;
3121370Sschwartz 
3131370Sschwartz 		/*
3141370Sschwartz 		 * All error cleanup (deleting kstats and freeing memory) is
3151370Sschwartz 		 * done in fire_kstat_fini. So we need to save the ksinfop
3161370Sschwartz 		 * pointer before any possible error exit so fire_kstat_fini
3171370Sschwartz 		 * can find it.
3181370Sschwartz 		 */
3191370Sschwartz 		if (reg_group == imu)
3201370Sschwartz 			fi_imu_kstats[i] = ksinfop;
3211370Sschwartz 		else if (reg_group == mmu)
3221370Sschwartz 			fi_mmu_kstats[i] = ksinfop;
3231370Sschwartz 		else if (reg_group == lpu)
3241370Sschwartz 			fi_lpu_kstats[i] = ksinfop;
3251370Sschwartz 		else if (reg_group == tlu)
3261370Sschwartz 			fi_tlu_kstats[i] = ksinfop;
3271370Sschwartz 		else if (reg_group == jbc)
3281370Sschwartz 			fi_jbc_kstat = ksinfop;
3291370Sschwartz 
3301370Sschwartz 		/* Create basic pic event-type pair (only once) */
3311370Sschwartz 		if (i == 0) {
3321370Sschwartz 			base_cntrid = 0;
3331370Sschwartz 
3341370Sschwartz 			/* The extra counter for TLU is handled separately */
3351370Sschwartz 			if (reg_group == tlu)
3361370Sschwartz 				num_cntrs--;
3371370Sschwartz 
3381370Sschwartz 			if (fpc_create_name_kstat(dev_name, ksinfop,
3391370Sschwartz 			    fire_events, base_cntrid, num_cntrs) != SUCCESS)
3401370Sschwartz 				goto err;
3411370Sschwartz 
3421370Sschwartz 			/*
3431370Sschwartz 			 * extra counter for TLU. The events associated with
3441370Sschwartz 			 * this third counter are different from the events
3451370Sschwartz 			 * for the first and second counters.
3461370Sschwartz 			 */
3471370Sschwartz 			if (reg_group == tlu) {
3481370Sschwartz 				ksinfop->pic_sel_shift[2] = PIC2_EVT_SEL_SHIFT;
3491370Sschwartz 				base_cntrid += num_cntrs;
3501370Sschwartz 				num_cntrs = 1;
3511370Sschwartz 				ksinfop->pic_num_events = num_events2;
3521370Sschwartz 				if (fpc_create_name_kstat(dev_name, ksinfop,
3531370Sschwartz 				    fire_events2, base_cntrid, num_cntrs)
3541370Sschwartz 				    != SUCCESS)
3551370Sschwartz 					goto err;
3561370Sschwartz 
3571370Sschwartz 				num_cntrs = NUM_TLU_COUNTERS;
3581370Sschwartz 			}
3591370Sschwartz 
3601370Sschwartz 		}
3611370Sschwartz 
3621370Sschwartz 		/* create counter kstats */
3631370Sschwartz 		ksinfop->cntr_ksp = fpc_create_cntr_kstat(dev_name, i,
3641370Sschwartz 				fpc_cntr_kstat_update, ksinfop, num_cntrs);
3651370Sschwartz 		if (ksinfop->cntr_ksp == NULL)
3661370Sschwartz 			goto err;
3671370Sschwartz 
3681370Sschwartz 	}
3691370Sschwartz 	return (SUCCESS);
3701370Sschwartz err:
3711370Sschwartz 	return (FAILURE);
3721370Sschwartz 
3731370Sschwartz }
3741370Sschwartz 
3751370Sschwartz static int
fpc_create_name_kstat(char * name,fi_ksinfo_t * pp,fi_kev_mask_t * ev,int base,int num_cntrs)3761370Sschwartz fpc_create_name_kstat(char *name, fi_ksinfo_t *pp, fi_kev_mask_t *ev,
3771370Sschwartz     int base, int num_cntrs)
3781370Sschwartz {
3791370Sschwartz 	int i;
3801370Sschwartz 
3811370Sschwartz #ifdef DEBUG
3821370Sschwartz 	FPC_DBG2("fpc_create_name_kstat: name: %s\n", name);
3831370Sschwartz #endif
3841370Sschwartz 
3851370Sschwartz 	for (i = base; i < (base + num_cntrs); i++) {
3861370Sschwartz 		pp->pic_name_ksp[i] = fpc_create_picN_kstat(name, i,
3871370Sschwartz 			pp->pic_sel_shift[i], pp->pic_num_events, ev);
3881370Sschwartz 
3891370Sschwartz 		if (pp->pic_name_ksp[i] == NULL)
3901370Sschwartz 			return (FAILURE);
3911370Sschwartz 	}
3921370Sschwartz 	return (SUCCESS);
3931370Sschwartz }
3941370Sschwartz 
3951370Sschwartz /*
3961370Sschwartz  * Create the picN kstat. Returns a pointer to the
3971370Sschwartz  * kstat which the driver must store to allow it
3981370Sschwartz  * to be deleted when necessary.
3991370Sschwartz  */
4001370Sschwartz static kstat_t *
fpc_create_picN_kstat(char * mod_name,int pic,int pic_sel_shift,int num_ev,fi_kev_mask_t * ev_array)4011370Sschwartz fpc_create_picN_kstat(char *mod_name, int pic, int pic_sel_shift, int num_ev,
4021370Sschwartz     fi_kev_mask_t *ev_array)
4031370Sschwartz {
4041370Sschwartz 	int event;
4051370Sschwartz 	char pic_name[PIC_STR_LEN];
4061370Sschwartz 	kstat_t	*picN_ksp = NULL;
4071370Sschwartz 	struct kstat_named *pic_named_data;
4081370Sschwartz 
4091370Sschwartz 	(void) snprintf(pic_name, sizeof (pic_name), "pic%d", pic);
4101370Sschwartz 	if ((picN_ksp = kstat_create(mod_name, 0, pic_name,
4111370Sschwartz 	    "bus", KSTAT_TYPE_NAMED, num_ev, NULL)) == NULL) {
4121370Sschwartz 		cmn_err(CE_WARN, "%s %s : kstat create failed",
4131370Sschwartz 		    mod_name, pic_name);
4141370Sschwartz 		return (NULL);
4151370Sschwartz 	}
4161370Sschwartz 
4171370Sschwartz 	pic_named_data = (struct kstat_named *)picN_ksp->ks_data;
4181370Sschwartz 
4191370Sschwartz 	/*
4201370Sschwartz 	 * Fill up data section of the kstat
4211370Sschwartz 	 * Write event names and their associated pcr masks.
4221370Sschwartz 	 * num_ev - 1 is because CLEAR_PIC is added separately.
4231370Sschwartz 	 */
4241370Sschwartz 	for (event = 0; event < num_ev - 1; event++) {
4251370Sschwartz 		pic_named_data[event].value.ui64 =
4261370Sschwartz 			(ev_array[event].pcr_mask << pic_sel_shift);
4271370Sschwartz 
4281370Sschwartz 		kstat_named_init(&pic_named_data[event],
4291370Sschwartz 		    ev_array[event].event_name, KSTAT_DATA_UINT64);
4301370Sschwartz 	}
4311370Sschwartz 
4321370Sschwartz 	/*
4331370Sschwartz 	 * add the clear_pic entry
4341370Sschwartz 	 */
4351370Sschwartz 	pic_named_data[event].value.ui64 =
4361370Sschwartz 			(uint64_t)~(ev_array[event].pcr_mask << pic_sel_shift);
4371370Sschwartz 
4381370Sschwartz 	kstat_named_init(&pic_named_data[event], ev_array[event].event_name,
4391370Sschwartz 	    KSTAT_DATA_UINT64);
4401370Sschwartz 
4411370Sschwartz 	kstat_install(picN_ksp);
4421370Sschwartz 
4431370Sschwartz #ifdef DEBUG
4441370Sschwartz 	FPC_DBG2("fpc_create_picN_kstat: name %s, pic %d, num_ev %d, "
4451370Sschwartz 	    "pic_sel_shift %d\n", mod_name, pic, num_ev, pic_sel_shift);
4461370Sschwartz #endif
4471370Sschwartz 
4481370Sschwartz 	return (picN_ksp);
4491370Sschwartz }
4501370Sschwartz 
4511370Sschwartz /*
4521370Sschwartz  * Create the "counters" kstat.
4531370Sschwartz  */
4541370Sschwartz static kstat_t *
fpc_create_cntr_kstat(char * name,int instance,int (* update)(kstat_t *,int),void * ksinfop,int num_pics)4551370Sschwartz fpc_create_cntr_kstat(char *name, int instance, int (*update)(kstat_t *, int),
4561370Sschwartz     void *ksinfop, int num_pics)
4571370Sschwartz {
4581370Sschwartz 	int i;
4591370Sschwartz 	char pic_str[PIC_STR_LEN];
4601370Sschwartz 	struct kstat *counters_ksp;
4611370Sschwartz 	struct kstat_named *counters_named_data;
4621370Sschwartz 
4631370Sschwartz #ifdef DEBUG
4641370Sschwartz 	FPC_DBG1("fpc_create_cntr_kstat: name: %s instance: %d\n",
4651370Sschwartz 	    name, instance);
4661370Sschwartz #endif
4671370Sschwartz 
4681370Sschwartz 	/*
4691370Sschwartz 	 * Size of kstat is num_pics + 1. extra one for pcr.
4701370Sschwartz 	 */
4711370Sschwartz 	if ((counters_ksp = kstat_create(name, instance, "counters", "bus",
4721370Sschwartz 	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
4731370Sschwartz 		cmn_err(CE_WARN, "kstat_create for %s%d failed",
4741370Sschwartz 						name, instance);
4751370Sschwartz 		return (NULL);
4761370Sschwartz 	}
4771370Sschwartz 
4781370Sschwartz 	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
4791370Sschwartz 	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
4801370Sschwartz 
4811370Sschwartz 	for (i = 0; i < num_pics; i++) {
4821370Sschwartz 		(void) snprintf(pic_str, sizeof (pic_str), "pic%d", i);
4831370Sschwartz 
4841370Sschwartz 		kstat_named_init(&counters_named_data[i+1], pic_str,
4851370Sschwartz 		    KSTAT_DATA_UINT64);
4861370Sschwartz 	}
4871370Sschwartz 
4881370Sschwartz 	/*
4891370Sschwartz 	 * Store the reg type and other info. in the kstat's private field
4901370Sschwartz 	 * so that they are available to the update function.
4911370Sschwartz 	 */
4921370Sschwartz 	counters_ksp->ks_private = (void *)ksinfop;
4931370Sschwartz 	counters_ksp->ks_update = update;
4941370Sschwartz 
4951370Sschwartz 	kstat_install(counters_ksp);
4961370Sschwartz 
4971370Sschwartz 	return (counters_ksp);
4981370Sschwartz }
4991370Sschwartz 
5001370Sschwartz /*
5011370Sschwartz  * kstat update function. Handles reads/writes
5021370Sschwartz  * from/to kstat.
5031370Sschwartz  */
5041370Sschwartz static int
fpc_cntr_kstat_update(kstat_t * ksp,int rw)5051370Sschwartz fpc_cntr_kstat_update(kstat_t *ksp, int rw)
5061370Sschwartz {
5071370Sschwartz 	struct kstat_named *data_p;
5081370Sschwartz 	fi_ksinfo_t *ksinfop = ksp->ks_private;
5091370Sschwartz 	uint64_t counters[NUM_MAX_COUNTERS];
5101370Sschwartz 	uint64_t event;
5111370Sschwartz 
5121370Sschwartz 	data_p = (struct kstat_named *)ksp->ks_data;
5131370Sschwartz 
5141370Sschwartz 	if (rw == KSTAT_WRITE) {
5151370Sschwartz #ifdef DEBUG
5161370Sschwartz 		FPC_DBG2("fpc_cntr_kstat_update: wr %ld\n",
5171370Sschwartz 		    data_p[0].value.ui64);
5181370Sschwartz #endif
5191370Sschwartz 
5201370Sschwartz 		if (fpc_perfcnt_program(ksinfop->pic_leaf_id,
5211370Sschwartz 		    ksinfop->pic_reg_group, data_p[0].value.ui64) != SUCCESS)
5221370Sschwartz 			return (EIO);
5231370Sschwartz 	} else {
5241370Sschwartz 		counters[2] = 0;
5251370Sschwartz 		if (fpc_perfcnt_read(ksinfop->pic_leaf_id,
5261370Sschwartz 		    ksinfop->pic_reg_group, &event, counters) != SUCCESS)
5271370Sschwartz 			return (EIO);
5281370Sschwartz 
5291370Sschwartz 		data_p[0].value.ui64 = event;
5301370Sschwartz 		data_p[1].value.ui64 = counters[0];
5311370Sschwartz 		data_p[2].value.ui64 = counters[1];
5321370Sschwartz 
5331370Sschwartz 		if (ksinfop->pic_reg_group == tlu) {
5341370Sschwartz 			data_p[3].value.ui64 = counters[2];
5351370Sschwartz 		}
5361370Sschwartz #ifdef DEBUG
5371370Sschwartz 		FPC_DBG2("fpc_cntr_kstat_update: rd event %ld, cntr0"
5381370Sschwartz 		    " %ld, cntr1 %ld, cntr2 %ld\n", data_p[0].value.ui64,
5391370Sschwartz 		    counters[0], counters[1], counters[2]);
5401370Sschwartz #endif
5411370Sschwartz 	}
5421370Sschwartz 	return (0);
5431370Sschwartz }
5441370Sschwartz 
5451370Sschwartz void
fpc_kstat_fini(dev_info_t * dip)5461370Sschwartz fpc_kstat_fini(dev_info_t *dip)
5471370Sschwartz {
5481370Sschwartz 	int i;
5491370Sschwartz 
5501370Sschwartz #ifdef DEBUG
5511370Sschwartz 	FPC_DBG1("fpc_kstat_fini called\n");
5521370Sschwartz #endif
5531370Sschwartz 
5541370Sschwartz 	for (i = 0; i < NUM_LEAVES; i++) {
5551370Sschwartz 		/* IMU */
5561370Sschwartz 		if (fi_imu_kstats[i] != NULL) {
5571370Sschwartz 			fpc_delete_name_kstat(fi_imu_kstats[i]);
5581370Sschwartz 			if (fi_imu_kstats[i]->cntr_ksp != NULL)
5591370Sschwartz 				kstat_delete(fi_imu_kstats[i]->cntr_ksp);
5601370Sschwartz 			kmem_free(fi_imu_kstats[i], sizeof (fi_ksinfo_t));
5611370Sschwartz 			fi_imu_kstats[i] = NULL;
5621370Sschwartz 		}
5631370Sschwartz 
5641370Sschwartz 		/* MMU */
5651370Sschwartz 		if (fi_mmu_kstats[i] != NULL) {
5661370Sschwartz 			fpc_delete_name_kstat(fi_mmu_kstats[i]);
5671370Sschwartz 			if (fi_mmu_kstats[i]->cntr_ksp != NULL)
5681370Sschwartz 				kstat_delete(fi_mmu_kstats[i]->cntr_ksp);
5691370Sschwartz 			kmem_free(fi_mmu_kstats[i], sizeof (fi_ksinfo_t));
5701370Sschwartz 			fi_mmu_kstats[i] = NULL;
5711370Sschwartz 		}
5721370Sschwartz 
5731370Sschwartz 		/* LPU */
5741370Sschwartz 		if (fi_lpu_kstats[i] != NULL) {
5751370Sschwartz 			fpc_delete_name_kstat(fi_lpu_kstats[i]);
5761370Sschwartz 			if (fi_lpu_kstats[i]->cntr_ksp != NULL)
5771370Sschwartz 				kstat_delete(fi_lpu_kstats[i]->cntr_ksp);
5781370Sschwartz 			kmem_free(fi_lpu_kstats[i], sizeof (fi_ksinfo_t));
5791370Sschwartz 			fi_lpu_kstats[i] = NULL;
5801370Sschwartz 		}
5811370Sschwartz 
5821370Sschwartz 		/* TLU */
5831370Sschwartz 		if (fi_tlu_kstats[i] != NULL) {
5841370Sschwartz 			fpc_delete_name_kstat(fi_tlu_kstats[i]);
5851370Sschwartz 			if (fi_tlu_kstats[i]->cntr_ksp != NULL)
5861370Sschwartz 				kstat_delete(fi_tlu_kstats[i]->cntr_ksp);
5871370Sschwartz 			kmem_free(fi_tlu_kstats[i], sizeof (fi_ksinfo_t));
5881370Sschwartz 			fi_tlu_kstats[i] = NULL;
5891370Sschwartz 		}
5901370Sschwartz 	}
5911370Sschwartz 
5921370Sschwartz 	/* JBC */
5931370Sschwartz 	if (fi_jbc_kstat != NULL) {
5941370Sschwartz 		fpc_delete_name_kstat(fi_jbc_kstat);
5951370Sschwartz 		if (fi_jbc_kstat->cntr_ksp != NULL)
5961370Sschwartz 			kstat_delete(fi_jbc_kstat->cntr_ksp);
5971370Sschwartz 		kmem_free(fi_jbc_kstat, sizeof (fi_ksinfo_t));
5981370Sschwartz 		fi_jbc_kstat = NULL;
5991370Sschwartz 	}
6001370Sschwartz 
6011370Sschwartz 	(void) fpc_perfcnt_module_fini(dip);
6021370Sschwartz }
6031370Sschwartz 
6041370Sschwartz static void
fpc_delete_name_kstat(fi_ksinfo_t * pp)6051370Sschwartz fpc_delete_name_kstat(fi_ksinfo_t *pp)
6061370Sschwartz {
6071370Sschwartz 	int i;
6081370Sschwartz 
6091370Sschwartz 	if (pp != NULL) {
6101370Sschwartz 		for (i = 0; i < NUM_MAX_COUNTERS; i++) {
6111370Sschwartz 			if (pp->pic_name_ksp[i] != NULL)
6121370Sschwartz 				kstat_delete(pp->pic_name_ksp[i]);
6131370Sschwartz 		}
6141370Sschwartz 	}
6151370Sschwartz }
616