/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <sys/async.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/ddi_impldefs.h>
#include <sys/machsystm.h>
#include <sys/hypervisor_api.h>
#include <sys/kstat.h>
#if defined(NIAGARA_IMPL)
#include <sys/niagararegs.h>
#elif defined(NIAGARA2_IMPL) || defined(VFALLS_IMPL)
#include <sys/niagara2regs.h>
#endif

extern char cpu_module_name[];

/*
 * Data structure used to build array of event-names and pcr-mask values
 */
typedef struct ni_kev_mask {
	char		*event_name;
	uint64_t	pcr_mask;
} ni_kev_mask_t;

/*
 * Kstat data structure for DRAM and JBUS performance counters
 *
 * Note that these performance counters are only 31 bits wide. Since
 * the "busstat" command assumes a 32-bit counter, we emulate a 32-bit
 * counter by detecting overflow on read of these performance counters
 * and using the least significant bit of the overflow count as the
 * most significant bit (i.e. bit# 31) of the DRAM and JBUS performance
 * counters.
 */
#define	NUM_OF_PICS	2

typedef struct ni_ksinfo {
	uint8_t		pic_no_evs;			/* number of events */
	uint8_t		pic_sel_shift[NUM_OF_PICS];
	uint8_t		pic_shift[NUM_OF_PICS];
	uint64_t	pic_mask[NUM_OF_PICS];
	kstat_t		*pic_name_ksp[NUM_OF_PICS];
	kstat_t		*cntr_ksp;
	uint32_t	pic_reg[NUM_OF_PICS];
	uint32_t	pcr_reg;
	uint32_t	pic_overflow[NUM_OF_PICS];	/* overflow count */
	uint32_t	pic_last_val[NUM_OF_PICS];	/* last PIC value */
} ni_ksinfo_t;

static ni_ksinfo_t	*ni_dram_kstats[NIAGARA_DRAM_BANKS];

#if defined(NIAGARA_IMPL)
static ni_ksinfo_t	*ni_jbus_kstat;
#endif

typedef struct ni_perf_regs {
	uint32_t	pcr_reg;
	uint32_t	pic_reg;
} ni_perf_regs_t;

static ni_perf_regs_t dram_perf_regs[] = {
	{HV_NIAGARA_DRAM_CTL0, HV_NIAGARA_DRAM_COUNT0},
	{HV_NIAGARA_DRAM_CTL1, HV_NIAGARA_DRAM_COUNT1},
	{HV_NIAGARA_DRAM_CTL2, HV_NIAGARA_DRAM_COUNT2},
	{HV_NIAGARA_DRAM_CTL3, HV_NIAGARA_DRAM_COUNT3},
#ifdef VFALLS_IMPL
	{HV_NIAGARA_DRAM_CTL4, HV_NIAGARA_DRAM_COUNT4},
	{HV_NIAGARA_DRAM_CTL5, HV_NIAGARA_DRAM_COUNT5},
	{HV_NIAGARA_DRAM_CTL6, HV_NIAGARA_DRAM_COUNT6},
	{HV_NIAGARA_DRAM_CTL7, HV_NIAGARA_DRAM_COUNT7}
#endif
};

#ifdef VFALLS_IMPL
/*
 * Kstat data structure for Zambezi performance counters
 * These performance counters are 64 bits wide.
 */
static ni_ksinfo_t	*zam_lpu_kstats[ZAMBEZI_LPU_COUNTERS];
static ni_ksinfo_t	*zam_gpd_kstats[ZAMBEZI_GPD_COUNTERS];
static ni_ksinfo_t	*zam_asu_kstats[ZAMBEZI_ASU_COUNTERS];

typedef struct zam_perf_regs {
	uint32_t	pcr_reg;
	uint32_t	pic_reg[NUM_OF_PICS];
} zam_perf_regs_t;

