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