xref: /onnv-gate/usr/src/uts/sun4v/cpu/niagara_perfctr.c (revision 911:ae0281c59454)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <sys/async.h>
310Sstevel@tonic-gate #include <sys/sunddi.h>
320Sstevel@tonic-gate #include <sys/sunndi.h>
330Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
340Sstevel@tonic-gate #include <sys/machsystm.h>
350Sstevel@tonic-gate #include <sys/niagararegs.h>
360Sstevel@tonic-gate #include <sys/hypervisor_api.h>
370Sstevel@tonic-gate #include <sys/kstat.h>
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #define	NIAGARA_MODNAME	"SUNW,UltraSPARC-T1"
400Sstevel@tonic-gate #define	NUM_OF_PICS	2
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  * Data structure used to build array of event-names and pcr-mask values
440Sstevel@tonic-gate  */
450Sstevel@tonic-gate typedef struct ni_kev_mask {
460Sstevel@tonic-gate 	char		*event_name;
470Sstevel@tonic-gate 	uint64_t	pcr_mask;
480Sstevel@tonic-gate } ni_kev_mask_t;
490Sstevel@tonic-gate 
500Sstevel@tonic-gate /*
510Sstevel@tonic-gate  * Kstat data structure for DRAM and JBUS performance counters
520Sstevel@tonic-gate  *
530Sstevel@tonic-gate  * Note that these performance counters are only 31 bits wide. Since
540Sstevel@tonic-gate  * the "busstat" command assumes a 32-bit counter, we emulate a 32-bit
550Sstevel@tonic-gate  * counter by detecting overflow on read of these performance counters
560Sstevel@tonic-gate  * and using the least significant bit of the overflow count as the
570Sstevel@tonic-gate  * most significant bit (i.e. bit# 31) of the DRAM and JBUS performance
580Sstevel@tonic-gate  * counters.
590Sstevel@tonic-gate  */
600Sstevel@tonic-gate typedef struct ni_ksinfo {
610Sstevel@tonic-gate 	uint8_t		pic_no_evs;			/* number of events */
620Sstevel@tonic-gate 	uint8_t		pic_sel_shift[NUM_OF_PICS];
630Sstevel@tonic-gate 	uint8_t		pic_shift[NUM_OF_PICS];
640Sstevel@tonic-gate 	uint64_t	pic_mask[NUM_OF_PICS];
650Sstevel@tonic-gate 	kstat_t		*pic_name_ksp[NUM_OF_PICS];
660Sstevel@tonic-gate 	kstat_t		*cntr_ksp;
670Sstevel@tonic-gate 	uint32_t	pic_reg;
680Sstevel@tonic-gate 	uint32_t	pcr_reg;
690Sstevel@tonic-gate 	uint32_t	pic_overflow[NUM_OF_PICS];	/* overflow count */
700Sstevel@tonic-gate 	uint32_t	pic_last_val[NUM_OF_PICS];	/* last PIC value */
710Sstevel@tonic-gate } ni_ksinfo_t;
720Sstevel@tonic-gate 
730Sstevel@tonic-gate static ni_ksinfo_t	*ni_dram_kstats[NIAGARA_DRAM_BANKS];
740Sstevel@tonic-gate static ni_ksinfo_t	*ni_jbus_kstat;
750Sstevel@tonic-gate 
760Sstevel@tonic-gate typedef struct ni_perf_regs {
770Sstevel@tonic-gate 	uint32_t	pcr_reg;
780Sstevel@tonic-gate 	uint32_t	pic_reg;
790Sstevel@tonic-gate } ni_perf_regs_t;
800Sstevel@tonic-gate 
810Sstevel@tonic-gate static ni_perf_regs_t dram_perf_regs[] = {
820Sstevel@tonic-gate 	{HV_NIAGARA_DRAM_CTL0, HV_NIAGARA_DRAM_COUNT0},
830Sstevel@tonic-gate 	{HV_NIAGARA_DRAM_CTL1, HV_NIAGARA_DRAM_COUNT1},
840Sstevel@tonic-gate 	{HV_NIAGARA_DRAM_CTL2, HV_NIAGARA_DRAM_COUNT2},
850Sstevel@tonic-gate 	{HV_NIAGARA_DRAM_CTL3, HV_NIAGARA_DRAM_COUNT3},
860Sstevel@tonic-gate };
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 
890Sstevel@tonic-gate static void ni_create_name_kstat(char *, ni_ksinfo_t *, ni_kev_mask_t *);
900Sstevel@tonic-gate static void ni_delete_name_kstat(ni_ksinfo_t *);
910Sstevel@tonic-gate 
920Sstevel@tonic-gate static kstat_t *ni_create_cntr_kstat(char *, int,
930Sstevel@tonic-gate 	int (*update)(kstat_t *, int), void *);
940Sstevel@tonic-gate 
950Sstevel@tonic-gate static int ni_cntr_kstat_update(kstat_t *, int);
960Sstevel@tonic-gate 
970Sstevel@tonic-gate static kstat_t *ni_create_picN_kstat(char *, int, int, int,
980Sstevel@tonic-gate 	ni_kev_mask_t *);
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate #ifdef DEBUG
1010Sstevel@tonic-gate static int	ni_perf_debug;
1020Sstevel@tonic-gate #endif
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate /*
1050Sstevel@tonic-gate  * Niagara DRAM Performance Events
1060Sstevel@tonic-gate  */
1070Sstevel@tonic-gate static ni_kev_mask_t
1080Sstevel@tonic-gate niagara_dram_events[] = {
1090Sstevel@tonic-gate 	{"mem_reads",		0x0},
1100Sstevel@tonic-gate 	{"mem_writes",		0x1},
1110Sstevel@tonic-gate 	{"mem_read_write",	0x2},
1120Sstevel@tonic-gate 	{"bank_busy_stalls",	0x3},
1130Sstevel@tonic-gate 	{"rd_queue_latency",	0x4},
1140Sstevel@tonic-gate 	{"wr_queue_latency",	0x5},
1150Sstevel@tonic-gate 	{"rw_queue_latency",	0x6},
1160Sstevel@tonic-gate 	{"wb_buf_hits",		0x7},
1170Sstevel@tonic-gate 	{"clear_pic", 0xf}
1180Sstevel@tonic-gate };
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate  * Niagara JBUS Performance Events
1230Sstevel@tonic-gate  */
1240Sstevel@tonic-gate static ni_kev_mask_t
1250Sstevel@tonic-gate niagara_jbus_events[] = {
1260Sstevel@tonic-gate 	{"jbus_cycles",		0x1},
1270Sstevel@tonic-gate 	{"dma_reads",		0x2},
1280Sstevel@tonic-gate 	{"dma_read_latency",	0x3},
1290Sstevel@tonic-gate 	{"dma_writes",		0x4},
1300Sstevel@tonic-gate 	{"dma_write8",		0x5},
1310Sstevel@tonic-gate 	{"ordering_waits",	0x6},
1320Sstevel@tonic-gate 	{"pio_reads",		0x8},
1330Sstevel@tonic-gate 	{"pio_read_latency",	0x9},
1340Sstevel@tonic-gate 	{"aok_dok_off_cycles",	0xc},
1350Sstevel@tonic-gate 	{"aok_off_cycles",	0xd},
1360Sstevel@tonic-gate 	{"dok_off_cycles",	0xe},
1370Sstevel@tonic-gate 	{"clear_pic",		0xf}
1380Sstevel@tonic-gate };
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate /*
1410Sstevel@tonic-gate  * Create the picN kstats for DRAM and JBUS events
1420Sstevel@tonic-gate  */
1430Sstevel@tonic-gate void
1440Sstevel@tonic-gate niagara_kstat_init()
1450Sstevel@tonic-gate {
1460Sstevel@tonic-gate 	int i;
1470Sstevel@tonic-gate 	ni_ksinfo_t *ksinfop;
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate #ifdef DEBUG
1500Sstevel@tonic-gate 	if (ni_perf_debug)
1510Sstevel@tonic-gate 		printf("ni_kstat_init called\n");
1520Sstevel@tonic-gate #endif
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	/*
1550Sstevel@tonic-gate 	 * Create DRAM perf events kstat
1560Sstevel@tonic-gate 	 */
1570Sstevel@tonic-gate 	for (i = 0; i < NIAGARA_DRAM_BANKS; i++) {
1580Sstevel@tonic-gate 		ksinfop = (ni_ksinfo_t *)kmem_zalloc(sizeof (ni_ksinfo_t),
1590Sstevel@tonic-gate 		    KM_NOSLEEP);
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 		if (ksinfop == NULL) {
1620Sstevel@tonic-gate 			cmn_err(CE_WARN,
1630Sstevel@tonic-gate 			    "%s: no space for niagara dram kstat\n",
1640Sstevel@tonic-gate 			    NIAGARA_MODNAME);
1650Sstevel@tonic-gate 			break;
1660Sstevel@tonic-gate 		}
1670Sstevel@tonic-gate 		ksinfop->pic_no_evs =
1680Sstevel@tonic-gate 			sizeof (niagara_dram_events) / sizeof (ni_kev_mask_t);
1690Sstevel@tonic-gate 		ksinfop->pic_sel_shift[0] = NIAGARA_DRAM_PIC0_SEL_SHIFT;
1700Sstevel@tonic-gate 		ksinfop->pic_shift[0] = NIAGARA_DRAM_PIC0_SHIFT;
1710Sstevel@tonic-gate 		ksinfop->pic_mask[0] = NIAGARA_DRAM_PIC0_MASK;
1720Sstevel@tonic-gate 		ksinfop->pic_sel_shift[1] = NIAGARA_DRAM_PIC1_SEL_SHIFT;
1730Sstevel@tonic-gate 		ksinfop->pic_shift[1] = NIAGARA_DRAM_PIC1_SHIFT;
1740Sstevel@tonic-gate 		ksinfop->pic_mask[1] = NIAGARA_DRAM_PIC1_MASK;
1750Sstevel@tonic-gate 		ksinfop->pic_reg = dram_perf_regs[i].pic_reg;
1760Sstevel@tonic-gate 		ksinfop->pcr_reg = dram_perf_regs[i].pcr_reg;
1770Sstevel@tonic-gate 		ni_dram_kstats[i] = ksinfop;
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 		/* create basic pic event/mask pair (only once) */
1800Sstevel@tonic-gate 		if (i == 0)
1810Sstevel@tonic-gate 			ni_create_name_kstat("dram", ksinfop,
1820Sstevel@tonic-gate 				    niagara_dram_events);
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 		/* create counter kstats */
1850Sstevel@tonic-gate 		ni_dram_kstats[i]->cntr_ksp = ni_create_cntr_kstat("dram", i,
1860Sstevel@tonic-gate 		    ni_cntr_kstat_update, ksinfop);
1870Sstevel@tonic-gate 	}
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	/*
1900Sstevel@tonic-gate 	 * Create JBUS perf events kstat
1910Sstevel@tonic-gate 	 */
1920Sstevel@tonic-gate 	ni_jbus_kstat = (ni_ksinfo_t *)kmem_alloc(sizeof (ni_ksinfo_t),
1930Sstevel@tonic-gate 		KM_NOSLEEP);
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	if (ni_jbus_kstat == NULL) {
1960Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s: no space for niagara jbus kstat\n",
1970Sstevel@tonic-gate 		    NIAGARA_MODNAME);
1980Sstevel@tonic-gate 	} else {
1990Sstevel@tonic-gate 		ni_jbus_kstat->pic_no_evs =
2000Sstevel@tonic-gate 			sizeof (niagara_jbus_events) / sizeof (ni_kev_mask_t);
2010Sstevel@tonic-gate 		ni_jbus_kstat->pic_sel_shift[0] = NIAGARA_JBUS_PIC0_SEL_SHIFT;
2020Sstevel@tonic-gate 		ni_jbus_kstat->pic_shift[0] = NIAGARA_JBUS_PIC0_SHIFT;
2030Sstevel@tonic-gate 		ni_jbus_kstat->pic_mask[0] = NIAGARA_JBUS_PIC0_MASK;
2040Sstevel@tonic-gate 		ni_jbus_kstat->pic_sel_shift[1] = NIAGARA_JBUS_PIC1_SEL_SHIFT;
2050Sstevel@tonic-gate 		ni_jbus_kstat->pic_shift[1] = NIAGARA_JBUS_PIC1_SHIFT;
2060Sstevel@tonic-gate 		ni_jbus_kstat->pic_mask[1] = NIAGARA_JBUS_PIC1_MASK;
2070Sstevel@tonic-gate 		ni_jbus_kstat->pic_reg = HV_NIAGARA_JBUS_COUNT;
2080Sstevel@tonic-gate 		ni_jbus_kstat->pcr_reg = HV_NIAGARA_JBUS_CTL;
2090Sstevel@tonic-gate 		ni_create_name_kstat("jbus", ni_jbus_kstat,
2100Sstevel@tonic-gate 		    niagara_jbus_events);
2110Sstevel@tonic-gate 		ni_jbus_kstat->cntr_ksp = ni_create_cntr_kstat("jbus", 0,
2120Sstevel@tonic-gate 		    ni_cntr_kstat_update, ni_jbus_kstat);
2130Sstevel@tonic-gate 	}
2140Sstevel@tonic-gate }
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate void
2170Sstevel@tonic-gate niagara_kstat_fini()
2180Sstevel@tonic-gate {
2190Sstevel@tonic-gate 	int i;
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate #ifdef DEBUG
2220Sstevel@tonic-gate 	if (ni_perf_debug)
2230Sstevel@tonic-gate 		printf("ni_kstat_fini called\n");
2240Sstevel@tonic-gate #endif
2250Sstevel@tonic-gate 	for (i = 0; i < NIAGARA_DRAM_BANKS; i++) {
2260Sstevel@tonic-gate 		if (ni_dram_kstats[i] != NULL) {
2270Sstevel@tonic-gate 			ni_delete_name_kstat(ni_dram_kstats[i]);
2280Sstevel@tonic-gate 			if (ni_dram_kstats[i]->cntr_ksp != NULL)
2290Sstevel@tonic-gate 				kstat_delete(ni_dram_kstats[i]->cntr_ksp);
2300Sstevel@tonic-gate 			kmem_free(ni_dram_kstats[i], sizeof (ni_ksinfo_t));
2310Sstevel@tonic-gate 			ni_dram_kstats[i] = NULL;
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 	}
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	if (ni_jbus_kstat != NULL) {
2360Sstevel@tonic-gate 		ni_delete_name_kstat(ni_jbus_kstat);
2370Sstevel@tonic-gate 		if (ni_jbus_kstat->cntr_ksp != NULL)
2380Sstevel@tonic-gate 			kstat_delete(ni_jbus_kstat->cntr_ksp);
2390Sstevel@tonic-gate 		kmem_free(ni_jbus_kstat, sizeof (ni_ksinfo_t));
2400Sstevel@tonic-gate 		ni_jbus_kstat = NULL;
2410Sstevel@tonic-gate 	}
2420Sstevel@tonic-gate }
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate static void
2450Sstevel@tonic-gate ni_create_name_kstat(char *name, ni_ksinfo_t *pp, ni_kev_mask_t *ev)
2460Sstevel@tonic-gate {
2470Sstevel@tonic-gate 	int	i;
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate #ifdef DEBUG
2500Sstevel@tonic-gate 	if (ni_perf_debug > 1)
2510Sstevel@tonic-gate 		printf("ni_create_name_kstat: name: %s\n", name);
2520Sstevel@tonic-gate #endif
2530Sstevel@tonic-gate 	for (i = 0; i < NUM_OF_PICS; i++) {
2540Sstevel@tonic-gate 		pp->pic_name_ksp[i] = ni_create_picN_kstat(name,
2550Sstevel@tonic-gate 			i, pp->pic_sel_shift[i], pp->pic_no_evs, ev);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 		if (pp->pic_name_ksp[i] == NULL) {
2580Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s: unable to create name kstat",
2590Sstevel@tonic-gate 			    NIAGARA_MODNAME);
2600Sstevel@tonic-gate 		}
2610Sstevel@tonic-gate 	}
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate static void
2650Sstevel@tonic-gate ni_delete_name_kstat(ni_ksinfo_t *pp)
2660Sstevel@tonic-gate {
2670Sstevel@tonic-gate 	int	i;
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	if (pp != NULL) {
2700Sstevel@tonic-gate 		for (i = 0; i < NUM_OF_PICS; i++) {
2710Sstevel@tonic-gate 			if (pp->pic_name_ksp[i] != NULL)
2720Sstevel@tonic-gate 				kstat_delete(pp->pic_name_ksp[i]);
2730Sstevel@tonic-gate 		}
2740Sstevel@tonic-gate 	}
2750Sstevel@tonic-gate }
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate /*
2780Sstevel@tonic-gate  * Create the picN kstat. Returns a pointer to the
2790Sstevel@tonic-gate  * kstat which the driver must store to allow it
2800Sstevel@tonic-gate  * to be deleted when necessary.
2810Sstevel@tonic-gate  */
2820Sstevel@tonic-gate static kstat_t *
2830Sstevel@tonic-gate ni_create_picN_kstat(char *mod_name, int pic, int pic_sel_shift,
2840Sstevel@tonic-gate 	int num_ev, ni_kev_mask_t *ev_array)
2850Sstevel@tonic-gate {
2860Sstevel@tonic-gate 	struct kstat_named *pic_named_data;
2870Sstevel@tonic-gate 	int	inst = 0;
2880Sstevel@tonic-gate 	int	event;
2890Sstevel@tonic-gate 	char	pic_name[30];
2900Sstevel@tonic-gate 	kstat_t	*picN_ksp = NULL;
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	(void) sprintf(pic_name, "pic%d", pic);
2930Sstevel@tonic-gate 	if ((picN_ksp = kstat_create(mod_name, inst, pic_name,
2940Sstevel@tonic-gate 	    "bus", KSTAT_TYPE_NAMED, num_ev, NULL)) == NULL) {
2950Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s %s : kstat create failed",
2960Sstevel@tonic-gate 			mod_name, pic_name);
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 		/*
2990Sstevel@tonic-gate 		 * It is up to the calling function to delete any kstats
3000Sstevel@tonic-gate 		 * that may have been created already. We just
3010Sstevel@tonic-gate 		 * return NULL to indicate an error has occured.
3020Sstevel@tonic-gate 		 */
3030Sstevel@tonic-gate 		return (NULL);
3040Sstevel@tonic-gate 	}
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	pic_named_data = (struct kstat_named *)
3070Sstevel@tonic-gate 	    picN_ksp->ks_data;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	/*
3100Sstevel@tonic-gate 	 * Write event names and their associated pcr masks. The
3110Sstevel@tonic-gate 	 * last entry in the array (clear_pic) is added seperately
3120Sstevel@tonic-gate 	 * below as the pic value must be inverted.
3130Sstevel@tonic-gate 	 */
3140Sstevel@tonic-gate 	for (event = 0; event < num_ev - 1; event++) {
3150Sstevel@tonic-gate 		pic_named_data[event].value.ui64 =
3160Sstevel@tonic-gate 			(ev_array[event].pcr_mask << pic_sel_shift);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 		kstat_named_init(&pic_named_data[event],
3190Sstevel@tonic-gate 			ev_array[event].event_name,
3200Sstevel@tonic-gate 			KSTAT_DATA_UINT64);
3210Sstevel@tonic-gate 	}
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	/*
3240Sstevel@tonic-gate 	 * add the clear_pic entry.
3250Sstevel@tonic-gate 	 */
3260Sstevel@tonic-gate 	pic_named_data[event].value.ui64 =
3270Sstevel@tonic-gate 		(uint64_t)~(ev_array[event].pcr_mask << pic_sel_shift);
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	kstat_named_init(&pic_named_data[event], ev_array[event].event_name,
3300Sstevel@tonic-gate 	    KSTAT_DATA_UINT64);
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	kstat_install(picN_ksp);
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	return (picN_ksp);
3350Sstevel@tonic-gate }
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate /*
3380Sstevel@tonic-gate  * Create the "counters" kstat.
3390Sstevel@tonic-gate  */
3400Sstevel@tonic-gate static kstat_t *
3410Sstevel@tonic-gate ni_create_cntr_kstat(char *name, int instance, int (*update)(kstat_t *, int),
3420Sstevel@tonic-gate 	void *ksinfop)
3430Sstevel@tonic-gate {
3440Sstevel@tonic-gate 	struct kstat	*counters_ksp;
3450Sstevel@tonic-gate 	struct kstat_named	*counters_named_data;
3460Sstevel@tonic-gate 	char		pic_str[10];
3470Sstevel@tonic-gate 	int		i;
3480Sstevel@tonic-gate 	int		num_pics = NUM_OF_PICS;
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate #ifdef DEBUG
3510Sstevel@tonic-gate 	if (ni_perf_debug > 1)
3520Sstevel@tonic-gate 		printf("ni_create_cntr_kstat: name: %s instance: %d\n",
3530Sstevel@tonic-gate 		    name, instance);
3540Sstevel@tonic-gate #endif
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	/*
3570Sstevel@tonic-gate 	 * Size of kstat is num_pics + 1 as it
3580Sstevel@tonic-gate 	 * also contains the %pcr
3590Sstevel@tonic-gate 	 */
3600Sstevel@tonic-gate 	if ((counters_ksp = kstat_create(name, instance, "counters", "bus",
3610Sstevel@tonic-gate 	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
3620Sstevel@tonic-gate 		cmn_err(CE_WARN,
3630Sstevel@tonic-gate 		    "%s: kstat_create for %s%d failed", NIAGARA_MODNAME,
3640Sstevel@tonic-gate 		    name, instance);
3650Sstevel@tonic-gate 		return (NULL);
3660Sstevel@tonic-gate 	}
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	/*
3710Sstevel@tonic-gate 	 * Iinitialize the named kstats
3720Sstevel@tonic-gate 	 */
3730Sstevel@tonic-gate 	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 	for (i = 0; i < num_pics; i++) {
3760Sstevel@tonic-gate 		(void) sprintf(pic_str, "pic%d", i);
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate 		kstat_named_init(&counters_named_data[i+1], pic_str,
3790Sstevel@tonic-gate 		    KSTAT_DATA_UINT64);
3800Sstevel@tonic-gate 	}
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	/*
3830Sstevel@tonic-gate 	 * Store the register offset's in the kstat's
3840Sstevel@tonic-gate 	 * private field so that they are available
3850Sstevel@tonic-gate 	 * to the update function.
3860Sstevel@tonic-gate 	 */
3870Sstevel@tonic-gate 	counters_ksp->ks_private = (void *)ksinfop;
3880Sstevel@tonic-gate 	counters_ksp->ks_update = update;
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	kstat_install(counters_ksp);
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	return (counters_ksp);
3930Sstevel@tonic-gate }
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate /*
3960Sstevel@tonic-gate  * kstat update function. Handles reads/writes
3970Sstevel@tonic-gate  * from/to kstat.
3980Sstevel@tonic-gate  */
3990Sstevel@tonic-gate static int
4000Sstevel@tonic-gate ni_cntr_kstat_update(kstat_t *ksp, int rw)
4010Sstevel@tonic-gate {
4020Sstevel@tonic-gate 	struct kstat_named	*data_p;
4030Sstevel@tonic-gate 	ni_ksinfo_t	*ksinfop = ksp->ks_private;
4040Sstevel@tonic-gate 	uint64_t	pic, pcr;
4050Sstevel@tonic-gate 	int		stat = 0;
4060Sstevel@tonic-gate 	uint32_t	pic0, pic1;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	data_p = (struct kstat_named *)ksp->ks_data;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	if (rw == KSTAT_WRITE) {
4110Sstevel@tonic-gate #ifdef DEBUG
4120Sstevel@tonic-gate 		if (ni_perf_debug)
413*911Siskreen 			printf("ni_cntr_kstat_update: wr pcr-%d: %lx\n",
4140Sstevel@tonic-gate 			    ksinfop->pcr_reg, data_p[0].value.ui64);
4150Sstevel@tonic-gate #endif
4160Sstevel@tonic-gate 		if (hv_niagara_setperf(ksinfop->pcr_reg, data_p[0].value.ui64))
4170Sstevel@tonic-gate 			stat = EACCES;
4180Sstevel@tonic-gate 	} else {
4190Sstevel@tonic-gate 		if (hv_niagara_getperf(ksinfop->pic_reg, &pic) != 0 ||
4200Sstevel@tonic-gate 		    hv_niagara_getperf(ksinfop->pcr_reg, &pcr) != 0)
4210Sstevel@tonic-gate 			stat = EACCES;
4220Sstevel@tonic-gate 		else {
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 			data_p[0].value.ui64 = pcr;
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 			/*
4270Sstevel@tonic-gate 			 * Generate a 32-bit PIC0 value by detecting overflow
4280Sstevel@tonic-gate 			 */
4290Sstevel@tonic-gate 			pic0 = (uint32_t)((pic >> ksinfop->pic_shift[0]) &
4300Sstevel@tonic-gate 			    ksinfop->pic_mask[0]);
4310Sstevel@tonic-gate 			if (pic0 < ksinfop->pic_last_val[0])
4320Sstevel@tonic-gate 				ksinfop->pic_overflow[0]++;
4330Sstevel@tonic-gate 			ksinfop->pic_last_val[0] = pic0;
4340Sstevel@tonic-gate 			pic0 += (ksinfop->pic_overflow[0] & 1) << 31;
4350Sstevel@tonic-gate 			data_p[1].value.ui64 = (uint64_t)pic0;
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 			/*
4380Sstevel@tonic-gate 			 * Generate a 32-bit PIC1 value by detecting overflow
4390Sstevel@tonic-gate 			 */
4400Sstevel@tonic-gate 			pic1 = (uint32_t)((pic >> ksinfop->pic_shift[1]) &
4410Sstevel@tonic-gate 			    ksinfop->pic_mask[1]);
4420Sstevel@tonic-gate 			if (pic1 < ksinfop->pic_last_val[1])
4430Sstevel@tonic-gate 				ksinfop->pic_overflow[1]++;
4440Sstevel@tonic-gate 			ksinfop->pic_last_val[1] = pic1;
4450Sstevel@tonic-gate 			pic1 += (ksinfop->pic_overflow[1] & 1) << 31;
4460Sstevel@tonic-gate 			data_p[2].value.ui64 = (uint64_t)pic1;
4470Sstevel@tonic-gate 		}
4480Sstevel@tonic-gate #ifdef DEBUG
4490Sstevel@tonic-gate 		if (ni_perf_debug)
4500Sstevel@tonic-gate 			printf("ni_cntr_kstat_update: rd pcr%d: %lx  "
4510Sstevel@tonic-gate 			    "pic%d: %16lx pic0: %8lx pic1: %8lx\n",
4520Sstevel@tonic-gate 			    ksinfop->pcr_reg, pcr, ksinfop->pic_reg, pic,
4530Sstevel@tonic-gate 			    data_p[1].value.ui64, data_p[2].value.ui64);
4540Sstevel@tonic-gate #endif
4550Sstevel@tonic-gate 	}
4560Sstevel@tonic-gate 	return (stat);
4570Sstevel@tonic-gate }
458