xref: /illumos-gate/usr/src/uts/sun4u/serengeti/io/sbdp.c (revision 1a220b56b93ff1dc80855691548503117af4cc10)
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/cmn_err.h>
31 #include <sys/conf.h>
32 #include <sys/ddi_impldefs.h>
33 #include <sys/autoconf.h>
34 #include <sys/systm.h>
35 #include <sys/modctl.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/sunndi.h>
39 #include <sys/ndi_impldefs.h>
40 #include <sys/promif.h>
41 #include <sys/stat.h>
42 #include <sys/kmem.h>
43 #include <sys/promif.h>
44 #include <sys/conf.h>
45 #include <sys/obpdefs.h>
46 #include <sys/cpuvar.h>
47 #include <vm/seg_kmem.h>
48 #include <sys/prom_plat.h>
49 #include <sys/machsystm.h>
50 #include <sys/note.h>
51 #include <sys/memlist.h>
52 #include <sys/ssm.h>
53 
54 #include <sys/sbd_ioctl.h>
55 #include <sys/sbd.h>
56 #include <sys/sbdp_priv.h>
57 #include <sys/sbdp_mem.h>
58 #include <sys/sbdp_error.h>
59 #include <sys/serengeti.h>
60 
61 #include <sys/sgsbbc.h>		/* To get fn_t type definition */
62 
63 /*
64  * Config information
65  */
66 #ifdef DEBUG
67 uint_t sbdp_debug = 0x0;
68 #endif /* DEBUG */
69 
70 /*
71  * Enable or disable dr
72  */
73 int sbdp_dr_available = 1;
74 
75 /* name properties for some Serengeti device nodes */
76 #define	CMP_DEVNAME		"cmp"
77 #define	MEM_DEVNAME		"memory"
78 #define	CPU_DEVNAME		"cpu"
79 #define	IO_PCI_DEVNAME		"pci"
80 #define	IO_SGHSC_DEVNAME	"sghsc"
81 #define	IO_WCI_DEVNAME		"wci"
82 
83 static	sbd_devattr_t	sbdp_devattr[] = {
84 	{ CMP_DEVNAME,		"cmp",			SBD_COMP_CMP },
85 	{ MEM_DEVNAME,		"memory-controller",	SBD_COMP_MEM },
86 	{ CPU_DEVNAME,		"cpu",			SBD_COMP_CPU },
87 	{ IO_PCI_DEVNAME,	"pci",			SBD_COMP_IO },
88 	{ IO_SGHSC_DEVNAME,	"sghsc",		SBD_COMP_IO },
89 	{ IO_WCI_DEVNAME,	"wci",			SBD_COMP_IO },
90 	/* last item must be blank */
91 	{ NULL,			NULL,			SBD_COMP_UNKNOWN }
92 };
93 
94 /*
95  * In the case of a busy mbox, if a status cmd comes in we return a cached
96  * copy.  This cache is a link list of wnodes that contains bd structs with
97  * the appropriate info.  When a new wnode is created a whole entry is added
98  * to the list.
99  */
100 sbdp_wnode_t	*first_node = NULL; /* first wnode. Entry to the link list */
101 int		cur_num_wnodes = 0; /* how many nodes are currently running */
102 
103 /* Macros to access fields in the previous array */
104 #define	SBDP_CT(i)		sbdp_devattr[i].s_dnodetype
105 #define	SBDP_DEVNAME(i)		sbdp_devattr[(i)].s_devname
106 #define	SBDP_OTYPE(i)		sbdp_devattr[(i)].s_obp_type
107 
108 /*
109  * Prototypes
110  */
111 sbdp_wnode_t *sbdp_get_wnodep(int);
112 
113 /*
114  * Module linkage information for the kernel.
115  */
116 
117 static struct modlmisc modlmisc = {
118 	&mod_miscops,
119 	"Serengeti sbdp",
120 };
121 
122 static struct modlinkage modlinkage = {
123 	MODREV_1,
124 	(void *)&modlmisc,
125 	NULL
126 };
127 
128 /*
129  * VA area used during CPU shutdown.
130  */
131 caddr_t	sbdp_shutdown_va;
132 
133 /*
134  * Mutex to protect our inventory
135  */
136 kmutex_t	sbdp_wnode_mutex;
137 
138 int
139 _init(void)
140 {
141 	int e;
142 
143 	e = mod_install(&modlinkage);
144 	if (e != 0)
145 		return (e);
146 
147 	sbdp_shutdown_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
148 	ASSERT(sbdp_shutdown_va != NULL);
149 	sbdp_valp = (uint64_t *)vmem_alloc(static_alloc_arena,
150 	    sizeof (uint64_t), VM_SLEEP);
151 
152 	mutex_init(&sbdp_wnode_mutex, NULL, MUTEX_DRIVER, NULL);
153 	return (e);
154 }
155 
156 int
157 _fini(void)
158 {
159 	int e;
160 
161 	/*
162 	 * Remove the module.
163 	 */
164 	e = mod_remove(&modlinkage);
165 	if (e != 0)
166 		return (e);
167 
168 	vmem_free(heap_arena, sbdp_shutdown_va, PAGESIZE);
169 	sbdp_shutdown_va = NULL;
170 	vmem_free(static_alloc_arena, (void *)sbdp_valp, sizeof (uint64_t));
171 	sbdp_valp = NULL;
172 
173 	mutex_destroy(&sbdp_wnode_mutex);
174 	return (e);
175 }
176 
177 int
178 _info(struct modinfo *modinfop)
179 {
180 	return (mod_info(&modlinkage, modinfop));
181 }
182 
183 int
184 sbdp_get_bd_and_wnode_num(pnode_t nodeid, int *bd, int *wnode)
185 {
186 	int portid;
187 	static fn_t	f = "sbdp_get_bd_and_wnode_num";
188 	extern int	get_portid(pnode_t node, pnode_t *cmpp);
189 
190 	SBDP_DBG_FUNC("%s\n", f);
191 
192 	if (sbdp_is_node_bad(nodeid))
193 		return (-1);
194 
195 	if ((portid = get_portid(nodeid, NULL)) == -1)
196 		return (-1);
197 
198 	/*
199 	 * decode the board number
200 	 */
201 	*bd = SG_PORTID_TO_BOARD_NUM(portid);
202 	*wnode = SG_PORTID_TO_NODEID(portid);
203 
204 	return (0);
205 }
206 
207 int
208 sbdp_get_board_num(sbdp_handle_t *hp, dev_info_t *dip)
209 {
210 	_NOTE(ARGUNUSED(hp))
211 
212 	pnode_t		nodeid;
213 	int		bd, wnode;
214 	static fn_t	f = "sbdp_get_board_num";
215 
216 	SBDP_DBG_FUNC("%s\n", f);
217 
218 	if (dip == NULL)
219 		return (-1);
220 
221 	nodeid = ddi_get_nodeid(dip);
222 
223 	/*
224 	 * Portid has encoded the nodeid and the agent id.  The top
225 	 * 4 bits are correspond to the wcnodeid and the lower 5 are the
226 	 * agent id.
227 	 * Each agent id represents a physical location hence we can
228 	 * obtain the board number
229 	 */
230 	if (sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) < 0)
231 		return (-1);
232 
233 	return (bd);
234 }
235 
236 
237 sbd_devattr_t *
238 sbdp_get_devattr(void)
239 {
240 	return (&sbdp_devattr[0]);
241 }
242 
243 int
244 sbdp_portid_to_cpu_unit(int cmp, int core)
245 {
246 	return (SG_PORTID_TO_CPU_UNIT(cmp, core));
247 }
248 
249 int
250 sbdp_get_unit_num(sbdp_handle_t *hp, dev_info_t *dip)
251 {
252 	int		unit = -1;
253 	int		portid;
254 	processorid_t	cpuid;
255 	sbd_comp_type_t	type;
256 	char		dev_type[OBP_MAXPROPNAME];
257 	int		i;
258 	pnode_t		nodeid;
259 	static fn_t	f = "sbdp_get_unit_num";
260 
261 	SBDP_DBG_FUNC("%s\n", f);
262 
263 	if (dip == NULL)
264 		return (-1);
265 
266 	nodeid = ddi_get_nodeid(dip);
267 
268 	if (sbdp_is_node_bad(nodeid))
269 		return (-1);
270 
271 	if (prom_getprop(nodeid, "device_type", (caddr_t)dev_type) < 0) {
272 		SBDP_DBG_MISC("%s: couldn't get device_type\n", f);
273 		return (-1);
274 	}
275 
276 	for (i = 0; SBDP_CT(i) != SBD_COMP_UNKNOWN; i++) {
277 		if (strcmp(dev_type, SBDP_OTYPE(i)) != 0)
278 			continue;
279 		type = SBDP_CT(i);
280 	}
281 
282 	switch (type) {
283 	case SBD_COMP_CPU:
284 		if ((cpuid = sbdp_get_cpuid(hp, dip)) != -1) {
285 			unit = SG_CPUID_TO_CPU_UNIT(cpuid);
286 		}
287 		break;
288 	case SBD_COMP_MEM:
289 		unit = 0;
290 		break;
291 	case SBD_COMP_IO: {
292 		regspace_t	regs[3];
293 		int		len = 0;
294 
295 		/*
296 		 * Check to see if this is a cpci node
297 		 * cpci nodes are assign unit nums of 5 for now
298 		 * So they don't conflict with the pci unit nums
299 		 */
300 
301 		if (strcmp(dev_type, "sghsc") == 0) {
302 			SBDP_DBG_MISC("it is a sghsc\n");
303 			return (4);
304 		}
305 
306 		if (prom_getprop(nodeid, "portid", (caddr_t)&portid) <= 0) {
307 			SBDP_DBG_MISC("%s: couldn't get portid\n", f);
308 			return (-1);
309 		}
310 
311 		len = prom_getproplen(nodeid, "reg");
312 		if (len <= 0) {
313 			SBDP_DBG_MISC("%s: couldn't get length\n", f);
314 			return (-1);
315 		}
316 
317 		if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0) {
318 			SBDP_DBG_MISC("%s: couldn't get registers\n", f);
319 			return (-1);
320 		}
321 
322 		if ((portid % 2) != 0)
323 			if ((regs[0].regspec_addr_lo & 0x700000) ==
324 				0x700000)
325 				unit = 0;
326 			else
327 				unit = 1;
328 		else
329 			if ((regs[0].regspec_addr_lo & 0x700000) ==
330 			    0x700000)
331 				unit = 2;
332 			else
333 				unit = 3;
334 
335 		SBDP_DBG_MISC("unit is %d\n", unit);
336 		break;
337 	}
338 	default:
339 		break;
340 
341 	}
342 
343 	return (unit);
344 }
345 
346 struct sbdp_mem_dip {
347 	sbdp_bd_t	*bdp;
348 	dev_info_t	*dip;
349 };
350 
351 static int
352 sbdp_get_mem_dip(pnode_t node, void *arg, uint_t flags)
353 {
354 	_NOTE(ARGUNUSED(flags))
355 
356 	struct sbdp_mem_dip	*smdp = (struct sbdp_mem_dip *)arg;
357 	mem_op_t	mem = {0};
358 
359 	if (node == OBP_NONODE || node == OBP_BADNODE)
360 		return (DDI_FAILURE);
361 
362 	mem.nodes = smdp->bdp->nodes;
363 	mem.board = smdp->bdp->bd;
364 	mem.nmem  = smdp->bdp->nnum;
365 
366 	(void) sbdp_is_mem(node, &mem);
367 
368 	/*
369 	 * We need to find the dip only for the first nodeid
370 	 */
371 	if (smdp->bdp->nnum == 0 && mem.nmem == 1) {
372 		ASSERT(smdp->dip == NULL);
373 		smdp->dip = e_ddi_nodeid_to_dip(node);
374 	}
375 
376 	smdp->bdp->nnum = mem.nmem;
377 
378 	return (DDI_SUCCESS);
379 }
380 
381 
382 /*
383  * Update the board info.  Required after a copy rename
384  */
385 void
386 sbdp_update_bd_info(sbdp_bd_t *bdp)
387 {
388 	attach_pkt_t		apkt, *apktp = &apkt;
389 	struct sbdp_mem_dip	smd = {0};
390 	static fn_t	f = "sbdp_update_bd_info";
391 
392 	SBDP_DBG_FUNC("%s\n", f);
393 
394 	if (bdp == NULL) {
395 		return;
396 	}
397 	/*
398 	 * Grab the lock
399 	 */
400 	mutex_enter(&bdp->bd_mutex);
401 
402 	/*
403 	 * we get the top nodes here.  This will have a side effect of
404 	 * updating the present bit for cpus
405 	 */
406 	apktp->node = bdp->wnode;
407 	apktp->board = bdp->bd;
408 	apktp->num_of_nodes = 0;
409 	apktp->flags = 0;
410 	sbdp_walk_prom_tree(prom_rootnode(), sbdp_select_top_nodes,
411 	    (void *) apktp);
412 
413 	/*
414 	 * We need to clear nnum since we are looking again for the
415 	 * nodes
416 	 */
417 	bdp->nnum = 0;
418 	smd.bdp = bdp;
419 
420 	/*
421 	 * If a dip is found by sbdp_get_mem_dip(), it will be
422 	 * returned held
423 	 */
424 	sbdp_walk_prom_tree(prom_rootnode(), sbdp_get_mem_dip, &smd);
425 	if (smd.dip != NULL) {
426 		sbdp_handle_t		*hp;
427 
428 		hp = kmem_zalloc(sizeof (sbdp_handle_t), KM_SLEEP);
429 		hp->h_board = bdp->bd;
430 		hp->h_wnode = bdp->wnode;
431 		hp->h_err = kmem_zalloc(sizeof (*hp->h_err), KM_SLEEP);
432 		if (bdp->ml != NULL) {
433 			sbdp_del_memlist(hp, bdp->ml);
434 		}
435 		bdp->ml = sbdp_get_memlist(hp, (dev_info_t *)NULL);
436 		/*
437 		 * if the board doesn't have banks initialize them,
438 		 * otherwise we assume they have been updated if
439 		 * necessary
440 		 */
441 		if (bdp->banks == NULL) {
442 			sbdp_init_bd_banks(bdp);
443 		}
444 #ifdef DEBUG
445 		sbdp_print_bd_banks(bdp);
446 #endif
447 
448 		if (sbdphw_get_base_physaddr(hp, smd.dip, &bdp->bpa))
449 			bdp->bpa = -1;
450 		ddi_release_devi(smd.dip);
451 		kmem_free(hp->h_err, sizeof (*hp->h_err));
452 		kmem_free(hp, sizeof (sbdp_handle_t));
453 	}
454 	mutex_exit(&bdp->bd_mutex);
455 }
456 
457 /*
458  * Initialize the board struct.  This remains cached.  We update it
459  * every time we have a successful show_board and after a copy-rename
460  */
461 void
462 sbdp_bd_init(sbdp_bd_t *bdp, int bd, int wnode)
463 {
464 	static fn_t	f = "sbdp_bd_init";
465 
466 	SBDP_DBG_FUNC("%s\n", f);
467 
468 	bdp->bd = bd;
469 	bdp->wnode = wnode;
470 
471 	SBDP_UNSET_ALL_CPUS_IN_RESET(bdp);
472 
473 	bdp->cpus_present = 0;
474 
475 	sbdp_update_bd_info(bdp);
476 
477 	mutex_init(&bdp->bd_mutex, NULL, MUTEX_DRIVER, NULL);
478 	bdp->bd_sc = (show_board_t *)kmem_zalloc(sizeof (show_board_t),
479 	    KM_SLEEP);
480 	bdp->valid_cp = -1;
481 }
482 
483 /*
484  * This entry is going away.  Clean up
485  */
486 void
487 sbdp_bd_fini(sbdp_bd_t *bdp)
488 {
489 	static fn_t	f = "sbdp_bd_fini";
490 
491 	SBDP_DBG_FUNC("%s\n", f);
492 
493 	sbdp_cleanup_bd(bdp->wnode, bdp->bd);
494 	kmem_free(bdp->bd_sc, sizeof (show_board_t));
495 	bdp->bd_sc = NULL;
496 	mutex_destroy(&bdp->bd_mutex);
497 #ifdef DEBUG
498 	sbdp_print_all_segs();
499 #endif
500 }
501 
502 /*
503  * A new wnode has arrived.  Initialize the struct and create
504  * the board structures.
505  */
506 void
507 sbdp_wnode_init(sbdp_wnode_t *wnodep, int wnode, int boards)
508 {
509 	int		i;
510 	static fn_t	f = "sbdp_wnode_init";
511 
512 	SBDP_DBG_FUNC("%s\n", f);
513 
514 	wnodep->wnode = wnode;
515 	wnodep->nbds = boards;
516 	wnodep->bds = kmem_zalloc(sizeof (sbdp_bd_t) * boards, KM_SLEEP);
517 	wnodep->next = wnodep->prev = NULL;
518 
519 	for (i = 0; i < boards; i++)
520 		sbdp_bd_init(&wnodep->bds[i], i, wnode);
521 }
522 
523 /*
524  * Wnode got DRed out.  Clean up all the node stuff including the boards
525  */
526 void
527 sbdp_wnode_fini(sbdp_wnode_t *wnodep)
528 {
529 	int	boards;
530 	int	i;
531 	static fn_t	f = "sbdp_wnode_fini";
532 
533 	SBDP_DBG_FUNC("%s\n", f);
534 
535 	boards = wnodep->nbds;
536 
537 	for (i = 0; i < boards; i++)
538 		sbdp_bd_fini(&wnodep->bds[i]);
539 
540 	kmem_free(wnodep->bds, sizeof (sbdp_bd_t) * boards);
541 	wnodep->next = wnodep->prev = NULL;
542 	kmem_free(wnodep, sizeof (sbdp_wnode_t));
543 }
544 
545 /*
546  * Add all the necessary fields to this board's struct
547  */
548 void
549 sbdp_add_new_bd_info(int wnode, int board)
550 {
551 	sbdp_wnode_t	*cur;
552 	static fn_t	f = "sbdp_add_new_bd_info";
553 
554 	SBDP_DBG_FUNC("%s\n", f);
555 
556 	cur = sbdp_get_wnodep(wnode);
557 
558 	SBDP_DBG_MISC("adding new board info %d\n", board);
559 
560 	sbdp_update_bd_info(&cur->bds[board]);
561 
562 }
563 
564 /*
565  * This board has gone away.  Clean the necessary fields
566  */
567 void
568 sbdp_cleanup_bd(int wnode, int board)
569 {
570 	sbdp_wnode_t	*cur;
571 	sbdp_handle_t	handle, *hp;
572 	sbdp_bd_t	*bdp;
573 	int		i;
574 	static fn_t	f = "sbdp_cleanup_bd";
575 
576 	SBDP_DBG_FUNC("%s\n", f);
577 
578 	cur = sbdp_get_wnodep(wnode);
579 
580 	SBDP_DBG_MISC("cleaning up bd info for bd %d\n", board);
581 	if (cur == NULL) {
582 		SBDP_DBG_MISC("cur is null\n");
583 		return;
584 	}
585 
586 	bdp = &cur->bds[board];
587 
588 	/*
589 	 * Grab the lock
590 	 */
591 	mutex_enter(&bdp->bd_mutex);
592 
593 	for (i = 0; i < bdp->nnum; i++)
594 		bdp->nodes[i] = (pnode_t)0;
595 	bdp->nnum = 0;
596 
597 	sbdp_fini_bd_banks(bdp);
598 
599 	hp = &handle;
600 	hp->h_board = bdp->bd;
601 	hp->h_wnode = bdp->wnode;
602 	if (bdp->ml) {
603 		sbdp_del_memlist(hp, bdp->ml);
604 	}
605 
606 	bdp->ml = NULL;
607 
608 	bdp->bpa = -1;
609 
610 	sbdp_cpu_in_reset(wnode, bdp->bd, SBDP_ALL_CPUS, 0);
611 
612 	bdp->cpus_present = 0;
613 
614 	mutex_exit(&bdp->bd_mutex);
615 }
616 
617 /*
618  *  Traverse the list looking for wnode. Return it when found
619  */
620 sbdp_wnode_t *
621 sbdp_get_wnodep(int wnode)
622 {
623 	sbdp_wnode_t	*cur;
624 	int		i;
625 	static fn_t	f = "sbdp_get_wnodep";
626 
627 	SBDP_DBG_FUNC("%s\n", f);
628 
629 	mutex_enter(&sbdp_wnode_mutex);
630 	for (i = 0, cur = first_node; i < cur_num_wnodes; i++,
631 	    cur = cur->next) {
632 		if (cur->wnode == wnode) {
633 			mutex_exit(&sbdp_wnode_mutex);
634 			return (cur);
635 		}
636 	}
637 	mutex_exit(&sbdp_wnode_mutex);
638 
639 	return (NULL);
640 }
641 
642 /*
643  * Insert this brand new node into our master list. It leaves it all
644  * initialized
645  */
646 void
647 sbdp_insert_wnode(int wnode, int max_boards)
648 {
649 	sbdp_wnode_t	*wnodep;
650 	sbdp_wnode_t	*cur;
651 	static fn_t	f = "sbdp_insert_wnode";
652 
653 	SBDP_DBG_FUNC("%s\n", f);
654 
655 	wnodep = kmem_zalloc(sizeof (sbdp_wnode_t), KM_SLEEP);
656 
657 	mutex_enter(&sbdp_wnode_mutex);
658 	if (first_node == NULL) {
659 		first_node = wnodep;
660 		cur_num_wnodes++;
661 	} else {
662 		cur = first_node + cur_num_wnodes++;
663 		cur->next = wnodep;
664 		wnodep->prev = cur;
665 	}
666 	mutex_exit(&sbdp_wnode_mutex);
667 	sbdp_wnode_init(wnodep, wnode, max_boards);
668 }
669 
670 /*
671  * This node is gone.  Remove it from the list and also clean up
672  */
673 void
674 sbdp_remove_wnode(sbdp_wnode_t *wnodep)
675 {
676 	sbdp_wnode_t	*cur;
677 	static fn_t	f = "sbdp_remove_wnode";
678 
679 	SBDP_DBG_FUNC("%s\n", f);
680 
681 	if (wnodep != NULL) {
682 		sbdp_wnode_fini(wnodep);
683 		mutex_enter(&sbdp_wnode_mutex);
684 
685 		if (first_node == wnodep)
686 			first_node = NULL;
687 		else {
688 			cur = wnodep->prev;
689 			if (cur != NULL)
690 				cur->next = wnodep->next;
691 			if (wnodep->next != NULL)
692 				wnodep->next->prev = cur;
693 		}
694 
695 		cur_num_wnodes--;
696 		mutex_exit(&sbdp_wnode_mutex);
697 	}
698 }
699 
700 /*
701  * Entry point from sbd.  This is called when a new node is added.  We
702  * create an entry in our inventory and initialize all the stuff that will be
703  * needed
704  */
705 int
706 sbdp_setup_instance(caddr_t arg)
707 {
708 	ssm_sbdp_info_t	*sbdp_info;
709 	int		instance;
710 	int		wnode;
711 	int		max_boards;
712 	static fn_t	f = "sbdp_setup_instance";
713 
714 	SBDP_DBG_FUNC("%s\n", f);
715 
716 	/*
717 	 * We get this directly from ssm
718 	 */
719 	sbdp_info = (ssm_sbdp_info_t *)arg;
720 
721 	instance = sbdp_info->instance;
722 	wnode = sbdp_info->wnode;
723 	max_boards = plat_max_boards();
724 
725 	SBDP_DBG_MISC("sbdp_setup_instance: instance %d wnode %d\n", instance,
726 	    sbdp_info->wnode);
727 
728 	if (sbdp_get_wnodep(wnode) == NULL) {
729 		/*
730 		 * This node has not been instanstiated
731 		 * create one
732 		 */
733 		sbdp_insert_wnode(wnode, max_boards);
734 	}
735 
736 	return (DDI_SUCCESS);
737 }
738 
739 /*
740  * Entry point from sbd. This is called when a node has been removed (or is
741  * going away. We do all the necessary cleanup
742  */
743 int
744 sbdp_teardown_instance(caddr_t arg)
745 {
746 	ssm_sbdp_info_t	*sbdp_info;
747 	int		instance;
748 	int		wnode;
749 	sbdp_wnode_t	*wnodep;
750 	static fn_t	f = "sbdp_teardown_instance";
751 
752 	SBDP_DBG_FUNC("%s\n", f);
753 
754 	/*
755 	 * ssm should have set this up
756 	 */
757 	sbdp_info = (ssm_sbdp_info_t *)arg;
758 
759 	instance = sbdp_info->instance;
760 	wnode = sbdp_info->wnode;
761 
762 	SBDP_DBG_MISC("sbdp_teardown_instance: instance %d wnode %d\n",
763 	    instance, wnode);
764 
765 	/*
766 	 * Find this node and then remove it
767 	 */
768 	if ((wnodep = sbdp_get_wnodep(wnode)) != NULL) {
769 		sbdp_remove_wnode(wnodep);
770 	}
771 	return (DDI_SUCCESS);
772 }
773 
774 int
775 sbdp_disabled_component(sbdp_handle_t *hp)
776 {
777 #ifdef lint
778 	hp = hp;
779 #endif
780 	return (0);
781 }
782 
783 /* ARGSUSED */
784 int
785 sbdp_release_component(sbdp_handle_t *hp, dev_info_t *dip)
786 {
787 	return (0);
788 }
789 
790 void
791 sbdp_set_err(sbd_error_t *ep, int ecode, char *rsc)
792 {
793 	static fn_t	f = "sbdp_set_err";
794 
795 	SBDP_DBG_FUNC("%s\n", f);
796 	ASSERT(ep != NULL);
797 	ep->e_code = ecode;
798 
799 	if (rsc != NULL) {
800 		(void) strcpy((caddr_t)(ep->e_rsc), (caddr_t)rsc);
801 	}
802 }
803 
804 /*
805  * Serengeti DR passthrus are for debugging purposes only.
806  */
807 static struct {
808 	const char	*name;
809 	int		(*handler)(sbdp_handle_t *, void *);
810 } sbdp_passthrus[] = {
811 #ifdef DEBUG
812 	{ "readmem",		sbdp_passthru_readmem		},
813 	{ "prep-script",	sbdp_passthru_prep_script	},
814 	{ "test-quiesce",	sbdp_passthru_test_quiesce	},
815 	{ "inject-error",	sbdp_passthru_inject_error	},
816 	{ "reset-error",	sbdp_passthru_reset_error	},
817 #endif
818 
819 	/* the following line must always be last */
820 	{ NULL,			NULL				}
821 };
822 
823 
824 /*ARGSUSED*/
825 int
826 sbdp_ioctl(sbdp_handle_t *hp, sbdp_ioctl_arg_t *sbdpi)
827 {
828 #ifdef DEBUG
829 	char buf[512];
830 	int rv;
831 	sbd_ioctl_arg_t *sbdi   = (sbd_ioctl_arg_t *)sbdpi->h_iap;
832 	int		i;
833 	static fn_t	f = "sbdp_ioctl";
834 
835 	SBDP_DBG_FUNC("%s\n", f);
836 
837 	if (sbdi->i_len >= sizeof (buf) ||
838 	    ddi_copyin(sbdi->i_opts, buf, sbdi->i_len, sbdpi->h_mode)) {
839 		sbdp_set_err(hp->h_err, ESBD_FAULT, NULL);
840 		return (-1);
841 	}
842 
843 	i = 0;
844 	while (sbdp_passthrus[i].name != NULL) {
845 		int	len;
846 
847 		len = strlen(sbdp_passthrus[i].name);
848 		if (strncmp(sbdp_passthrus[i].name, buf, len) == 0)
849 			break;
850 		i++;
851 	}
852 
853 	if (sbdp_passthrus[i].name == NULL) {
854 		sbdp_set_err(hp->h_err, ESBD_INVAL, NULL);
855 		rv = EIO;
856 	} else {
857 		rv = (*sbdp_passthrus[i].handler)(hp, buf);
858 		if (rv != ESBD_NOERROR) {
859 			sbdp_set_err(hp->h_err, rv, NULL);
860 			rv = EIO;
861 		}
862 
863 	}
864 
865 	return (rv);
866 #else
867 	return (0);
868 #endif
869 }
870 
871 /*
872  * Check the dnode we obtained.  Need to find a better way to determine
873  * if the node has the correct starting address
874  */
875 int
876 sbdp_is_node_bad(pnode_t node)
877 {
878 	static fn_t	f = "sbdp_is_node_bad";
879 
880 	SBDP_DBG_FUNC("%s\n", f);
881 
882 	return ((node == OBP_NONODE) || (node == OBP_BADNODE) ||
883 	    ((node & 0x80000000u) != 0x80000000u));
884 }
885 
886 /*
887  * Retrieve the information we have on this board from
888  * the inventory
889  */
890 sbdp_bd_t *
891 sbdp_get_bd_info(int wnode, int board)
892 {
893 	sbdp_wnode_t	*wnodep;
894 	sbdp_bd_t	*bdp;
895 	int		max_bds;
896 	static fn_t	f = "sbdp_get_bd_info";
897 
898 	SBDP_DBG_FUNC("%s\n", f);
899 
900 	wnodep = sbdp_get_wnodep(wnode);
901 	max_bds = plat_max_boards();
902 
903 	if ((wnodep == NULL) || ((board < 0) && (board > max_bds))) {
904 		return (NULL);
905 	}
906 
907 	bdp = &wnodep->bds[board];
908 
909 	/*
910 	 * We might not have the complete bd info.  With cheetah we
911 	 * cannot access the memory decode registers when then cpu is
912 	 * in reset. If the mem info is incomplete, then we try to gather it
913 	 * here
914 	 */
915 	sbdp_update_bd_info(bdp);
916 
917 	return (bdp);
918 }
919 
920 /*
921  * There are certain cases where obp marks components as failed
922  * If the status is ok the node won't have any status property. It
923  * is only there if the status is other than ok.
924  */
925 sbd_cond_t
926 sbdp_get_comp_status(pnode_t nodeid)
927 {
928 	char			status_buf[OBP_MAXPROPNAME];
929 	static const char	*status = "status";
930 	static const char	*failed = "fail";
931 	static const char	*disabled = "disabled";
932 	static fn_t		f = "sbdp_get_comp_status";
933 
934 	SBDP_DBG_FUNC("%s\n", f);
935 
936 	if (sbdp_is_node_bad(nodeid)) {
937 		SBDP_DBG_STATE("node is not ok\n");
938 		return (SBD_COND_UNKNOWN);
939 	}
940 
941 	if (prom_getproplen(nodeid, (char *)status) <= 0) {
942 		SBDP_DBG_STATE("status is ok\n");
943 		return (SBD_COND_OK);
944 	}
945 
946 	if (prom_getprop(nodeid, (char *)status, status_buf) < 0) {
947 		SBDP_DBG_STATE("status is unknown\n");
948 		return (SBD_COND_UNKNOWN);
949 	}
950 
951 	if (strncmp(status_buf, failed, strlen(failed)) == 0) {
952 		SBDP_DBG_STATE("status of failed\n");
953 		return (SBD_COND_FAILED);
954 	}
955 
956 	if (strcmp(status_buf, disabled) == 0) {
957 		SBDP_DBG_STATE("status of unusable\n");
958 		return (SBD_COND_UNUSABLE);
959 	}
960 
961 	return (SBD_COND_OK);
962 }
963 
964 void
965 sbdp_cpu_in_reset(int node, int bd, int unit, int reset)
966 {
967 	sbdp_wnode_t	*cur;
968 	sbdp_bd_t	*bdp;
969 	static fn_t	f = "sbdp_cpu_in_reset";
970 
971 	SBDP_DBG_FUNC("%s\n", f);
972 
973 	if ((unit < -1) || (bd < 0) || (node < 0)) {
974 		return;
975 	}
976 
977 	cur = sbdp_get_wnodep(node);
978 
979 	SBDP_DBG_MISC("marking cpu %d %s for board %d\n", unit,
980 	    (reset) ? "in reset" : "out of reset", bd);
981 
982 	if (cur == NULL) {
983 		return;
984 	}
985 
986 	bdp = &cur->bds[bd];
987 
988 	if (unit == SBDP_ALL_CPUS)
989 		if (reset == 1)
990 			SBDP_SET_ALL_CPUS_IN_RESET(bdp);
991 		else
992 			SBDP_UNSET_ALL_CPUS_IN_RESET(bdp);
993 	else
994 		if (reset == 1)
995 			SBDP_SET_CPU_IN_RESET(bdp, unit);
996 		else
997 			SBDP_UNSET_CPU_IN_RESET(bdp, unit);
998 }
999 
1000 int
1001 sbdp_set_cpu_present(int node, int bd, int unit)
1002 {
1003 	sbdp_wnode_t	*cur;
1004 	sbdp_bd_t	*bdp;
1005 	static fn_t	f = "sbdp_set_cpu_present";
1006 
1007 	SBDP_DBG_FUNC("%s\n", f);
1008 
1009 	if ((unit < 0) || (bd < 0) || (node < 0)) {
1010 		return (-1);
1011 	}
1012 
1013 	cur = sbdp_get_wnodep(node);
1014 	if (cur == NULL) {
1015 		return (-1);
1016 	}
1017 
1018 	bdp = &cur->bds[bd];
1019 
1020 	SBDP_SET_CPU_PRESENT(bdp, unit);
1021 
1022 	return (0);
1023 }
1024 
1025 int
1026 sbdp_is_cpu_present(int node, int bd, int unit)
1027 {
1028 	sbdp_wnode_t	*cur;
1029 	sbdp_bd_t	*bdp;
1030 	static fn_t	f = "sbdp_is_cpu_present";
1031 
1032 	SBDP_DBG_FUNC("%s\n", f);
1033 
1034 	if ((unit < 0) || (bd < 0) || (node < 0)) {
1035 		return (-1);
1036 	}
1037 
1038 	cur = sbdp_get_wnodep(node);
1039 	if (cur == NULL) {
1040 		return (-1);
1041 	}
1042 
1043 	bdp = &cur->bds[bd];
1044 
1045 	return (SBDP_IS_CPU_PRESENT(bdp, unit));
1046 }
1047 
1048 int
1049 sbdp_is_cpu_in_reset(int node, int bd, int unit)
1050 {
1051 	sbdp_wnode_t	*cur;
1052 	sbdp_bd_t	*bdp;
1053 	static fn_t	f = "sbdp_is_cpu_in_reset";
1054 
1055 	SBDP_DBG_FUNC("%s\n", f);
1056 
1057 	if ((unit < 0) || (bd < 0) || (node < 0)) {
1058 		return (-1);
1059 	}
1060 
1061 	cur = sbdp_get_wnodep(node);
1062 
1063 	if (cur == NULL) {
1064 		return (-1);
1065 	}
1066 
1067 	bdp = &cur->bds[bd];
1068 
1069 	return (SBDP_IS_CPU_IN_RESET(bdp, unit));
1070 }
1071 
1072 int
1073 sbdp_dr_avail(void)
1074 {
1075 	static fn_t	f = "sbdp_dr_avail";
1076 
1077 	SBDP_DBG_FUNC("%s\n", f);
1078 
1079 	if (sbdp_dr_available)
1080 		if (sg_prom_sb_dr_check() == 0)
1081 			return (1);
1082 	return (0);
1083 }
1084