static zam_perf_regs_t lpu_perf_regs[] = {
	{HV_ZAM0_LPU_A_PCR, HV_ZAM0_LPU_A_PIC0, HV_ZAM0_LPU_A_PIC1},
	{HV_ZAM0_LPU_B_PCR, HV_ZAM0_LPU_B_PIC0, HV_ZAM0_LPU_B_PIC1},
	{HV_ZAM0_LPU_C_PCR, HV_ZAM0_LPU_C_PIC0, HV_ZAM0_LPU_C_PIC1},
	{HV_ZAM0_LPU_D_PCR, HV_ZAM0_LPU_D_PIC0, HV_ZAM0_LPU_D_PIC1},

	{HV_ZAM1_LPU_A_PCR, HV_ZAM1_LPU_A_PIC0, HV_ZAM1_LPU_A_PIC1},
	{HV_ZAM1_LPU_B_PCR, HV_ZAM1_LPU_B_PIC0, HV_ZAM1_LPU_B_PIC1},
	{HV_ZAM1_LPU_C_PCR, HV_ZAM1_LPU_C_PIC0, HV_ZAM1_LPU_C_PIC1},
	{HV_ZAM1_LPU_D_PCR, HV_ZAM1_LPU_D_PIC0, HV_ZAM1_LPU_D_PIC1},

	{HV_ZAM2_LPU_A_PCR, HV_ZAM2_LPU_A_PIC0, HV_ZAM2_LPU_A_PIC1},
	{HV_ZAM2_LPU_B_PCR, HV_ZAM2_LPU_B_PIC0, HV_ZAM2_LPU_B_PIC1},
	{HV_ZAM2_LPU_C_PCR, HV_ZAM2_LPU_C_PIC0, HV_ZAM2_LPU_C_PIC1},
	{HV_ZAM2_LPU_D_PCR, HV_ZAM2_LPU_D_PIC0, HV_ZAM2_LPU_D_PIC1},

	{HV_ZAM3_LPU_A_PCR, HV_ZAM3_LPU_A_PIC0, HV_ZAM3_LPU_A_PIC1},
	{HV_ZAM3_LPU_B_PCR, HV_ZAM3_LPU_B_PIC0, HV_ZAM3_LPU_B_PIC1},
	{HV_ZAM3_LPU_C_PCR, HV_ZAM3_LPU_C_PIC0, HV_ZAM3_LPU_C_PIC1},
	{HV_ZAM3_LPU_D_PCR, HV_ZAM3_LPU_D_PIC0, HV_ZAM3_LPU_D_PIC1}
};

static zam_perf_regs_t gpd_perf_regs[] = {
	{HV_ZAM0_GPD_PCR, HV_ZAM0_GPD_PIC0, HV_ZAM0_GPD_PIC1},
	{HV_ZAM1_GPD_PCR, HV_ZAM1_GPD_PIC0, HV_ZAM1_GPD_PIC1},
	{HV_ZAM2_GPD_PCR, HV_ZAM2_GPD_PIC0, HV_ZAM2_GPD_PIC1},
	{HV_ZAM3_GPD_PCR, HV_ZAM3_GPD_PIC0, HV_ZAM3_GPD_PIC1}
};

static zam_perf_regs_t asu_perf_regs[] = {
	{HV_ZAM0_ASU_PCR, HV_ZAM0_ASU_PIC0, HV_ZAM0_ASU_PIC1},
	{HV_ZAM1_ASU_PCR, HV_ZAM1_ASU_PIC0, HV_ZAM1_ASU_PIC1},
	{HV_ZAM2_ASU_PCR, HV_ZAM2_ASU_PIC0, HV_ZAM2_ASU_PIC1},
	{HV_ZAM3_ASU_PCR, HV_ZAM3_ASU_PIC0, HV_ZAM3_ASU_PIC1}
};

static int zam_cntr_kstat_update(kstat_t *, int);
#endif

static void ni_create_name_kstat(char *, ni_ksinfo_t *, ni_kev_mask_t *);
static void ni_delete_name_kstat(ni_ksinfo_t *);

static kstat_t *ni_create_cntr_kstat(char *, int,
	int (*update)(kstat_t *, int), void *);

static int ni_cntr_kstat_update(kstat_t *, int);

static kstat_t *ni_create_picN_kstat(char *, int, int, int,
	ni_kev_mask_t *);

#ifdef DEBUG
static int	ni_perf_debug;
#endif

/*
 * Niagara, Niagara2 and VFalls DRAM Performance Events
 */
