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
45static fhc_bd_resizable_t boards; /* booted and hotplugged boards */
46static fhc_bd_resizable_t clocks; /* clocks under central. */
47
48static 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 */
59static krwlock_t fhc_bdlist_rwlock;
60static sysc_evt_handle_t fhc_bd_evt;
61static 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
68static int	fhc_bd_disabled(int);
69static void	fhc_check_array(int);
70static void	fhc_shell_sort(fhc_bd_t **, int, int);
71static int	fhc_b_search(fhc_bd_t **, int, int, int);
72static void	fhc_check_size(fhc_bd_resizable_t *);
73static 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 */
80int
81fhc_max_boards()
82{
83	return (fhc_bdmax + 1);
84}
85
86static int
87fhc_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
97static void
98fhc_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
115static void
116fhc_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
152static int
153fhc_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
178static void
179fhc_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
194int
195fhc_bdlist_locked()
196{
197	if (panicstr)
198		return (1);
199
200	return (rw_owner(&fhc_bdlist_rwlock) == curthread);
201}
202
203int
204fhc_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
214int
215fhc_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
228int
229fhc_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
242void
243fhc_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
258void
259fhc_bdlist_fini()
260{
261	rw_destroy(&fhc_bdlist_rwlock);
262}
263
264fhc_bd_t *
265fhc_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
284void
285fhc_bdlist_unlock()
286{
287	ASSERT(fhc_bdlist_locked());
288
289	rw_exit(&fhc_bdlist_rwlock);
290}
291
292static void
293fhc_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
312void
313fhc_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
368fhc_bd_t *
369fhc_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
382fhc_bd_t *
383fhc_bd_clock(void)
384{
385	ASSERT(fhc_bdlist_locked());
386	ASSERT(clocks.size != 0);
387
388	return (clocks.boards[0]);
389}
390
391fhc_bd_t *
392fhc_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
401fhc_bd_t *
402fhc_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
415int
416fhc_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
433enum board_type
434fhc_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
446char *
447fhc_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
493void
494fhc_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
502static void
503fhc_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
553static struct bd_info fhc_bd_info;
554
555static int
556fhc_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
606void
607fhc_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
623static void
624fhc_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
673struct jt_mstr *
674jtag_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
696void
697jtag_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
705void
706fhc_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
822struct cpu_speed {
823	int cpu_freq;
824	int sram_mode;
825	int system_div;
826	int system_dvd;
827};
828
829struct 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
840struct 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 */
857static void
858set_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
894int
895fhc_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
970int
971fhc_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
993int
994fhc_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
1038int
1039fhc_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
1049void
1050fhc_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
1056void
1057fhc_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 */
1079int
1080fhc_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 */
1128int
1129fhc_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