xref: /illumos-gate/usr/src/lib/libprtdiag/common/io.c (revision 6fb59094)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License, Version 1.0 only
603831d35Sstevel  * (the "License").  You may not use this file except in compliance
703831d35Sstevel  * with the License.
803831d35Sstevel  *
903831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1003831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1103831d35Sstevel  * See the License for the specific language governing permissions
1203831d35Sstevel  * and limitations under the License.
1303831d35Sstevel  *
1403831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1503831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1603831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1703831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1803831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1903831d35Sstevel  *
2003831d35Sstevel  * CDDL HEADER END
2103831d35Sstevel  */
2203831d35Sstevel /*
2303831d35Sstevel  * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
2403831d35Sstevel  * All rights reserved.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel #include <stdio.h>
2803831d35Sstevel #include <stdlib.h>
2903831d35Sstevel #include <unistd.h>
3003831d35Sstevel #include <ctype.h>
3103831d35Sstevel #include <string.h>
3203831d35Sstevel #include <kvm.h>
3303831d35Sstevel #include <varargs.h>
3403831d35Sstevel #include <errno.h>
3503831d35Sstevel #include <time.h>
3603831d35Sstevel #include <dirent.h>
3703831d35Sstevel #include <fcntl.h>
3803831d35Sstevel #include <sys/param.h>
3903831d35Sstevel #include <sys/stat.h>
4003831d35Sstevel #include <sys/types.h>
4103831d35Sstevel #include <sys/utsname.h>
4203831d35Sstevel #include <sys/openpromio.h>
4303831d35Sstevel #include <sys/systeminfo.h>
4403831d35Sstevel #include <kstat.h>
4503831d35Sstevel #include <libintl.h>
4603831d35Sstevel #include <syslog.h>
4703831d35Sstevel #include <sys/dkio.h>
4803831d35Sstevel #include "pdevinfo.h"
4903831d35Sstevel #include "display.h"
5003831d35Sstevel #include "pdevinfo_sun4u.h"
5103831d35Sstevel #include "display_sun4u.h"
5203831d35Sstevel #include "libprtdiag.h"
5303831d35Sstevel 
5403831d35Sstevel #if !defined(TEXT_DOMAIN)
5503831d35Sstevel #define	TEXT_DOMAIN	"SYS_TEST"
5603831d35Sstevel #endif
5703831d35Sstevel 
5803831d35Sstevel Prom_node *
find_pci_bus(Prom_node * node,int id,int bus)5903831d35Sstevel find_pci_bus(Prom_node *node, int id, int bus)
6003831d35Sstevel {
6103831d35Sstevel 	Prom_node *pnode;
6203831d35Sstevel 
6303831d35Sstevel 	/* find the first pci node */
6403831d35Sstevel 	pnode = dev_find_node(node, "pci");
6503831d35Sstevel 
6603831d35Sstevel 	while (pnode != NULL) {
6703831d35Sstevel 		int tmp_id;
6803831d35Sstevel 		int tmp_bus;
6903831d35Sstevel 
7003831d35Sstevel 		tmp_id = get_id(pnode);
7103831d35Sstevel 		tmp_bus = get_pci_bus(pnode);
7203831d35Sstevel 
7303831d35Sstevel 		if ((tmp_id == id) &&
7403831d35Sstevel 		    (tmp_bus == bus)) {
7503831d35Sstevel 			break;
7603831d35Sstevel 		}
7703831d35Sstevel 
7803831d35Sstevel 		pnode = dev_next_node(pnode, "pci");
7903831d35Sstevel 	}
8003831d35Sstevel 	return (pnode);
8103831d35Sstevel }
8203831d35Sstevel 
8303831d35Sstevel /*
8403831d35Sstevel  * get_pci_bus
8503831d35Sstevel  *
8603831d35Sstevel  * Determines the PCI bus, either A (0) or B (1). If the function cannot
8703831d35Sstevel  * find the bus-ranges property, it returns -1.
8803831d35Sstevel  */
8903831d35Sstevel int
get_pci_bus(Prom_node * pnode)9003831d35Sstevel get_pci_bus(Prom_node *pnode)
9103831d35Sstevel {
9203831d35Sstevel 	int *value;
9303831d35Sstevel 
9403831d35Sstevel 	/* look up the bus-range property */
9503831d35Sstevel 	if ((value = (int *)get_prop_val(find_prop(pnode, "bus-range"))) ==
9603831d35Sstevel 	    NULL) {
9703831d35Sstevel 		return (-1);
9803831d35Sstevel 	}
9903831d35Sstevel 
10003831d35Sstevel 	if (*value == 0) {
10103831d35Sstevel 		return (1);	/* B bus has a bus-range value = 0 */
10203831d35Sstevel 	} else {
10303831d35Sstevel 		return (0);
10403831d35Sstevel 	}
10503831d35Sstevel }
10603831d35Sstevel 
10703831d35Sstevel 
10803831d35Sstevel 
10903831d35Sstevel /*
11003831d35Sstevel  * Find the PCI device number of this PCI device. If no device number can
11103831d35Sstevel  * be determined, then return -1.
11203831d35Sstevel  */
11303831d35Sstevel int
get_pci_device(Prom_node * pnode)11403831d35Sstevel get_pci_device(Prom_node *pnode)
11503831d35Sstevel {
11603831d35Sstevel 	void *value;
11703831d35Sstevel 
118*6fb59094SToomas Soome 	value = get_prop_val(find_prop(pnode, "assigned-addresses"));
119*6fb59094SToomas Soome 	if (value != NULL) {
12003831d35Sstevel 		return (PCI_DEVICE(*(int *)value));
12103831d35Sstevel 	} else {
12203831d35Sstevel 		return (-1);
12303831d35Sstevel 	}
12403831d35Sstevel }
12503831d35Sstevel 
12603831d35Sstevel /*
12703831d35Sstevel  * Find the PCI device number of this PCI device. If no device number can
12803831d35Sstevel  * be determined, then return -1.
12903831d35Sstevel  */
13003831d35Sstevel int
get_pci_to_pci_device(Prom_node * pnode)13103831d35Sstevel get_pci_to_pci_device(Prom_node *pnode)
13203831d35Sstevel {
13303831d35Sstevel 	void *value;
13403831d35Sstevel 
135*6fb59094SToomas Soome 	value = get_prop_val(find_prop(pnode, "reg"));
136*6fb59094SToomas Soome 	if (value != NULL) {
13703831d35Sstevel 		return (PCI_DEVICE(*(int *)value));
13803831d35Sstevel 	} else {
13903831d35Sstevel 		return (-1);
14003831d35Sstevel 	}
14103831d35Sstevel }
14203831d35Sstevel 
14303831d35Sstevel /*
14403831d35Sstevel  * free_io_cards
14503831d35Sstevel  * Frees the memory allocated for an io card list.
14603831d35Sstevel  */
14703831d35Sstevel void
free_io_cards(struct io_card * card_list)14803831d35Sstevel free_io_cards(struct io_card *card_list)
14903831d35Sstevel {
15003831d35Sstevel 	/* Free the list */
15103831d35Sstevel 	if (card_list != NULL) {
15203831d35Sstevel 		struct io_card *p, *q;
15303831d35Sstevel 
15403831d35Sstevel 		for (p = card_list, q = NULL; p != NULL; p = q) {
15503831d35Sstevel 			q = p->next;
15603831d35Sstevel 			free(p);
15703831d35Sstevel 		}
15803831d35Sstevel 	}
15903831d35Sstevel }
16003831d35Sstevel 
16103831d35Sstevel 
16203831d35Sstevel /*
16303831d35Sstevel  * insert_io_card
16403831d35Sstevel  * Inserts an io_card structure into the list.  The list is maintained
16503831d35Sstevel  * in order based on board number and slot number.  Also, the storage
16603831d35Sstevel  * for the "card" argument is assumed to be handled by the caller,
16703831d35Sstevel  * so we won't touch it.
16803831d35Sstevel  */
16903831d35Sstevel struct io_card *
insert_io_card(struct io_card * list,struct io_card * card)17003831d35Sstevel insert_io_card(struct io_card *list, struct io_card *card)
17103831d35Sstevel {
17203831d35Sstevel 	struct io_card *newcard;
17303831d35Sstevel 	struct io_card *p, *q;
17403831d35Sstevel 
17503831d35Sstevel 	if (card == NULL)
17603831d35Sstevel 		return (list);
17703831d35Sstevel 
17803831d35Sstevel 	/* Copy the card to be added into new storage */
17903831d35Sstevel 	newcard = (struct io_card *)malloc(sizeof (struct io_card));
18003831d35Sstevel 	if (newcard == NULL) {
18103831d35Sstevel 		perror("malloc");
18203831d35Sstevel 		exit(2);
18303831d35Sstevel 	}
18403831d35Sstevel 	(void) memcpy(newcard, card, sizeof (struct io_card));
18503831d35Sstevel 	newcard->next = NULL;
18603831d35Sstevel 
18703831d35Sstevel 	if (list == NULL)
188*6fb59094SToomas Soome 		return (newcard);
18903831d35Sstevel 
19003831d35Sstevel 	/* Find the proper place in the list for the new card */
19103831d35Sstevel 	for (p = list, q = NULL; p != NULL; q = p, p = p->next) {
19203831d35Sstevel 		if (newcard->board < p->board)
19303831d35Sstevel 			break;
19403831d35Sstevel 		if ((newcard->board == p->board) && (newcard->slot < p->slot))
19503831d35Sstevel 			break;
19603831d35Sstevel 	}
19703831d35Sstevel 
19803831d35Sstevel 	/* Insert the new card into the list */
19903831d35Sstevel 	if (q == NULL) {
20003831d35Sstevel 		newcard->next = p;
20103831d35Sstevel 		return (newcard);
20203831d35Sstevel 	} else {
20303831d35Sstevel 		newcard->next = p;
20403831d35Sstevel 		q->next = newcard;
20503831d35Sstevel 		return (list);
20603831d35Sstevel 	}
20703831d35Sstevel }
20803831d35Sstevel 
20903831d35Sstevel 
21003831d35Sstevel char *
fmt_manf_id(unsigned int encoded_id,char * outbuf)21103831d35Sstevel fmt_manf_id(unsigned int encoded_id, char *outbuf)
21203831d35Sstevel {
21303831d35Sstevel 	union manuf manuf;
21403831d35Sstevel 
21503831d35Sstevel 	/*
21603831d35Sstevel 	 * Format the manufacturer's info.  Note a small inconsistency we
21703831d35Sstevel 	 * have to work around - Brooktree has it's part number in decimal,
21803831d35Sstevel 	 * while Mitsubishi has it's part number in hex.
21903831d35Sstevel 	 */
22003831d35Sstevel 	manuf.encoded_id = encoded_id;
22103831d35Sstevel 	switch (manuf.fld.manf) {
22203831d35Sstevel 	case MANF_BROOKTREE:
22303831d35Sstevel 		(void) sprintf(outbuf, "%s %d, version %d", "Brooktree",
224*6fb59094SToomas Soome 		    manuf.fld.partno, manuf.fld.version);
22503831d35Sstevel 		break;
22603831d35Sstevel 
22703831d35Sstevel 	case MANF_MITSUBISHI:
22803831d35Sstevel 		(void) sprintf(outbuf, "%s %x, version %d", "Mitsubishi",
229*6fb59094SToomas Soome 		    manuf.fld.partno, manuf.fld.version);
23003831d35Sstevel 		break;
23103831d35Sstevel 
23203831d35Sstevel 	default:
23303831d35Sstevel 		(void) sprintf(outbuf, "JED code %d, Part num 0x%x, version %d",
234*6fb59094SToomas Soome 		    manuf.fld.manf, manuf.fld.partno, manuf.fld.version);
23503831d35Sstevel 	}
23603831d35Sstevel 	return (outbuf);
23703831d35Sstevel }
23803831d35Sstevel 
23903831d35Sstevel 
24003831d35Sstevel /*
24103831d35Sstevel  * Find the sbus slot number of this Sbus device. If no slot number can
24203831d35Sstevel  * be determined, then return -1.
24303831d35Sstevel  */
24403831d35Sstevel int
get_sbus_slot(Prom_node * pnode)24503831d35Sstevel get_sbus_slot(Prom_node *pnode)
24603831d35Sstevel {
24703831d35Sstevel 	void *value;
24803831d35Sstevel 
24903831d35Sstevel 	if ((value = get_prop_val(find_prop(pnode, "reg"))) != NULL) {
25003831d35Sstevel 		return (*(int *)value);
25103831d35Sstevel 	} else {
25203831d35Sstevel 		return (-1);
25303831d35Sstevel 	}
25403831d35Sstevel }
25503831d35Sstevel 
25603831d35Sstevel 
25703831d35Sstevel /*
25803831d35Sstevel  * This routine is the generic link into displaying system IO
25903831d35Sstevel  * configuration. It displays the table header, then displays
26003831d35Sstevel  * all the SBus cards, then displays all fo the PCI IO cards.
26103831d35Sstevel  */
26203831d35Sstevel void
display_io_devices(Sys_tree * tree)26303831d35Sstevel display_io_devices(Sys_tree *tree)
26403831d35Sstevel {
26503831d35Sstevel 	Board_node *bnode;
26603831d35Sstevel 
26703831d35Sstevel 	/*
26803831d35Sstevel 	 * TRANSLATION_NOTE
26903831d35Sstevel 	 * Following string is used as a table header.
27003831d35Sstevel 	 * Please maintain the current alignment in
27103831d35Sstevel 	 * translation.
27203831d35Sstevel 	 */
27303831d35Sstevel 	log_printf("\n", 0);
27403831d35Sstevel 	log_printf("=========================", 0);
27503831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN, " IO Cards "), 0);
27603831d35Sstevel 	log_printf("=========================", 0);
27703831d35Sstevel 	log_printf("\n", 0);
27803831d35Sstevel 	log_printf("\n", 0);
27903831d35Sstevel 	bnode = tree->bd_list;
28003831d35Sstevel 	while (bnode != NULL) {
28103831d35Sstevel 		display_sbus(bnode);
28203831d35Sstevel 		display_pci(bnode);
28303831d35Sstevel 		display_ffb(bnode, 1);
28403831d35Sstevel 		bnode = bnode->next;
28503831d35Sstevel 	}
28603831d35Sstevel }
28703831d35Sstevel 
28803831d35Sstevel void
display_pci(Board_node * bnode)28903831d35Sstevel display_pci(Board_node *bnode)
29003831d35Sstevel {
29103831d35Sstevel #ifdef  lint
29203831d35Sstevel 	bnode = bnode;
29303831d35Sstevel #endif
29403831d35Sstevel 	/*
29503831d35Sstevel 	 * This function is intentionally empty
29603831d35Sstevel 	 */
29703831d35Sstevel }
29803831d35Sstevel 
29903831d35Sstevel 
30003831d35Sstevel /*
30103831d35Sstevel  * Print out all the io cards in the list.  Also print the column
30203831d35Sstevel  * headers if told to do so.
30303831d35Sstevel  */
30403831d35Sstevel void
display_io_cards(struct io_card * list)30503831d35Sstevel display_io_cards(struct io_card *list)
30603831d35Sstevel {
30703831d35Sstevel 	static int banner = 0; /* Have we printed the column headings? */
30803831d35Sstevel 	struct io_card *p;
30903831d35Sstevel 
31003831d35Sstevel 	if (list == NULL)
31103831d35Sstevel 		return;
31203831d35Sstevel 
31303831d35Sstevel 	if (banner == 0) {
31403831d35Sstevel 		log_printf("     Bus   Freq\n", 0);
31503831d35Sstevel 		log_printf("Brd  Type  MHz   Slot        "
316*6fb59094SToomas Soome 		    "Name                          "
317*6fb59094SToomas Soome 		    "Model", 0);
31803831d35Sstevel 		log_printf("\n", 0);
31903831d35Sstevel 		log_printf("---  ----  ----  ----------  "
320*6fb59094SToomas Soome 		    "----------------------------  "
321*6fb59094SToomas Soome 		    "--------------------", 0);
32203831d35Sstevel 		log_printf("\n", 0);
32303831d35Sstevel 		banner = 1;
32403831d35Sstevel 	}
32503831d35Sstevel 
32603831d35Sstevel 	for (p = list; p != NULL; p = p -> next) {
32703831d35Sstevel 		log_printf("%2d   ", p->board, 0);
32803831d35Sstevel 		log_printf("%-4s  ", p->bus_type, 0);
32903831d35Sstevel 		log_printf("%3d   ", p->freq, 0);
33003831d35Sstevel 		/*
33103831d35Sstevel 		 * We check to see if it's an int or
33203831d35Sstevel 		 * a char string to display for slot.
33303831d35Sstevel 		 */
33403831d35Sstevel 		if (p->slot == PCI_SLOT_IS_STRING)
33503831d35Sstevel 			log_printf("%10s  ", p->slot_str, 0);
33603831d35Sstevel 		else
33703831d35Sstevel 			log_printf("%10d  ", p->slot, 0);
33803831d35Sstevel 
33903831d35Sstevel 		log_printf("%-28.28s", p->name, 0);
34003831d35Sstevel 		if (strlen(p->name) > 28)
34103831d35Sstevel 			log_printf("+ ", 0);
34203831d35Sstevel 		else
34303831d35Sstevel 			log_printf("  ", 0);
34403831d35Sstevel 		log_printf("%-19.19s", p->model, 0);
34503831d35Sstevel 		if (strlen(p->model) > 19)
34603831d35Sstevel 			log_printf("+", 0);
34703831d35Sstevel 		log_printf("\n", 0);
34803831d35Sstevel 	}
34903831d35Sstevel }
35003831d35Sstevel 
35103831d35Sstevel /*
35203831d35Sstevel  * Display all FFBs on this board.  It can either be in tabular format,
35303831d35Sstevel  * or a more verbose format.
35403831d35Sstevel  */
35503831d35Sstevel void
display_ffb(Board_node * board,int table)35603831d35Sstevel display_ffb(Board_node *board, int table)
35703831d35Sstevel {
35803831d35Sstevel 	Prom_node *fb;
35903831d35Sstevel 	void *value;
36003831d35Sstevel 	struct io_card *card_list = NULL;
36103831d35Sstevel 	struct io_card card;
36203831d35Sstevel 	char *type;
36303831d35Sstevel 	char *label;
36403831d35Sstevel 
36503831d35Sstevel 	if (board == NULL)
36603831d35Sstevel 		return;
36703831d35Sstevel 
36803831d35Sstevel 	/* Fill in common information */
36903831d35Sstevel 	card.display = 1;
37003831d35Sstevel 	card.board = board->board_num;
37103831d35Sstevel 	(void) sprintf(card.bus_type, BUS_TYPE);
37203831d35Sstevel 	card.freq = sys_clk;
37303831d35Sstevel 
37403831d35Sstevel 	for (fb = dev_find_node_by_type(board->nodes, "device_type", "display");
37503831d35Sstevel 	    fb != NULL;
37603831d35Sstevel 	    fb = dev_next_node_by_type(fb, "device_type", "display")) {
37703831d35Sstevel 		value = get_prop_val(find_prop(fb, "name"));
37803831d35Sstevel 		if (value != NULL) {
37903831d35Sstevel 			if ((strcmp(FFB_NAME, value)) == 0) {
38003831d35Sstevel 				type = FFB_NAME;
38103831d35Sstevel 				label = "FFB";
38203831d35Sstevel 			} else if ((strcmp(AFB_NAME, value)) == 0) {
38303831d35Sstevel 				type = AFB_NAME;
38403831d35Sstevel 				label = "AFB";
38503831d35Sstevel 			} else
38603831d35Sstevel 				continue;
38703831d35Sstevel 		} else
38803831d35Sstevel 			continue;
38903831d35Sstevel 		if (table == 1) {
39003831d35Sstevel 			/* Print out in table format */
39103831d35Sstevel 
39203831d35Sstevel 			/* XXX - Get the slot number (hack) */
39303831d35Sstevel 			card.slot = get_id(fb);
39403831d35Sstevel 
39503831d35Sstevel 			/* Find out if it's single or double buffered */
39603831d35Sstevel 			(void) sprintf(card.name, "%s", label);
39703831d35Sstevel 			value = get_prop_val(find_prop(fb, "board_type"));
39803831d35Sstevel 			if (value != NULL)
39903831d35Sstevel 				if ((*(int *)value) & FFB_B_BUFF)
40003831d35Sstevel 					(void) sprintf(card.name,
401*6fb59094SToomas Soome 					    "%s, Double Buffered", label);
40203831d35Sstevel 				else
40303831d35Sstevel 					(void) sprintf(card.name,
404*6fb59094SToomas Soome 					    "%s, Single Buffered", label);
40503831d35Sstevel 
40603831d35Sstevel 			/*
40703831d35Sstevel 			 * Print model number only if board_type bit 2
40803831d35Sstevel 			 * is not set and it is not SUNW,XXX-XXXX.
40903831d35Sstevel 			 */
41003831d35Sstevel 			card.model[0] = '\0';
41103831d35Sstevel 
41203831d35Sstevel 			if (strcmp(type, AFB_NAME) == 0) {
41303831d35Sstevel 				if (((*(int *)value) & 0x4) != 0x4) {
41403831d35Sstevel 					value = get_prop_val(find_prop(fb,
415*6fb59094SToomas Soome 					    "model"));
41603831d35Sstevel 					if ((value != NULL) &&
41703831d35Sstevel 					    (strcmp(value,
41803831d35Sstevel 					    "SUNW,XXX-XXXX") != 0)) {
41903831d35Sstevel 						(void) sprintf(card.model, "%s",
42003831d35Sstevel 						    (char *)value);
42103831d35Sstevel 					}
42203831d35Sstevel 				}
42303831d35Sstevel 			} else {
42403831d35Sstevel 				value = get_prop_val(find_prop(fb, "model"));
42503831d35Sstevel 				if (value != NULL)
42603831d35Sstevel 					(void) sprintf(card.model, "%s",
42703831d35Sstevel 					    (char *)value);
42803831d35Sstevel 			}
42903831d35Sstevel 
43003831d35Sstevel 			card_list = insert_io_card(card_list, &card);
43103831d35Sstevel 		} else {
43203831d35Sstevel 			/* print in long format */
43303831d35Sstevel 			char device[MAXSTRLEN];
43403831d35Sstevel 			int fd = -1;
43503831d35Sstevel 			struct dirent *direntp;
43603831d35Sstevel 			DIR *dirp;
43703831d35Sstevel 			union strap_un strap;
43803831d35Sstevel 			struct ffb_sys_info fsi;
43903831d35Sstevel 
44003831d35Sstevel 			/* Find the device node using upa-portid/portid */
44103831d35Sstevel 			value = get_prop_val(find_prop(fb, "upa-portid"));
44203831d35Sstevel 			if (value == NULL)
44303831d35Sstevel 				value = get_prop_val(find_prop(fb, "portid"));
44403831d35Sstevel 
44503831d35Sstevel 			if (value == NULL)
446*6fb59094SToomas Soome 				continue;
44703831d35Sstevel 
44803831d35Sstevel 			(void) sprintf(device, "%s@%x", type,
449*6fb59094SToomas Soome 			    *(int *)value);
45003831d35Sstevel 			if ((dirp = opendir("/devices")) == NULL)
45103831d35Sstevel 				continue;
45203831d35Sstevel 
45303831d35Sstevel 			while ((direntp = readdir(dirp)) != NULL) {
45403831d35Sstevel 				if (strstr(direntp->d_name, device) != NULL) {
45503831d35Sstevel 					(void) sprintf(device, "/devices/%s",
456*6fb59094SToomas Soome 					    direntp->d_name);
45703831d35Sstevel 					fd = open(device, O_RDWR, 0666);
45803831d35Sstevel 					break;
45903831d35Sstevel 				}
46003831d35Sstevel 			}
46103831d35Sstevel 			(void) closedir(dirp);
46203831d35Sstevel 
46303831d35Sstevel 			if (fd == -1)
46403831d35Sstevel 				continue;
46503831d35Sstevel 
46603831d35Sstevel 			if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0)
46703831d35Sstevel 				continue;
46803831d35Sstevel 
46903831d35Sstevel 			log_printf("%s Hardware Configuration:\n", label, 0);
47003831d35Sstevel 			log_printf("-----------------------------------\n", 0);
47103831d35Sstevel 
47203831d35Sstevel 			strap.ffb_strap_bits = fsi.ffb_strap_bits;
47303831d35Sstevel 			log_printf("\tBoard rev: %d\n",
474*6fb59094SToomas Soome 			    (int)strap.fld.board_rev, 0);
47503831d35Sstevel 			log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0);
47603831d35Sstevel 			log_printf("\tDAC: %s\n",
477*6fb59094SToomas Soome 			    fmt_manf_id(fsi.dac_version, device), 0);
47803831d35Sstevel 			log_printf("\t3DRAM: %s\n",
479*6fb59094SToomas Soome 			    fmt_manf_id(fsi.fbram_version, device), 0);
48003831d35Sstevel 			log_printf("\n", 0);
48103831d35Sstevel 		}
48203831d35Sstevel 
48303831d35Sstevel 	}
48403831d35Sstevel 	display_io_cards(card_list);
48503831d35Sstevel 	free_io_cards(card_list);
48603831d35Sstevel }
48703831d35Sstevel 
48803831d35Sstevel 
48903831d35Sstevel /*
49003831d35Sstevel  * Display all the SBus IO cards on this board.
49103831d35Sstevel  */
49203831d35Sstevel void
display_sbus(Board_node * board)49303831d35Sstevel display_sbus(Board_node *board)
49403831d35Sstevel {
49503831d35Sstevel 	struct io_card card;
49603831d35Sstevel 	struct io_card *card_list = NULL;
49703831d35Sstevel 	int freq;
49803831d35Sstevel 	int card_num;
49903831d35Sstevel 	void *value;
50003831d35Sstevel 	Prom_node *sbus;
50103831d35Sstevel 	Prom_node *card_node;
50203831d35Sstevel 
50303831d35Sstevel 	if (board == NULL)
50403831d35Sstevel 		return;
50503831d35Sstevel 
50603831d35Sstevel 	for (sbus = dev_find_node(board->nodes, SBUS_NAME); sbus != NULL;
50703831d35Sstevel 	    sbus = dev_next_node(sbus, SBUS_NAME)) {
50803831d35Sstevel 
50903831d35Sstevel 		/* Skip failed nodes for now */
51003831d35Sstevel 		if (node_failed(sbus))
51103831d35Sstevel 			continue;
51203831d35Sstevel 
51303831d35Sstevel 		/* Calculate SBus frequency in MHz */
51403831d35Sstevel 		value = get_prop_val(find_prop(sbus, "clock-frequency"));
51503831d35Sstevel 		if (value != NULL)
51603831d35Sstevel 			freq = ((*(int *)value) + 500000) / 1000000;
51703831d35Sstevel 		else
51803831d35Sstevel 			freq = -1;
51903831d35Sstevel 
52003831d35Sstevel 		for (card_node = sbus->child; card_node != NULL;
52103831d35Sstevel 		    card_node = card_node->sibling) {
52203831d35Sstevel 			char *model;
52303831d35Sstevel 			char *name;
52403831d35Sstevel 			char *child_name;
52503831d35Sstevel 
52603831d35Sstevel 			card_num = get_sbus_slot(card_node);
52703831d35Sstevel 			if (card_num == -1)
52803831d35Sstevel 				continue;
52903831d35Sstevel 
53003831d35Sstevel 			/* Fill in card information */
53103831d35Sstevel 			card.display = 1;
53203831d35Sstevel 			card.freq = freq;
53303831d35Sstevel 			card.board = board->board_num;
53403831d35Sstevel 			(void) sprintf(card.bus_type, "SBus");
53503831d35Sstevel 			card.slot = card_num;
53603831d35Sstevel 			card.status[0] = '\0';
53703831d35Sstevel 
53803831d35Sstevel 			/* Try and get card status */
53903831d35Sstevel 			value = get_prop_val(find_prop(card_node, "status"));
54003831d35Sstevel 			if (value != NULL)
54103831d35Sstevel 				(void) strncpy(card.status, (char *)value,
542*6fb59094SToomas Soome 				    MAXSTRLEN);
54303831d35Sstevel 
54403831d35Sstevel 			/* XXX - For now, don't display failed cards */
54503831d35Sstevel 			if (strstr(card.status, "fail") != NULL)
54603831d35Sstevel 				continue;
54703831d35Sstevel 
54803831d35Sstevel 			/* Now gather all of the node names for that card */
54903831d35Sstevel 			model = (char *)get_prop_val(find_prop(card_node,
550*6fb59094SToomas Soome 			    "model"));
55103831d35Sstevel 			name = get_node_name(card_node);
55203831d35Sstevel 
55303831d35Sstevel 			if (name == NULL)
55403831d35Sstevel 				continue;
55503831d35Sstevel 
55603831d35Sstevel 			card.name[0] = '\0';
55703831d35Sstevel 			card.model[0] = '\0';
55803831d35Sstevel 
55903831d35Sstevel 			/* Figure out how we want to display the name */
56003831d35Sstevel 			child_name = get_node_name(card_node->child);
56103831d35Sstevel 			if ((card_node->child != NULL) &&
56203831d35Sstevel 			    (child_name != NULL)) {
56303831d35Sstevel 				value = get_prop_val(find_prop(card_node->child,
564*6fb59094SToomas Soome 				    "device_type"));
56503831d35Sstevel 				if (value != NULL)
56603831d35Sstevel 					(void) sprintf(card.name, "%s/%s (%s)",
567*6fb59094SToomas Soome 					    name, child_name,
568*6fb59094SToomas Soome 					    (char *)value);
56903831d35Sstevel 				else
57003831d35Sstevel 					(void) sprintf(card.name, "%s/%s", name,
571*6fb59094SToomas Soome 					    child_name);
57203831d35Sstevel 			} else {
57303831d35Sstevel 				(void) strncpy(card.name, name, MAXSTRLEN);
57403831d35Sstevel 			}
57503831d35Sstevel 
57603831d35Sstevel 			if (model != NULL)
57703831d35Sstevel 				(void) strncpy(card.model, model, MAXSTRLEN);
57803831d35Sstevel 
57903831d35Sstevel 			card_list = insert_io_card(card_list, &card);
58003831d35Sstevel 		}
58103831d35Sstevel 	}
58203831d35Sstevel 
58303831d35Sstevel 	/* We're all done gathering card info, now print it out */
58403831d35Sstevel 	display_io_cards(card_list);
58503831d35Sstevel 	free_io_cards(card_list);
58603831d35Sstevel }
58703831d35Sstevel 
58803831d35Sstevel 
58903831d35Sstevel /*
59003831d35Sstevel  * Get slot-names properties from parent node and
59103831d35Sstevel  * store them in an array.
59203831d35Sstevel  */
59303831d35Sstevel int
populate_slot_name_arr(Prom_node * pci,int * slot_name_bits,char ** slot_name_arr,int num_slots)59403831d35Sstevel populate_slot_name_arr(Prom_node *pci, int *slot_name_bits,
595*6fb59094SToomas Soome     char **slot_name_arr, int num_slots)
59603831d35Sstevel {
59703831d35Sstevel 	int	i, j, bit_mask;
59803831d35Sstevel 	char	*value;
59903831d35Sstevel 
60003831d35Sstevel 	value = (char *)get_prop_val(find_prop(pci, "slot-names"));
60103831d35Sstevel 	D_PRINTF("\n populate_slot_name_arr: value = [0x%x]\n", value);
60203831d35Sstevel 
60303831d35Sstevel 	if (value != NULL) {
60403831d35Sstevel 		char	*strings_arr[MAX_SLOTS_PER_IO_BD];
60503831d35Sstevel 		bit_mask = *slot_name_bits = *(int *)value;
60603831d35Sstevel 		D_PRINTF("\nslot_names 1st integer = [0x%x]", *slot_name_bits);
60703831d35Sstevel 
60803831d35Sstevel 		/* array starts after first int */
60903831d35Sstevel 		strings_arr[0] = value + sizeof (int);
61003831d35Sstevel 
61103831d35Sstevel 		/*
61203831d35Sstevel 		 * break the array out into num_slots number of strings
61303831d35Sstevel 		 */
61403831d35Sstevel 		for (i = 1; i < num_slots; i++) {
61503831d35Sstevel 			strings_arr[i] = (char *)strings_arr[i - 1]
61603831d35Sstevel 			    + strlen(strings_arr[i - 1]) + 1;
61703831d35Sstevel 		}
61803831d35Sstevel 
61903831d35Sstevel 		/*
62003831d35Sstevel 		 * process array of slot_names to remove blanks
62103831d35Sstevel 		 */
62203831d35Sstevel 		j = 0;
62303831d35Sstevel 		for (i = 0; i < num_slots; i++) {
62403831d35Sstevel 			if ((bit_mask >> i) & 0x1)
62503831d35Sstevel 				slot_name_arr[i] = strings_arr[j++];
62603831d35Sstevel 			else
62703831d35Sstevel 				slot_name_arr[i] = "";
62803831d35Sstevel 
62903831d35Sstevel 			D_PRINTF("\nslot_name_arr[%d] = [%s]", i,
630*6fb59094SToomas Soome 			    slot_name_arr[i]);
63103831d35Sstevel 		}
63203831d35Sstevel 		return (0);
63303831d35Sstevel 	} else {
63403831d35Sstevel 		D_PRINTF("\n populate_slot_name_arr: - psycho with no "
63503831d35Sstevel 		    "slot-names\n");
63603831d35Sstevel 		return (0);
63703831d35Sstevel 	}
63803831d35Sstevel }
63903831d35Sstevel 
64003831d35Sstevel int
get_card_frequency(Prom_node * pci)64103831d35Sstevel get_card_frequency(Prom_node *pci)
64203831d35Sstevel {
64303831d35Sstevel 	char	*value = get_prop_val(find_prop(pci, "clock-frequency"));
64403831d35Sstevel 
64503831d35Sstevel 	if (value == NULL)
64603831d35Sstevel 		return (-1);
64703831d35Sstevel 	else
64803831d35Sstevel 		return (int)(((*(int *)value) + 500000) / 1000000);
64903831d35Sstevel 
65003831d35Sstevel }
65103831d35Sstevel 
65203831d35Sstevel void
get_dev_func_num(Prom_node * card_node,int * dev_no,int * func_no)65303831d35Sstevel get_dev_func_num(Prom_node *card_node, int *dev_no, int *func_no)
65403831d35Sstevel {
65503831d35Sstevel 
65603831d35Sstevel 	void	*value = get_prop_val(find_prop(card_node, "reg"));
65703831d35Sstevel 
65803831d35Sstevel 	if (value != NULL) {
65903831d35Sstevel 		int int_val = *(int *)value;
66003831d35Sstevel 		*dev_no = PCI_REG_TO_DEV(int_val);
66103831d35Sstevel 		*func_no = PCI_REG_TO_FUNC(int_val);
66203831d35Sstevel 	} else {
66303831d35Sstevel 		*dev_no = -1;
66403831d35Sstevel 		*func_no = -1;
66503831d35Sstevel 	}
66603831d35Sstevel }
66703831d35Sstevel 
66803831d35Sstevel void
get_pci_class_codes(Prom_node * card_node,int * class_code,int * subclass_code)66903831d35Sstevel get_pci_class_codes(Prom_node *card_node, int *class_code, int *subclass_code)
67003831d35Sstevel {
67103831d35Sstevel 	int	class_code_reg = get_pci_class_code_reg(card_node);
67203831d35Sstevel 
67303831d35Sstevel 	*class_code = CLASS_REG_TO_CLASS(class_code_reg);
67403831d35Sstevel 	*subclass_code = CLASS_REG_TO_SUBCLASS(class_code_reg);
67503831d35Sstevel }
67603831d35Sstevel 
67703831d35Sstevel int
is_pci_bridge(Prom_node * card_node,char * name)67803831d35Sstevel is_pci_bridge(Prom_node *card_node, char *name)
67903831d35Sstevel {
68003831d35Sstevel 	int class_code, subclass_code;
68103831d35Sstevel 
68203831d35Sstevel 	if (card_node == NULL)
68303831d35Sstevel 		return (FALSE);
68403831d35Sstevel 
68503831d35Sstevel 	get_pci_class_codes(card_node, &class_code, &subclass_code);
68603831d35Sstevel 
68703831d35Sstevel 	if ((strncmp(name, "pci", 3) == 0) &&
68803831d35Sstevel 	    (class_code == PCI_BRIDGE_CLASS) &&
68903831d35Sstevel 	    (subclass_code == PCI_PCI_BRIDGE_SUBCLASS))
69003831d35Sstevel 		return (TRUE);
69103831d35Sstevel 	else
69203831d35Sstevel 		return (FALSE);
69303831d35Sstevel }
69403831d35Sstevel 
69503831d35Sstevel int
is_pci_bridge_other(Prom_node * card_node,char * name)69603831d35Sstevel is_pci_bridge_other(Prom_node *card_node, char *name)
69703831d35Sstevel {
69803831d35Sstevel 	int class_code, subclass_code;
69903831d35Sstevel 
70003831d35Sstevel 	if (card_node == NULL)
70103831d35Sstevel 		return (FALSE);
70203831d35Sstevel 
70303831d35Sstevel 	get_pci_class_codes(card_node, &class_code, &subclass_code);
70403831d35Sstevel 
70503831d35Sstevel 	if ((strncmp(name, "pci", 3) == 0) &&
70603831d35Sstevel 	    (class_code == PCI_BRIDGE_CLASS) &&
70703831d35Sstevel 	    (subclass_code == PCI_SUBCLASS_OTHER))
70803831d35Sstevel 		return (TRUE);
70903831d35Sstevel 	else
71003831d35Sstevel 		return (FALSE);
71103831d35Sstevel }
71203831d35Sstevel void
get_pci_card_model(Prom_node * card_node,char * model)71303831d35Sstevel get_pci_card_model(Prom_node *card_node, char *model)
71403831d35Sstevel {
71503831d35Sstevel 	char	*name = get_prop_val(find_prop(card_node, "name"));
71603831d35Sstevel 	char	*value = get_prop_val(find_prop(card_node, "model"));
717*6fb59094SToomas Soome 	int	pci_bridge = is_pci_bridge(card_node, name);
71803831d35Sstevel 
71903831d35Sstevel 	if (value == NULL)
72003831d35Sstevel 		model[0] = '\0';
72103831d35Sstevel 	else
722*6fb59094SToomas Soome 		(void) sprintf(model, "%s", value);
72303831d35Sstevel 
72403831d35Sstevel 	if (pci_bridge) {
72503831d35Sstevel 		if (strlen(model) == 0)
726*6fb59094SToomas Soome 			(void) sprintf(model, "%s", "pci-bridge");
72703831d35Sstevel 		else
728*6fb59094SToomas Soome 			(void) sprintf(model, "%s/pci-bridge", model);
72903831d35Sstevel 	}
73003831d35Sstevel }
73103831d35Sstevel 
73203831d35Sstevel void
create_io_card_name(Prom_node * card_node,char * name,char * card_name)73303831d35Sstevel create_io_card_name(Prom_node *card_node, char *name, char *card_name)
73403831d35Sstevel {
73503831d35Sstevel 	char	*value = get_prop_val(find_prop(card_node, "compatible"));
73603831d35Sstevel 	char	*child_name;
73703831d35Sstevel 	char	buf[MAXSTRLEN];
73803831d35Sstevel 
73903831d35Sstevel 	if (value != NULL) {
740*6fb59094SToomas Soome 		(void) sprintf(buf, "%s-%s", name, value);
74103831d35Sstevel 	} else
74203831d35Sstevel 		(void) sprintf(buf, "%s", name);
74303831d35Sstevel 
74403831d35Sstevel 	name = buf;
74503831d35Sstevel 
74603831d35Sstevel 	child_name = (char *)get_node_name(card_node->child);
74703831d35Sstevel 
74803831d35Sstevel 	if ((card_node->child != NULL) &&
74903831d35Sstevel 	    (child_name != NULL)) {
75003831d35Sstevel 		value = get_prop_val(find_prop(card_node->child,
751*6fb59094SToomas Soome 		    "device_type"));
75203831d35Sstevel 		if (value != NULL)
75303831d35Sstevel 			(void) sprintf(card_name, "%s/%s (%s)",
754*6fb59094SToomas Soome 			    name, child_name, value);
75503831d35Sstevel 		else
75603831d35Sstevel 			(void) sprintf(card_name, "%s/%s", name,
757*6fb59094SToomas Soome 			    child_name);
75803831d35Sstevel 	} else {
759*6fb59094SToomas Soome 		(void) sprintf(card_name, "%s", name);
76003831d35Sstevel 	}
76103831d35Sstevel }
76203831d35Sstevel 
76303831d35Sstevel 
76403831d35Sstevel /*
76503831d35Sstevel  * Desktop display_psycho_pci
76603831d35Sstevel  * Display all the psycho based PCI IO cards on this board.
76703831d35Sstevel  */
76803831d35Sstevel 
76903831d35Sstevel /* ARGSUSED */
77003831d35Sstevel void
display_psycho_pci(Board_node * board)77103831d35Sstevel display_psycho_pci(Board_node *board)
77203831d35Sstevel {
77303831d35Sstevel 	struct io_card	*card_list = NULL;
77403831d35Sstevel 	struct io_card	card;
77503831d35Sstevel 	void		*value;
77603831d35Sstevel 
77703831d35Sstevel 	Prom_node	*pci, *card_node, *pci_bridge_node = NULL;
77803831d35Sstevel 	char		*name;
779*6fb59094SToomas Soome 	int		slot_name_bits, pci_bridge_dev_no, class_code,
780*6fb59094SToomas Soome 	    subclass_code, pci_pci_bridge;
78103831d35Sstevel 	char		*slot_name_arr[MAX_SLOTS_PER_IO_BD];
78203831d35Sstevel 
78303831d35Sstevel 	if (board == NULL)
78403831d35Sstevel 		return;
78503831d35Sstevel 
78603831d35Sstevel 	/* Initialize all the common information */
78703831d35Sstevel 	card.display = 1;
78803831d35Sstevel 	card.board = board->board_num;
78903831d35Sstevel 	(void) sprintf(card.bus_type, "PCI");
79003831d35Sstevel 
79103831d35Sstevel 	for (pci = dev_find_node_by_type(board->nodes, "model", "SUNW,psycho");
79203831d35Sstevel 	    pci != NULL;
79303831d35Sstevel 	    pci = dev_next_node_by_type(pci, "model", "SUNW,psycho")) {
79403831d35Sstevel 
79503831d35Sstevel 		/*
79603831d35Sstevel 		 * If we have reached a pci-to-pci bridge node,
79703831d35Sstevel 		 * we are one level below the 'pci' nodes level
79803831d35Sstevel 		 * in the device tree. To get back to that level,
79903831d35Sstevel 		 * the search should continue with the sibling of
80003831d35Sstevel 		 * the parent or else the remaining 'pci' cards
80103831d35Sstevel 		 * will not show up in the output.
80203831d35Sstevel 		 */
80303831d35Sstevel 		if (find_prop(pci, "upa-portid") == NULL) {
80403831d35Sstevel 			if ((pci->parent->sibling != NULL) &&
805*6fb59094SToomas Soome 			    (strcmp(get_prop_val(
806*6fb59094SToomas Soome 			    find_prop(pci->parent->sibling,
807*6fb59094SToomas Soome 			    "name")), PCI_NAME) == 0)) {
80803831d35Sstevel 				pci = pci->parent->sibling;
809*6fb59094SToomas Soome 			} else {
81003831d35Sstevel 				pci = pci->parent->sibling;
81103831d35Sstevel 				continue;
81203831d35Sstevel 			}
81303831d35Sstevel 		}
81403831d35Sstevel 
81503831d35Sstevel 		D_PRINTF("\n\n------->Looking at device [%s][%d] - [%s]\n",
81603831d35Sstevel 		    PCI_NAME, *((int *)get_prop_val(find_prop(
817*6fb59094SToomas Soome 		    pci, "upa-portid"))),
818*6fb59094SToomas Soome 		    get_prop_val(find_prop(pci, "model")));
81903831d35Sstevel 
82003831d35Sstevel 		/* Skip all failed nodes for now */
82103831d35Sstevel 		if (node_failed(pci))
82203831d35Sstevel 			continue;
82303831d35Sstevel 
82403831d35Sstevel 		/* Fill in frequency */
82503831d35Sstevel 		card.freq = get_card_frequency(pci);
82603831d35Sstevel 
82703831d35Sstevel 		/*
82803831d35Sstevel 		 * Each PSYCHO device has a slot-names property that can be
82903831d35Sstevel 		 * used to determine the slot-name string for each IO
83003831d35Sstevel 		 * device under this node. We get this array now and use
83103831d35Sstevel 		 * it later when looking at the children of this PSYCHO.
83203831d35Sstevel 		 */
83303831d35Sstevel 		if ((populate_slot_name_arr(pci, &slot_name_bits,
83403831d35Sstevel 		    (char **)&slot_name_arr, MAX_SLOTS_PER_IO_BD)) != 0)
83503831d35Sstevel 			goto next_card;
83603831d35Sstevel 
83703831d35Sstevel 		/* Walk through the PSYCHO children */
83803831d35Sstevel 		card_node = pci->child;
83903831d35Sstevel 		while (card_node != NULL) {
84003831d35Sstevel 
84103831d35Sstevel 			pci_pci_bridge = FALSE;
84203831d35Sstevel 
84303831d35Sstevel 			/* If it doesn't have a name, skip it */
84403831d35Sstevel 			name = (char *)get_prop_val(
845*6fb59094SToomas Soome 			    find_prop(card_node, "name"));
84603831d35Sstevel 			if (name == NULL)
84703831d35Sstevel 				goto next_card;
84803831d35Sstevel 
84903831d35Sstevel 			/* get dev# and func# for this card. */
85003831d35Sstevel 			get_dev_func_num(card_node, &card.dev_no,
85103831d35Sstevel 			    &card.func_no);
85203831d35Sstevel 
85303831d35Sstevel 			/* get class/subclass code for this card. */
85403831d35Sstevel 			get_pci_class_codes(card_node, &class_code,
85503831d35Sstevel 			    &subclass_code);
85603831d35Sstevel 
85703831d35Sstevel 			D_PRINTF("\nName [%s] - ", name);
85803831d35Sstevel 			D_PRINTF("device no [%d] - ", card.dev_no);
85903831d35Sstevel 			D_PRINTF("class_code [%d] subclass_code [%d] - ",
86003831d35Sstevel 			    class_code, subclass_code);
86103831d35Sstevel 
86203831d35Sstevel 			/*
86303831d35Sstevel 			 * Weed out PCI Bridge, subclass 'other' and
86403831d35Sstevel 			 * ebus nodes.
86503831d35Sstevel 			 */
86603831d35Sstevel 			if (((class_code == PCI_BRIDGE_CLASS) &&
86703831d35Sstevel 			    (subclass_code == PCI_SUBCLASS_OTHER)) ||
86803831d35Sstevel 			    (strstr(name, "ebus"))) {
86903831d35Sstevel 				D_PRINTF("\nSkip ebus/class-other nodes [%s]",
87003831d35Sstevel 				    name);
87103831d35Sstevel 				goto next_card;
87203831d35Sstevel 			}
87303831d35Sstevel 
87403831d35Sstevel 			/*
87503831d35Sstevel 			 * If this is a PCI bridge, then we store it's dev_no
87603831d35Sstevel 			 * so that it's children can use it for getting at
87703831d35Sstevel 			 * the slot_name.
87803831d35Sstevel 			 */
87903831d35Sstevel 			if (is_pci_bridge(card_node, name)) {
88003831d35Sstevel 				pci_bridge_dev_no = card.dev_no;
88103831d35Sstevel 				pci_bridge_node = card_node;
88203831d35Sstevel 				pci_pci_bridge = TRUE;
88303831d35Sstevel 				D_PRINTF("\nPCI Bridge detected\n");
88403831d35Sstevel 			}
88503831d35Sstevel 
88603831d35Sstevel 			/*
88703831d35Sstevel 			 * If we are the child of a pci_bridge we use the
88803831d35Sstevel 			 * dev# of the pci_bridge as an index to get
88903831d35Sstevel 			 * the slot number. We know that we are a child of
89003831d35Sstevel 			 * a pci-bridge if our parent is the same as the last
89103831d35Sstevel 			 * pci_bridge node found above.
89203831d35Sstevel 			 */
89303831d35Sstevel 			if (card_node->parent == pci_bridge_node)
89403831d35Sstevel 				card.dev_no = pci_bridge_dev_no;
89503831d35Sstevel 
89603831d35Sstevel 			/* Get slot-names property from slot_names_arr. */
89703831d35Sstevel 			get_slot_number_str(&card, (char **)slot_name_arr,
89803831d35Sstevel 			    slot_name_bits);
89903831d35Sstevel 
900*6fb59094SToomas Soome 			if (slot_name_bits) {
90103831d35Sstevel 				D_PRINTF("\nIO Card [%s] dev_no [%d] SlotStr "
90203831d35Sstevel 				    "[%s] slot [%s]", name, card.dev_no,
90303831d35Sstevel 				    slot_name_arr[card.dev_no],
90403831d35Sstevel 				    card.slot_str);
905*6fb59094SToomas Soome 			}
90603831d35Sstevel 
90703831d35Sstevel 			/* XXX - Don't know how to get status for PCI cards */
90803831d35Sstevel 			card.status[0] = '\0';
90903831d35Sstevel 
91003831d35Sstevel 			/* Get the model of this card */
91103831d35Sstevel 			get_pci_card_model(card_node, (char *)&card.model);
91203831d35Sstevel 
91303831d35Sstevel 			/*
91403831d35Sstevel 			 * If we haven't figured out the frequency yet,
91503831d35Sstevel 			 * try and get it from the card.
91603831d35Sstevel 			 */
91703831d35Sstevel 			value = get_prop_val(find_prop(pci, "clock-frequency"));
91803831d35Sstevel 			if (value != NULL && card.freq == -1)
91903831d35Sstevel 				card.freq = ((*(int *)value) + 500000)
920*6fb59094SToomas Soome 				    / 1000000;
92103831d35Sstevel 
92203831d35Sstevel 
92303831d35Sstevel 			/* Figure out how we want to display the name */
92403831d35Sstevel 			create_io_card_name(card_node, name,
925*6fb59094SToomas Soome 			    (char *)&card.name);
92603831d35Sstevel 
92703831d35Sstevel 			if (card.freq != -1)
92803831d35Sstevel 				card_list = insert_io_card(card_list, &card);
92903831d35Sstevel 
93003831d35Sstevel next_card:
93103831d35Sstevel 			/*
93203831d35Sstevel 			 * If we are done with the children of the pci bridge,
93303831d35Sstevel 			 * we must continue with the remaining siblings of
93403831d35Sstevel 			 * the pci-to-pci bridge - otherwise we move onto our
93503831d35Sstevel 			 * own sibling.
93603831d35Sstevel 			 */
93703831d35Sstevel 			if (pci_pci_bridge) {
93803831d35Sstevel 				if (card_node->child != NULL)
93903831d35Sstevel 					card_node = card_node->child;
94003831d35Sstevel 				else
94103831d35Sstevel 					card_node = card_node->sibling;
94203831d35Sstevel 			} else {
94303831d35Sstevel 				if ((card_node->parent == pci_bridge_node) &&
94403831d35Sstevel 				    (card_node->sibling == NULL))
94503831d35Sstevel 					card_node = pci_bridge_node->sibling;
94603831d35Sstevel 				else
94703831d35Sstevel 					card_node = card_node->sibling;
94803831d35Sstevel 			}
94903831d35Sstevel 		} /* end-while */
95003831d35Sstevel 	} /* end-for */
95103831d35Sstevel 
95203831d35Sstevel 	D_PRINTF("\n\n");
95303831d35Sstevel 
95403831d35Sstevel 	display_io_cards(card_list);
95503831d35Sstevel 	free_io_cards(card_list);
95603831d35Sstevel }
95703831d35Sstevel 
95803831d35Sstevel void
get_slot_number_str(struct io_card * card,char ** slot_name_arr,int slot_name_bits)95903831d35Sstevel get_slot_number_str(struct io_card *card, char **slot_name_arr,
960*6fb59094SToomas Soome     int slot_name_bits)
96103831d35Sstevel {
96203831d35Sstevel 	if (card->dev_no != -1) {
96303831d35Sstevel 		char	*slot;
96403831d35Sstevel 		/*
96503831d35Sstevel 		 * slot_name_bits is a mask of the plug-in slots so if our
96603831d35Sstevel 		 * dev_no does not appear in this mask we must be an
96703831d35Sstevel 		 * on_board device so set the slot to 'On-Board'
96803831d35Sstevel 		 */
96903831d35Sstevel 		if (slot_name_bits & (1 << card->dev_no)) {
97003831d35Sstevel 			/* we are a plug-in card */
97103831d35Sstevel 			slot = slot_name_arr[card->dev_no];
97203831d35Sstevel 			if (strlen(slot) != 0) {
97303831d35Sstevel 				(void) sprintf(card->slot_str, "%s",
97403831d35Sstevel 				    slot);
97503831d35Sstevel 			} else
97603831d35Sstevel 				(void) sprintf(card->slot_str, "-");
97703831d35Sstevel 		} else {
97803831d35Sstevel 			/* this is an on-board dev. */
979*6fb59094SToomas Soome 			(void) sprintf(card->slot_str, "On-Board");
98003831d35Sstevel 		}
98103831d35Sstevel 
98203831d35Sstevel 	} else {
98303831d35Sstevel 		(void) sprintf(card->slot_str, "%c", '-');
98403831d35Sstevel 	}
98503831d35Sstevel 
98603831d35Sstevel 	/* Informs display_io_cards to print slot_str instead of slot */
98703831d35Sstevel 	card->slot = PCI_SLOT_IS_STRING;
98803831d35Sstevel }
98903831d35Sstevel 
99003831d35Sstevel 
99103831d35Sstevel /*
99203831d35Sstevel  * The output of a number of I/O cards are identical so we need to
99303831d35Sstevel  * differentiate between them.
99403831d35Sstevel  *
99503831d35Sstevel  * This function is called by the platform specific code and it decides
99603831d35Sstevel  * if the card needs further processing.
99703831d35Sstevel  *
99803831d35Sstevel  * It can be extended in the future if card types other than QLC have
99903831d35Sstevel  * the same problems.
100003831d35Sstevel  */
100103831d35Sstevel void
distinguish_identical_io_cards(char * name,Prom_node * node,struct io_card * card)100203831d35Sstevel distinguish_identical_io_cards(char *name, Prom_node *node,
1003*6fb59094SToomas Soome     struct io_card *card)
100403831d35Sstevel {
100503831d35Sstevel 	if ((name == NULL) || (node == NULL))
100603831d35Sstevel 		return;
100703831d35Sstevel 
100803831d35Sstevel 	if (strcmp(name, "SUNW,qlc") == 0)
100903831d35Sstevel 		decode_qlc_card_model_prop(node, card);
101003831d35Sstevel }
101103831d35Sstevel 
101203831d35Sstevel 
101303831d35Sstevel /*
101403831d35Sstevel  * The name/model properties for a number of the QLC FCAL PCI cards are
101503831d35Sstevel  * identical (*), so we need to distinguish them using the subsystem-id
101603831d35Sstevel  * and modify the model string to be more informative.
101703831d35Sstevel  *
101803831d35Sstevel  * (*) Currently the problem cards are:
101903831d35Sstevel  *	Amber
102003831d35Sstevel  *	Crystal+
102103831d35Sstevel  */
102203831d35Sstevel void
decode_qlc_card_model_prop(Prom_node * card_node,struct io_card * card)102303831d35Sstevel decode_qlc_card_model_prop(Prom_node *card_node, struct io_card *card)
102403831d35Sstevel {
102503831d35Sstevel 	void	*value = NULL;
102603831d35Sstevel 
102703831d35Sstevel 	if (card_node == NULL)
102803831d35Sstevel 		return;
102903831d35Sstevel 
103003831d35Sstevel 	value = get_prop_val(find_prop(card_node, "subsystem-id"));
103103831d35Sstevel 	if (value != NULL) {
103203831d35Sstevel 		int	id = *(int *)value;
103303831d35Sstevel 
103403831d35Sstevel 		switch (id) {
103503831d35Sstevel 		case AMBER_SUBSYSTEM_ID:
103603831d35Sstevel 			(void) snprintf(card->model, MAX_QLC_MODEL_LEN, "%s",
1037*6fb59094SToomas Soome 			    AMBER_CARD_NAME);
103803831d35Sstevel 			break;
103903831d35Sstevel 
104003831d35Sstevel 		case CRYSTAL_SUBSYSTEM_ID:
104103831d35Sstevel 			(void) snprintf(card->model, MAX_QLC_MODEL_LEN, "%s",
1042*6fb59094SToomas Soome 			    CRYSTAL_CARD_NAME);
104303831d35Sstevel 			break;
104403831d35Sstevel 
104503831d35Sstevel 		default:
104603831d35Sstevel 			/*
104703831d35Sstevel 			 * If information has been saved into the model field
104803831d35Sstevel 			 * before this function was called we will keep it as
104903831d35Sstevel 			 * it probably will be more meaningful that the
105003831d35Sstevel 			 * subsystem-id, otherwise we save the subsystem-id in
105103831d35Sstevel 			 * the hope that it will distinguish the cards.
105203831d35Sstevel 			 */
105303831d35Sstevel 			if (strcmp(card->model, "") == 0) {
105403831d35Sstevel 				(void) snprintf(card->model, MAX_QLC_MODEL_LEN,
1055*6fb59094SToomas Soome 				    "0x%x", id);
105603831d35Sstevel 			}
105703831d35Sstevel 			break;
105803831d35Sstevel 		}
105903831d35Sstevel 	}
106003831d35Sstevel }
1061