static ni_kev_mask_t
niagara_dram_events[] = {
	{"mem_reads",		0x0},
	{"mem_writes",		0x1},
	{"mem_read_write",	0x2},
	{"bank_busy_stalls",	0x3},
	{"rd_queue_latency",	0x4},
	{"wr_queue_latency",	0x5},
	{"rw_queue_latency",	0x6},
	{"wb_buf_hits",		0x7},
	{"clear_pic",		0xf}
};

#if defined(VFALLS_IMPL)
/*
 * Zambezi Performance Events
 */
static ni_kev_mask_t
zam_lpu_perf_events[] = {
	{"none",		0x0},
	{"clock_cycles",	0x1},
	{"cycles_c2c_portX",	0x2},
	{"cycles_mem_portX",	0x3},
	{"cycles_WB_portX",	0x4},
	{"cycles_NC_portX",	0x5},
	{"cycles_c2c_portY",	0x6},
	{"cycles_mem_portY",	0x7},
	{"cycles_WB_portY",	0x8},
	{"cycles_NC_portY",	0x9},
	{"cycles_c2c_portZ",	0xa},
	{"cycles_mem_portZ",	0xb},
	{"cycles_WB_portZ",	0xc},
	{"cycles_NC_portZ",	0xd},
	{"cycles_TID_WB",	0xe},
	{"cycles_TID_INV",	0xf},
	{"cycles_TID_RTD",	0x10},
	{"cycles_TID_RTO",	0x11},
	{"cycles_TID_RTS",	0x12},
	{"cycles_IO_WRM",	0x13},
	{"cycles_IO_RD",	0x14},
	{"cycles_WB_egress",	0x15},
	{"cycles_INV_egress",	0x16},
	{"cycles_RTO_egress",	0x17},
	{"cycles_RTD_egress",	0x18},
	{"cycles_RTS_egress",	0x19},
	{"cycles_no_WB",	0x1a},
	{"cycles_no_read/inv",	0x1b},
	{"cycles_HIT_M",	0x1c},
	{"cycles_HIT_O",	0x1d},
	{"cycles_HIT_S",	0x1e},
	{"cycles_WB_HIT",	0x1f},
	{"cycles_MISS",		0x20},
	{"cycles_READ_or_INV",	0x21},
	{"cycles_WB",		0x22},
	{"cycles_NDR",		0x23},
	{"cycles_cache_miss",	0x24},
	{"cycles_cache_hit",	0x25},
	{"cycles_CRC_errors",	0x26},
	{"cycles_replys_sent",	0x27},
	{"cycles_replys_recev",	0x28},
	{"cycles_link_retrain",	0x29},
	{"clear_pic",		0xff}
};

static ni_kev_mask_t
zam_gpd_perf_events[] = {
	{"none",		0x0},
	{"clock_cycles",	0x1},
	{"clear_pic",		0xf}
};

static ni_kev_mask_t
zam_asu_perf_events[] = {
	{"none",		0x0},
	{"clock_cycles",	0x1},
	{"asu_in_pck",		0x2},
	{"asu_out_pck",		0x3},
	{"asu_CAM_hit",		0x4},
	{"asu_wakeup",		0x5},
	{"clear_pic",		0xf}
};
#endif

#if defined(NIAGARA_IMPL)
/*
 * Niagara JBUS Performance Events
 */
static ni_kev_mask_t
niagara_jbus_events[] = {
	{"jbus_cycles",		0x1},
	{"dma_reads",		0x2},
	{"dma_read_latency",	0x3},
	{"dma_writes",		0x4},
	{"dma_write8",		0x5},
	{"ordering_waits",	0x6},
	{"pio_reads",		0x8},
	{"pio_read_latency",	0x9},
	{"aok_dok_off_cycles",	0xc},
	{"aok_off_cycles",	0xd},
	{"dok_off_cycles",	0xe},
	{"clear_pic",		0xf}
};
#endif

/*
 * Create the picN kstats for DRAM, JBUS and Zambezi events
 */
