xref: /illumos-gate/usr/src/cmd/pcieb/pcieb.c (revision 4e5ef1cee66fbfbbd2b3e56b81e2bb5700f4a59e)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Joyent, Inc.
14  */
15 
16 /*
17  * Private command to manipulate link speeds of PCIe bridges and allow
18  * retraining. This is designed to aid debugging.
19  */
20 
21 #include <unistd.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <libgen.h>
25 #include <string.h>
26 #include <err.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 
32 #include <pcieb_ioctl.h>
33 
34 static const char *pcieb_progname;
35 
36 static void
37 pcieb_usage(const char *fmt, ...)
38 {
39 	if (fmt != NULL) {
40 		va_list ap;
41 
42 		(void) fprintf(stderr, "%s: ", pcieb_progname);
43 		va_start(ap, fmt);
44 		(void) vfprintf(stderr, fmt, ap);
45 		va_end(ap);
46 	}
47 
48 	(void) fprintf(stderr, "Usage: %s [-x] [-s speed] pcie-bridge\n"
49 	    "\n"
50 	    "\t-s speed		Set link to speed\n",
51 	    "\t-x		Retrain link\n",
52 	    pcieb_progname);
53 
54 }
55 
56 static uint32_t
57 pcieb_parse_speed(const char *s)
58 {
59 	if (strcasecmp(s, "2.5") == 0 || strcasecmp(s, "gen1") == 0) {
60 		return (PCIEB_LINK_SPEED_GEN1);
61 	} else if (strcasecmp(s, "5") == 0 || strcasecmp(s, "gen2") == 0) {
62 		return (PCIEB_LINK_SPEED_GEN2);
63 	} else if (strcasecmp(s, "8") == 0 || strcasecmp(s, "gen3") == 0) {
64 		return (PCIEB_LINK_SPEED_GEN3);
65 	} else if (strcasecmp(s, "16") == 0 || strcasecmp(s, "gen4") == 0) {
66 		return (PCIEB_LINK_SPEED_GEN4);
67 	} else {
68 		errx(EXIT_FAILURE, "invalid speed: %s", s);
69 	}
70 }
71 
72 int
73 main(int argc, char *argv[])
74 {
75 	int c;
76 	boolean_t retrain = B_FALSE;
77 	boolean_t set = B_FALSE;
78 	boolean_t get = B_TRUE;
79 	uint32_t speed = PCIEB_LINK_SPEED_UNKNOWN;
80 	int fd;
81 
82 	pcieb_progname = basename(argv[0]);
83 
84 	while ((c = getopt(argc, argv, ":xs:")) != -1) {
85 		switch (c) {
86 		case 's':
87 			speed = pcieb_parse_speed(optarg);
88 			set = B_TRUE;
89 			get = B_FALSE;
90 			break;
91 		case 'x':
92 			retrain = B_TRUE;
93 			get = B_FALSE;
94 			break;
95 		case ':':
96 			pcieb_usage("option -%c requires an operand\n", optopt);
97 			return (2);
98 		case '?':
99 		default:
100 			pcieb_usage("unknown option: -%c\n", optopt);
101 			return (2);
102 
103 		}
104 	}
105 
106 	argc -= optind;
107 	argv += optind;
108 
109 	if (argc != 1) {
110 		pcieb_usage("missing required PCIe bridge device\n");
111 		return (2);
112 	}
113 
114 	if ((fd = open(argv[0], O_RDWR)) < 0) {
115 		err(EXIT_FAILURE, "failed to open %s", argv[0]);
116 	}
117 
118 	if (set) {
119 		pcieb_ioctl_target_speed_t pits;
120 
121 		pits.pits_flags = 0;
122 		pits.pits_speed = speed;
123 
124 		if (ioctl(fd, PCIEB_IOCTL_SET_TARGET_SPEED, &pits) != 0) {
125 			err(EXIT_FAILURE, "failed to set target speed");
126 		}
127 	}
128 
129 	if (retrain) {
130 		if (ioctl(fd, PCIEB_IOCTL_RETRAIN) != 0) {
131 			err(EXIT_FAILURE, "failed to retrain link");
132 		}
133 	}
134 
135 	if (get) {
136 		pcieb_ioctl_target_speed_t pits;
137 
138 		if (ioctl(fd, PCIEB_IOCTL_GET_TARGET_SPEED, &pits) != 0) {
139 			err(EXIT_FAILURE, "failed to get target speed");
140 		}
141 
142 		(void) printf("Bridge target speed: ");
143 		switch (pits.pits_speed) {
144 		case PCIEB_LINK_SPEED_GEN1:
145 			(void) printf("2.5 GT/s (gen1)\n");
146 			break;
147 		case PCIEB_LINK_SPEED_GEN2:
148 			(void) printf("5.0 GT/s (gen2)\n");
149 			break;
150 		case PCIEB_LINK_SPEED_GEN3:
151 			(void) printf("8.0 GT/s (gen3)\n");
152 			break;
153 		case PCIEB_LINK_SPEED_GEN4:
154 			(void) printf("16.0 GT/s (gen4)\n");
155 			break;
156 		default:
157 			(void) printf("Unknown Value: 0x%x\n", pits.pits_speed);
158 		}
159 
160 		if ((pits.pits_flags & ~PCIEB_FLAGS_ADMIN_SET) != 0) {
161 			(void) printf("Unknown flags: 0x%x\n", pits.pits_flags);
162 		} else if ((pits.pits_flags & PCIEB_FLAGS_ADMIN_SET) != 0) {
163 			(void) printf("Flags: Admin Set Speed\n");
164 		}
165 	}
166 
167 	(void) close(fd);
168 	return (0);
169 }
170