xref: /onnv-gate/usr/src/uts/sun4u/sunfire/io/ac.c (revision 11311:639e7bc0b42f)
11341Sstevel /*
21341Sstevel  * CDDL HEADER START
31341Sstevel  *
41341Sstevel  * The contents of this file are subject to the terms of the
51341Sstevel  * Common Development and Distribution License (the "License").
61341Sstevel  * You may not use this file except in compliance with the License.
71341Sstevel  *
81341Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91341Sstevel  * or http://www.opensolaris.org/os/licensing.
101341Sstevel  * See the License for the specific language governing permissions
111341Sstevel  * and limitations under the License.
121341Sstevel  *
131341Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141341Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151341Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161341Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171341Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181341Sstevel  *
191341Sstevel  * CDDL HEADER END
201341Sstevel  */
211341Sstevel 
221341Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241341Sstevel  * Use is subject to license terms.
251341Sstevel  */
261341Sstevel 
271341Sstevel 
281341Sstevel #include <sys/types.h>
291341Sstevel #include <sys/conf.h>
301341Sstevel #include <sys/ddi.h>
311341Sstevel #include <sys/sunddi.h>
321341Sstevel #include <sys/ddi_impldefs.h>
331341Sstevel #include <sys/obpdefs.h>
341341Sstevel #include <sys/cmn_err.h>
351341Sstevel #include <sys/errno.h>
361341Sstevel #include <sys/kmem.h>
371341Sstevel #include <sys/debug.h>
381341Sstevel #include <sys/sysmacros.h>
391341Sstevel #include <sys/machsystm.h>
401341Sstevel #include <vm/hat_sfmmu.h>
411341Sstevel #include <sys/autoconf.h>
421341Sstevel #include <sys/open.h>
431341Sstevel #include <sys/stat.h>
441341Sstevel #include <sys/modctl.h>
451341Sstevel #include <sys/fhc.h>
461341Sstevel #include <sys/ac.h>
471341Sstevel #include <sys/cpu_module.h>
481341Sstevel #include <sys/x_call.h>
491341Sstevel #include <sys/fpu/fpusystm.h>
501341Sstevel #include <sys/lgrp.h>
511341Sstevel 
521341Sstevel /* Useful debugging Stuff */
531341Sstevel #include <sys/nexusdebug.h>
541341Sstevel 
551341Sstevel /*
561341Sstevel  * Function prototypes
571341Sstevel  */
581341Sstevel 
591341Sstevel static int ac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
601341Sstevel 		void **result);
611341Sstevel static int ac_attach(dev_info_t *, ddi_attach_cmd_t);
621341Sstevel static int ac_detach(dev_info_t *, ddi_detach_cmd_t);
631341Sstevel static int ac_open(dev_t *, int, int, cred_t *);
641341Sstevel static int ac_close(dev_t, int, int, cred_t *);
651341Sstevel static int ac_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
661341Sstevel 
671341Sstevel static void ac_add_kstats(struct ac_soft_state *);
681341Sstevel static void ac_del_kstats(struct ac_soft_state *);
691341Sstevel static int ac_misc_kstat_update(kstat_t *, int);
701341Sstevel static void ac_add_picN_kstats(dev_info_t *dip);
711341Sstevel static int ac_counters_kstat_update(kstat_t *, int);
721341Sstevel static void ac_get_memory_status(struct ac_soft_state *, enum ac_bank_id);
731341Sstevel static void ac_eval_memory_status(struct ac_soft_state *, enum ac_bank_id);
741341Sstevel static void ac_ecache_flush(uint64_t, uint64_t);
751341Sstevel static int ac_pkt_init(ac_cfga_pkt_t *pkt, intptr_t arg, int flag);
761341Sstevel static int ac_pkt_fini(ac_cfga_pkt_t *pkt, intptr_t arg, int flag);
771341Sstevel static int ac_reset_timeout(int rw);
781341Sstevel static void ac_timeout(void *);
791341Sstevel static int ac_enter_transition(void);
801341Sstevel static void ac_exit_transition(void);
811341Sstevel 
821341Sstevel 
831341Sstevel int ac_add_memory(ac_cfga_pkt_t *);
841341Sstevel int ac_del_memory(ac_cfga_pkt_t *);
851341Sstevel int ac_mem_stat(ac_cfga_pkt_t *, int);
861341Sstevel int ac_mem_test_start(ac_cfga_pkt_t *, int);
871341Sstevel int ac_mem_test_stop(ac_cfga_pkt_t *, int);
881341Sstevel int ac_mem_test_read(ac_cfga_pkt_t *, int);
891341Sstevel int ac_mem_test_write(ac_cfga_pkt_t *, int);
901341Sstevel void ac_mem_test_stop_on_close(uint_t, uint_t);
911341Sstevel /*
921341Sstevel  * ac audit message events
931341Sstevel  */
941341Sstevel typedef enum {
951341Sstevel 	AC_AUDIT_OSTATE_CONFIGURE,
961341Sstevel 	AC_AUDIT_OSTATE_UNCONFIGURE,
971341Sstevel 	AC_AUDIT_OSTATE_SUCCEEDED,
981341Sstevel 	AC_AUDIT_OSTATE_CONFIGURE_FAILED,
991341Sstevel 	AC_AUDIT_OSTATE_UNCONFIGURE_FAILED
1001341Sstevel } ac_audit_evt_t;
1011341Sstevel static void ac_policy_audit_messages(ac_audit_evt_t event, ac_cfga_pkt_t *pkt);
1021341Sstevel static char *ac_ostate_typestr(sysc_cfga_ostate_t ostate, ac_audit_evt_t event);
1031341Sstevel 
1041341Sstevel /* The memory ioctl interface version of this driver. */
1051341Sstevel static ac_mem_version_t ac_mem_version = AC_MEM_ADMIN_VERSION;
1061341Sstevel 
1071341Sstevel static int ac_mem_exercise(ac_cfga_pkt_t *, int);
1081341Sstevel 
1091341Sstevel /*
1101341Sstevel  * Configuration data structures
1111341Sstevel  */
1121341Sstevel static struct cb_ops ac_cb_ops = {
1131341Sstevel 	ac_open,			/* open */
1141341Sstevel 	ac_close,			/* close */
1151341Sstevel 	nulldev,			/* strategy */
1161341Sstevel 	nulldev,			/* print */
1171341Sstevel 	nodev,				/* dump */
1181341Sstevel 	nulldev,			/* read */
1191341Sstevel 	nulldev,			/* write */
1201341Sstevel 	ac_ioctl,			/* ioctl */
1211341Sstevel 	nodev,				/* devmap */
1221341Sstevel 	nodev,				/* mmap */
1231341Sstevel 	nodev,				/* segmap */
1241341Sstevel 	nochpoll,			/* poll */
1251341Sstevel 	ddi_prop_op,			/* cb_prop_op */
1261341Sstevel 	0,				/* streamtab */
1271341Sstevel 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
1281341Sstevel 	CB_REV,				/* rev */
1291341Sstevel 	nodev,				/* cb_aread */
1301341Sstevel 	nodev				/* cb_awrite */
1311341Sstevel };
1321341Sstevel 
1331341Sstevel static struct dev_ops ac_ops = {
1341341Sstevel 	DEVO_REV,			/* devo_rev, */
1351341Sstevel 	0,				/* refcnt */
1361341Sstevel 	ac_info,			/* getinfo */
1371341Sstevel 	nulldev,			/* identify */
1381341Sstevel 	nulldev,			/* probe */
1391341Sstevel 	ac_attach,			/* attach */
1401341Sstevel 	ac_detach,			/* detach */
1411341Sstevel 	nulldev,			/* reset */
1421341Sstevel 	&ac_cb_ops,			/* cb_ops */
1431341Sstevel 	(struct bus_ops *)0,		/* bus_ops */
1447656SSherry.Moore@Sun.COM 	nulldev,			/* power */
1457656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,			/* quiesce */
1461341Sstevel };
1471341Sstevel 
1481341Sstevel /*
1491341Sstevel  * Driver globals
1501341Sstevel  */
1511341Sstevel void *acp;				/* ac soft state hook */
1521341Sstevel static kstat_t	*ac_picN_ksp[AC_NUM_PICS];	/* performance picN kstats */
1531341Sstevel static int	ac_attachcnt = 0;	/* number of instances attached */
1541341Sstevel static kmutex_t ac_attachcnt_mutex;	/* ac_attachcnt lock - attach/detach */
1551341Sstevel static kmutex_t ac_hot_plug_mode_mutex;
1561341Sstevel static timeout_id_t	ac_hot_plug_timeout;
1571341Sstevel static int		ac_hot_plug_timeout_interval = 10;
1581341Sstevel 
1591341Sstevel #define	AC_GETSOFTC(I) \
1601341Sstevel 	((struct ac_soft_state *)ddi_get_soft_state(acp, (I)))
1611341Sstevel 
1621341Sstevel extern struct mod_ops mod_driverops;
1631341Sstevel 
1641341Sstevel static struct modldrv modldrv = {
1651341Sstevel 	&mod_driverops,		/* Type of module.  This one is a driver */
1667656SSherry.Moore@Sun.COM 	"AC Leaf",		/* name of module */
1671341Sstevel 	&ac_ops,		/* driver ops */
1681341Sstevel };
1691341Sstevel 
1701341Sstevel static struct modlinkage modlinkage = {
1711341Sstevel 	MODREV_1,
1721341Sstevel 	(void *)&modldrv,
1731341Sstevel 	NULL
1741341Sstevel };
1751341Sstevel 
1761341Sstevel #ifndef lint
1771366Spetede char _depends_on[] = "drv/fhc";
1781341Sstevel #endif	/* lint */
1791341Sstevel 
1801341Sstevel /*
1811341Sstevel  * These are the module initialization routines.
1821341Sstevel  */
1831341Sstevel 
1841341Sstevel int
_init(void)1851341Sstevel _init(void)
1861341Sstevel {
1871341Sstevel 	int error;
1881341Sstevel 
1891341Sstevel 	if ((error = ddi_soft_state_init(&acp, sizeof (struct ac_soft_state),
1901341Sstevel 	    1)) != 0)
1911341Sstevel 		return (error);
1921341Sstevel 
1931341Sstevel 	if ((error = mod_install(&modlinkage)) != 0) {
1941341Sstevel 		ddi_soft_state_fini(&acp);
1951341Sstevel 		return (error);
1961341Sstevel 	}
1971341Sstevel 	/* Initialize global mutex */
1981341Sstevel 	mutex_init(&ac_attachcnt_mutex, NULL, MUTEX_DRIVER, NULL);
1991341Sstevel 	mutex_init(&ac_hot_plug_mode_mutex, NULL, MUTEX_DRIVER, NULL);
2001341Sstevel 	return (0);
2011341Sstevel }
2021341Sstevel 
2031341Sstevel int
_fini(void)2041341Sstevel _fini(void)
2051341Sstevel {
2061341Sstevel 	int error;
2071341Sstevel 
2081341Sstevel 	if ((error = mod_remove(&modlinkage)) == 0) {
2091341Sstevel 		ddi_soft_state_fini(&acp);
2101341Sstevel 		mutex_destroy(&ac_attachcnt_mutex);
2111341Sstevel 		mutex_destroy(&ac_hot_plug_mode_mutex);
2121341Sstevel 	}
2131341Sstevel 	return (error);
2141341Sstevel }
2151341Sstevel 
2161341Sstevel int
_info(struct modinfo * modinfop)2171341Sstevel _info(struct modinfo *modinfop)
2181341Sstevel {
2191341Sstevel 	return (mod_info(&modlinkage, modinfop));
2201341Sstevel }
2211341Sstevel 
2221341Sstevel /* ARGSUSED */
2231341Sstevel static int
ac_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2241341Sstevel ac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2251341Sstevel {
2261341Sstevel 	dev_t	dev;
2271341Sstevel 	int	instance;
2281341Sstevel 
2291341Sstevel 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
2301341Sstevel 		dev = (dev_t)arg;
2311341Sstevel 		instance = AC_GETINSTANCE(getminor(dev));
2321341Sstevel 		*result = (void *)(uintptr_t)instance;
2331341Sstevel 		return (DDI_SUCCESS);
2341341Sstevel 	}
2351341Sstevel 	return (DDI_FAILURE);
2361341Sstevel }
2371341Sstevel 
2381341Sstevel static int
ac_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)2391341Sstevel ac_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2401341Sstevel {
2411341Sstevel 	int instance;
2421341Sstevel 	struct ac_soft_state *softsp;
2431341Sstevel 	struct bd_list *list = NULL;
2441341Sstevel 
2451341Sstevel 	switch (cmd) {
2461341Sstevel 	case DDI_ATTACH:
2471341Sstevel 		break;
2481341Sstevel 
2491341Sstevel 	case DDI_RESUME:
2501341Sstevel 		return (DDI_SUCCESS);
2511341Sstevel 
2521341Sstevel 	default:
2531341Sstevel 		return (DDI_FAILURE);
2541341Sstevel 	}
2551341Sstevel 
2561341Sstevel 	instance = ddi_get_instance(devi);
2571341Sstevel 
2581341Sstevel 	if (ddi_soft_state_zalloc(acp, instance) != DDI_SUCCESS) {
2591341Sstevel 		cmn_err(CE_WARN, "ddi_soft_state_zalloc failed for ac%d",
2607656SSherry.Moore@Sun.COM 		    instance);
2611341Sstevel 		return (DDI_FAILURE);
2621341Sstevel 	}
2631341Sstevel 
2641341Sstevel 	softsp = ddi_get_soft_state(acp, instance);
2651341Sstevel 
2661341Sstevel 	/* Set the dip in the soft state */
2671341Sstevel 	softsp->dip = devi;
2681341Sstevel 
2691341Sstevel 	/* Get the board number from this nodes parent */
2701341Sstevel 	softsp->pdip = ddi_get_parent(softsp->dip);
2711341Sstevel 	if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip,
2721341Sstevel 	    DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
2731341Sstevel 		cmn_err(CE_WARN, "ac%d: unable to retrieve %s property",
2747656SSherry.Moore@Sun.COM 		    instance, OBP_BOARDNUM);
2751341Sstevel 		goto bad;
2761341Sstevel 	}
2771341Sstevel 
2781341Sstevel 	DPRINTF(AC_ATTACH_DEBUG, ("ac%d: devi= 0x%p\n,"
279*11311SSurya.Prakki@Sun.COM 	    " softsp=0x%p\n", instance, (void *)devi, (void *)softsp));
2801341Sstevel 
2811341Sstevel 	/* map in the registers for this device. */
2821341Sstevel 	if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->ac_base, 0, 0)) {
2831341Sstevel 		cmn_err(CE_WARN, "ac%d: unable to map registers", instance);
2841341Sstevel 		goto bad;
2851341Sstevel 	}
2861341Sstevel 
2871341Sstevel 	/* Setup the pointers to the hardware registers */
2881341Sstevel 	softsp->ac_id = (uint32_t *)softsp->ac_base;
2891341Sstevel 	softsp->ac_memctl = (uint64_t *)((char *)softsp->ac_base +
2907656SSherry.Moore@Sun.COM 	    AC_OFF_MEMCTL);
2911341Sstevel 	softsp->ac_memdecode0 = (uint64_t *)((char *)softsp->ac_base +
2927656SSherry.Moore@Sun.COM 	    AC_OFF_MEMDEC0);
2931341Sstevel 	softsp->ac_memdecode1 = (uint64_t *)((char *)softsp->ac_base +
2947656SSherry.Moore@Sun.COM 	    AC_OFF_MEMDEC1);
2951341Sstevel 	softsp->ac_counter = (uint64_t *)((char *)softsp->ac_base +
2967656SSherry.Moore@Sun.COM 	    AC_OFF_CNTR);
2971341Sstevel 	softsp->ac_mccr = (uint32_t *)((char *)softsp->ac_base +
2987656SSherry.Moore@Sun.COM 	    AC_OFF_MCCR);
2991341Sstevel 
3001341Sstevel 	/* nothing to suspend/resume here */
3011341Sstevel 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
3027656SSherry.Moore@Sun.COM 	    "pm-hardware-state", "no-suspend-resume");
3031341Sstevel 
3041341Sstevel 	/* setup the the AC counter registers to allow for hotplug. */
3051341Sstevel 	list = fhc_bdlist_lock(softsp->board);
3061341Sstevel 
3071341Sstevel 	if (list == NULL) {
3081341Sstevel 		cmn_err(CE_PANIC, "ac%d: Board %d not found in database",
3097656SSherry.Moore@Sun.COM 		    instance, softsp->board);
3101341Sstevel 	}
3111341Sstevel 
3121341Sstevel 	/* set the AC rev into the bd list structure */
3131341Sstevel 	list->sc.ac_compid = *softsp->ac_id;
3141341Sstevel 
3151341Sstevel 	list->ac_softsp = softsp;
3161341Sstevel 
3171341Sstevel 	if (list->sc.type == CPU_BOARD || list->sc.type == MEM_BOARD) {
3181341Sstevel 		/* Create the minor nodes */
3191341Sstevel 		if (ddi_create_minor_node(devi, NAME_BANK0, S_IFCHR,
3201341Sstevel 		    (AC_PUTINSTANCE(instance) | 0),
3211341Sstevel 		    DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
3221341Sstevel 			cmn_err(CE_WARN, "ac%d: \"%s\" "
3231341Sstevel 			    "ddi_create_minor_node failed", instance,
3241341Sstevel 			    NAME_BANK0);
3251341Sstevel 		}
3261341Sstevel 		if (ddi_create_minor_node(devi, NAME_BANK1, S_IFCHR,
3271341Sstevel 		    (AC_PUTINSTANCE(instance) | 1),
3281341Sstevel 		    DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
3291341Sstevel 			cmn_err(CE_WARN, "ac%d: \"%s\" "
3301341Sstevel 			    "ddi_create_minor_node failed", instance,
3311341Sstevel 			    NAME_BANK0);
3321341Sstevel 		}
3331341Sstevel 
3341341Sstevel 		/* purge previous fhc pa database entries */
3351341Sstevel 		fhc_del_memloc(softsp->board);
3361341Sstevel 
3371341Sstevel 		/* Inherit Memory Bank Status */
3381341Sstevel 		ac_get_memory_status(softsp, Bank0);
3391341Sstevel 		ac_get_memory_status(softsp, Bank1);
3401341Sstevel 		/* Final Memory Bank Status evaluation and messaging */
3411341Sstevel 		ac_eval_memory_status(softsp, Bank0);
3421341Sstevel 		ac_eval_memory_status(softsp, Bank1);
3431341Sstevel 	}
3441341Sstevel 
3451341Sstevel 	fhc_bdlist_unlock();
3461341Sstevel 
3471341Sstevel 	/* create the kstats for this device. */
3481341Sstevel 	ac_add_kstats(softsp);
3491341Sstevel 
3501341Sstevel 	ddi_report_dev(devi);
3511341Sstevel 
3521341Sstevel 	return (DDI_SUCCESS);
3531341Sstevel 
3541341Sstevel bad:
3551341Sstevel 	ddi_soft_state_free(acp, instance);
3561341Sstevel 	return (DDI_FAILURE);
3571341Sstevel }
3581341Sstevel 
3591341Sstevel /* ARGSUSED */
3601341Sstevel static int
ac_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)3611341Sstevel ac_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3621341Sstevel {
3631341Sstevel 	int instance;
3641341Sstevel 	struct ac_soft_state *softsp;
3651341Sstevel 	struct bd_list *list;
3661341Sstevel 
3671341Sstevel 	/* get the instance of this devi */
3681341Sstevel 	instance = ddi_get_instance(devi);
3691341Sstevel 
3701341Sstevel 	/* get the soft state pointer for this device node */
3711341Sstevel 	softsp = ddi_get_soft_state(acp, instance);
3721341Sstevel 
3731341Sstevel 	switch (cmd) {
3741341Sstevel 	case DDI_SUSPEND:
3751341Sstevel 		return (DDI_SUCCESS);
3761341Sstevel 
3771341Sstevel 	case DDI_DETACH:
3781341Sstevel 		list = fhc_bdlist_lock(softsp->board);
3791341Sstevel 
3801341Sstevel 		if (fhc_bd_detachable(softsp->board))
3811341Sstevel 			break;
3821341Sstevel 		else
3831341Sstevel 			fhc_bdlist_unlock();
3841341Sstevel 		/* FALLTHROUGH */
3851341Sstevel 
3861341Sstevel 	default:
3871341Sstevel 		return (DDI_FAILURE);
3881341Sstevel 	}
3891341Sstevel 
3901341Sstevel 	ASSERT(list->ac_softsp == softsp);
3911341Sstevel 
3921341Sstevel 	if (list->sc.type == CPU_BOARD || list->sc.type == MEM_BOARD) {
3931341Sstevel 		int cpui;
3941341Sstevel 
3951341Sstevel 		/*
3961341Sstevel 		 * Test to see if memory is in use on a CPU/MEM board.
3971341Sstevel 		 * In the case of a DR operation this condition
3981341Sstevel 		 * will have been assured when the board was unconfigured.
3991341Sstevel 		 */
4001341Sstevel 		if (softsp->bank[Bank0].busy != 0 ||
4011341Sstevel 		    softsp->bank[Bank0].ostate == SYSC_CFGA_OSTATE_CONFIGURED ||
4021341Sstevel 		    softsp->bank[Bank1].busy != 0 ||
4031341Sstevel 		    softsp->bank[Bank1].ostate == SYSC_CFGA_OSTATE_CONFIGURED) {
4041341Sstevel 			fhc_bdlist_unlock();
4051341Sstevel 			return (DDI_FAILURE);
4061341Sstevel 		}
4071341Sstevel 		/*
4081341Sstevel 		 * CPU busy test is done by the DR sequencer before
4091341Sstevel 		 * device detach called.
4101341Sstevel 		 */
4111341Sstevel 
4121341Sstevel 		/*
4131341Sstevel 		 * Flush all E-caches to remove references to this
4141341Sstevel 		 * board's memory.
4151341Sstevel 		 *
4161341Sstevel 		 * Do this one CPU at a time to avoid stalls and timeouts
4171341Sstevel 		 * due to all CPUs flushing concurrently.
4181341Sstevel 		 * xc_one returns silently for non-existant CPUs.
4191341Sstevel 		 */
4201341Sstevel 		for (cpui = 0; cpui < NCPU; cpui++)
4211341Sstevel 			xc_one(cpui, ac_ecache_flush, 0, 0);
4221341Sstevel 	}
4231341Sstevel 
4241341Sstevel 	list->ac_softsp = NULL;
4251341Sstevel 
4261341Sstevel 	/* delete the kstat for this driver. */
4271341Sstevel 	ac_del_kstats(softsp);
4281341Sstevel 
4291341Sstevel 	/* unmap the registers */
4301341Sstevel 	ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->ac_base, 0, 0);
4311341Sstevel 
4321341Sstevel 	fhc_bdlist_unlock();
4331341Sstevel 
4341341Sstevel 	/* Remove the minor nodes. */
4351341Sstevel 	ddi_remove_minor_node(devi, NULL);
4361341Sstevel 
4371341Sstevel 	/* free the soft state structure */
4381341Sstevel 	ddi_soft_state_free(acp, instance);
4391341Sstevel 	ddi_prop_remove_all(devi);
4401341Sstevel 
4411341Sstevel 	return (DDI_SUCCESS);
4421341Sstevel }
4431341Sstevel 
4441341Sstevel /* ARGSUSED */
4451341Sstevel static int
ac_open(dev_t * devp,int flag,int otyp,cred_t * credp)4461341Sstevel ac_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4471341Sstevel {
4481341Sstevel 	int instance;
4491341Sstevel 	dev_t dev;
4501341Sstevel 	struct ac_soft_state *softsp;
4511341Sstevel 	struct bd_list *board;
4521341Sstevel 	int vis;
4531341Sstevel 
4541341Sstevel 	dev = *devp;
4551341Sstevel 	instance = AC_GETINSTANCE(getminor(dev));
4561341Sstevel 	softsp = AC_GETSOFTC(instance);
4571341Sstevel 
4581341Sstevel 	/* Is the instance attached? */
4591341Sstevel 	if (softsp == NULL) {
4601341Sstevel #ifdef DEBUG
4611341Sstevel 		cmn_err(CE_WARN, "ac%d device not attached", instance);
4621341Sstevel #endif /* DEBUG */
4631341Sstevel 		return (ENXIO);
4641341Sstevel 	}
4651341Sstevel 
4661341Sstevel 	/*
4671341Sstevel 	 * If the board is not configured, hide the memory APs
4681341Sstevel 	 */
4691341Sstevel 	board = fhc_bdlist_lock(softsp->board);
4701341Sstevel 	vis = (board != NULL) && MEM_BOARD_VISIBLE(board);
4711341Sstevel 	fhc_bdlist_unlock();
4721341Sstevel 
4731341Sstevel 	if (!vis)
4741341Sstevel 		return (ENXIO);
4751341Sstevel 
4761341Sstevel 	/* verify that otyp is appropriate */
4771341Sstevel 	if (otyp != OTYP_CHR) {
4781341Sstevel 		return (EINVAL);
4791341Sstevel 	}
4801341Sstevel 
4811341Sstevel 	return (DDI_SUCCESS);
4821341Sstevel }
4831341Sstevel 
4841341Sstevel /* ARGSUSED */
4851341Sstevel static int
ac_close(dev_t devt,int flag,int otyp,cred_t * credp)4861341Sstevel ac_close(dev_t devt, int flag, int otyp, cred_t *credp)
4871341Sstevel {
4881341Sstevel 	struct ac_soft_state *softsp;
4891341Sstevel 	int instance;
4901341Sstevel 
4911341Sstevel 	instance = AC_GETINSTANCE(getminor(devt));
4921341Sstevel 	softsp = AC_GETSOFTC(instance);
4931341Sstevel 	ASSERT(softsp != NULL);
4941341Sstevel 	ac_mem_test_stop_on_close(softsp->board, AC_GETBANK(getminor(devt)));
4951341Sstevel 	return (DDI_SUCCESS);
4961341Sstevel }
4971341Sstevel 
4981341Sstevel static int
ac_pkt_init(ac_cfga_pkt_t * pkt,intptr_t arg,int flag)4991341Sstevel ac_pkt_init(ac_cfga_pkt_t *pkt, intptr_t arg, int flag)
5001341Sstevel {
5011341Sstevel #ifdef _MULTI_DATAMODEL
5021341Sstevel 	if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
5031341Sstevel 		ac_cfga_cmd32_t ac_cmd32;
5041341Sstevel 
5051341Sstevel 		if (ddi_copyin((void *)arg, &ac_cmd32,
5067656SSherry.Moore@Sun.COM 		    sizeof (ac_cfga_cmd32_t), flag) != 0) {
5071341Sstevel 			return (EFAULT);
5081341Sstevel 		}
5091341Sstevel 		pkt->cmd_cfga.force = ac_cmd32.force;
5101341Sstevel 		pkt->cmd_cfga.test = ac_cmd32.test;
5111341Sstevel 		pkt->cmd_cfga.arg = ac_cmd32.arg;
5121341Sstevel 		pkt->cmd_cfga.errtype = ac_cmd32.errtype;
5131341Sstevel 		pkt->cmd_cfga.outputstr =
5147656SSherry.Moore@Sun.COM 		    (char *)(uintptr_t)ac_cmd32.outputstr;
5151341Sstevel 		pkt->cmd_cfga.private =
5167656SSherry.Moore@Sun.COM 		    (void *)(uintptr_t)ac_cmd32.private;
5171341Sstevel 	} else
5181341Sstevel #endif /* _MULTI_DATAMODEL */
5191341Sstevel 	if (ddi_copyin((void *)arg, &(pkt->cmd_cfga),
5207656SSherry.Moore@Sun.COM 	    sizeof (ac_cfga_cmd_t), flag) != 0) {
5211341Sstevel 		return (EFAULT);
5221341Sstevel 	}
5231341Sstevel 	pkt->errbuf = kmem_zalloc(SYSC_OUTPUT_LEN, KM_SLEEP);
5241341Sstevel 	return (0);
5251341Sstevel }
5261341Sstevel 
5271341Sstevel static int
ac_pkt_fini(ac_cfga_pkt_t * pkt,intptr_t arg,int flag)5281341Sstevel ac_pkt_fini(ac_cfga_pkt_t *pkt, intptr_t arg, int flag)
5291341Sstevel {
5301341Sstevel 	int ret = TRUE;
5311341Sstevel 
5321341Sstevel #ifdef _MULTI_DATAMODEL
5331341Sstevel 	if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
5341341Sstevel 
5351341Sstevel 		if (ddi_copyout(&(pkt->cmd_cfga.errtype),
5367656SSherry.Moore@Sun.COM 		    (void *)&(((ac_cfga_cmd32_t *)arg)->errtype),
5377656SSherry.Moore@Sun.COM 		    sizeof (ac_err_t), flag) != 0) {
5381341Sstevel 			ret = FALSE;
5391341Sstevel 		}
5401341Sstevel 	} else
5411341Sstevel #endif
5421341Sstevel 	if (ddi_copyout(&(pkt->cmd_cfga.errtype),
5437656SSherry.Moore@Sun.COM 	    (void *)&(((ac_cfga_cmd_t *)arg)->errtype),
5447656SSherry.Moore@Sun.COM 	    sizeof (ac_err_t), flag) != 0) {
5451341Sstevel 		ret = FALSE;
5461341Sstevel 	}
5471341Sstevel 
5481341Sstevel 	if ((ret != FALSE) && ((pkt->cmd_cfga.outputstr != NULL) &&
5497656SSherry.Moore@Sun.COM 	    (ddi_copyout(pkt->errbuf, pkt->cmd_cfga.outputstr,
5507656SSherry.Moore@Sun.COM 	    SYSC_OUTPUT_LEN, flag) != 0))) {
5511341Sstevel 			ret = FALSE;
5521341Sstevel 	}
5531341Sstevel 
5541341Sstevel 	kmem_free(pkt->errbuf, SYSC_OUTPUT_LEN);
5551341Sstevel 	return (ret);
5561341Sstevel }
5571341Sstevel 
5581341Sstevel /* ARGSUSED */
5591341Sstevel static int
ac_ioctl(dev_t devt,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)5601341Sstevel ac_ioctl(
5611341Sstevel 	dev_t devt,
5621341Sstevel 	int cmd,
5631341Sstevel 	intptr_t arg,
5641341Sstevel 	int flag,
5651341Sstevel 	cred_t *cred_p,
5661341Sstevel 	int *rval_p)
5671341Sstevel {
5681341Sstevel 	struct ac_soft_state *softsp;
5691341Sstevel 	ac_cfga_pkt_t cfga_pkt, *pkt;
5701341Sstevel 	int instance;
5711341Sstevel 	int retval;
5721341Sstevel 
5731341Sstevel 	instance = AC_GETINSTANCE(getminor(devt));
5741341Sstevel 	softsp = AC_GETSOFTC(instance);
5751341Sstevel 	if (softsp == NULL) {
5761341Sstevel #ifdef DEBUG
5771341Sstevel 		cmn_err(CE_NOTE, "ac%d device not attached", instance);
5781341Sstevel #endif /* DEBUG */
5791341Sstevel 		return (ENXIO);
5801341Sstevel 	}
5811341Sstevel 
5821341Sstevel 	/*
5831341Sstevel 	 * Dispose of the easy ones first.
5841341Sstevel 	 */
5851341Sstevel 	switch (cmd) {
5861341Sstevel 	case AC_MEM_ADMIN_VER:
5871341Sstevel 		/*
5881341Sstevel 		 * Specify the revision of this ioctl interface driver.
5891341Sstevel 		 */
5901341Sstevel 		if (ddi_copyout(&ac_mem_version, (void *)arg,
5911341Sstevel 		    sizeof (ac_mem_version_t), flag) != 0)
5921341Sstevel 			return (EFAULT);
5931341Sstevel 		return (DDI_SUCCESS);
5941341Sstevel 
5951341Sstevel 	case AC_MEM_CONFIGURE:
5961341Sstevel 	case AC_MEM_UNCONFIGURE:
5971341Sstevel 	case AC_MEM_STAT:
5981341Sstevel 	case AC_MEM_TEST_START:
5991341Sstevel 	case AC_MEM_TEST_STOP:
6001341Sstevel 	case AC_MEM_TEST_READ:
6011341Sstevel 	case AC_MEM_TEST_WRITE:
6021341Sstevel 	case AC_MEM_EXERCISE:
6031341Sstevel 		break;
6041341Sstevel 
6051341Sstevel 	default:
6061341Sstevel 		return (ENOTTY);
6071341Sstevel 	}
6081341Sstevel 	if (cmd != AC_MEM_STAT && !fpu_exists) {
6091341Sstevel 		return (ENOTSUP);
6101341Sstevel 	}
6111341Sstevel 
6121341Sstevel 	pkt = &cfga_pkt;
6131341Sstevel 	if ((retval = ac_pkt_init(pkt, arg, flag)) != 0)
6141341Sstevel 		return (retval);
6151341Sstevel 	pkt->softsp = softsp;
6161341Sstevel 	pkt->bank = AC_GETBANK(getminor(devt));
6171341Sstevel 
6181341Sstevel 	switch (cmd) {
6191341Sstevel 	case AC_MEM_CONFIGURE:
6201341Sstevel 		if ((flag & FWRITE) == 0) {
6211341Sstevel 			retval = EBADF;
6221341Sstevel 			break;
6231341Sstevel 		}
6241341Sstevel 
6251341Sstevel 		if (pkt->cmd_cfga.private != NULL) {
6261341Sstevel 			retval = EINVAL;
6271341Sstevel 			break;
6281341Sstevel 		}
6291341Sstevel 		ac_policy_audit_messages(AC_AUDIT_OSTATE_CONFIGURE, pkt);
6301341Sstevel 		retval = ac_add_memory(pkt);
6311341Sstevel 		if (!retval)
6321341Sstevel 			ac_policy_audit_messages(
6337656SSherry.Moore@Sun.COM 			    AC_AUDIT_OSTATE_SUCCEEDED, pkt);
6341341Sstevel 		else
6351341Sstevel 			ac_policy_audit_messages(
6367656SSherry.Moore@Sun.COM 			    AC_AUDIT_OSTATE_CONFIGURE_FAILED, pkt);
6371341Sstevel 		break;
6381341Sstevel 
6391341Sstevel 	case AC_MEM_UNCONFIGURE:
6401341Sstevel 		if ((flag & FWRITE) == 0) {
6411341Sstevel 			retval = EBADF;
6421341Sstevel 			break;
6431341Sstevel 		}
6441341Sstevel 
6451341Sstevel 		if (pkt->cmd_cfga.private != NULL) {
6461341Sstevel 			retval = EINVAL;
6471341Sstevel 			break;
6481341Sstevel 		}
6491341Sstevel 		ac_policy_audit_messages(AC_AUDIT_OSTATE_UNCONFIGURE, pkt);
6501341Sstevel 		retval = ac_del_memory(pkt);
6511341Sstevel 		if (!retval) {
6521341Sstevel 			ac_policy_audit_messages(
6537656SSherry.Moore@Sun.COM 			    AC_AUDIT_OSTATE_SUCCEEDED, pkt);
6541341Sstevel 		} else
6551341Sstevel 			ac_policy_audit_messages(
6567656SSherry.Moore@Sun.COM 			    AC_AUDIT_OSTATE_UNCONFIGURE_FAILED, pkt);
6571341Sstevel 		break;
6581341Sstevel 
6591341Sstevel 	case AC_MEM_STAT:
6601341Sstevel 		/*
6611341Sstevel 		 * Query usage of a bank of memory.
6621341Sstevel 		 */
6631341Sstevel 		retval = ac_mem_stat(pkt, flag);
6641341Sstevel 		break;
6651341Sstevel 
6661341Sstevel 	case AC_MEM_TEST_START:
6671341Sstevel 		if ((flag & FWRITE) == 0) {
6681341Sstevel 			retval = EBADF;
6691341Sstevel 			break;
6701341Sstevel 		}
6711341Sstevel 
6721341Sstevel 		retval = ac_mem_test_start(pkt, flag);
6731341Sstevel 		break;
6741341Sstevel 
6751341Sstevel 	case AC_MEM_TEST_STOP:
6761341Sstevel 		if ((flag & FWRITE) == 0) {
6771341Sstevel 			retval = EBADF;
6781341Sstevel 			break;
6791341Sstevel 		}
6801341Sstevel 
6811341Sstevel 		retval =  ac_mem_test_stop(pkt, flag);
6821341Sstevel 		break;
6831341Sstevel 
6841341Sstevel 	case AC_MEM_TEST_READ:
6851341Sstevel 		/*
6861341Sstevel 		 * read a 'page' (or less) of memory safely.
6871341Sstevel 		 */
6881341Sstevel 		if ((flag & FWRITE) == 0) {
6891341Sstevel 			retval = EBADF;
6901341Sstevel 			break;
6911341Sstevel 		}
6921341Sstevel 
6931341Sstevel 		retval = ac_mem_test_read(pkt, flag);
6941341Sstevel 		break;
6951341Sstevel 
6961341Sstevel 	case AC_MEM_TEST_WRITE:
6971341Sstevel 		/*
6981341Sstevel 		 * write a 'page' (or less) of memory safely.
6991341Sstevel 		 */
7001341Sstevel 		if ((flag & FWRITE) == 0) {
7011341Sstevel 			retval = EBADF;
7021341Sstevel 			break;
7031341Sstevel 		}
7041341Sstevel 
7051341Sstevel 		retval = ac_mem_test_write(pkt, flag);
7061341Sstevel 		break;
7071341Sstevel 
7081341Sstevel 	case AC_MEM_EXERCISE:
7091341Sstevel 		retval = ac_mem_exercise(pkt, flag);
7101341Sstevel 		break;
7111341Sstevel 
7121341Sstevel 	default:
7131341Sstevel 		ASSERT(0);
7141341Sstevel 		retval = ENOTTY;
7151341Sstevel 		break;
7161341Sstevel 	}
7171341Sstevel 
7181341Sstevel 	if (ac_pkt_fini(pkt, arg, flag) != TRUE)
7191341Sstevel 		retval = EFAULT;
7201341Sstevel 
7211341Sstevel 	return (retval);
7221341Sstevel }
7231341Sstevel 
7241341Sstevel static void
ac_add_kstats(struct ac_soft_state * softsp)7251341Sstevel ac_add_kstats(struct ac_soft_state *softsp)
7261341Sstevel {
7271341Sstevel 	struct kstat *ac_ksp, *ac_counters_ksp;
7281341Sstevel 	struct ac_kstat *ac_named_ksp;
7291341Sstevel 	struct kstat_named *ac_counters_named_data;
7301341Sstevel 
7311341Sstevel 	/*
7321341Sstevel 	 * create the unix-misc kstat for address controller
7331341Sstevel 	 * using the board number as the instance.
7341341Sstevel 	 */
7351341Sstevel 	if ((ac_ksp = kstat_create("unix", softsp->board,
7361341Sstevel 	    AC_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
7371341Sstevel 	    sizeof (struct ac_kstat) / sizeof (kstat_named_t),
7381341Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
7391341Sstevel 		cmn_err(CE_WARN, "ac%d: kstat_create failed",
7407656SSherry.Moore@Sun.COM 		    ddi_get_instance(softsp->dip));
7411341Sstevel 		return;
7421341Sstevel 	}
7431341Sstevel 
7441341Sstevel 	ac_named_ksp = (struct ac_kstat *)(ac_ksp->ks_data);
7451341Sstevel 
7461341Sstevel 	/* initialize the named kstats */
7471341Sstevel 	kstat_named_init(&ac_named_ksp->ac_memctl,
7487656SSherry.Moore@Sun.COM 	    MEMCTL_KSTAT_NAMED,
7497656SSherry.Moore@Sun.COM 	    KSTAT_DATA_UINT64);
7501341Sstevel 
7511341Sstevel 	kstat_named_init(&ac_named_ksp->ac_memdecode0,
7527656SSherry.Moore@Sun.COM 	    MEMDECODE0_KSTAT_NAMED,
7537656SSherry.Moore@Sun.COM 	    KSTAT_DATA_UINT64);
7541341Sstevel 
7551341Sstevel 	kstat_named_init(&ac_named_ksp->ac_memdecode1,
7567656SSherry.Moore@Sun.COM 	    MEMDECODE1_KSTAT_NAMED,
7577656SSherry.Moore@Sun.COM 	    KSTAT_DATA_UINT64);
7581341Sstevel 
7591341Sstevel 	kstat_named_init(&ac_named_ksp->ac_mccr,
7607656SSherry.Moore@Sun.COM 	    MCCR_KSTAT_NAMED,
7617656SSherry.Moore@Sun.COM 	    KSTAT_DATA_UINT32);
7621341Sstevel 
7631341Sstevel 	kstat_named_init(&ac_named_ksp->ac_counter,
7647656SSherry.Moore@Sun.COM 	    CNTR_KSTAT_NAMED,
7657656SSherry.Moore@Sun.COM 	    KSTAT_DATA_UINT64);
7661341Sstevel 
7671341Sstevel 	kstat_named_init(&ac_named_ksp->ac_bank0_status,
7687656SSherry.Moore@Sun.COM 	    BANK_0_KSTAT_NAMED,
7697656SSherry.Moore@Sun.COM 	    KSTAT_DATA_CHAR);
7701341Sstevel 
7711341Sstevel 	kstat_named_init(&ac_named_ksp->ac_bank1_status,
7727656SSherry.Moore@Sun.COM 	    BANK_1_KSTAT_NAMED,
7737656SSherry.Moore@Sun.COM 	    KSTAT_DATA_CHAR);
7741341Sstevel 
7751341Sstevel 	ac_ksp->ks_update = ac_misc_kstat_update;
7761341Sstevel 	ac_ksp->ks_private = (void *)softsp;
7771341Sstevel 	softsp->ac_ksp = ac_ksp;
7781341Sstevel 	kstat_install(ac_ksp);
7791341Sstevel 
7801341Sstevel 	/*
7811341Sstevel 	 * Create the picN kstats if we are the first instance
7821341Sstevel 	 * to attach. We use ac_attachcnt as a count of how
7831341Sstevel 	 * many instances have attached. This is protected by
7841341Sstevel 	 * a mutex.
7851341Sstevel 	 */
7861341Sstevel 	mutex_enter(&ac_attachcnt_mutex);
7871341Sstevel 	if (ac_attachcnt == 0)
7881341Sstevel 		ac_add_picN_kstats(softsp->dip);
7891341Sstevel 
7901341Sstevel 	ac_attachcnt ++;
7911341Sstevel 	mutex_exit(&ac_attachcnt_mutex);
7921341Sstevel 
7931341Sstevel 	/*
7941341Sstevel 	 * Create the "counter" kstat for each AC instance.
7951341Sstevel 	 * This provides access to the %pcr and %pic
7961341Sstevel 	 * registers for that instance.
7971341Sstevel 	 *
7981341Sstevel 	 * The size of this kstat is AC_NUM_PICS + 1 for %pcr
7991341Sstevel 	 */
8001341Sstevel 	if ((ac_counters_ksp = kstat_create("ac",
8017656SSherry.Moore@Sun.COM 	    ddi_get_instance(softsp->dip), "counters",
8027656SSherry.Moore@Sun.COM 	    "bus", KSTAT_TYPE_NAMED, AC_NUM_PICS + 1,
8037656SSherry.Moore@Sun.COM 	    KSTAT_FLAG_WRITABLE)) == NULL) {
8041341Sstevel 
8051341Sstevel 		cmn_err(CE_WARN, "ac%d counters: kstat_create failed",
8067656SSherry.Moore@Sun.COM 		    ddi_get_instance(softsp->dip));
8071341Sstevel 		return;
8081341Sstevel 	}
8091341Sstevel 	ac_counters_named_data =
8107656SSherry.Moore@Sun.COM 	    (struct kstat_named *)(ac_counters_ksp->ks_data);
8111341Sstevel 
8121341Sstevel 	/* initialize the named kstats */
8131341Sstevel 	kstat_named_init(&ac_counters_named_data[0],
8147656SSherry.Moore@Sun.COM 	    "pcr", KSTAT_DATA_UINT64);
8151341Sstevel 
8161341Sstevel 	kstat_named_init(&ac_counters_named_data[1],
8177656SSherry.Moore@Sun.COM 	    "pic0", KSTAT_DATA_UINT64);
8181341Sstevel 
8191341Sstevel 	kstat_named_init(&ac_counters_named_data[2],
8207656SSherry.Moore@Sun.COM 	    "pic1", KSTAT_DATA_UINT64);
8211341Sstevel 
8221341Sstevel 	ac_counters_ksp->ks_update = ac_counters_kstat_update;
8231341Sstevel 	ac_counters_ksp->ks_private = (void *)softsp;
8241341Sstevel 	kstat_install(ac_counters_ksp);
8251341Sstevel 
8261341Sstevel 	/* update the sofstate */
8271341Sstevel 	softsp->ac_counters_ksp = ac_counters_ksp;
8281341Sstevel }
8291341Sstevel 
8301341Sstevel /*
8311341Sstevel  * called from ac_add_kstats() to create a kstat for each %pic
8321341Sstevel  * that the AC supports. These (read-only) kstats export the
8331341Sstevel  * event names and %pcr masks that each %pic supports.
8341341Sstevel  *
8351341Sstevel  * if we fail to create any of these kstats we must remove any
8361341Sstevel  * that we have already created and return;
8371341Sstevel  *
8381341Sstevel  * NOTE: because all AC's use the same events we only need to
8391341Sstevel  *       create the picN kstats once. All instances can use
8401341Sstevel  *       the same picN kstats.
8411341Sstevel  *
8421341Sstevel  *       The flexibility exists to allow each device specify it's
8431341Sstevel  *       own events by creating picN kstats with the instance number
8441341Sstevel  *       set to ddi_get_instance(softsp->dip).
8451341Sstevel  *
8461341Sstevel  *       When searching for a picN kstat for a device you should
8471341Sstevel  *       first search for a picN kstat using the instance number
8481341Sstevel  *       of the device you are interested in. If that fails you
8491341Sstevel  *       should use the first picN kstat found for that device.
8501341Sstevel  */
8511341Sstevel static void
ac_add_picN_kstats(dev_info_t * dip)8521341Sstevel ac_add_picN_kstats(dev_info_t *dip)
8531341Sstevel {
8541341Sstevel 	typedef struct ac_event_mask {
8551341Sstevel 		char *event_name;
8561341Sstevel 		uint64_t pcr_mask;
8571341Sstevel 	} ac_event_mask_t;
8581341Sstevel 
8591341Sstevel 	/*
8601341Sstevel 	 * AC Performance Events.
8611341Sstevel 	 *
8621341Sstevel 	 * We declare an array of event-names and event-masks.
8631341Sstevel 	 */
8641341Sstevel 	ac_event_mask_t ac_events_arr[] = {
8651341Sstevel 		{"mem_bank0_rds", 0x1}, {"mem_bank0_wrs", 0x2},
8661341Sstevel 		{"mem_bank0_stall", 0x3}, {"mem_bank1_rds", 0x4},
8671341Sstevel 		{"mem_bank1_wrs", 0x5}, {"mem_bank1_stall", 0x6},
8681341Sstevel 		{"clock_cycles", 0x7}, {"addr_pkts", 0x8},
8691341Sstevel 		{"data_pkts", 0x9}, {"flow_ctl_cyc", 0xa},
8701341Sstevel 		{"fast_arb_pkts", 0xb}, {"bus_cont_cyc", 0xc},
8711341Sstevel 		{"data_bus_can", 0xd}, {"ac_addr_pkts", 0xe},
8721341Sstevel 		{"ac_data_pkts", 0xf}, {"rts_pkts", 0x10},
8731341Sstevel 		{"rtsa_pkts", 0x11}, {"rto_pkts", 0x12},
8741341Sstevel 		{"rs_pkts", 0x13}, {"wb_pkts", 0x14},
8751341Sstevel 		{"ws_pkts", 0x15}, {"rio_pkts", 0x16},
8761341Sstevel 		{"rbio_pkts", 0x17}, {"wio_pkts", 0x18},
8771341Sstevel 		{"wbio_pkts", 0x19}, {"upa_a_rds_m", 0x1a},
8781341Sstevel 		{"upa_a_rdo_v", 0x1b}, {"upa_b_rds_m", 0x1c},
8791341Sstevel 		{"upa_b_rdo_v", 0x1d}, {"upa_a_preqs_fr", 0x20},
8801341Sstevel 		{"upa_a_sreqs_to", 0x21}, {"upa_a_preqs_to", 0x22},
8811341Sstevel 		{"upa_a_rds_fr", 0x23}, {"upa_a_rdsa_fr", 0x24},
8821341Sstevel 		{"upa_a_rdo_fr", 0x25}, {"upa_a_rdd_fr", 0x26},
8831341Sstevel 		{"upa_a_rio_rbio", 0x27}, {"upa_a_wio_wbio", 0x28},
8841341Sstevel 		{"upa_a_cpb_to", 0x29}, {"upa_a_inv_to", 0x2a},
8851341Sstevel 		{"upa_a_hits_buff", 0x2b}, {"upa_a_wb", 0x2c},
8861341Sstevel 		{"upa_a_wi", 0x2d}, {"upa_b_preqs_fr", 0x30},
8871341Sstevel 		{"upa_b_sreqs_to", 0x31}, {"upa_b_preqs_to", 0x32},
8881341Sstevel 		{"upa_b_rds_fr", 0x33}, {"upa_b_rdsa_fr", 0x34},
8891341Sstevel 		{"upa_b_rdo_fr", 0x35}, {"upa_b_rdd_fr", 0x36},
8901341Sstevel 		{"upa_b_rio_rbio", 0x37}, {"upa_b_wio_wbio", 0x38},
8911341Sstevel 		{"upa_b_cpb_to", 0x39}, {"upa_b_inv_to", 0x3a},
8921341Sstevel 		{"upa_b_hits_buff", 0x3b}, {"upa_b_wb", 0x3c},
8931341Sstevel 		{"upa_b_wi", 0x3d}
8941341Sstevel 	};
8951341Sstevel 
8961341Sstevel #define	AC_NUM_EVENTS sizeof (ac_events_arr) / sizeof (ac_events_arr[0])
8971341Sstevel 
8981341Sstevel 	/*
8991341Sstevel 	 * array of clear masks for each pic.
9001341Sstevel 	 * These masks are used to clear the %pcr bits for
9011341Sstevel 	 * each pic.
9021341Sstevel 	 */
9031341Sstevel 	ac_event_mask_t ac_clear_pic[AC_NUM_PICS] = {
9041341Sstevel 		/* pic0 */
9051341Sstevel 		{"clear_pic", (uint64_t)~(0x3f)},
9061341Sstevel 		/* pic1 */
9071341Sstevel 		{"clear_pic", (uint64_t)~(0x3f << 8)}
9081341Sstevel 	};
9091341Sstevel 
9101341Sstevel 	struct kstat_named *ac_pic_named_data;
9111341Sstevel 	int  		event, pic;
9121341Sstevel 	char 		pic_name[30];
9131341Sstevel 	int		instance = ddi_get_instance(dip);
9141341Sstevel 	int		pic_shift = 0;
9151341Sstevel 
9161341Sstevel 	for (pic = 0; pic < AC_NUM_PICS; pic++) {
9171341Sstevel 		/*
9181341Sstevel 		 * create the picN kstat. The size of this kstat is
9191341Sstevel 		 * AC_NUM_EVENTS + 1 for the clear_event_mask
9201341Sstevel 		 */
9211341Sstevel 		(void) sprintf(pic_name, "pic%d", pic);	/* pic0, pic1 ... */
9221341Sstevel 		if ((ac_picN_ksp[pic] = kstat_create("ac",
9237656SSherry.Moore@Sun.COM 		    instance, pic_name, "bus", KSTAT_TYPE_NAMED,
9247656SSherry.Moore@Sun.COM 		    AC_NUM_EVENTS + 1, NULL)) == NULL) {
9251341Sstevel 
9261341Sstevel 				cmn_err(CE_WARN, "ac %s: kstat_create failed",
9277656SSherry.Moore@Sun.COM 				    pic_name);
9281341Sstevel 
9291341Sstevel 				/* remove pic0 kstat if pic1 create fails */
9301341Sstevel 				if (pic == 1) {
9311341Sstevel 					kstat_delete(ac_picN_ksp[0]);
9321341Sstevel 					ac_picN_ksp[0] = NULL;
9331341Sstevel 				}
9341341Sstevel 				return;
9351341Sstevel 		}
9361341Sstevel 		ac_pic_named_data =
9377656SSherry.Moore@Sun.COM 		    (struct kstat_named *)(ac_picN_ksp[pic]->ks_data);
9381341Sstevel 
9391341Sstevel 		/*
9401341Sstevel 		 * when we are storing pcr_masks we need to shift bits
9411341Sstevel 		 * left by 8 for pic1 events.
9421341Sstevel 		 */
9431341Sstevel 		if (pic == 1)
9441341Sstevel 			pic_shift = 8;
9451341Sstevel 
9461341Sstevel 		/*
9471341Sstevel 		 * for each picN event we need to write a kstat record
9481341Sstevel 		 * (name = EVENT, value.ui64 = PCR_MASK)
9491341Sstevel 		 */
9501341Sstevel 		for (event = 0; event < AC_NUM_EVENTS; event ++) {
9511341Sstevel 
9521341Sstevel 			/* pcr_mask */
9531341Sstevel 			ac_pic_named_data[event].value.ui64 =
9547656SSherry.Moore@Sun.COM 			    ac_events_arr[event].pcr_mask << pic_shift;
9551341Sstevel 
9561341Sstevel 			/* event-name */
9571341Sstevel 			kstat_named_init(&ac_pic_named_data[event],
9587656SSherry.Moore@Sun.COM 			    ac_events_arr[event].event_name,
9597656SSherry.Moore@Sun.COM 			    KSTAT_DATA_UINT64);
9601341Sstevel 		}
9611341Sstevel 
9621341Sstevel 		/*
9631341Sstevel 		 * we add the clear_pic event and mask as the last
9641341Sstevel 		 * record in the kstat
9651341Sstevel 		 */
9661341Sstevel 		/* pcr mask */
9671341Sstevel 		ac_pic_named_data[AC_NUM_EVENTS].value.ui64 =
9687656SSherry.Moore@Sun.COM 		    ac_clear_pic[pic].pcr_mask;
9691341Sstevel 
9701341Sstevel 		/* event-name */
9711341Sstevel 		kstat_named_init(&ac_pic_named_data[AC_NUM_EVENTS],
9727656SSherry.Moore@Sun.COM 		    ac_clear_pic[pic].event_name,
9737656SSherry.Moore@Sun.COM 		    KSTAT_DATA_UINT64);
9741341Sstevel 
9751341Sstevel 		kstat_install(ac_picN_ksp[pic]);
9761341Sstevel 	}
9771341Sstevel }
9781341Sstevel 
9791341Sstevel 
9801341Sstevel static void
ac_del_kstats(struct ac_soft_state * softsp)9811341Sstevel ac_del_kstats(struct ac_soft_state *softsp)
9821341Sstevel {
9831341Sstevel 	struct kstat *ac_ksp;
9841341Sstevel 	int pic;
9851341Sstevel 
9861341Sstevel 	/* remove "misc" kstat */
9871341Sstevel 	ac_ksp = softsp->ac_ksp;
9881341Sstevel 	softsp->ac_ksp = NULL;
9891341Sstevel 	if (ac_ksp != NULL) {
9901341Sstevel 		ASSERT(ac_ksp->ks_private == (void *)softsp);
9911341Sstevel 		kstat_delete(ac_ksp);
9921341Sstevel 	}
9931341Sstevel 
9941341Sstevel 	/* remove "bus" kstat */
9951341Sstevel 	ac_ksp = softsp->ac_counters_ksp;
9961341Sstevel 	softsp->ac_counters_ksp = NULL;
9971341Sstevel 	if (ac_ksp != NULL) {
9981341Sstevel 		ASSERT(ac_ksp->ks_private == (void *)softsp);
9991341Sstevel 		kstat_delete(ac_ksp);
10001341Sstevel 	}
10011341Sstevel 
10021341Sstevel 	/*
10031341Sstevel 	 * if we are the last instance to detach we need to
10041341Sstevel 	 * remove the picN kstats. We use ac_attachcnt as a
10051341Sstevel 	 * count of how many instances are still attached. This
10061341Sstevel 	 * is protected by a mutex.
10071341Sstevel 	 */
10081341Sstevel 	mutex_enter(&ac_attachcnt_mutex);
10091341Sstevel 	ac_attachcnt --;
10101341Sstevel 	if (ac_attachcnt == 0) {
10111341Sstevel 		for (pic = 0; pic < AC_NUM_PICS; pic++) {
10121341Sstevel 			if (ac_picN_ksp[pic] != (kstat_t *)NULL) {
10131341Sstevel 				kstat_delete(ac_picN_ksp[pic]);
10141341Sstevel 				ac_picN_ksp[pic] = NULL;
10151341Sstevel 			}
10161341Sstevel 		}
10171341Sstevel 	}
10181341Sstevel 	mutex_exit(&ac_attachcnt_mutex);
10191341Sstevel }
10201341Sstevel 
10211341Sstevel static enum ac_bank_status
ac_kstat_stat(sysc_cfga_rstate_t rst,sysc_cfga_ostate_t ost)10221341Sstevel ac_kstat_stat(sysc_cfga_rstate_t rst, sysc_cfga_ostate_t ost)
10231341Sstevel {
10241341Sstevel 	switch (rst) {
10251341Sstevel 	case SYSC_CFGA_RSTATE_EMPTY:
10261341Sstevel 		return (StNoMem);
10271341Sstevel 	case SYSC_CFGA_RSTATE_DISCONNECTED:
10281341Sstevel 		return (StBad);
10291341Sstevel 	case SYSC_CFGA_RSTATE_CONNECTED:
10301341Sstevel 		switch (ost) {
10311341Sstevel 		case SYSC_CFGA_OSTATE_UNCONFIGURED:
10321341Sstevel 			return (StSpare);
10331341Sstevel 		case SYSC_CFGA_OSTATE_CONFIGURED:
10341341Sstevel 			return (StActive);
10351341Sstevel 		default:
10361341Sstevel 			return (StUnknown);
10371341Sstevel 		}
10381341Sstevel 	default:
10391341Sstevel 		return (StUnknown);
10401341Sstevel 	}
10411341Sstevel }
10421341Sstevel 
10431341Sstevel static enum ac_bank_condition
ac_kstat_cond(sysc_cfga_cond_t cond)10441341Sstevel ac_kstat_cond(sysc_cfga_cond_t cond)
10451341Sstevel {
10461341Sstevel 	switch (cond) {
10471341Sstevel 	case SYSC_CFGA_COND_UNKNOWN:
10481341Sstevel 		return (ConUnknown);
10491341Sstevel 	case SYSC_CFGA_COND_OK:
10501341Sstevel 		return (ConOK);
10511341Sstevel 	case SYSC_CFGA_COND_FAILING:
10521341Sstevel 		return (ConFailing);
10531341Sstevel 	case SYSC_CFGA_COND_FAILED:
10541341Sstevel 		return (ConFailed);
10551341Sstevel 	case SYSC_CFGA_COND_UNUSABLE:
10561341Sstevel 		return (ConBad);
10571341Sstevel 	default:
10581341Sstevel 		return (ConUnknown);
10591341Sstevel 	}
10601341Sstevel }
10611341Sstevel 
10621341Sstevel static int
ac_misc_kstat_update(kstat_t * ksp,int rw)10631341Sstevel ac_misc_kstat_update(kstat_t *ksp, int rw)
10641341Sstevel {
10651341Sstevel 	struct ac_kstat *acksp;
10661341Sstevel 	struct ac_soft_state *softsp;
10671341Sstevel 
10681341Sstevel 	acksp = (struct ac_kstat *)ksp->ks_data;
10691341Sstevel 	softsp = (struct ac_soft_state *)ksp->ks_private;
10701341Sstevel 	/* Need the NULL check in case kstat is about to be deleted. */
10711341Sstevel 	ASSERT(softsp->ac_ksp == NULL || ksp == softsp->ac_ksp);
10721341Sstevel 
10731341Sstevel 	/* this is a read-only kstat. Bail out on a write */
10741341Sstevel 	if (rw == KSTAT_WRITE) {
10751341Sstevel 		return (EACCES);
10761341Sstevel 	} else {
10771341Sstevel 		/*
10781341Sstevel 		 * copy the current state of the hardware into the
10791341Sstevel 		 * kstat structure.
10801341Sstevel 		 */
10811341Sstevel 		acksp->ac_memctl.value.ui64 = *softsp->ac_memctl;
10821341Sstevel 		acksp->ac_memdecode0.value.ui64 = *softsp->ac_memdecode0;
10831341Sstevel 		acksp->ac_memdecode1.value.ui64 = *softsp->ac_memdecode1;
10841341Sstevel 		acksp->ac_mccr.value.ui32 = *softsp->ac_mccr;
10851341Sstevel 		acksp->ac_counter.value.ui64 = *softsp->ac_counter;
10861341Sstevel 		acksp->ac_bank0_status.value.c[0] =
10871341Sstevel 		    ac_kstat_stat(softsp->bank[0].rstate,
10881341Sstevel 		    softsp->bank[0].ostate);
10891341Sstevel 		acksp->ac_bank0_status.value.c[1] =
10901341Sstevel 		    ac_kstat_cond(softsp->bank[0].condition);
10911341Sstevel 		acksp->ac_bank1_status.value.c[0] =
10921341Sstevel 		    ac_kstat_stat(softsp->bank[1].rstate,
10931341Sstevel 		    softsp->bank[1].ostate);
10941341Sstevel 		acksp->ac_bank1_status.value.c[1] =
10951341Sstevel 		    ac_kstat_cond(softsp->bank[1].condition);
10961341Sstevel 	}
10971341Sstevel 	return (0);
10981341Sstevel }
10991341Sstevel 
11001341Sstevel static int
ac_counters_kstat_update(kstat_t * ksp,int rw)11011341Sstevel ac_counters_kstat_update(kstat_t *ksp, int rw)
11021341Sstevel {
11031341Sstevel 	struct kstat_named *ac_counters_data;
11041341Sstevel 	struct ac_soft_state *softsp;
11051341Sstevel 	uint64_t pic_register;
11061341Sstevel 
11071341Sstevel 	ac_counters_data = (struct kstat_named *)ksp->ks_data;
11081341Sstevel 	softsp = (struct ac_soft_state *)ksp->ks_private;
11091341Sstevel 
11101341Sstevel 	/*
11111341Sstevel 	 * We need to start/restart the ac_timeout that will
11121341Sstevel 	 * return the AC counters to hot-plug mode after the
11131341Sstevel 	 * ac_hot_plug_timeout_interval has expired. We tell
11141341Sstevel 	 * ac_reset_timeout() whether this is a kstat_read or a
11151341Sstevel 	 * kstat_write call. If this fails we reject the kstat
11161341Sstevel 	 * operation.
11171341Sstevel 	 */
11181341Sstevel 	if (ac_reset_timeout(rw) != 0)
11191341Sstevel 		return (-1);
11201341Sstevel 
11211341Sstevel 
11221341Sstevel 	if (rw == KSTAT_WRITE) {
11231341Sstevel 		/*
11241341Sstevel 		 * Write the %pcr value to the softsp->ac_mccr.
11251341Sstevel 		 * This interface does not support writing to the
11261341Sstevel 		 * %pic.
11271341Sstevel 		 */
11281341Sstevel 		*softsp->ac_mccr =
11297656SSherry.Moore@Sun.COM 		    (uint32_t)ac_counters_data[0].value.ui64;
11301341Sstevel 	} else {
11311341Sstevel 		/*
11321341Sstevel 		 * Read %pcr and %pic register values and write them
11331341Sstevel 		 * into counters kstat.
11341341Sstevel 		 */
11351341Sstevel 
11361341Sstevel 		/* pcr */
11371341Sstevel 		ac_counters_data[0].value.ui64 = *softsp->ac_mccr;
11381341Sstevel 
11391341Sstevel 		pic_register = *softsp->ac_counter;
11401341Sstevel 		/*
11411341Sstevel 		 * ac pic register:
11421341Sstevel 		 *  (63:32) = pic1
11431341Sstevel 		 *  (31:00) = pic0
11441341Sstevel 		 */
11451341Sstevel 
11461341Sstevel 		/* pic0 */
11471341Sstevel 		ac_counters_data[1].value.ui64 =
11487656SSherry.Moore@Sun.COM 		    AC_COUNTER_TO_PIC0(pic_register);
11491341Sstevel 		/* pic1 */
11501341Sstevel 		ac_counters_data[2].value.ui64 =
11517656SSherry.Moore@Sun.COM 		    AC_COUNTER_TO_PIC1(pic_register);
11521341Sstevel 	}
11531341Sstevel 	return (0);
11541341Sstevel }
11551341Sstevel 
11561341Sstevel /*
11571341Sstevel  * Decode the memory state given to us and plug it into the soft state
11581341Sstevel  */
11591341Sstevel static void
ac_get_memory_status(struct ac_soft_state * softsp,enum ac_bank_id id)11601341Sstevel ac_get_memory_status(struct ac_soft_state *softsp, enum ac_bank_id id)
11611341Sstevel {
11621341Sstevel 	char	*property = (id == Bank0) ? AC_BANK0_STATUS : AC_BANK1_STATUS;
11631341Sstevel 	char	*propval;
11641341Sstevel 	int	proplen;
11651341Sstevel 	uint64_t memdec = (id == Bank0) ?
11667656SSherry.Moore@Sun.COM 	    *(softsp->ac_memdecode0) : *(softsp->ac_memdecode1);
11671341Sstevel 	uint_t		grp_size;
11681341Sstevel 
11691341Sstevel 	softsp->bank[id].busy = 0;
11701341Sstevel 	softsp->bank[id].status_change = ddi_get_time();
11711341Sstevel 
11721341Sstevel 	if (GRP_SIZE_IS_SET(memdec)) {
11731341Sstevel 		grp_size = GRP_SPANMB(memdec);
11741341Sstevel 
11751341Sstevel 		/* determine the memory bank size (in MB) */
11761341Sstevel 		softsp->bank[id].real_size = softsp->bank[id].use_size =
11771341Sstevel 		    (id == Bank0) ? (grp_size / INTLV0(*softsp->ac_memctl)) :
11781341Sstevel 		    (grp_size / INTLV1(*softsp->ac_memctl));
11791341Sstevel 	} else {
11801341Sstevel 		softsp->bank[id].real_size = softsp->bank[id].use_size = 0;
11811341Sstevel 	}
11821341Sstevel 
11831341Sstevel 	/*
11841341Sstevel 	 * decode the memory bank property.  set condition based
11851341Sstevel 	 * on the values.
11861341Sstevel 	 */
11871341Sstevel 	if (ddi_prop_op(DDI_DEV_T_ANY, softsp->dip, PROP_LEN_AND_VAL_ALLOC,
11881341Sstevel 	    DDI_PROP_DONTPASS, property, (caddr_t)&propval, &proplen) ==
11891341Sstevel 	    DDI_PROP_SUCCESS) {
11901341Sstevel 		if (strcmp(propval, AC_BANK_NOMEM) == 0) {
11911341Sstevel 			softsp->bank[id].rstate = SYSC_CFGA_RSTATE_EMPTY;
11921341Sstevel 			softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
11931341Sstevel 			softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
11941341Sstevel 		} else if (strcmp(propval, AC_BANK_OK) == 0) {
11951341Sstevel 			softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
11961341Sstevel 			softsp->bank[id].ostate = SYSC_CFGA_OSTATE_CONFIGURED;
11971341Sstevel 			softsp->bank[id].condition = SYSC_CFGA_COND_OK;
11981341Sstevel 		} else if (strcmp(propval, AC_BANK_SPARE) == 0) {
11991341Sstevel 			softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
12001341Sstevel 			softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
12011341Sstevel 			softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
12021341Sstevel 		} else if (strcmp(propval, AC_BANK_FAILED) == 0) {
12031341Sstevel 			softsp->bank[id].rstate = SYSC_CFGA_RSTATE_DISCONNECTED;
12041341Sstevel 			softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
12051341Sstevel 			softsp->bank[id].condition = SYSC_CFGA_COND_UNUSABLE;
12061341Sstevel 		} else {
12071341Sstevel 			cmn_err(CE_WARN, "ac%d: board %d, bank %d: "
12081341Sstevel 			    "unknown %smemory state [%s]",
12091341Sstevel 			    ddi_get_instance(softsp->dip), softsp->board, id,
12101341Sstevel 			    (memdec & AC_MEM_VALID) ? "connected " : "",
12111341Sstevel 			    propval);
12121341Sstevel 			if (memdec & AC_MEM_VALID) {
12131341Sstevel 				softsp->bank[id].rstate =
12141341Sstevel 				    SYSC_CFGA_RSTATE_CONNECTED;
12151341Sstevel 				softsp->bank[id].ostate =
12161341Sstevel 				    SYSC_CFGA_OSTATE_CONFIGURED;
12171341Sstevel 				softsp->bank[id].condition =
12181341Sstevel 				    SYSC_CFGA_COND_OK;
12191341Sstevel 			} else {
12201341Sstevel 				softsp->bank[id].rstate =
12211341Sstevel 				    SYSC_CFGA_RSTATE_DISCONNECTED;
12221341Sstevel 				softsp->bank[id].ostate =
12231341Sstevel 				    SYSC_CFGA_OSTATE_UNCONFIGURED;
12241341Sstevel 				softsp->bank[id].condition =
12251341Sstevel 				    SYSC_CFGA_COND_UNUSABLE;
12261341Sstevel 			}
12271341Sstevel 		}
12281341Sstevel 
12291341Sstevel 		kmem_free(propval, proplen);
12301341Sstevel 	} else {
12311341Sstevel 		/* we don't have the property, deduce the state of memory */
12321341Sstevel 		if (memdec & AC_MEM_VALID) {
12331341Sstevel 			softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
12341341Sstevel 			softsp->bank[id].ostate = SYSC_CFGA_OSTATE_CONFIGURED;
12351341Sstevel 			softsp->bank[id].condition = SYSC_CFGA_COND_OK;
12361341Sstevel 		} else {
12371341Sstevel 			/* could be an i/o board... */
12381341Sstevel 			softsp->bank[id].rstate = SYSC_CFGA_RSTATE_EMPTY;
12391341Sstevel 			softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
12401341Sstevel 			softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
12411341Sstevel 		}
12421341Sstevel 	}
12431341Sstevel 
12441341Sstevel 	/* we assume that all other bank statuses are NOT valid */
12451341Sstevel 	if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED) {
12461341Sstevel 		if ((memdec & AC_MEM_VALID) != 0) {
12471341Sstevel 			uint64_t	base_pa;
12481341Sstevel 
12491341Sstevel 			ASSERT((*softsp->ac_memctl & AC_CSR_REFEN) != 0);
12501341Sstevel 			/* register existence in the memloc database */
12511341Sstevel 			base_pa = GRP_REALBASE(memdec);
12521341Sstevel 			fhc_add_memloc(softsp->board, base_pa, grp_size);
12531341Sstevel 		}
12541341Sstevel 	}
12551341Sstevel }
12561341Sstevel 
12571341Sstevel static void
ac_eval_memory_status(struct ac_soft_state * softsp,enum ac_bank_id id)12581341Sstevel ac_eval_memory_status(struct ac_soft_state *softsp, enum ac_bank_id id)
12591341Sstevel {
12601341Sstevel 	uint64_t memdec = (id == Bank0) ?
12617656SSherry.Moore@Sun.COM 	    *(softsp->ac_memdecode0) : *(softsp->ac_memdecode1);
12621341Sstevel 	uint64_t	base_pa;
12631341Sstevel 
12641341Sstevel 	/*
12651341Sstevel 	 * Downgrade the status of any bank that did not get
12661341Sstevel 	 * programmed.
12671341Sstevel 	 */
12681341Sstevel 	if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED &&
12691341Sstevel 	    softsp->bank[id].ostate == SYSC_CFGA_OSTATE_UNCONFIGURED &&
12701341Sstevel 	    (memdec & AC_MEM_VALID) == 0) {
12711341Sstevel 		cmn_err(CE_WARN, "ac%d: board %d, bank %d: "
12721341Sstevel 		    "spare memory bank not valid - it was ",
12731341Sstevel 		    ddi_get_instance(softsp->dip), softsp->board, id);
12741341Sstevel 		cmn_err(CE_WARN, "misconfigured by the system "
12751341Sstevel 		    "firmware.  Disabling...");
12761341Sstevel 		softsp->bank[id].rstate = SYSC_CFGA_RSTATE_DISCONNECTED;
12771341Sstevel 		softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
12781341Sstevel 		softsp->bank[id].condition = SYSC_CFGA_COND_UNUSABLE;
12791341Sstevel 	}
12801341Sstevel 	/*
12811341Sstevel 	 * Log a message about good banks.
12821341Sstevel 	 */
12831341Sstevel 	if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED) {
12841341Sstevel 		ASSERT((memdec & AC_MEM_VALID) != 0);
12851341Sstevel 		base_pa = GRP_REALBASE(memdec);
12861341Sstevel 
12871341Sstevel 		cmn_err(CE_CONT, "?ac%d board %d bank %d: "
12881341Sstevel 		    "base 0x%" PRIx64 " size %dmb rstate %d "
12891341Sstevel 		    "ostate %d condition %d\n",
12901341Sstevel 		    ddi_get_instance(softsp->dip),
12911341Sstevel 		    softsp->board, id, base_pa, softsp->bank[id].real_size,
12921341Sstevel 		    softsp->bank[id].rstate, softsp->bank[id].ostate,
12931341Sstevel 		    softsp->bank[id].condition);
12941341Sstevel 	}
12951341Sstevel }
12961341Sstevel 
12971341Sstevel /*ARGSUSED*/
12981341Sstevel static void
ac_ecache_flush(uint64_t a,uint64_t b)12991341Sstevel ac_ecache_flush(uint64_t a, uint64_t b)
13001341Sstevel {
13011341Sstevel 	cpu_flush_ecache();
13021341Sstevel }
13031341Sstevel 
13041341Sstevel static char *
ac_ostate_typestr(sysc_cfga_ostate_t ostate,ac_audit_evt_t event)13051341Sstevel ac_ostate_typestr(sysc_cfga_ostate_t ostate, ac_audit_evt_t event)
13061341Sstevel {
13071341Sstevel 	char *type_str;
13081341Sstevel 
13091341Sstevel 	switch (ostate) {
13101341Sstevel 	case SYSC_CFGA_OSTATE_UNCONFIGURED:
13111341Sstevel 		switch (event) {
13121341Sstevel 		case AC_AUDIT_OSTATE_UNCONFIGURE:
13131341Sstevel 			type_str = "unconfiguring";
13141341Sstevel 			break;
13151341Sstevel 		case AC_AUDIT_OSTATE_SUCCEEDED:
13161341Sstevel 		case AC_AUDIT_OSTATE_UNCONFIGURE_FAILED:
13171341Sstevel 			type_str = "unconfigured";
13181341Sstevel 			break;
13191341Sstevel 		default:
13201341Sstevel 			type_str = "unconfigure?";
13211341Sstevel 			break;
13221341Sstevel 		}
13231341Sstevel 		break;
13241341Sstevel 	case SYSC_CFGA_OSTATE_CONFIGURED:
13251341Sstevel 		switch (event) {
13261341Sstevel 		case AC_AUDIT_OSTATE_CONFIGURE:
13271341Sstevel 			type_str = "configuring";
13281341Sstevel 			break;
13291341Sstevel 		case AC_AUDIT_OSTATE_SUCCEEDED:
13301341Sstevel 		case AC_AUDIT_OSTATE_CONFIGURE_FAILED:
13311341Sstevel 			type_str = "configured";
13321341Sstevel 			break;
13331341Sstevel 		default:
13341341Sstevel 			type_str = "configure?";
13351341Sstevel 			break;
13361341Sstevel 		}
13371341Sstevel 		break;
13381341Sstevel 
13391341Sstevel 	default:
13401341Sstevel 		type_str = "undefined occupant state";
13411341Sstevel 		break;
13421341Sstevel 	}
13431341Sstevel 	return (type_str);
13441341Sstevel }
13451341Sstevel 
13461341Sstevel static void
ac_policy_audit_messages(ac_audit_evt_t event,ac_cfga_pkt_t * pkt)13471341Sstevel ac_policy_audit_messages(ac_audit_evt_t event, ac_cfga_pkt_t *pkt)
13481341Sstevel {
13491341Sstevel 	struct ac_soft_state *softsp = pkt->softsp;
13501341Sstevel 
13511341Sstevel 	switch (event) {
13521341Sstevel 		case AC_AUDIT_OSTATE_CONFIGURE:
13531341Sstevel 			cmn_err(CE_NOTE,
13547656SSherry.Moore@Sun.COM 			    "%s memory bank %d in slot %d",
13557656SSherry.Moore@Sun.COM 			    ac_ostate_typestr(SYSC_CFGA_OSTATE_CONFIGURED,
13567656SSherry.Moore@Sun.COM 			    event), pkt->bank,
13577656SSherry.Moore@Sun.COM 			    softsp->board);
13581341Sstevel 			break;
13591341Sstevel 		case AC_AUDIT_OSTATE_UNCONFIGURE:
13601341Sstevel 			cmn_err(CE_NOTE,
13617656SSherry.Moore@Sun.COM 			    "%s memory bank %d in slot %d",
13627656SSherry.Moore@Sun.COM 			    ac_ostate_typestr(
13637656SSherry.Moore@Sun.COM 			    SYSC_CFGA_OSTATE_UNCONFIGURED,
13647656SSherry.Moore@Sun.COM 			    event), pkt->bank,
13657656SSherry.Moore@Sun.COM 			    softsp->board);
13661341Sstevel 			break;
13671341Sstevel 		case AC_AUDIT_OSTATE_SUCCEEDED:
13681341Sstevel 			cmn_err(CE_NOTE,
13697656SSherry.Moore@Sun.COM 			    "memory bank %d in slot %d is %s",
13707656SSherry.Moore@Sun.COM 			    pkt->bank, softsp->board,
13717656SSherry.Moore@Sun.COM 			    ac_ostate_typestr(
13727656SSherry.Moore@Sun.COM 			    softsp->bank[pkt->bank].ostate,
13737656SSherry.Moore@Sun.COM 			    event));
13741341Sstevel 			break;
13751341Sstevel 		case AC_AUDIT_OSTATE_CONFIGURE_FAILED:
13761341Sstevel 			cmn_err(CE_NOTE,
13771341Sstevel 			"memory bank %d in slot %d not %s",
13787656SSherry.Moore@Sun.COM 			    pkt->bank,
13797656SSherry.Moore@Sun.COM 			    softsp->board,
13807656SSherry.Moore@Sun.COM 			    ac_ostate_typestr(
13817656SSherry.Moore@Sun.COM 			    SYSC_CFGA_OSTATE_CONFIGURED,
13827656SSherry.Moore@Sun.COM 			    event));
13831341Sstevel 			break;
13841341Sstevel 		case AC_AUDIT_OSTATE_UNCONFIGURE_FAILED:
13851341Sstevel 			cmn_err(CE_NOTE,
13867656SSherry.Moore@Sun.COM 			    "memory bank %d in slot %d not %s",
13877656SSherry.Moore@Sun.COM 			    pkt->bank,
13887656SSherry.Moore@Sun.COM 			    softsp->board,
13897656SSherry.Moore@Sun.COM 			    ac_ostate_typestr(
13907656SSherry.Moore@Sun.COM 			    SYSC_CFGA_OSTATE_UNCONFIGURED,
13917656SSherry.Moore@Sun.COM 			    event));
13921341Sstevel 			break;
13931341Sstevel 		default:
13941341Sstevel 			cmn_err(CE_NOTE,
13957656SSherry.Moore@Sun.COM 			    "unknown audit of memory bank %d in slot %d",
13967656SSherry.Moore@Sun.COM 			    pkt->bank, softsp->board);
13971341Sstevel 			break;
13981341Sstevel 	}
13991341Sstevel }
14001341Sstevel 
14011341Sstevel #include <vm/page.h>
14021341Sstevel #include <vm/hat.h>
14031341Sstevel 
14041341Sstevel static int
ac_mem_exercise(ac_cfga_pkt_t * pkt,int flag)14051341Sstevel ac_mem_exercise(ac_cfga_pkt_t *pkt, int flag)
14061341Sstevel {
14071341Sstevel 	struct ac_mem_info *mem_info;
14081341Sstevel 	pfn_t base;
14091341Sstevel 	pgcnt_t npgs;
14101341Sstevel 
14111341Sstevel 	mem_info = &pkt->softsp->bank[pkt->bank];
14121341Sstevel 	if (mem_info->rstate == SYSC_CFGA_RSTATE_CONNECTED) {
14131341Sstevel 		uint64_t base_pa, bank_size;
14141341Sstevel 		uint64_t decode;
14151341Sstevel 
14161341Sstevel 		decode = (pkt->bank == Bank0) ?
14171341Sstevel 		    *pkt->softsp->ac_memdecode0 : *pkt->softsp->ac_memdecode1;
14181341Sstevel 		base_pa = GRP_REALBASE(decode);
14191341Sstevel 		bank_size = GRP_UK2SPAN(decode);
14201341Sstevel 
14211341Sstevel 		base = base_pa >> PAGESHIFT;
14221341Sstevel 		npgs = bank_size >> PAGESHIFT;
14231341Sstevel 	} else {
14241341Sstevel 		base = 0;
14251341Sstevel 		npgs = 0;
14261341Sstevel 	}
14271341Sstevel 	switch (pkt->cmd_cfga.arg) {
14281341Sstevel 	case AC_MEMX_RELOCATE_ALL: {
14291341Sstevel 		pfn_t pfn, pglim;
14301341Sstevel 		struct ac_memx_relocate_stats rstat;
14311341Sstevel 
14321341Sstevel 		if (npgs == 0 ||
14331341Sstevel 		    mem_info->ostate != SYSC_CFGA_OSTATE_CONFIGURED) {
14341341Sstevel 			return (EINVAL);
14351341Sstevel 		}
14361341Sstevel 		if (mem_info->busy != FALSE) {
14371341Sstevel 			return (EBUSY);
14381341Sstevel 		}
14391341Sstevel 		bzero(&rstat, sizeof (rstat));
14401341Sstevel 		rstat.base = (uint_t)base;
14411341Sstevel 		rstat.npgs = (uint_t)npgs;
14421341Sstevel 		pglim = base + npgs;
14431341Sstevel 		for (pfn = base; pfn < pglim; pfn++) {
14441341Sstevel 			page_t *pp, *pp_repl;
14451341Sstevel 
14461341Sstevel 		retry:
14471341Sstevel 			pp = page_numtopp_nolock(pfn);
14481341Sstevel 			if (pp != NULL) {
14491341Sstevel 				if (!page_trylock(pp, SE_EXCL)) {
14501341Sstevel 					pp = NULL;
14511341Sstevel 					rstat.nolock++;
14521341Sstevel 				}
14531341Sstevel 				if (pp != NULL && page_pptonum(pp) != pfn) {
14541341Sstevel 					page_unlock(pp);
14551341Sstevel 					goto retry;
14561341Sstevel 				}
14571341Sstevel 			} else {
14581341Sstevel 				rstat.nopaget++;
14591341Sstevel 			}
14601341Sstevel 			if (pp != NULL && PP_ISFREE(pp)) {
14611341Sstevel 				page_unlock(pp);
14621341Sstevel 				rstat.isfree++;
14631341Sstevel 				pp = NULL;
14641341Sstevel 			}
14651341Sstevel 			if (pp != NULL) {
14661341Sstevel 				spgcnt_t npgs;
14671341Sstevel 				int result;
14681341Sstevel 
14691341Sstevel 				pp_repl = NULL;
14701341Sstevel 				result = page_relocate(&pp, &pp_repl, 1, 1,
14711341Sstevel 				    &npgs, NULL);
14721341Sstevel 				if (result == 0) {
14731341Sstevel 					while (npgs-- > 0) {
14741341Sstevel 						page_t *tpp;
14751341Sstevel 
14761341Sstevel 						ASSERT(pp_repl != NULL);
14771341Sstevel 						tpp = pp_repl;
14781341Sstevel 						page_sub(&pp_repl, tpp);
14791341Sstevel 						page_unlock(tpp);
14801341Sstevel 					}
14811341Sstevel 
14821341Sstevel 					rstat.reloc++;
14831341Sstevel 				} else {
14841341Sstevel 					page_unlock(pp);
14851341Sstevel 					rstat.noreloc++;
14861341Sstevel 				}
14871341Sstevel 			}
14881341Sstevel 		}
14891341Sstevel 		if (pkt->cmd_cfga.private != NULL && ddi_copyout(&rstat,
14901341Sstevel 		    pkt->cmd_cfga.private, sizeof (rstat), flag) != 0)
14911341Sstevel 			return (EFAULT);
14921341Sstevel 		return (DDI_SUCCESS);
14931341Sstevel 	}
14941341Sstevel 
14951341Sstevel 	default:
14961341Sstevel 		return (EINVAL);
14971341Sstevel 	}
14981341Sstevel }
14991341Sstevel 
15001341Sstevel static int
ac_reset_timeout(int rw)15011341Sstevel ac_reset_timeout(int rw)
15021341Sstevel {
15031341Sstevel 	mutex_enter(&ac_hot_plug_mode_mutex);
15041341Sstevel 
15051341Sstevel 	if ((ac_hot_plug_timeout == (timeout_id_t)NULL) &&
15061341Sstevel 	    (rw == KSTAT_READ)) {
15071341Sstevel 		/*
15081341Sstevel 		 * We are in hot-plug mode. A kstat_read is not
15091341Sstevel 		 * going to affect this. return 0 to allow the
15101341Sstevel 		 * kstat_read to continue.
15111341Sstevel 		 */
15121341Sstevel 		mutex_exit(&ac_hot_plug_mode_mutex);
15131341Sstevel 		return (0);
15141341Sstevel 
15151341Sstevel 	} else if ((ac_hot_plug_timeout == (timeout_id_t)NULL) &&
15161341Sstevel 	    (rw == KSTAT_WRITE)) {
15171341Sstevel 		/*
15181341Sstevel 		 * There are no pending timeouts and we have received a
15191341Sstevel 		 * kstat_write request so we must be transitioning
15201341Sstevel 		 * from "hot-plug" mode to non "hot-plug" mode.
15211341Sstevel 		 * Try to lock all boards before allowing the kstat_write.
15221341Sstevel 		 */
15231341Sstevel 		if (ac_enter_transition() == TRUE)
15241341Sstevel 			fhc_bdlist_unlock();
15251341Sstevel 		else {
15261341Sstevel 			/* cannot lock boards so fail */
15271341Sstevel 			mutex_exit(&ac_hot_plug_mode_mutex);
15281341Sstevel 			return (-1);
15291341Sstevel 		}
15301341Sstevel 
15311341Sstevel 		/*
15321341Sstevel 		 * We need to display a Warning about hot-plugging any
15331341Sstevel 		 * boards. This message is only needed when we are
15341341Sstevel 		 * transitioning out of "hot-plug" mode.
15351341Sstevel 		 */
15361341Sstevel 		cmn_err(CE_WARN, "This machine is being taken out of "
15371341Sstevel 		    "hot-plug mode.");
15381341Sstevel 		cmn_err(CE_CONT, "Do not attempt to hot-plug boards "
15391341Sstevel 		    "or power supplies in this system until further notice.");
15401341Sstevel 
15411341Sstevel 	} else if (ac_hot_plug_timeout != (timeout_id_t)NULL) {
15421341Sstevel 		/*
15431341Sstevel 		 * There is a pending timeout so we must already be
15441341Sstevel 		 * in non "hot-plug" mode. It doesn't matter if the
15451341Sstevel 		 * kstat request is a read or a write.
15461341Sstevel 		 *
15471341Sstevel 		 * We need to cancel the existing timeout.
15481341Sstevel 		 */
15491341Sstevel 		(void) untimeout(ac_hot_plug_timeout);
15501341Sstevel 		ac_hot_plug_timeout = NULL;
15511341Sstevel 	}
15521341Sstevel 
15531341Sstevel 	/*
15541341Sstevel 	 * create a new timeout.
15551341Sstevel 	 */
15561341Sstevel 	ac_hot_plug_timeout = timeout(ac_timeout, NULL,
15571341Sstevel 	    drv_usectohz(ac_hot_plug_timeout_interval * 1000000));
15581341Sstevel 
15591341Sstevel 	mutex_exit(&ac_hot_plug_mode_mutex);
15601341Sstevel 	return (0);
15611341Sstevel }
15621341Sstevel 
15631341Sstevel static void
ac_timeout(void * arg)15641341Sstevel ac_timeout(void *arg)
15651341Sstevel {
15661341Sstevel 	struct ac_soft_state *softsp;
15671341Sstevel 	fhc_bd_t	*board;
15681341Sstevel 
15691341Sstevel #ifdef lint
15701341Sstevel 	arg = arg;
15711341Sstevel #endif /* lint */
15721341Sstevel 
15731341Sstevel 	ac_hot_plug_timeout = (timeout_id_t)NULL;
15741341Sstevel 
15751341Sstevel 	(void) fhc_bdlist_lock(-1);
15761341Sstevel 
15771341Sstevel 	/*
15781341Sstevel 	 * Foreach ac in the board list we need to
15791341Sstevel 	 * re-program the pcr into "hot-plug" mode.
15801341Sstevel 	 * We also program the pic register with the
15811341Sstevel 	 * bus pause timing
15821341Sstevel 	 */
15831341Sstevel 	board = fhc_bd_first();
15841341Sstevel 	while (board != NULL) {
15851341Sstevel 		softsp = board->ac_softsp;
15861341Sstevel 		if (softsp == NULL) {
15871341Sstevel 			/*
15881341Sstevel 			 * This board must not have an AC.
15891341Sstevel 			 * Skip it and move on.
15901341Sstevel 			 */
15911341Sstevel 			board = fhc_bd_next(board);
15921341Sstevel 			continue;
15931341Sstevel 		}
15941341Sstevel 		/* program the pcr into hot-plug mode */
15951341Sstevel 		*softsp->ac_mccr = AC_CLEAR_PCR(*softsp->ac_mccr);
15961341Sstevel 		*softsp->ac_mccr = AC_SET_HOT_PLUG(*softsp->ac_mccr);
15971341Sstevel 
15981341Sstevel 		/* program the pic with the bus pause time value */
15991341Sstevel 		*softsp->ac_counter = AC_SET_PIC_BUS_PAUSE(softsp->board);
16001341Sstevel 
16011341Sstevel 		/* get the next board */
16021341Sstevel 		board = fhc_bd_next(board);
16031341Sstevel 	}
16041341Sstevel 
16051341Sstevel 	ac_exit_transition();
16061341Sstevel 
16071341Sstevel 	fhc_bdlist_unlock();
16081341Sstevel 
16091341Sstevel 	/*
16101341Sstevel 	 * It is now safe to start hot-plugging again. We need
16111341Sstevel 	 * to display a message.
16121341Sstevel 	 */
16131341Sstevel 	cmn_err(CE_NOTE, "This machine is now in hot-plug mode.");
16141341Sstevel 	cmn_err(CE_CONT, "Board and power supply hot-plug operations "
16151341Sstevel 	    "can be resumed.");
16161341Sstevel }
16171341Sstevel 
16181341Sstevel /*
16191341Sstevel  * This function will acquire the lock and set the in_transition
16201341Sstevel  * bit for all the slots.  If the slots are being used,
16211341Sstevel  * we return FALSE; else set in_transition and return TRUE.
16221341Sstevel  */
16231341Sstevel static int
ac_enter_transition(void)16241341Sstevel ac_enter_transition(void)
16251341Sstevel {
16261341Sstevel 	fhc_bd_t	*list;
16271341Sstevel 	sysc_cfga_stat_t *sysc_stat_lk;
16281341Sstevel 
16291341Sstevel 	/* mutex lock the structure */
16301341Sstevel 	(void) fhc_bdlist_lock(-1);
16311341Sstevel 
16321341Sstevel 	list = fhc_bd_clock();
16331341Sstevel 
16341341Sstevel 	/* change the in_transition bit */
16351341Sstevel 	sysc_stat_lk = &list->sc;
16361341Sstevel 	if (sysc_stat_lk->in_transition == TRUE) {
16371341Sstevel 		fhc_bdlist_unlock();
16381341Sstevel 		return (FALSE);
16391341Sstevel 	} else {
16401341Sstevel 		sysc_stat_lk->in_transition = TRUE;
16411341Sstevel 		return (TRUE);
16421341Sstevel 	}
16431341Sstevel }
16441341Sstevel 
16451341Sstevel /*
16461341Sstevel  * clear the in_transition bit for all the slots.
16471341Sstevel  */
16481341Sstevel static void
ac_exit_transition(void)16491341Sstevel ac_exit_transition(void)
16501341Sstevel {
16511341Sstevel 	fhc_bd_t	*list;
16521341Sstevel 	sysc_cfga_stat_t *sysc_stat_lk;
16531341Sstevel 
16541341Sstevel 	ASSERT(fhc_bdlist_locked());
16551341Sstevel 
16561341Sstevel 	list = fhc_bd_clock();
16571341Sstevel 
16581341Sstevel 	sysc_stat_lk = &list->sc;
16591341Sstevel 	ASSERT(sysc_stat_lk->in_transition == TRUE);
16601341Sstevel 	sysc_stat_lk->in_transition = FALSE;
16611341Sstevel }
1662