void
niagara_kstat_init()
{
	int i;
	ni_ksinfo_t *ksinfop;
	uint64_t pcr;
	uint64_t stat;

#ifdef DEBUG
	if (ni_perf_debug)
		printf("ni_kstat_init called\n");
#endif

	/*
	 * Create DRAM perf events kstat
	 */
	for (i = 0; i < NIAGARA_DRAM_BANKS; i++) {
#ifdef VFALLS_IMPL
		/* check if this dram instance is enabled in the HW */
		stat = hv_niagara_getperf(dram_perf_regs[i].pcr_reg, &pcr);
		if ((stat != H_EINVAL) && (stat != H_ENOTSUPPORTED)) {
#endif
			ksinfop = (ni_ksinfo_t *)kmem_zalloc(
			    sizeof (ni_ksinfo_t), KM_NOSLEEP);

			if (ksinfop == NULL) {
				cmn_err(CE_WARN,
				    "%s: no space for dram kstat\n",
				    cpu_module_name);
				break;
			}
			ksinfop->pic_no_evs =
			    sizeof (niagara_dram_events) /
			    sizeof (ni_kev_mask_t);
			ksinfop->pic_sel_shift[0] = NIAGARA_DRAM_PIC0_SEL_SHIFT;
			ksinfop->pic_shift[0] = NIAGARA_DRAM_PIC0_SHIFT;
			ksinfop->pic_mask[0] = NIAGARA_DRAM_PIC0_MASK;
			ksinfop->pic_sel_shift[1] = NIAGARA_DRAM_PIC1_SEL_SHIFT;
			ksinfop->pic_shift[1] = NIAGARA_DRAM_PIC1_SHIFT;
			ksinfop->pic_mask[1] = NIAGARA_DRAM_PIC1_MASK;
			ksinfop->pic_reg[0] = dram_perf_regs[i].pic_reg;
			ksinfop->pcr_reg = dram_perf_regs[i].pcr_reg;
			ni_dram_kstats[i] = ksinfop;

			/* create basic pic event/mask pair (only once) */
			if (i == 0)
				ni_create_name_kstat("dram", ksinfop,
				    niagara_dram_events);

			/* create counter kstats */
			ni_dram_kstats[i]->cntr_ksp = ni_create_cntr_kstat(
			    "dram", i, ni_cntr_kstat_update, ksinfop);
#ifdef VFALLS_IMPL
		}
#endif
	}

#ifdef VFALLS_IMPL
	/*
	 * Create Zambezi LPU perf events kstat
	 */
	for (i = 0; i < ZAMBEZI_LPU_COUNTERS; i++) {
		/* check if this Zambezi LPU instance is enabled in the HW */
		stat = hv_niagara_getperf(lpu_perf_regs[i].pcr_reg, &pcr);
		if ((stat != H_EINVAL) && (stat != H_ENOTSUPPORTED)) {
			ksinfop = (ni_ksinfo_t *)kmem_zalloc(
			    sizeof (ni_ksinfo_t), KM_NOSLEEP);

			if (ksinfop == NULL) {
				cmn_err(CE_WARN,
				    "%s: no space for zambezi lpu kstat\n",
				    cpu_module_name);
				break;
			}
			ksinfop->pic_no_evs =
			    sizeof (zam_lpu_perf_events) /
			    sizeof (ni_kev_mask_t);
			ksinfop->pic_sel_shift[0] = ZAMBEZI_PIC0_SEL_SHIFT;
			ksinfop->pic_reg[0] = lpu_perf_regs[i].pic_reg[0];
			ksinfop->pic_sel_shift[1] = ZAMBEZI_PIC1_SEL_SHIFT;
			ksinfop->pic_reg[1] = lpu_perf_regs[i].pic_reg[1];
			ksinfop->pcr_reg = lpu_perf_regs[i].pcr_reg;
			zam_lpu_kstats[i] = ksinfop;

			/* create basic pic event/mask pair (only once) */
			if (i == 0)
				ni_create_name_kstat("lpu", ksinfop,
				    zam_lpu_perf_events);

			/* create counter kstats */
			zam_lpu_kstats[i]->cntr_ksp = ni_create_cntr_kstat(
			    "lpu", i, zam_cntr_kstat_update, ksinfop);
		}
	}
	/*
	 * Create Zambezi GPD perf events kstat
	 */
	for (i = 0; i < ZAMBEZI_GPD_COUNTERS; i++) {
		/* check if this Zambezi GPD instance is enabled in the HW */
		stat = hv_niagara_getperf(gpd_perf_regs[i].pcr_reg, &pcr);
		if ((stat != H_EINVAL) && (stat != H_ENOTSUPPORTED)) {
			ksinfop = (ni_ksinfo_t *)kmem_zalloc(
			    sizeof (ni_ksinfo_t), KM_NOSLEEP);

			if (ksinfop == NULL) {
				cmn_err(CE_WARN,
				    "%s: no space for zambezi gpd kstat\n",
				    cpu_module_name);
				break;
			}
			ksinfop->pic_no_evs =
			    sizeof (zam_gpd_perf_events) /
			    sizeof (ni_kev_mask_t);
			ksinfop->pic_sel_shift[0] = ZAMBEZI_PIC0_SEL_SHIFT;
			ksinfop->pic_reg[0] = gpd_perf_regs[i].pic_reg[0];
			ksinfop->pic_sel_shift[1] = ZAMBEZI_PIC1_SEL_SHIFT;
			ksinfop->pic_reg[1] = gpd_perf_regs[i].pic_reg[1];
			ksinfop->pcr_reg = gpd_perf_regs[i].pcr_reg;
			zam_gpd_kstats[i] = ksinfop;

			/* create basic pic event/mask pair (only once) */
			if (i == 0)
				ni_create_name_kstat("gpd", ksinfop,
				    zam_gpd_perf_events);

			/* create counter kstats */
			zam_lpu_kstats[i]->cntr_ksp = ni_create_cntr_kstat(
			    "gpd", i, zam_cntr_kstat_update, ksinfop);
		}
	}
	/*
	 * Create Zambezi ASU perf events kstat
	 */
	for (i = 0; i < ZAMBEZI_ASU_COUNTERS; i++) {
		/* check if this Zambezi ASU instance is enabled in the HW */
		stat = hv_niagara_getperf(asu_perf_regs[i].pcr_reg, &pcr);
		if ((stat != H_EINVAL) && (stat != H_ENOTSUPPORTED)) {
			ksinfop = (ni_ksinfo_t *)kmem_zalloc(
			    sizeof (ni_ksinfo_t), KM_NOSLEEP);

			if (ksinfop == NULL) {
				cmn_err(CE_WARN,
				    "%s: no space for zambezi asu kstat\n",
				    cpu_module_name);
				break;
			}
			ksinfop->pic_no_evs =
			    sizeof (zam_asu_perf_events) /
			    sizeof (ni_kev_mask_t);
			ksinfop->pic_sel_shift[0] = ZAMBEZI_PIC0_SEL_SHIFT;
			ksinfop->pic_reg[0] = asu_perf_regs[i].pic_reg[0];
			ksinfop->pic_sel_shift[1] = ZAMBEZI_PIC1_SEL_SHIFT;
			ksinfop->pic_reg[1] = asu_perf_regs[i].pic_reg[1];
			ksinfop->pcr_reg = asu_perf_regs[i].pcr_reg;
			zam_asu_kstats[i] = ksinfop;

			/* create basic pic event/mask pair (only once) */
			if (i == 0)
				ni_create_name_kstat("asu", ksinfop,
				    zam_asu_perf_events);

			/* create counter kstats */
			zam_lpu_kstats[i]->cntr_ksp = ni_create_cntr_kstat(
			    "asu", i, zam_cntr_kstat_update, ksinfop);
		}
	}
#endif

#if defined(NIAGARA_IMPL)
	/*
	 * Create JBUS perf events kstat
	 */
	ni_jbus_kstat = (ni_ksinfo_t *)kmem_alloc(sizeof (ni_ksinfo_t),
	    KM_NOSLEEP);

	if (ni_jbus_kstat == NULL) {
		cmn_err(CE_WARN, "%s: no space for niagara jbus kstat\n",
		    cpu_module_name);
	} else {
		ni_jbus_kstat->pic_no_evs =
		    sizeof (niagara_jbus_events) / sizeof (ni_kev_mask_t);
		ni_jbus_kstat->pic_sel_shift[0] = NIAGARA_JBUS_PIC0_SEL_SHIFT;
		ni_jbus_kstat->pic_shift[0] = NIAGARA_JBUS_PIC0_SHIFT;
		ni_jbus_kstat->pic_mask[0] = NIAGARA_JBUS_PIC0_MASK;
		ni_jbus_kstat->pic_sel_shift[1] = NIAGARA_JBUS_PIC1_SEL_SHIFT;
		ni_jbus_kstat->pic_shift[1] = NIAGARA_JBUS_PIC1_SHIFT;
		ni_jbus_kstat->pic_mask[1] = NIAGARA_JBUS_PIC1_MASK;
		ni_jbus_kstat->pic_reg[0] = HV_NIAGARA_JBUS_COUNT;
		ni_jbus_kstat->pcr_reg = HV_NIAGARA_JBUS_CTL;
		ni_create_name_kstat("jbus", ni_jbus_kstat,
		    niagara_jbus_events);
		ni_jbus_kstat->cntr_ksp = ni_create_cntr_kstat("jbus", 0,
		    ni_cntr_kstat_update, ni_jbus_kstat);
	}
#endif
}

