xref: /illumos-gate/usr/src/uts/sun4u/sunfire/io/fhc_bd.c (revision 508a0e8cf1600b06c1f7361ad76e736710d3fdf8)
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 #include <sys/types.h>
30 #include <sys/conf.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/ddi_impldefs.h>
34 #include <sys/obpdefs.h>
35 #include <sys/promif.h>
36 #include <sys/cmn_err.h>
37 #include <sys/errno.h>
38 #include <sys/kmem.h>
39 #include <sys/kstat.h>
40 #include <sys/debug.h>
41 #include <sys/fhc.h>
42 #include <sys/jtag.h>
43 #include <sys/sysctrl.h>
44 
45 static fhc_bd_resizable_t boards; /* booted and hotplugged boards */
46 static fhc_bd_resizable_t clocks; /* clocks under central. */
47 
48 static int fhc_bdmax;
49 /*
50  * !! IMPORTANT !! fhc_bdlist_rwlock is implemented as a single
51  * RW_WRITER lock with *no* RW_READERs -- and it should stay that
52  * way.  The fhc_bdlist_rwlock should never be used with RW_READER.
53  *
54  * The lock was originally a mutex, but was changed to a
55  * single-writer, zero-reader rwlock to force requesting threads
56  * to block (sleep, not spin) when the RW_WRITER lock is already
57  * held by a thread currently running.
58  */
59 static krwlock_t fhc_bdlist_rwlock;
60 static sysc_evt_handle_t fhc_bd_evt;
61 static sysc_evt_handle_t *fbe = &fhc_bd_evt;
62 
63 #define	fhc_bd_sc_evt(s, e)	(*fbe->update)(fbe->soft, s, e)
64 #define	FHC_INCREMENT 4
65 #define	FHC_B_SEARCH(in_array, board) \
66 	fhc_b_search(in_array.boards, board, 0, in_array.last);
67 
68 static int	fhc_bd_disabled(int);
69 static void	fhc_check_array(int);
70 static void	fhc_shell_sort(fhc_bd_t **, int, int);
71 static int	fhc_b_search(fhc_bd_t **, int, int, int);
72 static void	fhc_check_size(fhc_bd_resizable_t *);
73 static void	fhc_resize(fhc_bd_t ***, int, int);
74 
75 
76 /*
77  * fhc_bdmax gets set in fhc_bdlist_prime() and does not
78  * change thereafter.
79  */
80 int
81 fhc_max_boards()
82 {
83 	return (fhc_bdmax + 1);
84 }
85 
86 static int
87 fhc_bd_disabled(int board)
88 {
89 	int index;
90 
91 	ASSERT(boards.sorted);
92 	index = FHC_B_SEARCH(boards, board);
93 	ASSERT(index != -1);
94 	return (boards.boards[index]->flags & BDF_DISABLED);
95 }
96 
97 static void
98 fhc_check_array(int btype)
99 {
100 	if (btype == FHC_BOARDS) {
101 		ASSERT(fhc_bdlist_locked());
102 		if (!boards.sorted) {
103 			fhc_shell_sort(boards.boards, 0, boards.last);
104 			boards.sorted = TRUE;
105 		}
106 	} else {
107 		ASSERT(fhc_bdlist_locked());
108 		if (!clocks.sorted) {
109 			fhc_shell_sort(clocks.boards, 0, clocks.last);
110 			clocks.sorted = TRUE;
111 		}
112 	}
113 }
114 
115 static void
116 fhc_shell_sort(fhc_bd_t *a[], int lb, int ub)
117 {
118 	int n, h, i, j;
119 	fhc_bd_t *t;
120 
121 	/* sort array a[lb..ub] */
122 
123 	/* compute largest increment */
124 	n = ub - lb + 1;
125 	h = 1;
126 	if (n < 14)
127 		h = 1;
128 	else {
129 		while (h < n)
130 			h = 3 * h + 1;
131 		h /= 3;
132 		h /= 3;
133 	}
134 
135 	while (h > 0) {
136 		/* sort-by-insertion in increments of h */
137 		for (i = lb + h; i <= ub; i++) {
138 			t = a[i];
139 			for (j = i - h;
140 			    j >= lb && a[j]->sc.board > t->sc.board;
141 			    j -= h) {
142 				a[j+h] = a[j];
143 			}
144 			a[j+h] = t;
145 		}
146 
147 		/* compute next increment */
148 		h /= 3;
149 	}
150 }
151 
152 static int
153 fhc_b_search(fhc_bd_t *in_array[], int board, int first, int last)
154 {
155 	int mid;
156 
157 	/* Array of length 0 case. */
158 	if (in_array == NULL)
159 		return (-1);
160 
161 	/* Array of length > 0 case. */
162 	while (first < last) {
163 		mid = (first + last) / 2;
164 		if (in_array[mid]->sc.board < board)
165 			first = mid + 1;
166 		else
167 			last = mid;
168 	}
169 
170 	if (in_array[first]->sc.board == board) {
171 		return (first);
172 	} else {
173 		return (-1);
174 	}
175 
176 }
177 
178 static void
179 fhc_check_size(fhc_bd_resizable_t *resizable)
180 {
181 	int oldsize;
182 	int newsize;
183 
184 	ASSERT(fhc_bdlist_locked());
185 
186 	if (resizable->size == resizable->last + 1) {
187 		oldsize = sizeof (fhc_bd_t *) * resizable->size;
188 		resizable->size += FHC_INCREMENT;
189 		newsize = sizeof (fhc_bd_t *) * resizable->size;
190 		fhc_resize(&(resizable->boards), oldsize, newsize);
191 	}
192 }
193 
194 int
195 fhc_bdlist_locked()
196 {
197 	if (panicstr)
198 		return (1);
199 
200 	return (rw_owner(&fhc_bdlist_rwlock) == curthread);
201 }
202 
203 int
204 fhc_bd_busy(int board)
205 {
206 	int index;
207 
208 	ASSERT(boards.sorted);
209 	index = FHC_B_SEARCH(boards, board);
210 	ASSERT(index != -1);
211 	return (boards.boards[index]->sc.in_transition);
212 }
213 
214 int
215 fhc_bd_is_jtag_master(int board)
216 {
217 	int index;
218 
219 	ASSERT(boards.sorted);
220 	index = FHC_B_SEARCH(boards, board);
221 	ASSERT(index != -1);
222 	if (boards.boards[index]->softsp == NULL)
223 		return (FALSE);
224 	else
225 		return ((boards.boards[index]->softsp)->jt_master.is_master);
226 }
227 
228 int
229 fhc_bd_is_plus(int board)
230 {
231 	int index;
232 
233 	ASSERT(boards.sorted);
234 	index = FHC_B_SEARCH(boards, board);
235 	ASSERT(index != -1);
236 	if (boards.boards[index]->sc.plus_board)
237 		return (boards.boards[index]->sc.plus_board);
238 	else
239 		return (FALSE);
240 }
241 
242 void
243 fhc_bdlist_init()
244 {
245 	ASSERT(!fhc_bdmax);
246 	rw_init(&fhc_bdlist_rwlock, NULL, RW_DEFAULT, NULL);
247 	boards.boards = NULL;
248 	boards.size = 0;
249 	boards.last = -1;
250 	boards.sorted = TRUE; /* Array of 0 elements is sorted. */
251 
252 	clocks.boards = NULL;
253 	clocks.size = 0;
254 	clocks.last = -1;
255 	clocks.sorted = TRUE; /* Array of 0 elements is sorted. */
256 }
257 
258 void
259 fhc_bdlist_fini()
260 {
261 	rw_destroy(&fhc_bdlist_rwlock);
262 }
263 
264 fhc_bd_t *
265 fhc_bdlist_lock(int board)
266 {
267 	int index;
268 
269 	ASSERT(!fhc_bdlist_locked());
270 
271 	/* RW_WRITER *ONLY*.  Never use RW_READER! */
272 	rw_enter(&fhc_bdlist_rwlock, RW_WRITER);
273 
274 	if (board == -1)
275 		return (NULL);
276 	else {
277 		ASSERT(boards.sorted);
278 		index = FHC_B_SEARCH(boards, board);
279 		ASSERT(index != -1);
280 		return (boards.boards[index]);
281 	}
282 }
283 
284 void
285 fhc_bdlist_unlock()
286 {
287 	ASSERT(fhc_bdlist_locked());
288 
289 	rw_exit(&fhc_bdlist_rwlock);
290 }
291 
292 static void
293 fhc_resize(fhc_bd_t ***in_array, int oldsize, int newsize)
294 {
295 	fhc_bd_t **temp;
296 
297 	/* This function only grows arrays. */
298 	ASSERT(newsize > oldsize);
299 
300 	/* Allocate new array. */
301 	temp = kmem_alloc(newsize, KM_SLEEP);
302 
303 	/* Bcopy old array and free it. */
304 	if (*in_array != NULL) {
305 		ASSERT(oldsize > 0);
306 		bcopy(*in_array, temp, oldsize);
307 		kmem_free(*in_array, oldsize);
308 	}
309 	*in_array = temp;
310 }
311 
312 void
313 fhc_bd_init(struct fhc_soft_state *softsp, int board, enum board_type type)
314 {
315 	fhc_bd_t *bdp;
316 	int index;
317 
318 	(void) fhc_bdlist_lock(-1);
319 
320 	/* See if board already exists. */
321 	ASSERT(boards.sorted);
322 	ASSERT(clocks.sorted);
323 	if (softsp->is_central) {
324 		index = FHC_B_SEARCH(clocks, board);
325 	} else {
326 		index = FHC_B_SEARCH(boards, board);
327 	}
328 
329 	/* If index == -1 board does not exist. */
330 	if (index != -1) {
331 		if (softsp->is_central) {
332 			bdp = clocks.boards[index];
333 		} else {
334 			bdp = boards.boards[index];
335 		}
336 	} else {
337 		if (softsp->is_central) {
338 			fhc_check_size(&clocks);
339 			clocks.boards[clocks.last + 1] =
340 			    kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP);
341 			bdp = clocks.boards[clocks.last + 1];
342 			clocks.last++;
343 			clocks.sorted = FALSE;
344 		} else {
345 			fhc_check_size(&boards);
346 			boards.boards[boards.last + 1] =
347 			    kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP);
348 			bdp = boards.boards[boards.last + 1];
349 			boards.last++;
350 			boards.sorted = FALSE;
351 		}
352 	}
353 
354 	softsp->list = bdp;
355 	bdp->flags |= BDF_VALID;
356 	bdp->softsp = softsp;
357 	bdp->sc.type = type;
358 	bdp->sc.board = board;
359 	bdp->sc.plus_board = ISPLUSBRD(*softsp->bsr);
360 
361 	/* Keep arrays sorted. */
362 	fhc_check_array(FHC_BOARDS);
363 	fhc_check_array(FHC_CLOCKS);
364 
365 	fhc_bdlist_unlock();
366 }
367 
368 fhc_bd_t *
369 fhc_bd(int board)
370 {
371 	int index;
372 
373 	if (fhc_bdmax) {
374 		ASSERT(fhc_bdlist_locked());
375 	}
376 	ASSERT(boards.sorted);
377 	index = FHC_B_SEARCH(boards, board);
378 	ASSERT(index != -1);
379 	return (boards.boards[index]);
380 }
381 
382 fhc_bd_t *
383 fhc_bd_clock(void)
384 {
385 	ASSERT(fhc_bdlist_locked());
386 	ASSERT(clocks.size != 0);
387 
388 	return (clocks.boards[0]);
389 }
390 
391 fhc_bd_t *
392 fhc_bd_first()
393 {
394 	ASSERT(fhc_bdlist_locked());
395 	if (boards.boards != NULL)
396 		return (boards.boards[0]);
397 	else
398 		return (NULL);
399 }
400 
401 fhc_bd_t *
402 fhc_bd_next(fhc_bd_t *bdp)
403 {
404 	int index;
405 
406 	ASSERT(boards.sorted);
407 	index = FHC_B_SEARCH(boards, bdp->sc.board);
408 	ASSERT(index != -1);
409 	if (index < boards.last)
410 		return (boards.boards[index + 1]);
411 	else
412 		return (NULL);
413 }
414 
415 int
416 fhc_bd_valid(int bd)
417 {
418 	int index;
419 
420 	ASSERT(bd >= 0);
421 	/* Untill fhc_bdlist_prime runs anything is valid. */
422 	if (!fhc_bdmax)
423 		return (TRUE);
424 
425 	ASSERT(boards.sorted);
426 	index = FHC_B_SEARCH(boards, bd);
427 	if (index == -1)
428 		return (FALSE);
429 	else
430 		return (TRUE);
431 }
432 
433 enum board_type
434 fhc_bd_type(int board)
435 {
436 	int index;
437 
438 	ASSERT(boards.sorted);
439 	index = FHC_B_SEARCH(boards, board);
440 	if (index == -1)
441 		return (-1);
442 
443 	return (boards.boards[index]->sc.type);
444 }
445 
446 char *
447 fhc_bd_typestr(enum board_type type)
448 {
449 	char *type_str;
450 
451 	switch (type) {
452 	case MEM_BOARD:
453 		type_str = MEM_BD_NAME;
454 		break;
455 
456 	case CPU_BOARD:
457 		type_str = CPU_BD_NAME;
458 		break;
459 
460 	case IO_2SBUS_BOARD:
461 		type_str = IO_2SBUS_BD_NAME;
462 		break;
463 
464 	case IO_SBUS_FFB_BOARD:
465 		type_str = IO_SBUS_FFB_BD_NAME;
466 		break;
467 
468 	case IO_2SBUS_SOCPLUS_BOARD:
469 		type_str = IO_2SBUS_SOCPLUS_BD_NAME;
470 		break;
471 
472 	case IO_SBUS_FFB_SOCPLUS_BOARD:
473 		type_str = IO_SBUS_FFB_SOCPLUS_BD_NAME;
474 		break;
475 
476 	case IO_PCI_BOARD:
477 		type_str = IO_PCI_BD_NAME;
478 		break;
479 
480 	case DISK_BOARD:
481 		type_str = DISK_BD_NAME;
482 		break;
483 
484 	case UNKNOWN_BOARD:
485 	default:
486 		type_str = "unknown";
487 		break;
488 	}
489 
490 	return (type_str);
491 }
492 
493 void
494 fhc_bd_env_set(int board, void *env)
495 {
496 	fhc_bd_t *bdp;
497 
498 	bdp = fhc_bd(board);
499 	bdp->dev_softsp = env;
500 }
501 
502 static void
503 fhc_bd_dlist_init()
504 {
505 	int i;
506 	int len;
507 	int board;
508 	pnode_t node;
509 	char *dlist;
510 	int index;
511 
512 	/*
513 	 * Find the disabled board list property if present.
514 	 *
515 	 * The disabled board list is in the options node under root;
516 	 * it is a null terminated list of boards in a string.
517 	 * Each char represents a board. The driver must
518 	 * reject illegal chars in case a user places them in the
519 	 * property.
520 	 */
521 	if (((node = prom_finddevice("/options")) == OBP_BADNODE) ||
522 	    ((len = prom_getproplen(node, "disabled-board-list")) == -1))
523 		return;
524 
525 	dlist = kmem_alloc(len, KM_SLEEP);
526 	(void) prom_getprop(node, "disabled-board-list", dlist);
527 
528 	/*
529 	 * now loop thru the string, and create disabled board list
530 	 * entries for all legal boards in the list.
531 	 */
532 	for (i = 0; (i < len) && (dlist[i] != 0); i++) {
533 		char ch = dlist[i];
534 
535 		if (ch >= '0' && ch <= '9')
536 			board = ch - '0';
537 		else if (ch >= 'A' && ch <= 'F')
538 			board = ch - 'A' + 10;
539 		else if (ch >= 'a' && ch <= 'f')
540 			board = ch - 'a' + 10;
541 		else
542 			/* junk entry */
543 			continue;
544 
545 		index = FHC_B_SEARCH(boards, board);
546 		if (index != -1) {
547 			boards.boards[index]->flags |= BDF_DISABLED;
548 		}
549 	}
550 	kmem_free(dlist, len);
551 }
552 
553 static struct bd_info fhc_bd_info;
554 
555 static int
556 fhc_bd_ks_update(kstat_t *ksp, int rw)
557 {
558 	fhc_bd_t *bdp;
559 	sysc_cfga_stat_t *sc;
560 	struct bd_info *uip;
561 	enum board_state state;
562 
563 	if (rw == KSTAT_WRITE)
564 		return (EACCES);
565 
566 	bdp = (fhc_bd_t *)ksp->ks_private;
567 	uip = &fhc_bd_info;
568 	sc = &bdp->sc;
569 
570 	ASSERT(fhc_bd_valid(sc->board));
571 
572 	uip->board = sc->board;
573 	uip->type = sc->type;
574 	uip->fhc_compid = sc->fhc_compid;
575 	uip->ac_compid = sc->ac_compid;
576 	bcopy((caddr_t)sc->prom_rev, uip->prom_rev, sizeof (uip->prom_rev));
577 	bcopy((caddr_t)&sc->bd, &uip->bd, sizeof (union bd_un));
578 
579 	switch (sc->rstate) {
580 	case SYSC_CFGA_RSTATE_DISCONNECTED:
581 		switch (sc->condition) {
582 		case SYSC_CFGA_COND_OK:
583 		case SYSC_CFGA_COND_UNKNOWN:
584 			state = DISABLED_STATE;
585 			break;
586 		case SYSC_CFGA_COND_FAILING:
587 		case SYSC_CFGA_COND_FAILED:
588 		case SYSC_CFGA_COND_UNUSABLE:
589 			state = FAILED_STATE;
590 			break;
591 		default:
592 			state = UNKNOWN_STATE;
593 			break;
594 		}
595 		break;
596 	default:
597 		state = UNKNOWN_STATE;
598 		break;
599 	}
600 
601 	uip->state = state;
602 
603 	return (0);
604 }
605 
606 void
607 fhc_bd_ks_alloc(fhc_bd_t *bdp)
608 {
609 	ASSERT(!bdp->ksp);
610 
611 	bdp->ksp = kstat_create("unix", bdp->sc.board,
612 		BDLIST_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
613 		sizeof (struct bd_info), KSTAT_FLAG_VIRTUAL);
614 
615 	if (bdp->ksp != NULL) {
616 		bdp->ksp->ks_data = &fhc_bd_info;
617 		bdp->ksp->ks_update = fhc_bd_ks_update;
618 		bdp->ksp->ks_private = (void *)bdp;
619 		kstat_install(bdp->ksp);
620 	}
621 }
622 
623 static void
624 fhc_bdlist_dk_init()
625 {
626 	dev_info_t *dnode;
627 
628 	/*
629 	 * Search the children of root to see if there are any
630 	 * disk boards in the tree.
631 	 */
632 	for (dnode = ddi_get_child(ddi_root_node());
633 	    dnode != NULL; dnode = ddi_get_next_sibling(dnode)) {
634 		if (strcmp(ddi_node_name(dnode), "disk-board") == 0) {
635 			int id;
636 			int board;
637 			fhc_bd_t *bdp;
638 			sysc_cfga_stat_t *sc;
639 
640 			/*
641 			 * Get the board number property.
642 			 */
643 			if ((board = (int)ddi_getprop(DDI_DEV_T_ANY, dnode,
644 				DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
645 				cmn_err(CE_WARN,
646 					"Could not find board number");
647 				continue;
648 			}
649 			bdp = fhc_bd(board);
650 			sc = &bdp->sc;
651 
652 			if ((id = (int)ddi_getprop(DDI_DEV_T_ANY, dnode,
653 			    DDI_PROP_DONTPASS, "disk0-scsi-id", -1)) != -1) {
654 				sc->bd.dsk.disk_pres[0] = 1;
655 				sc->bd.dsk.disk_id[0] = id;
656 			} else {
657 				sc->bd.dsk.disk_pres[0] = 0;
658 			}
659 
660 			if ((id = (int)ddi_getprop(DDI_DEV_T_ANY, dnode,
661 			    DDI_PROP_DONTPASS, "disk1-scsi-id", -1)) != -1) {
662 				sc->bd.dsk.disk_pres[1] = 1;
663 				sc->bd.dsk.disk_id[1] = id;
664 			} else {
665 				sc->bd.dsk.disk_pres[1] = 0;
666 			}
667 
668 		}
669 	}
670 
671 }
672 
673 struct jt_mstr *
674 jtag_master_lock(void)
675 {
676 	fhc_bd_t *bdp;
677 	struct jt_mstr *master = NULL;
678 
679 	ASSERT(fhc_bdlist_locked());
680 
681 	/*
682 	 * Now search for the JTAG master and place the addresses for
683 	 * command into the fhc soft state structure.
684 	 * Disk board do not have softsp set.
685 	 */
686 	for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp))
687 		if (bdp->softsp && (bdp->softsp->jt_master.is_master == 1)) {
688 			master = &bdp->softsp->jt_master;
689 			mutex_enter(&master->lock);
690 			break;
691 		}
692 
693 	return (master);
694 }
695 
696 void
697 jtag_master_unlock(struct jt_mstr *mstr)
698 {
699 	ASSERT(fhc_bdlist_locked());
700 	ASSERT(mutex_owned(&mstr->lock));
701 
702 	mutex_exit(&mstr->lock);
703 }
704 
705 void
706 fhc_bdlist_prime(int first, int count, int incr)
707 {
708 	int board;
709 	fhc_bd_t *bdp;
710 	sysc_evt_t se;
711 	sysc_cfga_stat_t *sc;
712 	struct jt_mstr *jtm;
713 	int index;
714 	int nadded;
715 
716 	ASSERT(fbe->update);
717 
718 	(void) fhc_bdlist_lock(-1);
719 	nadded = 0;
720 	for (board = first; board < count; board += incr) {
721 		/*
722 		 * Search only subset of array. We hold mutex so
723 		 * noone can add new elements to it.
724 		 */
725 		index = fhc_b_search(boards.boards, board, 0,
726 		    boards.last - nadded);
727 		if (index == -1) {
728 			fhc_check_size(&boards);
729 			boards.boards[boards.last + 1] =
730 			    kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP);
731 			boards.boards[boards.last + 1]->sc.type = UNKNOWN_BOARD;
732 			boards.boards[boards.last + 1]->sc.board = board;
733 			boards.boards[boards.last + 1]->softsp = NULL;
734 			boards.last++;
735 			nadded++;
736 			boards.sorted = FALSE;
737 		}
738 	}
739 	fhc_check_array(FHC_BOARDS);
740 	fhc_bdlist_unlock();
741 
742 	fhc_bdmax = count - 1;
743 
744 	/*
745 	 * Initialize our copy of the disabled board list.
746 	 */
747 	fhc_bd_dlist_init();
748 
749 	(void) fhc_bdlist_lock(-1);
750 
751 	if ((jtm = jtag_master_lock()) == NULL)
752 		cmn_err(CE_PANIC, "fhc_bdlist_prime: no jtag master");
753 
754 	/*
755 	 * Go through the board list, skipping illegal slots
756 	 * and initialize each slot.
757 	 */
758 	for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) {
759 		sc = &bdp->sc;
760 		board = sc->board;
761 
762 		se = SYSC_EVT_BD_PRESENT;
763 
764 		if (sc->type == UNKNOWN_BOARD) {
765 			uint_t fhc_csr;
766 			uint_t fhc_bsr;
767 			enum board_type type;
768 
769 			type = jtag_get_board_type(jtm->jtag_cmd, sc);
770 			switch (type) {
771 			case -1:
772 				fhc_bd_sc_evt(sc, SYSC_EVT_BD_EMPTY);
773 				continue;
774 			case DISK_BOARD:
775 				/*
776 				 * Disk boards are handled differently
777 				 * in that they don't fail POST and have
778 				 * no fhc attached.
779 				 */
780 				sc->type = DISK_BOARD;
781 				(void) jtag_init_disk_board(jtm->jtag_cmd,
782 				    board,
783 				    &fhc_csr, &fhc_bsr);
784 				fhc_bd_ks_alloc(bdp);
785 				break;
786 			default:
787 				/*
788 				 * Set the condition to FAILED if POST has
789 				 * failed. A failed board is physically
790 				 * present, is not on the disabled list and
791 				 * is of type UNKNOWN.
792 				 * NOTE: a non-present board which is
793 				 * (potentially) on the disabled board
794 				 * list has been ignored in the empty
795 				 * slot case.
796 				 */
797 				if (fhc_bd_disabled(board)) {
798 					fhc_bd_ks_alloc(bdp);
799 					se = SYSC_EVT_BD_DISABLED;
800 				} else
801 					se = SYSC_EVT_BD_FAILED;
802 
803 				sc->type = type;
804 				break;
805 			}
806 		}
807 
808 		fhc_bd_sc_evt(sc, se);
809 	}
810 
811 	/*
812 	 * Do the disk specific initialization.  This routine scans
813 	 * for all disk boards, so we call it only once.
814 	 */
815 	fhc_bdlist_dk_init();
816 
817 	jtag_master_unlock(jtm);
818 
819 	fhc_bdlist_unlock();
820 }
821 
822 struct cpu_speed {
823 	int cpu_freq;
824 	int sram_mode;
825 	int system_div;
826 	int system_dvd;
827 };
828 
829 struct cpu_speed ultraI_speed_table[] = {
830 	{ 0,	0,	0,	0},
831 	{ 143,	1,	2,	1},
832 	{ 154,	1,	2,	1},
833 	{ 168,	1,	2,	1},
834 	{ 182,	1,	3,	1},
835 	{ 200,	1,	3,	1},
836 	{ 222,	1,	3,	1},
837 	{ 250,	1,	3,	1}
838 };
839 
840 struct cpu_speed ultraII_speed_table[] = {
841 	{ 0,	0,	0,	0},
842 	{ 360,	2,	2,	1},
843 	{ 400,	2,	4,	1},
844 	{ 400,	2,	5,	2},
845 	{ 248,	2,	3,	2},
846 	{ 496,	2,	5,	2},
847 	{ 296,	2,	2,	1},
848 	{ 336,	2,	2,	1}
849 };
850 
851 /*
852  * set_cpu_info
853  *
854  * This routine extracts CPU module information used later for
855  * determining hotplug compatibility.
856  */
857 static void
858 set_cpu_info(sysc_cfga_stat_t *sc, uint_t fhc_bsr)
859 {
860 	int i;
861 	int speed_pins;
862 	struct cpu_speed *table;
863 
864 	for (i = 0; i < 2; i++) {
865 		sc->bd.cpu[i].cpu_speed = 0;
866 		sc->bd.cpu[i].cpu_sram_mode = 0;
867 
868 		if (!sc->bd.cpu[i].cpu_detected)
869 			continue;
870 
871 		speed_pins = (i == 0) ? CPU_0_PINS(fhc_bsr) :
872 				CPU_1_PINS(fhc_bsr);
873 
874 		switch (sc->bd.cpu[i].cpu_compid & CID_REV_MASK) {
875 			case ULTRAI_COMPID:
876 				table = ultraI_speed_table;
877 				break;
878 			case ULTRAII_COMPID:
879 				table = ultraII_speed_table;
880 				break;
881 			default:
882 				cmn_err(CE_WARN, "board %d, cpu module %c "
883 					"unknown type", sc->board,
884 					(i == 0) ? 'A' : 'B');
885 				sc->bd.cpu[i].cpu_speed = -1;
886 				continue;
887 		}
888 
889 		sc->bd.cpu[i].cpu_speed = table[speed_pins].cpu_freq;
890 		sc->bd.cpu[i].cpu_sram_mode = table[speed_pins].sram_mode;
891 	}
892 }
893 
894 int
895 fhc_bdlist_scan(sysc_cfga_rstate_t rstate, struct jt_mstr *jtm)
896 {
897 	int board;
898 	int error;
899 	int found = 0;
900 	uint_t fhc_csr;
901 	uint_t fhc_bsr;
902 	fhc_bd_t *bdp;
903 	sysc_cfga_stat_t *sc;
904 	enum board_type type;
905 
906 	for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) {
907 
908 		sc = &bdp->sc;
909 		board = sc->board;
910 
911 		/*
912 		 * Check the boards in EMPTY and DISCONNECTED
913 		 * states.  We need to check a board in the
914 		 * DISCONNECTED state in case it had been replugged.
915 		 */
916 		if (sc->in_transition || sc->rstate != rstate)
917 			continue;
918 		else if (sc->rstate == SYSC_CFGA_RSTATE_EMPTY) {
919 			type = jtag_get_board_type(jtm->jtag_cmd, sc);
920 			if (type == -1)
921 				continue;	/* no board present */
922 			sc->type = type;
923 		} else
924 			type = sc->type;
925 
926 		if (type != UNKNOWN_BOARD)
927 			(void) jtag_get_board_info(jtm->jtag_cmd, sc);
928 
929 		error = 0;
930 
931 		if (type == DISK_BOARD)
932 			/*
933 			 * Scan the FHC to turn off the board insert
934 			 * interrupt and modify LEDs based on hotplug
935 			 * status.
936 			 */
937 			(void) jtag_init_disk_board(jtm->jtag_cmd, board,
938 					&fhc_csr, &fhc_bsr);
939 		else
940 			error = jtag_powerdown_board(jtm->jtag_cmd,
941 					board, type, &fhc_csr, &fhc_bsr, FALSE);
942 
943 		if (error) {
944 			fhc_bd_sc_evt(sc, SYSC_EVT_BD_INS_FAILED);
945 			continue;
946 		}
947 
948 		if (fhc_csr & FHC_NOT_BRD_PRES)
949 			continue;
950 
951 		if (type == CPU_BOARD) {
952 			set_cpu_info(sc, fhc_bsr);
953 		}
954 
955 		fhc_bd_sc_evt(sc, SYSC_EVT_BD_INSERTED);
956 
957 		/*
958 		 * A replugged board will still have its kstat info.
959 		 */
960 		if (!bdp->ksp)
961 			fhc_bd_ks_alloc(bdp);
962 
963 		found++;
964 		break;
965 	}
966 
967 	return (found);
968 }
969 
970 int
971 fhc_bd_insert_scan()
972 {
973 	struct jt_mstr *jtm;
974 	int found;
975 
976 	ASSERT(fhc_bdlist_locked());
977 
978 	if ((jtm = jtag_master_lock()) == NULL)
979 		cmn_err(CE_PANIC, "fhc_bd_insert_scan: no jtag master");
980 
981 	/* first check empty then disconnected */
982 	found = fhc_bdlist_scan(SYSC_CFGA_RSTATE_EMPTY, jtm);
983 	if (!found)
984 		found |= fhc_bdlist_scan(SYSC_CFGA_RSTATE_DISCONNECTED, jtm);
985 	if (!found)
986 		cmn_err(CE_WARN, "Could not find hotplugged core system board");
987 
988 	jtag_master_unlock(jtm);
989 
990 	return (found);
991 }
992 
993 int
994 fhc_bd_remove_scan()
995 {
996 	int poll = 0;
997 	fhc_bd_t *bdp;
998 	struct jt_mstr *jtm;
999 	sysc_cfga_stat_t *sc;
1000 
1001 	ASSERT(fhc_bdlist_locked());
1002 
1003 	if ((jtm = jtag_master_lock()) == NULL)
1004 		cmn_err(CE_PANIC, "fhc_bd_remove_scan: no jtag master");
1005 
1006 	for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) {
1007 		sc = &bdp->sc;
1008 
1009 		if (sc->rstate != SYSC_CFGA_RSTATE_DISCONNECTED)
1010 			continue;
1011 		/*
1012 		 * While there is a board in the disconnected state
1013 		 * continue polling. When the last board is removed,
1014 		 * we will get one last scan.
1015 		 */
1016 		poll++;
1017 
1018 		if (sc->in_transition)
1019 			continue;
1020 
1021 		/*
1022 		 * Scan to see if the board is still in.
1023 		 */
1024 		if (jtag_get_board_type(jtm->jtag_cmd, sc) == -1) {
1025 			if (bdp->ksp) {
1026 				kstat_delete(bdp->ksp);
1027 				bdp->ksp = NULL;
1028 			}
1029 			fhc_bd_sc_evt(sc, SYSC_EVT_BD_REMOVED);
1030 		}
1031 	}
1032 
1033 	jtag_master_unlock(jtm);
1034 
1035 	return (poll);
1036 }
1037 
1038 int
1039 fhc_bd_detachable(int board)
1040 {
1041 	fhc_bd_t *bdp = fhc_bd(board);
1042 
1043 	if (bdp->softsp != NULL)
1044 		return (bdp->flags & BDF_DETACH);
1045 	else
1046 		return (FALSE);
1047 }
1048 
1049 void
1050 fhc_bd_sc_register(void (*f)(void *, sysc_cfga_stat_t *, sysc_evt_t), void *sp)
1051 {
1052 	fhc_bd_evt.update = f;
1053 	fhc_bd_evt.soft = sp;
1054 }
1055 
1056 void
1057 fhc_bd_update(int board, sysc_evt_t evt)
1058 {
1059 	fhc_bd_t *bdp;
1060 
1061 	ASSERT(fhc_bd_valid(board));
1062 
1063 	/*
1064 	 * There is a window where this routine might be called
1065 	 * as a result of the environ thread before sysctrl has
1066 	 * attached and registered the callback.
1067 	 */
1068 	if (!(fbe->update))
1069 		return;
1070 
1071 	bdp = fhc_bdlist_lock(board);
1072 
1073 	fhc_bd_sc_evt(&bdp->sc, evt);
1074 
1075 	fhc_bdlist_unlock();
1076 }
1077 
1078 /* ARGSUSED */
1079 int
1080 fhc_bd_test(int board, sysc_cfga_pkt_t *pkt)
1081 {
1082 	uint_t fhc_csr, fhc_bsr;
1083 	fhc_bd_t *bdp;
1084 	struct jt_mstr *jtm;
1085 	sysc_cfga_stat_t *sc;
1086 
1087 	ASSERT(fhc_bdlist_locked());
1088 	ASSERT(fhc_bd_busy(board));
1089 
1090 	bdp = fhc_bd(board);
1091 	sc = &bdp->sc;
1092 
1093 	switch (sc->rstate) {
1094 	case SYSC_CFGA_RSTATE_EMPTY:
1095 		cmn_err(CE_NOTE, "fhc_bd_test: simulate board %d insertion",
1096 		    board);
1097 
1098 		jtm = jtag_master_lock();
1099 		ASSERT(jtm);
1100 		jtag_master_unlock(jtm);
1101 
1102 		(void) jtag_powerdown_board(jtm->jtag_cmd, board,
1103 			sc->type, &fhc_csr, &fhc_bsr, TRUE);
1104 		break;
1105 	case SYSC_CFGA_RSTATE_DISCONNECTED:
1106 		cmn_err(CE_NOTE, "fhc_bd_test: simulate board %d removal",
1107 		    board);
1108 
1109 		if (bdp->ksp) {
1110 			kstat_delete(bdp->ksp);
1111 			bdp->ksp = NULL;
1112 		}
1113 		fhc_bd_sc_evt(sc, SYSC_EVT_BD_REMOVED);
1114 		break;
1115 	default:
1116 		cmn_err(CE_NOTE,
1117 			"fhc_bd_test: invalid board state: %d", board);
1118 		break;
1119 	}
1120 
1121 	return (0);
1122 }
1123 
1124 /*
1125  * force a board condition for test purpose
1126  */
1127 /* ARGSUSED */
1128 int
1129 fhc_bd_test_set_cond(int board, sysc_cfga_pkt_t *sysc_pkt)
1130 {
1131 	fhc_bd_t *bdp;
1132 	sysc_cfga_stat_t *sc;
1133 	sysc_cfga_cond_t cond;
1134 
1135 	ASSERT(fhc_bdlist_locked());
1136 	ASSERT(fhc_bd_busy(board));
1137 
1138 	bdp = fhc_bd(board);
1139 	sc = &bdp->sc;
1140 
1141 	cond = (sysc_cfga_cond_t)sysc_pkt->cmd_cfga.arg;
1142 
1143 	switch (cond) {
1144 	case SYSC_CFGA_COND_UNKNOWN:
1145 	case SYSC_CFGA_COND_OK:
1146 	case SYSC_CFGA_COND_FAILING:
1147 	case SYSC_CFGA_COND_FAILED:
1148 	case SYSC_CFGA_COND_UNUSABLE:
1149 		sc->condition = cond;
1150 		return (0);
1151 	default:
1152 		return (EINVAL);
1153 	}
1154 }
1155