xref: /onnv-gate/usr/src/uts/sun4u/starcat/io/iosram.c (revision 7656:2621e50fdf4a)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel 
281708Sstevel /*
291708Sstevel  * IOSRAM leaf driver to SBBC nexus driver.  This driver is used
301708Sstevel  * by Starcat Domain SW to read/write from/to the IO sram.
311708Sstevel  */
321708Sstevel 
331708Sstevel #include <sys/types.h>
341708Sstevel #include <sys/conf.h>
351708Sstevel #include <sys/ddi.h>
361708Sstevel #include <sys/sunddi.h>
371708Sstevel #include <sys/ddi_impldefs.h>
381708Sstevel #include <sys/obpdefs.h>
391708Sstevel #include <sys/promif.h>
401708Sstevel #include <sys/prom_plat.h>
411708Sstevel #include <sys/cmn_err.h>
421708Sstevel #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
431708Sstevel #include <sys/modctl.h>		/* for modldrv */
441708Sstevel #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
451708Sstevel #include <sys/errno.h>
461708Sstevel #include <sys/kmem.h>
471708Sstevel #include <sys/kstat.h>
481708Sstevel #include <sys/debug.h>
491708Sstevel 
501708Sstevel #include <sys/axq.h>
511708Sstevel #include <sys/iosramreg.h>
521708Sstevel #include <sys/iosramio.h>
531708Sstevel #include <sys/iosramvar.h>
541708Sstevel 
551708Sstevel 
561708Sstevel #if defined(DEBUG)
571708Sstevel int	iosram_debug = 0;
581708Sstevel static void iosram_dprintf(const char *fmt, ...);
591708Sstevel #define	DPRINTF(level, arg)	\
601708Sstevel 		{ if (iosram_debug >= level) iosram_dprintf arg; }
611708Sstevel #else	/* !DEBUG */
621708Sstevel #define	DPRINTF(level, arg)
631708Sstevel #endif	/* !DEBUG */
641708Sstevel 
651708Sstevel 
661708Sstevel /*
671708Sstevel  * IOSRAM module global state
681708Sstevel  */
691708Sstevel static void	*iosramsoft_statep;	/* IOSRAM state pointer */
701708Sstevel static kmutex_t	iosram_mutex;		/* mutex lock */
711708Sstevel 
721708Sstevel static iosram_chunk_t	*chunks = NULL;	/* array of TOC entries */
731708Sstevel static int	nchunks = 0;		/* # of TOC entries */
741708Sstevel static iosram_chunk_t	*iosram_hashtab[IOSRAM_HASHSZ];	/* key hash table */
751708Sstevel 
761708Sstevel static kcondvar_t	iosram_tswitch_wait;	/* tunnel switch wait cv */
771708Sstevel static int	iosram_tswitch_wakeup = 0;	/* flag indicationg one or */
781708Sstevel 						/* more threads waiting on */
791708Sstevel 						/* iosram_tswitch_wait cv */
801708Sstevel static int	iosram_tswitch_active = 0;	/* tunnel switch active flag */
811708Sstevel static int	iosram_tswitch_aborted = 0;	/* tunnel switch abort flag */
821708Sstevel static clock_t	iosram_tswitch_tstamp = 0;	/* lbolt of last tswitch end */
831708Sstevel static kcondvar_t	iosram_rw_wait;		/* read/write wait cv */
841708Sstevel static int	iosram_rw_wakeup = 0;		/* flag indicationg one or */
851708Sstevel 						/* more threads waiting on */
861708Sstevel 						/* iosram_rw_wait cv */
871708Sstevel static int	iosram_rw_active = 0;		/* # threads accessing IOSRAM */
881708Sstevel #if defined(DEBUG)
891708Sstevel static int	iosram_rw_active_max = 0;
901708Sstevel #endif
911708Sstevel 
921708Sstevel static struct iosramsoft *iosram_new_master = NULL;	/* new tunnel target */
931708Sstevel static struct iosramsoft *iosram_master = NULL;		/* master tunnel */
941708Sstevel static struct iosramsoft *iosram_instances = NULL;	/* list of softstates */
951708Sstevel 
961708Sstevel static ddi_acc_handle_t	iosram_handle = NULL;	/* master IOSRAM map handle */
971708Sstevel 
981708Sstevel static void	(*iosram_hdrchange_handler)() = NULL;
991708Sstevel 
1001708Sstevel #if IOSRAM_STATS
1011708Sstevel static struct	iosram_stat iosram_stats;	/* IOSRAM statistics */
1021708Sstevel static void	iosram_print_stats();		/* forward declaration */
1031708Sstevel #endif /* IOSRAM_STATS */
1041708Sstevel 
1051708Sstevel 
1061708Sstevel #if IOSRAM_LOG
1071708Sstevel kmutex_t 	iosram_log_mutex;
1081708Sstevel int		iosram_log_level = 1;
1091708Sstevel int		iosram_log_print = 0;		/* print log when recorded */
1101708Sstevel uint32_t	iosram_logseq;
1111708Sstevel iosram_log_t	iosram_logbuf[IOSRAM_MAXLOG];
1121708Sstevel static void	iosram_print_log(int cnt);	/* forward declaration */
1131708Sstevel #endif	/* IOSRAM_LOG */
1141708Sstevel 
1151708Sstevel 
1161708Sstevel /* driver entry point fn definitions */
1171708Sstevel static int 	iosram_open(dev_t *, int, int, cred_t *);
1181708Sstevel static int	iosram_close(dev_t, int, int, cred_t *);
1191708Sstevel static int	iosram_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1201708Sstevel 
1211708Sstevel /* configuration entry point fn definitions */
1221708Sstevel static int 	iosram_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1231708Sstevel static int	iosram_attach(dev_info_t *, ddi_attach_cmd_t);
1241708Sstevel static int	iosram_detach(dev_info_t *, ddi_detach_cmd_t);
1251708Sstevel 
1261708Sstevel 
1271708Sstevel /* forward declaractions */
1281708Sstevel static iosram_chunk_t	*iosram_find_chunk(uint32_t key);
1291708Sstevel static void	iosram_set_master(struct iosramsoft *softp);
1301708Sstevel static int	iosram_is_chosen(struct iosramsoft *softp);
1311708Sstevel static int	iosram_tunnel_capable(struct iosramsoft *softp);
1321708Sstevel static int	iosram_read_toc(struct iosramsoft *softp);
1331708Sstevel static void	iosram_init_hashtab(void);
1341708Sstevel static void	iosram_update_addrs(struct iosramsoft *softp);
1351708Sstevel 
1361708Sstevel static int	iosram_setup_map(struct iosramsoft *softp);
1371708Sstevel static void	iosram_remove_map(struct iosramsoft *softp);
1381708Sstevel static int	iosram_add_intr(iosramsoft_t *);
1391708Sstevel static int	iosram_remove_intr(iosramsoft_t *);
1401708Sstevel 
1411708Sstevel static void	iosram_add_instance(struct iosramsoft *softp);
1421708Sstevel static void	iosram_remove_instance(int instance);
1431708Sstevel static int	iosram_switch_tunnel(iosramsoft_t *softp);
1441708Sstevel static void	iosram_abort_tswitch();
1451708Sstevel 
1461708Sstevel #if defined(DEBUG)
1471708Sstevel /* forward declaractions for debugging */
1481708Sstevel static int	iosram_get_keys(iosram_toc_entry_t *buf, uint32_t *len);
1491708Sstevel static void	iosram_print_cback();
1501708Sstevel static void	iosram_print_state(int);
1511708Sstevel static void	iosram_print_flags();
1521708Sstevel #endif
1531708Sstevel 
1541708Sstevel 
1551708Sstevel 
1561708Sstevel /*
1571708Sstevel  * cb_ops
1581708Sstevel  */
1591708Sstevel static struct cb_ops iosram_cb_ops = {
1601708Sstevel 	iosram_open,		/* cb_open */
1611708Sstevel 	iosram_close,		/* cb_close */
1621708Sstevel 	nodev,			/* cb_strategy */
1631708Sstevel 	nodev,			/* cb_print */
1641708Sstevel 	nodev,			/* cb_dump */
1651708Sstevel 	nodev,			/* cb_read */
1661708Sstevel 	nodev,			/* cb_write */
1671708Sstevel 	iosram_ioctl,		/* cb_ioctl */
1681708Sstevel 	nodev,			/* cb_devmap */
1691708Sstevel 	nodev,			/* cb_mmap */
1701708Sstevel 	nodev,			/* cb_segmap */
1711708Sstevel 	nochpoll,		/* cb_chpoll */
1721708Sstevel 	ddi_prop_op,		/* cb_prop_op */
1731708Sstevel 	NULL,			/* cb_stream */
1741708Sstevel 	(int)(D_NEW | D_MP | D_HOTPLUG)	/* cb_flag */
1751708Sstevel };
1761708Sstevel 
1771708Sstevel /*
1781708Sstevel  * Declare ops vectors for auto configuration.
1791708Sstevel  */
1801708Sstevel struct dev_ops  iosram_ops = {
1811708Sstevel 	DEVO_REV,		/* devo_rev */
1821708Sstevel 	0,			/* devo_refcnt */
1831708Sstevel 	iosram_getinfo,		/* devo_getinfo */
1841708Sstevel 	nulldev,		/* devo_identify */
1851708Sstevel 	nulldev,		/* devo_probe */
1861708Sstevel 	iosram_attach,		/* devo_attach */
1871708Sstevel 	iosram_detach,		/* devo_detach */
1881708Sstevel 	nodev,			/* devo_reset */
1891708Sstevel 	&iosram_cb_ops,		/* devo_cb_ops */
1901708Sstevel 	(struct bus_ops *)NULL,	/* devo_bus_ops */
191*7656SSherry.Moore@Sun.COM 	nulldev,		/* devo_power */
192*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1931708Sstevel };
1941708Sstevel 
1951708Sstevel /*
1961708Sstevel  * Loadable module support.
1971708Sstevel  */
1981708Sstevel extern struct mod_ops mod_driverops;
1991708Sstevel 
2001708Sstevel static struct modldrv iosrammodldrv = {
2011708Sstevel 	&mod_driverops,		/* type of module - driver */
202*7656SSherry.Moore@Sun.COM 	"IOSRAM Leaf driver",
2031708Sstevel 	&iosram_ops,
2041708Sstevel };
2051708Sstevel 
2061708Sstevel static struct modlinkage iosrammodlinkage = {
2071708Sstevel 	MODREV_1,
2081708Sstevel 	&iosrammodldrv,
2091708Sstevel 	NULL
2101708Sstevel };
2111708Sstevel 
2121708Sstevel 
2131708Sstevel int
2141708Sstevel _init(void)
2151708Sstevel {
2161708Sstevel 	int    error;
2171708Sstevel 	int	i;
2181708Sstevel 
2191708Sstevel 	mutex_init(&iosram_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
2201708Sstevel 	cv_init(&iosram_tswitch_wait, NULL, CV_DRIVER, NULL);
2211708Sstevel 	cv_init(&iosram_rw_wait, NULL, CV_DRIVER, NULL);
2221708Sstevel #if defined(IOSRAM_LOG)
2231708Sstevel 	mutex_init(&iosram_log_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
2241708Sstevel #endif
2251708Sstevel 
2261708Sstevel 	DPRINTF(1, ("_init:IOSRAM\n"));
2271708Sstevel 
2281708Sstevel 	for (i = 0; i < IOSRAM_HASHSZ; i++) {
2291708Sstevel 		iosram_hashtab[i] = NULL;
2301708Sstevel 	}
2311708Sstevel 
2321708Sstevel 	if ((error = ddi_soft_state_init(&iosramsoft_statep,
2331708Sstevel 	    sizeof (struct iosramsoft), 1)) != 0) {
2341708Sstevel 		goto failed;
2351708Sstevel 	}
2361708Sstevel 	if ((error = mod_install(&iosrammodlinkage)) != 0) {
2371708Sstevel 		ddi_soft_state_fini(&iosramsoft_statep);
2381708Sstevel 		goto failed;
2391708Sstevel 	}
2401708Sstevel 
2411708Sstevel 	IOSRAMLOG(0, "_init:IOSRAM ... error:%d  statep:%p\n",
2421708Sstevel 	    error, iosramsoft_statep, NULL, NULL);
2431708Sstevel 
2441708Sstevel 	return (error);
2451708Sstevel 
2461708Sstevel failed:
2471708Sstevel 	cv_destroy(&iosram_tswitch_wait);
2481708Sstevel 	cv_destroy(&iosram_rw_wait);
2491708Sstevel 	mutex_destroy(&iosram_mutex);
2501708Sstevel #if defined(IOSRAM_LOG)
2511708Sstevel 	mutex_destroy(&iosram_log_mutex);
2521708Sstevel #endif
2531708Sstevel 	IOSRAMLOG(0, "_init:IOSRAM ... error:%d  statep:%p\n",
2541708Sstevel 	    error, iosramsoft_statep, NULL, NULL);
2551708Sstevel 
2561708Sstevel 	return (error);
2571708Sstevel }
2581708Sstevel 
2591708Sstevel 
2601708Sstevel int
2611708Sstevel _fini(void)
2621708Sstevel {
2631708Sstevel #ifndef DEBUG
2641708Sstevel 	return (EBUSY);
2651708Sstevel #else /* !DEBUG */
2661708Sstevel 	int    error;
2671708Sstevel 
2681708Sstevel 	if ((error = mod_remove(&iosrammodlinkage)) == 0) {
2691708Sstevel 		ddi_soft_state_fini(&iosramsoft_statep);
2701708Sstevel 
2711708Sstevel 		cv_destroy(&iosram_tswitch_wait);
2721708Sstevel 		cv_destroy(&iosram_rw_wait);
2731708Sstevel 		mutex_destroy(&iosram_mutex);
2741708Sstevel #if defined(IOSRAM_LOG)
2751708Sstevel 		mutex_destroy(&iosram_log_mutex);
2761708Sstevel #endif
2771708Sstevel 	}
2781708Sstevel 	DPRINTF(1, ("_fini:IOSRAM  error:%d\n", error));
2791708Sstevel 
2801708Sstevel 	return (error);
2811708Sstevel #endif /* !DEBUG */
2821708Sstevel }
2831708Sstevel 
2841708Sstevel 
2851708Sstevel int
2861708Sstevel _info(struct modinfo *modinfop)
2871708Sstevel {
2881708Sstevel 	return (mod_info(&iosrammodlinkage, modinfop));
2891708Sstevel }
2901708Sstevel 
2911708Sstevel 
2921708Sstevel static int
2931708Sstevel iosram_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2941708Sstevel {
2951708Sstevel 	int	instance;
2961708Sstevel 	int	propval;
2971708Sstevel 	int	length;
2981708Sstevel 	char	name[32];
2991708Sstevel 	struct	iosramsoft *softp;
3001708Sstevel 
3011708Sstevel 	instance = ddi_get_instance(dip);
3021708Sstevel 
3031708Sstevel 	DPRINTF(1, ("iosram(%d): attach dip:%p\n", instance));
3041708Sstevel 
3051708Sstevel 	IOSRAMLOG(1, "ATTACH: dip:%p instance %d ... start\n",
3061708Sstevel 	    dip, instance, NULL, NULL);
3071708Sstevel 	switch (cmd) {
3081708Sstevel 	case DDI_ATTACH:
3091708Sstevel 		break;
3101708Sstevel 	case DDI_RESUME:
3111708Sstevel 		if (!(softp = ddi_get_soft_state(iosramsoft_statep,
3121708Sstevel 		    instance))) {
3131708Sstevel 			return (DDI_FAILURE);
3141708Sstevel 		}
3151708Sstevel 		mutex_enter(&iosram_mutex);
3161708Sstevel 		mutex_enter(&softp->intr_mutex);
3171708Sstevel 		if (!softp->suspended) {
3181708Sstevel 			mutex_exit(&softp->intr_mutex);
3191708Sstevel 			mutex_exit(&iosram_mutex);
3201708Sstevel 			return (DDI_FAILURE);
3211708Sstevel 		}
3221708Sstevel 		softp->suspended = 0;
3231708Sstevel 
3241708Sstevel 		/*
3251708Sstevel 		 * enable SBBC interrupts if SBBC is mapped in
3261708Sstevel 		 * restore the value saved during detach
3271708Sstevel 		 */
3281708Sstevel 		if (softp->sbbc_region) {
3291708Sstevel 			ddi_put32(softp->sbbc_handle,
3301708Sstevel 			    &(softp->sbbc_region->int_enable.reg),
3311708Sstevel 			    softp->int_enable_sav);
3321708Sstevel 		}
3331708Sstevel 
3341708Sstevel 		/*
3351708Sstevel 		 * Trigger soft interrupt handler to process any pending
3361708Sstevel 		 * interrupts.
3371708Sstevel 		 */
3381708Sstevel 		if (softp->intr_pending && !softp->intr_busy &&
3391708Sstevel 		    (softp->softintr_id != NULL)) {
3401708Sstevel 			ddi_trigger_softintr(softp->softintr_id);
3411708Sstevel 		}
3421708Sstevel 
3431708Sstevel 		mutex_exit(&softp->intr_mutex);
3441708Sstevel 		mutex_exit(&iosram_mutex);
3451708Sstevel 
3461708Sstevel 		return (DDI_SUCCESS);
3471708Sstevel 
3481708Sstevel 	default:
3491708Sstevel 		return (DDI_FAILURE);
3501708Sstevel 	}
3511708Sstevel 
3521708Sstevel 	if (ddi_soft_state_zalloc(iosramsoft_statep, instance) != 0) {
3531708Sstevel 		return (DDI_FAILURE);
3541708Sstevel 	}
3551708Sstevel 
3561708Sstevel 	if ((softp = ddi_get_soft_state(iosramsoft_statep, instance)) == NULL) {
357*7656SSherry.Moore@Sun.COM 			return (DDI_FAILURE);
3581708Sstevel 	}
3591708Sstevel 	softp->dip = dip;
3601708Sstevel 	softp->instance = instance;
3611708Sstevel 	softp->sbbc_region = NULL;
3621708Sstevel 
3631708Sstevel 	/*
3641708Sstevel 	 * If this instance is not tunnel capable, we don't attach it.
3651708Sstevel 	 */
3661708Sstevel 	if (iosram_tunnel_capable(softp) == 0) {
3671708Sstevel 		DPRINTF(1, ("iosram(%d): not tunnel_capable\n", instance));
3681708Sstevel 		IOSRAMLOG(1, "ATTACH(%d): not tunnel_capable\n", instance, NULL,
3691708Sstevel 		    NULL, NULL);
3701708Sstevel 		goto attach_fail;
3711708Sstevel 	}
3721708Sstevel 
3731708Sstevel 	/*
3741708Sstevel 	 * Need to create an "interrupt-priorities" property to define the PIL
3751708Sstevel 	 * to be used with the interrupt service routine.
3761708Sstevel 	 */
3771708Sstevel 	if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3781708Sstevel 	    "interrupt-priorities", &length) == DDI_PROP_NOT_FOUND) {
3791708Sstevel 		DPRINTF(1, ("iosram(%d): creating interrupt priority property",
3801708Sstevel 		    instance));
3811708Sstevel 		propval = IOSRAM_PIL;
3821708Sstevel 		if (ddi_prop_create(DDI_DEV_T_NONE, dip, 0,
3831708Sstevel 		    "interrupt-priorities", (caddr_t)&propval, sizeof (propval))
3841708Sstevel 		    != DDI_PROP_SUCCESS) {
3851708Sstevel 			cmn_err(CE_WARN,
3861708Sstevel 			    "iosram_attach: failed to create property");
3871708Sstevel 			goto attach_fail;
3881708Sstevel 		}
3891708Sstevel 	}
3901708Sstevel 
3911708Sstevel 	/*
3921708Sstevel 	 * Get interrupts cookies and initialize per-instance mutexes
3931708Sstevel 	 */
3941708Sstevel 	if (ddi_get_iblock_cookie(softp->dip, 0, &softp->real_iblk)
3951708Sstevel 	    != DDI_SUCCESS) {
3961708Sstevel 		IOSRAMLOG(1, "ATTACH(%d): cannot get soft intr cookie\n",
3971708Sstevel 		    instance, NULL, NULL, NULL);
3981708Sstevel 		goto attach_fail;
3991708Sstevel 	}
4001708Sstevel 	mutex_init(&softp->intr_mutex, NULL, MUTEX_DRIVER,
4011708Sstevel 	    (void *)softp->real_iblk);
4021708Sstevel 
4031708Sstevel 	/*
4041708Sstevel 	 * Add this instance to the iosram_instances list so that it can be used
4051708Sstevel 	 * for tunnel in future.
4061708Sstevel 	 */
4071708Sstevel 	mutex_enter(&iosram_mutex);
4081708Sstevel 	softp->state = IOSRAM_STATE_INIT;
4091708Sstevel 	iosram_add_instance(softp);
4101708Sstevel 
4111708Sstevel 	/*
4121708Sstevel 	 * If this is the chosen IOSRAM and there is no master IOSRAM yet, then
4131708Sstevel 	 * let's set this instance as the master.
4141708Sstevel 	 */
4151708Sstevel 	if (iosram_master == NULL && iosram_is_chosen(softp)) {
4161708Sstevel 		iosram_switch_tunnel(softp);
4171708Sstevel 
4181708Sstevel 		/*
4191708Sstevel 		 * XXX Do we need to panic if unable to setup master IOSRAM?
4201708Sstevel 		 */
4211708Sstevel 		if (iosram_master == NULL) {
4221708Sstevel 			cmn_err(CE_WARN,
4231708Sstevel 			    "iosram(%d): can't setup master tunnel\n",
4241708Sstevel 			    instance);
4251708Sstevel 			softp->state = 0;
4261708Sstevel 			iosram_remove_instance(softp->instance);
4271708Sstevel 			mutex_exit(&iosram_mutex);
4281708Sstevel 			mutex_destroy(&softp->intr_mutex);
4291708Sstevel 			goto attach_fail;
4301708Sstevel 		}
4311708Sstevel 	}
4321708Sstevel 
4331708Sstevel 	mutex_exit(&iosram_mutex);
4341708Sstevel 
4351708Sstevel 	/*
4361708Sstevel 	 * Create minor node
4371708Sstevel 	 */
4381708Sstevel 	(void) sprintf(name, "iosram%d", instance);
4391708Sstevel 	if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL, NULL) ==
4401708Sstevel 	    DDI_FAILURE) {
4411708Sstevel 		/*
4421708Sstevel 		 * Minor node seems to be needed only for debugging purposes.
4431708Sstevel 		 * Therefore, there is no need to fail this attach request.
4441708Sstevel 		 * Simply print a message out.
4451708Sstevel 		 */
4461708Sstevel 		cmn_err(CE_NOTE, "!iosram(%d): can't create minor node\n",
4471708Sstevel 		    instance);
4481708Sstevel 	}
4491708Sstevel 	ddi_report_dev(dip);
4501708Sstevel 
4511708Sstevel 	DPRINTF(1, ("iosram_attach(%d): success.\n", instance));
4521708Sstevel 	IOSRAMLOG(1, "ATTACH: dip:%p instance:%d ... success  softp:%p\n",
4531708Sstevel 	    dip, instance, softp, NULL);
4541708Sstevel 
4551708Sstevel 	return (DDI_SUCCESS);
4561708Sstevel 
4571708Sstevel attach_fail:
4581708Sstevel 	DPRINTF(1, ("iosram_attach(%d):failed.\n", instance));
4591708Sstevel 	IOSRAMLOG(1, "ATTACH: dip:%p instance:%d ... failed.\n",
4601708Sstevel 	    dip, instance, NULL, NULL);
4611708Sstevel 
4621708Sstevel 	ddi_soft_state_free(iosramsoft_statep, instance);
4631708Sstevel 	return (DDI_FAILURE);
4641708Sstevel }
4651708Sstevel 
4661708Sstevel 
4671708Sstevel static int
4681708Sstevel iosram_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4691708Sstevel {
4701708Sstevel 	int			instance;
4711708Sstevel 	struct iosramsoft	*softp;
4721708Sstevel 
4731708Sstevel 	instance = ddi_get_instance(dip);
4741708Sstevel 	if (!(softp = ddi_get_soft_state(iosramsoft_statep, instance))) {
4751708Sstevel 		return (DDI_FAILURE);
4761708Sstevel 	}
4771708Sstevel 
4781708Sstevel 	IOSRAMLOG(1, "DETACH: dip:%p instance %d softp:%p\n",
4791708Sstevel 	    dip, instance, softp, NULL);
4801708Sstevel 
4811708Sstevel 	switch (cmd) {
4821708Sstevel 	case DDI_DETACH:
4831708Sstevel 		break;
4841708Sstevel 	case DDI_SUSPEND:
4851708Sstevel 		mutex_enter(&iosram_mutex);
4861708Sstevel 		mutex_enter(&softp->intr_mutex);
4871708Sstevel 		if (softp->suspended) {
4881708Sstevel 			mutex_exit(&softp->intr_mutex);
4891708Sstevel 			mutex_exit(&iosram_mutex);
4901708Sstevel 			return (DDI_FAILURE);
4911708Sstevel 		}
4921708Sstevel 		softp->suspended = 1;
4931708Sstevel 		/*
4941708Sstevel 		 * Disable SBBC interrupts if SBBC is mapped in
4951708Sstevel 		 */
4961708Sstevel 		if (softp->sbbc_region) {
4971708Sstevel 			/* save current interrupt enable register */
4981708Sstevel 			softp->int_enable_sav = ddi_get32(softp->sbbc_handle,
4991708Sstevel 			    &(softp->sbbc_region->int_enable.reg));
5001708Sstevel 			ddi_put32(softp->sbbc_handle,
5011708Sstevel 			    &(softp->sbbc_region->int_enable.reg), 0x0);
5021708Sstevel 		}
5031708Sstevel 		mutex_exit(&softp->intr_mutex);
5041708Sstevel 		mutex_exit(&iosram_mutex);
5051708Sstevel 		return (DDI_SUCCESS);
5061708Sstevel 
5071708Sstevel 	default:
5081708Sstevel 		return (DDI_FAILURE);
5091708Sstevel 	}
5101708Sstevel 
5111708Sstevel 
5121708Sstevel 	/*
5131708Sstevel 	 * Indicate that this instance is being detached so that this instance
5141708Sstevel 	 * does not become a target for tunnel switch in future.
5151708Sstevel 	 */
5161708Sstevel 	mutex_enter(&iosram_mutex);
5171708Sstevel 	softp->state |= IOSRAM_STATE_DETACH;
5181708Sstevel 
5191708Sstevel 	/*
5201708Sstevel 	 * If this instance is currently the master or the target of the tunnel
5211708Sstevel 	 * switch, then we need to wait and switch tunnel, if necessary.
5221708Sstevel 	 */
5231708Sstevel 	if (iosram_master == softp || (softp->state & IOSRAM_STATE_TSWITCH)) {
5241708Sstevel 		mutex_exit(&iosram_mutex);
5251708Sstevel 		iosram_switchfrom(instance);
5261708Sstevel 		mutex_enter(&iosram_mutex);
5271708Sstevel 	}
5281708Sstevel 
5291708Sstevel 	/*
5301708Sstevel 	 * If the tunnel switch is in progress and we are the master or target
5311708Sstevel 	 * of tunnel relocation, then we can't detach this instance right now.
5321708Sstevel 	 */
5331708Sstevel 	if (softp->state & IOSRAM_STATE_TSWITCH) {
5341708Sstevel 		softp->state &= ~IOSRAM_STATE_DETACH;
5351708Sstevel 		mutex_exit(&iosram_mutex);
5361708Sstevel 		return (DDI_FAILURE);
5371708Sstevel 	}
5381708Sstevel 
5391708Sstevel 	/*
5401708Sstevel 	 * We can't allow master IOSRAM to be detached as we won't be able to
5411708Sstevel 	 * communicate otherwise.
5421708Sstevel 	 */
5431708Sstevel 	if (iosram_master == softp) {
5441708Sstevel 		softp->state &= ~IOSRAM_STATE_DETACH;
5451708Sstevel 		mutex_exit(&iosram_mutex);
5461708Sstevel 		return (DDI_FAILURE);
5471708Sstevel 	}
5481708Sstevel 
5491708Sstevel 	/*
5501708Sstevel 	 * Now remove our instance from the iosram_instances list.
5511708Sstevel 	 */
5521708Sstevel 	iosram_remove_instance(instance);
5531708Sstevel 	mutex_exit(&iosram_mutex);
5541708Sstevel 
5551708Sstevel 	/*
5561708Sstevel 	 * Instances should only ever be mapped if they are the master and/or
5571708Sstevel 	 * participating in a tunnel switch.  Neither should be the case here.
5581708Sstevel 	 */
5591708Sstevel 	ASSERT((softp->state & IOSRAM_STATE_MAPPED) == 0);
5601708Sstevel 
5611708Sstevel 	/*
5621708Sstevel 	 * Destroy per-instance mutexes
5631708Sstevel 	 */
5641708Sstevel 	mutex_destroy(&softp->intr_mutex);
5651708Sstevel 
5661708Sstevel 	ddi_remove_minor_node(dip, NULL);
5671708Sstevel 
5681708Sstevel 	/*
5691708Sstevel 	 * Finally remove our soft state structure
5701708Sstevel 	 */
5711708Sstevel 	ddi_soft_state_free(iosramsoft_statep, instance);
5721708Sstevel 
5731708Sstevel 	return (DDI_SUCCESS);
5741708Sstevel }
5751708Sstevel 
5761708Sstevel 
5771708Sstevel /* ARGSUSED0 */
5781708Sstevel static int
5791708Sstevel iosram_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
5801708Sstevel 		void **result)
5811708Sstevel {
5821708Sstevel 	dev_t			dev = (dev_t)arg;
5831708Sstevel 	struct iosramsoft	*softp;
5841708Sstevel 	int			instance, ret;
5851708Sstevel 
5861708Sstevel 	instance = getminor(dev);
5871708Sstevel 
5881708Sstevel 	IOSRAMLOG(2, "GETINFO: dip:%x instance %d dev:%x infocmd:%x\n",
5891708Sstevel 	    dip, instance, dev, infocmd);
5901708Sstevel 
5911708Sstevel 	switch (infocmd) {
5921708Sstevel 		case DDI_INFO_DEVT2DEVINFO:
5931708Sstevel 			softp = ddi_get_soft_state(iosramsoft_statep, instance);
5941708Sstevel 			if (softp == NULL) {
5951708Sstevel 				*result = NULL;
5961708Sstevel 				ret = DDI_FAILURE;
5971708Sstevel 			} else {
5981708Sstevel 				*result = softp->dip;
5991708Sstevel 				ret = DDI_SUCCESS;
6001708Sstevel 			}
6011708Sstevel 			break;
6021708Sstevel 		case DDI_INFO_DEVT2INSTANCE:
6031708Sstevel 			*result = (void *)(uintptr_t)instance;
6041708Sstevel 			ret = DDI_SUCCESS;
6051708Sstevel 			break;
6061708Sstevel 		default:
6071708Sstevel 			ret = DDI_FAILURE;
6081708Sstevel 			break;
6091708Sstevel 	}
6101708Sstevel 
6111708Sstevel 	return (ret);
6121708Sstevel }
6131708Sstevel 
6141708Sstevel 
6151708Sstevel /*ARGSUSED1*/
6161708Sstevel static int
6171708Sstevel iosram_open(dev_t *dev, int flag, int otype, cred_t *credp)
6181708Sstevel {
6191708Sstevel 	struct iosramsoft	*softp;
6201708Sstevel 	int			instance;
6211708Sstevel 
6221708Sstevel 	instance = getminor(*dev);
6231708Sstevel 	softp = ddi_get_soft_state(iosramsoft_statep, instance);
6241708Sstevel 
6251708Sstevel 	if (softp == NULL) {
6261708Sstevel 		return (ENXIO);
6271708Sstevel 	}
6281708Sstevel 
6291708Sstevel 	IOSRAMLOG(1, "OPEN: dev:%p otype:%x ... instance:%d softp:%p\n",
6301708Sstevel 	    *dev, otype, softp->instance, softp);
6311708Sstevel 
6321708Sstevel 	return (0);
6331708Sstevel }
6341708Sstevel 
6351708Sstevel 
6361708Sstevel /*ARGSUSED1*/
6371708Sstevel static int
6381708Sstevel iosram_close(dev_t dev, int flag, int otype, cred_t *credp)
6391708Sstevel {
6401708Sstevel 	struct iosramsoft	*softp;
6411708Sstevel 	int			instance;
6421708Sstevel 
6431708Sstevel 	instance = getminor(dev);
6441708Sstevel 	softp = ddi_get_soft_state(iosramsoft_statep, instance);
6451708Sstevel 	if (softp == NULL) {
6461708Sstevel 		return (ENXIO);
6471708Sstevel 	}
6481708Sstevel 
6491708Sstevel 	IOSRAMLOG(1, "CLOSE: dev:%p otype:%x ... instance:%d softp:%p\n",
6501708Sstevel 	    dev, otype, softp->instance, softp);
6511708Sstevel 
6521708Sstevel 	return (0);
6531708Sstevel }
6541708Sstevel 
6551708Sstevel 
6561708Sstevel int
6571708Sstevel iosram_rd(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
6581708Sstevel {
6591708Sstevel 	iosram_chunk_t		*chunkp;
6601708Sstevel 	uint32_t		chunk_len;
6611708Sstevel 	uint8_t			*iosramp;
6621708Sstevel 	ddi_acc_handle_t	handle;
6631708Sstevel 	int			boff;
6641708Sstevel 	union {
6651708Sstevel 		uchar_t	cbuf[UINT32SZ];
6661708Sstevel 		uint32_t  data;
6671708Sstevel 	} word;
6681708Sstevel 
6691708Sstevel 	int			error = 0;
6701708Sstevel 	uint8_t			*buf = (uint8_t *)dptr;
6711708Sstevel 
6721708Sstevel 	/*
6731708Sstevel 	 * We try to read from the IOSRAM using double word or word access
6741708Sstevel 	 * provided both "off" and "buf" are (or can be) double word or word
6751708Sstevel 	 * aligned.  Othewise, we try to align the "off" to a word boundary and
6761708Sstevel 	 * then try to read data from the IOSRAM using word access, but store it
6771708Sstevel 	 * into buf buffer using byte access.
6781708Sstevel 	 *
6791708Sstevel 	 * If the leading/trailing portion of the IOSRAM data is not word
6801708Sstevel 	 * aligned, it will always be copied using byte access.
6811708Sstevel 	 */
6821708Sstevel 	IOSRAMLOG(1, "RD: key: 0x%x off:%x len:%x buf:%p\n",
6831708Sstevel 	    key, off, len, buf);
6841708Sstevel 
6851708Sstevel 	/*
6861708Sstevel 	 * Acquire lock and look for the requested chunk.  If it exists, make
6871708Sstevel 	 * sure the requested read is within the chunk's bounds and no tunnel
6881708Sstevel 	 * switch is active.
6891708Sstevel 	 */
6901708Sstevel 	mutex_enter(&iosram_mutex);
6911708Sstevel 	chunkp = iosram_find_chunk(key);
6921708Sstevel 	chunk_len = (chunkp != NULL) ? chunkp->toc_data.len : 0;
6931708Sstevel 
6941708Sstevel 	if (iosram_master == NULL) {
6951708Sstevel 		error = EIO;
6961708Sstevel 	} else if (chunkp == NULL) {
6971708Sstevel 		error = EINVAL;
6981708Sstevel 	} else if ((off >= chunk_len) || (len > chunk_len) ||
6991708Sstevel 	    ((off + len) > chunk_len)) {
7001708Sstevel 		error = EMSGSIZE;
7011708Sstevel 	} else if (iosram_tswitch_active) {
7021708Sstevel 		error = EAGAIN;
7031708Sstevel 	}
7041708Sstevel 
7051708Sstevel 	if (error) {
7061708Sstevel 		mutex_exit(&iosram_mutex);
7071708Sstevel 		return (error);
7081708Sstevel 	}
7091708Sstevel 
7101708Sstevel 	/*
7111708Sstevel 	 * Bump reference count to indicate #thread accessing IOSRAM and release
7121708Sstevel 	 * the lock.
7131708Sstevel 	 */
7141708Sstevel 	iosram_rw_active++;
7151708Sstevel #if defined(DEBUG)
7161708Sstevel 	if (iosram_rw_active > iosram_rw_active_max) {
7171708Sstevel 		iosram_rw_active_max = iosram_rw_active;
7181708Sstevel 	}
7191708Sstevel #endif
7201708Sstevel 	mutex_exit(&iosram_mutex);
7211708Sstevel 
7221708Sstevel 	IOSRAM_STAT(read);
7231708Sstevel 	IOSRAM_STAT_ADD(bread, len);
7241708Sstevel 
7251708Sstevel 	/* Get starting address and map handle */
7261708Sstevel 	iosramp = chunkp->basep + off;
7271708Sstevel 	handle = iosram_handle;
7281708Sstevel 
7291708Sstevel 	/*
7301708Sstevel 	 * Align the off to word boundary and then try reading/writing data
7311708Sstevel 	 * using double word or word access.
7321708Sstevel 	 */
7331708Sstevel 	if ((boff = ((uintptr_t)iosramp & (UINT32SZ - 1))) != 0) {
7341708Sstevel 		int	cnt = UINT32SZ - boff;
7351708Sstevel 
7361708Sstevel 		if (cnt > len) {
7371708Sstevel 			cnt = len;
7381708Sstevel 		}
7391708Sstevel 		IOSRAMLOG(2,
7401708Sstevel 		    "RD: align rep_get8(buf:%p sramp:%p cnt:%x) len:%x\n",
7411708Sstevel 		    buf, iosramp, cnt, len);
7421708Sstevel 		ddi_rep_get8(handle, buf, iosramp, cnt, DDI_DEV_AUTOINCR);
7431708Sstevel 		buf += cnt;
7441708Sstevel 		iosramp += cnt;
7451708Sstevel 		len -= cnt;
7461708Sstevel 	}
7471708Sstevel 
7481708Sstevel 	if ((len >= UINT64SZ) &&
7491708Sstevel 	    ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT64SZ - 1)) == 0)) {
7501708Sstevel 		/*
7511708Sstevel 		 * Both source and destination are double word aligned
7521708Sstevel 		 */
7531708Sstevel 		int cnt = len/UINT64SZ;
7541708Sstevel 
7551708Sstevel 		IOSRAMLOG(2,
7561708Sstevel 		    "RD: rep_get64(buf:%p sramp:%p cnt:%x) len:%x\n",
7571708Sstevel 		    buf, iosramp, cnt, len);
7581708Sstevel 		ddi_rep_get64(handle, (uint64_t *)buf, (uint64_t *)iosramp,
7591708Sstevel 		    cnt, DDI_DEV_AUTOINCR);
7601708Sstevel 		iosramp += cnt * UINT64SZ;
7611708Sstevel 		buf += cnt * UINT64SZ;
7621708Sstevel 		len -= cnt * UINT64SZ;
7631708Sstevel 
7641708Sstevel 		/*
7651708Sstevel 		 * read remaining data using word and byte access
7661708Sstevel 		 */
7671708Sstevel 		if (len >= UINT32SZ) {
7681708Sstevel 			IOSRAMLOG(2,
7691708Sstevel 			    "RD: get32(buf:%p sramp:%p) len:%x\n",
7701708Sstevel 			    buf, iosramp, len, NULL);
7711708Sstevel 			*(uint32_t *)buf = ddi_get32(handle,
7721708Sstevel 			    (uint32_t *)iosramp);
7731708Sstevel 			iosramp += UINT32SZ;
7741708Sstevel 			buf += UINT32SZ;
7751708Sstevel 			len -= UINT32SZ;
7761708Sstevel 		}
7771708Sstevel 
7781708Sstevel 		if (len != 0) {
779*7656SSherry.Moore@Sun.COM 			ddi_rep_get8(handle, buf, iosramp, len,
780*7656SSherry.Moore@Sun.COM 			    DDI_DEV_AUTOINCR);
7811708Sstevel 		}
7821708Sstevel 	} else if ((len >= UINT32SZ) &&
7831708Sstevel 	    ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT32SZ - 1)) == 0)) {
7841708Sstevel 		/*
7851708Sstevel 		 * Both source and destination are word aligned
7861708Sstevel 		 */
7871708Sstevel 		int cnt = len/UINT32SZ;
7881708Sstevel 
7891708Sstevel 		IOSRAMLOG(2,
7901708Sstevel 		    "RD: rep_get32(buf:%p sramp:%p cnt:%x) len:%x\n",
7911708Sstevel 		    buf, iosramp, cnt, len);
7921708Sstevel 		ddi_rep_get32(handle, (uint32_t *)buf, (uint32_t *)iosramp,
7931708Sstevel 		    cnt, DDI_DEV_AUTOINCR);
7941708Sstevel 		iosramp += cnt * UINT32SZ;
7951708Sstevel 		buf += cnt * UINT32SZ;
7961708Sstevel 		len -= cnt * UINT32SZ;
7971708Sstevel 
7981708Sstevel 		/*
7991708Sstevel 		 * copy the remainder using byte access
8001708Sstevel 		 */
8011708Sstevel 		if (len != 0) {
802*7656SSherry.Moore@Sun.COM 			ddi_rep_get8(handle, buf, iosramp, len,
803*7656SSherry.Moore@Sun.COM 			    DDI_DEV_AUTOINCR);
8041708Sstevel 		}
8051708Sstevel 	} else if (len != 0) {
8061708Sstevel 		/*
8071708Sstevel 		 * We know that the "off" (i.e. iosramp) is at least word
8081708Sstevel 		 * aligned. We need to read IOSRAM word at a time and copy it
8091708Sstevel 		 * byte at a time.
8101708Sstevel 		 */
8111708Sstevel 		ASSERT(((uintptr_t)iosramp & (UINT32SZ - 1)) == 0);
8121708Sstevel 
8131708Sstevel 		IOSRAMLOG(2,
8141708Sstevel 		    "RD: unaligned get32(buf:%p sramp:%p) len:%x\n",
8151708Sstevel 		    buf, iosramp, len, NULL);
8161708Sstevel 		for (; len >= UINT32SZ; len -= UINT32SZ, iosramp += UINT32SZ) {
8171708Sstevel 			word.data =  ddi_get32(handle, (uint32_t *)iosramp);
8181708Sstevel 			*buf++ = word.cbuf[0];
8191708Sstevel 			*buf++ = word.cbuf[1];
8201708Sstevel 			*buf++ = word.cbuf[2];
8211708Sstevel 			*buf++ = word.cbuf[3];
8221708Sstevel 		}
8231708Sstevel 
8241708Sstevel 		/*
8251708Sstevel 		 * copy the remaining data using byte access
8261708Sstevel 		 */
8271708Sstevel 		if (len != 0) {
8281708Sstevel 			ddi_rep_get8(handle, buf, iosramp, len,
8291708Sstevel 			    DDI_DEV_AUTOINCR);
8301708Sstevel 		}
8311708Sstevel 	}
8321708Sstevel 
8331708Sstevel 	/*
8341708Sstevel 	 * Reacquire mutex lock, decrement refcnt and if refcnt is 0 and any
8351708Sstevel 	 * threads are waiting for r/w activity to complete, wake them up.
8361708Sstevel 	 */
8371708Sstevel 	mutex_enter(&iosram_mutex);
8381708Sstevel 	ASSERT(iosram_rw_active > 0);
8391708Sstevel 
8401708Sstevel 	if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
8411708Sstevel 		iosram_rw_wakeup = 0;
8421708Sstevel 		cv_broadcast(&iosram_rw_wait);
8431708Sstevel 	}
8441708Sstevel 	mutex_exit(&iosram_mutex);
8451708Sstevel 
8461708Sstevel 	return (error);
8471708Sstevel }
8481708Sstevel 
8491708Sstevel 
8501708Sstevel /*
8511708Sstevel  * _iosram_write(key, off, len, dptr, force)
8521708Sstevel  *	Internal common routine to write to the IOSRAM.
8531708Sstevel  */
8541708Sstevel static int
8551708Sstevel _iosram_write(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr, int force)
8561708Sstevel {
8571708Sstevel 	iosram_chunk_t		*chunkp;
8581708Sstevel 	uint32_t		chunk_len;
8591708Sstevel 	uint8_t			*iosramp;
8601708Sstevel 	ddi_acc_handle_t	handle;
8611708Sstevel 	int			boff;
8621708Sstevel 	union {
8631708Sstevel 		uint8_t	cbuf[UINT32SZ];
8641708Sstevel 		uint32_t data;
8651708Sstevel 	} word;
8661708Sstevel 
8671708Sstevel 	int			error = 0;
8681708Sstevel 	uint8_t			*buf = (uint8_t *)dptr;
8691708Sstevel 
8701708Sstevel 	/*
8711708Sstevel 	 * We try to write to the IOSRAM using double word or word access
8721708Sstevel 	 * provided both "off" and "buf" are (or can be) double word or word
8731708Sstevel 	 * aligned.  Othewise, we try to align the "off" to a word boundary and
8741708Sstevel 	 * then try to write data to the IOSRAM using word access, but read data
8751708Sstevel 	 * from the buf buffer using byte access.
8761708Sstevel 	 *
8771708Sstevel 	 * If the leading/trailing portion of the IOSRAM data is not word
8781708Sstevel 	 * aligned, it will always be written using byte access.
8791708Sstevel 	 */
8801708Sstevel 	IOSRAMLOG(1, "WR: key: 0x%x off:%x len:%x buf:%p\n",
8811708Sstevel 	    key, off, len, buf);
8821708Sstevel 
8831708Sstevel 	/*
8841708Sstevel 	 * Acquire lock and look for the requested chunk.  If it exists, make
8851708Sstevel 	 * sure the requested write is within the chunk's bounds and no tunnel
8861708Sstevel 	 * switch is active.
8871708Sstevel 	 */
8881708Sstevel 	mutex_enter(&iosram_mutex);
8891708Sstevel 	chunkp = iosram_find_chunk(key);
8901708Sstevel 	chunk_len = (chunkp != NULL) ? chunkp->toc_data.len : 0;
8911708Sstevel 
8921708Sstevel 	if (iosram_master == NULL) {
8931708Sstevel 		error = EIO;
8941708Sstevel 	} else if (chunkp == NULL) {
8951708Sstevel 		error = EINVAL;
8961708Sstevel 	} else if ((off >= chunk_len) || (len > chunk_len) ||
8971708Sstevel 	    ((off+len) > chunk_len)) {
8981708Sstevel 		error = EMSGSIZE;
8991708Sstevel 	} else if (iosram_tswitch_active && !force) {
9001708Sstevel 		error = EAGAIN;
9011708Sstevel 	}
9021708Sstevel 
9031708Sstevel 	if (error) {
9041708Sstevel 		mutex_exit(&iosram_mutex);
9051708Sstevel 		return (error);
9061708Sstevel 	}
9071708Sstevel 
9081708Sstevel 	/*
9091708Sstevel 	 * If this is a forced write and there's a tunnel switch in progress,
9101708Sstevel 	 * abort the switch.
9111708Sstevel 	 */
9121708Sstevel 	if (iosram_tswitch_active && force) {
9131708Sstevel 		cmn_err(CE_NOTE, "!iosram: Aborting tswitch on force_write");
9141708Sstevel 		iosram_abort_tswitch();
9151708Sstevel 	}
9161708Sstevel 
9171708Sstevel 	/*
9181708Sstevel 	 * Bump reference count to indicate #thread accessing IOSRAM
9191708Sstevel 	 * and release the lock.
9201708Sstevel 	 */
9211708Sstevel 	iosram_rw_active++;
9221708Sstevel #if defined(DEBUG)
9231708Sstevel 	if (iosram_rw_active > iosram_rw_active_max) {
9241708Sstevel 		iosram_rw_active_max = iosram_rw_active;
9251708Sstevel 	}
9261708Sstevel #endif
9271708Sstevel 	mutex_exit(&iosram_mutex);
9281708Sstevel 
9291708Sstevel 
9301708Sstevel 	IOSRAM_STAT(write);
9311708Sstevel 	IOSRAM_STAT_ADD(bwrite, len);
9321708Sstevel 
9331708Sstevel 	/* Get starting address and map handle */
9341708Sstevel 	iosramp = chunkp->basep + off;
9351708Sstevel 	handle = iosram_handle;
9361708Sstevel 
9371708Sstevel 	/*
9381708Sstevel 	 * Align the off to word boundary and then try reading/writing
9391708Sstevel 	 * data using double word or word access.
9401708Sstevel 	 */
9411708Sstevel 	if ((boff = ((uintptr_t)iosramp & (UINT32SZ - 1))) != 0) {
9421708Sstevel 		int	cnt = UINT32SZ - boff;
9431708Sstevel 
9441708Sstevel 		if (cnt > len) {
9451708Sstevel 			cnt = len;
9461708Sstevel 		}
9471708Sstevel 		IOSRAMLOG(2,
9481708Sstevel 		    "WR: align rep_put8(buf:%p sramp:%p cnt:%x) len:%x\n",
9491708Sstevel 		    buf, iosramp, cnt, len);
9501708Sstevel 		ddi_rep_put8(handle, buf, iosramp, cnt, DDI_DEV_AUTOINCR);
9511708Sstevel 		buf += cnt;
9521708Sstevel 		iosramp += cnt;
9531708Sstevel 		len -= cnt;
9541708Sstevel 	}
9551708Sstevel 
9561708Sstevel 	if ((len >= UINT64SZ) &&
9571708Sstevel 	    ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT64SZ - 1)) == 0)) {
9581708Sstevel 		/*
9591708Sstevel 		 * Both source and destination are double word aligned
9601708Sstevel 		 */
9611708Sstevel 		int cnt = len/UINT64SZ;
9621708Sstevel 
9631708Sstevel 		IOSRAMLOG(2,
9641708Sstevel 		    "WR: rep_put64(buf:%p sramp:%p cnt:%x) len:%x\n",
9651708Sstevel 		    buf, iosramp, cnt, len);
9661708Sstevel 		ddi_rep_put64(handle, (uint64_t *)buf, (uint64_t *)iosramp,
9671708Sstevel 		    cnt, DDI_DEV_AUTOINCR);
9681708Sstevel 		iosramp += cnt * UINT64SZ;
9691708Sstevel 		buf += cnt * UINT64SZ;
9701708Sstevel 		len -= cnt * UINT64SZ;
9711708Sstevel 
9721708Sstevel 		/*
9731708Sstevel 		 * Copy the remaining data using word & byte access
9741708Sstevel 		 */
9751708Sstevel 		if (len >= UINT32SZ) {
9761708Sstevel 			IOSRAMLOG(2,
9771708Sstevel 			    "WR: put32(buf:%p sramp:%p) len:%x\n", buf, iosramp,
9781708Sstevel 			    len, NULL);
9791708Sstevel 			ddi_put32(handle, (uint32_t *)iosramp,
9801708Sstevel 			    *(uint32_t *)buf);
9811708Sstevel 			iosramp += UINT32SZ;
9821708Sstevel 			buf += UINT32SZ;
9831708Sstevel 			len -= UINT32SZ;
9841708Sstevel 		}
9851708Sstevel 
9861708Sstevel 		if (len != 0) {
9871708Sstevel 			ddi_rep_put8(handle, buf, iosramp, len,
9881708Sstevel 			    DDI_DEV_AUTOINCR);
9891708Sstevel 		}
9901708Sstevel 	} else if ((len >= UINT32SZ) &&
9911708Sstevel 	    ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT32SZ - 1)) == 0)) {
9921708Sstevel 		/*
9931708Sstevel 		 * Both source and destination are word aligned
9941708Sstevel 		 */
9951708Sstevel 		int cnt = len/UINT32SZ;
9961708Sstevel 
9971708Sstevel 		IOSRAMLOG(2,
9981708Sstevel 		    "WR: rep_put32(buf:%p sramp:%p cnt:%x) len:%x\n",
9991708Sstevel 		    buf, iosramp, cnt, len);
10001708Sstevel 		ddi_rep_put32(handle, (uint32_t *)buf, (uint32_t *)iosramp,
10011708Sstevel 		    cnt, DDI_DEV_AUTOINCR);
10021708Sstevel 		iosramp += cnt * UINT32SZ;
10031708Sstevel 		buf += cnt * UINT32SZ;
10041708Sstevel 		len -= cnt * UINT32SZ;
10051708Sstevel 
10061708Sstevel 		/*
10071708Sstevel 		 * copy the remainder using byte access
10081708Sstevel 		 */
10091708Sstevel 		if (len != 0) {
10101708Sstevel 			ddi_rep_put8(handle, buf, iosramp, len,
10111708Sstevel 			    DDI_DEV_AUTOINCR);
10121708Sstevel 		}
10131708Sstevel 	} else if (len != 0) {
10141708Sstevel 		/*
10151708Sstevel 		 * We know that the "off" is at least word aligned. We
10161708Sstevel 		 * need to read data from buf buffer byte at a time, and
10171708Sstevel 		 * write it to the IOSRAM word at a time.
10181708Sstevel 		 */
10191708Sstevel 
10201708Sstevel 		ASSERT(((uintptr_t)iosramp & (UINT32SZ - 1)) == 0);
10211708Sstevel 
10221708Sstevel 		IOSRAMLOG(2,
10231708Sstevel 		    "WR: unaligned put32(buf:%p sramp:%p) len:%x\n",
10241708Sstevel 		    buf, iosramp, len, NULL);
10251708Sstevel 		for (; len >= UINT32SZ; len -= UINT32SZ, iosramp += UINT32SZ) {
10261708Sstevel 			word.cbuf[0] = *buf++;
10271708Sstevel 			word.cbuf[1] = *buf++;
10281708Sstevel 			word.cbuf[2] = *buf++;
10291708Sstevel 			word.cbuf[3] = *buf++;
10301708Sstevel 			ddi_put32(handle, (uint32_t *)iosramp, word.data);
10311708Sstevel 		}
10321708Sstevel 
10331708Sstevel 		/*
10341708Sstevel 		 * copy the remaining data using byte access
10351708Sstevel 		 */
10361708Sstevel 		if (len != 0) {
10371708Sstevel 			ddi_rep_put8(handle, buf, iosramp,
10381708Sstevel 			    len, DDI_DEV_AUTOINCR);
10391708Sstevel 		}
10401708Sstevel 	}
10411708Sstevel 
10421708Sstevel 	/*
10431708Sstevel 	 * Reacquire mutex lock, decrement refcnt and if refcnt is 0 and
10441708Sstevel 	 * any threads are waiting for r/w activity to complete, wake them up.
10451708Sstevel 	 */
10461708Sstevel 	mutex_enter(&iosram_mutex);
10471708Sstevel 	ASSERT(iosram_rw_active > 0);
10481708Sstevel 
10491708Sstevel 	if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
10501708Sstevel 		iosram_rw_wakeup = 0;
10511708Sstevel 		cv_broadcast(&iosram_rw_wait);
10521708Sstevel 	}
10531708Sstevel 	mutex_exit(&iosram_mutex);
10541708Sstevel 
10551708Sstevel 	return (error);
10561708Sstevel }
10571708Sstevel 
10581708Sstevel 
10591708Sstevel int
10601708Sstevel iosram_force_write(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
10611708Sstevel {
10621708Sstevel 	return (_iosram_write(key, off, len, dptr, 1 /* force */));
10631708Sstevel }
10641708Sstevel 
10651708Sstevel 
10661708Sstevel int
10671708Sstevel iosram_wr(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
10681708Sstevel {
10691708Sstevel 	return (_iosram_write(key, off, len, dptr, 0));
10701708Sstevel }
10711708Sstevel 
10721708Sstevel 
10731708Sstevel /*
10741708Sstevel  * iosram_register(key, handler, arg)
10751708Sstevel  *	Register a handler and an arg for the specified chunk.  This handler
10761708Sstevel  *	will be invoked when an interrupt is received from the other side and
10771708Sstevel  *	the int_pending flag for the corresponding key is marked
10781708Sstevel  *	IOSRAM_INT_TO_DOM.
10791708Sstevel  */
10801708Sstevel /* ARGSUSED */
10811708Sstevel int
10821708Sstevel iosram_register(uint32_t key, void (*handler)(), void *arg)
10831708Sstevel {
10841708Sstevel 	struct iosram_chunk	*chunkp;
10851708Sstevel 	int			error = 0;
10861708Sstevel 
10871708Sstevel 	/*
10881708Sstevel 	 * Acquire lock and look for the requested chunk.  If it exists, and no
10891708Sstevel 	 * other callback is registered, proceed with the registration.
10901708Sstevel 	 */
10911708Sstevel 	mutex_enter(&iosram_mutex);
10921708Sstevel 	chunkp = iosram_find_chunk(key);
10931708Sstevel 
10941708Sstevel 	if (iosram_master == NULL) {
10951708Sstevel 		error = EIO;
10961708Sstevel 	} else if (chunkp == NULL) {
10971708Sstevel 		error = EINVAL;
10981708Sstevel 	} else if (chunkp->cback.handler != NULL) {
10991708Sstevel 		error = EBUSY;
11001708Sstevel 	} else {
11011708Sstevel 		chunkp->cback.busy = 0;
11021708Sstevel 		chunkp->cback.unregister = 0;
11031708Sstevel 		chunkp->cback.handler = handler;
11041708Sstevel 		chunkp->cback.arg = arg;
11051708Sstevel 	}
11061708Sstevel 	mutex_exit(&iosram_mutex);
11071708Sstevel 
11081708Sstevel 	IOSRAMLOG(1, "REG: key: 0x%x hdlr:%p arg:%p error:%d\n",
11091708Sstevel 	    key, handler, arg, error);
11101708Sstevel 
11111708Sstevel 	return (error);
11121708Sstevel }
11131708Sstevel 
11141708Sstevel 
11151708Sstevel /*
11161708Sstevel  * iosram_unregister()
11171708Sstevel  *	Unregister handler associated with the specified chunk.
11181708Sstevel  */
11191708Sstevel int
11201708Sstevel iosram_unregister(uint32_t key)
11211708Sstevel {
11221708Sstevel 	struct iosram_chunk	*chunkp;
11231708Sstevel 	int			error = 0;
11241708Sstevel 
11251708Sstevel 	/*
11261708Sstevel 	 * Acquire lock and look for the requested chunk.  If it exists and has
11271708Sstevel 	 * a callback registered, unregister it.
11281708Sstevel 	 */
11291708Sstevel 	mutex_enter(&iosram_mutex);
11301708Sstevel 	chunkp = iosram_find_chunk(key);
11311708Sstevel 
11321708Sstevel 	if (iosram_master == NULL) {
11331708Sstevel 		error = EIO;
11341708Sstevel 	} else if (chunkp == NULL) {
11351708Sstevel 		error = EINVAL;
11361708Sstevel 	} else if (chunkp->cback.busy) {
11371708Sstevel 		/*
11381708Sstevel 		 * If the handler is already busy (being invoked), then we flag
11391708Sstevel 		 * it so it will be unregistered after the invocation completes.
11401708Sstevel 		 */
11411708Sstevel 		DPRINTF(1, ("IOSRAM(%d): unregister: delaying unreg k:0x%08x\n",
11421708Sstevel 		    iosram_master->instance, key));
11431708Sstevel 		chunkp->cback.unregister = 1;
11441708Sstevel 	} else if (chunkp->cback.handler != NULL) {
11451708Sstevel 		chunkp->cback.handler = NULL;
11461708Sstevel 		chunkp->cback.arg = NULL;
11471708Sstevel 	}
11481708Sstevel 	mutex_exit(&iosram_mutex);
11491708Sstevel 
11501708Sstevel 	IOSRAMLOG(1, "UNREG: key:%x error:%d\n", key, error, NULL, NULL);
11511708Sstevel 	return (error);
11521708Sstevel }
11531708Sstevel 
11541708Sstevel 
11551708Sstevel /*
11561708Sstevel  * iosram_get_flag():
11571708Sstevel  *	Get data_valid and/or int_pending flags associated with the
11581708Sstevel  *	specified key.
11591708Sstevel  */
11601708Sstevel int
11611708Sstevel iosram_get_flag(uint32_t key, uint8_t *data_valid, uint8_t *int_pending)
11621708Sstevel {
11631708Sstevel 	iosram_chunk_t	*chunkp;
11641708Sstevel 	iosram_flags_t	flags;
11651708Sstevel 	int		error = 0;
11661708Sstevel 
11671708Sstevel 	/*
11681708Sstevel 	 * Acquire lock and look for the requested chunk.  If it exists, and no
11691708Sstevel 	 * tunnel switch is in progress, read the chunk's flags.
11701708Sstevel 	 */
11711708Sstevel 	mutex_enter(&iosram_mutex);
11721708Sstevel 	chunkp = iosram_find_chunk(key);
11731708Sstevel 
11741708Sstevel 	if (iosram_master == NULL) {
11751708Sstevel 		error = EIO;
11761708Sstevel 	} else if (chunkp == NULL) {
11771708Sstevel 		error = EINVAL;
11781708Sstevel 	} else if (iosram_tswitch_active) {
11791708Sstevel 		error = EAGAIN;
11801708Sstevel 	} else {
11811708Sstevel 		IOSRAM_STAT(getflag);
11821708Sstevel 
11831708Sstevel 		/*
11841708Sstevel 		 * Read the flags
11851708Sstevel 		 */
11861708Sstevel 		ddi_rep_get8(iosram_handle, (uint8_t *)&flags,
11871708Sstevel 		    (uint8_t *)(chunkp->flagsp), sizeof (iosram_flags_t),
11881708Sstevel 		    DDI_DEV_AUTOINCR);
11891708Sstevel 
11901708Sstevel 		/*
11911708Sstevel 		 * Get each flag value that the caller is interested in.
11921708Sstevel 		 */
11931708Sstevel 		if (data_valid != NULL) {
11941708Sstevel 			*data_valid = flags.data_valid;
11951708Sstevel 		}
11961708Sstevel 
11971708Sstevel 		if (int_pending != NULL) {
11981708Sstevel 			*int_pending = flags.int_pending;
11991708Sstevel 		}
12001708Sstevel 	}
12011708Sstevel 	mutex_exit(&iosram_mutex);
12021708Sstevel 
12031708Sstevel 	IOSRAMLOG(1, "GetFlag key:%x data_valid:%x int_pending:%x error:%d\n",
12041708Sstevel 	    key, flags.data_valid, flags.int_pending, error);
12051708Sstevel 	return (error);
12061708Sstevel }
12071708Sstevel 
12081708Sstevel 
12091708Sstevel /*
12101708Sstevel  * iosram_set_flag():
12111708Sstevel  *	Set data_valid and int_pending flags associated with the specified key.
12121708Sstevel  */
12131708Sstevel int
12141708Sstevel iosram_set_flag(uint32_t key, uint8_t data_valid, uint8_t int_pending)
12151708Sstevel {
12161708Sstevel 	iosram_chunk_t	*chunkp;
12171708Sstevel 	iosram_flags_t	flags;
12181708Sstevel 	int		error = 0;
12191708Sstevel 
12201708Sstevel 	/*
12211708Sstevel 	 * Acquire lock and look for the requested chunk.  If it exists, and no
12221708Sstevel 	 * tunnel switch is in progress, write the chunk's flags.
12231708Sstevel 	 */
12241708Sstevel 	mutex_enter(&iosram_mutex);
12251708Sstevel 	chunkp = iosram_find_chunk(key);
12261708Sstevel 
12271708Sstevel 	if (iosram_master == NULL) {
12281708Sstevel 		error = EIO;
12291708Sstevel 	} else if ((chunkp == NULL) ||
12301708Sstevel 	    ((data_valid != IOSRAM_DATA_INVALID) &&
12311708Sstevel 	    (data_valid != IOSRAM_DATA_VALID)) ||
12321708Sstevel 	    ((int_pending != IOSRAM_INT_NONE) &&
12331708Sstevel 	    (int_pending != IOSRAM_INT_TO_SSC) &&
12341708Sstevel 	    (int_pending != IOSRAM_INT_TO_DOM))) {
12351708Sstevel 		error = EINVAL;
12361708Sstevel 	} else if (iosram_tswitch_active) {
12371708Sstevel 		error = EAGAIN;
12381708Sstevel 	} else {
12391708Sstevel 		IOSRAM_STAT(setflag);
12401708Sstevel 		flags.data_valid = data_valid;
12411708Sstevel 		flags.int_pending = int_pending;
12421708Sstevel 		ddi_rep_put8(iosram_handle, (uint8_t *)&flags,
12431708Sstevel 		    (uint8_t *)(chunkp->flagsp), sizeof (iosram_flags_t),
12441708Sstevel 		    DDI_DEV_AUTOINCR);
12451708Sstevel 	}
12461708Sstevel 	mutex_exit(&iosram_mutex);
12471708Sstevel 
12481708Sstevel 	IOSRAMLOG(1, "SetFlag key:%x data_valid:%x int_pending:%x error:%d\n",
12491708Sstevel 	    key, flags.data_valid, flags.int_pending, error);
12501708Sstevel 	return (error);
12511708Sstevel }
12521708Sstevel 
12531708Sstevel 
12541708Sstevel /*
12551708Sstevel  * iosram_ctrl()
12561708Sstevel  *	This function provides access to a variety of services not available
12571708Sstevel  *	through the basic API.
12581708Sstevel  */
12591708Sstevel int
12601708Sstevel iosram_ctrl(uint32_t key, uint32_t cmd, void *arg)
12611708Sstevel {
12621708Sstevel 	struct iosram_chunk	*chunkp;
12631708Sstevel 	int			error = 0;
12641708Sstevel 
12651708Sstevel 	/*
12661708Sstevel 	 * Acquire lock and do some argument sanity checking.
12671708Sstevel 	 */
12681708Sstevel 	mutex_enter(&iosram_mutex);
12691708Sstevel 	chunkp = iosram_find_chunk(key);
12701708Sstevel 
12711708Sstevel 	if (iosram_master == NULL) {
12721708Sstevel 		error = EIO;
12731708Sstevel 	} else if (chunkp == NULL) {
12741708Sstevel 		error = EINVAL;
12751708Sstevel 	}
12761708Sstevel 
12771708Sstevel 	if (error != 0) {
12781708Sstevel 		mutex_exit(&iosram_mutex);
12791708Sstevel 		return (error);
12801708Sstevel 	}
12811708Sstevel 
12821708Sstevel 	/*
12831708Sstevel 	 * Arguments seem okay so far, so process the command.
12841708Sstevel 	 */
12851708Sstevel 	switch (cmd) {
12861708Sstevel 		case IOSRAM_CMD_CHUNKLEN:
12871708Sstevel 			/*
12881708Sstevel 			 * Return the length of the chunk indicated by the key.
12891708Sstevel 			 */
12901708Sstevel 			if (arg == NULL) {
12911708Sstevel 				error = EINVAL;
12921708Sstevel 				break;
12931708Sstevel 			}
12941708Sstevel 
12951708Sstevel 			*(uint32_t *)arg = chunkp->toc_data.len;
12961708Sstevel 			break;
12971708Sstevel 
12981708Sstevel 		default:
12991708Sstevel 			error = ENOTSUP;
13001708Sstevel 			break;
13011708Sstevel 	}
13021708Sstevel 
13031708Sstevel 	mutex_exit(&iosram_mutex);
13041708Sstevel 	return (error);
13051708Sstevel }
13061708Sstevel 
13071708Sstevel 
13081708Sstevel /*
13091708Sstevel  * iosram_hdr_ctrl()
13101708Sstevel  *	This function provides an interface for the Mailbox Protocol
13111708Sstevel  *	implementation to use when interacting with the IOSRAM header.
13121708Sstevel  */
13131708Sstevel int
13141708Sstevel iosram_hdr_ctrl(uint32_t cmd, void *arg)
13151708Sstevel {
13161708Sstevel 	int	error = 0;
13171708Sstevel 
13181708Sstevel 	/*
13191708Sstevel 	 * Acquire lock and do some argument sanity checking.
13201708Sstevel 	 */
13211708Sstevel 	mutex_enter(&iosram_mutex);
13221708Sstevel 
13231708Sstevel 	if (iosram_master == NULL) {
13241708Sstevel 		error = EIO;
13251708Sstevel 	}
13261708Sstevel 
13271708Sstevel 	if (error != 0) {
13281708Sstevel 		mutex_exit(&iosram_mutex);
13291708Sstevel 		return (error);
13301708Sstevel 	}
13311708Sstevel 
13321708Sstevel 	switch (cmd) {
13331708Sstevel 		case IOSRAM_HDRCMD_GET_SMS_MBOX_VER:
13341708Sstevel 			/*
13351708Sstevel 			 * Return the value of the sms_mbox_version field.
13361708Sstevel 			 */
13371708Sstevel 			if (arg == NULL) {
13381708Sstevel 				error = EINVAL;
13391708Sstevel 				break;
13401708Sstevel 			}
13411708Sstevel 
13421708Sstevel 			*(uint32_t *)arg = IOSRAM_GET_HDRFIELD32(iosram_master,
13431708Sstevel 			    sms_mbox_version);
13441708Sstevel 			break;
13451708Sstevel 
13461708Sstevel 		case IOSRAM_HDRCMD_SET_OS_MBOX_VER:
13471708Sstevel 			/*
13481708Sstevel 			 * Set the value of the os_mbox_version field.
13491708Sstevel 			 */
13501708Sstevel 			IOSRAM_SET_HDRFIELD32(iosram_master, os_mbox_version,
13511708Sstevel 			    (uint32_t)(uintptr_t)arg);
13521708Sstevel 			IOSRAM_SET_HDRFIELD32(iosram_master, os_change_mask,
13531708Sstevel 			    IOSRAM_HDRFIELD_OS_MBOX_VER);
13541708Sstevel 			iosram_send_intr();
13551708Sstevel 			break;
13561708Sstevel 
13571708Sstevel 		case IOSRAM_HDRCMD_REG_CALLBACK:
13581708Sstevel 			iosram_hdrchange_handler = (void (*)())arg;
13591708Sstevel 			break;
13601708Sstevel 
13611708Sstevel 		default:
13621708Sstevel 			error = ENOTSUP;
13631708Sstevel 			break;
13641708Sstevel 	}
13651708Sstevel 
13661708Sstevel 	mutex_exit(&iosram_mutex);
13671708Sstevel 	return (error);
13681708Sstevel }
13691708Sstevel 
13701708Sstevel 
13711708Sstevel /*
13721708Sstevel  * iosram_softintr()
13731708Sstevel  *	IOSRAM soft interrupt handler
13741708Sstevel  */
13751708Sstevel static uint_t
13761708Sstevel iosram_softintr(caddr_t arg)
13771708Sstevel {
13781708Sstevel 	uint32_t	hdr_changes;
13791708Sstevel 	iosramsoft_t	*softp = (iosramsoft_t *)arg;
13801708Sstevel 	iosram_chunk_t	*chunkp;
13811708Sstevel 	void		(*handler)();
13821708Sstevel 	int		i;
13831708Sstevel 	uint8_t		flag;
13841708Sstevel 
13851708Sstevel 	DPRINTF(1, ("iosram(%d): in iosram_softintr\n", softp->instance));
13861708Sstevel 
13871708Sstevel 	IOSRAMLOG(2, "SINTR arg/softp:%p  pending:%d busy:%d\n",
13881708Sstevel 	    arg, softp->intr_pending, softp->intr_busy, NULL);
13891708Sstevel 
13901708Sstevel 	mutex_enter(&iosram_mutex);
13911708Sstevel 	mutex_enter(&softp->intr_mutex);
13921708Sstevel 
13931708Sstevel 	/*
13941708Sstevel 	 * Do not process interrupt if interrupt handler is already running or
13951708Sstevel 	 * no interrupts are pending.
13961708Sstevel 	 */
13971708Sstevel 	if (softp->intr_busy || !softp->intr_pending) {
13981708Sstevel 		mutex_exit(&softp->intr_mutex);
13991708Sstevel 		mutex_exit(&iosram_mutex);
14001708Sstevel 		DPRINTF(1, ("IOSRAM(%d): softintr: busy=%d pending=%d\n",
14011708Sstevel 		    softp->instance, softp->intr_busy, softp->intr_pending));
14021708Sstevel 		return (softp->intr_pending ? DDI_INTR_CLAIMED :
14031708Sstevel 		    DDI_INTR_UNCLAIMED);
14041708Sstevel 	}
14051708Sstevel 
14061708Sstevel 	/*
14071708Sstevel 	 * It's possible for the SC to send an interrupt on the new master
14081708Sstevel 	 * before we are able to set our internal state.  If so, we'll retrigger
14091708Sstevel 	 * soft interrupt right after tunnel switch completion.
14101708Sstevel 	 */
14111708Sstevel 	if (softp->state & IOSRAM_STATE_TSWITCH) {
14121708Sstevel 		mutex_exit(&softp->intr_mutex);
14131708Sstevel 		mutex_exit(&iosram_mutex);
14141708Sstevel 		DPRINTF(1, ("IOSRAM(%d): softintr: doing switch "
14151708Sstevel 		    "state=0x%x\n", softp->instance, softp->state));
14161708Sstevel 		return (DDI_INTR_CLAIMED);
14171708Sstevel 	}
14181708Sstevel 
14191708Sstevel 	/*
14201708Sstevel 	 * Do not process interrupt if we are not the master.
14211708Sstevel 	 */
14221708Sstevel 	if (!(softp->state & IOSRAM_STATE_MASTER)) {
14231708Sstevel 		mutex_exit(&softp->intr_mutex);
14241708Sstevel 		mutex_exit(&iosram_mutex);
14251708Sstevel 		DPRINTF(1, ("IOSRAM(%d): softintr: no master state=0x%x\n ",
14261708Sstevel 		    softp->instance, softp->state));
14271708Sstevel 		return (DDI_INTR_CLAIMED);
14281708Sstevel 	}
14291708Sstevel 
14301708Sstevel 	IOSRAM_STAT(sintr_recv);
14311708Sstevel 
14321708Sstevel 	/*
14331708Sstevel 	 * If the driver is suspended, then we should not process any
14341708Sstevel 	 * interrupts.  Instead, we trigger a soft interrupt when the driver
14351708Sstevel 	 * resumes.
14361708Sstevel 	 */
14371708Sstevel 	if (softp->suspended) {
14381708Sstevel 		mutex_exit(&softp->intr_mutex);
14391708Sstevel 		mutex_exit(&iosram_mutex);
14401708Sstevel 		DPRINTF(1, ("IOSRAM(%d): softintr: suspended\n",
14411708Sstevel 		    softp->instance));
14421708Sstevel 		return (DDI_INTR_CLAIMED);
14431708Sstevel 	}
14441708Sstevel 
14451708Sstevel 	/*
14461708Sstevel 	 * Indicate that the IOSRAM interrupt handler is busy.  Note that this
14471708Sstevel 	 * includes incrementing the reader/writer count, since we don't want
14481708Sstevel 	 * any tunnel switches to start up while we're processing callbacks.
14491708Sstevel 	 */
14501708Sstevel 	softp->intr_busy = 1;
14511708Sstevel 	iosram_rw_active++;
14521708Sstevel #if defined(DEBUG)
14531708Sstevel 	if (iosram_rw_active > iosram_rw_active_max) {
14541708Sstevel 		iosram_rw_active_max = iosram_rw_active;
14551708Sstevel 	}
14561708Sstevel #endif
14571708Sstevel 
14581708Sstevel 	do {
14591708Sstevel 		DPRINTF(1, ("IOSRAM(%d): softintr: processing interrupt\n",
14601708Sstevel 		    softp->instance));
14611708Sstevel 
14621708Sstevel 		softp->intr_pending = 0;
14631708Sstevel 
14641708Sstevel 		mutex_exit(&softp->intr_mutex);
14651708Sstevel 
14661708Sstevel 		/*
14671708Sstevel 		 * Process changes to the IOSRAM header.
14681708Sstevel 		 */
14691708Sstevel 		hdr_changes = IOSRAM_GET_HDRFIELD32(iosram_master,
14701708Sstevel 		    sms_change_mask);
14711708Sstevel 		if (hdr_changes != 0) {
14721708Sstevel 			int	error;
14731708Sstevel 
14741708Sstevel 			IOSRAM_SET_HDRFIELD32(iosram_master, sms_change_mask,
14751708Sstevel 			    0);
14761708Sstevel 			if (hdr_changes & IOSRAM_HDRFIELD_TOC_INDEX) {
14771708Sstevel 				/*
14781708Sstevel 				 * XXX is it safe to temporarily release the
14791708Sstevel 				 * iosram_mutex here?
14801708Sstevel 				 */
14811708Sstevel 				mutex_exit(&iosram_mutex);
14821708Sstevel 				error = iosram_read_toc(iosram_master);
14831708Sstevel 				mutex_enter(&iosram_mutex);
14841708Sstevel 				if (error) {
14851708Sstevel 					cmn_err(CE_WARN, "iosram_read_toc: new"
14861708Sstevel 					    " TOC invalid; using old TOC.");
14871708Sstevel 				}
14881708Sstevel 				iosram_update_addrs(iosram_master);
14891708Sstevel 			}
14901708Sstevel 
14911708Sstevel 			if (iosram_hdrchange_handler != NULL) {
14921708Sstevel 				mutex_exit(&iosram_mutex);
14931708Sstevel 				iosram_hdrchange_handler();
14941708Sstevel 				mutex_enter(&iosram_mutex);
14951708Sstevel 			}
14961708Sstevel 		}
14971708Sstevel 
14981708Sstevel 		/*
14991708Sstevel 		 * Get data_valid/int_pending flags and generate a callback if
15001708Sstevel 		 * applicable.  For now, we read only those flags for which a
15011708Sstevel 		 * callback has been registered.  We can optimize reading of
15021708Sstevel 		 * flags by reading them all at once and then process them
15031708Sstevel 		 * later.
15041708Sstevel 		 */
15051708Sstevel 		for (i = 0, chunkp = chunks; i < nchunks; i++,
15061708Sstevel 		    chunkp++) {
15071708Sstevel #if DEBUG
15081708Sstevel 			flag =  ddi_get8(iosram_handle,
15091708Sstevel 			    &(chunkp->flagsp->int_pending));
15101708Sstevel 			DPRINTF(1, ("IOSRAM(%d): softintr chunk #%d "
15111708Sstevel 			    "flag=0x%x handler=%p\n",
15121708Sstevel 			    softp->instance, i, (int)flag,
15131708Sstevel 			    chunkp->cback.handler));
15141708Sstevel #endif
15151708Sstevel 			if ((handler = chunkp->cback.handler) == NULL) {
15161708Sstevel 				continue;
15171708Sstevel 			}
15181708Sstevel 			flag = ddi_get8(iosram_handle,
15191708Sstevel 			    &(chunkp->flagsp->int_pending));
15201708Sstevel 			if (flag == IOSRAM_INT_TO_DOM) {
15211708Sstevel 				DPRINTF(1,
15221708Sstevel 				    ("IOSRAM(%d): softintr: invoking handler\n",
15231708Sstevel 				    softp->instance));
15241708Sstevel 				IOSRAMLOG(1,
15251708Sstevel 				    "SINTR invoking hdlr:%p arg:%p index:%d\n",
15261708Sstevel 				    handler, chunkp->cback.arg, i, NULL);
15271708Sstevel 				IOSRAM_STAT(callbacks);
15281708Sstevel 
15291708Sstevel 				ddi_put8(iosram_handle,
15301708Sstevel 				    &(chunkp->flagsp->int_pending),
15311708Sstevel 				    IOSRAM_INT_NONE);
15321708Sstevel 				chunkp->cback.busy = 1;
15331708Sstevel 				mutex_exit(&iosram_mutex);
15341708Sstevel 				(*handler)(chunkp->cback.arg);
15351708Sstevel 				mutex_enter(&iosram_mutex);
15361708Sstevel 				chunkp->cback.busy = 0;
15371708Sstevel 
15381708Sstevel 				/*
15391708Sstevel 				 * If iosram_unregister was called while the
15401708Sstevel 				 * callback was being invoked, complete the
15411708Sstevel 				 * unregistration here.
15421708Sstevel 				 */
15431708Sstevel 				if (chunkp->cback.unregister) {
15441708Sstevel 					DPRINTF(1, ("IOSRAM(%d): softintr: "
15451708Sstevel 					    "delayed unreg k:0x%08x\n",
15461708Sstevel 					    softp->instance,
15471708Sstevel 					    chunkp->toc_data.key));
15481708Sstevel 					chunkp->cback.handler = NULL;
15491708Sstevel 					chunkp->cback.arg = NULL;
15501708Sstevel 					chunkp->cback.unregister = 0;
15511708Sstevel 				}
15521708Sstevel 			}
15531708Sstevel 
15541708Sstevel 			/*
15551708Sstevel 			 * If there's a tunnel switch waiting to run, give it
15561708Sstevel 			 * higher priority than these callbacks by bailing out.
15571708Sstevel 			 * They'll still be invoked on the new master iosram
15581708Sstevel 			 * when the tunnel switch is done.
15591708Sstevel 			 */
15601708Sstevel 			if (iosram_tswitch_active) {
15611708Sstevel 				break;
15621708Sstevel 			}
15631708Sstevel 		}
15641708Sstevel 
15651708Sstevel 		mutex_enter(&softp->intr_mutex);
15661708Sstevel 
15671708Sstevel 	} while (softp->intr_pending && !softp->suspended &&
15681708Sstevel 	    !iosram_tswitch_active);
15691708Sstevel 
15701708Sstevel 	/*
15711708Sstevel 	 * Indicate IOSRAM interrupt handler is not BUSY any more
15721708Sstevel 	 */
15731708Sstevel 	softp->intr_busy = 0;
15741708Sstevel 
15751708Sstevel 	ASSERT(iosram_rw_active > 0);
15761708Sstevel 	if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
15771708Sstevel 		iosram_rw_wakeup = 0;
15781708Sstevel 		cv_broadcast(&iosram_rw_wait);
15791708Sstevel 	}
15801708Sstevel 
15811708Sstevel 	mutex_exit(&softp->intr_mutex);
15821708Sstevel 	mutex_exit(&iosram_mutex);
15831708Sstevel 
15841708Sstevel 	DPRINTF(1, ("iosram(%d): softintr exit\n", softp->instance));
15851708Sstevel 
15861708Sstevel 	return (DDI_INTR_CLAIMED);
15871708Sstevel }
15881708Sstevel 
15891708Sstevel 
15901708Sstevel /*
15911708Sstevel  * iosram_intr()
15921708Sstevel  *	IOSRAM real interrupt handler
15931708Sstevel  */
15941708Sstevel static uint_t
15951708Sstevel iosram_intr(caddr_t arg)
15961708Sstevel {
15971708Sstevel 	iosramsoft_t	*softp = (iosramsoft_t *)arg;
15981708Sstevel 	int		result = DDI_INTR_UNCLAIMED;
15991708Sstevel 	uint32_t	int_status;
16001708Sstevel 
16011708Sstevel 	DPRINTF(2, ("iosram(%d): in iosram_intr\n", softp->instance));
16021708Sstevel 
16031708Sstevel 	mutex_enter(&softp->intr_mutex);
16041708Sstevel 
16051708Sstevel 	if (softp->sbbc_handle == NULL) {
16061708Sstevel 		/*
16071708Sstevel 		 * The SBBC registers region is not mapped in.
16081708Sstevel 		 * Set the interrupt pending flag here, and process the
16091708Sstevel 		 * interrupt after the tunnel switch.
16101708Sstevel 		 */
16111708Sstevel 		DPRINTF(1, ("IOSRAM(%d): iosram_intr: SBBC not mapped\n",
16121708Sstevel 		    softp->instance));
16131708Sstevel 		softp->intr_pending = 1;
16141708Sstevel 		mutex_exit(&softp->intr_mutex);
16151708Sstevel 		return (DDI_INTR_UNCLAIMED);
16161708Sstevel 	}
16171708Sstevel 
16181708Sstevel 	int_status = ddi_get32(softp->sbbc_handle,
16191708Sstevel 	    &(softp->sbbc_region->int_status.reg));
16201708Sstevel 	DPRINTF(1, ("iosram_intr: int_status = 0x%08x\n", int_status));
16211708Sstevel 
16221708Sstevel 	if (int_status & IOSRAM_SBBC_INT0) {
16231708Sstevel 		result = DDI_INTR_CLAIMED;
16241708Sstevel 		DPRINTF(1, ("iosram_intr: int0 detected!\n"));
16251708Sstevel 	}
16261708Sstevel 
16271708Sstevel 	if (int_status & IOSRAM_SBBC_INT1) {
16281708Sstevel 		result = DDI_INTR_CLAIMED;
16291708Sstevel 		DPRINTF(1, ("iosram_intr: int1 detected!\n"));
16301708Sstevel 	}
16311708Sstevel 
16321708Sstevel 	if (result == DDI_INTR_CLAIMED) {
16331708Sstevel 		ddi_put32(softp->sbbc_handle,
16341708Sstevel 		    &(softp->sbbc_region->int_status.reg), int_status);
16351708Sstevel 		int_status = ddi_get32(softp->sbbc_handle,
16361708Sstevel 		    &(softp->sbbc_region->int_status.reg));
16371708Sstevel 		DPRINTF(1, ("iosram_intr: int_status = 0x%08x\n",
16381708Sstevel 		    int_status));
16391708Sstevel 
16401708Sstevel 		softp->intr_pending = 1;
16411708Sstevel 		/*
16421708Sstevel 		 * Trigger soft interrupt if not executing and
16431708Sstevel 		 * not suspended.
16441708Sstevel 		 */
16451708Sstevel 		if (!softp->intr_busy && !softp->suspended &&
16461708Sstevel 		    (softp->softintr_id != NULL)) {
16471708Sstevel 			DPRINTF(1, ("iosram(%d): trigger softint\n",
16481708Sstevel 			    softp->instance));
16491708Sstevel 			ddi_trigger_softintr(softp->softintr_id);
16501708Sstevel 		}
16511708Sstevel 	}
16521708Sstevel 
16531708Sstevel 	IOSRAM_STAT(intr_recv);
16541708Sstevel 
16551708Sstevel 	mutex_exit(&softp->intr_mutex);
16561708Sstevel 
16571708Sstevel 	IOSRAMLOG(2, "INTR arg/softp:%p  pending:%d busy:%d\n",
16581708Sstevel 	    arg, softp->intr_pending, softp->intr_busy, NULL);
16591708Sstevel 	DPRINTF(1, ("iosram(%d): iosram_intr exit\n", softp->instance));
16601708Sstevel 
16611708Sstevel 	return (result);
16621708Sstevel }
16631708Sstevel 
16641708Sstevel 
16651708Sstevel /*
16661708Sstevel  * iosram_send_intr()
16671708Sstevel  *	Send an interrupt to the SSP side via AXQ driver
16681708Sstevel  */
16691708Sstevel int
16701708Sstevel iosram_send_intr()
16711708Sstevel {
16721708Sstevel 	IOSRAMLOG(1, "SendIntr called\n", NULL, NULL, NULL, NULL);
16731708Sstevel 	IOSRAM_STAT(intr_send);
16741708Sstevel 	DPRINTF(1, ("iosram iosram_send_intr invoked\n"));
16751708Sstevel 
16761708Sstevel 	return (axq_cpu2ssc_intr(0));
16771708Sstevel }
16781708Sstevel 
16791708Sstevel 
16801708Sstevel #if defined(DEBUG)
16811708Sstevel static void
16821708Sstevel iosram_dummy_cback(void *arg)
16831708Sstevel {
16841708Sstevel 	DPRINTF(1, ("iosram_dummy_cback invoked arg:%p\n", arg));
16851708Sstevel }
16861708Sstevel #endif /* DEBUG */
16871708Sstevel 
16881708Sstevel 
16891708Sstevel /*ARGSUSED1*/
16901708Sstevel static int
16911708Sstevel iosram_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
16921708Sstevel 		int *rvalp)
16931708Sstevel {
16941708Sstevel 	struct iosramsoft	*softp;
16951708Sstevel 	int			error = DDI_SUCCESS;
16961708Sstevel 
16971708Sstevel 	softp = ddi_get_soft_state(iosramsoft_statep, getminor(dev));
16981708Sstevel 	if (softp == NULL) {
16991708Sstevel 		return (ENXIO);
17001708Sstevel 	}
17011708Sstevel 	IOSRAMLOG(1, "IOCTL: dev:%p cmd:%x arg:%p ... instance %d\n",
17021708Sstevel 	    dev, cmd, arg, softp->instance);
17031708Sstevel 
17041708Sstevel 	switch (cmd) {
17051708Sstevel #if defined(DEBUG)
17061708Sstevel 	case IOSRAM_GET_FLAG:
1707*7656SSherry.Moore@Sun.COM 		{
17081708Sstevel 		iosram_io_t	req;
17091708Sstevel 		uint8_t		data_valid, int_pending;
17101708Sstevel 
17111708Sstevel 		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
17121708Sstevel 			return (EFAULT);
17131708Sstevel 		}
17141708Sstevel 
17151708Sstevel 		DPRINTF(2, ("IOSRAM_GET_FLAG(key:%x\n", req.key));
17161708Sstevel 
17171708Sstevel 		req.retval = iosram_get_flag(req.key, &data_valid,
17181708Sstevel 		    &int_pending);
17191708Sstevel 		req.data_valid = (uint32_t)data_valid;
17201708Sstevel 		req.int_pending = (uint32_t)int_pending;
17211708Sstevel 
17221708Sstevel 		if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
17231708Sstevel 			DPRINTF(1,
17241708Sstevel 			    ("IOSRAM_GET_FLAG: can't copyout req.retval (%x)",
17251708Sstevel 			    req.retval));
17261708Sstevel 			error = EFAULT;
17271708Sstevel 		}
17281708Sstevel 
17291708Sstevel 		return (error);
1730*7656SSherry.Moore@Sun.COM 		}
17311708Sstevel 
17321708Sstevel 	case IOSRAM_SET_FLAG:
1733*7656SSherry.Moore@Sun.COM 		{
17341708Sstevel 		iosram_io_t	req;
17351708Sstevel 
17361708Sstevel 		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
17371708Sstevel 			return (EFAULT);
17381708Sstevel 		}
17391708Sstevel 
17401708Sstevel 		DPRINTF(2, ("IOSRAM_SET_FLAG(key:%x data_valid:%x "
17411708Sstevel 		    "int_pending:%x\n", req.key, req.data_valid,
17421708Sstevel 		    req.int_pending));
17431708Sstevel 
17441708Sstevel 		req.retval = iosram_set_flag(req.key, req.data_valid,
17451708Sstevel 		    req.int_pending);
17461708Sstevel 
17471708Sstevel 		if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
17481708Sstevel 			DPRINTF(1, ("IOSRAM_SET_FLAG: can't copyout req.retval"
17491708Sstevel 			    " (%x)\n", req.retval));
17501708Sstevel 			error = EFAULT;
17511708Sstevel 		}
17521708Sstevel 
17531708Sstevel 		return (error);
1754*7656SSherry.Moore@Sun.COM 		}
17551708Sstevel 
17561708Sstevel 	case IOSRAM_RD:
1757*7656SSherry.Moore@Sun.COM 		{
17581708Sstevel 		caddr_t		bufp;
17591708Sstevel 		int		len;
17601708Sstevel 		iosram_io_t	req;
17611708Sstevel 
17621708Sstevel 		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
17631708Sstevel 			return (EFAULT);
17641708Sstevel 		}
17651708Sstevel 
17661708Sstevel 		DPRINTF(2, ("IOSRAM_RD(k:%x o:%x len:%x bufp:%p\n", req.key,
17671708Sstevel 		    req.off, req.len, (void *)(uintptr_t)req.bufp));
17681708Sstevel 
17691708Sstevel 		len = req.len;
17701708Sstevel 		bufp = kmem_alloc(len, KM_SLEEP);
17711708Sstevel 
17721708Sstevel 		req.retval = iosram_rd(req.key, req.off, req.len, bufp);
17731708Sstevel 
17741708Sstevel 		if (ddi_copyout(bufp, (void *)(uintptr_t)req.bufp, len, mode)) {
17751708Sstevel 			DPRINTF(1, ("IOSRAM_RD: copyout(%p, %p,%x,%x) failed\n",
17761708Sstevel 			    bufp, (void *)(uintptr_t)req.bufp, len, mode));
17771708Sstevel 			error = EFAULT;
17781708Sstevel 		} else if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
17791708Sstevel 			DPRINTF(1, ("IOSRAM_RD: can't copyout retval (%x)\n",
17801708Sstevel 			    req.retval));
17811708Sstevel 			error = EFAULT;
17821708Sstevel 		}
17831708Sstevel 
17841708Sstevel 		kmem_free(bufp, len);
17851708Sstevel 		return (error);
1786*7656SSherry.Moore@Sun.COM 		}
17871708Sstevel 
17881708Sstevel 	case IOSRAM_WR:
1789*7656SSherry.Moore@Sun.COM 		{
17901708Sstevel 		caddr_t		bufp;
17911708Sstevel 		iosram_io_t	req;
17921708Sstevel 		int		len;
17931708Sstevel 
17941708Sstevel 		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
17951708Sstevel 			return (EFAULT);
17961708Sstevel 		}
17971708Sstevel 
17981708Sstevel 		DPRINTF(2, ("IOSRAM_WR(k:%x o:%x len:%x bufp:%p\n",
17991708Sstevel 		    req.key, req.off, req.len, req.bufp));
18001708Sstevel 		len = req.len;
18011708Sstevel 		bufp = kmem_alloc(len, KM_SLEEP);
18021708Sstevel 		if (ddi_copyin((void *)(uintptr_t)req.bufp, bufp, len, mode)) {
18031708Sstevel 			error = EFAULT;
18041708Sstevel 		} else {
18051708Sstevel 			req.retval = iosram_wr(req.key, req.off, req.len,
18061708Sstevel 			    bufp);
18071708Sstevel 
18081708Sstevel 			if (ddi_copyout(&req, (void *)arg, sizeof (req),
18091708Sstevel 			    mode)) {
18101708Sstevel 				error = EFAULT;
18111708Sstevel 			}
18121708Sstevel 		}
18131708Sstevel 		kmem_free(bufp, len);
18141708Sstevel 		return (error);
1815*7656SSherry.Moore@Sun.COM 		}
18161708Sstevel 
18171708Sstevel 	case IOSRAM_TOC:
1818*7656SSherry.Moore@Sun.COM 		{
18191708Sstevel 		caddr_t		bufp;
18201708Sstevel 		int		len;
18211708Sstevel 		iosram_io_t	req;
18221708Sstevel 
18231708Sstevel 		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
18241708Sstevel 			return (EFAULT);
18251708Sstevel 		}
18261708Sstevel 
18271708Sstevel 		DPRINTF(2, ("IOSRAM_TOC (req.bufp:%x req.len:%x) \n",
18281708Sstevel 		    req.bufp, req.len));
18291708Sstevel 
18301708Sstevel 		len = req.len;
18311708Sstevel 		bufp = kmem_alloc(len, KM_SLEEP);
18321708Sstevel 
18331708Sstevel 		req.retval = iosram_get_keys((iosram_toc_entry_t *)bufp,
18341708Sstevel 		    &req.len);
18351708Sstevel 
18361708Sstevel 		if (ddi_copyout(bufp, (void *)(uintptr_t)req.bufp, req.len,
18371708Sstevel 		    mode)) {
18381708Sstevel 			DPRINTF(1,
18391708Sstevel 			    ("IOSRAM_TOC: copyout(%p, %p,%x,%x) failed\n",
18401708Sstevel 			    bufp, (void *)(uintptr_t)req.bufp, req.len, mode));
18411708Sstevel 			error = EFAULT;
18421708Sstevel 		} else if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
18431708Sstevel 			DPRINTF(1, ("IOSRAM_TOC: can't copyout retval (%x)\n",
18441708Sstevel 			    req.retval));
18451708Sstevel 			error = EFAULT;
18461708Sstevel 		}
18471708Sstevel 		kmem_free(bufp, len);
18481708Sstevel 		return (error);
1849*7656SSherry.Moore@Sun.COM 		}
18501708Sstevel 
18511708Sstevel 	case IOSRAM_SEND_INTR:
1852*7656SSherry.Moore@Sun.COM 		{
18531708Sstevel 		DPRINTF(2, ("IOSRAM_SEND_INTR\n"));
18541708Sstevel 
18551708Sstevel 		switch ((int)arg) {
18561708Sstevel 		case 0x11:
18571708Sstevel 		case 0x22:
18581708Sstevel 		case 0x44:
18591708Sstevel 		case 0x88:
18601708Sstevel 			ddi_put32(softp->sbbc_handle,
18611708Sstevel 			    &(softp->sbbc_region->int_enable.reg), (int)arg);
18621708Sstevel 			DPRINTF(1, ("Wrote 0x%x to int_enable.reg\n",
18631708Sstevel 			    (int)arg));
18641708Sstevel 			break;
18651708Sstevel 		case 0xBB:
18661708Sstevel 			ddi_put32(softp->sbbc_handle,
18671708Sstevel 			    &(softp->sbbc_region->p0_int_gen.reg), 1);
18681708Sstevel 			DPRINTF(1, ("Wrote 1 to p0_int_gen.reg\n"));
18691708Sstevel 			break;
18701708Sstevel 		default:
18711708Sstevel 			error = iosram_send_intr();
18721708Sstevel 		}
18731708Sstevel 
18741708Sstevel 		return (error);
1875*7656SSherry.Moore@Sun.COM 		}
18761708Sstevel 
18771708Sstevel 	case IOSRAM_PRINT_CBACK:
18781708Sstevel 		iosram_print_cback();
18791708Sstevel 		break;
18801708Sstevel 
18811708Sstevel 	case IOSRAM_PRINT_STATE:
18821708Sstevel 		iosram_print_state((int)arg);
18831708Sstevel 		break;
18841708Sstevel 
18851708Sstevel #if IOSRAM_STATS
18861708Sstevel 	case IOSRAM_PRINT_STATS:
18871708Sstevel 		iosram_print_stats();
18881708Sstevel 		break;
18891708Sstevel #endif
18901708Sstevel 
18911708Sstevel #if IOSRAM_LOG
18921708Sstevel 	case IOSRAM_PRINT_LOG:
18931708Sstevel 		iosram_print_log((int)arg);
18941708Sstevel 		break;
18951708Sstevel #endif
18961708Sstevel 
18971708Sstevel 	case IOSRAM_TUNNEL_SWITCH:
18981708Sstevel 		error = iosram_switchfrom((int)arg);
18991708Sstevel 		break;
19001708Sstevel 
19011708Sstevel 	case IOSRAM_PRINT_FLAGS:
19021708Sstevel 		iosram_print_flags();
19031708Sstevel 		break;
19041708Sstevel 
19051708Sstevel 	case IOSRAM_REG_CBACK:
1906*7656SSherry.Moore@Sun.COM 		{
19071708Sstevel 		iosram_io_t	req;
19081708Sstevel 
19091708Sstevel 		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
19101708Sstevel 			return (EFAULT);
19111708Sstevel 		}
19121708Sstevel 
19131708Sstevel 		DPRINTF(2, ("IOSRAM_REG_CBACK(k:%x)\n", req.key));
19141708Sstevel 
19151708Sstevel 		req.retval = iosram_register(req.key, iosram_dummy_cback,
19161708Sstevel 		    (void *)(uintptr_t)req.key);
19171708Sstevel 		if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
19181708Sstevel 			error = EFAULT;
19191708Sstevel 		}
19201708Sstevel 
19211708Sstevel 		return (error);
1922*7656SSherry.Moore@Sun.COM 		}
19231708Sstevel 
19241708Sstevel 	case IOSRAM_UNREG_CBACK:
1925*7656SSherry.Moore@Sun.COM 		{
19261708Sstevel 		iosram_io_t	req;
19271708Sstevel 
19281708Sstevel 		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
19291708Sstevel 			return (EFAULT);
19301708Sstevel 		}
19311708Sstevel 
19321708Sstevel 		DPRINTF(2, ("IOSRAM_REG_CBACK(k:%x)\n", req.key));
19331708Sstevel 
19341708Sstevel 		req.retval = iosram_unregister(req.key);
19351708Sstevel 		if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
19361708Sstevel 			error = EFAULT;
19371708Sstevel 		}
19381708Sstevel 
19391708Sstevel 		return (error);
1940*7656SSherry.Moore@Sun.COM 		}
19411708Sstevel 
19421708Sstevel 	case IOSRAM_SEMA_ACQUIRE:
19431708Sstevel 	{
19441708Sstevel 		DPRINTF(1, ("IOSRAM_SEMA_ACQUIRE\n"));
19451708Sstevel 		error = iosram_sema_acquire(NULL);
19461708Sstevel 		return (error);
19471708Sstevel 	}
19481708Sstevel 
19491708Sstevel 	case IOSRAM_SEMA_RELEASE:
19501708Sstevel 	{
19511708Sstevel 		DPRINTF(1, ("IOSRAM_SEMA_RELEASE\n"));
19521708Sstevel 		error = iosram_sema_release();
19531708Sstevel 		return (error);
19541708Sstevel 	}
19551708Sstevel 
19561708Sstevel #endif /* DEBUG */
19571708Sstevel 
19581708Sstevel 	default:
19591708Sstevel 		DPRINTF(1, ("iosram_ioctl: Illegal command %x\n", cmd));
19601708Sstevel 		error = ENOTTY;
19611708Sstevel 	}
19621708Sstevel 
19631708Sstevel 	return (error);
19641708Sstevel }
19651708Sstevel 
19661708Sstevel 
19671708Sstevel /*
19681708Sstevel  * iosram_switch_tunnel(softp)
19691708Sstevel  *	Switch master tunnel to the specified instance
19701708Sstevel  *	Must be called while holding iosram_mutex
19711708Sstevel  */
19721708Sstevel /*ARGSUSED*/
19731708Sstevel static int
19741708Sstevel iosram_switch_tunnel(iosramsoft_t *softp)
19751708Sstevel {
19761708Sstevel #ifdef DEBUG
19771708Sstevel 	int		instance = softp->instance;
19781708Sstevel #endif
19791708Sstevel 	int		error = 0;
19801708Sstevel 	iosramsoft_t	*prev_master;
19811708Sstevel 
19821708Sstevel 	ASSERT(mutex_owned(&iosram_mutex));
19831708Sstevel 
19841708Sstevel 	DPRINTF(1, ("tunnel switch new master:%p (%d) current master:%p (%d)\n",
19851708Sstevel 	    softp, instance, iosram_master,
19861708Sstevel 	    ((iosram_master) ? iosram_master->instance : -1)));
19871708Sstevel 	IOSRAMLOG(1, "TSWTCH: new_master:%p (%p) iosram_master:%p (%d)\n",
19881708Sstevel 	    softp, instance, iosram_master,
19891708Sstevel 	    ((iosram_master) ? iosram_master->instance : -1));
19901708Sstevel 
19911708Sstevel 	if (softp == NULL || (softp->state & IOSRAM_STATE_DETACH)) {
19921708Sstevel 		return (ENXIO);
19931708Sstevel 	}
19941708Sstevel 	if (iosram_master == softp) {
19951708Sstevel 		return (0);
19961708Sstevel 	}
19971708Sstevel 
19981708Sstevel 
19991708Sstevel 	/*
20001708Sstevel 	 * We protect against the softp structure being deallocated by setting
20011708Sstevel 	 * the IOSRAM_STATE_TSWITCH state flag. The detach routine will check
20021708Sstevel 	 * for this flag and if set, it will wait for this flag to be reset or
20031708Sstevel 	 * refuse the detach operation.
20041708Sstevel 	 */
20051708Sstevel 	iosram_new_master = softp;
20061708Sstevel 	softp->state |= IOSRAM_STATE_TSWITCH;
20071708Sstevel 	prev_master = iosram_master;
20081708Sstevel 	if (prev_master) {
20091708Sstevel 		prev_master->state |= IOSRAM_STATE_TSWITCH;
20101708Sstevel 	}
20111708Sstevel 	mutex_exit(&iosram_mutex);
20121708Sstevel 
20131708Sstevel 	/*
20141708Sstevel 	 * Map the target IOSRAM, read the TOC, and register interrupts if not
20151708Sstevel 	 * already done.
20161708Sstevel 	 */
20171708Sstevel 	DPRINTF(1, ("iosram(%d): mapping IOSRAM and SBBC\n",
20181708Sstevel 	    softp->instance));
20191708Sstevel 	IOSRAMLOG(1, "TSWTCH: mapping instance:%d  softp:%p\n",
20201708Sstevel 	    instance, softp, NULL, NULL);
20211708Sstevel 
20221708Sstevel 	if (iosram_setup_map(softp) != DDI_SUCCESS) {
20231708Sstevel 		error = ENXIO;
20241708Sstevel 	} else if ((chunks == NULL) && (iosram_read_toc(softp) != 0)) {
20251708Sstevel 		iosram_remove_map(softp);
20261708Sstevel 		error = EINVAL;
20271708Sstevel 	} else if (iosram_add_intr(softp) != DDI_SUCCESS) {
20281708Sstevel 		/*
20291708Sstevel 		 * If there was no previous master, purge the TOC data that
20301708Sstevel 		 * iosram_read_toc() created.
20311708Sstevel 		 */
20321708Sstevel 		if ((prev_master == NULL) && (chunks != NULL)) {
20331708Sstevel 			kmem_free(chunks, nchunks * sizeof (iosram_chunk_t));
20341708Sstevel 			chunks = NULL;
20351708Sstevel 			nchunks = 0;
20361708Sstevel 			iosram_init_hashtab();
20371708Sstevel 		}
20381708Sstevel 		iosram_remove_map(softp);
20391708Sstevel 		error = ENXIO;
20401708Sstevel 	}
20411708Sstevel 
20421708Sstevel 	/*
20431708Sstevel 	 * If we are asked to abort tunnel switch, do so now, before invoking
20441708Sstevel 	 * the OBP callback.
20451708Sstevel 	 */
20461708Sstevel 	if (iosram_tswitch_aborted) {
20471708Sstevel 
20481708Sstevel 		/*
20491708Sstevel 		 * Once the tunnel switch is aborted, this thread should not
20501708Sstevel 		 * resume.  If it does, we simply log a message.  We can't unmap
20511708Sstevel 		 * the new master IOSRAM as it may be accessed in
20521708Sstevel 		 * iosram_abort_tswitch(). It will be unmapped when it is
20531708Sstevel 		 * detached.
20541708Sstevel 		 */
20551708Sstevel 		IOSRAMLOG(1,
20561708Sstevel 		    "TSWTCH: aborted (pre OBP cback). Thread resumed.\n",
20571708Sstevel 		    NULL, NULL, NULL, NULL);
20581708Sstevel 		error = EIO;
20591708Sstevel 	}
20601708Sstevel 
20611708Sstevel 	if (error) {
20621708Sstevel 		IOSRAMLOG(1,
20631708Sstevel 		    "TSWTCH: map failed instance:%d  softp:%p error:%x\n",
20641708Sstevel 		    instance, softp, error, NULL);
20651708Sstevel 		goto done;
20661708Sstevel 	}
20671708Sstevel 
20681708Sstevel 	if (prev_master != NULL) {
20691708Sstevel 		int	result;
20701708Sstevel 
20711708Sstevel 		/*
20721708Sstevel 		 * Now invoke the OBP interface to do the tunnel switch.
20731708Sstevel 		 */
20741708Sstevel 		result = prom_starcat_switch_tunnel(softp->portid,
20751708Sstevel 		    OBP_TSWITCH_REQREPLY);
20761708Sstevel 		if (result != 0) {
20771708Sstevel 			error = EIO;
20781708Sstevel 		}
20791708Sstevel 		IOSRAMLOG(1,
20801708Sstevel 		    "TSWTCH: OBP tswitch portid:%x result:%x error:%x\n",
20811708Sstevel 		    softp->portid, result, error, NULL);
20821708Sstevel 		IOSRAM_STAT(tswitch);
20831708Sstevel 		iosram_tswitch_tstamp = ddi_get_lbolt();
20841708Sstevel 	}
20851708Sstevel 
20861708Sstevel 	mutex_enter(&iosram_mutex);
20871708Sstevel 	if (iosram_tswitch_aborted) {
20881708Sstevel 		/*
20891708Sstevel 		 * Tunnel switch aborted.  This thread should not resume.
20901708Sstevel 		 * For now, we simply log a message, but don't unmap any
20911708Sstevel 		 * IOSRAM at this stage as it may be accessed within the
20921708Sstevel 		 * isoram_abort_tswitch(). The IOSRAM will be unmapped
20931708Sstevel 		 * when that instance is detached.
20941708Sstevel 		 */
20951708Sstevel 		if (iosram_tswitch_aborted) {
20961708Sstevel 			IOSRAMLOG(1,
20971708Sstevel 			    "TSWTCH: aborted (post OBP cback). Thread"
20981708Sstevel 			    " resumed.\n", NULL, NULL, NULL, NULL);
20991708Sstevel 			error = EIO;
21001708Sstevel 			mutex_exit(&iosram_mutex);
21011708Sstevel 		}
21021708Sstevel 	} else if (error) {
21031708Sstevel 		/*
21041708Sstevel 		 * Tunnel switch failed.  Continue using previous tunnel.
21051708Sstevel 		 * However, unmap new (target) IOSRAM.
21061708Sstevel 		 */
21071708Sstevel 		iosram_new_master = NULL;
21081708Sstevel 		mutex_exit(&iosram_mutex);
21091708Sstevel 		iosram_remove_intr(softp);
21101708Sstevel 		iosram_remove_map(softp);
21111708Sstevel 	} else {
21121708Sstevel 		/*
21131708Sstevel 		 * Tunnel switch was successful.  Set the new master.
21141708Sstevel 		 * Also unmap old master IOSRAM and remove any interrupts
21151708Sstevel 		 * associated with that.
21161708Sstevel 		 *
21171708Sstevel 		 * Note that a call to iosram_force_write() allows access
21181708Sstevel 		 * to the IOSRAM while tunnel switch is in progress.  That
21191708Sstevel 		 * means we need to set the new master before unmapping
21201708Sstevel 		 * the old master.
21211708Sstevel 		 */
21221708Sstevel 		iosram_set_master(softp);
21231708Sstevel 		iosram_new_master = NULL;
21241708Sstevel 		mutex_exit(&iosram_mutex);
21251708Sstevel 
21261708Sstevel 		if (prev_master) {
21271708Sstevel 			IOSRAMLOG(1, "TSWTCH: unmapping prev_master:%p (%d)\n",
21281708Sstevel 			    prev_master, prev_master->instance, NULL, NULL);
21291708Sstevel 			iosram_remove_intr(prev_master);
21301708Sstevel 			iosram_remove_map(prev_master);
21311708Sstevel 		}
21321708Sstevel 	}
21331708Sstevel 
21341708Sstevel done:
21351708Sstevel 	mutex_enter(&iosram_mutex);
21361708Sstevel 
21371708Sstevel 	/*
21381708Sstevel 	 * Clear the tunnel switch flag on the source and destination
21391708Sstevel 	 * instances.
21401708Sstevel 	 */
21411708Sstevel 	if (prev_master) {
21421708Sstevel 		prev_master->state &= ~IOSRAM_STATE_TSWITCH;
21431708Sstevel 	}
21441708Sstevel 	softp->state &= ~IOSRAM_STATE_TSWITCH;
21451708Sstevel 
21461708Sstevel 	/*
21471708Sstevel 	 * Since incoming interrupts could get lost during a tunnel switch,
21481708Sstevel 	 * trigger a soft interrupt just in case.  No harm other than a bit
21491708Sstevel 	 * of wasted effort will be caused if no interrupts were dropped.
21501708Sstevel 	 */
21511708Sstevel 	mutex_enter(&softp->intr_mutex);
21521708Sstevel 	iosram_master->intr_pending = 1;
21531708Sstevel 	if ((iosram_master->softintr_id != NULL) &&
21541708Sstevel 	    (iosram_master->intr_busy == 0)) {
21551708Sstevel 		ddi_trigger_softintr(iosram_master->softintr_id);
21561708Sstevel 	}
21571708Sstevel 	mutex_exit(&softp->intr_mutex);
21581708Sstevel 
21591708Sstevel 	IOSRAMLOG(1, "TSWTCH: done error:%d iosram_master:%p instance:%d\n",
21601708Sstevel 	    error, iosram_master,
21611708Sstevel 	    (iosram_master) ? iosram_master->instance : -1, NULL);
21621708Sstevel 
21631708Sstevel 	return (error);
21641708Sstevel }
21651708Sstevel 
21661708Sstevel 
21671708Sstevel /*
21681708Sstevel  * iosram_abort_tswitch()
21691708Sstevel  * Must be called while holding iosram_mutex.
21701708Sstevel  */
21711708Sstevel static void
21721708Sstevel iosram_abort_tswitch()
21731708Sstevel {
21741708Sstevel 	uint32_t  master_valid, new_master_valid;
21751708Sstevel 
21761708Sstevel 	ASSERT(mutex_owned(&iosram_mutex));
21771708Sstevel 
21781708Sstevel 	if ((!iosram_tswitch_active) || iosram_tswitch_aborted) {
21791708Sstevel 		return;
21801708Sstevel 	}
21811708Sstevel 
21821708Sstevel 	ASSERT(iosram_master != NULL);
21831708Sstevel 
21841708Sstevel 	IOSRAMLOG(1, "ABORT: iosram_master:%p (%d) iosram_new_master:%p (%d)\n",
21851708Sstevel 	    iosram_master, iosram_master->instance, iosram_new_master,
21861708Sstevel 	    (iosram_new_master == NULL) ? -1 : iosram_new_master->instance);
21871708Sstevel 
21881708Sstevel 	/*
21891708Sstevel 	 * The first call to iosram_force_write() in the middle of tunnel switch
21901708Sstevel 	 * will get here. We lookup IOSRAM VALID location and setup appropriate
21911708Sstevel 	 * master, if one is still valid.  We also set iosram_tswitch_aborted to
21921708Sstevel 	 * prevent reentering this code and to catch if the OBP callback thread
21931708Sstevel 	 * somehow resumes.
21941708Sstevel 	 */
21951708Sstevel 	iosram_tswitch_aborted = 1;
21961708Sstevel 
21971708Sstevel 	if ((iosram_new_master == NULL) ||
21981708Sstevel 	    (iosram_new_master = iosram_master)) {
21991708Sstevel 		/*
22001708Sstevel 		 * New master hasn't been selected yet, or OBP callback
22011708Sstevel 		 * succeeded and we already selected new IOSRAM as master, but
22021708Sstevel 		 * system crashed in the middle of unmapping previous master or
22031708Sstevel 		 * cleaning up state.  Use the existing master.
22041708Sstevel 		 */
22051708Sstevel 		ASSERT(iosram_master->iosramp != NULL);
22061708Sstevel 		ASSERT(IOSRAM_GET_HDRFIELD32(iosram_master, status) ==
22071708Sstevel 		    IOSRAM_VALID);
22081708Sstevel 		IOSRAMLOG(1, "ABORT: master (%d) already determined.\n",
22091708Sstevel 		    iosram_master->instance, NULL, NULL, NULL);
22101708Sstevel 
22111708Sstevel 		return;
22121708Sstevel 	}
22131708Sstevel 
22141708Sstevel 	/*
22151708Sstevel 	 * System crashed in the middle of tunnel switch and we know that the
22161708Sstevel 	 * new target has not been marked master yet.  That means, the old
22171708Sstevel 	 * master should still be mapped.  We need to abort the tunnel switch
22181708Sstevel 	 * and setup a valid master, if possible, so that we can write to the
22191708Sstevel 	 * IOSRAM.
22201708Sstevel 	 *
22211708Sstevel 	 * We select a new master based upon the IOSRAM header status fields in
22221708Sstevel 	 * the previous master IOSRAM and the target IOSRAM as follows:
22231708Sstevel 	 *
22241708Sstevel 	 *	iosram_master	iosram-tswitch
22251708Sstevel 	 * 	(Prev Master)	(New Target)	Decision
22261708Sstevel 	 *	---------------	---------------	-----------
22271708Sstevel 	 *	  VALID		  don't care	prev master
22281708Sstevel 	 *	  INTRANSIT	  INVALID	prev master
22291708Sstevel 	 *	  INTRANSIT	  INTRANSIT	prev master
22301708Sstevel 	 *	  INTRANSIT	  VALID		new target
22311708Sstevel 	 *	  INVALID	  INVALID	shouldn't ever happen
22321708Sstevel 	 *	  INVALID	  INTRANSIT	shouldn't ever happen
22331708Sstevel 	 *	  INVALID	  VALID		new target
22341708Sstevel 	 */
22351708Sstevel 
22361708Sstevel 	master_valid = (iosram_master->iosramp != NULL) ?
22371708Sstevel 	    IOSRAM_GET_HDRFIELD32(iosram_master, status) : IOSRAM_INVALID;
22381708Sstevel 	new_master_valid = (iosram_new_master->iosramp != NULL) ?
22391708Sstevel 	    IOSRAM_GET_HDRFIELD32(iosram_new_master, status) : IOSRAM_INVALID;
22401708Sstevel 
22411708Sstevel 	if (master_valid == IOSRAM_VALID) {
22421708Sstevel 		/* EMPTY */
22431708Sstevel 		/*
22441708Sstevel 		 * OBP hasn't been called yet or, if it has, it hasn't started
22451708Sstevel 		 * copying yet.  Use the existing master.  Note that the new
22461708Sstevel 		 * master may not be mapped yet.
22471708Sstevel 		 */
22481708Sstevel 		IOSRAMLOG(1, "ABORT: prev master(%d) is VALID\n",
22491708Sstevel 		    iosram_master->instance, NULL, NULL, NULL);
22501708Sstevel 	} else if (master_valid == IOSRAM_INTRANSIT) {
22511708Sstevel 		/*
22521708Sstevel 		 * The system crashed after OBP started processing the tunnel
22531708Sstevel 		 * switch but before the iosram driver determined that it was
22541708Sstevel 		 * complete.  Use the new master if it has been marked valid,
22551708Sstevel 		 * meaning that OBP finished copying data to it, or the old
22561708Sstevel 		 * master otherwise.
22571708Sstevel 		 */
22581708Sstevel 		IOSRAMLOG(1, "ABORT: prev master(%d) is INTRANSIT\n",
22591708Sstevel 		    iosram_master->instance, NULL, NULL, NULL);
22601708Sstevel 
22611708Sstevel 		if (new_master_valid == IOSRAM_VALID) {
22621708Sstevel 			iosram_set_master(iosram_new_master);
22631708Sstevel 			IOSRAMLOG(1, "ABORT: new master(%d) is VALID\n",
22641708Sstevel 			    iosram_new_master->instance, NULL, NULL,
22651708Sstevel 			    NULL);
22661708Sstevel 		} else {
22671708Sstevel 			prom_starcat_switch_tunnel(iosram_master->portid,
22681708Sstevel 			    OBP_TSWITCH_NOREPLY);
22691708Sstevel 
22701708Sstevel 			IOSRAMLOG(1, "ABORT: new master(%d) is INVALID\n",
22711708Sstevel 			    iosram_new_master->instance, NULL, NULL,
22721708Sstevel 			    NULL);
22731708Sstevel 		}
22741708Sstevel 	} else {
22751708Sstevel 		/*
22761708Sstevel 		 * The system crashed after OBP marked the old master INVALID,
22771708Sstevel 		 * which means the new master is the way to go.
22781708Sstevel 		 */
22791708Sstevel 		IOSRAMLOG(1, "ABORT: prev master(%d) is INVALID\n",
22801708Sstevel 		    iosram_master->instance, NULL, NULL, NULL);
22811708Sstevel 
22821708Sstevel 		ASSERT(new_master_valid == IOSRAM_VALID);
22831708Sstevel 
22841708Sstevel 		iosram_set_master(iosram_new_master);
22851708Sstevel 	}
22861708Sstevel 
22871708Sstevel 	IOSRAMLOG(1, "ABORT: Instance %d selected as master\n",
2288*7656SSherry.Moore@Sun.COM 	    iosram_master->instance, NULL, NULL, NULL);
22891708Sstevel }
22901708Sstevel 
22911708Sstevel 
22921708Sstevel /*
22931708Sstevel  * iosram_switchfrom(instance)
22941708Sstevel  *	Switch master tunnel away from the specified instance
22951708Sstevel  */
22961708Sstevel /*ARGSUSED*/
22971708Sstevel int
22981708Sstevel iosram_switchfrom(int instance)
22991708Sstevel {
23001708Sstevel 	struct iosramsoft	*softp;
23011708Sstevel 	int			error = 0;
23021708Sstevel 	int			count;
23031708Sstevel 	clock_t			current_tstamp;
23041708Sstevel 	clock_t			tstamp_interval;
23051708Sstevel 	struct iosramsoft	*last_master = NULL;
23061708Sstevel 	static int		last_master_instance = -1;
23071708Sstevel 
23081708Sstevel 	IOSRAMLOG(1, "SwtchFrom: instance:%d  iosram_master:%p (%d)\n",
23091708Sstevel 	    instance, iosram_master,
23101708Sstevel 	    ((iosram_master) ? iosram_master->instance : -1), NULL);
23111708Sstevel 
23121708Sstevel 	mutex_enter(&iosram_mutex);
23131708Sstevel 
23141708Sstevel 	/*
23151708Sstevel 	 * Wait if another tunnel switch is in progress
23161708Sstevel 	 */
23171708Sstevel 	for (count = 0; iosram_tswitch_active && count < IOSRAM_TSWITCH_RETRY;
23181708Sstevel 	    count++) {
23191708Sstevel 		iosram_tswitch_wakeup = 1;
23201708Sstevel 		cv_wait(&iosram_tswitch_wait, &iosram_mutex);
23211708Sstevel 	}
23221708Sstevel 
23231708Sstevel 	if (iosram_tswitch_active) {
23241708Sstevel 		mutex_exit(&iosram_mutex);
23251708Sstevel 		return (EAGAIN);
23261708Sstevel 	}
23271708Sstevel 
23281708Sstevel 	/*
23291708Sstevel 	 * Check if the specified instance holds the tunnel. If not,
23301708Sstevel 	 * then we are done.
23311708Sstevel 	 */
23321708Sstevel 	if ((iosram_master == NULL) || (iosram_master->instance != instance)) {
23331708Sstevel 		mutex_exit(&iosram_mutex);
23341708Sstevel 		return (0);
23351708Sstevel 	}
23361708Sstevel 
23371708Sstevel 	/*
23381708Sstevel 	 * Before beginning the tunnel switch process, wait for any outstanding
23391708Sstevel 	 * read/write activity to complete.
23401708Sstevel 	 */
23411708Sstevel 	iosram_tswitch_active = 1;
23421708Sstevel 	while (iosram_rw_active) {
23431708Sstevel 		iosram_rw_wakeup = 1;
23441708Sstevel 		cv_wait(&iosram_rw_wait, &iosram_mutex);
23451708Sstevel 	}
23461708Sstevel 
23471708Sstevel 	/*
23481708Sstevel 	 * If a previous tunnel switch just completed, we have to make sure
23491708Sstevel 	 * HWAD has enough time to find the new tunnel before we switch
23501708Sstevel 	 * away from it.  Otherwise, OBP's mailbox message to OSD will never
23511708Sstevel 	 * get through.  Just to be paranoid about synchronization of lbolt
23521708Sstevel 	 * across different CPUs, make sure the current attempt isn't noted
23531708Sstevel 	 * as starting _before_ the last tunnel switch completed.
23541708Sstevel 	 */
23551708Sstevel 	current_tstamp = ddi_get_lbolt();
23561708Sstevel 	if (current_tstamp > iosram_tswitch_tstamp) {
23571708Sstevel 		tstamp_interval = current_tstamp - iosram_tswitch_tstamp;
23581708Sstevel 	} else {
23591708Sstevel 		tstamp_interval = 0;
23601708Sstevel 	}
23611708Sstevel 	if (drv_hztousec(tstamp_interval) < IOSRAM_TSWITCH_DELAY_US) {
23621708Sstevel 		mutex_exit(&iosram_mutex);
23631708Sstevel 		delay(drv_usectohz(IOSRAM_TSWITCH_DELAY_US) - tstamp_interval);
23641708Sstevel 		mutex_enter(&iosram_mutex);
23651708Sstevel 	}
23661708Sstevel 
23671708Sstevel 	/*
23681708Sstevel 	 * The specified instance holds the tunnel.  We need to move it to some
23691708Sstevel 	 * other IOSRAM.  Try out all possible IOSRAMs listed in
23701708Sstevel 	 * iosram_instances.  For now, we always search from the first entry.
23711708Sstevel 	 * In future, it may be desirable to start where we left off.
23721708Sstevel 	 */
23731708Sstevel 	for (softp = iosram_instances; softp != NULL; softp = softp->next) {
23741708Sstevel 		if (iosram_tswitch_aborted) {
23751708Sstevel 			break;
23761708Sstevel 		}
23771708Sstevel 
23781708Sstevel 		/* we can't switch _to_ the instance we're switching _from_ */
23791708Sstevel 		if (softp->instance == instance) {
23801708Sstevel 			continue;
23811708Sstevel 		}
23821708Sstevel 
23831708Sstevel 		/* skip over instances being detached */
23841708Sstevel 		if (softp->state & IOSRAM_STATE_DETACH) {
23851708Sstevel 			continue;
23861708Sstevel 		}
23871708Sstevel 
23881708Sstevel 		/*
23891708Sstevel 		 * Try to avoid reverting to the last instance we switched away
23901708Sstevel 		 * from, as we expect that one to be detached eventually.  Keep
23911708Sstevel 		 * track of it, though, so we can go ahead and try switching to
23921708Sstevel 		 * it if no other viable candidates are found.
23931708Sstevel 		 */
23941708Sstevel 		if (softp->instance == last_master_instance) {
23951708Sstevel 			last_master = softp;
23961708Sstevel 			continue;
23971708Sstevel 		}
23981708Sstevel 
23991708Sstevel 		/*
24001708Sstevel 		 * Do the tunnel switch.  If successful, record the instance of
24011708Sstevel 		 * the master we just left behind so we can try to avoid
24021708Sstevel 		 * reverting to it next time.
24031708Sstevel 		 */
24041708Sstevel 		if (iosram_switch_tunnel(softp) == 0) {
24051708Sstevel 			last_master_instance = instance;
24061708Sstevel 			break;
24071708Sstevel 		}
24081708Sstevel 	}
24091708Sstevel 
24101708Sstevel 	/*
24111708Sstevel 	 * If we failed to switch the tunnel, but we skipped over an instance
24121708Sstevel 	 * that had previously been switched out of because we expected it to be
24131708Sstevel 	 * detached, go ahead and try it anyway (unless the tswitch was aborted
24141708Sstevel 	 * or the instance we skipped is finally being detached).
24151708Sstevel 	 */
24161708Sstevel 	if ((softp == NULL) && (last_master != NULL) &&
24171708Sstevel 	    !iosram_tswitch_aborted &&
24181708Sstevel 	    !(last_master->state & IOSRAM_STATE_DETACH)) {
24191708Sstevel 		if (iosram_switch_tunnel(last_master) == 0) {
24201708Sstevel 			softp = last_master;
24211708Sstevel 			last_master_instance = instance;
24221708Sstevel 		}
24231708Sstevel 	}
24241708Sstevel 
24251708Sstevel 	if ((softp == NULL) || (iosram_tswitch_aborted)) {
24261708Sstevel 		error = EIO;
24271708Sstevel 	}
24281708Sstevel 
24291708Sstevel 	/*
24301708Sstevel 	 * If there are additional tunnel switches queued up waiting for this
24311708Sstevel 	 * one to complete, wake them up.
24321708Sstevel 	 */
24331708Sstevel 	if (iosram_tswitch_wakeup) {
24341708Sstevel 		iosram_tswitch_wakeup = 0;
24351708Sstevel 		cv_broadcast(&iosram_tswitch_wait);
24361708Sstevel 	}
24371708Sstevel 	iosram_tswitch_active = 0;
24381708Sstevel 	mutex_exit(&iosram_mutex);
24391708Sstevel 	return (error);
24401708Sstevel }
24411708Sstevel 
24421708Sstevel 
24431708Sstevel /*
24441708Sstevel  * iosram_tunnel_capable(softp)
24451708Sstevel  *	Check if this IOSRAM instance is tunnel-capable by looing at
24461708Sstevel  *	"tunnel-capable" property.
24471708Sstevel  */
24481708Sstevel static int
24491708Sstevel iosram_tunnel_capable(struct iosramsoft *softp)
24501708Sstevel {
24511708Sstevel 	int	proplen;
24521708Sstevel 	int	tunnel_capable;
24531708Sstevel 
24541708Sstevel 	/*
24551708Sstevel 	 * Look up IOSRAM_TUNNELOK_PROP property, if any.
24561708Sstevel 	 */
24571708Sstevel 	proplen = sizeof (tunnel_capable);
24581708Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, softp->dip,
24591708Sstevel 	    DDI_PROP_DONTPASS, IOSRAM_TUNNELOK_PROP, (caddr_t)&tunnel_capable,
24601708Sstevel 	    &proplen) != DDI_PROP_SUCCESS) {
24611708Sstevel 		tunnel_capable = 0;
24621708Sstevel 	}
24631708Sstevel 	return (tunnel_capable);
24641708Sstevel }
24651708Sstevel 
24661708Sstevel 
24671708Sstevel static int
24681708Sstevel iosram_sbbc_setup_map(struct iosramsoft *softp)
24691708Sstevel {
24701708Sstevel 	int				rv;
24711708Sstevel 	struct ddi_device_acc_attr	attr;
24721708Sstevel 	dev_info_t			*dip = softp->dip;
24731708Sstevel 	uint32_t			sema_val;
24741708Sstevel 
24751708Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
24761708Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
24771708Sstevel 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
24781708Sstevel 
24791708Sstevel 	mutex_enter(&iosram_mutex);
24801708Sstevel 	mutex_enter(&softp->intr_mutex);
24811708Sstevel 
24821708Sstevel 	/*
24831708Sstevel 	 * Map SBBC region in
24841708Sstevel 	 */
24851708Sstevel 	if ((rv = ddi_regs_map_setup(dip, IOSRAM_SBBC_MAP_INDEX,
24861708Sstevel 	    (caddr_t *)&softp->sbbc_region,
24871708Sstevel 	    IOSRAM_SBBC_MAP_OFFSET, sizeof (iosram_sbbc_region_t),
24881708Sstevel 	    &attr, &softp->sbbc_handle)) != DDI_SUCCESS) {
24891708Sstevel 		DPRINTF(1, ("Failed to map SBBC region.\n"));
24901708Sstevel 		mutex_exit(&softp->intr_mutex);
24911708Sstevel 		mutex_exit(&iosram_mutex);
24921708Sstevel 		return (rv);
24931708Sstevel 	}
24941708Sstevel 
24951708Sstevel 	/*
24961708Sstevel 	 * Disable SBBC interrupts. SBBC interrupts are enabled
24971708Sstevel 	 * once the interrupt handler is registered.
24981708Sstevel 	 */
24991708Sstevel 	ddi_put32(softp->sbbc_handle,
25001708Sstevel 	    &(softp->sbbc_region->int_enable.reg), 0x0);
25011708Sstevel 
25021708Sstevel 	/*
25031708Sstevel 	 * Clear hardware semaphore value if appropriate.
25041708Sstevel 	 * When the first SBBC is mapped in by the IOSRAM driver,
25051708Sstevel 	 * the value of the semaphore should be initialized only
25061708Sstevel 	 * if it is not held by SMS. For subsequent SBBC's, the
25071708Sstevel 	 * semaphore will be always initialized.
25081708Sstevel 	 */
25091708Sstevel 	sema_val = IOSRAM_SEMA_RD(softp);
25101708Sstevel 
25111708Sstevel 	if (!iosram_master) {
25121708Sstevel 		/* the first SBBC is being mapped in */
25131708Sstevel 		if (!(IOSRAM_SEMA_IS_HELD(sema_val) &&
25141708Sstevel 		    IOSRAM_SEMA_GET_IDX(sema_val) == IOSRAM_SEMA_SMS_IDX)) {
25151708Sstevel 			/* not held by SMS, we clear the semaphore */
25161708Sstevel 			IOSRAM_SEMA_WR(softp, 0);
25171708Sstevel 		}
25181708Sstevel 	} else {
25191708Sstevel 		/* not the first SBBC, we clear the semaphore */
25201708Sstevel 		IOSRAM_SEMA_WR(softp, 0);
25211708Sstevel 	}
25221708Sstevel 
25231708Sstevel 	mutex_exit(&softp->intr_mutex);
25241708Sstevel 	mutex_exit(&iosram_mutex);
25251708Sstevel 	return (0);
25261708Sstevel }
25271708Sstevel 
25281708Sstevel 
25291708Sstevel static int
25301708Sstevel iosram_setup_map(struct iosramsoft *softp)
25311708Sstevel {
25321708Sstevel 	int				instance = softp->instance;
25331708Sstevel 	dev_info_t			*dip = softp->dip;
25341708Sstevel 	int				portid;
25351708Sstevel 	int				proplen;
25361708Sstevel 	caddr_t				propvalue;
25371708Sstevel 	struct ddi_device_acc_attr	attr;
25381708Sstevel 
25391708Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
25401708Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
25411708Sstevel 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
25421708Sstevel 
25431708Sstevel 	/*
25441708Sstevel 	 * Lookup IOSRAM_REG_PROP property to find out our IOSRAM length
25451708Sstevel 	 */
25461708Sstevel 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
25471708Sstevel 	    DDI_PROP_DONTPASS, IOSRAM_REG_PROP, (caddr_t)&propvalue,
25481708Sstevel 	    &proplen) != DDI_PROP_SUCCESS) {
25491708Sstevel 		cmn_err(CE_WARN, "iosram(%d): can't find register property.\n",
25501708Sstevel 		    instance);
25511708Sstevel 		return (DDI_FAILURE);
25521708Sstevel 	} else {
25531708Sstevel 		iosram_reg_t	*regprop = (iosram_reg_t *)propvalue;
25541708Sstevel 
25551708Sstevel 		DPRINTF(1, ("SetupMap(%d): Got reg prop: %x %x %x\n",
25561708Sstevel 		    instance, regprop->addr_hi,
25571708Sstevel 		    regprop->addr_lo, regprop->size));
25581708Sstevel 
25591708Sstevel 		softp->iosramlen = regprop->size;
25601708Sstevel 
25611708Sstevel 		kmem_free(propvalue, proplen);
25621708Sstevel 	}
25631708Sstevel 	DPRINTF(1, ("SetupMap(%d): IOSRAM length: 0x%x\n", instance,
25641708Sstevel 	    softp->iosramlen));
25651708Sstevel 	softp->handle = NULL;
25661708Sstevel 
25671708Sstevel 	/*
25681708Sstevel 	 * To minimize boot time, we map the entire IOSRAM as opposed to
25691708Sstevel 	 * mapping individual chunk via ddi_regs_map_setup() call.
25701708Sstevel 	 */
25711708Sstevel 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&softp->iosramp,
25721708Sstevel 	    0x0, softp->iosramlen, &attr, &softp->handle) != DDI_SUCCESS) {
25731708Sstevel 		cmn_err(CE_WARN, "iosram(%d): failed to map IOSRAM len:%x\n",
25741708Sstevel 		    instance, softp->iosramlen);
25751708Sstevel 		iosram_remove_map(softp);
25761708Sstevel 		return (DDI_FAILURE);
25771708Sstevel 	}
25781708Sstevel 
25791708Sstevel 	/*
25801708Sstevel 	 * Lookup PORTID property on my parent hierarchy
25811708Sstevel 	 */
25821708Sstevel 	proplen = sizeof (portid);
25831708Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
25841708Sstevel 	    0, IOSRAM_PORTID_PROP, (caddr_t)&portid,
25851708Sstevel 	    &proplen) != DDI_PROP_SUCCESS) {
25861708Sstevel 		cmn_err(CE_WARN, "iosram(%d): can't find portid property.\n",
25871708Sstevel 		    instance);
25881708Sstevel 		iosram_remove_map(softp);
25891708Sstevel 		return (DDI_FAILURE);
25901708Sstevel 	}
25911708Sstevel 	softp->portid = portid;
25921708Sstevel 
25931708Sstevel 	if (iosram_sbbc_setup_map(softp) != DDI_SUCCESS) {
25941708Sstevel 		cmn_err(CE_WARN, "iosram(%d): can't map SBBC region.\n",
25951708Sstevel 		    instance);
25961708Sstevel 		iosram_remove_map(softp);
25971708Sstevel 		return (DDI_FAILURE);
25981708Sstevel 	}
25991708Sstevel 
26001708Sstevel 	mutex_enter(&iosram_mutex);
26011708Sstevel 	softp->state |= IOSRAM_STATE_MAPPED;
26021708Sstevel 	mutex_exit(&iosram_mutex);
26031708Sstevel 
26041708Sstevel 	return (DDI_SUCCESS);
26051708Sstevel }
26061708Sstevel 
26071708Sstevel 
26081708Sstevel static void
26091708Sstevel iosram_remove_map(struct iosramsoft *softp)
26101708Sstevel {
26111708Sstevel 	mutex_enter(&iosram_mutex);
26121708Sstevel 
26131708Sstevel 	ASSERT((softp->state & IOSRAM_STATE_MASTER) == 0);
26141708Sstevel 
26151708Sstevel 	if (softp->handle) {
26161708Sstevel 		ddi_regs_map_free(&softp->handle);
26171708Sstevel 		softp->handle = NULL;
26181708Sstevel 	}
26191708Sstevel 	softp->iosramp = NULL;
26201708Sstevel 
26211708Sstevel 	/*
26221708Sstevel 	 * Umap SBBC registers region. Shared with handler for SBBC
26231708Sstevel 	 * interrupts, take intr_mutex.
26241708Sstevel 	 */
26251708Sstevel 	mutex_enter(&softp->intr_mutex);
26261708Sstevel 	if (softp->sbbc_region) {
26271708Sstevel 		ddi_regs_map_free(&softp->sbbc_handle);
26281708Sstevel 		softp->sbbc_region = NULL;
26291708Sstevel 	}
26301708Sstevel 	mutex_exit(&softp->intr_mutex);
26311708Sstevel 
26321708Sstevel 	softp->state &= ~IOSRAM_STATE_MAPPED;
26331708Sstevel 
26341708Sstevel 	mutex_exit(&iosram_mutex);
26351708Sstevel }
26361708Sstevel 
26371708Sstevel 
26381708Sstevel /*
26391708Sstevel  * iosram_is_chosen(struct iosramsoft *softp)
26401708Sstevel  *
26411708Sstevel  *	Looks up "chosen" node property to
26421708Sstevel  *	determine if it is the chosen IOSRAM.
26431708Sstevel  */
26441708Sstevel static int
26451708Sstevel iosram_is_chosen(struct iosramsoft *softp)
26461708Sstevel {
26471708Sstevel 	char		chosen_iosram[MAXNAMELEN];
26481708Sstevel 	char		pn[MAXNAMELEN];
26491708Sstevel 	int		nodeid;
26501708Sstevel 	int		chosen;
26511708Sstevel 	pnode_t		dnode;
26521708Sstevel 
26531708Sstevel 	/*
26541708Sstevel 	 * Get /chosen node info. prom interface will handle errors.
26551708Sstevel 	 */
26561708Sstevel 	dnode = prom_chosennode();
26571708Sstevel 
26581708Sstevel 	/*
26591708Sstevel 	 * Look for the "iosram" property on the chosen node with a prom
26601708Sstevel 	 * interface as ddi_find_devinfo() couldn't be used (calls
26611708Sstevel 	 * ddi_walk_devs() that creates one extra lock on the device tree).
26621708Sstevel 	 */
26631708Sstevel 	if (prom_getprop(dnode, IOSRAM_CHOSEN_PROP, (caddr_t)&nodeid) <= 0) {
26641708Sstevel 		/*
26651708Sstevel 		 * Can't find IOSRAM_CHOSEN_PROP property under chosen node
26661708Sstevel 		 */
26671708Sstevel 		cmn_err(CE_WARN,
26681708Sstevel 		    "iosram(%d): can't find chosen iosram property\n",
26691708Sstevel 		    softp->instance);
26701708Sstevel 		return (0);
26711708Sstevel 	}
26721708Sstevel 
26731708Sstevel 	DPRINTF(1, ("iosram(%d): Got '%x' for chosen '%s' property\n",
26741708Sstevel 	    softp->instance, nodeid, IOSRAM_CHOSEN_PROP));
26751708Sstevel 
26761708Sstevel 	/*
26771708Sstevel 	 * get the full OBP pathname of this node
26781708Sstevel 	 */
26791708Sstevel 	if (prom_phandle_to_path((phandle_t)nodeid, chosen_iosram,
26801708Sstevel 	    sizeof (chosen_iosram)) < 0) {
26811708Sstevel 		cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", nodeid);
26821708Sstevel 		return (0);
26831708Sstevel 	}
26841708Sstevel 	DPRINTF(1, ("iosram(%d): prom_phandle_to_path(%x) is '%s'\n",
26851708Sstevel 	    softp->instance, nodeid, chosen_iosram));
26861708Sstevel 
26871708Sstevel 	(void) ddi_pathname(softp->dip, pn);
26881708Sstevel 	DPRINTF(1, ("iosram(%d): ddi_pathname(%p) is '%s'\n",
26891708Sstevel 	    softp->instance, softp->dip, pn));
26901708Sstevel 
26911708Sstevel 	chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0;
26921708Sstevel 	DPRINTF(1, ("iosram(%d): ... %s\n", softp->instance,
26931708Sstevel 	    chosen ? "MASTER" : "SLAVE"));
26941708Sstevel 	IOSRAMLOG(1, "iosram(%d): ... %s\n", softp->instance,
26951708Sstevel 	    (chosen ? "MASTER" : "SLAVE"), NULL, NULL);
26961708Sstevel 
26971708Sstevel 	return (chosen);
26981708Sstevel }
26991708Sstevel 
27001708Sstevel 
27011708Sstevel /*
27021708Sstevel  * iosram_set_master(struct iosramsoft *softp)
27031708Sstevel  *
27041708Sstevel  *	Set master tunnel to the specified IOSRAM
27051708Sstevel  *	Must be called while holding iosram_mutex.
27061708Sstevel  */
27071708Sstevel static void
27081708Sstevel iosram_set_master(struct iosramsoft *softp)
27091708Sstevel {
27101708Sstevel 	ASSERT(mutex_owned(&iosram_mutex));
27111708Sstevel 	ASSERT(softp != NULL);
27121708Sstevel 	ASSERT(softp->state & IOSRAM_STATE_MAPPED);
27131708Sstevel 	ASSERT(IOSRAM_GET_HDRFIELD32(softp, status) == IOSRAM_VALID);
27141708Sstevel 
27151708Sstevel 	/*
27161708Sstevel 	 * Clear MASTER flag on any previous IOSRAM master, if any
27171708Sstevel 	 */
27181708Sstevel 	if (iosram_master && (iosram_master != softp)) {
27191708Sstevel 		iosram_master->state &= ~IOSRAM_STATE_MASTER;
27201708Sstevel 	}
27211708Sstevel 
27221708Sstevel 	/*
27231708Sstevel 	 * Setup new IOSRAM master
27241708Sstevel 	 */
27251708Sstevel 	iosram_update_addrs(softp);
27261708Sstevel 	iosram_handle = softp->handle;
27271708Sstevel 	softp->state |= IOSRAM_STATE_MASTER;
27281708Sstevel 	softp->tswitch_ok++;
27291708Sstevel 	iosram_master = softp;
27301708Sstevel 
27311708Sstevel 	IOSRAMLOG(1, "SETMASTER: softp:%p instance:%d\n", softp,
27321708Sstevel 	    softp->instance, NULL, NULL);
27331708Sstevel }
27341708Sstevel 
27351708Sstevel 
27361708Sstevel /*
27371708Sstevel  * iosram_read_toc()
27381708Sstevel  *
27391708Sstevel  *	Read the TOC from an IOSRAM instance that has been mapped in.
27401708Sstevel  *	If the TOC is flawed or the IOSRAM isn't valid, return an error.
27411708Sstevel  */
27421708Sstevel static int
27431708Sstevel iosram_read_toc(struct iosramsoft *softp)
27441708Sstevel {
27451708Sstevel 	int			i;
27461708Sstevel 	int			instance = softp->instance;
27471708Sstevel 	uint8_t			*toc_entryp;
27481708Sstevel 	iosram_flags_t		*flagsp = NULL;
27491708Sstevel 	int			new_nchunks;
27501708Sstevel 	iosram_chunk_t		*new_chunks;
27511708Sstevel 	iosram_chunk_t		*chunkp;
27521708Sstevel 	iosram_chunk_t		*old_chunkp;
27531708Sstevel 	iosram_toc_entry_t	index;
27541708Sstevel 
27551708Sstevel 	/*
27561708Sstevel 	 * Never try to read the TOC out of an unmapped IOSRAM.
27571708Sstevel 	 */
27581708Sstevel 	ASSERT(softp->state & IOSRAM_STATE_MAPPED);
27591708Sstevel 
27601708Sstevel 	mutex_enter(&iosram_mutex);
27611708Sstevel 
27621708Sstevel 	/*
27631708Sstevel 	 * Check to make sure this IOSRAM is marked valid.  Return
27641708Sstevel 	 * an error if it isn't.
27651708Sstevel 	 */
27661708Sstevel 	if (IOSRAM_GET_HDRFIELD32(softp, status) != IOSRAM_VALID) {
27671708Sstevel 		DPRINTF(1, ("iosram_read_toc(%d): IOSRAM not flagged valid\n",
27681708Sstevel 		    instance));
27691708Sstevel 		mutex_exit(&iosram_mutex);
27701708Sstevel 		return (EINVAL);
27711708Sstevel 	}
27721708Sstevel 
27731708Sstevel 	/*
27741708Sstevel 	 * Get the location of the TOC.
27751708Sstevel 	 */
27761708Sstevel 	toc_entryp = softp->iosramp + IOSRAM_GET_HDRFIELD32(softp, toc_offset);
27771708Sstevel 
27781708Sstevel 	/*
27791708Sstevel 	 * Read the index entry from the TOC and make sure it looks correct.
27801708Sstevel 	 */
27811708Sstevel 	ddi_rep_get8(softp->handle, (uint8_t *)&index, toc_entryp,
27821708Sstevel 	    sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR);
27831708Sstevel 	if ((index.key != IOSRAM_INDEX_KEY) ||
27841708Sstevel 	    (index.off != IOSRAM_INDEX_OFF)) {
27851708Sstevel 		cmn_err(CE_WARN, "iosram(%d): invalid TOC index.\n", instance);
27861708Sstevel 		mutex_exit(&iosram_mutex);
27871708Sstevel 		return (EINVAL);
27881708Sstevel 	}
27891708Sstevel 
27901708Sstevel 	/*
27911708Sstevel 	 * Allocate storage for the new chunks array and initialize it with data
27921708Sstevel 	 * from the TOC and callback data from the corresponding old chunk, if
27931708Sstevel 	 * it exists.
27941708Sstevel 	 */
27951708Sstevel 	new_nchunks = index.len - 1;
27961708Sstevel 	new_chunks = (iosram_chunk_t *)kmem_zalloc(new_nchunks *
27971708Sstevel 	    sizeof (iosram_chunk_t), KM_SLEEP);
27981708Sstevel 	for (i = 0, chunkp = new_chunks; i < new_nchunks; i++, chunkp++) {
27991708Sstevel 		toc_entryp += sizeof (iosram_toc_entry_t);
28001708Sstevel 		ddi_rep_get8(softp->handle, (uint8_t *)&(chunkp->toc_data),
28011708Sstevel 		    toc_entryp, sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR);
28021708Sstevel 		chunkp->hash = NULL;
28031708Sstevel 		if ((chunkp->toc_data.off < softp->iosramlen) &&
28041708Sstevel 		    (chunkp->toc_data.len <= softp->iosramlen) &&
28051708Sstevel 		    ((chunkp->toc_data.off + chunkp->toc_data.len) <=
28061708Sstevel 		    softp->iosramlen)) {
28071708Sstevel 			chunkp->basep = softp->iosramp + chunkp->toc_data.off;
28081708Sstevel 			DPRINTF(1,
28091708Sstevel 			    ("iosram_read_toc(%d): k:%x o:%x l:%x p:%x\n",
28101708Sstevel 			    instance, chunkp->toc_data.key,
28111708Sstevel 			    chunkp->toc_data.off, chunkp->toc_data.len,
28121708Sstevel 			    chunkp->basep));
28131708Sstevel 		} else {
28141708Sstevel 			cmn_err(CE_WARN, "iosram(%d): TOC entry %d"
28151708Sstevel 			    "out of range... off:%x  len:%x\n",
28161708Sstevel 			    instance, i + 1, chunkp->toc_data.off,
28171708Sstevel 			    chunkp->toc_data.len);
28181708Sstevel 			kmem_free(new_chunks, new_nchunks *
28191708Sstevel 			    sizeof (iosram_chunk_t));
28201708Sstevel 			mutex_exit(&iosram_mutex);
28211708Sstevel 			return (EINVAL);
28221708Sstevel 		}
28231708Sstevel 
28241708Sstevel 		/*
28251708Sstevel 		 * Note the existence of the flags chunk, which is required in
28261708Sstevel 		 * a correct TOC.
28271708Sstevel 		 */
28281708Sstevel 		if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
28291708Sstevel 			flagsp = (iosram_flags_t *)chunkp->basep;
28301708Sstevel 		}
28311708Sstevel 
28321708Sstevel 		/*
28331708Sstevel 		 * If there was an entry for this chunk in the old list, copy
28341708Sstevel 		 * the callback data from old to new storage.
28351708Sstevel 		 */
28361708Sstevel 		if ((nchunks > 0) &&
28371708Sstevel 		    ((old_chunkp = iosram_find_chunk(chunkp->toc_data.key)) !=
28381708Sstevel 		    NULL)) {
28391708Sstevel 			bcopy(&(old_chunkp->cback), &(chunkp->cback),
28401708Sstevel 			    sizeof (iosram_cback_t));
28411708Sstevel 		}
28421708Sstevel 	}
28431708Sstevel 	/*
28441708Sstevel 	 * The TOC is malformed if there is no entry for the flags chunk.
28451708Sstevel 	 */
28461708Sstevel 	if (flagsp == NULL) {
28471708Sstevel 		kmem_free(new_chunks, new_nchunks * sizeof (iosram_chunk_t));
28481708Sstevel 		mutex_exit(&iosram_mutex);
28491708Sstevel 		return (EINVAL);
28501708Sstevel 	}
28511708Sstevel 
28521708Sstevel 	/*
28531708Sstevel 	 * Free any memory that is no longer needed and install the new data
28541708Sstevel 	 * as current data.
28551708Sstevel 	 */
28561708Sstevel 	if (chunks != NULL) {
28571708Sstevel 		kmem_free(chunks, nchunks * sizeof (iosram_chunk_t));
28581708Sstevel 	}
28591708Sstevel 	chunks = new_chunks;
28601708Sstevel 	nchunks = new_nchunks;
28611708Sstevel 	iosram_init_hashtab();
28621708Sstevel 
28631708Sstevel 	mutex_exit(&iosram_mutex);
28641708Sstevel 	return (0);
28651708Sstevel }
28661708Sstevel 
28671708Sstevel 
28681708Sstevel /*
28691708Sstevel  * iosram_init_hashtab()
28701708Sstevel  *
28711708Sstevel  *	Initialize the hash table and populate it with the IOSRAM
28721708Sstevel  *	chunks previously read from the TOC.  The caller must hold the
28731708Sstevel  *	ioram_mutex lock.
28741708Sstevel  */
28751708Sstevel static void
28761708Sstevel iosram_init_hashtab(void)
28771708Sstevel {
28781708Sstevel 	int		i, bucket;
28791708Sstevel 	iosram_chunk_t	*chunkp;
28801708Sstevel 
28811708Sstevel 	ASSERT(mutex_owned(&iosram_mutex));
28821708Sstevel 
28831708Sstevel 	for (i = 0; i < IOSRAM_HASHSZ; i++) {
28841708Sstevel 		iosram_hashtab[i] = NULL;
28851708Sstevel 	}
28861708Sstevel 
28871708Sstevel 	if (chunks) {
28881708Sstevel 		for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
28891708Sstevel 			/*
28901708Sstevel 			 * Hide the flags chunk by leaving it out of the hash
28911708Sstevel 			 * table.
28921708Sstevel 			 */
28931708Sstevel 			if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
28941708Sstevel 				continue;
28951708Sstevel 			}
28961708Sstevel 
28971708Sstevel 			/*
28981708Sstevel 			 * Add the current chunk to the hash table.
28991708Sstevel 			 */
29001708Sstevel 			bucket = IOSRAM_HASH(chunkp->toc_data.key);
29011708Sstevel 			chunkp->hash = iosram_hashtab[bucket];
29021708Sstevel 			iosram_hashtab[bucket] = chunkp;
29031708Sstevel 		}
29041708Sstevel 	}
29051708Sstevel }
29061708Sstevel 
29071708Sstevel 
29081708Sstevel /*
29091708Sstevel  * iosram_update_addrs()
29101708Sstevel  *
29111708Sstevel  *	Process the chunk list, updating each chunk's basep, which is a pointer
29121708Sstevel  *	to the beginning of the chunk's memory in kvaddr space.  Record the
29131708Sstevel  *	basep value of the flags chunk to speed up flag access.  The caller
29141708Sstevel  *	must hold the iosram_mutex lock.
29151708Sstevel  */
29161708Sstevel static void
29171708Sstevel iosram_update_addrs(struct iosramsoft *softp)
29181708Sstevel {
29191708Sstevel 	int		i;
29201708Sstevel 	iosram_flags_t	*flagsp;
29211708Sstevel 	iosram_chunk_t	*chunkp;
29221708Sstevel 
29231708Sstevel 	ASSERT(mutex_owned(&iosram_mutex));
29241708Sstevel 
29251708Sstevel 	/*
29261708Sstevel 	 * First go through all of the chunks updating their base pointers and
29271708Sstevel 	 * looking for the flags chunk.
29281708Sstevel 	 */
29291708Sstevel 	for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
29301708Sstevel 		chunkp->basep = softp->iosramp + chunkp->toc_data.off;
29311708Sstevel 		if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
29321708Sstevel 			flagsp = (iosram_flags_t *)(chunkp->basep);
29331708Sstevel 			DPRINTF(1,
29341708Sstevel 			    ("iosram_update_addrs flags: o:0x%08x p:%p",
29351708Sstevel 			    chunkp->toc_data.off, flagsp));
29361708Sstevel 		}
29371708Sstevel 	}
29381708Sstevel 
29391708Sstevel 	/*
29401708Sstevel 	 * Now, go through and update each chunk's flags pointer.  This can't be
29411708Sstevel 	 * done in the first loop because we don't have the address of the flags
29421708Sstevel 	 * chunk yet.
29431708Sstevel 	 */
29441708Sstevel 	for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
29451708Sstevel 		chunkp->flagsp = flagsp++;
29461708Sstevel 		DPRINTF(1, ("iosram_update_addrs: k:0x%x f:%p\n",
29471708Sstevel 		    chunkp->toc_data.key, chunkp->flagsp));
29481708Sstevel 	}
29491708Sstevel }
29501708Sstevel 
29511708Sstevel /*
29521708Sstevel  * iosram_find_chunk(key)
29531708Sstevel  *
29541708Sstevel  *	Return a pointer to iosram_chunk structure corresponding to the
29551708Sstevel  *	"key" IOSRAM chunk.  The caller must hold the iosram_mutex lock.
29561708Sstevel  */
29571708Sstevel static iosram_chunk_t *
29581708Sstevel iosram_find_chunk(uint32_t key)
29591708Sstevel {
29601708Sstevel 	iosram_chunk_t	*chunkp;
29611708Sstevel 	int		index = IOSRAM_HASH(key);
29621708Sstevel 
29631708Sstevel 	ASSERT(mutex_owned(&iosram_mutex));
29641708Sstevel 
29651708Sstevel 	for (chunkp = iosram_hashtab[index]; chunkp; chunkp = chunkp->hash) {
29661708Sstevel 		if (chunkp->toc_data.key == key) {
29671708Sstevel 			break;
29681708Sstevel 		}
29691708Sstevel 	}
29701708Sstevel 
29711708Sstevel 	return (chunkp);
29721708Sstevel }
29731708Sstevel 
29741708Sstevel 
29751708Sstevel /*
29761708Sstevel  * iosram_add_intr(iosramsoft_t *)
29771708Sstevel  */
29781708Sstevel static int
29791708Sstevel iosram_add_intr(iosramsoft_t *softp)
29801708Sstevel {
29811708Sstevel 	IOSRAMLOG(2, "ADDINTR: softp:%p  instance:%d\n",
29821708Sstevel 	    softp, softp->instance, NULL, NULL);
29831708Sstevel 
29841708Sstevel 	if (ddi_add_softintr(softp->dip, DDI_SOFTINT_MED,
29851708Sstevel 	    &softp->softintr_id, &softp->soft_iblk, NULL,
29861708Sstevel 	    iosram_softintr, (caddr_t)softp) != DDI_SUCCESS) {
29871708Sstevel 		cmn_err(CE_WARN,
29881708Sstevel 		    "iosram(%d): Can't register softintr.\n",
29891708Sstevel 		    softp->instance);
29901708Sstevel 		return (DDI_FAILURE);
29911708Sstevel 	}
29921708Sstevel 
29931708Sstevel 	if (ddi_add_intr(softp->dip, 0, &softp->real_iblk, NULL,
29941708Sstevel 	    iosram_intr, (caddr_t)softp) != DDI_SUCCESS) {
29951708Sstevel 		cmn_err(CE_WARN,
29961708Sstevel 		    "iosram(%d): Can't register intr"
29971708Sstevel 		    " handler.\n", softp->instance);
29981708Sstevel 		ddi_remove_softintr(softp->softintr_id);
29991708Sstevel 		return (DDI_FAILURE);
30001708Sstevel 	}
30011708Sstevel 
30021708Sstevel 	/*
30031708Sstevel 	 * Enable SBBC interrupts
30041708Sstevel 	 */
30051708Sstevel 	ddi_put32(softp->sbbc_handle, &(softp->sbbc_region->int_enable.reg),
30061708Sstevel 	    IOSRAM_SBBC_INT0|IOSRAM_SBBC_INT1);
30071708Sstevel 
30081708Sstevel 	return (DDI_SUCCESS);
30091708Sstevel }
30101708Sstevel 
30111708Sstevel 
30121708Sstevel /*
30131708Sstevel  * iosram_remove_intr(iosramsoft_t *)
30141708Sstevel  */
30151708Sstevel static int
30161708Sstevel iosram_remove_intr(iosramsoft_t *softp)
30171708Sstevel {
30181708Sstevel 	IOSRAMLOG(2, "REMINTR: softp:%p  instance:%d\n",
30191708Sstevel 	    softp, softp->instance, NULL, NULL);
30201708Sstevel 
30211708Sstevel 	/*
30221708Sstevel 	 * Disable SBBC interrupts if SBBC is mapped in
30231708Sstevel 	 */
30241708Sstevel 	if (softp->sbbc_region) {
30251708Sstevel 		ddi_put32(softp->sbbc_handle,
30261708Sstevel 		    &(softp->sbbc_region->int_enable.reg), 0);
30271708Sstevel 	}
30281708Sstevel 
30291708Sstevel 	/*
30301708Sstevel 	 * Remove SBBC interrupt handler
30311708Sstevel 	 */
30321708Sstevel 	ddi_remove_intr(softp->dip, 0, softp->real_iblk);
30331708Sstevel 
30341708Sstevel 	/*
30351708Sstevel 	 * Remove soft interrupt handler
30361708Sstevel 	 */
30371708Sstevel 	mutex_enter(&iosram_mutex);
30381708Sstevel 	if (softp->softintr_id != NULL) {
30391708Sstevel 		ddi_remove_softintr(softp->softintr_id);
30401708Sstevel 		softp->softintr_id = NULL;
30411708Sstevel 	}
30421708Sstevel 	mutex_exit(&iosram_mutex);
30431708Sstevel 
30441708Sstevel 	return (0);
30451708Sstevel }
30461708Sstevel 
30471708Sstevel 
30481708Sstevel /*
30491708Sstevel  * iosram_add_instance(iosramsoft_t *)
30501708Sstevel  * Must be called while holding iosram_mutex
30511708Sstevel  */
30521708Sstevel static void
30531708Sstevel iosram_add_instance(iosramsoft_t *new_softp)
30541708Sstevel {
30551708Sstevel #ifdef DEBUG
30561708Sstevel 	int		instance = new_softp->instance;
30571708Sstevel 	iosramsoft_t	*softp;
30581708Sstevel #endif
30591708Sstevel 
30601708Sstevel 	ASSERT(mutex_owned(&iosram_mutex));
30611708Sstevel 
30621708Sstevel #if defined(DEBUG)
30631708Sstevel 	/* Verify that this instance is not in the list */
30641708Sstevel 	for (softp = iosram_instances; softp != NULL; softp = softp->next) {
30651708Sstevel 		ASSERT(softp->instance != instance);
30661708Sstevel 	}
30671708Sstevel #endif
30681708Sstevel 
30691708Sstevel 	/*
30701708Sstevel 	 * Add this instance to the list
30711708Sstevel 	 */
30721708Sstevel 	if (iosram_instances != NULL) {
30731708Sstevel 		iosram_instances->prev = new_softp;
30741708Sstevel 	}
30751708Sstevel 	new_softp->next = iosram_instances;
30761708Sstevel 	new_softp->prev = NULL;
30771708Sstevel 	iosram_instances = new_softp;
30781708Sstevel }
30791708Sstevel 
30801708Sstevel 
30811708Sstevel /*
30821708Sstevel  * iosram_remove_instance(int instance)
30831708Sstevel  * Must be called while holding iosram_mutex
30841708Sstevel  */
30851708Sstevel static void
30861708Sstevel iosram_remove_instance(int instance)
30871708Sstevel {
30881708Sstevel 	iosramsoft_t *softp;
30891708Sstevel 
30901708Sstevel 	/*
30911708Sstevel 	 * Remove specified instance from the iosram_instances list so that
30921708Sstevel 	 * it can't be chosen for tunnel in future.
30931708Sstevel 	 */
30941708Sstevel 	ASSERT(mutex_owned(&iosram_mutex));
30951708Sstevel 
30961708Sstevel 	for (softp = iosram_instances; softp != NULL; softp = softp->next) {
30971708Sstevel 		if (softp->instance == instance) {
30981708Sstevel 			if (softp->next != NULL) {
30991708Sstevel 				softp->next->prev = softp->prev;
31001708Sstevel 			}
31011708Sstevel 			if (softp->prev != NULL) {
31021708Sstevel 				softp->prev->next = softp->next;
31031708Sstevel 			}
31041708Sstevel 			if (iosram_instances == softp) {
31051708Sstevel 				iosram_instances = softp->next;
31061708Sstevel 			}
31071708Sstevel 
31081708Sstevel 			return;
31091708Sstevel 		}
31101708Sstevel 	}
31111708Sstevel }
31121708Sstevel 
31131708Sstevel 
31141708Sstevel /*
31151708Sstevel  * iosram_sema_acquire: Acquire hardware semaphore.
31161708Sstevel  * Return 0 if the semaphore could be acquired, or one of the following
31171708Sstevel  * possible values:
31181708Sstevel  * EAGAIN: there is a tunnel switch in progress
31191708Sstevel  * EBUSY: the semaphore was already "held"
31201708Sstevel  * ENXIO:  an IO error occured (e.g. SBBC not mapped)
31211708Sstevel  * If old_value is not NULL, the location it points to will be updated
31221708Sstevel  * with the semaphore value read when attempting to acquire it.
31231708Sstevel  */
31241708Sstevel int
31251708Sstevel iosram_sema_acquire(uint32_t *old_value)
31261708Sstevel {
31271708Sstevel 	struct iosramsoft	*softp;
31281708Sstevel 	int			rv;
31291708Sstevel 	uint32_t		sema_val;
31301708Sstevel 
31311708Sstevel 	DPRINTF(2, ("IOSRAM: in iosram_sema_acquire\n"));
31321708Sstevel 
31331708Sstevel 	mutex_enter(&iosram_mutex);
31341708Sstevel 
31351708Sstevel 	/*
31361708Sstevel 	 * Disallow access if there is a tunnel switch in progress.
31371708Sstevel 	 */
31381708Sstevel 	if (iosram_tswitch_active) {
31391708Sstevel 		mutex_exit(&iosram_mutex);
31401708Sstevel 		return (EAGAIN);
31411708Sstevel 	}
31421708Sstevel 
31431708Sstevel 	/*
31441708Sstevel 	 * Use current master IOSRAM for operation, fail if none is
31451708Sstevel 	 * currently active.
31461708Sstevel 	 */
31471708Sstevel 	if ((softp = iosram_master) == NULL) {
31481708Sstevel 		mutex_exit(&iosram_mutex);
31491708Sstevel 		DPRINTF(1, ("IOSRAM: iosram_sema_acquire: no master\n"));
31501708Sstevel 		return (ENXIO);
31511708Sstevel 	}
31521708Sstevel 
31531708Sstevel 	mutex_enter(&softp->intr_mutex);
31541708Sstevel 
31551708Sstevel 	/*
31561708Sstevel 	 * Fail if SBBC region has not been mapped. This shouldn't
31571708Sstevel 	 * happen if we have a master IOSRAM, but we double-check.
31581708Sstevel 	 */
31591708Sstevel 	if (softp->sbbc_region == NULL) {
31601708Sstevel 		mutex_exit(&softp->intr_mutex);
31611708Sstevel 		mutex_exit(&iosram_mutex);
31621708Sstevel 		DPRINTF(1, ("IOSRAM(%d): iosram_sema_acquire: "
31631708Sstevel 		    "SBBC not mapped\n", softp->instance));
31641708Sstevel 		return (ENXIO);
31651708Sstevel 	}
31661708Sstevel 
31671708Sstevel 	/* read semaphore value */
31681708Sstevel 	sema_val = IOSRAM_SEMA_RD(softp);
31691708Sstevel 	if (old_value != NULL)
31701708Sstevel 		*old_value = sema_val;
31711708Sstevel 
31721708Sstevel 	if (IOSRAM_SEMA_IS_HELD(sema_val)) {
31731708Sstevel 		/* semaphore was held by someone else */
31741708Sstevel 		rv = EBUSY;
31751708Sstevel 	} else {
31761708Sstevel 		/* semaphore was not held, we just acquired it */
31771708Sstevel 		rv = 0;
31781708Sstevel 	}
31791708Sstevel 
31801708Sstevel 	mutex_exit(&softp->intr_mutex);
31811708Sstevel 	mutex_exit(&iosram_mutex);
31821708Sstevel 
31831708Sstevel 	DPRINTF(1, ("IOSRAM(%d): iosram_sema_acquire: "
31841708Sstevel 	    "old value=0x%x rv=%d\n", softp->instance, sema_val, rv));
31851708Sstevel 
31861708Sstevel 	return (rv);
31871708Sstevel }
31881708Sstevel 
31891708Sstevel 
31901708Sstevel /*
31911708Sstevel  * iosram_sema_release: Release hardware semaphore.
31921708Sstevel  * This function will "release" the hardware semaphore, and return 0 on
31931708Sstevel  * success. If an error occured, one of the following values will be
31941708Sstevel  * returned:
31951708Sstevel  * EAGAIN: there is a tunnel switch in progress
31961708Sstevel  * ENXIO:  an IO error occured (e.g. SBBC not mapped)
31971708Sstevel  */
31981708Sstevel int
31991708Sstevel iosram_sema_release(void)
32001708Sstevel {
32011708Sstevel 	struct iosramsoft	*softp;
32021708Sstevel 
32031708Sstevel 	DPRINTF(2, ("IOSRAM: in iosram_sema_release\n"));
32041708Sstevel 
32051708Sstevel 	mutex_enter(&iosram_mutex);
32061708Sstevel 
32071708Sstevel 	/*
32081708Sstevel 	 * Disallow access if there is a tunnel switch in progress.
32091708Sstevel 	 */
32101708Sstevel 	if (iosram_tswitch_active) {
32111708Sstevel 		mutex_exit(&iosram_mutex);
32121708Sstevel 		return (EAGAIN);
32131708Sstevel 	}
32141708Sstevel 
32151708Sstevel 	/*
32161708Sstevel 	 * Use current master IOSRAM for operation, fail if none is
32171708Sstevel 	 * currently active.
32181708Sstevel 	 */
32191708Sstevel 	if ((softp = iosram_master) == NULL) {
32201708Sstevel 		mutex_exit(&iosram_mutex);
32211708Sstevel 		DPRINTF(1, ("IOSRAM: iosram_sema_release: no master\n"));
32221708Sstevel 		return (ENXIO);
32231708Sstevel 	}
32241708Sstevel 
32251708Sstevel 	mutex_enter(&softp->intr_mutex);
32261708Sstevel 
32271708Sstevel 	/*
32281708Sstevel 	 * Fail if SBBC region has not been mapped in. This shouldn't
32291708Sstevel 	 * happen if we have a master IOSRAM, but we double-check.
32301708Sstevel 	 */
32311708Sstevel 	if (softp->sbbc_region == NULL) {
32321708Sstevel 		mutex_exit(&softp->intr_mutex);
32331708Sstevel 		mutex_exit(&iosram_mutex);
32341708Sstevel 		DPRINTF(1, ("IOSRAM(%d): iosram_sema_release: "
32351708Sstevel 		    "SBBC not mapped\n", softp->instance));
32361708Sstevel 		return (ENXIO);
32371708Sstevel 	}
32381708Sstevel 
32391708Sstevel 	/* Release semaphore by clearing our semaphore register */
32401708Sstevel 	IOSRAM_SEMA_WR(softp, 0);
32411708Sstevel 
32421708Sstevel 	mutex_exit(&softp->intr_mutex);
32431708Sstevel 	mutex_exit(&iosram_mutex);
32441708Sstevel 
32451708Sstevel 	DPRINTF(1, ("IOSRAM(%d): iosram_sema_release: success\n",
32461708Sstevel 	    softp->instance));
32471708Sstevel 
32481708Sstevel 	return (0);
32491708Sstevel }
32501708Sstevel 
32511708Sstevel 
32521708Sstevel #if defined(IOSRAM_LOG)
32531708Sstevel void
32541708Sstevel iosram_log(caddr_t fmt, intptr_t a1, intptr_t a2, intptr_t a3, intptr_t a4)
32551708Sstevel {
32561708Sstevel 	uint32_t	seq;
32571708Sstevel 	iosram_log_t	*logp;
32581708Sstevel 
32591708Sstevel 	mutex_enter(&iosram_log_mutex);
32601708Sstevel 
32611708Sstevel 	seq = iosram_logseq++;
32621708Sstevel 	logp = &iosram_logbuf[seq % IOSRAM_MAXLOG];
32631708Sstevel 	logp->seq = seq;
32641708Sstevel 	logp->tstamp = lbolt;
32651708Sstevel 	logp->fmt = fmt;
32661708Sstevel 	logp->arg1 = a1;
32671708Sstevel 	logp->arg2 = a2;
32681708Sstevel 	logp->arg3 = a3;
32691708Sstevel 	logp->arg4 = a4;
32701708Sstevel 
32711708Sstevel 	mutex_exit(&iosram_log_mutex);
32721708Sstevel 
32731708Sstevel 	if (iosram_log_print) {
32741708Sstevel 		cmn_err(CE_CONT, "#%x @%lx ", logp->seq, logp->tstamp);
32751708Sstevel 		if (logp->fmt) {
32761708Sstevel 			cmn_err(CE_CONT, logp->fmt, logp->arg1, logp->arg2,
32771708Sstevel 			    logp->arg3, logp->arg4);
32781708Sstevel 			if (logp->fmt[strlen(logp->fmt)-1] != '\n') {
32791708Sstevel 				cmn_err(CE_CONT, "\n");
32801708Sstevel 			}
32811708Sstevel 		} else {
32821708Sstevel 			cmn_err(CE_CONT, "fmt:%p args: %lx %lx %lx %lx\n",
32831708Sstevel 			    logp->fmt, logp->arg1, logp->arg2, logp->arg3,
32841708Sstevel 			    logp->arg4);
32851708Sstevel 		}
32861708Sstevel 	}
32871708Sstevel }
32881708Sstevel #endif /* IOSRAM_LOG */
32891708Sstevel 
32901708Sstevel 
32911708Sstevel #if defined(DEBUG)
32921708Sstevel /*
32931708Sstevel  * iosram_get_keys(buf, len)
32941708Sstevel  *	Return IOSRAM TOC in the specified buffer
32951708Sstevel  */
32961708Sstevel static int
32971708Sstevel iosram_get_keys(iosram_toc_entry_t *bufp, uint32_t *len)
32981708Sstevel {
32991708Sstevel 	struct iosram_chunk	*chunkp;
33001708Sstevel 	int			error = 0;
33011708Sstevel 	int			i;
33021708Sstevel 	int			cnt = (*len) / sizeof (iosram_toc_entry_t);
33031708Sstevel 
33041708Sstevel 	IOSRAMLOG(2, "iosram_get_keys(bufp:%p *len:%x)\n", bufp, *len, NULL,
33051708Sstevel 	    NULL);
33061708Sstevel 
33071708Sstevel 	/*
33081708Sstevel 	 * Copy data while holding the lock to prevent any data
33091708Sstevel 	 * corruption or invalid pointer dereferencing.
33101708Sstevel 	 */
33111708Sstevel 	mutex_enter(&iosram_mutex);
33121708Sstevel 
33131708Sstevel 	if (iosram_master == NULL) {
33141708Sstevel 		error = EIO;
33151708Sstevel 	} else {
33161708Sstevel 		for (i = 0, chunkp = chunks; i < nchunks && i < cnt;
33171708Sstevel 		    i++, chunkp++) {
33181708Sstevel 			bufp[i].key = chunkp->toc_data.key;
33191708Sstevel 			bufp[i].off = chunkp->toc_data.off;
33201708Sstevel 			bufp[i].len = chunkp->toc_data.len;
33211708Sstevel 			bufp[i].unused = chunkp->toc_data.unused;
33221708Sstevel 		}
33231708Sstevel 		*len = i * sizeof (iosram_toc_entry_t);
33241708Sstevel 	}
33251708Sstevel 
33261708Sstevel 	mutex_exit(&iosram_mutex);
33271708Sstevel 	return (error);
33281708Sstevel }
33291708Sstevel 
33301708Sstevel 
33311708Sstevel /*
33321708Sstevel  * iosram_print_state(instance)
33331708Sstevel  */
33341708Sstevel static void
33351708Sstevel iosram_print_state(int instance)
33361708Sstevel {
33371708Sstevel 	struct iosramsoft	*softp;
33381708Sstevel 	char			pn[MAXNAMELEN];
33391708Sstevel 
33401708Sstevel 	if (instance < 0) {
33411708Sstevel 		softp = iosram_master;
33421708Sstevel 	} else {
33431708Sstevel 		softp = ddi_get_soft_state(iosramsoft_statep, instance);
33441708Sstevel 	}
33451708Sstevel 
33461708Sstevel 	if (softp == NULL) {
33471708Sstevel 		cmn_err(CE_CONT, "iosram_print_state: Can't find instance %d\n",
33481708Sstevel 		    instance);
33491708Sstevel 		return;
33501708Sstevel 	}
33511708Sstevel 	instance = softp->instance;
33521708Sstevel 
33531708Sstevel 	mutex_enter(&iosram_mutex);
33541708Sstevel 	mutex_enter(&softp->intr_mutex);
33551708Sstevel 
33561708Sstevel 	cmn_err(CE_CONT, "iosram_print_state(%d): ... %s\n", instance,
33571708Sstevel 	    ((softp == iosram_master) ? "MASTER" : "SLAVE"));
33581708Sstevel 
33591708Sstevel 	(void) ddi_pathname(softp->dip, pn);
33601708Sstevel 	cmn_err(CE_CONT, "  pathname:%s\n", pn);
33611708Sstevel 	cmn_err(CE_CONT, "  instance:%d  portid:%d iosramlen:0x%x\n",
33621708Sstevel 	    softp->instance, softp->portid, softp->iosramlen);
33631708Sstevel 	cmn_err(CE_CONT, "  softp:%p  handle:%p  iosramp:%p\n", softp,
33641708Sstevel 	    softp->handle, softp->iosramp);
33651708Sstevel 	cmn_err(CE_CONT, "  state:0x%x  tswitch_ok:%x  tswitch_fail:%x\n",
33661708Sstevel 	    softp->state, softp->tswitch_ok, softp->tswitch_fail);
33671708Sstevel 	cmn_err(CE_CONT, "  softintr_id:%p  intr_busy:%x  intr_pending:%x\n",
33681708Sstevel 	    softp->softintr_id, softp->intr_busy, softp->intr_pending);
33691708Sstevel 
33701708Sstevel 	mutex_exit(&softp->intr_mutex);
33711708Sstevel 	mutex_exit(&iosram_mutex);
33721708Sstevel }
33731708Sstevel 
33741708Sstevel 
33751708Sstevel /*
33761708Sstevel  * iosram_print_stats()
33771708Sstevel  */
33781708Sstevel static void
33791708Sstevel iosram_print_stats()
33801708Sstevel {
33811708Sstevel 	uint32_t	calls;
33821708Sstevel 
33831708Sstevel 	cmn_err(CE_CONT, "iosram_stats:\n");
33841708Sstevel 	calls = iosram_stats.read;
33851708Sstevel 	cmn_err(CE_CONT, " read  ... calls:%x  bytes:%lx  avg_sz:%x\n",
33861708Sstevel 	    calls, iosram_stats.bread,
33871708Sstevel 	    (uint32_t)((calls != 0) ? (iosram_stats.bread/calls) : 0));
33881708Sstevel 
33891708Sstevel 	calls = iosram_stats.write;
33901708Sstevel 	cmn_err(CE_CONT, " write ... calls:%x  bytes:%lx  avg_sz:%x\n",
33911708Sstevel 	    calls, iosram_stats.bwrite,
33921708Sstevel 	    (uint32_t)((calls != 0) ? (iosram_stats.bwrite/calls) : 0));
33931708Sstevel 
33941708Sstevel 	cmn_err(CE_CONT, " intr recv (real:%x  soft:%x)  sent:%x  cback:%x\n",
33951708Sstevel 	    iosram_stats.intr_recv, iosram_stats.sintr_recv,
33961708Sstevel 	    iosram_stats.intr_send, iosram_stats.callbacks);
33971708Sstevel 
33981708Sstevel 	cmn_err(CE_CONT, " tswitch: %x  getflag:%x  setflag:%x\n",
33991708Sstevel 	    iosram_stats.tswitch, iosram_stats.getflag,
34001708Sstevel 	    iosram_stats.setflag);
34011708Sstevel 
34021708Sstevel 	cmn_err(CE_CONT, " iosram_rw_active_max: %x\n", iosram_rw_active_max);
34031708Sstevel }
34041708Sstevel 
34051708Sstevel 
34061708Sstevel static void
34071708Sstevel iosram_print_cback()
34081708Sstevel {
34091708Sstevel 	iosram_chunk_t	*chunkp;
34101708Sstevel 	int		i;
34111708Sstevel 
34121708Sstevel 	/*
34131708Sstevel 	 * Print callback handlers
34141708Sstevel 	 */
34151708Sstevel 	mutex_enter(&iosram_mutex);
34161708Sstevel 
34171708Sstevel 	cmn_err(CE_CONT, "IOSRAM callbacks:\n");
34181708Sstevel 	for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
34191708Sstevel 		if (chunkp->cback.handler) {
34201708Sstevel 			cmn_err(CE_CONT, "  %2d: key:0x%x  hdlr:%p  arg:%p "
34211708Sstevel 			    "busy:%d unreg:%d\n", i, chunkp->toc_data.key,
34221708Sstevel 			    chunkp->cback.handler, chunkp->cback.arg,
34231708Sstevel 			    chunkp->cback.busy, chunkp->cback.unregister);
34241708Sstevel 		}
34251708Sstevel 	}
34261708Sstevel 	mutex_exit(&iosram_mutex);
34271708Sstevel }
34281708Sstevel 
34291708Sstevel 
34301708Sstevel static void
34311708Sstevel iosram_print_flags()
34321708Sstevel {
34331708Sstevel 	int		i;
34341708Sstevel 	uint32_t	*keys;
34351708Sstevel 	iosram_flags_t	*flags;
34361708Sstevel 
34371708Sstevel 	mutex_enter(&iosram_mutex);
34381708Sstevel 
34391708Sstevel 	if (iosram_master == NULL) {
34401708Sstevel 		mutex_exit(&iosram_mutex);
34411708Sstevel 		cmn_err(CE_CONT, "IOSRAM Flags: not accessible\n");
34421708Sstevel 		return;
34431708Sstevel 	}
34441708Sstevel 
34451708Sstevel 	keys = kmem_alloc(nchunks * sizeof (uint32_t), KM_SLEEP);
34461708Sstevel 	flags = kmem_alloc(nchunks * sizeof (iosram_flags_t), KM_SLEEP);
34471708Sstevel 
34481708Sstevel 	for (i = 0; i < nchunks; i++) {
34491708Sstevel 		keys[i] = chunks[i].toc_data.key;
34501708Sstevel 		ddi_rep_get8(iosram_handle, (uint8_t *)&(flags[i]),
34511708Sstevel 		    (uint8_t *)(chunks[i].flagsp), sizeof (iosram_flags_t),
34521708Sstevel 		    DDI_DEV_AUTOINCR);
34531708Sstevel 	}
34541708Sstevel 
34551708Sstevel 	mutex_exit(&iosram_mutex);
34561708Sstevel 
34571708Sstevel 	cmn_err(CE_CONT, "IOSRAM Flags:\n");
34581708Sstevel 	for (i = 0; i < nchunks; i++) {
34591708Sstevel 		cmn_err(CE_CONT,
34601708Sstevel 		    "  %2d: key: 0x%x  data_valid:%x  int_pending:%x\n",
34611708Sstevel 		    i, keys[i], flags[i].data_valid, flags[i].int_pending);
34621708Sstevel 	}
34631708Sstevel 
34641708Sstevel 	kmem_free(keys, nchunks * sizeof (uint32_t));
34651708Sstevel 	kmem_free(flags, nchunks * sizeof (iosram_flags_t));
34661708Sstevel }
34671708Sstevel 
34681708Sstevel 
34691708Sstevel /*PRINTFLIKE1*/
34701708Sstevel static void
34711708Sstevel iosram_dprintf(const char *fmt, ...)
34721708Sstevel {
34731708Sstevel 	char	msg_buf[256];
34741708Sstevel 	va_list	adx;
34751708Sstevel 
34761708Sstevel 	va_start(adx, fmt);
34771708Sstevel 	vsprintf(msg_buf, fmt, adx);
34781708Sstevel 	va_end(adx);
34791708Sstevel 
34801708Sstevel 	cmn_err(CE_CONT, "%s", msg_buf);
34811708Sstevel }
34821708Sstevel #endif /* DEBUG */
34831708Sstevel 
34841708Sstevel 
34851708Sstevel #if IOSRAM_LOG
34861708Sstevel /*
34871708Sstevel  * iosram_print_log(int cnt)
34881708Sstevel  *	Print last few entries of the IOSRAM log in reverse order
34891708Sstevel  */
34901708Sstevel static void
34911708Sstevel iosram_print_log(int cnt)
34921708Sstevel {
34931708Sstevel 	int	i;
34941708Sstevel 
34951708Sstevel 	if (cnt <= 0) {
34961708Sstevel 		cnt = 20;
34971708Sstevel 	} else if (cnt > IOSRAM_MAXLOG) {
34981708Sstevel 		cnt = IOSRAM_MAXLOG;
34991708Sstevel 	}
35001708Sstevel 
35011708Sstevel 
35021708Sstevel 	cmn_err(CE_CONT,
35031708Sstevel 	    "\niosram_logseq: 0x%x  lbolt: %lx  iosram_log_level:%x\n",
35041708Sstevel 	    iosram_logseq, lbolt, iosram_log_level);
35051708Sstevel 	cmn_err(CE_CONT, "iosram_logbuf: %p  max entries:0x%x\n",
35061708Sstevel 	    iosram_logbuf, IOSRAM_MAXLOG);
35071708Sstevel 	for (i = iosram_logseq;  --i >= 0 && --cnt >= 0; ) {
35081708Sstevel 		iosram_log_t	*logp;
35091708Sstevel 
35101708Sstevel 		mutex_enter(&iosram_log_mutex);
35111708Sstevel 
35121708Sstevel 		logp = &iosram_logbuf[i %IOSRAM_MAXLOG];
35131708Sstevel 		cmn_err(CE_CONT, "#%x @%lx ", logp->seq, logp->tstamp);
35141708Sstevel 
35151708Sstevel 		if (logp->fmt) {
35161708Sstevel 			cmn_err(CE_CONT, logp->fmt, logp->arg1, logp->arg2,
35171708Sstevel 			    logp->arg3, logp->arg4);
35181708Sstevel 			if (logp->fmt[strlen(logp->fmt)-1] != '\n') {
35191708Sstevel 				cmn_err(CE_CONT, "\n");
35201708Sstevel 			}
35211708Sstevel 		} else {
35221708Sstevel 			cmn_err(CE_CONT, "fmt:%p args: %lx %lx %lx %lx\n",
35231708Sstevel 			    logp->fmt, logp->arg1, logp->arg2,
35241708Sstevel 			    logp->arg3, logp->arg4);
35251708Sstevel 		}
35261708Sstevel 
35271708Sstevel 		mutex_exit(&iosram_log_mutex);
35281708Sstevel 	}
35291708Sstevel }
35301708Sstevel #endif	/* IOSRAM_LOG */
3531