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