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