xref: /onnv-gate/usr/src/uts/sun4u/serengeti/io/sgsbbc_iosram.c (revision 11311:639e7bc0b42f)
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*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel /*
281708Sstevel  * Driver for handling Serengeti I/O SRAM
291708Sstevel  * for Solaris <-> SC comm.
301708Sstevel  */
311708Sstevel 
321708Sstevel #include <sys/types.h>
331708Sstevel #include <sys/systm.h>
341708Sstevel #include <sys/cpuvar.h>
351708Sstevel #include <sys/dditypes.h>
361708Sstevel #include <sys/sunndi.h>
371708Sstevel #include <sys/param.h>
381708Sstevel #include <sys/mutex.h>
391708Sstevel #include <sys/sysmacros.h>
401708Sstevel #include <sys/errno.h>
411708Sstevel #include <sys/file.h>
421708Sstevel #include <sys/kmem.h>
431708Sstevel #include <sys/promif.h>
441708Sstevel #include <sys/prom_plat.h>
451708Sstevel #include <sys/sunddi.h>
461708Sstevel #include <sys/ddi.h>
471708Sstevel 
481708Sstevel #include <sys/serengeti.h>
491708Sstevel #include <sys/sgsbbc_priv.h>
501708Sstevel #include <sys/sgsbbc_iosram_priv.h>
511708Sstevel #include <sys/sgsbbc_mailbox_priv.h>
521708Sstevel 
531708Sstevel /*
541708Sstevel  * Local stuff
551708Sstevel  */
561708Sstevel static int iosram_rw(int, uint32_t, caddr_t, uint32_t, int);
571708Sstevel static int iosram_convert_key(char *);
581708Sstevel static int iosram_switch_intr(void);
591708Sstevel static int tunnel_init(sbbc_softstate_t *, tunnel_t *);
601708Sstevel static void tunnel_fini(tunnel_t *);
611708Sstevel static void tunnel_commit(sbbc_softstate_t *, tunnel_t *);
621708Sstevel static void clear_break();
631708Sstevel 
641708Sstevel #define	IOSRAM_GETB(tunnel, buf, sram, count) \
651708Sstevel 	ddi_rep_get8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR)
661708Sstevel 
671708Sstevel #define	IOSRAM_PUTB(tunnel, buf, sram, count) \
681708Sstevel 	ddi_rep_put8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR)
691708Sstevel 
701708Sstevel #define	IOSRAM_PUT(tunnel, sram, buf, size) \
711708Sstevel 	/* CSTYLED */ \
721708Sstevel 	ddi_put##size(tunnel->reg_handle, (uint##size##_t *)sram, \
731708Sstevel 	/* CSTYLED */ \
741708Sstevel 	*((uint##size##_t *)buf))
751708Sstevel 
761708Sstevel #define	IOSRAM_GET(tunnel, sram, buf, size) \
771708Sstevel 	/* CSTYLED */ \
781708Sstevel 	*(uint##size##_t *)buf = ddi_get##size(tunnel->reg_handle, \
791708Sstevel 	/* CSTYLED */ \
801708Sstevel 	(uint##size##_t *)sram)
811708Sstevel 
821708Sstevel /*
831708Sstevel  * sgsbbc_iosram_is_chosen(struct sbbc_softstate *softsp)
841708Sstevel  *
851708Sstevel  *      Looks up "chosen" node property to
861708Sstevel  *      determine if it is the chosen IOSRAM.
871708Sstevel  */
881708Sstevel int
sgsbbc_iosram_is_chosen(sbbc_softstate_t * softsp)891708Sstevel sgsbbc_iosram_is_chosen(sbbc_softstate_t *softsp)
901708Sstevel {
911708Sstevel 	char		pn[MAXNAMELEN];
921708Sstevel 	char		chosen_iosram[MAXNAMELEN];
931708Sstevel 	int		nodeid;
941708Sstevel 	int		chosen;
951708Sstevel 	uint_t		tunnel;
961708Sstevel 	extern		pnode_t chosen_nodeid;
971708Sstevel 
981708Sstevel 	ASSERT(chosen_nodeid);
991708Sstevel 
1001708Sstevel 	nodeid = chosen_nodeid;
1011708Sstevel 	(void) prom_getprop(nodeid, "iosram", (caddr_t)&tunnel);
1021708Sstevel 
1031708Sstevel 	/*
1041708Sstevel 	 * get the full OBP pathname of this node
1051708Sstevel 	 */
1061708Sstevel 	if (prom_phandle_to_path((phandle_t)tunnel, chosen_iosram,
1071708Sstevel 		sizeof (chosen_iosram)) < 0) {
1081708Sstevel 		cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", tunnel);
1091708Sstevel 		return (0);
1101708Sstevel 	}
1111708Sstevel 
1121708Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): prom_phandle_to_path(%x) is '%s'\n",
1131708Sstevel 	softsp->sbbc_instance, nodeid, chosen_iosram);
1141708Sstevel 
1151708Sstevel 	(void) ddi_pathname(softsp->dip, pn);
1161708Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ddi_pathname(%p) is '%s'\n",
117*11311SSurya.Prakki@Sun.COM 	    softsp->sbbc_instance, (void *)softsp->dip, pn);
1181708Sstevel 
1191708Sstevel 	chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0;
1201708Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
121*11311SSurya.Prakki@Sun.COM 	    chosen? "MASTER" : "SLAVE");
1221708Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
123*11311SSurya.Prakki@Sun.COM 	    (chosen ? "MASTER" : "SLAVE"));
1241708Sstevel 
1251708Sstevel 	return (chosen);
1261708Sstevel }
1271708Sstevel 
1281708Sstevel void
iosram_init()1291708Sstevel iosram_init()
1301708Sstevel {
1311708Sstevel 	int	i;
1321708Sstevel 
1331708Sstevel 	if ((master_iosram = kmem_zalloc(sizeof (struct chosen_iosram),
1341708Sstevel 	    KM_NOSLEEP)) == NULL) {
1351708Sstevel 		prom_printf("Can't allocate space for Chosen IOSRAM\n");
1361708Sstevel 		panic("Can't allocate space for Chosen IOSRAM");
1371708Sstevel 	}
1381708Sstevel 
1391708Sstevel 	if ((master_iosram->tunnel = kmem_zalloc(sizeof (tunnel_t),
1401708Sstevel 	    KM_NOSLEEP)) == NULL) {
1411708Sstevel 		prom_printf("Can't allocate space for tunnel\n");
1421708Sstevel 		panic("Can't allocate space for tunnel");
1431708Sstevel 	}
1441708Sstevel 
1451708Sstevel 	master_iosram->iosram_sbbc = NULL;
1461708Sstevel 
1471708Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
1481708Sstevel 		master_iosram->tunnel->tunnel_keys[i].key = 0;
1491708Sstevel 		master_iosram->tunnel->tunnel_keys[i].base = NULL;
1501708Sstevel 		master_iosram->tunnel->tunnel_keys[i].size = 0;
1511708Sstevel 	}
1521708Sstevel 
1531708Sstevel 	for (i = 0; i < SBBC_MAX_INTRS; i++)
1541708Sstevel 		master_iosram->intrs[i].sbbc_handler = NULL;
1551708Sstevel 
1561708Sstevel 	mutex_init(&master_iosram->iosram_lock, NULL, MUTEX_DEFAULT, NULL);
1571708Sstevel 	rw_init(&master_iosram->tunnel_lock, NULL, RW_DEFAULT, NULL);
1581708Sstevel }
1591708Sstevel void
iosram_fini()1601708Sstevel iosram_fini()
1611708Sstevel {
1621708Sstevel 	struct	tunnel_key	*tunnel;
1631708Sstevel 	int			i;
1641708Sstevel 
1651708Sstevel 	rw_destroy(&master_iosram->tunnel_lock);
1661708Sstevel 	mutex_destroy(&master_iosram->iosram_lock);
1671708Sstevel 
1681708Sstevel 	/*
1691708Sstevel 	 * destroy any tunnel maps
1701708Sstevel 	 */
1711708Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
1721708Sstevel 		tunnel = &master_iosram->tunnel->tunnel_keys[i];
1731708Sstevel 		if (tunnel->base != NULL) {
1741708Sstevel 			ddi_regs_map_free(&tunnel->reg_handle);
1751708Sstevel 			tunnel->base = NULL;
1761708Sstevel 		}
1771708Sstevel 	}
1781708Sstevel 
1791708Sstevel 	kmem_free(master_iosram->tunnel, sizeof (tunnel_t));
1801708Sstevel 
1811708Sstevel 	kmem_free(master_iosram, sizeof (struct chosen_iosram));
1821708Sstevel 
1831708Sstevel 	master_iosram = NULL;
1841708Sstevel }
1851708Sstevel 
1861708Sstevel static void
check_iosram_ver(uint16_t version)1871708Sstevel check_iosram_ver(uint16_t version)
1881708Sstevel {
1891708Sstevel 	uint8_t	max_ver = MAX_IOSRAM_TOC_VER;
1901708Sstevel 	uint8_t	major_ver =
1911708Sstevel 		(version >> IOSRAM_TOC_VER_SHIFT) & IOSRAM_TOC_VER_MASK;
1921708Sstevel 
1931708Sstevel 	SGSBBC_DBG_ALL("IOSRAM TOC version: %d.%d\n", major_ver,
1941708Sstevel 		version & IOSRAM_TOC_VER_MASK);
1951708Sstevel 	SGSBBC_DBG_ALL("Max supported IOSRAM TOC version: %d\n", max_ver);
1961708Sstevel 	if (major_ver > max_ver) {
1971708Sstevel 		panic("Up-rev System Controller version.\n"
1981708Sstevel 		    "You must restore an earlier revision of System "
1991708Sstevel 		    "Controller firmware, or upgrade Solaris.\n"
2001708Sstevel 		    "Please consult the System Controller release notice "
2011708Sstevel 		    "for additional details.");
2021708Sstevel 	}
2031708Sstevel }
2041708Sstevel 
2051708Sstevel static void
tunnel_commit(sbbc_softstate_t * softsp,tunnel_t * new_tunnel)2061708Sstevel tunnel_commit(sbbc_softstate_t *softsp, tunnel_t *new_tunnel)
2071708Sstevel {
2081708Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
2091708Sstevel 
2101708Sstevel 	master_iosram->iosram_sbbc = softsp;
2111708Sstevel 	master_iosram->tunnel = new_tunnel;
2121708Sstevel 	softsp->chosen = TRUE;
2131708Sstevel 
2141708Sstevel 	/*
2151708Sstevel 	 * SBBC has pointer to interrupt handlers for simplicity
2161708Sstevel 	 */
2171708Sstevel 	softsp->intr_hdlrs = master_iosram->intrs;
2181708Sstevel }
2191708Sstevel 
2201708Sstevel static int
tunnel_init(sbbc_softstate_t * softsp,tunnel_t * new_tunnel)2211708Sstevel tunnel_init(sbbc_softstate_t *softsp, tunnel_t *new_tunnel)
2221708Sstevel {
2231708Sstevel 	struct iosram_toc		*toc = NULL;
2241708Sstevel 	int				i, key;
2251708Sstevel 	struct	tunnel_key		*tunnel;
2261708Sstevel 	ddi_acc_handle_t		toc_handle;
2271708Sstevel 	struct ddi_device_acc_attr	attr;
2281708Sstevel 
2291708Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
2301708Sstevel 
2311708Sstevel 	if ((softsp == (sbbc_softstate_t *)NULL) ||
2321708Sstevel 		(new_tunnel == (tunnel_t *)NULL)) {
2331708Sstevel 
2341708Sstevel 		return (DDI_FAILURE);
2351708Sstevel 	}
2361708Sstevel 
2371708Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
2381708Sstevel 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
2391708Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
2401708Sstevel 
2411708Sstevel 	SGSBBC_DBG_ALL("map in the IOSRAM TOC at offset %x\n",
2421708Sstevel 		softsp->sram_toc);
2431708Sstevel 
2441708Sstevel 	/*
2451708Sstevel 	 * First map in the TOC, then set up the tunnel
2461708Sstevel 	 */
2471708Sstevel 	if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
2481708Sstevel 		(caddr_t *)&toc,
2491708Sstevel 		SBBC_SRAM_OFFSET + softsp->sram_toc,
2501708Sstevel 		sizeof (struct iosram_toc),
2511708Sstevel 		&attr, &toc_handle) != DDI_SUCCESS) {
2521708Sstevel 			cmn_err(CE_WARN, "sbbc%d: unable to map SRAM "
2531708Sstevel 			    "registers", ddi_get_instance(softsp->dip));
2541708Sstevel 			return (DDI_FAILURE);
2551708Sstevel 	}
256*11311SSurya.Prakki@Sun.COM 	SGSBBC_DBG_ALL("dip=%p mapped TOC %p\n", (void *)softsp->dip,
257*11311SSurya.Prakki@Sun.COM 	    (void *)toc);
2581708Sstevel 
2591708Sstevel 	check_iosram_ver(toc->iosram_version);
2601708Sstevel 
2611708Sstevel 	for (i = 0; i < toc->iosram_tagno; i++) {
2621708Sstevel 		key = iosram_convert_key(toc->iosram_keys[i].key);
2631708Sstevel 		if ((key > 0) && (key < SBBC_MAX_KEYS)) {
2641708Sstevel 			tunnel = &new_tunnel->tunnel_keys[key];
2651708Sstevel 			tunnel->key = key;
2661708Sstevel 			tunnel->size = toc->iosram_keys[i].size;
2671708Sstevel 			/*
2681708Sstevel 			 * map in the SRAM area using the offset
2691708Sstevel 			 * from the base of SRAM + SRAM offset into
2701708Sstevel 			 * the register property for the SBBC base
2711708Sstevel 			 * address
2721708Sstevel 			 */
2731708Sstevel 			if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
2741708Sstevel 				(caddr_t *)&tunnel->base,
2751708Sstevel 				SBBC_SRAM_OFFSET + toc->iosram_keys[i].offset,
2761708Sstevel 				toc->iosram_keys[i].size, &attr,
2771708Sstevel 				&tunnel->reg_handle) != DDI_SUCCESS) {
2781708Sstevel 				cmn_err(CE_WARN, "sbbc%d: unable to map SRAM "
2791708Sstevel 				    "registers", ddi_get_instance(softsp->dip));
2801708Sstevel 				return (DDI_FAILURE);
2811708Sstevel 			}
2821708Sstevel 			SGSBBC_DBG_ALL("%d: key %s size %d offset %x addr %p\n",
283*11311SSurya.Prakki@Sun.COM 			    i, toc->iosram_keys[i].key,
284*11311SSurya.Prakki@Sun.COM 			    toc->iosram_keys[i].size,
285*11311SSurya.Prakki@Sun.COM 			    toc->iosram_keys[i].offset,
286*11311SSurya.Prakki@Sun.COM 			    (void *)tunnel->base);
2871708Sstevel 
2881708Sstevel 		}
2891708Sstevel 	}
2901708Sstevel 
2911708Sstevel 
2921708Sstevel 	if (toc != NULL) {
2931708Sstevel 		ddi_regs_map_free(&toc_handle);
2941708Sstevel 	}
2951708Sstevel 
2961708Sstevel 	/*
2971708Sstevel 	 * Set up the 'interrupt reason' SRAM pointers
2981708Sstevel 	 * for the SBBC interrupt handler
2991708Sstevel 	 */
3001708Sstevel 	if (INVALID_KEY(new_tunnel, SBBC_SC_INTR_KEY)) {
3011708Sstevel 		/*
3021708Sstevel 		 * Can't really do much if these are not here
3031708Sstevel 		 */
3041708Sstevel 		prom_printf("No Interrupt Reason Fields set by SC\n");
3051708Sstevel 		cmn_err(CE_WARN, "No Interrupt Reason Fields set by SC");
3061708Sstevel 		return (DDI_FAILURE);
3071708Sstevel 	}
3081708Sstevel 
3091708Sstevel 	return (DDI_SUCCESS);
3101708Sstevel }
3111708Sstevel 
3121708Sstevel /*
3131708Sstevel  * Unmap a tunnel
3141708Sstevel  */
3151708Sstevel static void
tunnel_fini(tunnel_t * tunnel)3161708Sstevel tunnel_fini(tunnel_t *tunnel)
3171708Sstevel {
3181708Sstevel 	int	i;
3191708Sstevel 	struct	tunnel_key	*tunnel_key;
3201708Sstevel 
3211708Sstevel 	/*
3221708Sstevel 	 * Unmap the tunnel
3231708Sstevel 	 */
3241708Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
3251708Sstevel 		tunnel_key = &tunnel->tunnel_keys[i];
3261708Sstevel 		if (tunnel_key->base != NULL) {
3271708Sstevel 			ddi_regs_map_free(&tunnel_key->reg_handle);
3281708Sstevel 			tunnel_key->base = NULL;
3291708Sstevel 		}
3301708Sstevel 	}
3311708Sstevel }
3321708Sstevel 
3331708Sstevel static void
clear_break()3341708Sstevel clear_break()
3351708Sstevel {
3361708Sstevel 	struct tunnel_key	tunnel_key;
3371708Sstevel 	uint32_t		*intr_in_reason;
3381708Sstevel 	ddi_acc_handle_t	intr_in_handle;
3391708Sstevel 
3401708Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
3411708Sstevel 
3421708Sstevel 	tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY];
3431708Sstevel 	intr_in_reason = (uint32_t *)tunnel_key.base;
3441708Sstevel 	intr_in_handle = tunnel_key.reg_handle;
3451708Sstevel 	ddi_put32(intr_in_handle, intr_in_reason,
3461708Sstevel 		ddi_get32(intr_in_handle, intr_in_reason) & ~SBBC_CONSOLE_BRK);
3471708Sstevel }
3481708Sstevel 
3491708Sstevel int
iosram_tunnel_init(sbbc_softstate_t * softsp)3501708Sstevel iosram_tunnel_init(sbbc_softstate_t *softsp)
3511708Sstevel {
3521708Sstevel 	int	rc;
3531708Sstevel 
3541708Sstevel 	ASSERT(master_iosram);
3551708Sstevel 
3561708Sstevel 	mutex_enter(&master_iosram->iosram_lock);
3571708Sstevel 
3581708Sstevel 	if ((rc = tunnel_init(softsp, master_iosram->tunnel)) == DDI_SUCCESS) {
3591708Sstevel 		tunnel_commit(softsp, master_iosram->tunnel);
3601708Sstevel 		clear_break();
3611708Sstevel 	}
3621708Sstevel 
3631708Sstevel 
3641708Sstevel 	mutex_exit(&master_iosram->iosram_lock);
3651708Sstevel 
3661708Sstevel 	return (rc);
3671708Sstevel }
3681708Sstevel 
3691708Sstevel int
iosram_read(int key,uint32_t offset,caddr_t buf,uint32_t size)3701708Sstevel iosram_read(int key, uint32_t offset, caddr_t buf, uint32_t size)
3711708Sstevel {
3721708Sstevel 	return (iosram_rw(key, offset, buf, size, FREAD));
3731708Sstevel }
3741708Sstevel 
3751708Sstevel int
iosram_write(int key,uint32_t offset,caddr_t buf,uint32_t size)3761708Sstevel iosram_write(int key, uint32_t offset, caddr_t buf, uint32_t size)
3771708Sstevel {
3781708Sstevel 	return (iosram_rw(key, offset, buf, size, FWRITE));
3791708Sstevel }
3801708Sstevel 
3811708Sstevel 
3821708Sstevel static int
iosram_rw(int key,uint32_t offset,caddr_t buf,uint32_t size,int flag)3831708Sstevel iosram_rw(int key, uint32_t offset, caddr_t buf, uint32_t size, int flag)
3841708Sstevel {
3851708Sstevel 	struct	tunnel_key	*tunnel;
3861708Sstevel 	caddr_t 		sram_src;
3871708Sstevel 
3881708Sstevel 	/*
3891708Sstevel 	 * Return right away if there is nothing to read/write.
3901708Sstevel 	 */
3911708Sstevel 	if (size == 0)
3921708Sstevel 		return (0);
3931708Sstevel 
3941708Sstevel 	rw_enter(&master_iosram->tunnel_lock, RW_READER);
3951708Sstevel 
3961708Sstevel 	/*
3971708Sstevel 	 * Key not matched ?
3981708Sstevel 	 */
3991708Sstevel 	if (INVALID_KEY(master_iosram->tunnel, key)) {
4001708Sstevel 		rw_exit(&master_iosram->tunnel_lock);
4011708Sstevel 		return (ENXIO);
4021708Sstevel 	}
4031708Sstevel 
4041708Sstevel 	tunnel = &master_iosram->tunnel->tunnel_keys[key];
4051708Sstevel 	if ((offset + size) > tunnel->size) {
4061708Sstevel 		rw_exit(&master_iosram->tunnel_lock);
4071708Sstevel 		return (EFBIG);
4081708Sstevel 	}
4091708Sstevel 
4101708Sstevel 	sram_src = tunnel->base + offset;
4111708Sstevel 
4121708Sstevel 	/*
4131708Sstevel 	 * Atomic reads/writes might be necessary for some clients.
4141708Sstevel 	 * We assume that such clients could guarantee their buffers
4151708Sstevel 	 * are aligned at the boundary of the request sizes.  We also
4161708Sstevel 	 * assume that the source/destination of such requests are
4171708Sstevel 	 * aligned at the right boundaries in IOSRAM.  If either
4181708Sstevel 	 * condition fails, byte access is performed.
4191708Sstevel 	 */
4201708Sstevel 	if (flag == FREAD) {
4211708Sstevel 		switch (size) {
4221708Sstevel 		case sizeof (uint16_t):
4231708Sstevel 		case sizeof (uint32_t):
4241708Sstevel 		case sizeof (uint64_t):
4251708Sstevel 			if (IS_P2ALIGNED(sram_src, size) &&
4261708Sstevel 				IS_P2ALIGNED(buf, size)) {
4271708Sstevel 
4281708Sstevel 				if (size == sizeof (uint16_t))
4291708Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 16);
4301708Sstevel 				else if (size == sizeof (uint32_t))
4311708Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 32);
4321708Sstevel 				else
4331708Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 64);
4341708Sstevel 				break;
4351708Sstevel 			}
4361708Sstevel 			/* FALLTHRU */
4371708Sstevel 		default:
4381708Sstevel 			IOSRAM_GETB(tunnel, (uint8_t *)buf,
4391708Sstevel 				(uint8_t *)sram_src, (size_t)size);
4401708Sstevel 			break;
4411708Sstevel 		}
4421708Sstevel 	} else {
4431708Sstevel 		switch (size) {
4441708Sstevel 		case sizeof (uint16_t):
4451708Sstevel 		case sizeof (uint32_t):
4461708Sstevel 		case sizeof (uint64_t):
4471708Sstevel 			if (IS_P2ALIGNED(sram_src, size) &&
4481708Sstevel 				IS_P2ALIGNED(buf, size)) {
4491708Sstevel 
4501708Sstevel 				if (size == sizeof (uint16_t))
4511708Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 16);
4521708Sstevel 				else if (size == sizeof (uint32_t))
4531708Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 32);
4541708Sstevel 				else
4551708Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 64);
4561708Sstevel 				break;
4571708Sstevel 			}
4581708Sstevel 			/* FALLTHRU */
4591708Sstevel 		default:
4601708Sstevel 			IOSRAM_PUTB(tunnel, (uint8_t *)buf,
4611708Sstevel 				(uint8_t *)sram_src, (size_t)size);
4621708Sstevel 			break;
4631708Sstevel 		}
4641708Sstevel 	}
4651708Sstevel 
4661708Sstevel 	rw_exit(&master_iosram->tunnel_lock);
4671708Sstevel 	return (0);
4681708Sstevel 
4691708Sstevel }
4701708Sstevel 
4711708Sstevel int
iosram_size(int key)4721708Sstevel iosram_size(int key)
4731708Sstevel {
4741708Sstevel 	int size = -1;
4751708Sstevel 
4761708Sstevel 	rw_enter(&master_iosram->tunnel_lock, RW_READER);
4771708Sstevel 
4781708Sstevel 	/*
4791708Sstevel 	 * Key not matched ?
4801708Sstevel 	 */
4811708Sstevel 	if (!INVALID_KEY(master_iosram->tunnel, key))
4821708Sstevel 		size = master_iosram->tunnel->tunnel_keys[key].size;
4831708Sstevel 
4841708Sstevel 	rw_exit(&master_iosram->tunnel_lock);
4851708Sstevel 
4861708Sstevel 	return (size);
4871708Sstevel }
4881708Sstevel 
4891708Sstevel /*
4901708Sstevel  * Generate an interrupt to the SC using the SBBC EPLD
4911708Sstevel  *
4921708Sstevel  * Note: intr_num can be multiple interrupts OR'ed together
4931708Sstevel  */
4941708Sstevel int
iosram_send_intr(uint32_t intr_num)4951708Sstevel iosram_send_intr(uint32_t intr_num)
4961708Sstevel {
4971708Sstevel 
4981708Sstevel 	int		rc = 0;
4991708Sstevel 	uint32_t	intr_reason;
5001708Sstevel 	uint32_t	intr_enabled;
5011708Sstevel 
5021708Sstevel 	/*
5031708Sstevel 	 * Verify that we have already set up the master sbbc
5041708Sstevel 	 */
5051708Sstevel 	if (master_iosram == NULL)
5061708Sstevel 		return (ENXIO);
5071708Sstevel 
5081708Sstevel 	/*
5091708Sstevel 	 * Grab the lock to prevent tunnel switch in the middle
5101708Sstevel 	 * of sending an interrupt.
5111708Sstevel 	 */
5121708Sstevel 	mutex_enter(&master_iosram->iosram_lock);
5131708Sstevel 
5141708Sstevel 	if (master_iosram->iosram_sbbc == NULL) {
5151708Sstevel 		rc = ENXIO;
5161708Sstevel 		goto send_intr_exit;
5171708Sstevel 	}
5181708Sstevel 
5191708Sstevel 	if ((rc = sbbc_send_intr(master_iosram->iosram_sbbc, FALSE)) != 0) {
5201708Sstevel 		/*
5211708Sstevel 		 * previous interrupts have not been cleared yet by the SC
5221708Sstevel 		 */
5231708Sstevel 		goto send_intr_exit;
5241708Sstevel 	}
5251708Sstevel 
5261708Sstevel 	/*
5271708Sstevel 	 * Set a bit in the interrupt reason field
5281708Sstevel 	 * call back into the sbbc handler to hit the EPLD
5291708Sstevel 	 *
5301708Sstevel 	 * First check the interrupts enabled by the SC
5311708Sstevel 	 */
5321708Sstevel 	if ((rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0,
5331708Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
5341708Sstevel 
5351708Sstevel 		goto send_intr_exit;
5361708Sstevel 	}
5371708Sstevel 
5381708Sstevel 	if ((intr_enabled & intr_num) != intr_num) {
5391708Sstevel 		/*
5401708Sstevel 		 * at least one of the interrupts is
5411708Sstevel 		 * not enabled by the SC
5421708Sstevel 		 */
5431708Sstevel 		rc = ENOTSUP;
5441708Sstevel 		goto send_intr_exit;
5451708Sstevel 	}
5461708Sstevel 
5471708Sstevel 	if ((rc = iosram_read(SBBC_INTR_SC_KEY, 0,
5481708Sstevel 		(caddr_t)&intr_reason, sizeof (intr_reason))) != 0) {
5491708Sstevel 
5501708Sstevel 		goto send_intr_exit;
5511708Sstevel 	}
5521708Sstevel 
5531708Sstevel 	if ((intr_reason & intr_num) == intr_num) {
5541708Sstevel 		/*
5551708Sstevel 		 * All interrupts specified are already pending
5561708Sstevel 		 */
5571708Sstevel 		rc = EBUSY;
5581708Sstevel 		goto send_intr_exit;
5591708Sstevel 	}
5601708Sstevel 
5611708Sstevel 	intr_reason |= intr_num;
5621708Sstevel 
5631708Sstevel 	if ((rc = iosram_write(SBBC_INTR_SC_KEY, 0,
5641708Sstevel 		(caddr_t)&intr_reason, sizeof (intr_reason))) != 0) {
5651708Sstevel 
5661708Sstevel 		goto send_intr_exit;
5671708Sstevel 	}
5681708Sstevel 
5691708Sstevel 	/*
5701708Sstevel 	 * Hit the EPLD interrupt bit
5711708Sstevel 	 */
5721708Sstevel 
5731708Sstevel 	rc = sbbc_send_intr(master_iosram->iosram_sbbc, TRUE);
5741708Sstevel 
5751708Sstevel send_intr_exit:
5761708Sstevel 
5771708Sstevel 	mutex_exit(&master_iosram->iosram_lock);
5781708Sstevel 
5791708Sstevel 	return (rc);
5801708Sstevel }
5811708Sstevel 
5821708Sstevel /*
5831708Sstevel  * Register an interrupt handler
5841708Sstevel  */
5851708Sstevel int
iosram_reg_intr(uint32_t intr_num,sbbc_intrfunc_t intr_handler,caddr_t arg,uint_t * state,kmutex_t * lock)5861708Sstevel iosram_reg_intr(uint32_t intr_num, sbbc_intrfunc_t intr_handler,
5871708Sstevel 		caddr_t arg, uint_t *state, kmutex_t *lock)
5881708Sstevel {
5891708Sstevel 	sbbc_softstate_t	*softsp;
5901708Sstevel 	int			rc = 0;
5911708Sstevel 	sbbc_intrs_t		*intr;
5921708Sstevel 	int			intr_no;
5931708Sstevel 	uint32_t		intr_enabled;
5941708Sstevel 
5951708Sstevel 	/*
5961708Sstevel 	 * Verify that we have already set up the master sbbc
5971708Sstevel 	 */
5981708Sstevel 	if (master_iosram == NULL)
5991708Sstevel 		return (ENXIO);
6001708Sstevel 
6011708Sstevel 	/*
6021708Sstevel 	 * determine which bit is this intr_num for ?
6031708Sstevel 	 */
6041708Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
6051708Sstevel 		if (intr_num == (1 << intr_no))
6061708Sstevel 			break;
6071708Sstevel 	}
6081708Sstevel 
6091708Sstevel 	/*
6101708Sstevel 	 * Check the parameters
6111708Sstevel 	 */
6121708Sstevel 	if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS) ||
6131708Sstevel 		(intr_handler == NULL) || (state == NULL) ||
6141708Sstevel 		(lock == NULL))
6151708Sstevel 		return (EINVAL);
6161708Sstevel 
6171708Sstevel 	mutex_enter(&master_iosram->iosram_lock);
6181708Sstevel 
6191708Sstevel 	if ((softsp = master_iosram->iosram_sbbc) == NULL) {
6201708Sstevel 		mutex_exit(&master_iosram->iosram_lock);
6211708Sstevel 		return (ENXIO);
6221708Sstevel 	}
6231708Sstevel 
6241708Sstevel 	mutex_enter(&softsp->sbbc_lock);
6251708Sstevel 
6261708Sstevel 	intr = &master_iosram->intrs[intr_no];
6271708Sstevel 
6281708Sstevel 	if (intr->sbbc_handler != (sbbc_intrfunc_t)NULL) {
6291708Sstevel 		rc = EBUSY;
6301708Sstevel 		goto reg_intr_exit;
6311708Sstevel 	}
6321708Sstevel 
6331708Sstevel 	intr->sbbc_handler  = intr_handler;
6341708Sstevel 	intr->sbbc_arg = (void *)arg;
6351708Sstevel 	intr->sbbc_intr_state = state;
6361708Sstevel 	intr->sbbc_intr_lock = lock;
6371708Sstevel 	intr->sbbc_intr_next = (sbbc_intrs_t *)NULL;
6381708Sstevel 
6391708Sstevel 	/*
6401708Sstevel 	 * we need to make sure that the mutex is for
6411708Sstevel 	 * an ADAPTIVE lock, so call mutex_init() again with
6421708Sstevel 	 * the sbbc iblock cookie
6431708Sstevel 	 */
6441708Sstevel 	mutex_init(lock, NULL, MUTEX_DRIVER,
6451708Sstevel 		(void *)softsp->iblock);
6461708Sstevel 
6471708Sstevel 	if (ddi_add_softintr(softsp->dip, DDI_SOFTINT_HIGH,
6481708Sstevel 		&intr->sbbc_intr_id, NULL, NULL,
6491708Sstevel 		intr_handler, (caddr_t)arg) != DDI_SUCCESS) {
6501708Sstevel 
6511708Sstevel 		cmn_err(CE_WARN, "Can't add SBBC softint");
6521708Sstevel 		rc = EAGAIN;
6531708Sstevel 		goto reg_intr_exit;
6541708Sstevel 	}
6551708Sstevel 
6561708Sstevel 	/*
6571708Sstevel 	 * Set the bit in the Interrupts Enabled Field for this
6581708Sstevel 	 * interrupt
6591708Sstevel 	 */
6601708Sstevel 	if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
6611708Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
6621708Sstevel 
6631708Sstevel 		goto reg_intr_exit;
6641708Sstevel 	}
6651708Sstevel 
6661708Sstevel 	intr_enabled |= intr_num;
6671708Sstevel 
6681708Sstevel 	if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0,
6691708Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
6701708Sstevel 
6711708Sstevel 		goto reg_intr_exit;
6721708Sstevel 	}
6731708Sstevel 
6741708Sstevel reg_intr_exit:
6751708Sstevel 
6761708Sstevel 	mutex_exit(&softsp->sbbc_lock);
6771708Sstevel 	mutex_exit(&master_iosram->iosram_lock);
6781708Sstevel 
6791708Sstevel 	return (rc);
6801708Sstevel }
6811708Sstevel 
6821708Sstevel /*
6831708Sstevel  * Remove an interrupt handler
6841708Sstevel  */
6851708Sstevel int
iosram_unreg_intr(uint32_t intr_num)6861708Sstevel iosram_unreg_intr(uint32_t intr_num)
6871708Sstevel {
6881708Sstevel 	sbbc_softstate_t	*softsp;
6891708Sstevel 	int			rc = 0;
6901708Sstevel 	sbbc_intrs_t		*intr;
6911708Sstevel 	int			intr_no;
6921708Sstevel 	uint32_t		intr_enabled;
6931708Sstevel 
6941708Sstevel 	/*
6951708Sstevel 	 * Verify that we have already set up the master sbbc
6961708Sstevel 	 */
6971708Sstevel 	if (master_iosram == NULL)
6981708Sstevel 		return (ENXIO);
6991708Sstevel 
7001708Sstevel 	/*
7011708Sstevel 	 * determine which bit is this intr_num for ?
7021708Sstevel 	 */
7031708Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
7041708Sstevel 		if (intr_num == (1 << intr_no))
7051708Sstevel 			break;
7061708Sstevel 	}
7071708Sstevel 
7081708Sstevel 	if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS))
7091708Sstevel 		return (EINVAL);
7101708Sstevel 
7111708Sstevel 	mutex_enter(&master_iosram->iosram_lock);
7121708Sstevel 
7131708Sstevel 	if ((softsp = master_iosram->iosram_sbbc) == NULL) {
7141708Sstevel 		mutex_exit(&master_iosram->iosram_lock);
7151708Sstevel 		return (ENXIO);
7161708Sstevel 	}
7171708Sstevel 
7181708Sstevel 	mutex_enter(&softsp->sbbc_lock);
7191708Sstevel 
7201708Sstevel 	intr = &master_iosram->intrs[intr_no];
7211708Sstevel 
7221708Sstevel 	/*
7231708Sstevel 	 * No handler installed
7241708Sstevel 	 */
7251708Sstevel 	if (intr->sbbc_handler == (sbbc_intrfunc_t)NULL) {
7261708Sstevel 		rc = EINVAL;
7271708Sstevel 		goto unreg_intr_exit;
7281708Sstevel 	}
7291708Sstevel 
7301708Sstevel 	/*
7311708Sstevel 	 * Unset the bit in the Interrupts Enabled Field for this
7321708Sstevel 	 * interrupt
7331708Sstevel 	 */
7341708Sstevel 	if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
7351708Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
7361708Sstevel 
7371708Sstevel 		goto unreg_intr_exit;
7381708Sstevel 	}
7391708Sstevel 
7401708Sstevel 	intr_enabled &= ~intr_num;
7411708Sstevel 
7421708Sstevel 	if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0,
7431708Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
7441708Sstevel 
7451708Sstevel 		goto unreg_intr_exit;
7461708Sstevel 	}
7471708Sstevel 
7481708Sstevel 	/*
7491708Sstevel 	 * If handler is running, wait until it's done.
7501708Sstevel 	 * It won't get triggered again because we disabled it above.
7511708Sstevel 	 * When we wait, drop sbbc_lock so other interrupt handlers
7521708Sstevel 	 * can still run.
7531708Sstevel 	 */
7541708Sstevel 	for (; ; ) {
7551708Sstevel 		mutex_enter(intr->sbbc_intr_lock);
7561708Sstevel 		if (*(intr->sbbc_intr_state) != SBBC_INTR_IDLE) {
7571708Sstevel 			mutex_exit(intr->sbbc_intr_lock);
7581708Sstevel 			mutex_exit(&softsp->sbbc_lock);
7591708Sstevel 			delay(drv_usectohz(10000));
7601708Sstevel 			mutex_enter(&softsp->sbbc_lock);
7611708Sstevel 			mutex_enter(intr->sbbc_intr_lock);
7621708Sstevel 		} else {
7631708Sstevel 			break;
7641708Sstevel 		}
7651708Sstevel 		mutex_exit(intr->sbbc_intr_lock);
7661708Sstevel 	}
7671708Sstevel 
7681708Sstevel 	if (intr->sbbc_intr_id)
7691708Sstevel 		ddi_remove_softintr(intr->sbbc_intr_id);
7701708Sstevel 
7711708Sstevel 	intr->sbbc_handler  = (sbbc_intrfunc_t)NULL;
7721708Sstevel 	intr->sbbc_arg = (void *)NULL;
7731708Sstevel 	intr->sbbc_intr_id = 0;
7741708Sstevel 	intr->sbbc_intr_state = NULL;
7751708Sstevel 	intr->sbbc_intr_lock = (kmutex_t *)NULL;
7761708Sstevel 	intr->sbbc_intr_next = (sbbc_intrs_t *)NULL;
7771708Sstevel 
7781708Sstevel unreg_intr_exit:
7791708Sstevel 
7801708Sstevel 	mutex_exit(&softsp->sbbc_lock);
7811708Sstevel 	mutex_exit(&master_iosram->iosram_lock);
7821708Sstevel 
7831708Sstevel 	return (rc);
7841708Sstevel }
7851708Sstevel 
7861708Sstevel /*
7871708Sstevel  * sgsbbc_iosram_switchfrom(softsp)
7881708Sstevel  *      Switch master tunnel away from the specified instance.
7891708Sstevel  */
7901708Sstevel int
sgsbbc_iosram_switchfrom(struct sbbc_softstate * softsp)7911708Sstevel sgsbbc_iosram_switchfrom(struct sbbc_softstate *softsp)
7921708Sstevel {
7931708Sstevel 	struct sbbc_softstate	*sp;
7941708Sstevel 	int			rv = DDI_FAILURE;
7951708Sstevel 	int			new_instance;
7961708Sstevel 
7971708Sstevel 	/*
7981708Sstevel 	 * Find the candidate target of tunnel from the linked list.
7991708Sstevel 	 */
8001708Sstevel 	mutex_enter(&chosen_lock);
8011708Sstevel 	ASSERT(sgsbbc_instances);
8021708Sstevel 
8031708Sstevel 	for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
8041708Sstevel 		if (softsp == sp)
8051708Sstevel 			continue;
8061708Sstevel 
8071708Sstevel 		if (sp->sbbc_state & SBBC_STATE_DETACH)
8081708Sstevel 			continue;
8091708Sstevel 		break;
8101708Sstevel 	}
8111708Sstevel 	if (sp == NULL) {
8121708Sstevel 		/* at least one IOSRAM should be attached */
8131708Sstevel 		rv = DDI_FAILURE;
8141708Sstevel 	} else {
8151708Sstevel 		/* Do the tunnel switch */
8161708Sstevel 		new_instance = ddi_get_instance(sp->dip);
8171708Sstevel 		rv = iosram_switch_tunnel(new_instance);
8181708Sstevel 		if (rv == DDI_SUCCESS) {
8191708Sstevel 			/* reset the chosen_iosram back ref */
8201708Sstevel 			sp->iosram = master_iosram;
8211708Sstevel 		}
8221708Sstevel 	}
8231708Sstevel 	mutex_exit(&chosen_lock);
8241708Sstevel 	return (rv);
8251708Sstevel }
8261708Sstevel 
8271708Sstevel 
8281708Sstevel /*
8291708Sstevel  * Switch the tunnel to a different I/O board.
8301708Sstevel  * At the moment, we will say that this is
8311708Sstevel  * called with the instance of the SBBC to switch
8321708Sstevel  * to. This will probably change, but as long as we
8331708Sstevel  * can get a devinfo/softstate for the target SBBC it
8341708Sstevel  * doesn't matter what the parameter is.
8351708Sstevel  */
8361708Sstevel int
iosram_switch_tunnel(int instance)8371708Sstevel iosram_switch_tunnel(int instance)
8381708Sstevel {
8391708Sstevel 
8401708Sstevel 	sbbc_softstate_t	*to_softsp, *from_softsp;
8411708Sstevel 	dev_info_t		*pdip;	/* parent dip */
8421708Sstevel 	tunnel_t		*new_tunnel; /* new tunnel */
8431708Sstevel 	int			portid;
8441708Sstevel 	uint_t			node;	/* node id to pass to OBP */
8451708Sstevel 	uint_t			board;	/* board number to pass to OBP */
8461708Sstevel 	int			rc = DDI_SUCCESS;
8471708Sstevel 	static fn_t		f = "iosram_switch_tunnel";
8481708Sstevel 
8491708Sstevel 	/* Check the firmware for tunnel switch support */
8501708Sstevel 	if (prom_test("SUNW,switch-tunnel") != 0) {
8511708Sstevel 		cmn_err(CE_WARN, "Firmware does not support tunnel switch");
8521708Sstevel 		return (DDI_FAILURE);
8531708Sstevel 	}
8541708Sstevel 
8551708Sstevel 	if ((master_iosram == NULL) || (master_mbox == NULL))
8561708Sstevel 		return (DDI_FAILURE);
8571708Sstevel 
8581708Sstevel 	if (!(to_softsp = sbbc_get_soft_state(instance)))
8591708Sstevel 		return (DDI_FAILURE);
8601708Sstevel 
8611708Sstevel 	/*
8621708Sstevel 	 * create the new tunnel
8631708Sstevel 	 */
8641708Sstevel 	if ((new_tunnel = kmem_zalloc(sizeof (tunnel_t), KM_NOSLEEP)) == NULL) {
8651708Sstevel 		cmn_err(CE_WARN, "Can't allocate space for new tunnel");
8661708Sstevel 		return (DDI_FAILURE);
8671708Sstevel 	}
8681708Sstevel 
8691708Sstevel 	pdip = ddi_get_parent(to_softsp->dip);
8701708Sstevel 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
8711708Sstevel 		"portid", -1)) < 0) {
8721708Sstevel 
8731708Sstevel 		SGSBBC_DBG_ALL("%s: couldn't get portid\n", f);
8741708Sstevel 		return (DDI_FAILURE);
8751708Sstevel 	}
8761708Sstevel 
8771708Sstevel 	/*
8781708Sstevel 	 * Compute node id and board number from port id
8791708Sstevel 	 */
8801708Sstevel 	node	= SG_PORTID_TO_NODEID(portid);
8811708Sstevel 	board	= SG_IO_BD_PORTID_TO_BD_NUM(portid);
8821708Sstevel 
8831708Sstevel 	/*
8841708Sstevel 	 * lock the chosen IOSRAM
8851708Sstevel 	 */
8861708Sstevel 	mutex_enter(&master_iosram->iosram_lock);
8871708Sstevel 
8881708Sstevel 	if (master_iosram->iosram_sbbc == NULL) {
8891708Sstevel 		mutex_exit(&master_iosram->iosram_lock);
8901708Sstevel 		return (DDI_FAILURE);
8911708Sstevel 	}
8921708Sstevel 
8931708Sstevel 	/*
8941708Sstevel 	 * If the target SBBC has not mapped in its
8951708Sstevel 	 * register address space, do it now
8961708Sstevel 	 */
8971708Sstevel 	mutex_enter(&to_softsp->sbbc_lock);
8981708Sstevel 	if (to_softsp->sbbc_regs == NULL) {
8991708Sstevel 		if (sbbc_map_regs(to_softsp) != DDI_SUCCESS) {
9001708Sstevel 			mutex_exit(&to_softsp->sbbc_lock);
9011708Sstevel 			mutex_exit(&master_iosram->iosram_lock);
9021708Sstevel 			return (DDI_FAILURE);
9031708Sstevel 		}
9041708Sstevel 	}
9051708Sstevel 
9061708Sstevel 	/*
9071708Sstevel 	 * Get a pointer to the current sbbc
9081708Sstevel 	 */
9091708Sstevel 	from_softsp = master_iosram->iosram_sbbc;
9101708Sstevel 
9111708Sstevel 	mutex_enter(&from_softsp->sbbc_lock);
9121708Sstevel 
9131708Sstevel 	/*
9141708Sstevel 	 * Disable interrupts from the SC now
9151708Sstevel 	 */
9161708Sstevel 	sbbc_disable_intr(from_softsp);
9171708Sstevel 
9181708Sstevel 	/*
9191708Sstevel 	 * move SC interrupts to the new tunnel
9201708Sstevel 	 */
9211708Sstevel 	if ((rc = sbbc_add_intr(to_softsp)) == DDI_FAILURE) {
9221708Sstevel 		cmn_err(CE_WARN, "Failed to add new interrupt handler");
9231708Sstevel 	} else if ((rc = tunnel_init(to_softsp, new_tunnel)) == DDI_FAILURE) {
9241708Sstevel 		cmn_err(CE_WARN, "Failed to initialize new tunnel");
9251708Sstevel 		ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock);
9261708Sstevel 	} else {
9271708Sstevel 		rw_enter(&master_iosram->tunnel_lock, RW_WRITER);
9281708Sstevel 
9291708Sstevel 		/*
9301708Sstevel 		 * If OBP switch is unsuccessful, abort the switch.
9311708Sstevel 		 */
9321708Sstevel 		if ((rc = prom_serengeti_tunnel_switch(node, board))
9331708Sstevel 			!= DDI_SUCCESS) {
9341708Sstevel 
9351708Sstevel 			/*
9361708Sstevel 			 * Restart other CPUs.
9371708Sstevel 			 */
9381708Sstevel 			rw_exit(&master_iosram->tunnel_lock);
9391708Sstevel 
9401708Sstevel 			cmn_err(CE_WARN, "OBP failed to switch tunnel");
9411708Sstevel 
9421708Sstevel 			/*
9431708Sstevel 			 * Remove interrupt
9441708Sstevel 			 */
9451708Sstevel 			ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock);
9461708Sstevel 
9471708Sstevel 			/*
9481708Sstevel 			 * Unmap new tunnel
9491708Sstevel 			 */
9501708Sstevel 			tunnel_fini(new_tunnel);
9511708Sstevel 		} else {
9521708Sstevel 			tunnel_t		*orig_tunnel;
9531708Sstevel 
9541708Sstevel 			orig_tunnel = master_iosram->tunnel;
9551708Sstevel 			tunnel_commit(to_softsp, new_tunnel);
9561708Sstevel 
9571708Sstevel 			rw_exit(&master_iosram->tunnel_lock);
9581708Sstevel 
9591708Sstevel 			/*
9601708Sstevel 			 * Remove interrupt from original softsp
9611708Sstevel 			 */
9621708Sstevel 			ddi_remove_intr(from_softsp->dip, 0,
9631708Sstevel 			    from_softsp->iblock);
9641708Sstevel 			/*
9651708Sstevel 			 * Unmap original tunnel
9661708Sstevel 			 */
9671708Sstevel 			tunnel_fini(orig_tunnel);
9681708Sstevel 			kmem_free(orig_tunnel, sizeof (tunnel_t));
9691708Sstevel 
9701708Sstevel 			/*
9711708Sstevel 			 * Move the softintrs to the new dip.
9721708Sstevel 			 */
9731708Sstevel 			(void) iosram_switch_intr();
9741708Sstevel 			(void) sbbc_mbox_switch(to_softsp);
9751708Sstevel 
9761708Sstevel 			from_softsp->chosen = FALSE;
9771708Sstevel 
9781708Sstevel 		}
9791708Sstevel 	}
9801708Sstevel 
9811708Sstevel 	/*
9821708Sstevel 	 * Enable interrupt.
9831708Sstevel 	 */
9841708Sstevel 	sbbc_enable_intr(master_iosram->iosram_sbbc);
9851708Sstevel 
9861708Sstevel 	/*
9871708Sstevel 	 * Unlock and get out
9881708Sstevel 	 */
9891708Sstevel 	mutex_exit(&from_softsp->sbbc_lock);
9901708Sstevel 	mutex_exit(&to_softsp->sbbc_lock);
9911708Sstevel 	mutex_exit(&master_iosram->iosram_lock);
9921708Sstevel 
9931708Sstevel 	/*
9941708Sstevel 	 * Call the interrupt handler directly in case
9951708Sstevel 	 * we have missed an interrupt
9961708Sstevel 	 */
997*11311SSurya.Prakki@Sun.COM 	(void) sbbc_intr_handler((caddr_t)master_iosram->iosram_sbbc);
9981708Sstevel 
9991708Sstevel 	if (rc != DDI_SUCCESS) {
10001708Sstevel 		/*
10011708Sstevel 		 * Free up the new_tunnel
10021708Sstevel 		 */
10031708Sstevel 		kmem_free(new_tunnel, sizeof (tunnel_t));
10041708Sstevel 		cmn_err(CE_WARN, "Tunnel switch failed");
10051708Sstevel 	}
10061708Sstevel 
10071708Sstevel 	return (rc);
10081708Sstevel 
10091708Sstevel }
10101708Sstevel 
10111708Sstevel /*
10121708Sstevel  * convert an alphanumeric OBP key to
10131708Sstevel  * our defined numeric keys
10141708Sstevel  */
10151708Sstevel static int
iosram_convert_key(char * toc_key)10161708Sstevel iosram_convert_key(char *toc_key)
10171708Sstevel {
10181708Sstevel 
10191708Sstevel 	if (strcmp(toc_key, TOCKEY_DOMSTAT)  == 0)
10201708Sstevel 		return (SBBC_DOMAIN_KEY);
10211708Sstevel 	if (strcmp(toc_key, TOCKEY_KEYSWPO)  == 0)
10221708Sstevel 		return (SBBC_KEYSWITCH_KEY);
10231708Sstevel 	if (strcmp(toc_key, TOCKEY_TODDATA)  == 0)
10241708Sstevel 		return (SBBC_TOD_KEY);
10251708Sstevel 	if (strcmp(toc_key, TOCKEY_SOLCONS) == 0)
10261708Sstevel 		return (SBBC_CONSOLE_KEY);
10271708Sstevel 	if (strcmp(toc_key, TOCKEY_SOLMBOX)  == 0)
10281708Sstevel 		return (SBBC_MAILBOX_KEY);
10291708Sstevel 	if (strcmp(toc_key, TOCKEY_SOLSCIR)  == 0)
10301708Sstevel 		return (SBBC_INTR_SC_KEY);
10311708Sstevel 	if (strcmp(toc_key, TOCKEY_SCSOLIR)  == 0)
10321708Sstevel 		return (SBBC_SC_INTR_KEY);
10331708Sstevel 	if (strcmp(toc_key, TOCKEY_ENVINFO)  == 0)
10341708Sstevel 		return (SBBC_ENVCTRL_KEY);
10351708Sstevel 	if (strcmp(toc_key, TOCKEY_SOLSCIE)  == 0)
10361708Sstevel 		return (SBBC_INTR_SC_ENABLED_KEY);
10371708Sstevel 	if (strcmp(toc_key, TOCKEY_SCSOLIE)  == 0)
10381708Sstevel 		return (SBBC_SC_INTR_ENABLED_KEY);
10391708Sstevel 	if (strcmp(toc_key, TOCKEY_SIGBLCK)  == 0)
10401708Sstevel 		return (SBBC_SIGBLCK_KEY);
10411708Sstevel 
10421708Sstevel 	/* Unknown key */
10431708Sstevel 	return (-1);
10441708Sstevel }
10451708Sstevel 
10461708Sstevel /*
10471708Sstevel  * Move the software interrupts from the old dip to the new dip
10481708Sstevel  * when doing tunnel switch.
10491708Sstevel  */
10501708Sstevel static int
iosram_switch_intr()10511708Sstevel iosram_switch_intr()
10521708Sstevel {
10531708Sstevel 	sbbc_intrs_t	*intr;
10541708Sstevel 	int		intr_no;
10551708Sstevel 	int		rc = 0;
10561708Sstevel 
10571708Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
10581708Sstevel 
10591708Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
10601708Sstevel 		intr = &master_iosram->intrs[intr_no];
10611708Sstevel 
10621708Sstevel 		if (intr->sbbc_intr_id) {
10631708Sstevel 			ddi_remove_softintr(intr->sbbc_intr_id);
10641708Sstevel 
10651708Sstevel 			if (ddi_add_softintr(master_iosram->iosram_sbbc->dip,
10661708Sstevel 				DDI_SOFTINT_HIGH,
10671708Sstevel 				&intr->sbbc_intr_id, NULL, NULL,
10681708Sstevel 				intr->sbbc_handler, intr->sbbc_arg)
10691708Sstevel 				!= DDI_SUCCESS) {
10701708Sstevel 
10711708Sstevel 				cmn_err(CE_WARN, "Can't add SBBC softint for "
10721708Sstevel 					"interrupt %x", intr_no << 1);
10731708Sstevel 				rc = EAGAIN;
10741708Sstevel 			}
10751708Sstevel 		}
10761708Sstevel 	}
10771708Sstevel 
10781708Sstevel 	return (rc);
10791708Sstevel }
1080