void
niagara_kstat_fini()
{
	int i;

#ifdef DEBUG
	if (ni_perf_debug)
		printf("ni_kstat_fini called\n");
#endif

	for (i = 0; i < NIAGARA_DRAM_BANKS; i++) {
		if (ni_dram_kstats[i] != NULL) {
			ni_delete_name_kstat(ni_dram_kstats[i]);
			if (ni_dram_kstats[i]->cntr_ksp != NULL)
				kstat_delete(ni_dram_kstats[i]->cntr_ksp);
			kmem_free(ni_dram_kstats[i], sizeof (ni_ksinfo_t));
			ni_dram_kstats[i] = NULL;
		}
	}

#if defined(VFALLS_IMPL)
	for (i = 0; i < ZAMBEZI_LPU_COUNTERS; i++) {
		if (zam_lpu_kstats[i] != NULL) {
			ni_delete_name_kstat(zam_lpu_kstats[i]);
			if (zam_lpu_kstats[i]->cntr_ksp != NULL)
				kstat_delete(zam_lpu_kstats[i]->cntr_ksp);
			kmem_free(zam_lpu_kstats[i], sizeof (ni_ksinfo_t));
			zam_lpu_kstats[i] = NULL;
		}
	}

	for (i = 0; i < ZAMBEZI_GPD_COUNTERS; i++) {
		if (zam_gpd_kstats[i] != NULL) {
			ni_delete_name_kstat(zam_gpd_kstats[i]);
			if (zam_gpd_kstats[i]->cntr_ksp != NULL)
				kstat_delete(zam_gpd_kstats[i]->cntr_ksp);
			kmem_free(zam_gpd_kstats[i], sizeof (ni_ksinfo_t));
			zam_gpd_kstats[i] = NULL;
		}
	}

	for (i = 0; i < ZAMBEZI_ASU_COUNTERS; i++) {
		if (zam_asu_kstats[i] != NULL) {
			ni_delete_name_kstat(zam_asu_kstats[i]);
			if (zam_asu_kstats[i]->cntr_ksp != NULL)
				kstat_delete(zam_asu_kstats[i]->cntr_ksp);
			kmem_free(zam_asu_kstats[i], sizeof (ni_ksinfo_t));
			zam_asu_kstats[i] = NULL;
		}
	}
#endif

#if defined(NIAGARA_IMPL)
	if (ni_jbus_kstat != NULL) {
		ni_delete_name_kstat(ni_jbus_kstat);
		if (ni_jbus_kstat->cntr_ksp != NULL)
			kstat_delete(ni_jbus_kstat->cntr_ksp);
		kmem_free(ni_jbus_kstat, sizeof (ni_ksinfo_t));
		ni_jbus_kstat = NULL;
	}
#endif
}

