1*03831d35Sstevel /*
2*03831d35Sstevel  * CDDL HEADER START
3*03831d35Sstevel  *
4*03831d35Sstevel  * The contents of this file are subject to the terms of the
5*03831d35Sstevel  * Common Development and Distribution License (the "License").
6*03831d35Sstevel  * You may not use this file except in compliance with the License.
7*03831d35Sstevel  *
8*03831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*03831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
10*03831d35Sstevel  * See the License for the specific language governing permissions
11*03831d35Sstevel  * and limitations under the License.
12*03831d35Sstevel  *
13*03831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
14*03831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*03831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
16*03831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
17*03831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
18*03831d35Sstevel  *
19*03831d35Sstevel  * CDDL HEADER END
20*03831d35Sstevel  */
21*03831d35Sstevel 
22*03831d35Sstevel /*
23*03831d35Sstevel  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*03831d35Sstevel  * Use is subject to license terms.
25*03831d35Sstevel  */
26*03831d35Sstevel 
27*03831d35Sstevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*03831d35Sstevel 
29*03831d35Sstevel /*
30*03831d35Sstevel  * Driver for handling Serengeti I/O SRAM
31*03831d35Sstevel  * for Solaris <-> SC comm.
32*03831d35Sstevel  */
33*03831d35Sstevel 
34*03831d35Sstevel #include <sys/types.h>
35*03831d35Sstevel #include <sys/systm.h>
36*03831d35Sstevel #include <sys/cpuvar.h>
37*03831d35Sstevel #include <sys/dditypes.h>
38*03831d35Sstevel #include <sys/sunndi.h>
39*03831d35Sstevel #include <sys/param.h>
40*03831d35Sstevel #include <sys/mutex.h>
41*03831d35Sstevel #include <sys/sysmacros.h>
42*03831d35Sstevel #include <sys/errno.h>
43*03831d35Sstevel #include <sys/file.h>
44*03831d35Sstevel #include <sys/kmem.h>
45*03831d35Sstevel #include <sys/promif.h>
46*03831d35Sstevel #include <sys/prom_plat.h>
47*03831d35Sstevel #include <sys/sunddi.h>
48*03831d35Sstevel #include <sys/ddi.h>
49*03831d35Sstevel 
50*03831d35Sstevel #include <sys/serengeti.h>
51*03831d35Sstevel #include <sys/sgsbbc_priv.h>
52*03831d35Sstevel #include <sys/sgsbbc_iosram_priv.h>
53*03831d35Sstevel #include <sys/sgsbbc_mailbox_priv.h>
54*03831d35Sstevel 
55*03831d35Sstevel /*
56*03831d35Sstevel  * Local stuff
57*03831d35Sstevel  */
58*03831d35Sstevel static int iosram_rw(int, uint32_t, caddr_t, uint32_t, int);
59*03831d35Sstevel static int iosram_convert_key(char *);
60*03831d35Sstevel static int iosram_switch_intr(void);
61*03831d35Sstevel static int tunnel_init(sbbc_softstate_t *, tunnel_t *);
62*03831d35Sstevel static void tunnel_fini(tunnel_t *);
63*03831d35Sstevel static void tunnel_commit(sbbc_softstate_t *, tunnel_t *);
64*03831d35Sstevel static void clear_break();
65*03831d35Sstevel 
66*03831d35Sstevel #define	IOSRAM_GETB(tunnel, buf, sram, count) \
67*03831d35Sstevel 	ddi_rep_get8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR)
68*03831d35Sstevel 
69*03831d35Sstevel #define	IOSRAM_PUTB(tunnel, buf, sram, count) \
70*03831d35Sstevel 	ddi_rep_put8(tunnel->reg_handle, buf, sram, count, DDI_DEV_AUTOINCR)
71*03831d35Sstevel 
72*03831d35Sstevel #define	IOSRAM_PUT(tunnel, sram, buf, size) \
73*03831d35Sstevel 	/* CSTYLED */ \
74*03831d35Sstevel 	ddi_put##size(tunnel->reg_handle, (uint##size##_t *)sram, \
75*03831d35Sstevel 	/* CSTYLED */ \
76*03831d35Sstevel 	*((uint##size##_t *)buf))
77*03831d35Sstevel 
78*03831d35Sstevel #define	IOSRAM_GET(tunnel, sram, buf, size) \
79*03831d35Sstevel 	/* CSTYLED */ \
80*03831d35Sstevel 	*(uint##size##_t *)buf = ddi_get##size(tunnel->reg_handle, \
81*03831d35Sstevel 	/* CSTYLED */ \
82*03831d35Sstevel 	(uint##size##_t *)sram)
83*03831d35Sstevel 
84*03831d35Sstevel /*
85*03831d35Sstevel  * sgsbbc_iosram_is_chosen(struct sbbc_softstate *softsp)
86*03831d35Sstevel  *
87*03831d35Sstevel  *      Looks up "chosen" node property to
88*03831d35Sstevel  *      determine if it is the chosen IOSRAM.
89*03831d35Sstevel  */
90*03831d35Sstevel int
91*03831d35Sstevel sgsbbc_iosram_is_chosen(sbbc_softstate_t *softsp)
92*03831d35Sstevel {
93*03831d35Sstevel 	char		pn[MAXNAMELEN];
94*03831d35Sstevel 	char		chosen_iosram[MAXNAMELEN];
95*03831d35Sstevel 	int		nodeid;
96*03831d35Sstevel 	int		chosen;
97*03831d35Sstevel 	uint_t		tunnel;
98*03831d35Sstevel 	extern		pnode_t chosen_nodeid;
99*03831d35Sstevel 
100*03831d35Sstevel 	ASSERT(chosen_nodeid);
101*03831d35Sstevel 
102*03831d35Sstevel 	nodeid = chosen_nodeid;
103*03831d35Sstevel 	(void) prom_getprop(nodeid, "iosram", (caddr_t)&tunnel);
104*03831d35Sstevel 
105*03831d35Sstevel 	/*
106*03831d35Sstevel 	 * get the full OBP pathname of this node
107*03831d35Sstevel 	 */
108*03831d35Sstevel 	if (prom_phandle_to_path((phandle_t)tunnel, chosen_iosram,
109*03831d35Sstevel 		sizeof (chosen_iosram)) < 0) {
110*03831d35Sstevel 		cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", tunnel);
111*03831d35Sstevel 		return (0);
112*03831d35Sstevel 	}
113*03831d35Sstevel 
114*03831d35Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): prom_phandle_to_path(%x) is '%s'\n",
115*03831d35Sstevel 	softsp->sbbc_instance, nodeid, chosen_iosram);
116*03831d35Sstevel 
117*03831d35Sstevel 	(void) ddi_pathname(softsp->dip, pn);
118*03831d35Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ddi_pathname(%p) is '%s'\n",
119*03831d35Sstevel 			softsp->sbbc_instance, softsp->dip, pn);
120*03831d35Sstevel 
121*03831d35Sstevel 	chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0;
122*03831d35Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
123*03831d35Sstevel 			chosen? "MASTER" : "SLAVE");
124*03831d35Sstevel 	SGSBBC_DBG_ALL("sgsbbc_iosram(%d): ... %s\n", softsp->sbbc_instance,
125*03831d35Sstevel 			(chosen ? "MASTER" : "SLAVE"));
126*03831d35Sstevel 
127*03831d35Sstevel 	return (chosen);
128*03831d35Sstevel }
129*03831d35Sstevel 
130*03831d35Sstevel void
131*03831d35Sstevel iosram_init()
132*03831d35Sstevel {
133*03831d35Sstevel 	int	i;
134*03831d35Sstevel 
135*03831d35Sstevel 	if ((master_iosram = kmem_zalloc(sizeof (struct chosen_iosram),
136*03831d35Sstevel 	    KM_NOSLEEP)) == NULL) {
137*03831d35Sstevel 		prom_printf("Can't allocate space for Chosen IOSRAM\n");
138*03831d35Sstevel 		panic("Can't allocate space for Chosen IOSRAM");
139*03831d35Sstevel 	}
140*03831d35Sstevel 
141*03831d35Sstevel 	if ((master_iosram->tunnel = kmem_zalloc(sizeof (tunnel_t),
142*03831d35Sstevel 	    KM_NOSLEEP)) == NULL) {
143*03831d35Sstevel 		prom_printf("Can't allocate space for tunnel\n");
144*03831d35Sstevel 		panic("Can't allocate space for tunnel");
145*03831d35Sstevel 	}
146*03831d35Sstevel 
147*03831d35Sstevel 	master_iosram->iosram_sbbc = NULL;
148*03831d35Sstevel 
149*03831d35Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
150*03831d35Sstevel 		master_iosram->tunnel->tunnel_keys[i].key = 0;
151*03831d35Sstevel 		master_iosram->tunnel->tunnel_keys[i].base = NULL;
152*03831d35Sstevel 		master_iosram->tunnel->tunnel_keys[i].size = 0;
153*03831d35Sstevel 	}
154*03831d35Sstevel 
155*03831d35Sstevel 	for (i = 0; i < SBBC_MAX_INTRS; i++)
156*03831d35Sstevel 		master_iosram->intrs[i].sbbc_handler = NULL;
157*03831d35Sstevel 
158*03831d35Sstevel 	mutex_init(&master_iosram->iosram_lock, NULL, MUTEX_DEFAULT, NULL);
159*03831d35Sstevel 	rw_init(&master_iosram->tunnel_lock, NULL, RW_DEFAULT, NULL);
160*03831d35Sstevel }
161*03831d35Sstevel void
162*03831d35Sstevel iosram_fini()
163*03831d35Sstevel {
164*03831d35Sstevel 	struct	tunnel_key	*tunnel;
165*03831d35Sstevel 	int			i;
166*03831d35Sstevel 
167*03831d35Sstevel 	rw_destroy(&master_iosram->tunnel_lock);
168*03831d35Sstevel 	mutex_destroy(&master_iosram->iosram_lock);
169*03831d35Sstevel 
170*03831d35Sstevel 	/*
171*03831d35Sstevel 	 * destroy any tunnel maps
172*03831d35Sstevel 	 */
173*03831d35Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
174*03831d35Sstevel 		tunnel = &master_iosram->tunnel->tunnel_keys[i];
175*03831d35Sstevel 		if (tunnel->base != NULL) {
176*03831d35Sstevel 			ddi_regs_map_free(&tunnel->reg_handle);
177*03831d35Sstevel 			tunnel->base = NULL;
178*03831d35Sstevel 		}
179*03831d35Sstevel 	}
180*03831d35Sstevel 
181*03831d35Sstevel 	kmem_free(master_iosram->tunnel, sizeof (tunnel_t));
182*03831d35Sstevel 
183*03831d35Sstevel 	kmem_free(master_iosram, sizeof (struct chosen_iosram));
184*03831d35Sstevel 
185*03831d35Sstevel 	master_iosram = NULL;
186*03831d35Sstevel }
187*03831d35Sstevel 
188*03831d35Sstevel static void
189*03831d35Sstevel check_iosram_ver(uint16_t version)
190*03831d35Sstevel {
191*03831d35Sstevel 	uint8_t	max_ver = MAX_IOSRAM_TOC_VER;
192*03831d35Sstevel 	uint8_t	major_ver =
193*03831d35Sstevel 		(version >> IOSRAM_TOC_VER_SHIFT) & IOSRAM_TOC_VER_MASK;
194*03831d35Sstevel 
195*03831d35Sstevel 	SGSBBC_DBG_ALL("IOSRAM TOC version: %d.%d\n", major_ver,
196*03831d35Sstevel 		version & IOSRAM_TOC_VER_MASK);
197*03831d35Sstevel 	SGSBBC_DBG_ALL("Max supported IOSRAM TOC version: %d\n", max_ver);
198*03831d35Sstevel 	if (major_ver > max_ver) {
199*03831d35Sstevel 		panic("Up-rev System Controller version.\n"
200*03831d35Sstevel 		    "You must restore an earlier revision of System "
201*03831d35Sstevel 		    "Controller firmware, or upgrade Solaris.\n"
202*03831d35Sstevel 		    "Please consult the System Controller release notice "
203*03831d35Sstevel 		    "for additional details.");
204*03831d35Sstevel 	}
205*03831d35Sstevel }
206*03831d35Sstevel 
207*03831d35Sstevel static void
208*03831d35Sstevel tunnel_commit(sbbc_softstate_t *softsp, tunnel_t *new_tunnel)
209*03831d35Sstevel {
210*03831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
211*03831d35Sstevel 
212*03831d35Sstevel 	master_iosram->iosram_sbbc = softsp;
213*03831d35Sstevel 	master_iosram->tunnel = new_tunnel;
214*03831d35Sstevel 	softsp->chosen = TRUE;
215*03831d35Sstevel 
216*03831d35Sstevel 	/*
217*03831d35Sstevel 	 * SBBC has pointer to interrupt handlers for simplicity
218*03831d35Sstevel 	 */
219*03831d35Sstevel 	softsp->intr_hdlrs = master_iosram->intrs;
220*03831d35Sstevel }
221*03831d35Sstevel 
222*03831d35Sstevel static int
223*03831d35Sstevel tunnel_init(sbbc_softstate_t *softsp, tunnel_t *new_tunnel)
224*03831d35Sstevel {
225*03831d35Sstevel 	struct iosram_toc		*toc = NULL;
226*03831d35Sstevel 	int				i, key;
227*03831d35Sstevel 	struct	tunnel_key		*tunnel;
228*03831d35Sstevel 	ddi_acc_handle_t		toc_handle;
229*03831d35Sstevel 	struct ddi_device_acc_attr	attr;
230*03831d35Sstevel 
231*03831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
232*03831d35Sstevel 
233*03831d35Sstevel 	if ((softsp == (sbbc_softstate_t *)NULL) ||
234*03831d35Sstevel 		(new_tunnel == (tunnel_t *)NULL)) {
235*03831d35Sstevel 
236*03831d35Sstevel 		return (DDI_FAILURE);
237*03831d35Sstevel 	}
238*03831d35Sstevel 
239*03831d35Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
240*03831d35Sstevel 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
241*03831d35Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
242*03831d35Sstevel 
243*03831d35Sstevel 	SGSBBC_DBG_ALL("map in the IOSRAM TOC at offset %x\n",
244*03831d35Sstevel 		softsp->sram_toc);
245*03831d35Sstevel 
246*03831d35Sstevel 	/*
247*03831d35Sstevel 	 * First map in the TOC, then set up the tunnel
248*03831d35Sstevel 	 */
249*03831d35Sstevel 	if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
250*03831d35Sstevel 		(caddr_t *)&toc,
251*03831d35Sstevel 		SBBC_SRAM_OFFSET + softsp->sram_toc,
252*03831d35Sstevel 		sizeof (struct iosram_toc),
253*03831d35Sstevel 		&attr, &toc_handle) != DDI_SUCCESS) {
254*03831d35Sstevel 			cmn_err(CE_WARN, "sbbc%d: unable to map SRAM "
255*03831d35Sstevel 			    "registers", ddi_get_instance(softsp->dip));
256*03831d35Sstevel 			return (DDI_FAILURE);
257*03831d35Sstevel 	}
258*03831d35Sstevel 	SGSBBC_DBG_ALL("dip=%p mapped TOC %p\n", softsp->dip, toc);
259*03831d35Sstevel 
260*03831d35Sstevel 	check_iosram_ver(toc->iosram_version);
261*03831d35Sstevel 
262*03831d35Sstevel 	for (i = 0; i < toc->iosram_tagno; i++) {
263*03831d35Sstevel 		key = iosram_convert_key(toc->iosram_keys[i].key);
264*03831d35Sstevel 		if ((key > 0) && (key < SBBC_MAX_KEYS)) {
265*03831d35Sstevel 			tunnel = &new_tunnel->tunnel_keys[key];
266*03831d35Sstevel 			tunnel->key = key;
267*03831d35Sstevel 			tunnel->size = toc->iosram_keys[i].size;
268*03831d35Sstevel 			/*
269*03831d35Sstevel 			 * map in the SRAM area using the offset
270*03831d35Sstevel 			 * from the base of SRAM + SRAM offset into
271*03831d35Sstevel 			 * the register property for the SBBC base
272*03831d35Sstevel 			 * address
273*03831d35Sstevel 			 */
274*03831d35Sstevel 			if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
275*03831d35Sstevel 				(caddr_t *)&tunnel->base,
276*03831d35Sstevel 				SBBC_SRAM_OFFSET + toc->iosram_keys[i].offset,
277*03831d35Sstevel 				toc->iosram_keys[i].size, &attr,
278*03831d35Sstevel 				&tunnel->reg_handle) != DDI_SUCCESS) {
279*03831d35Sstevel 				cmn_err(CE_WARN, "sbbc%d: unable to map SRAM "
280*03831d35Sstevel 				    "registers", ddi_get_instance(softsp->dip));
281*03831d35Sstevel 				return (DDI_FAILURE);
282*03831d35Sstevel 			}
283*03831d35Sstevel 			SGSBBC_DBG_ALL("%d: key %s size %d offset %x addr %p\n",
284*03831d35Sstevel 				i, toc->iosram_keys[i].key,
285*03831d35Sstevel 				toc->iosram_keys[i].size,
286*03831d35Sstevel 				toc->iosram_keys[i].offset,
287*03831d35Sstevel 				tunnel->base);
288*03831d35Sstevel 
289*03831d35Sstevel 		}
290*03831d35Sstevel 	}
291*03831d35Sstevel 
292*03831d35Sstevel 
293*03831d35Sstevel 	if (toc != NULL) {
294*03831d35Sstevel 		ddi_regs_map_free(&toc_handle);
295*03831d35Sstevel 	}
296*03831d35Sstevel 
297*03831d35Sstevel 	/*
298*03831d35Sstevel 	 * Set up the 'interrupt reason' SRAM pointers
299*03831d35Sstevel 	 * for the SBBC interrupt handler
300*03831d35Sstevel 	 */
301*03831d35Sstevel 	if (INVALID_KEY(new_tunnel, SBBC_SC_INTR_KEY)) {
302*03831d35Sstevel 		/*
303*03831d35Sstevel 		 * Can't really do much if these are not here
304*03831d35Sstevel 		 */
305*03831d35Sstevel 		prom_printf("No Interrupt Reason Fields set by SC\n");
306*03831d35Sstevel 		cmn_err(CE_WARN, "No Interrupt Reason Fields set by SC");
307*03831d35Sstevel 		return (DDI_FAILURE);
308*03831d35Sstevel 	}
309*03831d35Sstevel 
310*03831d35Sstevel 	return (DDI_SUCCESS);
311*03831d35Sstevel }
312*03831d35Sstevel 
313*03831d35Sstevel /*
314*03831d35Sstevel  * Unmap a tunnel
315*03831d35Sstevel  */
316*03831d35Sstevel static void
317*03831d35Sstevel tunnel_fini(tunnel_t *tunnel)
318*03831d35Sstevel {
319*03831d35Sstevel 	int	i;
320*03831d35Sstevel 	struct	tunnel_key	*tunnel_key;
321*03831d35Sstevel 
322*03831d35Sstevel 	/*
323*03831d35Sstevel 	 * Unmap the tunnel
324*03831d35Sstevel 	 */
325*03831d35Sstevel 	for (i = 0; i < SBBC_MAX_KEYS; i++) {
326*03831d35Sstevel 		tunnel_key = &tunnel->tunnel_keys[i];
327*03831d35Sstevel 		if (tunnel_key->base != NULL) {
328*03831d35Sstevel 			ddi_regs_map_free(&tunnel_key->reg_handle);
329*03831d35Sstevel 			tunnel_key->base = NULL;
330*03831d35Sstevel 		}
331*03831d35Sstevel 	}
332*03831d35Sstevel }
333*03831d35Sstevel 
334*03831d35Sstevel static void
335*03831d35Sstevel clear_break()
336*03831d35Sstevel {
337*03831d35Sstevel 	struct tunnel_key	tunnel_key;
338*03831d35Sstevel 	uint32_t		*intr_in_reason;
339*03831d35Sstevel 	ddi_acc_handle_t	intr_in_handle;
340*03831d35Sstevel 
341*03831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
342*03831d35Sstevel 
343*03831d35Sstevel 	tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY];
344*03831d35Sstevel 	intr_in_reason = (uint32_t *)tunnel_key.base;
345*03831d35Sstevel 	intr_in_handle = tunnel_key.reg_handle;
346*03831d35Sstevel 	ddi_put32(intr_in_handle, intr_in_reason,
347*03831d35Sstevel 		ddi_get32(intr_in_handle, intr_in_reason) & ~SBBC_CONSOLE_BRK);
348*03831d35Sstevel }
349*03831d35Sstevel 
350*03831d35Sstevel int
351*03831d35Sstevel iosram_tunnel_init(sbbc_softstate_t *softsp)
352*03831d35Sstevel {
353*03831d35Sstevel 	int	rc;
354*03831d35Sstevel 
355*03831d35Sstevel 	ASSERT(master_iosram);
356*03831d35Sstevel 
357*03831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
358*03831d35Sstevel 
359*03831d35Sstevel 	if ((rc = tunnel_init(softsp, master_iosram->tunnel)) == DDI_SUCCESS) {
360*03831d35Sstevel 		tunnel_commit(softsp, master_iosram->tunnel);
361*03831d35Sstevel 		clear_break();
362*03831d35Sstevel 	}
363*03831d35Sstevel 
364*03831d35Sstevel 
365*03831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
366*03831d35Sstevel 
367*03831d35Sstevel 	return (rc);
368*03831d35Sstevel }
369*03831d35Sstevel 
370*03831d35Sstevel int
371*03831d35Sstevel iosram_read(int key, uint32_t offset, caddr_t buf, uint32_t size)
372*03831d35Sstevel {
373*03831d35Sstevel 	return (iosram_rw(key, offset, buf, size, FREAD));
374*03831d35Sstevel }
375*03831d35Sstevel 
376*03831d35Sstevel int
377*03831d35Sstevel iosram_write(int key, uint32_t offset, caddr_t buf, uint32_t size)
378*03831d35Sstevel {
379*03831d35Sstevel 	return (iosram_rw(key, offset, buf, size, FWRITE));
380*03831d35Sstevel }
381*03831d35Sstevel 
382*03831d35Sstevel 
383*03831d35Sstevel static int
384*03831d35Sstevel iosram_rw(int key, uint32_t offset, caddr_t buf, uint32_t size, int flag)
385*03831d35Sstevel {
386*03831d35Sstevel 	struct	tunnel_key	*tunnel;
387*03831d35Sstevel 	caddr_t 		sram_src;
388*03831d35Sstevel 
389*03831d35Sstevel 	/*
390*03831d35Sstevel 	 * Return right away if there is nothing to read/write.
391*03831d35Sstevel 	 */
392*03831d35Sstevel 	if (size == 0)
393*03831d35Sstevel 		return (0);
394*03831d35Sstevel 
395*03831d35Sstevel 	rw_enter(&master_iosram->tunnel_lock, RW_READER);
396*03831d35Sstevel 
397*03831d35Sstevel 	/*
398*03831d35Sstevel 	 * Key not matched ?
399*03831d35Sstevel 	 */
400*03831d35Sstevel 	if (INVALID_KEY(master_iosram->tunnel, key)) {
401*03831d35Sstevel 		rw_exit(&master_iosram->tunnel_lock);
402*03831d35Sstevel 		return (ENXIO);
403*03831d35Sstevel 	}
404*03831d35Sstevel 
405*03831d35Sstevel 	tunnel = &master_iosram->tunnel->tunnel_keys[key];
406*03831d35Sstevel 	if ((offset + size) > tunnel->size) {
407*03831d35Sstevel 		rw_exit(&master_iosram->tunnel_lock);
408*03831d35Sstevel 		return (EFBIG);
409*03831d35Sstevel 	}
410*03831d35Sstevel 
411*03831d35Sstevel 	sram_src = tunnel->base + offset;
412*03831d35Sstevel 
413*03831d35Sstevel 	/*
414*03831d35Sstevel 	 * Atomic reads/writes might be necessary for some clients.
415*03831d35Sstevel 	 * We assume that such clients could guarantee their buffers
416*03831d35Sstevel 	 * are aligned at the boundary of the request sizes.  We also
417*03831d35Sstevel 	 * assume that the source/destination of such requests are
418*03831d35Sstevel 	 * aligned at the right boundaries in IOSRAM.  If either
419*03831d35Sstevel 	 * condition fails, byte access is performed.
420*03831d35Sstevel 	 */
421*03831d35Sstevel 	if (flag == FREAD) {
422*03831d35Sstevel 		switch (size) {
423*03831d35Sstevel 		case sizeof (uint16_t):
424*03831d35Sstevel 		case sizeof (uint32_t):
425*03831d35Sstevel 		case sizeof (uint64_t):
426*03831d35Sstevel 			if (IS_P2ALIGNED(sram_src, size) &&
427*03831d35Sstevel 				IS_P2ALIGNED(buf, size)) {
428*03831d35Sstevel 
429*03831d35Sstevel 				if (size == sizeof (uint16_t))
430*03831d35Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 16);
431*03831d35Sstevel 				else if (size == sizeof (uint32_t))
432*03831d35Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 32);
433*03831d35Sstevel 				else
434*03831d35Sstevel 					IOSRAM_GET(tunnel, sram_src, buf, 64);
435*03831d35Sstevel 				break;
436*03831d35Sstevel 			}
437*03831d35Sstevel 			/* FALLTHRU */
438*03831d35Sstevel 		default:
439*03831d35Sstevel 			IOSRAM_GETB(tunnel, (uint8_t *)buf,
440*03831d35Sstevel 				(uint8_t *)sram_src, (size_t)size);
441*03831d35Sstevel 			break;
442*03831d35Sstevel 		}
443*03831d35Sstevel 	} else {
444*03831d35Sstevel 		switch (size) {
445*03831d35Sstevel 		case sizeof (uint16_t):
446*03831d35Sstevel 		case sizeof (uint32_t):
447*03831d35Sstevel 		case sizeof (uint64_t):
448*03831d35Sstevel 			if (IS_P2ALIGNED(sram_src, size) &&
449*03831d35Sstevel 				IS_P2ALIGNED(buf, size)) {
450*03831d35Sstevel 
451*03831d35Sstevel 				if (size == sizeof (uint16_t))
452*03831d35Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 16);
453*03831d35Sstevel 				else if (size == sizeof (uint32_t))
454*03831d35Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 32);
455*03831d35Sstevel 				else
456*03831d35Sstevel 					IOSRAM_PUT(tunnel, sram_src, buf, 64);
457*03831d35Sstevel 				break;
458*03831d35Sstevel 			}
459*03831d35Sstevel 			/* FALLTHRU */
460*03831d35Sstevel 		default:
461*03831d35Sstevel 			IOSRAM_PUTB(tunnel, (uint8_t *)buf,
462*03831d35Sstevel 				(uint8_t *)sram_src, (size_t)size);
463*03831d35Sstevel 			break;
464*03831d35Sstevel 		}
465*03831d35Sstevel 	}
466*03831d35Sstevel 
467*03831d35Sstevel 	rw_exit(&master_iosram->tunnel_lock);
468*03831d35Sstevel 	return (0);
469*03831d35Sstevel 
470*03831d35Sstevel }
471*03831d35Sstevel 
472*03831d35Sstevel int
473*03831d35Sstevel iosram_size(int key)
474*03831d35Sstevel {
475*03831d35Sstevel 	int size = -1;
476*03831d35Sstevel 
477*03831d35Sstevel 	rw_enter(&master_iosram->tunnel_lock, RW_READER);
478*03831d35Sstevel 
479*03831d35Sstevel 	/*
480*03831d35Sstevel 	 * Key not matched ?
481*03831d35Sstevel 	 */
482*03831d35Sstevel 	if (!INVALID_KEY(master_iosram->tunnel, key))
483*03831d35Sstevel 		size = master_iosram->tunnel->tunnel_keys[key].size;
484*03831d35Sstevel 
485*03831d35Sstevel 	rw_exit(&master_iosram->tunnel_lock);
486*03831d35Sstevel 
487*03831d35Sstevel 	return (size);
488*03831d35Sstevel }
489*03831d35Sstevel 
490*03831d35Sstevel /*
491*03831d35Sstevel  * Generate an interrupt to the SC using the SBBC EPLD
492*03831d35Sstevel  *
493*03831d35Sstevel  * Note: intr_num can be multiple interrupts OR'ed together
494*03831d35Sstevel  */
495*03831d35Sstevel int
496*03831d35Sstevel iosram_send_intr(uint32_t intr_num)
497*03831d35Sstevel {
498*03831d35Sstevel 
499*03831d35Sstevel 	int		rc = 0;
500*03831d35Sstevel 	uint32_t	intr_reason;
501*03831d35Sstevel 	uint32_t	intr_enabled;
502*03831d35Sstevel 
503*03831d35Sstevel 	/*
504*03831d35Sstevel 	 * Verify that we have already set up the master sbbc
505*03831d35Sstevel 	 */
506*03831d35Sstevel 	if (master_iosram == NULL)
507*03831d35Sstevel 		return (ENXIO);
508*03831d35Sstevel 
509*03831d35Sstevel 	/*
510*03831d35Sstevel 	 * Grab the lock to prevent tunnel switch in the middle
511*03831d35Sstevel 	 * of sending an interrupt.
512*03831d35Sstevel 	 */
513*03831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
514*03831d35Sstevel 
515*03831d35Sstevel 	if (master_iosram->iosram_sbbc == NULL) {
516*03831d35Sstevel 		rc = ENXIO;
517*03831d35Sstevel 		goto send_intr_exit;
518*03831d35Sstevel 	}
519*03831d35Sstevel 
520*03831d35Sstevel 	if ((rc = sbbc_send_intr(master_iosram->iosram_sbbc, FALSE)) != 0) {
521*03831d35Sstevel 		/*
522*03831d35Sstevel 		 * previous interrupts have not been cleared yet by the SC
523*03831d35Sstevel 		 */
524*03831d35Sstevel 		goto send_intr_exit;
525*03831d35Sstevel 	}
526*03831d35Sstevel 
527*03831d35Sstevel 	/*
528*03831d35Sstevel 	 * Set a bit in the interrupt reason field
529*03831d35Sstevel 	 * call back into the sbbc handler to hit the EPLD
530*03831d35Sstevel 	 *
531*03831d35Sstevel 	 * First check the interrupts enabled by the SC
532*03831d35Sstevel 	 */
533*03831d35Sstevel 	if ((rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0,
534*03831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
535*03831d35Sstevel 
536*03831d35Sstevel 		goto send_intr_exit;
537*03831d35Sstevel 	}
538*03831d35Sstevel 
539*03831d35Sstevel 	if ((intr_enabled & intr_num) != intr_num) {
540*03831d35Sstevel 		/*
541*03831d35Sstevel 		 * at least one of the interrupts is
542*03831d35Sstevel 		 * not enabled by the SC
543*03831d35Sstevel 		 */
544*03831d35Sstevel 		rc = ENOTSUP;
545*03831d35Sstevel 		goto send_intr_exit;
546*03831d35Sstevel 	}
547*03831d35Sstevel 
548*03831d35Sstevel 	if ((rc = iosram_read(SBBC_INTR_SC_KEY, 0,
549*03831d35Sstevel 		(caddr_t)&intr_reason, sizeof (intr_reason))) != 0) {
550*03831d35Sstevel 
551*03831d35Sstevel 		goto send_intr_exit;
552*03831d35Sstevel 	}
553*03831d35Sstevel 
554*03831d35Sstevel 	if ((intr_reason & intr_num) == intr_num) {
555*03831d35Sstevel 		/*
556*03831d35Sstevel 		 * All interrupts specified are already pending
557*03831d35Sstevel 		 */
558*03831d35Sstevel 		rc = EBUSY;
559*03831d35Sstevel 		goto send_intr_exit;
560*03831d35Sstevel 	}
561*03831d35Sstevel 
562*03831d35Sstevel 	intr_reason |= intr_num;
563*03831d35Sstevel 
564*03831d35Sstevel 	if ((rc = iosram_write(SBBC_INTR_SC_KEY, 0,
565*03831d35Sstevel 		(caddr_t)&intr_reason, sizeof (intr_reason))) != 0) {
566*03831d35Sstevel 
567*03831d35Sstevel 		goto send_intr_exit;
568*03831d35Sstevel 	}
569*03831d35Sstevel 
570*03831d35Sstevel 	/*
571*03831d35Sstevel 	 * Hit the EPLD interrupt bit
572*03831d35Sstevel 	 */
573*03831d35Sstevel 
574*03831d35Sstevel 	rc = sbbc_send_intr(master_iosram->iosram_sbbc, TRUE);
575*03831d35Sstevel 
576*03831d35Sstevel send_intr_exit:
577*03831d35Sstevel 
578*03831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
579*03831d35Sstevel 
580*03831d35Sstevel 	return (rc);
581*03831d35Sstevel }
582*03831d35Sstevel 
583*03831d35Sstevel /*
584*03831d35Sstevel  * Register an interrupt handler
585*03831d35Sstevel  */
586*03831d35Sstevel int
587*03831d35Sstevel iosram_reg_intr(uint32_t intr_num, sbbc_intrfunc_t intr_handler,
588*03831d35Sstevel 		caddr_t arg, uint_t *state, kmutex_t *lock)
589*03831d35Sstevel {
590*03831d35Sstevel 	sbbc_softstate_t	*softsp;
591*03831d35Sstevel 	int			rc = 0;
592*03831d35Sstevel 	sbbc_intrs_t		*intr;
593*03831d35Sstevel 	int			intr_no;
594*03831d35Sstevel 	uint32_t		intr_enabled;
595*03831d35Sstevel 
596*03831d35Sstevel 	/*
597*03831d35Sstevel 	 * Verify that we have already set up the master sbbc
598*03831d35Sstevel 	 */
599*03831d35Sstevel 	if (master_iosram == NULL)
600*03831d35Sstevel 		return (ENXIO);
601*03831d35Sstevel 
602*03831d35Sstevel 	/*
603*03831d35Sstevel 	 * determine which bit is this intr_num for ?
604*03831d35Sstevel 	 */
605*03831d35Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
606*03831d35Sstevel 		if (intr_num == (1 << intr_no))
607*03831d35Sstevel 			break;
608*03831d35Sstevel 	}
609*03831d35Sstevel 
610*03831d35Sstevel 	/*
611*03831d35Sstevel 	 * Check the parameters
612*03831d35Sstevel 	 */
613*03831d35Sstevel 	if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS) ||
614*03831d35Sstevel 		(intr_handler == NULL) || (state == NULL) ||
615*03831d35Sstevel 		(lock == NULL))
616*03831d35Sstevel 		return (EINVAL);
617*03831d35Sstevel 
618*03831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
619*03831d35Sstevel 
620*03831d35Sstevel 	if ((softsp = master_iosram->iosram_sbbc) == NULL) {
621*03831d35Sstevel 		mutex_exit(&master_iosram->iosram_lock);
622*03831d35Sstevel 		return (ENXIO);
623*03831d35Sstevel 	}
624*03831d35Sstevel 
625*03831d35Sstevel 	mutex_enter(&softsp->sbbc_lock);
626*03831d35Sstevel 
627*03831d35Sstevel 	intr = &master_iosram->intrs[intr_no];
628*03831d35Sstevel 
629*03831d35Sstevel 	if (intr->sbbc_handler != (sbbc_intrfunc_t)NULL) {
630*03831d35Sstevel 		rc = EBUSY;
631*03831d35Sstevel 		goto reg_intr_exit;
632*03831d35Sstevel 	}
633*03831d35Sstevel 
634*03831d35Sstevel 	intr->sbbc_handler  = intr_handler;
635*03831d35Sstevel 	intr->sbbc_arg = (void *)arg;
636*03831d35Sstevel 	intr->sbbc_intr_state = state;
637*03831d35Sstevel 	intr->sbbc_intr_lock = lock;
638*03831d35Sstevel 	intr->sbbc_intr_next = (sbbc_intrs_t *)NULL;
639*03831d35Sstevel 
640*03831d35Sstevel 	/*
641*03831d35Sstevel 	 * we need to make sure that the mutex is for
642*03831d35Sstevel 	 * an ADAPTIVE lock, so call mutex_init() again with
643*03831d35Sstevel 	 * the sbbc iblock cookie
644*03831d35Sstevel 	 */
645*03831d35Sstevel 	mutex_init(lock, NULL, MUTEX_DRIVER,
646*03831d35Sstevel 		(void *)softsp->iblock);
647*03831d35Sstevel 
648*03831d35Sstevel 	if (ddi_add_softintr(softsp->dip, DDI_SOFTINT_HIGH,
649*03831d35Sstevel 		&intr->sbbc_intr_id, NULL, NULL,
650*03831d35Sstevel 		intr_handler, (caddr_t)arg) != DDI_SUCCESS) {
651*03831d35Sstevel 
652*03831d35Sstevel 		cmn_err(CE_WARN, "Can't add SBBC softint");
653*03831d35Sstevel 		rc = EAGAIN;
654*03831d35Sstevel 		goto reg_intr_exit;
655*03831d35Sstevel 	}
656*03831d35Sstevel 
657*03831d35Sstevel 	/*
658*03831d35Sstevel 	 * Set the bit in the Interrupts Enabled Field for this
659*03831d35Sstevel 	 * interrupt
660*03831d35Sstevel 	 */
661*03831d35Sstevel 	if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
662*03831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
663*03831d35Sstevel 
664*03831d35Sstevel 		goto reg_intr_exit;
665*03831d35Sstevel 	}
666*03831d35Sstevel 
667*03831d35Sstevel 	intr_enabled |= intr_num;
668*03831d35Sstevel 
669*03831d35Sstevel 	if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0,
670*03831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
671*03831d35Sstevel 
672*03831d35Sstevel 		goto reg_intr_exit;
673*03831d35Sstevel 	}
674*03831d35Sstevel 
675*03831d35Sstevel reg_intr_exit:
676*03831d35Sstevel 
677*03831d35Sstevel 	mutex_exit(&softsp->sbbc_lock);
678*03831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
679*03831d35Sstevel 
680*03831d35Sstevel 	return (rc);
681*03831d35Sstevel }
682*03831d35Sstevel 
683*03831d35Sstevel /*
684*03831d35Sstevel  * Remove an interrupt handler
685*03831d35Sstevel  */
686*03831d35Sstevel int
687*03831d35Sstevel iosram_unreg_intr(uint32_t intr_num)
688*03831d35Sstevel {
689*03831d35Sstevel 	sbbc_softstate_t	*softsp;
690*03831d35Sstevel 	int			rc = 0;
691*03831d35Sstevel 	sbbc_intrs_t		*intr;
692*03831d35Sstevel 	int			intr_no;
693*03831d35Sstevel 	uint32_t		intr_enabled;
694*03831d35Sstevel 
695*03831d35Sstevel 	/*
696*03831d35Sstevel 	 * Verify that we have already set up the master sbbc
697*03831d35Sstevel 	 */
698*03831d35Sstevel 	if (master_iosram == NULL)
699*03831d35Sstevel 		return (ENXIO);
700*03831d35Sstevel 
701*03831d35Sstevel 	/*
702*03831d35Sstevel 	 * determine which bit is this intr_num for ?
703*03831d35Sstevel 	 */
704*03831d35Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
705*03831d35Sstevel 		if (intr_num == (1 << intr_no))
706*03831d35Sstevel 			break;
707*03831d35Sstevel 	}
708*03831d35Sstevel 
709*03831d35Sstevel 	if ((intr_no < 0) || (intr_no >= SBBC_MAX_INTRS))
710*03831d35Sstevel 		return (EINVAL);
711*03831d35Sstevel 
712*03831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
713*03831d35Sstevel 
714*03831d35Sstevel 	if ((softsp = master_iosram->iosram_sbbc) == NULL) {
715*03831d35Sstevel 		mutex_exit(&master_iosram->iosram_lock);
716*03831d35Sstevel 		return (ENXIO);
717*03831d35Sstevel 	}
718*03831d35Sstevel 
719*03831d35Sstevel 	mutex_enter(&softsp->sbbc_lock);
720*03831d35Sstevel 
721*03831d35Sstevel 	intr = &master_iosram->intrs[intr_no];
722*03831d35Sstevel 
723*03831d35Sstevel 	/*
724*03831d35Sstevel 	 * No handler installed
725*03831d35Sstevel 	 */
726*03831d35Sstevel 	if (intr->sbbc_handler == (sbbc_intrfunc_t)NULL) {
727*03831d35Sstevel 		rc = EINVAL;
728*03831d35Sstevel 		goto unreg_intr_exit;
729*03831d35Sstevel 	}
730*03831d35Sstevel 
731*03831d35Sstevel 	/*
732*03831d35Sstevel 	 * Unset the bit in the Interrupts Enabled Field for this
733*03831d35Sstevel 	 * interrupt
734*03831d35Sstevel 	 */
735*03831d35Sstevel 	if ((rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
736*03831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
737*03831d35Sstevel 
738*03831d35Sstevel 		goto unreg_intr_exit;
739*03831d35Sstevel 	}
740*03831d35Sstevel 
741*03831d35Sstevel 	intr_enabled &= ~intr_num;
742*03831d35Sstevel 
743*03831d35Sstevel 	if ((rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY, 0,
744*03831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) != 0) {
745*03831d35Sstevel 
746*03831d35Sstevel 		goto unreg_intr_exit;
747*03831d35Sstevel 	}
748*03831d35Sstevel 
749*03831d35Sstevel 	/*
750*03831d35Sstevel 	 * If handler is running, wait until it's done.
751*03831d35Sstevel 	 * It won't get triggered again because we disabled it above.
752*03831d35Sstevel 	 * When we wait, drop sbbc_lock so other interrupt handlers
753*03831d35Sstevel 	 * can still run.
754*03831d35Sstevel 	 */
755*03831d35Sstevel 	for (; ; ) {
756*03831d35Sstevel 		mutex_enter(intr->sbbc_intr_lock);
757*03831d35Sstevel 		if (*(intr->sbbc_intr_state) != SBBC_INTR_IDLE) {
758*03831d35Sstevel 			mutex_exit(intr->sbbc_intr_lock);
759*03831d35Sstevel 			mutex_exit(&softsp->sbbc_lock);
760*03831d35Sstevel 			delay(drv_usectohz(10000));
761*03831d35Sstevel 			mutex_enter(&softsp->sbbc_lock);
762*03831d35Sstevel 			mutex_enter(intr->sbbc_intr_lock);
763*03831d35Sstevel 		} else {
764*03831d35Sstevel 			break;
765*03831d35Sstevel 		}
766*03831d35Sstevel 		mutex_exit(intr->sbbc_intr_lock);
767*03831d35Sstevel 	}
768*03831d35Sstevel 
769*03831d35Sstevel 	if (intr->sbbc_intr_id)
770*03831d35Sstevel 		ddi_remove_softintr(intr->sbbc_intr_id);
771*03831d35Sstevel 
772*03831d35Sstevel 	intr->sbbc_handler  = (sbbc_intrfunc_t)NULL;
773*03831d35Sstevel 	intr->sbbc_arg = (void *)NULL;
774*03831d35Sstevel 	intr->sbbc_intr_id = 0;
775*03831d35Sstevel 	intr->sbbc_intr_state = NULL;
776*03831d35Sstevel 	intr->sbbc_intr_lock = (kmutex_t *)NULL;
777*03831d35Sstevel 	intr->sbbc_intr_next = (sbbc_intrs_t *)NULL;
778*03831d35Sstevel 
779*03831d35Sstevel unreg_intr_exit:
780*03831d35Sstevel 
781*03831d35Sstevel 	mutex_exit(&softsp->sbbc_lock);
782*03831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
783*03831d35Sstevel 
784*03831d35Sstevel 	return (rc);
785*03831d35Sstevel }
786*03831d35Sstevel 
787*03831d35Sstevel /*
788*03831d35Sstevel  * sgsbbc_iosram_switchfrom(softsp)
789*03831d35Sstevel  *      Switch master tunnel away from the specified instance.
790*03831d35Sstevel  */
791*03831d35Sstevel int
792*03831d35Sstevel sgsbbc_iosram_switchfrom(struct sbbc_softstate *softsp)
793*03831d35Sstevel {
794*03831d35Sstevel 	struct sbbc_softstate	*sp;
795*03831d35Sstevel 	int			rv = DDI_FAILURE;
796*03831d35Sstevel 	int			new_instance;
797*03831d35Sstevel 
798*03831d35Sstevel 	/*
799*03831d35Sstevel 	 * Find the candidate target of tunnel from the linked list.
800*03831d35Sstevel 	 */
801*03831d35Sstevel 	mutex_enter(&chosen_lock);
802*03831d35Sstevel 	ASSERT(sgsbbc_instances);
803*03831d35Sstevel 
804*03831d35Sstevel 	for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
805*03831d35Sstevel 		if (softsp == sp)
806*03831d35Sstevel 			continue;
807*03831d35Sstevel 
808*03831d35Sstevel 		if (sp->sbbc_state & SBBC_STATE_DETACH)
809*03831d35Sstevel 			continue;
810*03831d35Sstevel 		break;
811*03831d35Sstevel 	}
812*03831d35Sstevel 	if (sp == NULL) {
813*03831d35Sstevel 		/* at least one IOSRAM should be attached */
814*03831d35Sstevel 		rv = DDI_FAILURE;
815*03831d35Sstevel 	} else {
816*03831d35Sstevel 		/* Do the tunnel switch */
817*03831d35Sstevel 		new_instance = ddi_get_instance(sp->dip);
818*03831d35Sstevel 		rv = iosram_switch_tunnel(new_instance);
819*03831d35Sstevel 		if (rv == DDI_SUCCESS) {
820*03831d35Sstevel 			/* reset the chosen_iosram back ref */
821*03831d35Sstevel 			sp->iosram = master_iosram;
822*03831d35Sstevel 		}
823*03831d35Sstevel 	}
824*03831d35Sstevel 	mutex_exit(&chosen_lock);
825*03831d35Sstevel 	return (rv);
826*03831d35Sstevel }
827*03831d35Sstevel 
828*03831d35Sstevel 
829*03831d35Sstevel /*
830*03831d35Sstevel  * Switch the tunnel to a different I/O board.
831*03831d35Sstevel  * At the moment, we will say that this is
832*03831d35Sstevel  * called with the instance of the SBBC to switch
833*03831d35Sstevel  * to. This will probably change, but as long as we
834*03831d35Sstevel  * can get a devinfo/softstate for the target SBBC it
835*03831d35Sstevel  * doesn't matter what the parameter is.
836*03831d35Sstevel  */
837*03831d35Sstevel int
838*03831d35Sstevel iosram_switch_tunnel(int instance)
839*03831d35Sstevel {
840*03831d35Sstevel 
841*03831d35Sstevel 	sbbc_softstate_t	*to_softsp, *from_softsp;
842*03831d35Sstevel 	dev_info_t		*pdip;	/* parent dip */
843*03831d35Sstevel 	tunnel_t		*new_tunnel; /* new tunnel */
844*03831d35Sstevel 	int			portid;
845*03831d35Sstevel 	uint_t			node;	/* node id to pass to OBP */
846*03831d35Sstevel 	uint_t			board;	/* board number to pass to OBP */
847*03831d35Sstevel 	int			rc = DDI_SUCCESS;
848*03831d35Sstevel 	static fn_t		f = "iosram_switch_tunnel";
849*03831d35Sstevel 
850*03831d35Sstevel 	/* Check the firmware for tunnel switch support */
851*03831d35Sstevel 	if (prom_test("SUNW,switch-tunnel") != 0) {
852*03831d35Sstevel 		cmn_err(CE_WARN, "Firmware does not support tunnel switch");
853*03831d35Sstevel 		return (DDI_FAILURE);
854*03831d35Sstevel 	}
855*03831d35Sstevel 
856*03831d35Sstevel 	if ((master_iosram == NULL) || (master_mbox == NULL))
857*03831d35Sstevel 		return (DDI_FAILURE);
858*03831d35Sstevel 
859*03831d35Sstevel 	if (!(to_softsp = sbbc_get_soft_state(instance)))
860*03831d35Sstevel 		return (DDI_FAILURE);
861*03831d35Sstevel 
862*03831d35Sstevel 	/*
863*03831d35Sstevel 	 * create the new tunnel
864*03831d35Sstevel 	 */
865*03831d35Sstevel 	if ((new_tunnel = kmem_zalloc(sizeof (tunnel_t), KM_NOSLEEP)) == NULL) {
866*03831d35Sstevel 		cmn_err(CE_WARN, "Can't allocate space for new tunnel");
867*03831d35Sstevel 		return (DDI_FAILURE);
868*03831d35Sstevel 	}
869*03831d35Sstevel 
870*03831d35Sstevel 	pdip = ddi_get_parent(to_softsp->dip);
871*03831d35Sstevel 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
872*03831d35Sstevel 		"portid", -1)) < 0) {
873*03831d35Sstevel 
874*03831d35Sstevel 		SGSBBC_DBG_ALL("%s: couldn't get portid\n", f);
875*03831d35Sstevel 		return (DDI_FAILURE);
876*03831d35Sstevel 	}
877*03831d35Sstevel 
878*03831d35Sstevel 	/*
879*03831d35Sstevel 	 * Compute node id and board number from port id
880*03831d35Sstevel 	 */
881*03831d35Sstevel 	node	= SG_PORTID_TO_NODEID(portid);
882*03831d35Sstevel 	board	= SG_IO_BD_PORTID_TO_BD_NUM(portid);
883*03831d35Sstevel 
884*03831d35Sstevel 	/*
885*03831d35Sstevel 	 * lock the chosen IOSRAM
886*03831d35Sstevel 	 */
887*03831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
888*03831d35Sstevel 
889*03831d35Sstevel 	if (master_iosram->iosram_sbbc == NULL) {
890*03831d35Sstevel 		mutex_exit(&master_iosram->iosram_lock);
891*03831d35Sstevel 		return (DDI_FAILURE);
892*03831d35Sstevel 	}
893*03831d35Sstevel 
894*03831d35Sstevel 	/*
895*03831d35Sstevel 	 * If the target SBBC has not mapped in its
896*03831d35Sstevel 	 * register address space, do it now
897*03831d35Sstevel 	 */
898*03831d35Sstevel 	mutex_enter(&to_softsp->sbbc_lock);
899*03831d35Sstevel 	if (to_softsp->sbbc_regs == NULL) {
900*03831d35Sstevel 		if (sbbc_map_regs(to_softsp) != DDI_SUCCESS) {
901*03831d35Sstevel 			mutex_exit(&to_softsp->sbbc_lock);
902*03831d35Sstevel 			mutex_exit(&master_iosram->iosram_lock);
903*03831d35Sstevel 			return (DDI_FAILURE);
904*03831d35Sstevel 		}
905*03831d35Sstevel 	}
906*03831d35Sstevel 
907*03831d35Sstevel 	/*
908*03831d35Sstevel 	 * Get a pointer to the current sbbc
909*03831d35Sstevel 	 */
910*03831d35Sstevel 	from_softsp = master_iosram->iosram_sbbc;
911*03831d35Sstevel 
912*03831d35Sstevel 	mutex_enter(&from_softsp->sbbc_lock);
913*03831d35Sstevel 
914*03831d35Sstevel 	/*
915*03831d35Sstevel 	 * Disable interrupts from the SC now
916*03831d35Sstevel 	 */
917*03831d35Sstevel 	sbbc_disable_intr(from_softsp);
918*03831d35Sstevel 
919*03831d35Sstevel 	/*
920*03831d35Sstevel 	 * move SC interrupts to the new tunnel
921*03831d35Sstevel 	 */
922*03831d35Sstevel 	if ((rc = sbbc_add_intr(to_softsp)) == DDI_FAILURE) {
923*03831d35Sstevel 		cmn_err(CE_WARN, "Failed to add new interrupt handler");
924*03831d35Sstevel 	} else if ((rc = tunnel_init(to_softsp, new_tunnel)) == DDI_FAILURE) {
925*03831d35Sstevel 		cmn_err(CE_WARN, "Failed to initialize new tunnel");
926*03831d35Sstevel 		ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock);
927*03831d35Sstevel 	} else {
928*03831d35Sstevel 		rw_enter(&master_iosram->tunnel_lock, RW_WRITER);
929*03831d35Sstevel 
930*03831d35Sstevel 		/*
931*03831d35Sstevel 		 * If OBP switch is unsuccessful, abort the switch.
932*03831d35Sstevel 		 */
933*03831d35Sstevel 		if ((rc = prom_serengeti_tunnel_switch(node, board))
934*03831d35Sstevel 			!= DDI_SUCCESS) {
935*03831d35Sstevel 
936*03831d35Sstevel 			/*
937*03831d35Sstevel 			 * Restart other CPUs.
938*03831d35Sstevel 			 */
939*03831d35Sstevel 			rw_exit(&master_iosram->tunnel_lock);
940*03831d35Sstevel 
941*03831d35Sstevel 			cmn_err(CE_WARN, "OBP failed to switch tunnel");
942*03831d35Sstevel 
943*03831d35Sstevel 			/*
944*03831d35Sstevel 			 * Remove interrupt
945*03831d35Sstevel 			 */
946*03831d35Sstevel 			ddi_remove_intr(to_softsp->dip, 0, to_softsp->iblock);
947*03831d35Sstevel 
948*03831d35Sstevel 			/*
949*03831d35Sstevel 			 * Unmap new tunnel
950*03831d35Sstevel 			 */
951*03831d35Sstevel 			tunnel_fini(new_tunnel);
952*03831d35Sstevel 		} else {
953*03831d35Sstevel 			tunnel_t		*orig_tunnel;
954*03831d35Sstevel 
955*03831d35Sstevel 			orig_tunnel = master_iosram->tunnel;
956*03831d35Sstevel 			tunnel_commit(to_softsp, new_tunnel);
957*03831d35Sstevel 
958*03831d35Sstevel 			rw_exit(&master_iosram->tunnel_lock);
959*03831d35Sstevel 
960*03831d35Sstevel 			/*
961*03831d35Sstevel 			 * Remove interrupt from original softsp
962*03831d35Sstevel 			 */
963*03831d35Sstevel 			ddi_remove_intr(from_softsp->dip, 0,
964*03831d35Sstevel 			    from_softsp->iblock);
965*03831d35Sstevel 			/*
966*03831d35Sstevel 			 * Unmap original tunnel
967*03831d35Sstevel 			 */
968*03831d35Sstevel 			tunnel_fini(orig_tunnel);
969*03831d35Sstevel 			kmem_free(orig_tunnel, sizeof (tunnel_t));
970*03831d35Sstevel 
971*03831d35Sstevel 			/*
972*03831d35Sstevel 			 * Move the softintrs to the new dip.
973*03831d35Sstevel 			 */
974*03831d35Sstevel 			(void) iosram_switch_intr();
975*03831d35Sstevel 			(void) sbbc_mbox_switch(to_softsp);
976*03831d35Sstevel 
977*03831d35Sstevel 			from_softsp->chosen = FALSE;
978*03831d35Sstevel 
979*03831d35Sstevel 		}
980*03831d35Sstevel 	}
981*03831d35Sstevel 
982*03831d35Sstevel 	/*
983*03831d35Sstevel 	 * Enable interrupt.
984*03831d35Sstevel 	 */
985*03831d35Sstevel 	sbbc_enable_intr(master_iosram->iosram_sbbc);
986*03831d35Sstevel 
987*03831d35Sstevel 	/*
988*03831d35Sstevel 	 * Unlock and get out
989*03831d35Sstevel 	 */
990*03831d35Sstevel 	mutex_exit(&from_softsp->sbbc_lock);
991*03831d35Sstevel 	mutex_exit(&to_softsp->sbbc_lock);
992*03831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
993*03831d35Sstevel 
994*03831d35Sstevel 	/*
995*03831d35Sstevel 	 * Call the interrupt handler directly in case
996*03831d35Sstevel 	 * we have missed an interrupt
997*03831d35Sstevel 	 */
998*03831d35Sstevel 	(void) sbbc_intr_handler(master_iosram->iosram_sbbc);
999*03831d35Sstevel 
1000*03831d35Sstevel 	if (rc != DDI_SUCCESS) {
1001*03831d35Sstevel 		/*
1002*03831d35Sstevel 		 * Free up the new_tunnel
1003*03831d35Sstevel 		 */
1004*03831d35Sstevel 		kmem_free(new_tunnel, sizeof (tunnel_t));
1005*03831d35Sstevel 		cmn_err(CE_WARN, "Tunnel switch failed");
1006*03831d35Sstevel 	}
1007*03831d35Sstevel 
1008*03831d35Sstevel 	return (rc);
1009*03831d35Sstevel 
1010*03831d35Sstevel }
1011*03831d35Sstevel 
1012*03831d35Sstevel /*
1013*03831d35Sstevel  * convert an alphanumeric OBP key to
1014*03831d35Sstevel  * our defined numeric keys
1015*03831d35Sstevel  */
1016*03831d35Sstevel static int
1017*03831d35Sstevel iosram_convert_key(char *toc_key)
1018*03831d35Sstevel {
1019*03831d35Sstevel 
1020*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_DOMSTAT)  == 0)
1021*03831d35Sstevel 		return (SBBC_DOMAIN_KEY);
1022*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_KEYSWPO)  == 0)
1023*03831d35Sstevel 		return (SBBC_KEYSWITCH_KEY);
1024*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_TODDATA)  == 0)
1025*03831d35Sstevel 		return (SBBC_TOD_KEY);
1026*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SOLCONS) == 0)
1027*03831d35Sstevel 		return (SBBC_CONSOLE_KEY);
1028*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SOLMBOX)  == 0)
1029*03831d35Sstevel 		return (SBBC_MAILBOX_KEY);
1030*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SOLSCIR)  == 0)
1031*03831d35Sstevel 		return (SBBC_INTR_SC_KEY);
1032*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SCSOLIR)  == 0)
1033*03831d35Sstevel 		return (SBBC_SC_INTR_KEY);
1034*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_ENVINFO)  == 0)
1035*03831d35Sstevel 		return (SBBC_ENVCTRL_KEY);
1036*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SOLSCIE)  == 0)
1037*03831d35Sstevel 		return (SBBC_INTR_SC_ENABLED_KEY);
1038*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SCSOLIE)  == 0)
1039*03831d35Sstevel 		return (SBBC_SC_INTR_ENABLED_KEY);
1040*03831d35Sstevel 	if (strcmp(toc_key, TOCKEY_SIGBLCK)  == 0)
1041*03831d35Sstevel 		return (SBBC_SIGBLCK_KEY);
1042*03831d35Sstevel 
1043*03831d35Sstevel 	/* Unknown key */
1044*03831d35Sstevel 	return (-1);
1045*03831d35Sstevel }
1046*03831d35Sstevel 
1047*03831d35Sstevel /*
1048*03831d35Sstevel  * Move the software interrupts from the old dip to the new dip
1049*03831d35Sstevel  * when doing tunnel switch.
1050*03831d35Sstevel  */
1051*03831d35Sstevel static int
1052*03831d35Sstevel iosram_switch_intr()
1053*03831d35Sstevel {
1054*03831d35Sstevel 	sbbc_intrs_t	*intr;
1055*03831d35Sstevel 	int		intr_no;
1056*03831d35Sstevel 	int		rc = 0;
1057*03831d35Sstevel 
1058*03831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
1059*03831d35Sstevel 
1060*03831d35Sstevel 	for (intr_no = 0; intr_no < SBBC_MAX_INTRS; intr_no++) {
1061*03831d35Sstevel 		intr = &master_iosram->intrs[intr_no];
1062*03831d35Sstevel 
1063*03831d35Sstevel 		if (intr->sbbc_intr_id) {
1064*03831d35Sstevel 			ddi_remove_softintr(intr->sbbc_intr_id);
1065*03831d35Sstevel 
1066*03831d35Sstevel 			if (ddi_add_softintr(master_iosram->iosram_sbbc->dip,
1067*03831d35Sstevel 				DDI_SOFTINT_HIGH,
1068*03831d35Sstevel 				&intr->sbbc_intr_id, NULL, NULL,
1069*03831d35Sstevel 				intr->sbbc_handler, intr->sbbc_arg)
1070*03831d35Sstevel 				!= DDI_SUCCESS) {
1071*03831d35Sstevel 
1072*03831d35Sstevel 				cmn_err(CE_WARN, "Can't add SBBC softint for "
1073*03831d35Sstevel 					"interrupt %x", intr_no << 1);
1074*03831d35Sstevel 				rc = EAGAIN;
1075*03831d35Sstevel 			}
1076*03831d35Sstevel 		}
1077*03831d35Sstevel 	}
1078*03831d35Sstevel 
1079*03831d35Sstevel 	return (rc);
1080*03831d35Sstevel }
1081