103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
5*6def3553Skd  * Common Development and Distribution License (the "License").
6*6def3553Skd  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel /*
22*6def3553Skd  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
2303831d35Sstevel  * Use is subject to license terms.
2403831d35Sstevel  *
2503831d35Sstevel  * Cherrystone platform-specific functions that aren't platform specific
2603831d35Sstevel  *
2703831d35Sstevel  */
2803831d35Sstevel 
29*6def3553Skd #include <psvc_objects.h>
3003831d35Sstevel #include <libprtdiag.h>
3103831d35Sstevel #include <sys/mc.h>
3203831d35Sstevel 
33*6def3553Skd /* prtdiag exit codes */
34*6def3553Skd #define	PD_SUCCESS		0
35*6def3553Skd #define	PD_SYSTEM_FAILURE	1
36*6def3553Skd #define	PD_INTERNAL_FAILURE	2
37*6def3553Skd 
38*6def3553Skd static int exit_code = PD_SUCCESS;
3903831d35Sstevel 
4003831d35Sstevel static Prom_node *dev_next_node_by_compat(Prom_node *root, char *model);
4103831d35Sstevel static Prom_node *dev_find_node_by_compat(Prom_node *root, char *model);
4203831d35Sstevel 
4303831d35Sstevel void	print_us3_memory_line(int portid,
4403831d35Sstevel 				int bank_id,
4503831d35Sstevel 				uint64_t bank_size,
4603831d35Sstevel 				char *bank_status,
4703831d35Sstevel 				uint64_t dimm_size,
4803831d35Sstevel 				uint32_t intlv,
4903831d35Sstevel 				int seg_id);
5003831d35Sstevel 
5103831d35Sstevel void	add_node(Sys_tree *root, Prom_node *pnode);
5203831d35Sstevel int	do_prominfo(int syserrlog,
5303831d35Sstevel 		    char *pgname,
5403831d35Sstevel 		    int log_flag,
5503831d35Sstevel 		    int prt_flag);
5603831d35Sstevel 
5703831d35Sstevel void	*get_prop_val(Prop *prop);
5803831d35Sstevel Prop	*find_prop(Prom_node *pnode, char *name);
5903831d35Sstevel char	*get_node_name(Prom_node *pnode);
6003831d35Sstevel char	*get_node_type(Prom_node *pnode);
6103831d35Sstevel 
6203831d35Sstevel void	fill_pci_card_list(Prom_node *pci_instance,
6303831d35Sstevel 			    Prom_node *pci_card_node,
6403831d35Sstevel 			    struct io_card *pci_card,
6503831d35Sstevel 			    struct io_card **pci_card_list,
6603831d35Sstevel 			    char **pci_slot_name_arr);
6703831d35Sstevel 
6803831d35Sstevel static Prom_node	*next_pci_card(Prom_node *curr_card, int *is_bridge,
6903831d35Sstevel 				int is_pcidev, Prom_node *curr_bridge,
7003831d35Sstevel 				Prom_node * parent_bridge, Prom_node *pci);
7103831d35Sstevel 
7203831d35Sstevel #define	HZ_TO_MHZ(x)	(((x) + 500000) / 1000000)
7303831d35Sstevel 
7403831d35Sstevel /*
7503831d35Sstevel  * Start from the current node and return the next node besides
7603831d35Sstevel  * the current one which has the requested model property.
7703831d35Sstevel  */
7803831d35Sstevel static Prom_node *
dev_next_node_by_compat(Prom_node * root,char * compat)7903831d35Sstevel dev_next_node_by_compat(Prom_node *root, char *compat)
8003831d35Sstevel {
8103831d35Sstevel 	Prom_node *node;
8203831d35Sstevel 
8303831d35Sstevel 	if (root == NULL)
84*6def3553Skd 		return (NULL);
8503831d35Sstevel 
8603831d35Sstevel 	/* look at your children first */
8703831d35Sstevel 	if ((node = dev_find_node_by_compat(root->child, compat)) != NULL)
88*6def3553Skd 		return (node);
8903831d35Sstevel 
9003831d35Sstevel 	/* now look at your siblings */
9103831d35Sstevel 	if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL)
92*6def3553Skd 		return (node);
9303831d35Sstevel 
9403831d35Sstevel 	return (NULL);  /* not found */
9503831d35Sstevel }
9603831d35Sstevel 
9703831d35Sstevel /*
9803831d35Sstevel  * Do a depth-first walk of a device tree and
9903831d35Sstevel  * return the first node with the matching model.
10003831d35Sstevel  */
10103831d35Sstevel static Prom_node *
dev_find_node_by_compat(Prom_node * root,char * compat)10203831d35Sstevel dev_find_node_by_compat(Prom_node *root, char *compat)
10303831d35Sstevel {
10403831d35Sstevel 	Prom_node	*node;
10503831d35Sstevel 	char		*compatible;
10603831d35Sstevel 	char		*name;
10703831d35Sstevel 
10803831d35Sstevel 	if (root == NULL)
10903831d35Sstevel 		return (NULL);
11003831d35Sstevel 
11103831d35Sstevel 	if (compat == NULL)
11203831d35Sstevel 		return (NULL);
11303831d35Sstevel 
11403831d35Sstevel 	name = get_node_name(root);
11503831d35Sstevel 	if (name == NULL)
11603831d35Sstevel 		name = "";
11703831d35Sstevel 
11803831d35Sstevel 	compatible = (char *)get_prop_val(find_prop(root, "compatible"));
11903831d35Sstevel 
12003831d35Sstevel 	if (compatible == NULL)
121*6def3553Skd 		return (NULL);
12203831d35Sstevel 
12303831d35Sstevel 	if ((strcmp(name, "pci") == 0) && (compatible != NULL) &&
12403831d35Sstevel 	    (strcmp(compatible, compat) == 0)) {
12503831d35Sstevel 		return (root); /* found a match */
12603831d35Sstevel 	}
12703831d35Sstevel 
12803831d35Sstevel 	/* look at your children first */
12903831d35Sstevel 	if ((node = dev_find_node_by_compat(root->child, compat)) != NULL)
130*6def3553Skd 		return (node);
13103831d35Sstevel 
13203831d35Sstevel 	/* now look at your siblings */
13303831d35Sstevel 	if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL)
134*6def3553Skd 		return (node);
13503831d35Sstevel 
13603831d35Sstevel 	return (NULL);  /* not found */
13703831d35Sstevel }
13803831d35Sstevel 
13903831d35Sstevel int32_t
find_child_device(picl_nodehdl_t parent,char * child_name,picl_nodehdl_t * child)14003831d35Sstevel find_child_device(picl_nodehdl_t parent, char *child_name,
14103831d35Sstevel 		picl_nodehdl_t *child)
14203831d35Sstevel {
14303831d35Sstevel 	int32_t		err;
14403831d35Sstevel 	char		name[PICL_PROPNAMELEN_MAX];
14503831d35Sstevel 
14603831d35Sstevel 	err = picl_get_propval_by_name(parent, PICL_PROP_CHILD, &(*child),
147*6def3553Skd 	    sizeof (picl_nodehdl_t));
14803831d35Sstevel 	switch (err) {
14903831d35Sstevel 	case PICL_SUCCESS:
15003831d35Sstevel 		break;
15103831d35Sstevel 	case PICL_PROPNOTFOUND:
15203831d35Sstevel 		err = PICL_INVALIDHANDLE;
15303831d35Sstevel 		return (err);
15403831d35Sstevel 	default:
15503831d35Sstevel #ifdef WORKFILE_DEBUG
15603831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN,
15703831d35Sstevel 		    "Failed picl_get_propval_by_name with %s\n"),
15803831d35Sstevel 		    picl_strerror(err));
15903831d35Sstevel #endif
16003831d35Sstevel 		return (err);
16103831d35Sstevel 	}
16203831d35Sstevel 
16303831d35Sstevel 	err = picl_get_propval_by_name(*child, PICL_PROP_NAME, name,
16403831d35Sstevel 	    PICL_PROPNAMELEN_MAX);
16503831d35Sstevel 
16603831d35Sstevel #ifdef WORKFILE_DEBUG
16703831d35Sstevel 	if (err != PICL_SUCCESS) {
16803831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN,
16903831d35Sstevel 		    "failed the get name for root\n"));
17003831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN, "%s\n"), picl_strerror(err));
17103831d35Sstevel 	}
17203831d35Sstevel #endif
17303831d35Sstevel 
17403831d35Sstevel 	if (strcmp(name, child_name) == 0)
17503831d35Sstevel 		return (err);
17603831d35Sstevel 
17703831d35Sstevel 	while (err != PICL_PROPNOTFOUND) {
17803831d35Sstevel #ifdef WORKFILE_DEBUG
17903831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN, "child name is %s\n"), name);
18003831d35Sstevel #endif
18103831d35Sstevel 		err = picl_get_propval_by_name(*child, PICL_PROP_PEER,
182*6def3553Skd 		    &(*child), sizeof (picl_nodehdl_t));
18303831d35Sstevel 		switch (err) {
18403831d35Sstevel 		case PICL_SUCCESS:
18503831d35Sstevel 			err = picl_get_propval_by_name(*child, PICL_PROP_NAME,
18603831d35Sstevel 			    name, PICL_PROPNAMELEN_MAX);
18703831d35Sstevel 			if (strcmp(name, child_name) == 0)
18803831d35Sstevel 				return (err);
18903831d35Sstevel 			break;
19003831d35Sstevel 		case PICL_PROPNOTFOUND:
19103831d35Sstevel 			break;
19203831d35Sstevel 		default:
19303831d35Sstevel #ifdef WORKFILE_DEBUG
19403831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN,
19503831d35Sstevel 			    "Failed picl_get_propval_by_name with %s\n"),
19603831d35Sstevel 			    picl_strerror(err));
19703831d35Sstevel #endif
19803831d35Sstevel 			return (err);
19903831d35Sstevel 		}
20003831d35Sstevel 	}
20103831d35Sstevel 	err = PICL_INVALIDHANDLE;
20203831d35Sstevel 	return (err);
20303831d35Sstevel }
20403831d35Sstevel 
20503831d35Sstevel int32_t
fill_device_from_id(picl_nodehdl_t device_id,char * assoc_id,picl_nodehdl_t * device)20603831d35Sstevel fill_device_from_id(picl_nodehdl_t device_id, char *assoc_id,
20703831d35Sstevel 		picl_nodehdl_t *device)
20803831d35Sstevel {
20903831d35Sstevel 	int32_t		err;
21003831d35Sstevel 	picl_prophdl_t	tbl_hdl;
21103831d35Sstevel 	picl_prophdl_t	reference_property;
21203831d35Sstevel 
21303831d35Sstevel 	err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl,
214*6def3553Skd 	    sizeof (picl_prophdl_t));
21503831d35Sstevel 	if (err != PICL_SUCCESS) {
21603831d35Sstevel #ifdef WORKFILE_DEBUG
21703831d35Sstevel 		if (err != PICL_INVALIDHANDLE) {
21803831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN,
21903831d35Sstevel 			"fill_device_from_id failure in "
22003831d35Sstevel 			"picl_get_propval_by_name err is %s\n"),
22103831d35Sstevel 			    picl_strerror(err));
22203831d35Sstevel 		}
22303831d35Sstevel #endif
22403831d35Sstevel 		return (err);
22503831d35Sstevel 	}
22603831d35Sstevel 
22703831d35Sstevel 	err = picl_get_next_by_row(tbl_hdl, &reference_property);
22803831d35Sstevel 	if (err != PICL_SUCCESS) {
22903831d35Sstevel #ifdef WORKFILE_DEBUG
23003831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN,
23103831d35Sstevel 		    "fill_device_from_id failure in picl_get_next_by_row"
23203831d35Sstevel 		    " err is %s\n"), picl_strerror(err));
23303831d35Sstevel #endif
23403831d35Sstevel 		return (err);
23503831d35Sstevel 	}
23603831d35Sstevel 
23703831d35Sstevel 	/* get node associated with reference property */
23803831d35Sstevel 	err = picl_get_propval(reference_property, &(*device),
239*6def3553Skd 	    sizeof (picl_nodehdl_t));
24003831d35Sstevel 
24103831d35Sstevel #ifdef WORKFILE_DEBUG
24203831d35Sstevel 	if (err != 0) {
24303831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN,
24403831d35Sstevel 		"fill_device_from_id failure in picl_get_propval"
24503831d35Sstevel 		" err is %s\n"), picl_strerror(err));
24603831d35Sstevel 	}
24703831d35Sstevel #endif
24803831d35Sstevel 
24903831d35Sstevel 	return (err);
25003831d35Sstevel }
25103831d35Sstevel 
25203831d35Sstevel int32_t
fill_device_array_from_id(picl_nodehdl_t device_id,char * assoc_id,int32_t * number_of_devices,picl_nodehdl_t * device_array[])25303831d35Sstevel fill_device_array_from_id(picl_nodehdl_t device_id, char *assoc_id,
25403831d35Sstevel 	int32_t *number_of_devices, picl_nodehdl_t *device_array[])
25503831d35Sstevel {
25603831d35Sstevel 	int32_t		err;
25703831d35Sstevel 	int		i;
25803831d35Sstevel 	picl_prophdl_t	tbl_hdl;
25903831d35Sstevel 	picl_prophdl_t	entry;
26003831d35Sstevel 	int		devs = 0;
26103831d35Sstevel 
26203831d35Sstevel 	err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl,
263*6def3553Skd 	    sizeof (picl_prophdl_t));
26403831d35Sstevel 	if ((err != PICL_SUCCESS) && (err != PICL_INVALIDHANDLE)) {
26503831d35Sstevel #ifdef WORKFILE_DEBUG
266*6def3553Skd 		log_printf(dgettext(TEXT_DOMAIN,
267*6def3553Skd 		    "fill_device_array_from_id failure in "
268*6def3553Skd 		    "picl_get_propval_by_name err is %s\n"),
269*6def3553Skd 		    picl_strerror(err));
27003831d35Sstevel #endif
271*6def3553Skd 		return (err);
27203831d35Sstevel 	}
27303831d35Sstevel 
27403831d35Sstevel 	entry = tbl_hdl;
27503831d35Sstevel 	while (picl_get_next_by_row(entry, &entry) == 0)
27603831d35Sstevel 		++devs;
27703831d35Sstevel 
27803831d35Sstevel 	*device_array = calloc((devs), sizeof (picl_nodehdl_t));
27903831d35Sstevel 	if (*device_array == NULL) {
28003831d35Sstevel 
28103831d35Sstevel #ifdef WORFILE_DEBUG
28203831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN,
28303831d35Sstevel 		"fill_device_array_from_id failure getting memory"
28403831d35Sstevel 		" for array\n"));
28503831d35Sstevel #endif
28603831d35Sstevel 		return (PICL_FAILURE);
28703831d35Sstevel 	}
28803831d35Sstevel 
28903831d35Sstevel 	entry = tbl_hdl;
29003831d35Sstevel 	for (i = 0; i < devs; i++) {
29103831d35Sstevel 		err = picl_get_next_by_row(entry, &entry);
29203831d35Sstevel 		if (err != 0) {
29303831d35Sstevel #ifdef WORKFILE_DEBUG
29403831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN,
29503831d35Sstevel 			"fill_device_array_from_id failure in "
29603831d35Sstevel 			"picl_get_next_by_row err is %s\n"),
29703831d35Sstevel 			    picl_strerror(err));
29803831d35Sstevel #endif
29903831d35Sstevel 			return (err);
30003831d35Sstevel 		}
30103831d35Sstevel 
30203831d35Sstevel 		/* get node associated with reference property */
30303831d35Sstevel 		err = picl_get_propval(entry, &((*device_array)[i]),
304*6def3553Skd 		    sizeof (picl_nodehdl_t));
30503831d35Sstevel 		if (err != 0) {
30603831d35Sstevel #ifdef WORKFILE_DEBUG
30703831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN,
30803831d35Sstevel 			"fill_device_array_from_id failure in "
30903831d35Sstevel 			"picl_get_propval err is %s\n"), picl_strerror(err));
31003831d35Sstevel #endif
31103831d35Sstevel 
31203831d35Sstevel 			return (err);
31303831d35Sstevel 		}
31403831d35Sstevel 	}
31503831d35Sstevel 	*number_of_devices = devs;
31603831d35Sstevel 	return (err);
31703831d35Sstevel }
31803831d35Sstevel 
31903831d35Sstevel /*
32003831d35Sstevel  * add_node
32103831d35Sstevel  *
32203831d35Sstevel  * This function adds a board node to the board structure where that
32303831d35Sstevel  * that node's physical component lives.
32403831d35Sstevel  */
32503831d35Sstevel void
add_node(Sys_tree * root,Prom_node * pnode)32603831d35Sstevel add_node(Sys_tree *root, Prom_node *pnode)
32703831d35Sstevel {
32803831d35Sstevel 	int	board	= -1;
32903831d35Sstevel 	int	portid	= -1;
33003831d35Sstevel 
33103831d35Sstevel 	void		*value	= NULL;
33203831d35Sstevel 	Board_node	*bnode	= NULL;
33303831d35Sstevel 	Prom_node	*p	= NULL;
33403831d35Sstevel 
33503831d35Sstevel 	/* Get the board number of this board from the portid prop */
33603831d35Sstevel 	value = get_prop_val(find_prop(pnode, "portid"));
33703831d35Sstevel 	if (value != NULL) {
33803831d35Sstevel 		portid = *(int *)value;
33903831d35Sstevel 	}
34003831d35Sstevel 
34103831d35Sstevel 	board = CHERRYSTONE_GETSLOT(portid);
34203831d35Sstevel 
34303831d35Sstevel 	if ((bnode = find_board(root, board)) == NULL) {
34403831d35Sstevel 		bnode = insert_board(root, board);
34503831d35Sstevel 	}
34603831d35Sstevel 
34703831d35Sstevel 	/* now attach this prom node to the board list */
34803831d35Sstevel 	/* Insert this node at the end of the list */
34903831d35Sstevel 	pnode->sibling = NULL;
35003831d35Sstevel 	if (bnode->nodes == NULL)
35103831d35Sstevel 		bnode->nodes = pnode;
35203831d35Sstevel 	else {
35303831d35Sstevel 		p = bnode->nodes;
35403831d35Sstevel 		while (p->sibling != NULL)
35503831d35Sstevel 			p = p->sibling;
35603831d35Sstevel 		p->sibling = pnode;
35703831d35Sstevel 	}
35803831d35Sstevel }
35903831d35Sstevel 
36003831d35Sstevel /*
36103831d35Sstevel  * This function provides formatting of the memory config
36203831d35Sstevel  * information that get_us3_mem_regs() and display_us3_banks() code has
36303831d35Sstevel  * gathered. It overrides the generic print_us3_memory_line() code
36403831d35Sstevel  * which prints an error message.
36503831d35Sstevel  */
36603831d35Sstevel void
print_us3_memory_line(int portid,int bank_id,uint64_t bank_size,char * bank_status,uint64_t dimm_size,uint32_t intlv,int seg_id)36703831d35Sstevel print_us3_memory_line(int portid, int bank_id, uint64_t bank_size,
36803831d35Sstevel 	char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id)
36903831d35Sstevel {
37003831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN,
371*6def3553Skd 	    "\n %-1c   %2d    %2d      %4lldMB   %11-s  %4lldMB "
372*6def3553Skd 	    "   %2d-way        %d"),
373*6def3553Skd 	    CHERRYSTONE_GETSLOT_LABEL(portid), portid,
374*6def3553Skd 	    (bank_id % 4), bank_size, bank_status, dimm_size,
375*6def3553Skd 	    intlv, seg_id, 0);
37603831d35Sstevel }
37703831d35Sstevel 
37803831d35Sstevel /*
379*6def3553Skd  * We call do_devinfo() in order to use the libdevinfo device tree instead of
380*6def3553Skd  * OBP's device tree. Ignore its return value and use our exit_code instead.
381*6def3553Skd  * Its return value comes from calling error_check() which is not implemented
382*6def3553Skd  * because the device tree does not keep track of the status property for the
383*6def3553Skd  * 480/490. The exit_code we return is set while do_devinfo() calls our local
384*6def3553Skd  * functions to gather/print data. That way we can report both internal and
385*6def3553Skd  * device failures.
38603831d35Sstevel  */
38703831d35Sstevel int
do_prominfo(int syserrlog,char * pgname,int log_flag,int prt_flag)38803831d35Sstevel do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag)
38903831d35Sstevel {
390*6def3553Skd 	(void) do_devinfo(syserrlog, pgname, log_flag, prt_flag);
391*6def3553Skd 	return (exit_code);
39203831d35Sstevel }
39303831d35Sstevel 
39403831d35Sstevel /*
39503831d35Sstevel  * return the property value for the Prop
39603831d35Sstevel  * passed in. (When using libdevinfo)
39703831d35Sstevel  */
39803831d35Sstevel void *
get_prop_val(Prop * prop)39903831d35Sstevel get_prop_val(Prop *prop)
40003831d35Sstevel {
40103831d35Sstevel 	if (prop == NULL)
40203831d35Sstevel 		return (NULL);
40303831d35Sstevel 
40403831d35Sstevel 	return ((void *)(prop->value.val_ptr));
40503831d35Sstevel }
40603831d35Sstevel 
40703831d35Sstevel /*
40803831d35Sstevel  * Search a Prom node and retrieve the property with the correct
40903831d35Sstevel  * name. (When using libdevinfo)
41003831d35Sstevel  */
41103831d35Sstevel Prop *
find_prop(Prom_node * pnode,char * name)41203831d35Sstevel find_prop(Prom_node *pnode, char *name)
41303831d35Sstevel {
41403831d35Sstevel 	Prop *prop;
41503831d35Sstevel 
41603831d35Sstevel 	if (pnode  == NULL)
41703831d35Sstevel 		return (NULL);
41803831d35Sstevel 
41903831d35Sstevel 	if (pnode->props == NULL)
42003831d35Sstevel 		return (NULL);
42103831d35Sstevel 
42203831d35Sstevel 	prop = pnode->props;
42303831d35Sstevel 	if (prop == NULL)
42403831d35Sstevel 		return (NULL);
42503831d35Sstevel 
42603831d35Sstevel 	if (prop->name.val_ptr == NULL)
42703831d35Sstevel 		return (NULL);
42803831d35Sstevel 
42903831d35Sstevel 	while ((prop != NULL) && (strcmp((char *)(prop->name.val_ptr), name))) {
43003831d35Sstevel 		prop = prop->next;
43103831d35Sstevel 	}
43203831d35Sstevel 	return (prop);
43303831d35Sstevel }
43403831d35Sstevel 
43503831d35Sstevel /*
43603831d35Sstevel  * This function searches through the properties of the node passed in
43703831d35Sstevel  * and returns a pointer to the value of the name property.
43803831d35Sstevel  * (When using libdevinfo)
43903831d35Sstevel  */
44003831d35Sstevel char *
get_node_name(Prom_node * pnode)44103831d35Sstevel get_node_name(Prom_node *pnode)
44203831d35Sstevel {
44303831d35Sstevel 	Prop *prop;
44403831d35Sstevel 
44503831d35Sstevel 	if (pnode == NULL) {
44603831d35Sstevel 		return (NULL);
44703831d35Sstevel 	}
44803831d35Sstevel 
44903831d35Sstevel 	prop = pnode->props;
45003831d35Sstevel 	while (prop != NULL) {
45103831d35Sstevel 		if (strcmp("name", (char *)prop->name.val_ptr) == 0)
45203831d35Sstevel 			return (prop->value.val_ptr);
45303831d35Sstevel 		prop = prop->next;
45403831d35Sstevel 	}
45503831d35Sstevel 	return (NULL);
45603831d35Sstevel }
45703831d35Sstevel 
45803831d35Sstevel /*
45903831d35Sstevel  * This function searches through the properties of the node passed in
46003831d35Sstevel  * and returns a pointer to the value of the device_type property.
46103831d35Sstevel  * (When using libdevinfo)
46203831d35Sstevel  */
46303831d35Sstevel char *
get_node_type(Prom_node * pnode)46403831d35Sstevel get_node_type(Prom_node *pnode)
46503831d35Sstevel {
46603831d35Sstevel 	Prop *prop;
46703831d35Sstevel 
46803831d35Sstevel 	if (pnode == NULL) {
46903831d35Sstevel 		return (NULL);
47003831d35Sstevel 	}
47103831d35Sstevel 
47203831d35Sstevel 	prop = pnode->props;
47303831d35Sstevel 	while (prop != NULL) {
47403831d35Sstevel 		if (strcmp("device_type", (char *)prop->name.val_ptr) == 0)
47503831d35Sstevel 			return (prop->value.val_ptr);
47603831d35Sstevel 		prop = prop->next;
47703831d35Sstevel 	}
47803831d35Sstevel 	return (NULL);
47903831d35Sstevel }
48003831d35Sstevel 
48103831d35Sstevel 
48203831d35Sstevel /*
48303831d35Sstevel  * Fills in the i/o card list to be displayed later in display_pci();
48403831d35Sstevel  */
48503831d35Sstevel void
fill_pci_card_list(Prom_node * pci_instance,Prom_node * pci_card_node,struct io_card * pci_card,struct io_card ** pci_card_list,char ** slot_name_arr)48603831d35Sstevel fill_pci_card_list(Prom_node * pci_instance, Prom_node * pci_card_node,
48703831d35Sstevel 			struct io_card *pci_card,
48803831d35Sstevel 			struct io_card **pci_card_list, char **slot_name_arr)
48903831d35Sstevel {
49003831d35Sstevel 	Prom_node	*pci_bridge_node;
49103831d35Sstevel 	Prom_node	*pci_parent_bridge;
49203831d35Sstevel 	int		*int_val;
49303831d35Sstevel 	int		pci_bridge = FALSE;
49403831d35Sstevel 	int		pci_bridge_dev_no = -1;
49503831d35Sstevel 	int		portid;
49603831d35Sstevel 	int		pci_bus;
49703831d35Sstevel 	char		buf[MAXSTRLEN];
49803831d35Sstevel 	char		*slot_name = NULL;	/* info in "slot-names" prop */
49903831d35Sstevel 	char		*child_name;
50003831d35Sstevel 	char		*name;
50103831d35Sstevel 	char		*type;
50203831d35Sstevel 	void		*value;
50303831d35Sstevel 
50403831d35Sstevel 	while (pci_card_node != NULL) {
50503831d35Sstevel 		int is_pci = FALSE;
50603831d35Sstevel 		type = NULL;
50703831d35Sstevel 		name = NULL;
50803831d35Sstevel 		/* If it doesn't have a name, skip it */
50903831d35Sstevel 		name = (char *)get_prop_val(
510*6def3553Skd 		    find_prop(pci_card_node, "name"));
51103831d35Sstevel 		if (name == NULL) {
51203831d35Sstevel 			pci_card_node = pci_card_node->sibling;
51303831d35Sstevel 			continue;
51403831d35Sstevel 		}
51503831d35Sstevel 
51603831d35Sstevel 		/*
51703831d35Sstevel 		 * Get the portid of the schizo that this card
51803831d35Sstevel 		 * lives under.
51903831d35Sstevel 		 */
52003831d35Sstevel 		portid = -1;
52103831d35Sstevel 		value = get_prop_val(find_prop(pci_instance, "portid"));
52203831d35Sstevel 		if (value != NULL) {
52303831d35Sstevel 			portid = *(int *)value;
52403831d35Sstevel 		}
52503831d35Sstevel 		pci_card->schizo_portid = portid;
52603831d35Sstevel 		if (pci_card->schizo_portid != 8) {
52703831d35Sstevel 			/*
52803831d35Sstevel 			 * Schizo0 (portid 8) has no slots on Cherrystone.
52903831d35Sstevel 			 * So if that's who we're looking at, we're done.
53003831d35Sstevel 			 */
53103831d35Sstevel 			return;
53203831d35Sstevel 		}
53303831d35Sstevel 
53403831d35Sstevel 		/*
53503831d35Sstevel 		 * Find out whether this is PCI bus A or B
53603831d35Sstevel 		 * using the 'reg' property.
53703831d35Sstevel 		 */
53803831d35Sstevel 		int_val = (int *)get_prop_val(find_prop(pci_instance, "reg"));
53903831d35Sstevel 
54003831d35Sstevel 		if (int_val != NULL) {
54103831d35Sstevel 			int_val++; /* skip over first integer */
54203831d35Sstevel 			pci_bus = ((*int_val) & 0x7f0000);
54303831d35Sstevel 			if (pci_bus == 0x600000)
54403831d35Sstevel 				pci_card->pci_bus = 'A';
54503831d35Sstevel 			else if (pci_bus == 0x700000)
54603831d35Sstevel 				pci_card->pci_bus = 'B';
54703831d35Sstevel 			else {
54803831d35Sstevel 				assert(0); /* should never happen */
54903831d35Sstevel 				pci_card->pci_bus = '-';
55003831d35Sstevel 			}
55103831d35Sstevel 		} else {
55203831d35Sstevel 			assert(0); /* should never happen */
55303831d35Sstevel 			pci_card->pci_bus = '-';
55403831d35Sstevel 		}
55503831d35Sstevel 
55603831d35Sstevel 		/*
55703831d35Sstevel 		 * get dev# and func# for this card from the
55803831d35Sstevel 		 * 'reg' property.
55903831d35Sstevel 		 */
56003831d35Sstevel 		int_val = (int *)get_prop_val(
561*6def3553Skd 		    find_prop(pci_card_node, "reg"));
56203831d35Sstevel 		if (int_val != NULL) {
56303831d35Sstevel 			pci_card->dev_no = (((*int_val) & 0xF800) >> 11);
56403831d35Sstevel 			pci_card->func_no = (((*int_val) & 0x700) >> 8);
56503831d35Sstevel 		} else {
56603831d35Sstevel 			pci_card->dev_no = -1;
56703831d35Sstevel 			pci_card->func_no = -1;
56803831d35Sstevel 		}
56903831d35Sstevel 
57003831d35Sstevel 		switch (pci_card->pci_bus) {
57103831d35Sstevel 		case 'A':
57203831d35Sstevel 			if ((pci_card->dev_no < 1 || pci_card->dev_no > 2) &&
573*6def3553Skd 			    (!pci_bridge)) {
57403831d35Sstevel 				pci_card_node = pci_card_node->sibling;
57503831d35Sstevel 				continue;
57603831d35Sstevel 			}
57703831d35Sstevel 			break;
57803831d35Sstevel 		case 'B':
57903831d35Sstevel 			if ((pci_card->dev_no < 2 || pci_card->dev_no > 5) &&
580*6def3553Skd 			    (!pci_bridge)) {
58103831d35Sstevel 				pci_card_node = pci_card_node->sibling;
58203831d35Sstevel 				continue;
58303831d35Sstevel 			}
58403831d35Sstevel 			break;
58503831d35Sstevel 		default:
58603831d35Sstevel 			pci_card_node = pci_card_node->sibling;
58703831d35Sstevel 			continue;
58803831d35Sstevel 		}
58903831d35Sstevel 
59003831d35Sstevel 		type = (char *)get_prop_val(
591*6def3553Skd 		    find_prop(pci_card_node, "device_type"));
59203831d35Sstevel 		/*
59303831d35Sstevel 		 * If this is a pci-bridge, then store its dev#
59403831d35Sstevel 		 * as its children nodes need this to get their slot#.
59503831d35Sstevel 		 * We set the pci_bridge flag so that we know we are
59603831d35Sstevel 		 * looking at a pci-bridge node. This flag gets reset
59703831d35Sstevel 		 * every time we enter this while loop.
59803831d35Sstevel 		 */
59903831d35Sstevel 
60003831d35Sstevel 		/*
60103831d35Sstevel 		 * Check for a PCI-PCI Bridge for PCI and cPCI
60203831d35Sstevel 		 * IO Boards using the name and type properties.
60303831d35Sstevel 		 */
60403831d35Sstevel 		if ((type != NULL) && (strncmp(name, "pci", 3) == 0) &&
60503831d35Sstevel 		    (strcmp(type, "pci") == 0)) {
60603831d35Sstevel 			pci_bridge_node = pci_card_node;
60703831d35Sstevel 			is_pci = TRUE;
60803831d35Sstevel 			if (!pci_bridge) {
60903831d35Sstevel 				pci_bridge_dev_no = pci_card->dev_no;
61003831d35Sstevel 				pci_parent_bridge = pci_bridge_node;
61103831d35Sstevel 				pci_bridge = TRUE;
61203831d35Sstevel 			}
61303831d35Sstevel 		}
61403831d35Sstevel 
61503831d35Sstevel 		/*
61603831d35Sstevel 		 * Get slot-names property from slot_names_arr.
61703831d35Sstevel 		 * If we are the child of a pci_bridge we use the
61803831d35Sstevel 		 * dev# of the pci_bridge as an index to get
61903831d35Sstevel 		 * the slot number. We know that we are a child of
62003831d35Sstevel 		 * a pci-bridge if our parent is the same as the last
62103831d35Sstevel 		 * pci_bridge node found above.
62203831d35Sstevel 		 */
62303831d35Sstevel 		if (pci_card->dev_no != -1) {
62403831d35Sstevel 			/*
62503831d35Sstevel 			 * We compare this cards parent node with the
62603831d35Sstevel 			 * pci_bridge_node to see if it's a child.
62703831d35Sstevel 			 */
62803831d35Sstevel 			if (pci_card_node->parent != pci_instance &&
62903831d35Sstevel 			    pci_bridge) {
63003831d35Sstevel 				/* use dev_no of pci_bridge */
63103831d35Sstevel 				if (pci_card->pci_bus == 'B') {
63203831d35Sstevel 					slot_name =
63303831d35Sstevel 					    slot_name_arr[pci_bridge_dev_no -2];
63403831d35Sstevel 				} else {
63503831d35Sstevel 					slot_name =
63603831d35Sstevel 					    slot_name_arr[pci_bridge_dev_no -1];
63703831d35Sstevel 				}
63803831d35Sstevel 			} else {
63903831d35Sstevel 				if (pci_card->pci_bus == 'B') {
64003831d35Sstevel 				slot_name =
641*6def3553Skd 				    slot_name_arr[pci_card->dev_no-2];
64203831d35Sstevel 				} else {
64303831d35Sstevel 				slot_name =
644*6def3553Skd 				    slot_name_arr[pci_card->dev_no-1];
64503831d35Sstevel 				}
64603831d35Sstevel 			}
64703831d35Sstevel 
64803831d35Sstevel 			if (slot_name != NULL &&
64903831d35Sstevel 			    strlen(slot_name) != 0) {
65003831d35Sstevel 				/* Slot num is last char in string */
65103831d35Sstevel 				(void) snprintf(pci_card->slot_str, MAXSTRLEN,
65203831d35Sstevel 				    "%c", slot_name[strlen(slot_name) - 1]);
65303831d35Sstevel 			} else {
65403831d35Sstevel 				(void) snprintf(pci_card->slot_str, MAXSTRLEN,
65503831d35Sstevel 				    "-");
65603831d35Sstevel 			}
65703831d35Sstevel 
65803831d35Sstevel 		} else {
65903831d35Sstevel 			(void) snprintf(pci_card->slot_str, MAXSTRLEN,
66003831d35Sstevel 			    "%c", '-');
66103831d35Sstevel 		}
66203831d35Sstevel 
66303831d35Sstevel 		/*
66403831d35Sstevel 		 * Check for failed status.
66503831d35Sstevel 		 */
66603831d35Sstevel 		if (node_failed(pci_card_node))
667*6def3553Skd 			(void) strcpy(pci_card->status, "fail");
66803831d35Sstevel 		else
669*6def3553Skd 			(void) strcpy(pci_card->status, "ok");
67003831d35Sstevel 
67103831d35Sstevel 		/* Get the model of this pci_card */
67203831d35Sstevel 		value = get_prop_val(find_prop(pci_card_node, "model"));
67303831d35Sstevel 		if (value == NULL)
67403831d35Sstevel 			pci_card->model[0] = '\0';
67503831d35Sstevel 		else {
67603831d35Sstevel 			(void) snprintf(pci_card->model, MAXSTRLEN, "%s",
677*6def3553Skd 			    (char *)value);
67803831d35Sstevel 		}
67903831d35Sstevel 		/*
68003831d35Sstevel 		 * The card may have a "clock-frequency" but we
68103831d35Sstevel 		 * are not interested in that. Instead we get the
68203831d35Sstevel 		 * "clock-frequency" of the PCI Bus that the card
68303831d35Sstevel 		 * resides on. PCI-A can operate at 33Mhz or 66Mhz
68403831d35Sstevel 		 * depending on what card is plugged into the Bus.
68503831d35Sstevel 		 * PCI-B always operates at 33Mhz.
68603831d35Sstevel 		 */
68703831d35Sstevel 		int_val = get_prop_val(find_prop(pci_instance,
688*6def3553Skd 		    "clock-frequency"));
68903831d35Sstevel 		if (int_val != NULL) {
69003831d35Sstevel 			pci_card->freq = HZ_TO_MHZ(*int_val);
69103831d35Sstevel 		} else {
69203831d35Sstevel 			pci_card->freq = -1;
69303831d35Sstevel 		}
69403831d35Sstevel 
69503831d35Sstevel 		/*
69603831d35Sstevel 		 * Figure out how we want to display the name
69703831d35Sstevel 		 */
69803831d35Sstevel 		value = get_prop_val(find_prop(pci_card_node,
699*6def3553Skd 		    "compatible"));
70003831d35Sstevel 		if (value != NULL) {
70103831d35Sstevel 			/* use 'name'-'compatible' */
70203831d35Sstevel 			(void) snprintf(buf, MAXSTRLEN, "%s-%s", name,
703*6def3553Skd 			    (char *)value);
70403831d35Sstevel 		} else {
70503831d35Sstevel 			/* just use 'name' */
70603831d35Sstevel 			(void) snprintf(buf, MAXSTRLEN, "%s", name);
70703831d35Sstevel 		}
70803831d35Sstevel 		name = buf;
70903831d35Sstevel 
71003831d35Sstevel 		/*
71103831d35Sstevel 		 * If this node has children, add the device_type
71203831d35Sstevel 		 * of the child to the name value of this pci_card->
71303831d35Sstevel 		 */
71403831d35Sstevel 		child_name = (char *)get_node_name(pci_card_node->child);
71503831d35Sstevel 		if ((pci_card_node->child != NULL) &&
716*6def3553Skd 		    (child_name != NULL)) {
71703831d35Sstevel 			value = get_prop_val(find_prop(pci_card_node->child,
718*6def3553Skd 			    "device_type"));
71903831d35Sstevel 			if (value != NULL) {
72003831d35Sstevel 				/* add device_type of child to name */
72103831d35Sstevel 				(void) snprintf(pci_card->name, MAXSTRLEN,
72203831d35Sstevel 				    "%s/%s (%s)", name, child_name,
723*6def3553Skd 				    (char *)value);
72403831d35Sstevel 			} else {
72503831d35Sstevel 				/* just add childs name */
72603831d35Sstevel 				(void) snprintf(pci_card->name, MAXSTRLEN,
72703831d35Sstevel 				    "%s/%s", name, child_name);
72803831d35Sstevel 			}
72903831d35Sstevel 		} else {
73003831d35Sstevel 			(void) snprintf(pci_card->name, MAXSTRLEN, "%s",
73103831d35Sstevel 			    (char *)name);
73203831d35Sstevel 		}
73303831d35Sstevel 
73403831d35Sstevel 		/*
73503831d35Sstevel 		 * If this is a pci-bridge, then add the word
73603831d35Sstevel 		 * 'pci-bridge' to its model.  If we can't find
73703831d35Sstevel 		 * a model, then we just describe what the device
73803831d35Sstevel 		 * is based on some properties.
73903831d35Sstevel 		 */
74003831d35Sstevel 		if (pci_bridge) {
74103831d35Sstevel 			if (strlen(pci_card->model) == 0) {
742*6def3553Skd 				if (pci_card_node->parent == pci_bridge_node)
743*6def3553Skd 					(void) snprintf(pci_card->model,
744*6def3553Skd 					    MAXSTRLEN,
745*6def3553Skd 					    "%s", "device on pci-bridge");
746*6def3553Skd 				else if (pci_card_node->parent
747*6def3553Skd 				    == pci_parent_bridge)
748*6def3553Skd 					(void) snprintf(pci_card->model,
749*6def3553Skd 					    MAXSTRLEN,
750*6def3553Skd 					    "%s", "pci-bridge/pci-bridge");
751*6def3553Skd 				else
752*6def3553Skd 					(void) snprintf(pci_card->model,
753*6def3553Skd 					    MAXSTRLEN,
754*6def3553Skd 					    "%s", "PCI-BRIDGE");
75503831d35Sstevel 			}
75603831d35Sstevel 			else
75703831d35Sstevel 				(void) snprintf(pci_card->model, MAXSTRLEN,
75803831d35Sstevel 				    "%s/pci-bridge", pci_card->model);
75903831d35Sstevel 		}
76003831d35Sstevel 		/* insert this pci_card in the list to be displayed later */
76103831d35Sstevel 
76203831d35Sstevel 		*pci_card_list = insert_io_card(*pci_card_list, pci_card);
76303831d35Sstevel 
76403831d35Sstevel 		/*
76503831d35Sstevel 		 * If we are dealing with a pci-bridge, we need to move
76603831d35Sstevel 		 * down to the children of this bridge if there are any.
76703831d35Sstevel 		 *
76803831d35Sstevel 		 * If we are not, we are either dealing with a regular
76903831d35Sstevel 		 * card (in which case we move onto the sibling of this
77003831d35Sstevel 		 * card) or we are dealing with a child of a pci-bridge
77103831d35Sstevel 		 * (in which case we move onto the child's siblings or
77203831d35Sstevel 		 * if there are no more siblings for this child, we
77303831d35Sstevel 		 * move onto the parents siblings).
77403831d35Sstevel 		 */
77503831d35Sstevel 		pci_card_node = next_pci_card(pci_card_node, &pci_bridge,
776*6def3553Skd 		    is_pci, pci_bridge_node,
777*6def3553Skd 		    pci_parent_bridge, pci_instance);
77803831d35Sstevel 	} /* end-while */
77903831d35Sstevel }
78003831d35Sstevel 
78103831d35Sstevel /*
78203831d35Sstevel  * Helper function for fill_pci_card_list().  Indicates which
78303831d35Sstevel  * card node to go to next.
78403831d35Sstevel  * Parameters:
78503831d35Sstevel  * -----------
78603831d35Sstevel  * Prom_node * curr_card: pointer to the current card node
78703831d35Sstevel  *
78803831d35Sstevel  * int * is_bridge: indicates whether or not the card (is | is on)
78903831d35Sstevel  *                  a pci bridge
79003831d35Sstevel  *
79103831d35Sstevel  * int is_pcidev: indicates whether or not the current card
79203831d35Sstevel  *                is a pci bridge
79303831d35Sstevel  *
79403831d35Sstevel  * Prom_node * curr_bridge: pointer to the current pci bridge.  Eg:
79503831d35Sstevel  *                          curr_card->parent.
79603831d35Sstevel  *
79703831d35Sstevel  * Prom_node * parent_bridge: pointer to the first pci bridge encountered.
79803831d35Sstevel  *			      we could have nested pci bridges, this would
79903831d35Sstevel  *			      be the first one.
80003831d35Sstevel  *
80103831d35Sstevel  * Prom_node * pci: pointer to the pci instance that we are attached to.
80203831d35Sstevel  *		    This would be parent_bridge->parent, or
80303831d35Sstevel  *		    curr_node->parent, if curr_node is not on a pci bridge.
80403831d35Sstevel  */
80503831d35Sstevel static Prom_node *
next_pci_card(Prom_node * curr_card,int * is_bridge,int is_pcidev,Prom_node * curr_bridge,Prom_node * parent_bridge,Prom_node * pci)80603831d35Sstevel next_pci_card(Prom_node *curr_card, int *is_bridge, int is_pcidev,
80703831d35Sstevel 		Prom_node *curr_bridge, Prom_node *parent_bridge,
80803831d35Sstevel 		Prom_node *pci)
80903831d35Sstevel {
81003831d35Sstevel 	Prom_node * curr_node = curr_card;
81103831d35Sstevel 	if (*is_bridge) {
81203831d35Sstevel 		/*
81303831d35Sstevel 		 * is_pcidev is used to prevent us from following the
81403831d35Sstevel 		 * children of something like a scsi device.
81503831d35Sstevel 		 */
81603831d35Sstevel 		if (curr_node->child != NULL && is_pcidev) {
81703831d35Sstevel 			curr_node = curr_node->child;
81803831d35Sstevel 		} else {
81903831d35Sstevel 			curr_node = curr_node->sibling;
82003831d35Sstevel 			if (curr_node == NULL) {
82103831d35Sstevel 				curr_node = curr_bridge->sibling;
82203831d35Sstevel 				while (curr_node == NULL &&
823*6def3553Skd 				    curr_bridge != parent_bridge &&
824*6def3553Skd 				    curr_bridge != NULL) {
82503831d35Sstevel 					curr_node =
826*6def3553Skd 					    curr_bridge->parent->sibling;
82703831d35Sstevel 					curr_bridge = curr_bridge->parent;
82803831d35Sstevel 					if (curr_node != NULL &&
82903831d35Sstevel 					    curr_node->parent == pci)
83003831d35Sstevel 						break;
83103831d35Sstevel 				}
83203831d35Sstevel 				if (curr_bridge == NULL ||
83303831d35Sstevel 				    curr_node == NULL ||
83403831d35Sstevel 				    curr_node->parent == pci ||
83503831d35Sstevel 				    curr_bridge == parent_bridge ||
83603831d35Sstevel 				    curr_node == parent_bridge) {
83703831d35Sstevel 					*is_bridge = FALSE;
83803831d35Sstevel 				}
83903831d35Sstevel 			}
84003831d35Sstevel 		}
84103831d35Sstevel 
84203831d35Sstevel 	} else {
84303831d35Sstevel 		curr_node = curr_node->sibling;
84403831d35Sstevel 	}
84503831d35Sstevel 	return (curr_node);
84603831d35Sstevel }
847