static void
ni_create_name_kstat(char *name, ni_ksinfo_t *pp, ni_kev_mask_t *ev)
{
	int	i;

#ifdef DEBUG
	if (ni_perf_debug > 1)
		printf("ni_create_name_kstat: name: %s\n", name);
#endif
	for (i = 0; i < NUM_OF_PICS; i++) {
		pp->pic_name_ksp[i] = ni_create_picN_kstat(name,
		    i, pp->pic_sel_shift[i], pp->pic_no_evs, ev);

		if (pp->pic_name_ksp[i] == NULL) {
			cmn_err(CE_WARN, "%s: unable to create name kstat",
			    cpu_module_name);
		}
	}
}

static void
ni_delete_name_kstat(ni_ksinfo_t *pp)
{
	int	i;

	if (pp != NULL) {
		for (i = 0; i < NUM_OF_PICS; i++) {
			if (pp->pic_name_ksp[i] != NULL)
				kstat_delete(pp->pic_name_ksp[i]);
		}
	}
}

/*
 * Create the picN kstat. Returns a pointer to the
 * kstat which the driver must store to allow it
 * to be deleted when necessary.
 */
static kstat_t *
ni_create_picN_kstat(char *mod_name, int pic, int pic_sel_shift,
	int num_ev, ni_kev_mask_t *ev_array)
{
	struct kstat_named *pic_named_data;
	int	inst = 0;
	int	event;
	char	pic_name[30];
	kstat_t	*picN_ksp = NULL;

	(void) sprintf(pic_name, "pic%d", pic);
	if ((picN_ksp = kstat_create(mod_name, inst, pic_name,
	    "bus", KSTAT_TYPE_NAMED, num_ev, NULL)) == NULL) {
		cmn_err(CE_WARN, "%s %s : kstat create failed",
		    mod_name, pic_name);

		/*
		 * It is up to the calling function to delete any kstats
		 * that may have been created already. We just
		 * return NULL to indicate an error has occured.
		 */
		return (NULL);
	}

	pic_named_data = (struct kstat_named *)
	    picN_ksp->ks_data;

	/*
	 * Write event names and their associated pcr masks. The
	 * last entry in the array (clear_pic) is added seperately
	 * below as the pic value must be inverted.
	 */
	for (event = 0; event < num_ev - 1; event++) {
		pic_named_data[event].value.ui64 =
		    (ev_array[event].pcr_mask << pic_sel_shift);

		kstat_named_init(&pic_named_data[event],
		    ev_array[event].event_name,
		    KSTAT_DATA_UINT64);
	}

	/*
	 * add the clear_pic entry.
	 */
	pic_named_data[event].value.ui64 =
	    (uint64_t)~(ev_array[event].pcr_mask << pic_sel_shift);

	kstat_named_init(&pic_named_data[event], ev_array[event].event_name,
	    KSTAT_DATA_UINT64);

	kstat_install(picN_ksp);

	return (picN_ksp);
}

