xref: /titanic_51/usr/src/lib/libprtdiag/common/io.c (revision fe598cdcd847f8359013532d5c691bb6190378c0)
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 (c) 1999-2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <string.h>
34 #include <kvm.h>
35 #include <varargs.h>
36 #include <errno.h>
37 #include <time.h>
38 #include <dirent.h>
39 #include <fcntl.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <sys/openpromio.h>
45 #include <sys/systeminfo.h>
46 #include <kstat.h>
47 #include <libintl.h>
48 #include <syslog.h>
49 #include <sys/dkio.h>
50 #include "pdevinfo.h"
51 #include "display.h"
52 #include "pdevinfo_sun4u.h"
53 #include "display_sun4u.h"
54 #include "libprtdiag.h"
55 
56 #if !defined(TEXT_DOMAIN)
57 #define	TEXT_DOMAIN	"SYS_TEST"
58 #endif
59 
60 Prom_node *
61 find_pci_bus(Prom_node *node, int id, int bus)
62 {
63 	Prom_node *pnode;
64 
65 	/* find the first pci node */
66 	pnode = dev_find_node(node, "pci");
67 
68 	while (pnode != NULL) {
69 		int tmp_id;
70 		int tmp_bus;
71 
72 		tmp_id = get_id(pnode);
73 		tmp_bus = get_pci_bus(pnode);
74 
75 		if ((tmp_id == id) &&
76 		    (tmp_bus == bus)) {
77 			break;
78 		}
79 
80 		pnode = dev_next_node(pnode, "pci");
81 	}
82 	return (pnode);
83 }
84 
85 /*
86  * get_pci_bus
87  *
88  * Determines the PCI bus, either A (0) or B (1). If the function cannot
89  * find the bus-ranges property, it returns -1.
90  */
91 int
92 get_pci_bus(Prom_node *pnode)
93 {
94 	int *value;
95 
96 	/* look up the bus-range property */
97 	if ((value = (int *)get_prop_val(find_prop(pnode, "bus-range"))) ==
98 	    NULL) {
99 		return (-1);
100 	}
101 
102 	if (*value == 0) {
103 		return (1);	/* B bus has a bus-range value = 0 */
104 	} else {
105 		return (0);
106 	}
107 }
108 
109 
110 
111 /*
112  * Find the PCI device number of this PCI device. If no device number can
113  * be determined, then return -1.
114  */
115 int
116 get_pci_device(Prom_node *pnode)
117 {
118 	void *value;
119 
120 	if ((value = get_prop_val(find_prop(pnode, "assigned-addresses"))) !=
121 		NULL) {
122 		return (PCI_DEVICE(*(int *)value));
123 	} else {
124 		return (-1);
125 	}
126 }
127 
128 /*
129  * Find the PCI device number of this PCI device. If no device number can
130  * be determined, then return -1.
131  */
132 int
133 get_pci_to_pci_device(Prom_node *pnode)
134 {
135 	void *value;
136 
137 	if ((value = get_prop_val(find_prop(pnode, "reg"))) !=
138 		NULL) {
139 		return (PCI_DEVICE(*(int *)value));
140 	} else {
141 		return (-1);
142 	}
143 }
144 
145 /*
146  * free_io_cards
147  * Frees the memory allocated for an io card list.
148  */
149 void
150 free_io_cards(struct io_card *card_list)
151 {
152 	/* Free the list */
153 	if (card_list != NULL) {
154 		struct io_card *p, *q;
155 
156 		for (p = card_list, q = NULL; p != NULL; p = q) {
157 			q = p->next;
158 			free(p);
159 		}
160 	}
161 }
162 
163 
164 /*
165  * insert_io_card
166  * Inserts an io_card structure into the list.  The list is maintained
167  * in order based on board number and slot number.  Also, the storage
168  * for the "card" argument is assumed to be handled by the caller,
169  * so we won't touch it.
170  */
171 struct io_card *
172 insert_io_card(struct io_card *list, struct io_card *card)
173 {
174 	struct io_card *newcard;
175 	struct io_card *p, *q;
176 
177 	if (card == NULL)
178 		return (list);
179 
180 	/* Copy the card to be added into new storage */
181 	newcard = (struct io_card *)malloc(sizeof (struct io_card));
182 	if (newcard == NULL) {
183 		perror("malloc");
184 		exit(2);
185 	}
186 	(void) memcpy(newcard, card, sizeof (struct io_card));
187 	newcard->next = NULL;
188 
189 	if (list == NULL)
190 	return (newcard);
191 
192 	/* Find the proper place in the list for the new card */
193 	for (p = list, q = NULL; p != NULL; q = p, p = p->next) {
194 		if (newcard->board < p->board)
195 			break;
196 		if ((newcard->board == p->board) && (newcard->slot < p->slot))
197 			break;
198 	}
199 
200 	/* Insert the new card into the list */
201 	if (q == NULL) {
202 		newcard->next = p;
203 		return (newcard);
204 	} else {
205 		newcard->next = p;
206 		q->next = newcard;
207 		return (list);
208 	}
209 }
210 
211 
212 char *
213 fmt_manf_id(unsigned int encoded_id, char *outbuf)
214 {
215 	union manuf manuf;
216 
217 	/*
218 	 * Format the manufacturer's info.  Note a small inconsistency we
219 	 * have to work around - Brooktree has it's part number in decimal,
220 	 * while Mitsubishi has it's part number in hex.
221 	 */
222 	manuf.encoded_id = encoded_id;
223 	switch (manuf.fld.manf) {
224 	case MANF_BROOKTREE:
225 		(void) sprintf(outbuf, "%s %d, version %d", "Brooktree",
226 			manuf.fld.partno, manuf.fld.version);
227 		break;
228 
229 	case MANF_MITSUBISHI:
230 		(void) sprintf(outbuf, "%s %x, version %d", "Mitsubishi",
231 			manuf.fld.partno, manuf.fld.version);
232 		break;
233 
234 	default:
235 		(void) sprintf(outbuf, "JED code %d, Part num 0x%x, version %d",
236 			manuf.fld.manf, manuf.fld.partno, manuf.fld.version);
237 	}
238 	return (outbuf);
239 }
240 
241 
242 /*
243  * Find the sbus slot number of this Sbus device. If no slot number can
244  * be determined, then return -1.
245  */
246 int
247 get_sbus_slot(Prom_node *pnode)
248 {
249 	void *value;
250 
251 	if ((value = get_prop_val(find_prop(pnode, "reg"))) != NULL) {
252 		return (*(int *)value);
253 	} else {
254 		return (-1);
255 	}
256 }
257 
258 
259 /*
260  * This routine is the generic link into displaying system IO
261  * configuration. It displays the table header, then displays
262  * all the SBus cards, then displays all fo the PCI IO cards.
263  */
264 void
265 display_io_devices(Sys_tree *tree)
266 {
267 	Board_node *bnode;
268 
269 	/*
270 	 * TRANSLATION_NOTE
271 	 * Following string is used as a table header.
272 	 * Please maintain the current alignment in
273 	 * translation.
274 	 */
275 	log_printf("\n", 0);
276 	log_printf("=========================", 0);
277 	log_printf(dgettext(TEXT_DOMAIN, " IO Cards "), 0);
278 	log_printf("=========================", 0);
279 	log_printf("\n", 0);
280 	log_printf("\n", 0);
281 	bnode = tree->bd_list;
282 	while (bnode != NULL) {
283 		display_sbus(bnode);
284 		display_pci(bnode);
285 		display_ffb(bnode, 1);
286 		bnode = bnode->next;
287 	}
288 }
289 
290 void
291 display_pci(Board_node *bnode)
292 {
293 #ifdef  lint
294 	bnode = bnode;
295 #endif
296 	/*
297 	 * This function is intentionally empty
298 	 */
299 }
300 
301 
302 /*
303  * Print out all the io cards in the list.  Also print the column
304  * headers if told to do so.
305  */
306 void
307 display_io_cards(struct io_card *list)
308 {
309 	static int banner = 0; /* Have we printed the column headings? */
310 	struct io_card *p;
311 
312 	if (list == NULL)
313 		return;
314 
315 	if (banner == 0) {
316 		log_printf("     Bus   Freq\n", 0);
317 		log_printf("Brd  Type  MHz   Slot        "
318 			"Name                          "
319 			"Model", 0);
320 		log_printf("\n", 0);
321 		log_printf("---  ----  ----  ----------  "
322 			"----------------------------  "
323 			"--------------------", 0);
324 		log_printf("\n", 0);
325 		banner = 1;
326 	}
327 
328 	for (p = list; p != NULL; p = p -> next) {
329 		log_printf("%2d   ", p->board, 0);
330 		log_printf("%-4s  ", p->bus_type, 0);
331 		log_printf("%3d   ", p->freq, 0);
332 		/*
333 		 * We check to see if it's an int or
334 		 * a char string to display for slot.
335 		 */
336 		if (p->slot == PCI_SLOT_IS_STRING)
337 			log_printf("%10s  ", p->slot_str, 0);
338 		else
339 			log_printf("%10d  ", p->slot, 0);
340 
341 		log_printf("%-28.28s", p->name, 0);
342 		if (strlen(p->name) > 28)
343 			log_printf("+ ", 0);
344 		else
345 			log_printf("  ", 0);
346 		log_printf("%-19.19s", p->model, 0);
347 		if (strlen(p->model) > 19)
348 			log_printf("+", 0);
349 		log_printf("\n", 0);
350 	}
351 }
352 
353 /*
354  * Display all FFBs on this board.  It can either be in tabular format,
355  * or a more verbose format.
356  */
357 void
358 display_ffb(Board_node *board, int table)
359 {
360 	Prom_node *fb;
361 	void *value;
362 	struct io_card *card_list = NULL;
363 	struct io_card card;
364 	char *type;
365 	char *label;
366 
367 	if (board == NULL)
368 		return;
369 
370 	/* Fill in common information */
371 	card.display = 1;
372 	card.board = board->board_num;
373 	(void) sprintf(card.bus_type, BUS_TYPE);
374 	card.freq = sys_clk;
375 
376 	for (fb = dev_find_node_by_type(board->nodes, "device_type", "display");
377 	    fb != NULL;
378 	    fb = dev_next_node_by_type(fb, "device_type", "display")) {
379 		value = get_prop_val(find_prop(fb, "name"));
380 		if (value != NULL) {
381 			if ((strcmp(FFB_NAME, value)) == 0) {
382 				type = FFB_NAME;
383 				label = "FFB";
384 			} else if ((strcmp(AFB_NAME, value)) == 0) {
385 				type = AFB_NAME;
386 				label = "AFB";
387 			} else
388 				continue;
389 		} else
390 			continue;
391 		if (table == 1) {
392 			/* Print out in table format */
393 
394 			/* XXX - Get the slot number (hack) */
395 			card.slot = get_id(fb);
396 
397 			/* Find out if it's single or double buffered */
398 			(void) sprintf(card.name, "%s", label);
399 			value = get_prop_val(find_prop(fb, "board_type"));
400 			if (value != NULL)
401 				if ((*(int *)value) & FFB_B_BUFF)
402 					(void) sprintf(card.name,
403 						"%s, Double Buffered", label);
404 				else
405 					(void) sprintf(card.name,
406 						"%s, Single Buffered", label);
407 
408 			/*
409 			 * Print model number only if board_type bit 2
410 			 * is not set and it is not SUNW,XXX-XXXX.
411 			 */
412 			card.model[0] = '\0';
413 
414 			if (strcmp(type, AFB_NAME) == 0) {
415 				if (((*(int *)value) & 0x4) != 0x4) {
416 					value = get_prop_val(find_prop(fb,
417 						    "model"));
418 					if ((value != NULL) &&
419 					    (strcmp(value,
420 					    "SUNW,XXX-XXXX") != 0)) {
421 						(void) sprintf(card.model, "%s",
422 						    (char *)value);
423 					}
424 				}
425 			} else {
426 				value = get_prop_val(find_prop(fb, "model"));
427 				if (value != NULL)
428 					(void) sprintf(card.model, "%s",
429 					    (char *)value);
430 			}
431 
432 			card_list = insert_io_card(card_list, &card);
433 		} else {
434 			/* print in long format */
435 			char device[MAXSTRLEN];
436 			int fd = -1;
437 			struct dirent *direntp;
438 			DIR *dirp;
439 			union strap_un strap;
440 			struct ffb_sys_info fsi;
441 
442 			/* Find the device node using upa-portid/portid */
443 			value = get_prop_val(find_prop(fb, "upa-portid"));
444 			if (value == NULL)
445 				value = get_prop_val(find_prop(fb, "portid"));
446 
447 			if (value == NULL)
448 			    continue;
449 
450 			(void) sprintf(device, "%s@%x", type,
451 				*(int *)value);
452 			if ((dirp = opendir("/devices")) == NULL)
453 				continue;
454 
455 			while ((direntp = readdir(dirp)) != NULL) {
456 				if (strstr(direntp->d_name, device) != NULL) {
457 					(void) sprintf(device, "/devices/%s",
458 						direntp->d_name);
459 					fd = open(device, O_RDWR, 0666);
460 					break;
461 				}
462 			}
463 			(void) closedir(dirp);
464 
465 			if (fd == -1)
466 				continue;
467 
468 			if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0)
469 				continue;
470 
471 			log_printf("%s Hardware Configuration:\n", label, 0);
472 			log_printf("-----------------------------------\n", 0);
473 
474 			strap.ffb_strap_bits = fsi.ffb_strap_bits;
475 			log_printf("\tBoard rev: %d\n",
476 				(int)strap.fld.board_rev, 0);
477 			log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0);
478 			log_printf("\tDAC: %s\n",
479 				fmt_manf_id(fsi.dac_version, device), 0);
480 			log_printf("\t3DRAM: %s\n",
481 				fmt_manf_id(fsi.fbram_version, device), 0);
482 			log_printf("\n", 0);
483 		}
484 
485 	}
486 	display_io_cards(card_list);
487 	free_io_cards(card_list);
488 }
489 
490 
491 /*
492  * Display all the SBus IO cards on this board.
493  */
494 void
495 display_sbus(Board_node *board)
496 {
497 	struct io_card card;
498 	struct io_card *card_list = NULL;
499 	int freq;
500 	int card_num;
501 	void *value;
502 	Prom_node *sbus;
503 	Prom_node *card_node;
504 
505 	if (board == NULL)
506 		return;
507 
508 	for (sbus = dev_find_node(board->nodes, SBUS_NAME); sbus != NULL;
509 	    sbus = dev_next_node(sbus, SBUS_NAME)) {
510 
511 		/* Skip failed nodes for now */
512 		if (node_failed(sbus))
513 			continue;
514 
515 		/* Calculate SBus frequency in MHz */
516 		value = get_prop_val(find_prop(sbus, "clock-frequency"));
517 		if (value != NULL)
518 			freq = ((*(int *)value) + 500000) / 1000000;
519 		else
520 			freq = -1;
521 
522 		for (card_node = sbus->child; card_node != NULL;
523 		    card_node = card_node->sibling) {
524 			char *model;
525 			char *name;
526 			char *child_name;
527 
528 			card_num = get_sbus_slot(card_node);
529 			if (card_num == -1)
530 				continue;
531 
532 			/* Fill in card information */
533 			card.display = 1;
534 			card.freq = freq;
535 			card.board = board->board_num;
536 			(void) sprintf(card.bus_type, "SBus");
537 			card.slot = card_num;
538 			card.status[0] = '\0';
539 
540 			/* Try and get card status */
541 			value = get_prop_val(find_prop(card_node, "status"));
542 			if (value != NULL)
543 				(void) strncpy(card.status, (char *)value,
544 					MAXSTRLEN);
545 
546 			/* XXX - For now, don't display failed cards */
547 			if (strstr(card.status, "fail") != NULL)
548 				continue;
549 
550 			/* Now gather all of the node names for that card */
551 			model = (char *)get_prop_val(find_prop(card_node,
552 				"model"));
553 			name = get_node_name(card_node);
554 
555 			if (name == NULL)
556 				continue;
557 
558 			card.name[0] = '\0';
559 			card.model[0] = '\0';
560 
561 			/* Figure out how we want to display the name */
562 			child_name = get_node_name(card_node->child);
563 			if ((card_node->child != NULL) &&
564 			    (child_name != NULL)) {
565 				value = get_prop_val(find_prop(card_node->child,
566 					"device_type"));
567 				if (value != NULL)
568 					(void) sprintf(card.name, "%s/%s (%s)",
569 						name, child_name,
570 						(char *)value);
571 				else
572 					(void) sprintf(card.name, "%s/%s", name,
573 						child_name);
574 			} else {
575 				(void) strncpy(card.name, name, MAXSTRLEN);
576 			}
577 
578 			if (model != NULL)
579 				(void) strncpy(card.model, model, MAXSTRLEN);
580 
581 			card_list = insert_io_card(card_list, &card);
582 		}
583 	}
584 
585 	/* We're all done gathering card info, now print it out */
586 	display_io_cards(card_list);
587 	free_io_cards(card_list);
588 }
589 
590 
591 /*
592  * Get slot-names properties from parent node and
593  * store them in an array.
594  */
595 int
596 populate_slot_name_arr(Prom_node *pci, int *slot_name_bits,
597 			char **slot_name_arr, int num_slots)
598 {
599 	int	i, j, bit_mask;
600 	char	*value;
601 
602 	value = (char *)get_prop_val(find_prop(pci, "slot-names"));
603 	D_PRINTF("\n populate_slot_name_arr: value = [0x%x]\n", value);
604 
605 	if (value != NULL) {
606 		char	*strings_arr[MAX_SLOTS_PER_IO_BD];
607 		bit_mask = *slot_name_bits = *(int *)value;
608 		D_PRINTF("\nslot_names 1st integer = [0x%x]", *slot_name_bits);
609 
610 		/* array starts after first int */
611 		strings_arr[0] = value + sizeof (int);
612 
613 		/*
614 		 * break the array out into num_slots number of strings
615 		 */
616 		for (i = 1; i < num_slots; i++) {
617 			strings_arr[i] = (char *)strings_arr[i - 1]
618 			    + strlen(strings_arr[i - 1]) + 1;
619 		}
620 
621 		/*
622 		 * process array of slot_names to remove blanks
623 		 */
624 		j = 0;
625 		for (i = 0; i < num_slots; i++) {
626 			if ((bit_mask >> i) & 0x1)
627 				slot_name_arr[i] = strings_arr[j++];
628 			else
629 				slot_name_arr[i] = "";
630 
631 			D_PRINTF("\nslot_name_arr[%d] = [%s]", i,
632 				slot_name_arr[i]);
633 		}
634 		return (0);
635 	} else {
636 		D_PRINTF("\n populate_slot_name_arr: - psycho with no "
637 		    "slot-names\n");
638 		return (0);
639 	}
640 }
641 
642 int
643 get_card_frequency(Prom_node *pci)
644 {
645 	char	*value = get_prop_val(find_prop(pci, "clock-frequency"));
646 
647 	if (value == NULL)
648 		return (-1);
649 	else
650 		return (int)(((*(int *)value) + 500000) / 1000000);
651 
652 }
653 
654 void
655 get_dev_func_num(Prom_node *card_node, int *dev_no, int *func_no)
656 {
657 
658 	void	*value = get_prop_val(find_prop(card_node, "reg"));
659 
660 	if (value != NULL) {
661 		int int_val = *(int *)value;
662 		*dev_no = PCI_REG_TO_DEV(int_val);
663 		*func_no = PCI_REG_TO_FUNC(int_val);
664 	} else {
665 		*dev_no = -1;
666 		*func_no = -1;
667 	}
668 }
669 
670 void
671 get_pci_class_codes(Prom_node *card_node, int *class_code, int *subclass_code)
672 {
673 	int	class_code_reg = get_pci_class_code_reg(card_node);
674 
675 	*class_code = CLASS_REG_TO_CLASS(class_code_reg);
676 	*subclass_code = CLASS_REG_TO_SUBCLASS(class_code_reg);
677 }
678 
679 int
680 is_pci_bridge(Prom_node *card_node, char *name)
681 {
682 	int class_code, subclass_code;
683 
684 	if (card_node == NULL)
685 		return (FALSE);
686 
687 	get_pci_class_codes(card_node, &class_code, &subclass_code);
688 
689 	if ((strncmp(name, "pci", 3) == 0) &&
690 	    (class_code == PCI_BRIDGE_CLASS) &&
691 	    (subclass_code == PCI_PCI_BRIDGE_SUBCLASS))
692 		return (TRUE);
693 	else
694 		return (FALSE);
695 }
696 
697 int
698 is_pci_bridge_other(Prom_node *card_node, char *name)
699 {
700 	int class_code, subclass_code;
701 
702 	if (card_node == NULL)
703 		return (FALSE);
704 
705 	get_pci_class_codes(card_node, &class_code, &subclass_code);
706 
707 	if ((strncmp(name, "pci", 3) == 0) &&
708 	    (class_code == PCI_BRIDGE_CLASS) &&
709 	    (subclass_code == PCI_SUBCLASS_OTHER))
710 		return (TRUE);
711 	else
712 		return (FALSE);
713 }
714 void
715 get_pci_card_model(Prom_node *card_node, char *model)
716 {
717 	char	*name = get_prop_val(find_prop(card_node, "name"));
718 	char	*value = get_prop_val(find_prop(card_node, "model"));
719 	int 	pci_bridge = is_pci_bridge(card_node, name);
720 
721 	if (value == NULL)
722 		model[0] = '\0';
723 	else
724 		(void) sprintf(model, "%s",
725 			(char *)value);
726 
727 	if (pci_bridge) {
728 		if (strlen(model) == 0)
729 			(void) sprintf(model,
730 				"%s", "pci-bridge");
731 		else
732 			(void) sprintf(model,
733 				"%s/pci-bridge", model);
734 	}
735 }
736 
737 void
738 create_io_card_name(Prom_node *card_node, char *name, char *card_name)
739 {
740 	char	*value = get_prop_val(find_prop(card_node, "compatible"));
741 	char	*child_name;
742 	char	buf[MAXSTRLEN];
743 
744 	if (value != NULL) {
745 		(void) sprintf(buf, "%s-%s", name,
746 			(char *)value);
747 	} else
748 		(void) sprintf(buf, "%s", name);
749 
750 	name = buf;
751 
752 	child_name = (char *)get_node_name(card_node->child);
753 
754 	if ((card_node->child != NULL) &&
755 	    (child_name != NULL)) {
756 		value = get_prop_val(find_prop(card_node->child,
757 			"device_type"));
758 		if (value != NULL)
759 			(void) sprintf(card_name, "%s/%s (%s)",
760 				name, child_name,
761 				(char *)value);
762 		else
763 			(void) sprintf(card_name, "%s/%s", name,
764 				child_name);
765 	} else {
766 		(void) sprintf(card_name, "%s", (char *)name);
767 	}
768 }
769 
770 
771 /*
772  * Desktop display_psycho_pci
773  * Display all the psycho based PCI IO cards on this board.
774  */
775 
776 /* ARGSUSED */
777 void
778 display_psycho_pci(Board_node *board)
779 {
780 	struct io_card	*card_list = NULL;
781 	struct io_card	card;
782 	void		*value;
783 
784 	Prom_node	*pci, *card_node, *pci_bridge_node = NULL;
785 	char		*name;
786 	int		slot_name_bits, pci_bridge_dev_no,
787 			class_code, subclass_code,
788 			pci_pci_bridge;
789 	char		*slot_name_arr[MAX_SLOTS_PER_IO_BD];
790 
791 	if (board == NULL)
792 		return;
793 
794 	/* Initialize all the common information */
795 	card.display = 1;
796 	card.board = board->board_num;
797 	(void) sprintf(card.bus_type, "PCI");
798 
799 	for (pci = dev_find_node_by_type(board->nodes, "model", "SUNW,psycho");
800 	    pci != NULL;
801 	    pci = dev_next_node_by_type(pci, "model", "SUNW,psycho")) {
802 
803 		/*
804 		 * If we have reached a pci-to-pci bridge node,
805 		 * we are one level below the 'pci' nodes level
806 		 * in the device tree. To get back to that level,
807 		 * the search should continue with the sibling of
808 		 * the parent or else the remaining 'pci' cards
809 		 * will not show up in the output.
810 		 */
811 		if (find_prop(pci, "upa-portid") == NULL) {
812 			if ((pci->parent->sibling != NULL) &&
813 				(strcmp(get_prop_val(
814 				find_prop(pci->parent->sibling,
815 				"name")), PCI_NAME) == 0))
816 				pci = pci->parent->sibling;
817 			else {
818 				pci = pci->parent->sibling;
819 				continue;
820 			}
821 		}
822 
823 		D_PRINTF("\n\n------->Looking at device [%s][%d] - [%s]\n",
824 		    PCI_NAME, *((int *)get_prop_val(find_prop(
825 			pci, "upa-portid"))),
826 			get_prop_val(find_prop(pci, "model")));
827 
828 		/* Skip all failed nodes for now */
829 		if (node_failed(pci))
830 			continue;
831 
832 		/* Fill in frequency */
833 		card.freq = get_card_frequency(pci);
834 
835 		/*
836 		 * Each PSYCHO device has a slot-names property that can be
837 		 * used to determine the slot-name string for each IO
838 		 * device under this node. We get this array now and use
839 		 * it later when looking at the children of this PSYCHO.
840 		 */
841 		if ((populate_slot_name_arr(pci, &slot_name_bits,
842 		    (char **)&slot_name_arr, MAX_SLOTS_PER_IO_BD)) != 0)
843 			goto next_card;
844 
845 		/* Walk through the PSYCHO children */
846 		card_node = pci->child;
847 		while (card_node != NULL) {
848 
849 			pci_pci_bridge = FALSE;
850 
851 			/* If it doesn't have a name, skip it */
852 			name = (char *)get_prop_val(
853 				find_prop(card_node, "name"));
854 			if (name == NULL)
855 				goto next_card;
856 
857 			/* get dev# and func# for this card. */
858 			get_dev_func_num(card_node, &card.dev_no,
859 			    &card.func_no);
860 
861 			/* get class/subclass code for this card. */
862 			get_pci_class_codes(card_node, &class_code,
863 			    &subclass_code);
864 
865 			D_PRINTF("\nName [%s] - ", name);
866 			D_PRINTF("device no [%d] - ", card.dev_no);
867 			D_PRINTF("class_code [%d] subclass_code [%d] - ",
868 			    class_code, subclass_code);
869 
870 			/*
871 			 * Weed out PCI Bridge, subclass 'other' and
872 			 * ebus nodes.
873 			 */
874 			if (((class_code == PCI_BRIDGE_CLASS) &&
875 			    (subclass_code == PCI_SUBCLASS_OTHER)) ||
876 			    (strstr(name, "ebus"))) {
877 				D_PRINTF("\nSkip ebus/class-other nodes [%s]",
878 				    name);
879 				goto next_card;
880 			}
881 
882 			/*
883 			 * If this is a PCI bridge, then we store it's dev_no
884 			 * so that it's children can use it for getting at
885 			 * the slot_name.
886 			 */
887 			if (is_pci_bridge(card_node, name)) {
888 				pci_bridge_dev_no = card.dev_no;
889 				pci_bridge_node = card_node;
890 				pci_pci_bridge = TRUE;
891 				D_PRINTF("\nPCI Bridge detected\n");
892 			}
893 
894 			/*
895 			 * If we are the child of a pci_bridge we use the
896 			 * dev# of the pci_bridge as an index to get
897 			 * the slot number. We know that we are a child of
898 			 * a pci-bridge if our parent is the same as the last
899 			 * pci_bridge node found above.
900 			 */
901 			if (card_node->parent == pci_bridge_node)
902 				card.dev_no = pci_bridge_dev_no;
903 
904 			/* Get slot-names property from slot_names_arr. */
905 			get_slot_number_str(&card, (char **)slot_name_arr,
906 			    slot_name_bits);
907 
908 			if (slot_name_bits)
909 				D_PRINTF("\nIO Card [%s] dev_no [%d] SlotStr "
910 				    "[%s] slot [%s]", name, card.dev_no,
911 				    slot_name_arr[card.dev_no],
912 				    card.slot_str);
913 
914 			/* XXX - Don't know how to get status for PCI cards */
915 			card.status[0] = '\0';
916 
917 			/* Get the model of this card */
918 			get_pci_card_model(card_node, (char *)&card.model);
919 
920 			/*
921 			 * If we haven't figured out the frequency yet,
922 			 * try and get it from the card.
923 			 */
924 			value = get_prop_val(find_prop(pci, "clock-frequency"));
925 			if (value != NULL && card.freq == -1)
926 				card.freq = ((*(int *)value) + 500000)
927 					/ 1000000;
928 
929 
930 			/* Figure out how we want to display the name */
931 			create_io_card_name(card_node, name,
932 				(char *)&card.name);
933 
934 			if (card.freq != -1)
935 				card_list = insert_io_card(card_list, &card);
936 
937 next_card:
938 			/*
939 			 * If we are done with the children of the pci bridge,
940 			 * we must continue with the remaining siblings of
941 			 * the pci-to-pci bridge - otherwise we move onto our
942 			 * own sibling.
943 			 */
944 			if (pci_pci_bridge) {
945 				if (card_node->child != NULL)
946 					card_node = card_node->child;
947 				else
948 					card_node = card_node->sibling;
949 			} else {
950 				if ((card_node->parent == pci_bridge_node) &&
951 				    (card_node->sibling == NULL))
952 					card_node = pci_bridge_node->sibling;
953 				else
954 					card_node = card_node->sibling;
955 			}
956 		} /* end-while */
957 	} /* end-for */
958 
959 	D_PRINTF("\n\n");
960 
961 	display_io_cards(card_list);
962 	free_io_cards(card_list);
963 }
964 
965 void
966 get_slot_number_str(struct io_card *card, char **slot_name_arr,
967 	int slot_name_bits)
968 {
969 	if (card->dev_no != -1) {
970 		char	*slot;
971 		/*
972 		 * slot_name_bits is a mask of the plug-in slots so if our
973 		 * dev_no does not appear in this mask we must be an
974 		 * on_board device so set the slot to 'On-Board'
975 		 */
976 		if (slot_name_bits & (1 << card->dev_no)) {
977 			/* we are a plug-in card */
978 			slot = slot_name_arr[card->dev_no];
979 			if (strlen(slot) != 0) {
980 				(void) sprintf(card->slot_str, "%s",
981 				    slot);
982 			} else
983 				(void) sprintf(card->slot_str, "-");
984 		} else {
985 			/* this is an on-board dev. */
986 			sprintf(card->slot_str, "On-Board");
987 		}
988 
989 	} else {
990 		(void) sprintf(card->slot_str, "%c", '-');
991 	}
992 
993 	/* Informs display_io_cards to print slot_str instead of slot */
994 	card->slot = PCI_SLOT_IS_STRING;
995 }
996 
997 
998 /*
999  * The output of a number of I/O cards are identical so we need to
1000  * differentiate between them.
1001  *
1002  * This function is called by the platform specific code and it decides
1003  * if the card needs further processing.
1004  *
1005  * It can be extended in the future if card types other than QLC have
1006  * the same problems.
1007  */
1008 void
1009 distinguish_identical_io_cards(char *name, Prom_node *node,
1010 				struct io_card *card)
1011 {
1012 	if ((name == NULL) || (node == NULL))
1013 		return;
1014 
1015 	if (strcmp(name, "SUNW,qlc") == 0)
1016 		decode_qlc_card_model_prop(node, card);
1017 }
1018 
1019 
1020 /*
1021  * The name/model properties for a number of the QLC FCAL PCI cards are
1022  * identical (*), so we need to distinguish them using the subsystem-id
1023  * and modify the model string to be more informative.
1024  *
1025  * (*) Currently the problem cards are:
1026  *	Amber
1027  *	Crystal+
1028  */
1029 void
1030 decode_qlc_card_model_prop(Prom_node *card_node, struct io_card *card)
1031 {
1032 	void	*value = NULL;
1033 
1034 	if (card_node == NULL)
1035 		return;
1036 
1037 	value = get_prop_val(find_prop(card_node, "subsystem-id"));
1038 	if (value != NULL) {
1039 		int	id = *(int *)value;
1040 
1041 		switch (id) {
1042 		case AMBER_SUBSYSTEM_ID:
1043 			(void) snprintf(card->model, MAX_QLC_MODEL_LEN, "%s",
1044 				AMBER_CARD_NAME);
1045 			break;
1046 
1047 		case CRYSTAL_SUBSYSTEM_ID:
1048 			(void) snprintf(card->model, MAX_QLC_MODEL_LEN, "%s",
1049 				CRYSTAL_CARD_NAME);
1050 			break;
1051 
1052 		default:
1053 			/*
1054 			 * If information has been saved into the model field
1055 			 * before this function was called we will keep it as
1056 			 * it probably will be more meaningful that the
1057 			 * subsystem-id, otherwise we save the subsystem-id in
1058 			 * the hope that it will distinguish the cards.
1059 			 */
1060 			if (strcmp(card->model, "") == 0) {
1061 				(void) snprintf(card->model, MAX_QLC_MODEL_LEN,
1062 					"0x%x", id);
1063 			}
1064 			break;
1065 		}
1066 	}
1067 }
1068