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
60Prom_node *
61find_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 */
91int
92get_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 */
115int
116get_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 */
132int
133get_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 */
149void
150free_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 */
171struct io_card *
172insert_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
212char *
213fmt_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 */
246int
247get_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 */
264void
265display_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
290void
291display_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 */
306void
307display_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 */
357void
358display_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 */
494void
495display_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 */
595int
596populate_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
642int
643get_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
654void
655get_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
670void
671get_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
679int
680is_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
697int
698is_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}
714void
715get_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
737void
738create_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 */
777void
778display_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
937next_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
965void
966get_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 */
1008void
1009distinguish_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 */
1029void
1030decode_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