/*
 * Create the "counters" kstat.
 */
static kstat_t *
ni_create_cntr_kstat(char *name, int instance, int (*update)(kstat_t *, int),
	void *ksinfop)
{
	struct kstat	*counters_ksp;
	struct kstat_named	*counters_named_data;
	char		pic_str[10];
	int		i;
	int		num_pics = NUM_OF_PICS;

#ifdef DEBUG
	if (ni_perf_debug > 1)
		printf("ni_create_cntr_kstat: name: %s instance: %d\n",
		    name, instance);
#endif

	/*
	 * Size of kstat is num_pics + 1 as it
	 * also contains the %pcr
	 */
	if ((counters_ksp = kstat_create(name, instance, "counters", "bus",
	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
		cmn_err(CE_WARN,
		    "%s: kstat_create for %s%d failed", cpu_module_name,
		    name, instance);
		return (NULL);
	}

	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);

	/*
	 * Iinitialize the named kstats
	 */
	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);

	for (i = 0; i < num_pics; i++) {
		(void) sprintf(pic_str, "pic%d", i);

		kstat_named_init(&counters_named_data[i+1], pic_str,
		    KSTAT_DATA_UINT64);
	}

	/*
	 * Store the register offset's in the kstat's
	 * private field so that they are available
	 * to the update function.
	 */
	counters_ksp->ks_private = (void *)ksinfop;
	counters_ksp->ks_update = update;

	kstat_install(counters_ksp);

	return (counters_ksp);
}

#if defined(VFALLS_IMPL)
/*
 * zambezi kstat update function. Handles reads/writes
 * from/to kstat.
 */
