103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
23*07d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel /*
2803831d35Sstevel  * Driver for handling Serengeti I/O SRAM
2903831d35Sstevel  * for Solaris <-> SC comm.
3003831d35Sstevel  */
3103831d35Sstevel 
3203831d35Sstevel #include <sys/types.h>
3303831d35Sstevel #include <sys/systm.h>
3403831d35Sstevel #include <sys/cpuvar.h>
3503831d35Sstevel #include <sys/dditypes.h>
3603831d35Sstevel #include <sys/sunndi.h>
3703831d35Sstevel #include <sys/param.h>
3803831d35Sstevel #include <sys/mutex.h>
3903831d35Sstevel #include <sys/sysmacros.h>
4003831d35Sstevel #include <sys/errno.h>
4103831d35Sstevel #include <sys/file.h>
4203831d35Sstevel #include <sys/kmem.h>
4303831d35Sstevel #include <sys/promif.h>
4403831d35Sstevel #include <sys/prom_plat.h>
4503831d35Sstevel #include <sys/sunddi.h>
4603831d35Sstevel #include <sys/ddi.h>
4703831d35Sstevel 
4803831d35Sstevel #include <sys/serengeti.h>
4903831d35Sstevel #include <sys/sgsbbc_priv.h>
5003831d35Sstevel #include <sys/sgsbbc_iosram_priv.h>
5103831d35Sstevel #include <sys/sgsbbc_mailbox_priv.h>
5203831d35Sstevel 
5303831d35Sstevel /*
5403831d35Sstevel  * Local stuff
5503831d35Sstevel  */
5603831d35Sstevel static int iosram_rw(int, uint32_t, caddr_t, uint32_t, int);
5703831d35Sstevel static int iosram_convert_key(char *);
5803831d35Sstevel static int iosram_switch_intr(void);
5903831d35Sstevel static int tunnel_init(sbbc_softstate_t *, tunnel_t *);
6003831d35Sstevel static void tunnel_fini(tunnel_t *);
6103831d35Sstevel static void tunnel_commit(sbbc_softstate_t *, tunnel_t *);
6203831d35Sstevel static void clear_break();
6303831d35Sstevel 
6403831d35Sstevel #define	IOSRAM_GETB(tunnel, buf, sram, count) \
6503831d35Sstevel 	ddi_rep_get8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR)
6603831d35Sstevel 
6703831d35Sstevel #define	IOSRAM_PUTB(tunnel, buf, sram, count) \
6803831d35Sstevel 	ddi_rep_put8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR)
6903831d35Sstevel 
7003831d35Sstevel #define	IOSRAM_PUT(tunnel, sram, buf, size) \
7103831d35Sstevel 	/* CSTYLED */ \
7203831d35Sstevel 	ddi_put##size(tunnel->reg_handle, (uint##size##_t *)sram, \
7303831d35Sstevel 	/* CSTYLED */ \
7403831d35Sstevel 	*((uint##size##_t *)buf))
7503831d35Sstevel 
7603831d35Sstevel #define	IOSRAM_GET(tunnel, sram, buf, size) \
7703831d35Sstevel 	/* CSTYLED */ \
7803831d35Sstevel 	*(uint##size##_t *)buf = ddi_get##size(tunnel->reg_handle, \
7903831d35Sstevel 	/* CSTYLED */ \
8003831d35Sstevel 	(uint##size##_t *)sram)
8103831d35Sstevel 
8203831d35Sstevel /*
8303831d35Sstevel  * sgsbbc_iosram_is_chosen(struct sbbc_softstate *softsp)
8403831d35Sstevel  *
8503831d35Sstevel  *      Looks up "chosen" node property to
8603831d35Sstevel  *      determine if it is the chosen IOSRAM.
8703831d35Sstevel  */
8803831d35Sstevel int
sgsbbc_iosram_is_chosen(sbbc_softstate_t * softsp)8903831d35Sstevel sgsbbc_iosram_is_chosen(sbbc_softstate_t *softsp)
9003831d35Sstevel {
9103831d35Sstevel 	char		pn[MAXNAMELEN];
9203831d35Sstevel 	char		chosen_iosram[MAXNAMELEN];
9303831d35Sstevel 	int		nodeid;
9403831d35Sstevel 	int		chosen;
9503831d35Sstevel 	uint_t		tunnel;
9603831d35Sstevel 	extern		pnode_t chosen_nodeid;
9703831d35Sstevel 
9803831d35Sstevel 	ASSERT(chosen_nodeid);
9903831d35Sstevel 
10003831d35Sstevel 	nodeid = chosen_nodeid;
10103831d35Sstevel 	(void) prom_getprop(nodeid, "iosram", (caddr_t)&tunnel);
10203831d35Sstevel 
10303831d35Sstevel 	/*
10403831d35Sstevel 	 * get the full OBP pathname of this node
10503831d35Sstevel 	 */
10603831d35Sstevel 	if (prom_phandle_to_path((phandle_t)tunnel, chosen_iosram,
10703831d35Sstevel 		sizeof (chosen_iosram)) < 0) {
10803831d35Sstevel 		cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", tunnel);
10903831d35Sstevel 		return (0);
11003831d35Sstevel 	}
11103831d35Sstevel 
11203831d35Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): prom_phandle_to_path(%x) is '%s'\n",
11303831d35Sstevel 	softsp->sbbc_instance, nodeid, chosen_iosram);
11403831d35Sstevel 
11503831d35Sstevel 	(void) ddi_pathname(softsp->dip, pn);
11603831d35Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ddi_pathname(%p) is '%s'\n",
117*07d06da5SSurya Prakki 	    softsp->sbbc_instance, (void *)softsp->dip, pn);
11803831d35Sstevel 
11903831d35Sstevel 	chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0;
12003831d35Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
121*07d06da5SSurya Prakki 	    chosen? "MASTER" : "SLAVE");
12203831d35Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
123*07d06da5SSurya Prakki 	    (chosen ? "MASTER" : "SLAVE"));
12403831d35Sstevel 
12503831d35Sstevel 	return (chosen);
12603831d35Sstevel }
12703831d35Sstevel 
12803831d35Sstevel void
iosram_init()12903831d35Sstevel iosram_init()
13003831d35Sstevel {
13103831d35Sstevel 	int	i;
13203831d35Sstevel 
13303831d35Sstevel 	if ((master_iosram = kmem_zalloc(sizeof (struct chosen_iosram),
13403831d35Sstevel 	    KM_NOSLEEP)) == NULL) {
13503831d35Sstevel 		prom_printf("Can't allocate space for Chosen IOSRAM\n");
13603831d35Sstevel 		panic("Can't allocate space for Chosen IOSRAM");
13703831d35Sstevel 	}
13803831d35Sstevel 
13903831d35Sstevel 	if ((master_iosram->tunnel = kmem_zalloc(sizeof (tunnel_t),
14003831d35Sstevel 	    KM_NOSLEEP)) == NULL) {
14103831d35Sstevel 		prom_printf("Can't allocate space for tunnel\n");
14203831d35Sstevel 		panic("Can't allocate space for tunnel");
14303831d35Sstevel 	}
14403831d35Sstevel 
14503831d35Sstevel 	master_iosram->iosram_sbbc = NULL;
14603831d35Sstevel 
14703831d35Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
14803831d35Sstevel 		master_iosram->tunnel->tunnel_keys[i].key = 0;
14903831d35Sstevel 		master_iosram->tunnel->tunnel_keys[i].base = NULL;
15003831d35Sstevel 		master_iosram->tunnel->tunnel_keys[i].size = 0;
15103831d35Sstevel 	}
15203831d35Sstevel 
15303831d35Sstevel 	for (i = 0; i < SBBC_MAX_INTRS; i++)
15403831d35Sstevel 		master_iosram->intrs[i].sbbc_handler = NULL;
15503831d35Sstevel 
15603831d35Sstevel 	mutex_init(&master_iosram->iosram_lock, NULL, MUTEX_DEFAULT, NULL);
15703831d35Sstevel 	rw_init(&master_iosram->tunnel_lock, NULL, RW_DEFAULT, NULL);
15803831d35Sstevel }
15903831d35Sstevel void
iosram_fini()16003831d35Sstevel iosram_fini()
16103831d35Sstevel {
16203831d35Sstevel 	struct	tunnel_key	*tunnel;
16303831d35Sstevel 	int			i;
16403831d35Sstevel 
16503831d35Sstevel 	rw_destroy(&master_iosram->tunnel_lock);
16603831d35Sstevel 	mutex_destroy(&master_iosram->iosram_lock);
16703831d35Sstevel 
16803831d35Sstevel 	/*
16903831d35Sstevel 	 * destroy any tunnel maps
17003831d35Sstevel 	 */
17103831d35Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
17203831d35Sstevel 		tunnel = &master_iosram->tunnel->tunnel_keys[i];
17303831d35Sstevel 		if (tunnel->base != NULL) {
17403831d35Sstevel 			ddi_regs_map_free(&tunnel->reg_handle);
17503831d35Sstevel 			tunnel->base = NULL;
17603831d35Sstevel 		}
17703831d35Sstevel 	}
17803831d35Sstevel 
17903831d35Sstevel 	kmem_free(master_iosram->tunnel, sizeof (tunnel_t));
18003831d35Sstevel 
18103831d35Sstevel 	kmem_free(master_iosram, sizeof (struct chosen_iosram));
18203831d35Sstevel 
18303831d35Sstevel 	master_iosram = NULL;
18403831d35Sstevel }
18503831d35Sstevel 
18603831d35Sstevel static void
check_iosram_ver(uint16_t version)18703831d35Sstevel check_iosram_ver(uint16_t version)
18803831d35Sstevel {
18903831d35Sstevel 	uint8_t	max_ver = MAX_IOSRAM_TOC_VER;
19003831d35Sstevel 	uint8_t	major_ver =
19103831d35Sstevel 		(version >> IOSRAM_TOC_VER_SHIFT) & IOSRAM_TOC_VER_MASK;
19203831d35Sstevel 
19303831d35Sstevel 	SGSBBC_DBG_ALL("IOSRAM TOC version: %d.%d\n", major_ver,
19403831d35Sstevel 		version & IOSRAM_TOC_VER_MASK);
19503831d35Sstevel 	SGSBBC_DBG_ALL("Max supported IOSRAM TOC version: %d\n", max_ver);
19603831d35Sstevel 	if (major_ver > max_ver) {
19703831d35Sstevel 		panic("Up-rev System Controller version.\n"
19803831d35Sstevel 		    "You must restore an earlier revision of System "
19903831d35Sstevel 		    "Controller firmware, or upgrade Solaris.\n"
20003831d35Sstevel 		    "Please consult the System Controller release notice "
20103831d35Sstevel 		    "for additional details.");
20203831d35Sstevel 	}
20303831d35Sstevel }
20403831d35Sstevel 
20503831d35Sstevel static void
tunnel_commit(sbbc_softstate_t * softsp,tunnel_t * new_tunnel)20603831d35Sstevel tunnel_commit(sbbc_softstate_t *softsp, tunnel_t *new_tunnel)
20703831d35Sstevel {
20803831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
20903831d35Sstevel 
21003831d35Sstevel 	master_iosram->iosram_sbbc = softsp;
21103831d35Sstevel 	master_iosram->tunnel = new_tunnel;
21203831d35Sstevel 	softsp->chosen = TRUE;
21303831d35Sstevel 
21403831d35Sstevel 	/*
21503831d35Sstevel 	 * SBBC has pointer to interrupt handlers for simplicity
21603831d35Sstevel 	 */
21703831d35Sstevel 	softsp->intr_hdlrs = master_iosram->intrs;
21803831d35Sstevel }
21903831d35Sstevel 
22003831d35Sstevel static int
tunnel_init(sbbc_softstate_t * softsp,tunnel_t * new_tunnel)22103831d35Sstevel tunnel_init(sbbc_softstate_t *softsp, tunnel_t *new_tunnel)
22203831d35Sstevel {
22303831d35Sstevel 	struct iosram_toc		*toc = NULL;
22403831d35Sstevel 	int				i, key;
22503831d35Sstevel 	struct	tunnel_key		*tunnel;
22603831d35Sstevel 	ddi_acc_handle_t		toc_handle;
22703831d35Sstevel 	struct ddi_device_acc_attr	attr;
22803831d35Sstevel 
22903831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
23003831d35Sstevel 
23103831d35Sstevel 	if ((softsp == (sbbc_softstate_t *)NULL) ||
23203831d35Sstevel 		(new_tunnel == (tunnel_t *)NULL)) {
23303831d35Sstevel 
23403831d35Sstevel 		return (DDI_FAILURE);
23503831d35Sstevel 	}
23603831d35Sstevel 
23703831d35Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
23803831d35Sstevel 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
23903831d35Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
24003831d35Sstevel 
24103831d35Sstevel 	SGSBBC_DBG_ALL("map in the IOSRAM TOC at offset %x\n",
24203831d35Sstevel 		softsp->sram_toc);
24303831d35Sstevel 
24403831d35Sstevel 	/*
24503831d35Sstevel 	 * First map in the TOC, then set up the tunnel
24603831d35Sstevel 	 */
24703831d35Sstevel 	if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
24803831d35Sstevel 		(caddr_t *)&toc,
24903831d35Sstevel 		SBBC_SRAM_OFFSET + softsp->sram_toc,
25003831d35Sstevel 		sizeof (struct iosram_toc),
25103831d35Sstevel 		&attr, &toc_handle) != DDI_SUCCESS) {
25203831d35Sstevel 			cmn_err(CE_WARN, "sbbc%d: unable to map SRAM "
25303831d35Sstevel 			    "registers", ddi_get_instance(softsp->dip));
25403831d35Sstevel 			return (DDI_FAILURE);
25503831d35Sstevel 	}
256*07d06da5SSurya Prakki 	SGSBBC_DBG_ALL("dip=%p mapped TOC %p\n", (void *)softsp->dip,
257*07d06da5SSurya Prakki 	    (void *)toc);
25803831d35Sstevel 
25903831d35Sstevel 	check_iosram_ver(toc->iosram_version);
26003831d35Sstevel 
26103831d35Sstevel 	for (i = 0; i < toc->iosram_tagno; i++) {
26203831d35Sstevel 		key = iosram_convert_key(toc->iosram_keys[i].key);
26303831d35Sstevel 		if ((key > 0) && (key < SBBC_MAX_KEYS)) {
26403831d35Sstevel 			tunnel = &new_tunnel->tunnel_keys[key];
26503831d35Sstevel 			tunnel->key = key;
26603831d35Sstevel 			tunnel->size = toc->iosram_keys[i].size;
26703831d35Sstevel 			/*
26803831d35Sstevel 			 * map in the SRAM area using the offset
26903831d35Sstevel 			 * from the base of SRAM + SRAM offset into
27003831d35Sstevel 			 * the register property for the SBBC base
27103831d35Sstevel 			 * address
27203831d35Sstevel 			 */
27303831d35Sstevel 			if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
27403831d35Sstevel 				(caddr_t *)&tunnel->base,
27503831d35Sstevel 				SBBC_SRAM_OFFSET + toc->iosram_keys[i].offset,
27603831d35Sstevel 				toc->iosram_keys[i].size, &attr,
27703831d35Sstevel 				&tunnel->reg_handle) != DDI_SUCCESS) {
27803831d35Sstevel 				cmn_err(CE_WARN, "sbbc%d: unable to map SRAM "
27903831d35Sstevel 				    "registers", ddi_get_instance(softsp->dip));
28003831d35Sstevel 				return (DDI_FAILURE);
28103831d35Sstevel 			}
28203831d35Sstevel 			SGSBBC_DBG_ALL("%d: key %s size %d offset %x addr %p\n",
283*07d06da5SSurya Prakki 			    i, toc->iosram_keys[i].key,
284*07d06da5SSurya Prakki 			    toc->iosram_keys[i].size,
285*07d06da5SSurya Prakki 			    toc->iosram_keys[i].offset,
286*07d06da5SSurya Prakki 			    (void *)tunnel->base);
28703831d35Sstevel 
28803831d35Sstevel 		}
28903831d35Sstevel 	}
29003831d35Sstevel 
29103831d35Sstevel 
29203831d35Sstevel 	if (toc != NULL) {
29303831d35Sstevel 		ddi_regs_map_free(&toc_handle);
29403831d35Sstevel 	}
29503831d35Sstevel 
29603831d35Sstevel 	/*
29703831d35Sstevel 	 * Set up the 'interrupt reason' SRAM pointers
29803831d35Sstevel 	 * for the SBBC interrupt handler
29903831d35Sstevel 	 */
30003831d35Sstevel 	if (INVALID_KEY(new_tunnel, SBBC_SC_INTR_KEY)) {
30103831d35Sstevel 		/*
30203831d35Sstevel 		 * Can't really do much if these are not here
30303831d35Sstevel 		 */
30403831d35Sstevel 		prom_printf("No Interrupt Reason Fields set by SC\n");
30503831d35Sstevel 		cmn_err(CE_WARN, "No Interrupt Reason Fields set by SC");
30603831d35Sstevel 		return (DDI_FAILURE);
30703831d35Sstevel 	}
30803831d35Sstevel 
30903831d35Sstevel 	return (DDI_SUCCESS);
31003831d35Sstevel }
31103831d35Sstevel 
31203831d35Sstevel /*
31303831d35Sstevel  * Unmap a tunnel
31403831d35Sstevel  */
31503831d35Sstevel static void
tunnel_fini(tunnel_t * tunnel)31603831d35Sstevel tunnel_fini(tunnel_t *tunnel)
31703831d35Sstevel {
31803831d35Sstevel 	int	i;
31903831d35Sstevel 	struct	tunnel_key	*tunnel_key;
32003831d35Sstevel 
32103831d35Sstevel 	/*
32203831d35Sstevel 	 * Unmap the tunnel
32303831d35Sstevel 	 */
32403831d35Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
32503831d35Sstevel 		tunnel_key = &tunnel->tunnel_keys[i];
32603831d35Sstevel 		if (tunnel_key->base != NULL) {
32703831d35Sstevel 			ddi_regs_map_free(&tunnel_key->reg_handle);
32803831d35Sstevel 			tunnel_key->base = NULL;
32903831d35Sstevel 		}
33003831d35Sstevel 	}
33103831d35Sstevel }
33203831d35Sstevel 
33303831d35Sstevel static void
clear_break()33403831d35Sstevel clear_break()
33503831d35Sstevel {
33603831d35Sstevel 	struct tunnel_key	tunnel_key;
33703831d35Sstevel 	uint32_t		*intr_in_reason;
33803831d35Sstevel 	ddi_acc_handle_t	intr_in_handle;
33903831d35Sstevel 
34003831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
34103831d35Sstevel 
34203831d35Sstevel 	tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY];
34303831d35Sstevel 	intr_in_reason = (uint32_t *)tunnel_key.base;
34403831d35Sstevel 	intr_in_handle = tunnel_key.reg_handle;
34503831d35Sstevel 	ddi_put32(intr_in_handle, intr_in_reason,
34603831d35Sstevel 		ddi_get32(intr_in_handle, intr_in_reason) & ~SBBC_CONSOLE_BRK);
34703831d35Sstevel }
34803831d35Sstevel 
34903831d35Sstevel int
iosram_tunnel_init(sbbc_softstate_t * softsp)35003831d35Sstevel iosram_tunnel_init(sbbc_softstate_t *softsp)
35103831d35Sstevel {
35203831d35Sstevel 	int	rc;
35303831d35Sstevel 
35403831d35Sstevel 	ASSERT(master_iosram);
35503831d35Sstevel 
35603831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
35703831d35Sstevel 
35803831d35Sstevel 	if ((rc = tunnel_init(softsp, master_iosram->tunnel)) == DDI_SUCCESS) {
35903831d35Sstevel 		tunnel_commit(softsp, master_iosram->tunnel);
36003831d35Sstevel 		clear_break();
36103831d35Sstevel 	}
36203831d35Sstevel 
36303831d35Sstevel 
36403831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
36503831d35Sstevel 
36603831d35Sstevel 	return (rc);
36703831d35Sstevel }
36803831d35Sstevel 
36903831d35Sstevel int
iosram_read(int key,uint32_t offset,caddr_t buf,uint32_t size)37003831d35Sstevel iosram_read(int key, uint32_t offset, caddr_t buf, uint32_t size)
37103831d35Sstevel {
37203831d35Sstevel 	return (iosram_rw(key, offset, buf, size, FREAD));
37303831d35Sstevel }
37403831d35Sstevel 
37503831d35Sstevel int
iosram_write(int key,uint32_t offset,caddr_t buf,uint32_t size)37603831d35Sstevel iosram_write(int key, uint32_t offset, caddr_t buf, uint32_t size)
37703831d35Sstevel {
37803831d35Sstevel 	return (iosram_rw(key, offset, buf, size, FWRITE));
37903831d35Sstevel }
38003831d35Sstevel 
38103831d35Sstevel 
38203831d35Sstevel static int
iosram_rw(int key,uint32_t offset,caddr_t buf,uint32_t size,int flag)38303831d35Sstevel iosram_rw(int key, uint32_t offset, caddr_t buf, uint32_t size, int flag)
38403831d35Sstevel {
38503831d35Sstevel 	struct	tunnel_key	*tunnel;
38603831d35Sstevel 	caddr_t 		sram_src;
38703831d35Sstevel 
38803831d35Sstevel 	/*
38903831d35Sstevel 	 * Return right away if there is nothing to read/write.
39003831d35Sstevel 	 */
39103831d35Sstevel 	if (size == 0)
39203831d35Sstevel 		return (0);
39303831d35Sstevel 
39403831d35Sstevel 	rw_enter(&master_iosram->tunnel_lock, RW_READER);
39503831d35Sstevel 
39603831d35Sstevel 	/*
39703831d35Sstevel 	 * Key not matched ?
39803831d35Sstevel 	 */
39903831d35Sstevel 	if (INVALID_KEY(master_iosram->tunnel, key)) {
40003831d35Sstevel 		rw_exit(&master_iosram->tunnel_lock);
40103831d35Sstevel 		return (ENXIO);
40203831d35Sstevel 	}
40303831d35Sstevel 
40403831d35Sstevel 	tunnel = &master_iosram->tunnel->tunnel_keys[key];
40503831d35Sstevel 	if ((offset + size) > tunnel->size) {
40603831d35Sstevel 		rw_exit(&master_iosram->tunnel_lock);
40703831d35Sstevel 		return (EFBIG);
40803831d35Sstevel 	}
40903831d35Sstevel 
41003831d35Sstevel 	sram_src = tunnel->base + offset;
41103831d35Sstevel 
41203831d35Sstevel 	/*
41303831d35Sstevel 	 * Atomic reads/writes might be necessary for some clients.
41403831d35Sstevel 	 * We assume that such clients could guarantee their buffers
41503831d35Sstevel 	 * are aligned at the boundary of the request sizes.  We also
41603831d35Sstevel 	 * assume that the source/destination of such requests are
41703831d35Sstevel 	 * aligned at the right boundaries in IOSRAM.  If either
41803831d35Sstevel 	 * condition fails, byte access is performed.
41903831d35Sstevel 	 */
42003831d35Sstevel 	if (flag == FREAD) {
42103831d35Sstevel 		switch (size) {
42203831d35Sstevel 		case sizeof (uint16_t):
42303831d35Sstevel 		case sizeof (uint32_t):
42403831d35Sstevel 		case sizeof (uint64_t):
42503831d35Sstevel 			if (IS_P2ALIGNED(sram_src, size) &&
42603831d35Sstevel 				IS_P2ALIGNED(buf, size)) {
42703831d35Sstevel 
42803831d35Sstevel 				if (size == sizeof (uint16_t))
42903831d35Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 16);
43003831d35Sstevel 				else if (size == sizeof (uint32_t))
43103831d35Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 32);
43203831d35Sstevel 				else
43303831d35Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 64);
43403831d35Sstevel 				break;
43503831d35Sstevel 			}
43603831d35Sstevel 			/* FALLTHRU */
43703831d35Sstevel 		default:
43803831d35Sstevel 			IOSRAM_GETB(tunnel, (uint8_t *)buf,
43903831d35Sstevel 				(uint8_t *)sram_src, (size_t)size);
44003831d35Sstevel 			break;
44103831d35Sstevel 		}
44203831d35Sstevel 	} else {
44303831d35Sstevel 		switch (size) {
44403831d35Sstevel 		case sizeof (uint16_t):
44503831d35Sstevel 		case sizeof (uint32_t):
44603831d35Sstevel 		case sizeof (uint64_t):
44703831d35Sstevel 			if (IS_P2ALIGNED(sram_src, size) &&
44803831d35Sstevel 				IS_P2ALIGNED(buf, size)) {
44903831d35Sstevel 
45003831d35Sstevel 				if (size == sizeof (uint16_t))
45103831d35Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 16);
45203831d35Sstevel 				else if (size == sizeof (uint32_t))
45303831d35Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 32);
45403831d35Sstevel 				else
45503831d35Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 64);
45603831d35Sstevel 				break;
45703831d35Sstevel 			}
45803831d35Sstevel 			/* FALLTHRU */
45903831d35Sstevel 		default:
46003831d35Sstevel 			IOSRAM_PUTB(tunnel, (uint8_t *)buf,
46103831d35Sstevel 				(uint8_t *)sram_src, (size_t)size);
46203831d35Sstevel 			break;
46303831d35Sstevel 		}
46403831d35Sstevel 	}
46503831d35Sstevel 
46603831d35Sstevel 	rw_exit(&master_iosram->tunnel_lock);
46703831d35Sstevel 	return (0);
46803831d35Sstevel 
46903831d35Sstevel }
47003831d35Sstevel 
47103831d35Sstevel int
iosram_size(int key)47203831d35Sstevel iosram_size(int key)
47303831d35Sstevel {
47403831d35Sstevel 	int size = -1;
47503831d35Sstevel 
47603831d35Sstevel 	rw_enter(&master_iosram->tunnel_lock, RW_READER);
47703831d35Sstevel 
47803831d35Sstevel 	/*
47903831d35Sstevel 	 * Key not matched ?
48003831d35Sstevel 	 */
48103831d35Sstevel 	if (!INVALID_KEY(master_iosram->tunnel, key))
48203831d35Sstevel 		size = master_iosram->tunnel->tunnel_keys[key].size;
48303831d35Sstevel 
48403831d35Sstevel 	rw_exit(&master_iosram->tunnel_lock);
48503831d35Sstevel 
48603831d35Sstevel 	return (size);
48703831d35Sstevel }
48803831d35Sstevel 
48903831d35Sstevel /*
49003831d35Sstevel  * Generate an interrupt to the SC using the SBBC EPLD
49103831d35Sstevel  *
49203831d35Sstevel  * Note: intr_num can be multiple interrupts OR'ed together
49303831d35Sstevel  */
49403831d35Sstevel int
iosram_send_intr(uint32_t intr_num)49503831d35Sstevel iosram_send_intr(uint32_t intr_num)
49603831d35Sstevel {
49703831d35Sstevel 
49803831d35Sstevel 	int		rc = 0;
49903831d35Sstevel 	uint32_t	intr_reason;
50003831d35Sstevel 	uint32_t	intr_enabled;
50103831d35Sstevel 
50203831d35Sstevel 	/*
50303831d35Sstevel 	 * Verify that we have already set up the master sbbc
50403831d35Sstevel 	 */
50503831d35Sstevel 	if (master_iosram == NULL)
50603831d35Sstevel 		return (ENXIO);
50703831d35Sstevel 
50803831d35Sstevel 	/*
50903831d35Sstevel 	 * Grab the lock to prevent tunnel switch in the middle
51003831d35Sstevel 	 * of sending an interrupt.
51103831d35Sstevel 	 */
51203831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
51303831d35Sstevel 
51403831d35Sstevel 	if (master_iosram->iosram_sbbc == NULL) {
51503831d35Sstevel 		rc = ENXIO;
51603831d35Sstevel 		goto send_intr_exit;
51703831d35Sstevel 	}
51803831d35Sstevel 
51903831d35Sstevel 	if ((rc = sbbc_send_intr(master_iosram->iosram_sbbc, FALSE)) != 0) {
52003831d35Sstevel 		/*
52103831d35Sstevel 		 * previous interrupts have not been cleared yet by the SC
52203831d35Sstevel 		 */
52303831d35Sstevel 		goto send_intr_exit;
52403831d35Sstevel 	}
52503831d35Sstevel 
52603831d35Sstevel 	/*
52703831d35Sstevel 	 * Set a bit in the interrupt reason field
52803831d35Sstevel 	 * call back into the sbbc handler to hit the EPLD
52903831d35Sstevel 	 *
53003831d35Sstevel 	 * First check the interrupts enabled by the SC
53103831d35Sstevel 	 */
53203831d35Sstevel 	if ((rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0,
53303831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
53403831d35Sstevel 
53503831d35Sstevel 		goto send_intr_exit;
53603831d35Sstevel 	}
53703831d35Sstevel 
53803831d35Sstevel 	if ((intr_enabled & intr_num) != intr_num) {
53903831d35Sstevel 		/*
54003831d35Sstevel 		 * at least one of the interrupts is
54103831d35Sstevel 		 * not enabled by the SC
54203831d35Sstevel 		 */
54303831d35Sstevel 		rc = ENOTSUP;
54403831d35Sstevel 		goto send_intr_exit;
54503831d35Sstevel 	}
54603831d35Sstevel 
54703831d35Sstevel 	if ((rc = iosram_read(SBBC_INTR_SC_KEY, 0,
54803831d35Sstevel 		(caddr_t)&intr_reason, sizeof (intr_reason))) != 0) {
54903831d35Sstevel 
55003831d35Sstevel 		goto send_intr_exit;
55103831d35Sstevel 	}
55203831d35Sstevel 
55303831d35Sstevel 	if ((intr_reason & intr_num) == intr_num) {
55403831d35Sstevel 		/*
55503831d35Sstevel 		 * All interrupts specified are already pending
55603831d35Sstevel 		 */
55703831d35Sstevel 		rc = EBUSY;
55803831d35Sstevel 		goto send_intr_exit;
55903831d35Sstevel 	}
56003831d35Sstevel 
56103831d35Sstevel 	intr_reason |= intr_num;
56203831d35Sstevel 
56303831d35Sstevel 	if ((rc = iosram_write(SBBC_INTR_SC_KEY, 0,
56403831d35Sstevel 		(caddr_t)&intr_reason, sizeof (intr_reason))) != 0) {
56503831d35Sstevel 
56603831d35Sstevel 		goto send_intr_exit;
56703831d35Sstevel 	}
56803831d35Sstevel 
56903831d35Sstevel 	/*
57003831d35Sstevel 	 * Hit the EPLD interrupt bit
57103831d35Sstevel 	 */
57203831d35Sstevel 
57303831d35Sstevel 	rc = sbbc_send_intr(master_iosram->iosram_sbbc, TRUE);
57403831d35Sstevel 
57503831d35Sstevel send_intr_exit:
57603831d35Sstevel 
57703831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
57803831d35Sstevel 
57903831d35Sstevel 	return (rc);
58003831d35Sstevel }
58103831d35Sstevel 
58203831d35Sstevel /*
58303831d35Sstevel  * Register an interrupt handler
58403831d35Sstevel  */
58503831d35Sstevel int
iosram_reg_intr(uint32_t intr_num,sbbc_intrfunc_t intr_handler,caddr_t arg,uint_t * state,kmutex_t * lock)58603831d35Sstevel iosram_reg_intr(uint32_t intr_num, sbbc_intrfunc_t intr_handler,
58703831d35Sstevel 		caddr_t arg, uint_t *state, kmutex_t *lock)
58803831d35Sstevel {
58903831d35Sstevel 	sbbc_softstate_t	*softsp;
59003831d35Sstevel 	int			rc = 0;
59103831d35Sstevel 	sbbc_intrs_t		*intr;
59203831d35Sstevel 	int			intr_no;
59303831d35Sstevel 	uint32_t		intr_enabled;
59403831d35Sstevel 
59503831d35Sstevel 	/*
59603831d35Sstevel 	 * Verify that we have already set up the master sbbc
59703831d35Sstevel 	 */
59803831d35Sstevel 	if (master_iosram == NULL)
59903831d35Sstevel 		return (ENXIO);
60003831d35Sstevel 
60103831d35Sstevel 	/*
60203831d35Sstevel 	 * determine which bit is this intr_num for ?
60303831d35Sstevel 	 */
60403831d35Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
60503831d35Sstevel 		if (intr_num == (1 << intr_no))
60603831d35Sstevel 			break;
60703831d35Sstevel 	}
60803831d35Sstevel 
60903831d35Sstevel 	/*
61003831d35Sstevel 	 * Check the parameters
61103831d35Sstevel 	 */
61203831d35Sstevel 	if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS) ||
61303831d35Sstevel 		(intr_handler == NULL) || (state == NULL) ||
61403831d35Sstevel 		(lock == NULL))
61503831d35Sstevel 		return (EINVAL);
61603831d35Sstevel 
61703831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
61803831d35Sstevel 
61903831d35Sstevel 	if ((softsp = master_iosram->iosram_sbbc) == NULL) {
62003831d35Sstevel 		mutex_exit(&master_iosram->iosram_lock);
62103831d35Sstevel 		return (ENXIO);
62203831d35Sstevel 	}
62303831d35Sstevel 
62403831d35Sstevel 	mutex_enter(&softsp->sbbc_lock);
62503831d35Sstevel 
62603831d35Sstevel 	intr = &master_iosram->intrs[intr_no];
62703831d35Sstevel 
62803831d35Sstevel 	if (intr->sbbc_handler != (sbbc_intrfunc_t)NULL) {
62903831d35Sstevel 		rc = EBUSY;
63003831d35Sstevel 		goto reg_intr_exit;
63103831d35Sstevel 	}
63203831d35Sstevel 
63303831d35Sstevel 	intr->sbbc_handler  = intr_handler;
63403831d35Sstevel 	intr->sbbc_arg = (void *)arg;
63503831d35Sstevel 	intr->sbbc_intr_state = state;
63603831d35Sstevel 	intr->sbbc_intr_lock = lock;
63703831d35Sstevel 	intr->sbbc_intr_next = (sbbc_intrs_t *)NULL;
63803831d35Sstevel 
63903831d35Sstevel 	/*
64003831d35Sstevel 	 * we need to make sure that the mutex is for
64103831d35Sstevel 	 * an ADAPTIVE lock, so call mutex_init() again with
64203831d35Sstevel 	 * the sbbc iblock cookie
64303831d35Sstevel 	 */
64403831d35Sstevel 	mutex_init(lock, NULL, MUTEX_DRIVER,
64503831d35Sstevel 		(void *)softsp->iblock);
64603831d35Sstevel 
64703831d35Sstevel 	if (ddi_add_softintr(softsp->dip, DDI_SOFTINT_HIGH,
64803831d35Sstevel 		&intr->sbbc_intr_id, NULL, NULL,
64903831d35Sstevel 		intr_handler, (caddr_t)arg) != DDI_SUCCESS) {
65003831d35Sstevel 
65103831d35Sstevel 		cmn_err(CE_WARN, "Can't add SBBC softint");
65203831d35Sstevel 		rc = EAGAIN;
65303831d35Sstevel 		goto reg_intr_exit;
65403831d35Sstevel 	}
65503831d35Sstevel 
65603831d35Sstevel 	/*
65703831d35Sstevel 	 * Set the bit in the Interrupts Enabled Field for this
65803831d35Sstevel 	 * interrupt
65903831d35Sstevel 	 */
66003831d35Sstevel 	if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
66103831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
66203831d35Sstevel 
66303831d35Sstevel 		goto reg_intr_exit;
66403831d35Sstevel 	}
66503831d35Sstevel 
66603831d35Sstevel 	intr_enabled |= intr_num;
66703831d35Sstevel 
66803831d35Sstevel 	if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0,
66903831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
67003831d35Sstevel 
67103831d35Sstevel 		goto reg_intr_exit;
67203831d35Sstevel 	}
67303831d35Sstevel 
67403831d35Sstevel reg_intr_exit:
67503831d35Sstevel 
67603831d35Sstevel 	mutex_exit(&softsp->sbbc_lock);
67703831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
67803831d35Sstevel 
67903831d35Sstevel 	return (rc);
68003831d35Sstevel }
68103831d35Sstevel 
68203831d35Sstevel /*
68303831d35Sstevel  * Remove an interrupt handler
68403831d35Sstevel  */
68503831d35Sstevel int
iosram_unreg_intr(uint32_t intr_num)68603831d35Sstevel iosram_unreg_intr(uint32_t intr_num)
68703831d35Sstevel {
68803831d35Sstevel 	sbbc_softstate_t	*softsp;
68903831d35Sstevel 	int			rc = 0;
69003831d35Sstevel 	sbbc_intrs_t		*intr;
69103831d35Sstevel 	int			intr_no;
69203831d35Sstevel 	uint32_t		intr_enabled;
69303831d35Sstevel 
69403831d35Sstevel 	/*
69503831d35Sstevel 	 * Verify that we have already set up the master sbbc
69603831d35Sstevel 	 */
69703831d35Sstevel 	if (master_iosram == NULL)
69803831d35Sstevel 		return (ENXIO);
69903831d35Sstevel 
70003831d35Sstevel 	/*
70103831d35Sstevel 	 * determine which bit is this intr_num for ?
70203831d35Sstevel 	 */
70303831d35Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
70403831d35Sstevel 		if (intr_num == (1 << intr_no))
70503831d35Sstevel 			break;
70603831d35Sstevel 	}
70703831d35Sstevel 
70803831d35Sstevel 	if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS))
70903831d35Sstevel 		return (EINVAL);
71003831d35Sstevel 
71103831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
71203831d35Sstevel 
71303831d35Sstevel 	if ((softsp = master_iosram->iosram_sbbc) == NULL) {
71403831d35Sstevel 		mutex_exit(&master_iosram->iosram_lock);
71503831d35Sstevel 		return (ENXIO);
71603831d35Sstevel 	}
71703831d35Sstevel 
71803831d35Sstevel 	mutex_enter(&softsp->sbbc_lock);
71903831d35Sstevel 
72003831d35Sstevel 	intr = &master_iosram->intrs[intr_no];
72103831d35Sstevel 
72203831d35Sstevel 	/*
72303831d35Sstevel 	 * No handler installed
72403831d35Sstevel 	 */
72503831d35Sstevel 	if (intr->sbbc_handler == (sbbc_intrfunc_t)NULL) {
72603831d35Sstevel 		rc = EINVAL;
72703831d35Sstevel 		goto unreg_intr_exit;
72803831d35Sstevel 	}
72903831d35Sstevel 
73003831d35Sstevel 	/*
73103831d35Sstevel 	 * Unset the bit in the Interrupts Enabled Field for this
73203831d35Sstevel 	 * interrupt
73303831d35Sstevel 	 */
73403831d35Sstevel 	if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
73503831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
73603831d35Sstevel 
73703831d35Sstevel 		goto unreg_intr_exit;
73803831d35Sstevel 	}
73903831d35Sstevel 
74003831d35Sstevel 	intr_enabled &= ~intr_num;
74103831d35Sstevel 
74203831d35Sstevel 	if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0,
74303831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
74403831d35Sstevel 
74503831d35Sstevel 		goto unreg_intr_exit;
74603831d35Sstevel 	}
74703831d35Sstevel 
74803831d35Sstevel 	/*
74903831d35Sstevel 	 * If handler is running, wait until it's done.
75003831d35Sstevel 	 * It won't get triggered again because we disabled it above.
75103831d35Sstevel 	 * When we wait, drop sbbc_lock so other interrupt handlers
75203831d35Sstevel 	 * can still run.
75303831d35Sstevel 	 */
75403831d35Sstevel 	for (; ; ) {
75503831d35Sstevel 		mutex_enter(intr->sbbc_intr_lock);
75603831d35Sstevel 		if (*(intr->sbbc_intr_state) != SBBC_INTR_IDLE) {
75703831d35Sstevel 			mutex_exit(intr->sbbc_intr_lock);
75803831d35Sstevel 			mutex_exit(&softsp->sbbc_lock);
75903831d35Sstevel 			delay(drv_usectohz(10000));
76003831d35Sstevel 			mutex_enter(&softsp->sbbc_lock);
76103831d35Sstevel 			mutex_enter(intr->sbbc_intr_lock);
76203831d35Sstevel 		} else {
76303831d35Sstevel 			break;
76403831d35Sstevel 		}
76503831d35Sstevel 		mutex_exit(intr->sbbc_intr_lock);
76603831d35Sstevel 	}
76703831d35Sstevel 
76803831d35Sstevel 	if (intr->sbbc_intr_id)
76903831d35Sstevel 		ddi_remove_softintr(intr->sbbc_intr_id);
77003831d35Sstevel 
77103831d35Sstevel 	intr->sbbc_handler  = (sbbc_intrfunc_t)NULL;
77203831d35Sstevel 	intr->sbbc_arg = (void *)NULL;
77303831d35Sstevel 	intr->sbbc_intr_id = 0;
77403831d35Sstevel 	intr->sbbc_intr_state = NULL;
77503831d35Sstevel 	intr->sbbc_intr_lock = (kmutex_t *)NULL;
77603831d35Sstevel 	intr->sbbc_intr_next = (sbbc_intrs_t *)NULL;
77703831d35Sstevel 
77803831d35Sstevel unreg_intr_exit:
77903831d35Sstevel 
78003831d35Sstevel 	mutex_exit(&softsp->sbbc_lock);
78103831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
78203831d35Sstevel 
78303831d35Sstevel 	return (rc);
78403831d35Sstevel }
78503831d35Sstevel 
78603831d35Sstevel /*
78703831d35Sstevel  * sgsbbc_iosram_switchfrom(softsp)
78803831d35Sstevel  *      Switch master tunnel away from the specified instance.
78903831d35Sstevel  */
79003831d35Sstevel int
sgsbbc_iosram_switchfrom(struct sbbc_softstate * softsp)79103831d35Sstevel sgsbbc_iosram_switchfrom(struct sbbc_softstate *softsp)
79203831d35Sstevel {
79303831d35Sstevel 	struct sbbc_softstate	*sp;
79403831d35Sstevel 	int			rv = DDI_FAILURE;
79503831d35Sstevel 	int			new_instance;
79603831d35Sstevel 
79703831d35Sstevel 	/*
79803831d35Sstevel 	 * Find the candidate target of tunnel from the linked list.
79903831d35Sstevel 	 */
80003831d35Sstevel 	mutex_enter(&chosen_lock);
80103831d35Sstevel 	ASSERT(sgsbbc_instances);
80203831d35Sstevel 
80303831d35Sstevel 	for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
80403831d35Sstevel 		if (softsp == sp)
80503831d35Sstevel 			continue;
80603831d35Sstevel 
80703831d35Sstevel 		if (sp->sbbc_state & SBBC_STATE_DETACH)
80803831d35Sstevel 			continue;
80903831d35Sstevel 		break;
81003831d35Sstevel 	}
81103831d35Sstevel 	if (sp == NULL) {
81203831d35Sstevel 		/* at least one IOSRAM should be attached */
81303831d35Sstevel 		rv = DDI_FAILURE;
81403831d35Sstevel 	} else {
81503831d35Sstevel 		/* Do the tunnel switch */
81603831d35Sstevel 		new_instance = ddi_get_instance(sp->dip);
81703831d35Sstevel 		rv = iosram_switch_tunnel(new_instance);
81803831d35Sstevel 		if (rv == DDI_SUCCESS) {
81903831d35Sstevel 			/* reset the chosen_iosram back ref */
82003831d35Sstevel 			sp->iosram = master_iosram;
82103831d35Sstevel 		}
82203831d35Sstevel 	}
82303831d35Sstevel 	mutex_exit(&chosen_lock);
82403831d35Sstevel 	return (rv);
82503831d35Sstevel }
82603831d35Sstevel 
82703831d35Sstevel 
82803831d35Sstevel /*
82903831d35Sstevel  * Switch the tunnel to a different I/O board.
83003831d35Sstevel  * At the moment, we will say that this is
83103831d35Sstevel  * called with the instance of the SBBC to switch
83203831d35Sstevel  * to. This will probably change, but as long as we
83303831d35Sstevel  * can get a devinfo/softstate for the target SBBC it
83403831d35Sstevel  * doesn't matter what the parameter is.
83503831d35Sstevel  */
83603831d35Sstevel int
iosram_switch_tunnel(int instance)83703831d35Sstevel iosram_switch_tunnel(int instance)
83803831d35Sstevel {
83903831d35Sstevel 
84003831d35Sstevel 	sbbc_softstate_t	*to_softsp, *from_softsp;
84103831d35Sstevel 	dev_info_t		*pdip;	/* parent dip */
84203831d35Sstevel 	tunnel_t		*new_tunnel; /* new tunnel */
84303831d35Sstevel 	int			portid;
84403831d35Sstevel 	uint_t			node;	/* node id to pass to OBP */
84503831d35Sstevel 	uint_t			board;	/* board number to pass to OBP */
84603831d35Sstevel 	int			rc = DDI_SUCCESS;
84703831d35Sstevel 	static fn_t		f = "iosram_switch_tunnel";
84803831d35Sstevel 
84903831d35Sstevel 	/* Check the firmware for tunnel switch support */
85003831d35Sstevel 	if (prom_test("SUNW,switch-tunnel") != 0) {
85103831d35Sstevel 		cmn_err(CE_WARN, "Firmware does not support tunnel switch");
85203831d35Sstevel 		return (DDI_FAILURE);
85303831d35Sstevel 	}
85403831d35Sstevel 
85503831d35Sstevel 	if ((master_iosram == NULL) || (master_mbox == NULL))
85603831d35Sstevel 		return (DDI_FAILURE);
85703831d35Sstevel 
85803831d35Sstevel 	if (!(to_softsp = sbbc_get_soft_state(instance)))
85903831d35Sstevel 		return (DDI_FAILURE);
86003831d35Sstevel 
86103831d35Sstevel 	/*
86203831d35Sstevel 	 * create the new tunnel
86303831d35Sstevel 	 */
86403831d35Sstevel 	if ((new_tunnel = kmem_zalloc(sizeof (tunnel_t), KM_NOSLEEP)) == NULL) {
86503831d35Sstevel 		cmn_err(CE_WARN, "Can't allocate space for new tunnel");
86603831d35Sstevel 		return (DDI_FAILURE);
86703831d35Sstevel 	}
86803831d35Sstevel 
86903831d35Sstevel 	pdip = ddi_get_parent(to_softsp->dip);
87003831d35Sstevel 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
87103831d35Sstevel 		"portid", -1)) < 0) {
87203831d35Sstevel 
87303831d35Sstevel 		SGSBBC_DBG_ALL("%s: couldn't get portid\n", f);
87403831d35Sstevel 		return (DDI_FAILURE);
87503831d35Sstevel 	}
87603831d35Sstevel 
87703831d35Sstevel 	/*
87803831d35Sstevel 	 * Compute node id and board number from port id
87903831d35Sstevel 	 */
88003831d35Sstevel 	node	= SG_PORTID_TO_NODEID(portid);
88103831d35Sstevel 	board	= SG_IO_BD_PORTID_TO_BD_NUM(portid);
88203831d35Sstevel 
88303831d35Sstevel 	/*
88403831d35Sstevel 	 * lock the chosen IOSRAM
88503831d35Sstevel 	 */
88603831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
88703831d35Sstevel 
88803831d35Sstevel 	if (master_iosram->iosram_sbbc == NULL) {
88903831d35Sstevel 		mutex_exit(&master_iosram->iosram_lock);
89003831d35Sstevel 		return (DDI_FAILURE);
89103831d35Sstevel 	}
89203831d35Sstevel 
89303831d35Sstevel 	/*
89403831d35Sstevel 	 * If the target SBBC has not mapped in its
89503831d35Sstevel 	 * register address space, do it now
89603831d35Sstevel 	 */
89703831d35Sstevel 	mutex_enter(&to_softsp->sbbc_lock);
89803831d35Sstevel 	if (to_softsp->sbbc_regs == NULL) {
89903831d35Sstevel 		if (sbbc_map_regs(to_softsp) != DDI_SUCCESS) {
90003831d35Sstevel 			mutex_exit(&to_softsp->sbbc_lock);
90103831d35Sstevel 			mutex_exit(&master_iosram->iosram_lock);
90203831d35Sstevel 			return (DDI_FAILURE);
90303831d35Sstevel 		}
90403831d35Sstevel 	}
90503831d35Sstevel 
90603831d35Sstevel 	/*
90703831d35Sstevel 	 * Get a pointer to the current sbbc
90803831d35Sstevel 	 */
90903831d35Sstevel 	from_softsp = master_iosram->iosram_sbbc;
91003831d35Sstevel 
91103831d35Sstevel 	mutex_enter(&from_softsp->sbbc_lock);
91203831d35Sstevel 
91303831d35Sstevel 	/*
91403831d35Sstevel 	 * Disable interrupts from the SC now
91503831d35Sstevel 	 */
91603831d35Sstevel 	sbbc_disable_intr(from_softsp);
91703831d35Sstevel 
91803831d35Sstevel 	/*
91903831d35Sstevel 	 * move SC interrupts to the new tunnel
92003831d35Sstevel 	 */
92103831d35Sstevel 	if ((rc = sbbc_add_intr(to_softsp)) == DDI_FAILURE) {
92203831d35Sstevel 		cmn_err(CE_WARN, "Failed to add new interrupt handler");
92303831d35Sstevel 	} else if ((rc = tunnel_init(to_softsp, new_tunnel)) == DDI_FAILURE) {
92403831d35Sstevel 		cmn_err(CE_WARN, "Failed to initialize new tunnel");
92503831d35Sstevel 		ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock);
92603831d35Sstevel 	} else {
92703831d35Sstevel 		rw_enter(&master_iosram->tunnel_lock, RW_WRITER);
92803831d35Sstevel 
92903831d35Sstevel 		/*
93003831d35Sstevel 		 * If OBP switch is unsuccessful, abort the switch.
93103831d35Sstevel 		 */
93203831d35Sstevel 		if ((rc = prom_serengeti_tunnel_switch(node, board))
93303831d35Sstevel 			!= DDI_SUCCESS) {
93403831d35Sstevel 
93503831d35Sstevel 			/*
93603831d35Sstevel 			 * Restart other CPUs.
93703831d35Sstevel 			 */
93803831d35Sstevel 			rw_exit(&master_iosram->tunnel_lock);
93903831d35Sstevel 
94003831d35Sstevel 			cmn_err(CE_WARN, "OBP failed to switch tunnel");
94103831d35Sstevel 
94203831d35Sstevel 			/*
94303831d35Sstevel 			 * Remove interrupt
94403831d35Sstevel 			 */
94503831d35Sstevel 			ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock);
94603831d35Sstevel 
94703831d35Sstevel 			/*
94803831d35Sstevel 			 * Unmap new tunnel
94903831d35Sstevel 			 */
95003831d35Sstevel 			tunnel_fini(new_tunnel);
95103831d35Sstevel 		} else {
95203831d35Sstevel 			tunnel_t		*orig_tunnel;
95303831d35Sstevel 
95403831d35Sstevel 			orig_tunnel = master_iosram->tunnel;
95503831d35Sstevel 			tunnel_commit(to_softsp, new_tunnel);
95603831d35Sstevel 
95703831d35Sstevel 			rw_exit(&master_iosram->tunnel_lock);
95803831d35Sstevel 
95903831d35Sstevel 			/*
96003831d35Sstevel 			 * Remove interrupt from original softsp
96103831d35Sstevel 			 */
96203831d35Sstevel 			ddi_remove_intr(from_softsp->dip, 0,
96303831d35Sstevel 			    from_softsp->iblock);
96403831d35Sstevel 			/*
96503831d35Sstevel 			 * Unmap original tunnel
96603831d35Sstevel 			 */
96703831d35Sstevel 			tunnel_fini(orig_tunnel);
96803831d35Sstevel 			kmem_free(orig_tunnel, sizeof (tunnel_t));
96903831d35Sstevel 
97003831d35Sstevel 			/*
97103831d35Sstevel 			 * Move the softintrs to the new dip.
97203831d35Sstevel 			 */
97303831d35Sstevel 			(void) iosram_switch_intr();
97403831d35Sstevel 			(void) sbbc_mbox_switch(to_softsp);
97503831d35Sstevel 
97603831d35Sstevel 			from_softsp->chosen = FALSE;
97703831d35Sstevel 
97803831d35Sstevel 		}
97903831d35Sstevel 	}
98003831d35Sstevel 
98103831d35Sstevel 	/*
98203831d35Sstevel 	 * Enable interrupt.
98303831d35Sstevel 	 */
98403831d35Sstevel 	sbbc_enable_intr(master_iosram->iosram_sbbc);
98503831d35Sstevel 
98603831d35Sstevel 	/*
98703831d35Sstevel 	 * Unlock and get out
98803831d35Sstevel 	 */
98903831d35Sstevel 	mutex_exit(&from_softsp->sbbc_lock);
99003831d35Sstevel 	mutex_exit(&to_softsp->sbbc_lock);
99103831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
99203831d35Sstevel 
99303831d35Sstevel 	/*
99403831d35Sstevel 	 * Call the interrupt handler directly in case
99503831d35Sstevel 	 * we have missed an interrupt
99603831d35Sstevel 	 */
997*07d06da5SSurya Prakki 	(void) sbbc_intr_handler((caddr_t)master_iosram->iosram_sbbc);
99803831d35Sstevel 
99903831d35Sstevel 	if (rc != DDI_SUCCESS) {
100003831d35Sstevel 		/*
100103831d35Sstevel 		 * Free up the new_tunnel
100203831d35Sstevel 		 */
100303831d35Sstevel 		kmem_free(new_tunnel, sizeof (tunnel_t));
100403831d35Sstevel 		cmn_err(CE_WARN, "Tunnel switch failed");
100503831d35Sstevel 	}
100603831d35Sstevel 
100703831d35Sstevel 	return (rc);
100803831d35Sstevel 
100903831d35Sstevel }
101003831d35Sstevel 
101103831d35Sstevel /*
101203831d35Sstevel  * convert an alphanumeric OBP key to
101303831d35Sstevel  * our defined numeric keys
101403831d35Sstevel  */
101503831d35Sstevel static int
iosram_convert_key(char * toc_key)101603831d35Sstevel iosram_convert_key(char *toc_key)
101703831d35Sstevel {
101803831d35Sstevel 
101903831d35Sstevel 	if (strcmp(toc_key, TOCKEY_DOMSTAT)  == 0)
102003831d35Sstevel 		return (SBBC_DOMAIN_KEY);
102103831d35Sstevel 	if (strcmp(toc_key, TOCKEY_KEYSWPO)  == 0)
102203831d35Sstevel 		return (SBBC_KEYSWITCH_KEY);
102303831d35Sstevel 	if (strcmp(toc_key, TOCKEY_TODDATA)  == 0)
102403831d35Sstevel 		return (SBBC_TOD_KEY);
102503831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SOLCONS) == 0)
102603831d35Sstevel 		return (SBBC_CONSOLE_KEY);
102703831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SOLMBOX)  == 0)
102803831d35Sstevel 		return (SBBC_MAILBOX_KEY);
102903831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SOLSCIR)  == 0)
103003831d35Sstevel 		return (SBBC_INTR_SC_KEY);
103103831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SCSOLIR)  == 0)
103203831d35Sstevel 		return (SBBC_SC_INTR_KEY);
103303831d35Sstevel 	if (strcmp(toc_key, TOCKEY_ENVINFO)  == 0)
103403831d35Sstevel 		return (SBBC_ENVCTRL_KEY);
103503831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SOLSCIE)  == 0)
103603831d35Sstevel 		return (SBBC_INTR_SC_ENABLED_KEY);
103703831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SCSOLIE)  == 0)
103803831d35Sstevel 		return (SBBC_SC_INTR_ENABLED_KEY);
103903831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SIGBLCK)  == 0)
104003831d35Sstevel 		return (SBBC_SIGBLCK_KEY);
104103831d35Sstevel 
104203831d35Sstevel 	/* Unknown key */
104303831d35Sstevel 	return (-1);
104403831d35Sstevel }
104503831d35Sstevel 
104603831d35Sstevel /*
104703831d35Sstevel  * Move the software interrupts from the old dip to the new dip
104803831d35Sstevel  * when doing tunnel switch.
104903831d35Sstevel  */
105003831d35Sstevel static int
iosram_switch_intr()105103831d35Sstevel iosram_switch_intr()
105203831d35Sstevel {
105303831d35Sstevel 	sbbc_intrs_t	*intr;
105403831d35Sstevel 	int		intr_no;
105503831d35Sstevel 	int		rc = 0;
105603831d35Sstevel 
105703831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
105803831d35Sstevel 
105903831d35Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
106003831d35Sstevel 		intr = &master_iosram->intrs[intr_no];
106103831d35Sstevel 
106203831d35Sstevel 		if (intr->sbbc_intr_id) {
106303831d35Sstevel 			ddi_remove_softintr(intr->sbbc_intr_id);
106403831d35Sstevel 
106503831d35Sstevel 			if (ddi_add_softintr(master_iosram->iosram_sbbc->dip,
106603831d35Sstevel 				DDI_SOFTINT_HIGH,
106703831d35Sstevel 				&intr->sbbc_intr_id, NULL, NULL,
106803831d35Sstevel 				intr->sbbc_handler, intr->sbbc_arg)
106903831d35Sstevel 				!= DDI_SUCCESS) {
107003831d35Sstevel 
107103831d35Sstevel 				cmn_err(CE_WARN, "Can't add SBBC softint for "
107203831d35Sstevel 					"interrupt %x", intr_no << 1);
107303831d35Sstevel 				rc = EAGAIN;
107403831d35Sstevel 			}
107503831d35Sstevel 		}
107603831d35Sstevel 	}
107703831d35Sstevel 
107803831d35Sstevel 	return (rc);
107903831d35Sstevel }
1080