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
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.
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]
23 * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
24 * All rights reserved.
27 #pragma ident "%Z%%M% %I% %E% SMI"
40 #include <sys/param.h>
42 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <sys/openpromio.h>
45 #include <sys/systeminfo.h>
52 #include "pdevinfo_sun4u.h"
53 #include "display_sun4u.h"
54 #include "libprtdiag.h"
56 #if !defined(TEXT_DOMAIN)
57 #define TEXT_DOMAIN "SYS_TEST"
61 find_pci_bus(Prom_node
*node
, int id
, int bus
)
65 /* find the first pci node */
66 pnode
= dev_find_node(node
, "pci");
68 while (pnode
!= NULL
) {
72 tmp_id
= get_id(pnode
);
73 tmp_bus
= get_pci_bus(pnode
);
80 pnode
= dev_next_node(pnode
, "pci");
88 * Determines the PCI bus, either A (0) or B (1). If the function cannot
89 * find the bus-ranges property, it returns -1.
92 get_pci_bus(Prom_node
*pnode
)
96 /* look up the bus-range property */
97 if ((value
= (int *)get_prop_val(find_prop(pnode
, "bus-range"))) ==
103 return (1); /* B bus has a bus-range value = 0 */
112 * Find the PCI device number of this PCI device. If no device number can
113 * be determined, then return -1.
116 get_pci_device(Prom_node
*pnode
)
120 if ((value
= get_prop_val(find_prop(pnode
, "assigned-addresses"))) !=
122 return (PCI_DEVICE(*(int *)value
));
129 * Find the PCI device number of this PCI device. If no device number can
130 * be determined, then return -1.
133 get_pci_to_pci_device(Prom_node
*pnode
)
137 if ((value
= get_prop_val(find_prop(pnode
, "reg"))) !=
139 return (PCI_DEVICE(*(int *)value
));
147 * Frees the memory allocated for an io card list.
150 free_io_cards(struct io_card
*card_list
)
153 if (card_list
!= NULL
) {
154 struct io_card
*p
, *q
;
156 for (p
= card_list
, q
= NULL
; p
!= NULL
; p
= q
) {
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.
172 insert_io_card(struct io_card
*list
, struct io_card
*card
)
174 struct io_card
*newcard
;
175 struct io_card
*p
, *q
;
180 /* Copy the card to be added into new storage */
181 newcard
= (struct io_card
*)malloc(sizeof (struct io_card
));
182 if (newcard
== NULL
) {
186 (void) memcpy(newcard
, card
, sizeof (struct io_card
));
187 newcard
->next
= NULL
;
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
)
196 if ((newcard
->board
== p
->board
) && (newcard
->slot
< p
->slot
))
200 /* Insert the new card into the list */
213 fmt_manf_id(unsigned int encoded_id
, char *outbuf
)
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.
222 manuf
.encoded_id
= encoded_id
;
223 switch (manuf
.fld
.manf
) {
225 (void) sprintf(outbuf
, "%s %d, version %d", "Brooktree",
226 manuf
.fld
.partno
, manuf
.fld
.version
);
229 case MANF_MITSUBISHI
:
230 (void) sprintf(outbuf
, "%s %x, version %d", "Mitsubishi",
231 manuf
.fld
.partno
, manuf
.fld
.version
);
235 (void) sprintf(outbuf
, "JED code %d, Part num 0x%x, version %d",
236 manuf
.fld
.manf
, manuf
.fld
.partno
, manuf
.fld
.version
);
243 * Find the sbus slot number of this Sbus device. If no slot number can
244 * be determined, then return -1.
247 get_sbus_slot(Prom_node
*pnode
)
251 if ((value
= get_prop_val(find_prop(pnode
, "reg"))) != NULL
) {
252 return (*(int *)value
);
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.
265 display_io_devices(Sys_tree
*tree
)
271 * Following string is used as a table header.
272 * Please maintain the current alignment in
276 log_printf("=========================", 0);
277 log_printf(dgettext(TEXT_DOMAIN
, " IO Cards "), 0);
278 log_printf("=========================", 0);
281 bnode
= tree
->bd_list
;
282 while (bnode
!= NULL
) {
285 display_ffb(bnode
, 1);
291 display_pci(Board_node
*bnode
)
297 * This function is intentionally empty
303 * Print out all the io cards in the list. Also print the column
304 * headers if told to do so.
307 display_io_cards(struct io_card
*list
)
309 static int banner
= 0; /* Have we printed the column headings? */
316 log_printf(" Bus Freq\n", 0);
317 log_printf("Brd Type MHz Slot "
321 log_printf("--- ---- ---- ---------- "
322 "---------------------------- "
323 "--------------------", 0);
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);
333 * We check to see if it's an int or
334 * a char string to display for slot.
336 if (p
->slot
== PCI_SLOT_IS_STRING
)
337 log_printf("%10s ", p
->slot_str
, 0);
339 log_printf("%10d ", p
->slot
, 0);
341 log_printf("%-28.28s", p
->name
, 0);
342 if (strlen(p
->name
) > 28)
346 log_printf("%-19.19s", p
->model
, 0);
347 if (strlen(p
->model
) > 19)
354 * Display all FFBs on this board. It can either be in tabular format,
355 * or a more verbose format.
358 display_ffb(Board_node
*board
, int table
)
362 struct io_card
*card_list
= NULL
;
370 /* Fill in common information */
372 card
.board
= board
->board_num
;
373 (void) sprintf(card
.bus_type
, BUS_TYPE
);
376 for (fb
= dev_find_node_by_type(board
->nodes
, "device_type", "display");
378 fb
= dev_next_node_by_type(fb
, "device_type", "display")) {
379 value
= get_prop_val(find_prop(fb
, "name"));
381 if ((strcmp(FFB_NAME
, value
)) == 0) {
384 } else if ((strcmp(AFB_NAME
, value
)) == 0) {
392 /* Print out in table format */
394 /* XXX - Get the slot number (hack) */
395 card
.slot
= get_id(fb
);
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"));
401 if ((*(int *)value
) & FFB_B_BUFF
)
402 (void) sprintf(card
.name
,
403 "%s, Double Buffered", label
);
405 (void) sprintf(card
.name
,
406 "%s, Single Buffered", label
);
409 * Print model number only if board_type bit 2
410 * is not set and it is not SUNW,XXX-XXXX.
412 card
.model
[0] = '\0';
414 if (strcmp(type
, AFB_NAME
) == 0) {
415 if (((*(int *)value
) & 0x4) != 0x4) {
416 value
= get_prop_val(find_prop(fb
,
418 if ((value
!= NULL
) &&
420 "SUNW,XXX-XXXX") != 0)) {
421 (void) sprintf(card
.model
, "%s",
426 value
= get_prop_val(find_prop(fb
, "model"));
428 (void) sprintf(card
.model
, "%s",
432 card_list
= insert_io_card(card_list
, &card
);
434 /* print in long format */
435 char device
[MAXSTRLEN
];
437 struct dirent
*direntp
;
439 union strap_un strap
;
440 struct ffb_sys_info fsi
;
442 /* Find the device node using upa-portid/portid */
443 value
= get_prop_val(find_prop(fb
, "upa-portid"));
445 value
= get_prop_val(find_prop(fb
, "portid"));
450 (void) sprintf(device
, "%s@%x", type
,
452 if ((dirp
= opendir("/devices")) == NULL
)
455 while ((direntp
= readdir(dirp
)) != NULL
) {
456 if (strstr(direntp
->d_name
, device
) != NULL
) {
457 (void) sprintf(device
, "/devices/%s",
459 fd
= open(device
, O_RDWR
, 0666);
463 (void) closedir(dirp
);
468 if (ioctl(fd
, FFB_SYS_INFO
, &fsi
) < 0)
471 log_printf("%s Hardware Configuration:\n", label
, 0);
472 log_printf("-----------------------------------\n", 0);
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);
486 display_io_cards(card_list
);
487 free_io_cards(card_list
);
492 * Display all the SBus IO cards on this board.
495 display_sbus(Board_node
*board
)
498 struct io_card
*card_list
= NULL
;
503 Prom_node
*card_node
;
508 for (sbus
= dev_find_node(board
->nodes
, SBUS_NAME
); sbus
!= NULL
;
509 sbus
= dev_next_node(sbus
, SBUS_NAME
)) {
511 /* Skip failed nodes for now */
512 if (node_failed(sbus
))
515 /* Calculate SBus frequency in MHz */
516 value
= get_prop_val(find_prop(sbus
, "clock-frequency"));
518 freq
= ((*(int *)value
) + 500000) / 1000000;
522 for (card_node
= sbus
->child
; card_node
!= NULL
;
523 card_node
= card_node
->sibling
) {
528 card_num
= get_sbus_slot(card_node
);
532 /* Fill in card information */
535 card
.board
= board
->board_num
;
536 (void) sprintf(card
.bus_type
, "SBus");
537 card
.slot
= card_num
;
538 card
.status
[0] = '\0';
540 /* Try and get card status */
541 value
= get_prop_val(find_prop(card_node
, "status"));
543 (void) strncpy(card
.status
, (char *)value
,
546 /* XXX - For now, don't display failed cards */
547 if (strstr(card
.status
, "fail") != NULL
)
550 /* Now gather all of the node names for that card */
551 model
= (char *)get_prop_val(find_prop(card_node
,
553 name
= get_node_name(card_node
);
559 card
.model
[0] = '\0';
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
,
568 (void) sprintf(card
.name
, "%s/%s (%s)",
572 (void) sprintf(card
.name
, "%s/%s", name
,
575 (void) strncpy(card
.name
, name
, MAXSTRLEN
);
579 (void) strncpy(card
.model
, model
, MAXSTRLEN
);
581 card_list
= insert_io_card(card_list
, &card
);
585 /* We're all done gathering card info, now print it out */
586 display_io_cards(card_list
);
587 free_io_cards(card_list
);
592 * Get slot-names properties from parent node and
593 * store them in an array.
596 populate_slot_name_arr(Prom_node
*pci
, int *slot_name_bits
,
597 char **slot_name_arr
, int num_slots
)
602 value
= (char *)get_prop_val(find_prop(pci
, "slot-names"));
603 D_PRINTF("\n populate_slot_name_arr: value = [0x%x]\n", value
);
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
);
610 /* array starts after first int */
611 strings_arr
[0] = value
+ sizeof (int);
614 * break the array out into num_slots number of strings
616 for (i
= 1; i
< num_slots
; i
++) {
617 strings_arr
[i
] = (char *)strings_arr
[i
- 1]
618 + strlen(strings_arr
[i
- 1]) + 1;
622 * process array of slot_names to remove blanks
625 for (i
= 0; i
< num_slots
; i
++) {
626 if ((bit_mask
>> i
) & 0x1)
627 slot_name_arr
[i
] = strings_arr
[j
++];
629 slot_name_arr
[i
] = "";
631 D_PRINTF("\nslot_name_arr[%d] = [%s]", i
,
636 D_PRINTF("\n populate_slot_name_arr: - psycho with no "
643 get_card_frequency(Prom_node
*pci
)
645 char *value
= get_prop_val(find_prop(pci
, "clock-frequency"));
650 return (int)(((*(int *)value
) + 500000) / 1000000);
655 get_dev_func_num(Prom_node
*card_node
, int *dev_no
, int *func_no
)
658 void *value
= get_prop_val(find_prop(card_node
, "reg"));
661 int int_val
= *(int *)value
;
662 *dev_no
= PCI_REG_TO_DEV(int_val
);
663 *func_no
= PCI_REG_TO_FUNC(int_val
);
671 get_pci_class_codes(Prom_node
*card_node
, int *class_code
, int *subclass_code
)
673 int class_code_reg
= get_pci_class_code_reg(card_node
);
675 *class_code
= CLASS_REG_TO_CLASS(class_code_reg
);
676 *subclass_code
= CLASS_REG_TO_SUBCLASS(class_code_reg
);
680 is_pci_bridge(Prom_node
*card_node
, char *name
)
682 int class_code
, subclass_code
;
684 if (card_node
== NULL
)
687 get_pci_class_codes(card_node
, &class_code
, &subclass_code
);
689 if ((strncmp(name
, "pci", 3) == 0) &&
690 (class_code
== PCI_BRIDGE_CLASS
) &&
691 (subclass_code
== PCI_PCI_BRIDGE_SUBCLASS
))
698 is_pci_bridge_other(Prom_node
*card_node
, char *name
)
700 int class_code
, subclass_code
;
702 if (card_node
== NULL
)
705 get_pci_class_codes(card_node
, &class_code
, &subclass_code
);
707 if ((strncmp(name
, "pci", 3) == 0) &&
708 (class_code
== PCI_BRIDGE_CLASS
) &&
709 (subclass_code
== PCI_SUBCLASS_OTHER
))
715 get_pci_card_model(Prom_node
*card_node
, char *model
)
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
);
724 (void) sprintf(model
, "%s",
728 if (strlen(model
) == 0)
729 (void) sprintf(model
,
732 (void) sprintf(model
,
733 "%s/pci-bridge", model
);
738 create_io_card_name(Prom_node
*card_node
, char *name
, char *card_name
)
740 char *value
= get_prop_val(find_prop(card_node
, "compatible"));
745 (void) sprintf(buf
, "%s-%s", name
,
748 (void) sprintf(buf
, "%s", name
);
752 child_name
= (char *)get_node_name(card_node
->child
);
754 if ((card_node
->child
!= NULL
) &&
755 (child_name
!= NULL
)) {
756 value
= get_prop_val(find_prop(card_node
->child
,
759 (void) sprintf(card_name
, "%s/%s (%s)",
763 (void) sprintf(card_name
, "%s/%s", name
,
766 (void) sprintf(card_name
, "%s", (char *)name
);
772 * Desktop display_psycho_pci
773 * Display all the psycho based PCI IO cards on this board.
778 display_psycho_pci(Board_node
*board
)
780 struct io_card
*card_list
= NULL
;
784 Prom_node
*pci
, *card_node
, *pci_bridge_node
= NULL
;
786 int slot_name_bits
, pci_bridge_dev_no
,
787 class_code
, subclass_code
,
789 char *slot_name_arr
[MAX_SLOTS_PER_IO_BD
];
794 /* Initialize all the common information */
796 card
.board
= board
->board_num
;
797 (void) sprintf(card
.bus_type
, "PCI");
799 for (pci
= dev_find_node_by_type(board
->nodes
, "model", "SUNW,psycho");
801 pci
= dev_next_node_by_type(pci
, "model", "SUNW,psycho")) {
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.
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
;
818 pci
= pci
->parent
->sibling
;
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")));
828 /* Skip all failed nodes for now */
829 if (node_failed(pci
))
832 /* Fill in frequency */
833 card
.freq
= get_card_frequency(pci
);
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.
841 if ((populate_slot_name_arr(pci
, &slot_name_bits
,
842 (char **)&slot_name_arr
, MAX_SLOTS_PER_IO_BD
)) != 0)
845 /* Walk through the PSYCHO children */
846 card_node
= pci
->child
;
847 while (card_node
!= NULL
) {
849 pci_pci_bridge
= FALSE
;
851 /* If it doesn't have a name, skip it */
852 name
= (char *)get_prop_val(
853 find_prop(card_node
, "name"));
857 /* get dev# and func# for this card. */
858 get_dev_func_num(card_node
, &card
.dev_no
,
861 /* get class/subclass code for this card. */
862 get_pci_class_codes(card_node
, &class_code
,
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
);
871 * Weed out PCI Bridge, subclass 'other' and
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]",
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
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");
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.
901 if (card_node
->parent
== pci_bridge_node
)
902 card
.dev_no
= pci_bridge_dev_no
;
904 /* Get slot-names property from slot_names_arr. */
905 get_slot_number_str(&card
, (char **)slot_name_arr
,
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
],
914 /* XXX - Don't know how to get status for PCI cards */
915 card
.status
[0] = '\0';
917 /* Get the model of this card */
918 get_pci_card_model(card_node
, (char *)&card
.model
);
921 * If we haven't figured out the frequency yet,
922 * try and get it from the card.
924 value
= get_prop_val(find_prop(pci
, "clock-frequency"));
925 if (value
!= NULL
&& card
.freq
== -1)
926 card
.freq
= ((*(int *)value
) + 500000)
930 /* Figure out how we want to display the name */
931 create_io_card_name(card_node
, name
,
935 card_list
= insert_io_card(card_list
, &card
);
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
944 if (pci_pci_bridge
) {
945 if (card_node
->child
!= NULL
)
946 card_node
= card_node
->child
;
948 card_node
= card_node
->sibling
;
950 if ((card_node
->parent
== pci_bridge_node
) &&
951 (card_node
->sibling
== NULL
))
952 card_node
= pci_bridge_node
->sibling
;
954 card_node
= card_node
->sibling
;
961 display_io_cards(card_list
);
962 free_io_cards(card_list
);
966 get_slot_number_str(struct io_card
*card
, char **slot_name_arr
,
969 if (card
->dev_no
!= -1) {
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'
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",
983 (void) sprintf(card
->slot_str
, "-");
985 /* this is an on-board dev. */
986 sprintf(card
->slot_str
, "On-Board");
990 (void) sprintf(card
->slot_str
, "%c", '-');
993 /* Informs display_io_cards to print slot_str instead of slot */
994 card
->slot
= PCI_SLOT_IS_STRING
;
999 * The output of a number of I/O cards are identical so we need to
1000 * differentiate between them.
1002 * This function is called by the platform specific code and it decides
1003 * if the card needs further processing.
1005 * It can be extended in the future if card types other than QLC have
1006 * the same problems.
1009 distinguish_identical_io_cards(char *name
, Prom_node
*node
,
1010 struct io_card
*card
)
1012 if ((name
== NULL
) || (node
== NULL
))
1015 if (strcmp(name
, "SUNW,qlc") == 0)
1016 decode_qlc_card_model_prop(node
, card
);
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.
1025 * (*) Currently the problem cards are:
1030 decode_qlc_card_model_prop(Prom_node
*card_node
, struct io_card
*card
)
1034 if (card_node
== NULL
)
1037 value
= get_prop_val(find_prop(card_node
, "subsystem-id"));
1038 if (value
!= NULL
) {
1039 int id
= *(int *)value
;
1042 case AMBER_SUBSYSTEM_ID
:
1043 (void) snprintf(card
->model
, MAX_QLC_MODEL_LEN
, "%s",
1047 case CRYSTAL_SUBSYSTEM_ID
:
1048 (void) snprintf(card
->model
, MAX_QLC_MODEL_LEN
, "%s",
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.
1060 if (strcmp(card
->model
, "") == 0) {
1061 (void) snprintf(card
->model
, MAX_QLC_MODEL_LEN
,