static int
zam_cntr_kstat_update(kstat_t *ksp, int rw)
{
	struct kstat_named	*data_p;
	ni_ksinfo_t	*ksinfop = ksp->ks_private;
	uint64_t	pic0, pic1, pcr;
	int		stat = 0;
	uint64_t	pic0_stat = 0, pic1_stat = 0, pcr_stat = 0;

	data_p = (struct kstat_named *)ksp->ks_data;

	if (rw == KSTAT_WRITE) {
#ifdef DEBUG
		if (ni_perf_debug)
			printf("zam_cntr_kstat_update: wr pcr-%d: %lx\n",
			    ksinfop->pcr_reg, data_p[0].value.ui64);
#endif
		if (hv_niagara_setperf(ksinfop->pcr_reg, data_p[0].value.ui64))
			stat = EACCES;
	} else {
		do {
			pic0_stat = hv_niagara_getperf(ksinfop->pic_reg[0],
			    &pic0);
		} while (pic0_stat == H_EWOULDBLOCK);
		do {
			pic1_stat = hv_niagara_getperf(ksinfop->pic_reg[1],
			    &pic1);
		} while (pic1_stat == H_EWOULDBLOCK);
		do {
			pcr_stat = hv_niagara_getperf(ksinfop->pcr_reg,
			    &pcr);
		} while (pcr_stat == H_EWOULDBLOCK);
		if (pic0_stat != 0 || pic1_stat != 0 || pcr_stat != 0)
			stat = EACCES;
		else {
			data_p[0].value.ui64 = pcr;
			data_p[1].value.ui64 = pic0;
			data_p[2].value.ui64 = pic1;
		}
#ifdef DEBUG
		if (ni_perf_debug)
			printf("zam_cntr_kstat_update: rd pcr%d: %lx  "
			    "pic0: %16lx pic1: %16lx\n",
			    ksinfop->pcr_reg, pcr,
			    data_p[1].value.ui64, data_p[2].value.ui64);
#endif
	}

	return (stat);
}
#endif

/*
 * kstat update function. Handles reads/writes
 * from/to kstat.
 */
static int
ni_cntr_kstat_update(kstat_t *ksp, int rw)
{
	struct kstat_named	*data_p;
	ni_ksinfo_t	*ksinfop = ksp->ks_private;
	uint64_t	pic, pcr;
	int		stat = 0;
	uint32_t	pic0, pic1;

	data_p = (struct kstat_named *)ksp->ks_data;

	if (rw == KSTAT_WRITE) {
#ifdef DEBUG
		if (ni_perf_debug)
			printf("ni_cntr_kstat_update: wr pcr-%d: %lx\n",
			    ksinfop->pcr_reg, data_p[0].value.ui64);
#endif
		if (hv_niagara_setperf(ksinfop->pcr_reg, data_p[0].value.ui64))
			stat = EACCES;
	} else {
		if (hv_niagara_getperf(ksinfop->pic_reg[0], &pic) != 0 ||
		    hv_niagara_getperf(ksinfop->pcr_reg, &pcr) != 0)
			stat = EACCES;
		else {

			data_p[0].value.ui64 = pcr;

			/*
			 * Generate a 32-bit PIC0 value by detecting overflow
			 */
			pic0 = (uint32_t)((pic >> ksinfop->pic_shift[0]) &
			    ksinfop->pic_mask[0]);
			if (pic0 < ksinfop->pic_last_val[0])
				ksinfop->pic_overflow[0]++;
			ksinfop->pic_last_val[0] = pic0;
			pic0 += (ksinfop->pic_overflow[0] & 1) << 31;
			data_p[1].value.ui64 = (uint64_t)pic0;

			/*
			 * Generate a 32-bit PIC1 value by detecting overflow
			 */
			pic1 = (uint32_t)((pic >> ksinfop->pic_shift[1]) &
			    ksinfop->pic_mask[1]);
			if (pic1 < ksinfop->pic_last_val[1])
				ksinfop->pic_overflow[1]++;
			ksinfop->pic_last_val[1] = pic1;
			pic1 += (ksinfop->pic_overflow[1] & 1) << 31;
			data_p[2].value.ui64 = (uint64_t)pic1;
		}
#ifdef DEBUG
		if (ni_perf_debug)
			printf("ni_cntr_kstat_update: rd pcr%d: %lx  "
			    "pic%d: %16lx pic0: %8lx pic1: %8lx\n",
			    ksinfop->pcr_reg, pcr, ksinfop->pic_reg[0], pic,
			    data_p[1].value.ui64, data_p[2].value.ui64);
#endif
	}
	return (stat);
}