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