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