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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2001, 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Cherrystone platform-specific functions that aren't platform specific
27  *
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <libprtdiag.h>
33 #include <sys/mc.h>
34 
35 
36 static Prom_node *dev_next_node_by_compat(Prom_node *root, char *model);
37 static Prom_node *dev_find_node_by_compat(Prom_node *root, char *model);
38 
39 void	print_us3_memory_line(int portid,
40 				int bank_id,
41 				uint64_t bank_size,
42 				char *bank_status,
43 				uint64_t dimm_size,
44 				uint32_t intlv,
45 				int seg_id);
46 
47 void	add_node(Sys_tree *root, Prom_node *pnode);
48 int	do_prominfo(int syserrlog,
49 		    char *pgname,
50 		    int log_flag,
51 		    int prt_flag);
52 
53 void	*get_prop_val(Prop *prop);
54 Prop	*find_prop(Prom_node *pnode, char *name);
55 char	*get_node_name(Prom_node *pnode);
56 char	*get_node_type(Prom_node *pnode);
57 
58 void	fill_pci_card_list(Prom_node *pci_instance,
59 			    Prom_node *pci_card_node,
60 			    struct io_card *pci_card,
61 			    struct io_card **pci_card_list,
62 			    char **pci_slot_name_arr);
63 
64 static Prom_node	*next_pci_card(Prom_node *curr_card, int *is_bridge,
65 				int is_pcidev, Prom_node *curr_bridge,
66 				Prom_node * parent_bridge, Prom_node *pci);
67 
68 #define	HZ_TO_MHZ(x)	(((x) + 500000) / 1000000)
69 
70 /*
71  * Start from the current node and return the next node besides
72  * the current one which has the requested model property.
73  */
74 static Prom_node *
75 dev_next_node_by_compat(Prom_node *root, char *compat)
76 {
77 	Prom_node *node;
78 
79 	if (root == NULL)
80 	    return (NULL);
81 
82 	/* look at your children first */
83 	if ((node = dev_find_node_by_compat(root->child, compat)) != NULL)
84 	    return (node);
85 
86 	/* now look at your siblings */
87 	if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL)
88 	    return (node);
89 
90 	return (NULL);  /* not found */
91 }
92 
93 /*
94  * Do a depth-first walk of a device tree and
95  * return the first node with the matching model.
96  */
97 static Prom_node *
98 dev_find_node_by_compat(Prom_node *root, char *compat)
99 {
100 	Prom_node	*node;
101 	char		*compatible;
102 	char		*name;
103 
104 	if (root == NULL)
105 		return (NULL);
106 
107 	if (compat == NULL)
108 		return (NULL);
109 
110 	name = get_node_name(root);
111 	if (name == NULL)
112 		name = "";
113 
114 	compatible = (char *)get_prop_val(find_prop(root, "compatible"));
115 
116 	if (compatible == NULL)
117 	    return (NULL);
118 
119 	if ((strcmp(name, "pci") == 0) && (compatible != NULL) &&
120 	    (strcmp(compatible, compat) == 0)) {
121 		return (root); /* found a match */
122 	}
123 
124 	/* look at your children first */
125 	if ((node = dev_find_node_by_compat(root->child, compat)) != NULL)
126 	    return (node);
127 
128 	/* now look at your siblings */
129 	if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL)
130 	    return (node);
131 
132 	return (NULL);  /* not found */
133 }
134 
135 int32_t
136 find_child_device(picl_nodehdl_t parent, char *child_name,
137 		picl_nodehdl_t *child)
138 {
139 	int32_t		err;
140 	char		name[PICL_PROPNAMELEN_MAX];
141 
142 	err = picl_get_propval_by_name(parent, PICL_PROP_CHILD, &(*child),
143 		sizeof (picl_nodehdl_t));
144 	switch (err) {
145 	case PICL_SUCCESS:
146 		break;
147 	case PICL_PROPNOTFOUND:
148 		err = PICL_INVALIDHANDLE;
149 		return (err);
150 	default:
151 #ifdef WORKFILE_DEBUG
152 		log_printf(dgettext(TEXT_DOMAIN,
153 		    "Failed picl_get_propval_by_name with %s\n"),
154 		    picl_strerror(err));
155 #endif
156 		return (err);
157 	}
158 
159 	err = picl_get_propval_by_name(*child, PICL_PROP_NAME, name,
160 	    PICL_PROPNAMELEN_MAX);
161 
162 #ifdef WORKFILE_DEBUG
163 	if (err != PICL_SUCCESS) {
164 		log_printf(dgettext(TEXT_DOMAIN,
165 		    "failed the get name for root\n"));
166 		log_printf(dgettext(TEXT_DOMAIN, "%s\n"), picl_strerror(err));
167 	}
168 #endif
169 
170 	if (strcmp(name, child_name) == 0)
171 		return (err);
172 
173 	while (err != PICL_PROPNOTFOUND) {
174 #ifdef WORKFILE_DEBUG
175 		log_printf(dgettext(TEXT_DOMAIN, "child name is %s\n"), name);
176 #endif
177 		err = picl_get_propval_by_name(*child, PICL_PROP_PEER,
178 			&(*child), sizeof (picl_nodehdl_t));
179 		switch (err) {
180 		case PICL_SUCCESS:
181 			err = picl_get_propval_by_name(*child, PICL_PROP_NAME,
182 			    name, PICL_PROPNAMELEN_MAX);
183 			if (strcmp(name, child_name) == 0)
184 				return (err);
185 			break;
186 		case PICL_PROPNOTFOUND:
187 			break;
188 		default:
189 #ifdef WORKFILE_DEBUG
190 			log_printf(dgettext(TEXT_DOMAIN,
191 			    "Failed picl_get_propval_by_name with %s\n"),
192 			    picl_strerror(err));
193 #endif
194 			return (err);
195 		}
196 	}
197 	err = PICL_INVALIDHANDLE;
198 	return (err);
199 }
200 
201 int32_t
202 fill_device_from_id(picl_nodehdl_t device_id, char *assoc_id,
203 		picl_nodehdl_t *device)
204 {
205 	int32_t		err;
206 	picl_prophdl_t	tbl_hdl;
207 	picl_prophdl_t	reference_property;
208 
209 	err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl,
210 		sizeof (picl_prophdl_t));
211 	if (err != PICL_SUCCESS) {
212 #ifdef WORKFILE_DEBUG
213 		if (err != PICL_INVALIDHANDLE) {
214 			log_printf(dgettext(TEXT_DOMAIN,
215 			"fill_device_from_id failure in "
216 			"picl_get_propval_by_name err is %s\n"),
217 			    picl_strerror(err));
218 		}
219 #endif
220 		return (err);
221 	}
222 
223 	err = picl_get_next_by_row(tbl_hdl, &reference_property);
224 	if (err != PICL_SUCCESS) {
225 #ifdef WORKFILE_DEBUG
226 		log_printf(dgettext(TEXT_DOMAIN,
227 		    "fill_device_from_id failure in picl_get_next_by_row"
228 		    " err is %s\n"), picl_strerror(err));
229 #endif
230 		return (err);
231 	}
232 
233 	/* get node associated with reference property */
234 	err = picl_get_propval(reference_property, &(*device),
235 		sizeof (picl_nodehdl_t));
236 
237 #ifdef WORKFILE_DEBUG
238 	if (err != 0) {
239 		log_printf(dgettext(TEXT_DOMAIN,
240 		"fill_device_from_id failure in picl_get_propval"
241 		" err is %s\n"), picl_strerror(err));
242 	}
243 #endif
244 
245 	return (err);
246 }
247 
248 int32_t
249 fill_device_array_from_id(picl_nodehdl_t device_id, char *assoc_id,
250 	int32_t *number_of_devices, picl_nodehdl_t *device_array[])
251 {
252 	int32_t		err;
253 	int		i;
254 	picl_prophdl_t	tbl_hdl;
255 	picl_prophdl_t	entry;
256 	int		devs = 0;
257 
258 	err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl,
259 		sizeof (picl_prophdl_t));
260 	if ((err != PICL_SUCCESS) && (err != PICL_INVALIDHANDLE)) {
261 #ifdef WORKFILE_DEBUG
262 	    log_printf(dgettext(TEXT_DOMAIN,
263 	    "fill_device_array_from_id failure in "
264 	    "picl_get_propval_by_name err is %s\n"), picl_strerror(err));
265 #endif
266 	    return (err);
267 	}
268 
269 	entry = tbl_hdl;
270 	while (picl_get_next_by_row(entry, &entry) == 0)
271 		++devs;
272 
273 	*device_array = calloc((devs), sizeof (picl_nodehdl_t));
274 	if (*device_array == NULL) {
275 
276 #ifdef WORFILE_DEBUG
277 		log_printf(dgettext(TEXT_DOMAIN,
278 		"fill_device_array_from_id failure getting memory"
279 		" for array\n"));
280 #endif
281 		return (PICL_FAILURE);
282 	}
283 
284 	entry = tbl_hdl;
285 	for (i = 0; i < devs; i++) {
286 		err = picl_get_next_by_row(entry, &entry);
287 		if (err != 0) {
288 #ifdef WORKFILE_DEBUG
289 			log_printf(dgettext(TEXT_DOMAIN,
290 			"fill_device_array_from_id failure in "
291 			"picl_get_next_by_row err is %s\n"),
292 			    picl_strerror(err));
293 #endif
294 			return (err);
295 		}
296 
297 		/* get node associated with reference property */
298 		err = picl_get_propval(entry, &((*device_array)[i]),
299 			sizeof (picl_nodehdl_t));
300 		if (err != 0) {
301 #ifdef WORKFILE_DEBUG
302 			log_printf(dgettext(TEXT_DOMAIN,
303 			"fill_device_array_from_id failure in "
304 			"picl_get_propval err is %s\n"), picl_strerror(err));
305 #endif
306 
307 			return (err);
308 		}
309 	}
310 	*number_of_devices = devs;
311 	return (err);
312 }
313 
314 /*
315  * add_node
316  *
317  * This function adds a board node to the board structure where that
318  * that node's physical component lives.
319  */
320 void
321 add_node(Sys_tree *root, Prom_node *pnode)
322 {
323 	int	board	= -1;
324 	int	portid	= -1;
325 
326 	void		*value	= NULL;
327 	Board_node	*bnode	= NULL;
328 	Prom_node	*p	= NULL;
329 
330 	/* Get the board number of this board from the portid prop */
331 	value = get_prop_val(find_prop(pnode, "portid"));
332 	if (value != NULL) {
333 		portid = *(int *)value;
334 	}
335 
336 	board = CHERRYSTONE_GETSLOT(portid);
337 
338 	if ((bnode = find_board(root, board)) == NULL) {
339 		bnode = insert_board(root, board);
340 	}
341 
342 	/* now attach this prom node to the board list */
343 	/* Insert this node at the end of the list */
344 	pnode->sibling = NULL;
345 	if (bnode->nodes == NULL)
346 		bnode->nodes = pnode;
347 	else {
348 		p = bnode->nodes;
349 		while (p->sibling != NULL)
350 			p = p->sibling;
351 		p->sibling = pnode;
352 	}
353 }
354 
355 /*
356  * This function provides formatting of the memory config
357  * information that get_us3_mem_regs() and display_us3_banks() code has
358  * gathered. It overrides the generic print_us3_memory_line() code
359  * which prints an error message.
360  */
361 void
362 print_us3_memory_line(int portid, int bank_id, uint64_t bank_size,
363 	char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id)
364 {
365 	log_printf(dgettext(TEXT_DOMAIN,
366 		"\n %-1c   %2d    %2d      %4lldMB   %11-s  %4lldMB "
367 		"   %2d-way        %d"),
368 			CHERRYSTONE_GETSLOT_LABEL(portid), portid,
369 			(bank_id % 4), bank_size, bank_status, dimm_size,
370 			intlv, seg_id, 0);
371 }
372 
373 /*
374  * We call do_devinfo() in order to use the libdevinfo device tree
375  * instead of OBP's device tree.
376  */
377 int
378 do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag)
379 {
380 	return (do_devinfo(syserrlog, pgname, log_flag, prt_flag));
381 }
382 
383 /*
384  * return the property value for the Prop
385  * passed in. (When using libdevinfo)
386  */
387 void *
388 get_prop_val(Prop *prop)
389 {
390 	if (prop == NULL)
391 		return (NULL);
392 
393 	return ((void *)(prop->value.val_ptr));
394 }
395 
396 /*
397  * Search a Prom node and retrieve the property with the correct
398  * name. (When using libdevinfo)
399  */
400 Prop *
401 find_prop(Prom_node *pnode, char *name)
402 {
403 	Prop *prop;
404 
405 	if (pnode  == NULL)
406 		return (NULL);
407 
408 	if (pnode->props == NULL)
409 		return (NULL);
410 
411 	prop = pnode->props;
412 	if (prop == NULL)
413 		return (NULL);
414 
415 	if (prop->name.val_ptr == NULL)
416 		return (NULL);
417 
418 	while ((prop != NULL) && (strcmp((char *)(prop->name.val_ptr), name))) {
419 		prop = prop->next;
420 	}
421 	return (prop);
422 }
423 
424 /*
425  * This function searches through the properties of the node passed in
426  * and returns a pointer to the value of the name property.
427  * (When using libdevinfo)
428  */
429 char *
430 get_node_name(Prom_node *pnode)
431 {
432 	Prop *prop;
433 
434 	if (pnode == NULL) {
435 		return (NULL);
436 	}
437 
438 	prop = pnode->props;
439 	while (prop != NULL) {
440 		if (strcmp("name", (char *)prop->name.val_ptr) == 0)
441 			return (prop->value.val_ptr);
442 		prop = prop->next;
443 	}
444 	return (NULL);
445 }
446 
447 /*
448  * This function searches through the properties of the node passed in
449  * and returns a pointer to the value of the device_type property.
450  * (When using libdevinfo)
451  */
452 char *
453 get_node_type(Prom_node *pnode)
454 {
455 	Prop *prop;
456 
457 	if (pnode == NULL) {
458 		return (NULL);
459 	}
460 
461 	prop = pnode->props;
462 	while (prop != NULL) {
463 		if (strcmp("device_type", (char *)prop->name.val_ptr) == 0)
464 			return (prop->value.val_ptr);
465 		prop = prop->next;
466 	}
467 	return (NULL);
468 }
469 
470 
471 /*
472  * Fills in the i/o card list to be displayed later in display_pci();
473  */
474 void
475 fill_pci_card_list(Prom_node * pci_instance, Prom_node * pci_card_node,
476 			struct io_card *pci_card,
477 			struct io_card **pci_card_list, char **slot_name_arr)
478 {
479 	Prom_node	*pci_bridge_node;
480 	Prom_node	*pci_parent_bridge;
481 	int		*int_val;
482 	int		pci_bridge = FALSE;
483 	int		pci_bridge_dev_no = -1;
484 	int		portid;
485 	int		pci_bus;
486 	char		buf[MAXSTRLEN];
487 	char		*slot_name = NULL;	/* info in "slot-names" prop */
488 	char		*child_name;
489 	char		*name;
490 	char		*type;
491 	void		*value;
492 
493 	while (pci_card_node != NULL) {
494 		int is_pci = FALSE;
495 		type = NULL;
496 		name = NULL;
497 		/* If it doesn't have a name, skip it */
498 		name = (char *)get_prop_val(
499 				find_prop(pci_card_node, "name"));
500 		if (name == NULL) {
501 			pci_card_node = pci_card_node->sibling;
502 			continue;
503 		}
504 
505 		/*
506 		 * Get the portid of the schizo that this card
507 		 * lives under.
508 		 */
509 		portid = -1;
510 		value = get_prop_val(find_prop(pci_instance, "portid"));
511 		if (value != NULL) {
512 			portid = *(int *)value;
513 		}
514 		pci_card->schizo_portid = portid;
515 		if (pci_card->schizo_portid != 8) {
516 			/*
517 			 * Schizo0 (portid 8) has no slots on Cherrystone.
518 			 * So if that's who we're looking at, we're done.
519 			 */
520 			return;
521 		}
522 
523 		/*
524 		 * Find out whether this is PCI bus A or B
525 		 * using the 'reg' property.
526 		 */
527 		int_val = (int *)get_prop_val(find_prop(pci_instance, "reg"));
528 
529 		if (int_val != NULL) {
530 			int_val++; /* skip over first integer */
531 			pci_bus = ((*int_val) & 0x7f0000);
532 			if (pci_bus == 0x600000)
533 				pci_card->pci_bus = 'A';
534 			else if (pci_bus == 0x700000)
535 				pci_card->pci_bus = 'B';
536 			else {
537 				assert(0); /* should never happen */
538 				pci_card->pci_bus = '-';
539 			}
540 		} else {
541 			assert(0); /* should never happen */
542 			pci_card->pci_bus = '-';
543 		}
544 
545 		/*
546 		 * get dev# and func# for this card from the
547 		 * 'reg' property.
548 		 */
549 		int_val = (int *)get_prop_val(
550 			find_prop(pci_card_node, "reg"));
551 		if (int_val != NULL) {
552 			pci_card->dev_no = (((*int_val) & 0xF800) >> 11);
553 			pci_card->func_no = (((*int_val) & 0x700) >> 8);
554 		} else {
555 			pci_card->dev_no = -1;
556 			pci_card->func_no = -1;
557 		}
558 
559 		switch (pci_card->pci_bus) {
560 		case 'A':
561 			if ((pci_card->dev_no < 1 || pci_card->dev_no > 2) &&
562 			(!pci_bridge)) {
563 				pci_card_node = pci_card_node->sibling;
564 				continue;
565 			}
566 			break;
567 		case 'B':
568 			if ((pci_card->dev_no < 2 || pci_card->dev_no > 5) &&
569 			(!pci_bridge)) {
570 				pci_card_node = pci_card_node->sibling;
571 				continue;
572 			}
573 			break;
574 		default:
575 			pci_card_node = pci_card_node->sibling;
576 			continue;
577 		}
578 
579 		type = (char *)get_prop_val(
580 				find_prop(pci_card_node, "device_type"));
581 		/*
582 		 * If this is a pci-bridge, then store its dev#
583 		 * as its children nodes need this to get their slot#.
584 		 * We set the pci_bridge flag so that we know we are
585 		 * looking at a pci-bridge node. This flag gets reset
586 		 * every time we enter this while loop.
587 		 */
588 
589 		/*
590 		 * Check for a PCI-PCI Bridge for PCI and cPCI
591 		 * IO Boards using the name and type properties.
592 		 */
593 		if ((type != NULL) && (strncmp(name, "pci", 3) == 0) &&
594 		    (strcmp(type, "pci") == 0)) {
595 			pci_bridge_node = pci_card_node;
596 			is_pci = TRUE;
597 			if (!pci_bridge) {
598 				pci_bridge_dev_no = pci_card->dev_no;
599 				pci_parent_bridge = pci_bridge_node;
600 				pci_bridge = TRUE;
601 			}
602 		}
603 
604 		/*
605 		 * Get slot-names property from slot_names_arr.
606 		 * If we are the child of a pci_bridge we use the
607 		 * dev# of the pci_bridge as an index to get
608 		 * the slot number. We know that we are a child of
609 		 * a pci-bridge if our parent is the same as the last
610 		 * pci_bridge node found above.
611 		 */
612 		if (pci_card->dev_no != -1) {
613 			/*
614 			 * We compare this cards parent node with the
615 			 * pci_bridge_node to see if it's a child.
616 			 */
617 			if (pci_card_node->parent != pci_instance &&
618 			    pci_bridge) {
619 				/* use dev_no of pci_bridge */
620 				if (pci_card->pci_bus == 'B') {
621 					slot_name =
622 					    slot_name_arr[pci_bridge_dev_no -2];
623 				} else {
624 					slot_name =
625 					    slot_name_arr[pci_bridge_dev_no -1];
626 				}
627 			} else {
628 				if (pci_card->pci_bus == 'B') {
629 				slot_name =
630 					slot_name_arr[pci_card->dev_no-2];
631 				} else {
632 				slot_name =
633 					slot_name_arr[pci_card->dev_no-1];
634 				}
635 			}
636 
637 			if (slot_name != NULL &&
638 			    strlen(slot_name) != 0) {
639 				/* Slot num is last char in string */
640 				(void) snprintf(pci_card->slot_str, MAXSTRLEN,
641 				    "%c", slot_name[strlen(slot_name) - 1]);
642 			} else {
643 				(void) snprintf(pci_card->slot_str, MAXSTRLEN,
644 				    "-");
645 			}
646 
647 		} else {
648 			(void) snprintf(pci_card->slot_str, MAXSTRLEN,
649 			    "%c", '-');
650 		}
651 
652 		/*
653 		 * Check for failed status.
654 		 */
655 		if (node_failed(pci_card_node))
656 			strcpy(pci_card->status, "fail");
657 		else
658 			strcpy(pci_card->status, "ok");
659 
660 		/* Get the model of this pci_card */
661 		value = get_prop_val(find_prop(pci_card_node, "model"));
662 		if (value == NULL)
663 			pci_card->model[0] = '\0';
664 		else {
665 			(void) snprintf(pci_card->model, MAXSTRLEN, "%s",
666 				(char *)value);
667 		}
668 		/*
669 		 * The card may have a "clock-frequency" but we
670 		 * are not interested in that. Instead we get the
671 		 * "clock-frequency" of the PCI Bus that the card
672 		 * resides on. PCI-A can operate at 33Mhz or 66Mhz
673 		 * depending on what card is plugged into the Bus.
674 		 * PCI-B always operates at 33Mhz.
675 		 */
676 		int_val = get_prop_val(find_prop(pci_instance,
677 			"clock-frequency"));
678 		if (int_val != NULL) {
679 			pci_card->freq = HZ_TO_MHZ(*int_val);
680 		} else {
681 			pci_card->freq = -1;
682 		}
683 
684 		/*
685 		 * Figure out how we want to display the name
686 		 */
687 		value = get_prop_val(find_prop(pci_card_node,
688 					"compatible"));
689 		if (value != NULL) {
690 			/* use 'name'-'compatible' */
691 			(void) snprintf(buf, MAXSTRLEN, "%s-%s", name,
692 				(char *)value);
693 		} else {
694 			/* just use 'name' */
695 			(void) snprintf(buf, MAXSTRLEN, "%s", name);
696 		}
697 		name = buf;
698 
699 		/*
700 		 * If this node has children, add the device_type
701 		 * of the child to the name value of this pci_card->
702 		 */
703 		child_name = (char *)get_node_name(pci_card_node->child);
704 		if ((pci_card_node->child != NULL) &&
705 			(child_name != NULL)) {
706 			value = get_prop_val(find_prop(pci_card_node->child,
707 				"device_type"));
708 			if (value != NULL) {
709 				/* add device_type of child to name */
710 				(void) snprintf(pci_card->name, MAXSTRLEN,
711 				    "%s/%s (%s)", name, child_name,
712 					(char *)value);
713 			} else {
714 				/* just add childs name */
715 				(void) snprintf(pci_card->name, MAXSTRLEN,
716 				    "%s/%s", name, child_name);
717 			}
718 		} else {
719 			(void) snprintf(pci_card->name, MAXSTRLEN, "%s",
720 			    (char *)name);
721 		}
722 
723 		/*
724 		 * If this is a pci-bridge, then add the word
725 		 * 'pci-bridge' to its model.  If we can't find
726 		 * a model, then we just describe what the device
727 		 * is based on some properties.
728 		 */
729 		if (pci_bridge) {
730 			if (strlen(pci_card->model) == 0) {
731 			    if (pci_card_node->parent == pci_bridge_node)
732 				(void) snprintf(pci_card->model, MAXSTRLEN,
733 				    "%s", "device on pci-bridge");
734 			    else if (pci_card_node->parent
735 					== pci_parent_bridge)
736 				(void) snprintf(pci_card->model, MAXSTRLEN,
737 					"%s", "pci-bridge/pci-bridge");
738 			    else
739 				(void) snprintf(pci_card->model, MAXSTRLEN,
740 				    "%s", "PCI-BRIDGE");
741 			}
742 			else
743 				(void) snprintf(pci_card->model, MAXSTRLEN,
744 				    "%s/pci-bridge", pci_card->model);
745 		}
746 		/* insert this pci_card in the list to be displayed later */
747 
748 		*pci_card_list = insert_io_card(*pci_card_list, pci_card);
749 
750 		/*
751 		 * If we are dealing with a pci-bridge, we need to move
752 		 * down to the children of this bridge if there are any.
753 		 *
754 		 * If we are not, we are either dealing with a regular
755 		 * card (in which case we move onto the sibling of this
756 		 * card) or we are dealing with a child of a pci-bridge
757 		 * (in which case we move onto the child's siblings or
758 		 * if there are no more siblings for this child, we
759 		 * move onto the parents siblings).
760 		 */
761 		pci_card_node = next_pci_card(pci_card_node, &pci_bridge,
762 			is_pci, pci_bridge_node,
763 			pci_parent_bridge, pci_instance);
764 	} /* end-while */
765 }
766 
767 /*
768  * Helper function for fill_pci_card_list().  Indicates which
769  * card node to go to next.
770  * Parameters:
771  * -----------
772  * Prom_node * curr_card: pointer to the current card node
773  *
774  * int * is_bridge: indicates whether or not the card (is | is on)
775  *                  a pci bridge
776  *
777  * int is_pcidev: indicates whether or not the current card
778  *                is a pci bridge
779  *
780  * Prom_node * curr_bridge: pointer to the current pci bridge.  Eg:
781  *                          curr_card->parent.
782  *
783  * Prom_node * parent_bridge: pointer to the first pci bridge encountered.
784  *			      we could have nested pci bridges, this would
785  *			      be the first one.
786  *
787  * Prom_node * pci: pointer to the pci instance that we are attached to.
788  *		    This would be parent_bridge->parent, or
789  *		    curr_node->parent, if curr_node is not on a pci bridge.
790  */
791 static Prom_node *
792 next_pci_card(Prom_node *curr_card, int *is_bridge, int is_pcidev,
793 		Prom_node *curr_bridge, Prom_node *parent_bridge,
794 		Prom_node *pci)
795 {
796 	Prom_node * curr_node = curr_card;
797 	if (*is_bridge) {
798 		/*
799 		 * is_pcidev is used to prevent us from following the
800 		 * children of something like a scsi device.
801 		 */
802 		if (curr_node->child != NULL && is_pcidev) {
803 			curr_node = curr_node->child;
804 		} else {
805 			curr_node = curr_node->sibling;
806 			if (curr_node == NULL) {
807 				curr_node = curr_bridge->sibling;
808 				while (curr_node == NULL &&
809 					curr_bridge != parent_bridge &&
810 					curr_bridge != NULL) {
811 					curr_node =
812 						curr_bridge->parent->sibling;
813 					curr_bridge = curr_bridge->parent;
814 					if (curr_node != NULL &&
815 					    curr_node->parent == pci)
816 						break;
817 				}
818 				if (curr_bridge == NULL ||
819 				    curr_node == NULL ||
820 				    curr_node->parent == pci ||
821 				    curr_bridge == parent_bridge ||
822 				    curr_node == parent_bridge) {
823 					*is_bridge = FALSE;
824 				}
825 			}
826 		}
827 
828 	} else {
829 		curr_node = curr_node->sibling;
830 	}
831 	return (curr_node);
832 }
833