xref: /illumos-gate/usr/src/lib/libprtdiag_psr/sparc/opl/common/opl_picl.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Opl platform specific PICL functions.
26  *
27  * 	called when :
28  *	machine_type == MTYPE_OPL
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <kstat.h>
37 #include <fcntl.h>
38 #include <string.h>
39 #include <assert.h>
40 #include <libintl.h>
41 #include <note.h>
42 #include <dlfcn.h>
43 #include <errno.h>
44 #include <sys/systeminfo.h>
45 #include <sys/openpromio.h>
46 #include <sys/sysmacros.h>
47 #include <picl.h>
48 #include "picldefs.h"
49 #include <pdevinfo.h>
50 #include <display.h>
51 #include <libprtdiag.h>
52 #include <alloca.h>
53 #include "opl_picl.h"
54 #include <sys/pci.h>
55 #include <sys/pci_tools.h>
56 #include <sys/types.h>
57 
58 #if !defined(TEXT_DOMAIN)
59 #define	TEXT_DOMAIN	"SYS_TEST"
60 #endif
61 
62 static picl_errno_t do_walk(picl_nodehdl_t rooth, const char *classname,
63     void *c_args, picl_errno_t (*callback_fn)(picl_nodehdl_t hdl, void *args));
64 static int opl_get_node_by_name(picl_nodehdl_t rooth, char *name,
65     picl_nodehdl_t *nodeh);
66 static picl_errno_t get_lane_width(char *device_path, int bus_no, int func_no,
67     int dev_no, int *actual, int *maximum, uint32_t *speed_max,
68     uint32_t *speed_at, int *type);
69 static int	opl_display_pci(int syserrlog, picl_nodehdl_t plafh);
70 static picl_errno_t opl_pci_callback(picl_nodehdl_t pcih, void *args);
71 static int opl_get_first_compatible_value(picl_nodehdl_t nodeh,
72     char **outbuf);
73 static int picldiag_get_clock_freq(picl_nodehdl_t modh,
74     uint32_t *freq);
75 static uint64_t picldiag_get_uint_propval(picl_nodehdl_t modh,
76     char *prop_name, int *ret);
77 static uint32_t	read_long(int fd, int bus, int dev, int func,
78     int offset, int *ret);
79 static uint8_t read_byte(int fd, int bus, int dev, int func, int offset,
80     int *ret);
81 static uint16_t read_word(int fd, int bus, int dev, int func, int offset,
82     int *ret);
83 
84 
85 /*
86  * Collect I/O nodes information.
87  */
88 /* ARGSUSED */
89 static picl_errno_t
90 opl_pci_callback(picl_nodehdl_t pcih, void *args)
91 {
92 	picl_errno_t	err = PICL_SUCCESS;
93 	picl_nodehdl_t	nodeh;
94 	picl_prophdl_t  proph;
95 	picl_propinfo_t pinfo;
96 	char		path[MAXSTRLEN];
97 	char		parent_path[MAXSTRLEN];
98 	static char	root_path[MAXSTRLEN];
99 	char		piclclass[PICL_CLASSNAMELEN_MAX];
100 	char		name[MAXSTRLEN];
101 	char		model[MAXSTRLEN];
102 	char		*compatible;
103 	char		binding_name[MAXSTRLEN];
104 	struct io_card	pci_card;
105 	char		status[6] = "N/A";
106 	int		portid = PROP_INVALID;
107 	int		*reg_val;
108 	int		board = PROP_INVALID;
109 	static int	saved_board = PROP_INVALID;
110 	static int	saved_portid = PROP_INVALID;
111 	int 		actual = PROP_INVALID, maximum = PROP_INVALID;
112 	int 		bus_type;
113 	int 		rev_id = PROP_INVALID, dev_id = PROP_INVALID;
114 	int		ven_id = PROP_INVALID;
115 	size_t		prop_size;
116 
117 	(void) memset(&pci_card, 0, sizeof (pci_card));
118 
119 	err = picl_get_propval_by_name(pcih, PICL_PROP_CLASSNAME,
120 		piclclass, sizeof (piclclass));
121 
122 	if (err !=  PICL_SUCCESS)
123 		/* Do not proceed to parse this branch */
124 		return (err);
125 
126 	if (!IS_PCI(piclclass))
127 		/* Do not parse non-pci nodes */
128 		return (PICL_INVALIDARG);
129 
130 	err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, parent_path,
131 	    sizeof (parent_path));
132 	if (err != PICL_SUCCESS)
133 		/* Do not proceed to parse this branch */
134 		return (err);
135 	err = picl_get_propval_by_name(pcih, OBP_PROP_BOARD_NUM, &board,
136 		sizeof (board));
137 
138 	if (err == PICL_NORESPONSE)
139 		/* Do not proceed to parse this branch */
140 		return (err);
141 	else if (err != PICL_PROPNOTFOUND) {
142 		saved_board = board;
143 		/* Save board node's pathname */
144 		prop_size = sizeof (parent_path) + 1;
145 		if (prop_size > MAXSTRLEN)
146 			prop_size = MAXSTRLEN;
147 		(void) strlcpy(root_path, parent_path, prop_size);
148 	}
149 
150 	err = picl_get_propval_by_name
151 		(pcih, OBP_PROP_PORTID, &portid, sizeof (portid));
152 
153 	if (err != PICL_PROPNOTFOUND)
154 		saved_portid = portid;
155 
156 	/* Walk through the children */
157 
158 	err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh,
159 	    sizeof (picl_nodehdl_t));
160 
161 	while (err == PICL_SUCCESS) {
162 		uint32_t	freq_max = 0, freq_at = 0;
163 
164 		err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
165 		    piclclass, sizeof (piclclass));
166 		if (err !=  PICL_SUCCESS)
167 			/* Do not proceed to parse this node */
168 			return (err);
169 
170 		if (IS_EBUS(piclclass)) {
171 			err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER,
172 				&nodeh, sizeof (picl_nodehdl_t));
173 			continue;
174 		}
175 
176 		err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH,
177 		    path, sizeof (path));
178 		if (err != PICL_SUCCESS) {
179 			/* Do not proceed to parse this node */
180 			return (err);
181 		}
182 
183 		prop_size = sizeof (path) + 1;
184 		if (prop_size > MAXSTRLEN)
185 			prop_size = MAXSTRLEN;
186 		(void) strlcpy(pci_card.notes, path, prop_size);
187 
188 		pci_card.board = saved_board;
189 		pci_card.schizo_portid = saved_portid;
190 
191 		/*
192 		 * Get bus#, dev# and func# for this card from 'reg' property.
193 		 */
194 
195 		err = picl_get_propinfo_by_name
196 			(nodeh, OBP_PROP_REG, &pinfo, &proph);
197 		if (err == PICL_SUCCESS) {
198 			/* All of the array of bytes of "reg" have to be read */
199 			reg_val = malloc(pinfo.size);
200 			if (reg_val == NULL)
201 				return (PICL_FAILURE);
202 
203 
204 			err = picl_get_propval_by_name
205 			    (nodeh, OBP_PROP_REG, reg_val, pinfo.size);
206 
207 			if (err != PICL_SUCCESS) {
208 				free(reg_val);
209 				/* Do not proceed to parse this node */
210 				return (err);
211 			}
212 
213 			if (reg_val[0] != 0) {
214 				pci_card.dev_no =
215 					(((reg_val[0]) & PCI_DEV_MASK) >> 11);
216 				pci_card.func_no =
217 					(((reg_val[0]) & PCI_FUNC_MASK) >> 8);
218 				pci_card.slot =
219 					(((reg_val[0]) & PCI_BUS_MASK) >> 16);
220 			} else
221 				free(reg_val);
222 		}
223 
224 		err = get_lane_width
225 			(root_path, pci_card.slot, pci_card.dev_no,
226 				pci_card.func_no, &actual, &maximum, &freq_max,
227 				&freq_at, &bus_type);
228 
229 		if (err != PICL_SUCCESS) {
230 			/* Move on to next node */
231 			log_printf("Getting lane width failed for path %s\n",
232 				pci_card.notes);
233 			err = picl_get_propval_by_name
234 				(nodeh, PICL_PROP_PEER, &nodeh,
235 					sizeof (picl_nodehdl_t));
236 			continue;
237 		}
238 
239 
240 		err = picl_get_propval_by_name
241 		    (nodeh, PICL_PROP_NAME, name, sizeof (name));
242 		if (err != PICL_SUCCESS)
243 			(void) strcpy(name, "");
244 
245 		/*
246 		 * Get the name of this card. If binding_name is found,
247 		 * name will be <nodename>-<binding_name>
248 		 */
249 
250 		err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME,
251 		    binding_name, sizeof (binding_name));
252 		if (err == PICL_PROPNOTFOUND) {
253 			/*
254 			 * if compatible prop is found, name will be
255 			 * <nodename>-<compatible>
256 			 */
257 			err = opl_get_first_compatible_value(nodeh,
258 			    &compatible);
259 			if (err == PICL_SUCCESS) {
260 				(void) strlcat(name, "-", MAXSTRLEN);
261 				(void) strlcat(name, compatible, MAXSTRLEN);
262 				free(compatible);
263 			}
264 		} else if (err != PICL_SUCCESS) {
265 			/* No binding-name or compatible */
266 			(void) strcpy(binding_name, "N/A");
267 		} else if (strcmp(name, binding_name) != 0) {
268 			(void) strlcat(name, "-", MAXSTRLEN);
269 			(void) strlcat(name, binding_name, MAXSTRLEN);
270 		}
271 
272 
273 		prop_size = sizeof (name) + 1;
274 		if (prop_size > MAXSTRLEN)
275 			prop_size =  MAXSTRLEN;
276 		(void) strlcpy(pci_card.name, name, prop_size);
277 
278 		/* Get the status of the card */
279 		err = picl_get_propval_by_name
280 		    (nodeh, PICL_PROP_STATUS, status, sizeof (status));
281 
282 
283 		/* Get the model of this card */
284 
285 		err = picl_get_propval_by_name
286 		    (nodeh, OBP_PROP_MODEL, model, sizeof (model));
287 		prop_size = sizeof (model) + 1;
288 		if (prop_size > MAXSTRLEN)
289 			prop_size =  MAXSTRLEN;
290 		if (err != PICL_SUCCESS)
291 			(void) strcpy(model, "N/A");
292 		(void) strlcpy(pci_card.model, model, prop_size);
293 
294 		if (bus_type == PCI)
295 			(void) strlcpy
296 			(pci_card.bus_type, "PCI", sizeof (pci_card.bus_type));
297 		else if (bus_type == PCIX)
298 			(void) strlcpy
299 			(pci_card.bus_type, "PCIx", sizeof (pci_card.bus_type));
300 		else if (bus_type == PCIE)
301 			(void) strlcpy
302 			(pci_card.bus_type, "PCIe", sizeof (pci_card.bus_type));
303 		else
304 			(void) strlcpy
305 			(pci_card.bus_type, "UNKN", sizeof (pci_card.bus_type));
306 
307 		/* Get revision id */
308 		err = picl_get_propval_by_name
309 			(nodeh, OBP_PROP_REVISION_ID, &rev_id, sizeof (rev_id));
310 
311 		/* Get device id */
312 		err = picl_get_propval_by_name
313 			(nodeh, OBP_PROP_DEVICE_ID, &dev_id, sizeof (dev_id));
314 
315 		/* Get vendor id */
316 		err = picl_get_propval_by_name
317 			(nodeh, OBP_PROP_VENDOR_ID, &ven_id, sizeof (ven_id));
318 
319 		/*
320 		 * prtdiag -v prints all devices
321 		 */
322 
323 		/* Print board number */
324 		log_printf("%02d  ", pci_card.board);
325 		/* Print IO Type */
326 		log_printf("%-5.5s ", pci_card.bus_type);
327 
328 		log_printf("%-3d  ", pci_card.schizo_portid);
329 		log_printf("%4x, %4x, %4x     ", rev_id, dev_id, ven_id);
330 
331 		log_printf
332 		("%3d, %2d, %2d",
333 			pci_card.slot, pci_card.dev_no, pci_card.func_no);
334 
335 		/* Print status */
336 		log_printf("  %-5.5s ", status);
337 
338 		/* Print Lane widths, Max/Sup Freq, Speed */
339 		if (bus_type == PCIE) {
340 			PRINT_FMT(actual, maximum);
341 		} else if (bus_type == PCIX) {
342 			PRINT_FREQ_FMT(freq_at, freq_max);
343 		} else if (bus_type == PCI) {
344 			err = picldiag_get_clock_freq(nodeh, &freq_at);
345 			PRINT_FREQ_FMT(freq_at, freq_max);
346 		} else
347 			log_printf(" -- , --   ");
348 
349 		/* Print Card Name */
350 		log_printf("%-30.30s", pci_card.name);
351 
352 		/* Print Card Model */
353 		log_printf(" %-20.20s", pci_card.model);
354 
355 		log_printf("\n");
356 
357 		log_printf("%4s%-100.100s", " ", pci_card.notes);
358 		log_printf("\n");
359 		log_printf("\n");
360 
361 
362 		err = picl_get_propval_by_name
363 		    (nodeh, PICL_PROP_PEER, &nodeh, sizeof (picl_nodehdl_t));
364 
365 	}
366 
367 	return (PICL_WALK_CONTINUE);
368 }
369 
370 /*
371  * opl_display_pci
372  * Display all the PCI IO cards on this board.
373  */
374 static int
375 opl_display_pci(int syserrlog, picl_nodehdl_t plafh)
376 {
377 	picl_errno_t err;
378 	char	*fmt = "%-3s %-5s %-4s %-20s %-11s %-5s %-11s %-30s %-20s";
379 	char 	*fmt2 = "%-16s";
380 	static int banner = FALSE; /* Have we printed the column headings? */
381 
382 	if (banner == FALSE) {
383 		log_printf("\n", 0);
384 		log_printf("=========================", 0);
385 		log_printf(dgettext(TEXT_DOMAIN, " IO Devices "), 0);
386 		log_printf("=========================", 0);
387 		log_printf("\n", 0);
388 		log_printf("\n", 0);
389 		log_printf(fmt, "", "IO", "", "", "", "", "Lane/Frq",
390 			"", "", 0);
391 		log_printf("\n", 0);
392 
393 		log_printf(fmt, "LSB", "Type", "LPID", "  RvID,DvID,VnID",
394 			"  BDF", "State", "Act,  Max", "Name", "Model", 0);
395 
396 		log_printf("\n");
397 
398 		log_printf
399 			(fmt, "---", "-----", "----", "  ------------------",
400 			"  ---------", "-----", "-----------",
401 			"------------------------------",
402 			"--------------------", 0);
403 		log_printf("\n");
404 		log_printf(fmt2, "    Logical Path");
405 		log_printf("\n");
406 		log_printf(fmt2, "    ------------");
407 		log_printf("\n");
408 		banner = TRUE;
409 	}
410 
411 	err = do_walk(plafh, PICL_CLASS_PCI, PICL_CLASS_PCI, opl_pci_callback);
412 	return (err);
413 }
414 
415 
416 /*
417  * return the first compatible value
418  */
419 static int
420 opl_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf)
421 {
422 	picl_errno_t	err;
423 	picl_prophdl_t	proph;
424 	picl_propinfo_t	pinfo;
425 	picl_prophdl_t	tblh;
426 	picl_prophdl_t	rowproph;
427 	char		*pval;
428 
429 	err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE,
430 	    &pinfo, &proph);
431 	if (err != PICL_SUCCESS)
432 	    return (err);
433 
434 	if (pinfo.type == PICL_PTYPE_CHARSTRING) {
435 		pval = malloc(pinfo.size);
436 		if (pval == NULL)
437 			return (PICL_FAILURE);
438 		err = picl_get_propval(proph, pval, pinfo.size);
439 		if (err != PICL_SUCCESS) {
440 			free(pval);
441 			return (err);
442 		}
443 		*outbuf = pval;
444 		return (PICL_SUCCESS);
445 	}
446 
447 	if (pinfo.type != PICL_PTYPE_TABLE)
448 		return (PICL_FAILURE);
449 
450 	/* get first string from table */
451 	err = picl_get_propval(proph, &tblh, pinfo.size);
452 	if (err != PICL_SUCCESS)
453 		return (err);
454 
455 	err = picl_get_next_by_row(tblh, &rowproph);
456 	if (err != PICL_SUCCESS)
457 		return (err);
458 
459 	err = picl_get_propinfo(rowproph, &pinfo);
460 	if (err != PICL_SUCCESS)
461 	    return (err);
462 
463 	pval = malloc(pinfo.size);
464 	if (pval == NULL)
465 		return (PICL_FAILURE);
466 
467 	err = picl_get_propval(rowproph, pval, pinfo.size);
468 	if (err != PICL_SUCCESS) {
469 		free(pval);
470 		return (err);
471 	}
472 
473 	*outbuf = pval;
474 	return (PICL_SUCCESS);
475 }
476 
477 int
478 do_piclinfo(int syserrlog)
479 {
480 	picl_nodehdl_t rooth;		/* root PICL node for IO display */
481 	picl_nodehdl_t plafh;		/* Platform PICL node for IO display */
482 
483 	picl_errno_t err;
484 
485 	err = picl_initialize();
486 	if (err != PICL_SUCCESS) {
487 		(void) log_printf("picl_initialize failed: %s\n",
488 			picl_strerror(err));
489 		return (err);
490 	}
491 
492 
493 	err = picl_get_root(&rooth);
494 	if (err != PICL_SUCCESS) {
495 		(void) log_printf("Getting root node failed: %s\n",
496 			picl_strerror(err));
497 		return (err);
498 	}
499 
500 	err = opl_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh);
501 
502 	if (err != PICL_SUCCESS) {
503 		(void) log_printf("Getting nodes by name failed: %s\n",
504 			picl_strerror(err));
505 		return (err);
506 	}
507 
508 	err = opl_display_pci(syserrlog, plafh);
509 
510 	(void) picl_shutdown();
511 
512 	return (err);
513 }
514 
515 /*
516  * search children to get the node by the nodename
517  */
518 static int
519 opl_get_node_by_name(picl_nodehdl_t rooth, char *name,
520     picl_nodehdl_t *nodeh)
521 {
522 	picl_nodehdl_t	childh;
523 	int		err;
524 	char		*nodename;
525 
526 	nodename = alloca(strlen(name) + 1);
527 	if (nodename == NULL)
528 		return (PICL_FAILURE);
529 
530 	err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh,
531 		sizeof (picl_nodehdl_t));
532 
533 	while (err == PICL_SUCCESS) {
534 		err = picl_get_propval_by_name(childh, PICL_PROP_NAME,
535 			nodename, (strlen(name) + 1));
536 		if (err != PICL_SUCCESS) {
537 			err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
538 				&childh, sizeof (picl_nodehdl_t));
539 			continue;
540 		}
541 
542 		if (strcmp(nodename, name) == 0) {
543 			*nodeh = childh;
544 			return (PICL_SUCCESS);
545 		}
546 
547 		err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
548 			&childh, sizeof (picl_nodehdl_t));
549 	}
550 
551 	return (err);
552 }
553 
554 static int
555 open_root_complex(char *root_complex)
556 {
557 	char *path;
558 	static char device_str[] = {"/devices"};
559 	static char devctl_str[] = {":reg"};
560 	int fd;
561 
562 	path = malloc(
563 	    strlen(root_complex) + sizeof (device_str) + sizeof (devctl_str));
564 	if (path == NULL)
565 		return (PICL_FAILURE);
566 	(void) strcpy(path, device_str);
567 	(void) strcat(path, root_complex);
568 	(void) strcat(path, devctl_str);
569 
570 	if ((fd = open(path, O_RDWR)) == -1) {
571 		return (-1);
572 	}
573 	return (fd);
574 }
575 
576 static uint32_t
577 read_long(int fd, int bus, int dev, int func, int offset, int *ret)
578 {
579 	int rval;
580 	pcitool_reg_t prg;
581 
582 	prg.user_version = PCITOOL_USER_VERSION;
583 	prg.barnum = 0;
584 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 +
585 	    PCITOOL_ACC_ATTR_ENDN_LTL;
586 	prg.bus_no = bus;
587 	prg.dev_no = dev;
588 	prg.func_no = func;
589 	prg.offset = offset;
590 	rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg);
591 	if (rval != 0) {
592 		log_printf
593 		("DEV_GET failed %d %s\n", rval, strerror(errno));
594 		log_printf
595 		("%d.%d.%d offset 0x%x\n", bus, dev, func, offset);
596 	}
597 	*ret = rval;
598 	return ((uint32_t)prg.data);
599 }
600 
601 static uint16_t
602 read_word(int fd, int bus, int dev, int func, int offset, int *ret)
603 {
604 	int rval;
605 	pcitool_reg_t prg;
606 
607 	prg.user_version = PCITOOL_USER_VERSION;
608 	prg.barnum = 0;
609 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 +
610 	    PCITOOL_ACC_ATTR_ENDN_LTL;
611 	prg.bus_no = bus;
612 	prg.dev_no = dev;
613 	prg.func_no = func;
614 	prg.offset = offset;
615 	rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg);
616 	if (rval != 0) {
617 		log_printf
618 		("DEV_GET failed %d %s\n", rval, strerror(errno));
619 		log_printf
620 		("%d.%d.%d offset 0x%x\n", bus, dev, func, offset);
621 	}
622 	*ret = rval;
623 	return ((uint16_t)prg.data);
624 }
625 
626 static uint8_t
627 read_byte(int fd, int bus, int dev, int func, int offset, int *ret)
628 {
629 	int rval;
630 	pcitool_reg_t prg;
631 
632 	prg.user_version = PCITOOL_USER_VERSION;
633 	prg.barnum = 0;
634 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 +
635 	    PCITOOL_ACC_ATTR_ENDN_LTL;
636 	prg.bus_no = bus;
637 	prg.dev_no = dev;
638 	prg.func_no = func;
639 	prg.offset = offset;
640 	rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg);
641 	if (rval != 0) {
642 		log_printf
643 		("DEV_GET failed %d %s\n", rval, strerror(errno));
644 		log_printf
645 		("%d.%d.%d offset 0x%x\n", bus, dev, func, offset);
646 	}
647 	*ret = rval;
648 	return ((uint8_t)prg.data);
649 }
650 
651 
652 static picl_errno_t
653 get_lane_width
654 	(char *device_path, int bus, int dev, int func, int *actual,
655 	int *maximum, uint32_t *speed_max, uint32_t *speed_at, int *type)
656 {
657 	uint_t cap_ptr, cap_reg, link_status, link_cap, capid;
658 	int fd, ret;
659 
660 	if (device_path == NULL)
661 		return (PICL_FAILURE);
662 
663 	fd = open_root_complex(device_path);
664 	if (fd == -1)
665 		return (PICL_FAILURE);
666 
667 	/*
668 	 * Link Capabilities and Link Status registers are in the
669 	 * PCI-E capabilities register.  They are at offset
670 	 * 0xc and 0x12 respectively. They are documented in section
671 	 * 7.8 of the PCI Express Base Specification. The address of
672 	 * that structure is not fixed, it's kind of a linked list.
673 	 * The Capabilities Pointer reg (8 bits) is always at 0x34.
674 	 * It contains a pointer to the first capabilities structure.
675 	 * For each capability structure, the first 8 bits is the capability
676 	 * ID. The next 8 bits is the pointer to the next structure.
677 	 * If the Next Cap register is zero, it's the end of the list.
678 	 * The capability ID for the PCI-E strucutre is 0x10.  The idea
679 	 * is to follow the links until you find a Cap ID of 0x10, then
680 	 * read the registers at 0xc and 0x12 from there.
681 	 * If there's no Cap ID 0x10, then it's not a PCI-E device.
682 	 */
683 
684 	cap_ptr = read_byte(fd, bus, dev, func, PCI_CONF_CAP_PTR, &ret);
685 	if (ret != 0) {
686 		/* ioctl failure */
687 		return (PICL_FAILURE);
688 	}
689 	cap_reg = read_word(fd, bus, dev, func, cap_ptr, &ret);
690 	if (ret != 0) {
691 		/* ioctl failure */
692 		return (PICL_FAILURE);
693 	}
694 	capid = cap_reg & PCI_CAP_MASK;
695 	while (cap_ptr != 0) {
696 
697 		if (capid == PCI_CAP_ID_PCI_E) {
698 			link_cap = read_long(fd, bus, dev, func, cap_ptr +
699 			    PCIE_LINKCAP, &ret);
700 			if (ret != 0) {
701 				return (PICL_FAILURE);
702 			}
703 			link_status = read_word(fd, bus, dev, func, cap_ptr +
704 				PCIE_LINKSTS, &ret);
705 			if (ret != 0) {
706 				return (PICL_FAILURE);
707 			}
708 			*actual = ((link_status >> PCI_LINK_SHIFT) &
709 				PCI_LINK_MASK);
710 			*maximum = ((link_cap >> PCI_LINK_SHIFT) &
711 				PCI_LINK_MASK);
712 			*type = PCIE;
713 		}
714 		if (capid == PCI_CAP_ID_PCIX) {
715 			uint32_t pcix_status;
716 			uint8_t hdr_type;
717 			int max_speed = PCI_FREQ_66;
718 
719 			hdr_type = read_byte
720 				(fd, bus, dev, func, PCI_CONF_HEADER, &ret);
721 			if (ret != 0) {
722 				/* ioctl failure */
723 				return (PICL_FAILURE);
724 			}
725 			if ((hdr_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
726 				/* This is a PCI-X bridge */
727 				uint16_t sec_status, mode;
728 				sec_status = read_word(fd, bus, dev, func,
729 					cap_ptr + PCI_PCIX_SEC_STATUS, &ret);
730 				if (ret != 0) {
731 					/* ioctl failure */
732 					return (PICL_FAILURE);
733 				}
734 				if (sec_status & PCI_SEC_133)
735 					max_speed = PCI_FREQ_133;
736 				if (sec_status & PCI_SEC_266)
737 					max_speed = PCI_FREQ_266;
738 				if (sec_status & PCI_SEC_533)
739 					max_speed = PCI_FREQ_533;
740 				*speed_max = max_speed;
741 				*type = PCIX;
742 				mode = (sec_status >> PCI_CLASS_BRIDGE)
743 					& PCI_BRIDGE_MC;
744 				if (mode) {
745 					int speed;
746 					if (mode == PCI_MODE_66)
747 						speed = PCI_FREQ_66;
748 					else if (mode == PCI_MODE_100)
749 						speed = PCI_FREQ_100;
750 					else if (mode == PCI_MODE_133)
751 						speed = PCI_FREQ_133;
752 					*speed_at = speed;
753 				}
754 
755 			} else {  /* Leaf device */
756 				pcix_status = read_long(fd, bus, dev, func,
757 				    cap_ptr + PCI_PCIX_STATUS, &ret);
758 				if (ret != 0) {
759 					/* ioctl failure */
760 					return (PICL_FAILURE);
761 				}
762 				if (pcix_status &
763 					(PCI_LEAF_ULONG << PCI_SHIFT_133))
764 					max_speed = PCI_FREQ_133;
765 				if (pcix_status &
766 					(PCI_LEAF_ULONG << PCI_SHIFT_266))
767 					max_speed = PCI_FREQ_266;
768 				if (pcix_status &
769 					(PCI_LEAF_ULONG << PCI_SHIFT_533))
770 					max_speed = PCI_FREQ_533;
771 				*speed_max = max_speed;
772 				*type = PCI;
773 			}
774 		}
775 		cap_ptr = (cap_reg >> PCI_REG_FUNC_SHIFT);
776 		cap_reg = read_word(fd, bus, dev, func, cap_ptr, &ret);
777 		if (ret != 0) {
778 			/* ioctl failure */
779 			return (PICL_FAILURE);
780 		}
781 		capid = cap_reg & PCI_CAP_MASK;
782 	}
783 
784 	return (PICL_SUCCESS);
785 }
786 
787 /*
788  * get the clock frequency
789  */
790 static int
791 picldiag_get_clock_freq(picl_nodehdl_t modh, uint32_t *freq)
792 {
793 	int		err;
794 	uint64_t	clk_freq;
795 
796 	clk_freq = picldiag_get_uint_propval(modh, OBP_PROP_CLOCK_FREQ, &err);
797 	if (err != PICL_SUCCESS)
798 		return (err);
799 
800 	*freq = ROUND_TO_MHZ(clk_freq);
801 
802 	return (PICL_SUCCESS);
803 }
804 
805 static uint64_t
806 picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret)
807 {
808 	int		err;
809 	picl_prophdl_t	proph;
810 	picl_propinfo_t pinfo;
811 	uint8_t		uint8v;
812 	uint16_t	uint16v;
813 	uint32_t	uint32v;
814 	uint64_t	uint64v;
815 
816 	err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph);
817 	if (err != PICL_SUCCESS) {
818 		*ret = err;
819 		return (0);
820 	}
821 
822 	/*
823 	 * If it is not an int or uint prop, return failure
824 	 */
825 	if ((pinfo.type != PICL_PTYPE_INT) &&
826 		(pinfo.type != PICL_PTYPE_UNSIGNED_INT)) {
827 		*ret = PICL_FAILURE;
828 		return (0);
829 	}
830 
831 
832 	/* uint prop */
833 
834 	switch (pinfo.size) {
835 	case sizeof (uint8_t):
836 		err = picl_get_propval(proph, &uint8v, sizeof (uint8v));
837 		*ret = err;
838 		return (uint8v);
839 	case sizeof (uint16_t):
840 		err = picl_get_propval(proph, &uint16v, sizeof (uint16v));
841 		*ret = err;
842 		return (uint16v);
843 	case sizeof (uint32_t):
844 		err = picl_get_propval(proph, &uint32v, sizeof (uint32v));
845 		*ret = err;
846 		return (uint32v);
847 	case sizeof (uint64_t):
848 		err = picl_get_propval(proph, &uint64v, sizeof (uint64v));
849 		*ret = err;
850 		return (uint64v);
851 	default:	/* not supported size */
852 		*ret = PICL_FAILURE;
853 		return (0);
854 	}
855 }
856 
857 /*
858  * recursively visit all nodes
859  */
860 static picl_errno_t
861 do_walk(picl_nodehdl_t rooth, const char *classname,
862     void *c_args, picl_errno_t (*callback_fn)(picl_nodehdl_t hdl, void *args))
863 {
864 	picl_errno_t	err;
865 	picl_nodehdl_t  chdh;
866 	char		classval[PICL_CLASSNAMELEN_MAX];
867 
868 	err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &chdh,
869 		sizeof (chdh));
870 	while (err == PICL_SUCCESS) {
871 		err = picl_get_propval_by_name(chdh, PICL_PROP_NAME,
872 			classval, sizeof (classval));
873 		if (err != PICL_SUCCESS)
874 			return (err);
875 
876 		err = callback_fn(chdh, c_args);
877 
878 		if ((err = do_walk(chdh, classname, c_args, callback_fn)) !=
879 			PICL_WALK_CONTINUE)
880 			return (err);
881 
882 		err = picl_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh,
883 			sizeof (chdh));
884 	}
885 	if (err == PICL_PROPNOTFOUND)   /* end of a branch */
886 		return (PICL_WALK_CONTINUE);
887 	return (err);
888 }
889