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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Sun4v Platform specific functions.
29  *
30  * 	called when :
31  *      machine_type ==  StPaul
32  *
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <kstat.h>
41 #include <fcntl.h>
42 #include <string.h>
43 #include <assert.h>
44 #include <libintl.h>
45 #include <note.h>
46 #include <sys/systeminfo.h>
47 #include <sys/openpromio.h>
48 #include <sys/sysmacros.h>
49 #include <picl.h>
50 #include "picldefs.h"
51 #include <pdevinfo.h>
52 #include <display.h>
53 #include <display_sun4v.h>
54 #include <libprtdiag.h>
55 #include "stpaul.h"
56 
57 /* prototypes for local functions */
58 static void get_bus_type(char *path, struct io_card *card);
59 static void get_slot_number(char *path, struct io_card *card);
60 static int stpaul_get_network_instance(char *path);
61 static int stpaul_get_usb_instance(char *path);
62 static int stpaul_get_io_instance(char *path, char *type);
63 static int stpaul_get_first_compatible_value(picl_nodehdl_t nodeh,
64     char **outbuf);
65 static int64_t stpaul_get_int_propval(picl_nodehdl_t modh, char *prop_name,
66     int *ret);
67 
68 /* ARGSUSED */
69 int
70 stpaul_pci_callback(picl_nodehdl_t pcih, void *args)
71 {
72 	int		err = PICL_SUCCESS;
73 	picl_nodehdl_t	nodeh;
74 	char		path[MAXSTRLEN];
75 	char		parent_path[MAXSTRLEN];
76 	char		piclclass[PICL_CLASSNAMELEN_MAX];
77 	char		name[MAXSTRLEN];
78 	char		model[MAXSTRLEN];
79 	char		*compatible;
80 	char		binding_name[MAXSTRLEN];
81 	struct io_card	pci_card;
82 	int32_t		instance;
83 	char		pn_type;
84 
85 	err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, parent_path,
86 	    sizeof (parent_path));
87 	if (err != PICL_SUCCESS)
88 		return (err);
89 
90 	/* Walk through the children */
91 
92 	err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh,
93 	    sizeof (picl_nodehdl_t));
94 
95 	while (err == PICL_SUCCESS) {
96 		err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
97 		    piclclass, sizeof (piclclass));
98 		if (err !=  PICL_SUCCESS)
99 			return (err);
100 
101 		if (strcmp(piclclass, PICL_CLASS_PCIEX) == 0) {
102 			err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER,
103 			    &nodeh, sizeof (picl_nodehdl_t));
104 			continue;
105 		}
106 
107 		if (strcmp(piclclass, PICL_CLASS_PCI) == 0) {
108 			err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD,
109 			    &nodeh, sizeof (picl_nodehdl_t));
110 			continue;
111 		}
112 
113 		err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH,
114 		    path, sizeof (path));
115 		if (err != PICL_SUCCESS)
116 			return (err);
117 
118 		(void) strlcpy(pci_card.notes, path, sizeof (pci_card.notes));
119 
120 		get_bus_type(path, &pci_card);
121 
122 		get_slot_number(path, &pci_card);
123 
124 		err = picl_get_propval_by_name(nodeh, PICL_PROP_NAME, &name,
125 		    sizeof (name));
126 		if (err == PICL_PROPNOTFOUND)
127 			(void) strlcpy(name, "", sizeof (name));
128 		else if (err != PICL_SUCCESS)
129 			return (err);
130 
131 		/* Figure NAC name */
132 		if ((strcmp(name, NETWORK) == 0) &&
133 		    (strcmp(pci_card.slot_str, MOTHERBOARD) == 0)) {
134 			instance = stpaul_get_network_instance(path);
135 			(void) snprintf(pci_card.status,
136 			    sizeof (pci_card.status), "%s/%s%d",
137 			    MOTHERBOARD, "NET", instance);
138 
139 
140 		} else if ((strcmp(name, SCSI) == 0) &&
141 		    (strcmp(pci_card.slot_str, MOTHERBOARD) == 0)) {
142 			(void) snprintf(pci_card.status,
143 			    sizeof (pci_card.status), "%s/%s",
144 			    MOTHERBOARD, SPL_SCSI_TAG);
145 
146 		} else {
147 			if (pci_card.slot != -1) {
148 				(void) snprintf(pci_card.status,
149 				    sizeof (pci_card.status), "%s/%s%d",
150 				    MOTHERBOARD, pci_card.bus_type,
151 				    pci_card.slot);
152 			} else {
153 				(void) snprintf(pci_card.status,
154 				    sizeof (pci_card.status), "%s/%s",
155 				    MOTHERBOARD, pci_card.bus_type);
156 			}
157 		}
158 
159 		/* Special case for USB */
160 		if (strncmp(name, USB, strlen(USB)) == 0) {
161 			instance = stpaul_get_usb_instance(path);
162 			if (instance != -1)
163 				(void) snprintf(pci_card.status,
164 				    sizeof (pci_card.status), "%s/%s%d",
165 				    MOTHERBOARD, "USB", instance);
166 		}
167 
168 		/* PEM/NEM case is handled here */
169 		if ((instance = stpaul_get_io_instance(path, &pn_type)) != -1) {
170 			if (pn_type == SPL_PEM_TYPE)
171 				(void) snprintf(pci_card.status,
172 				    sizeof (pci_card.status), "%s/%s%d",
173 				    MOTHERBOARD, "PCI-EM", instance);
174 			else if (pn_type == SPL_NEM_TYPE)
175 				(void) snprintf(pci_card.status,
176 				    sizeof (pci_card.status), "%s/%s%d",
177 				    MOTHERBOARD, "NEM", instance);
178 		}
179 		/*
180 		 * Get the name of this card. If binding_name is found,
181 		 * name will be <nodename>-<binding_name>
182 		 */
183 
184 		err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME,
185 		    &binding_name, sizeof (binding_name));
186 		if (err == PICL_PROPNOTFOUND) {
187 			/*
188 			 * if compatible prop is found, name will be
189 			 * <nodename>-<compatible>
190 			 */
191 			err = stpaul_get_first_compatible_value(nodeh,
192 			    &compatible);
193 			if (err == PICL_SUCCESS) {
194 				(void) strlcat(name, "-", MAXSTRLEN);
195 				(void) strlcat(name, compatible, MAXSTRLEN);
196 				free(compatible);
197 			} else if (err != PICL_PROPNOTFOUND)
198 				return (err);
199 		} else if (err != PICL_SUCCESS)
200 			return (err);
201 		else if (strcmp(name, binding_name) != 0) {
202 			(void) strlcat(name, "-", MAXSTRLEN);
203 			(void) strlcat(name, binding_name, MAXSTRLEN);
204 		}
205 
206 		(void) strlcpy(pci_card.name, name, sizeof (pci_card.name));
207 
208 		/* Get the model of this card */
209 
210 		err = picl_get_propval_by_name(nodeh, OBP_PROP_MODEL,
211 		    &model, sizeof (model));
212 		if (err == PICL_PROPNOTFOUND)
213 			(void) strlcpy(model, "", sizeof (model));
214 		else if (err != PICL_SUCCESS)
215 			return (err);
216 		(void) strlcpy(pci_card.model, model, sizeof (pci_card.model));
217 
218 		/* Print NAC name */
219 		log_printf("%-11s", pci_card.status);
220 		/* Print IO Type */
221 		log_printf("%6s", pci_card.bus_type);
222 		/* Print Slot # */
223 		log_printf("%5s", pci_card.slot_str);
224 		/* Print Parent Path */
225 		log_printf("%46.45s", pci_card.notes);
226 		/* Printf Card Name */
227 		if (strlen(pci_card.name) > 24)
228 			log_printf("%25.24s+", pci_card.name);
229 		else
230 			log_printf("%26s", pci_card.name);
231 		/* Print Card Model */
232 		if (strlen(pci_card.model) > 10)
233 			log_printf("%10.9s+", pci_card.model);
234 		else
235 			log_printf("%10s", pci_card.model);
236 		log_printf("\n");
237 
238 		err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh,
239 		    sizeof (picl_nodehdl_t));
240 
241 	}
242 
243 	return (PICL_WALK_CONTINUE);
244 }
245 
246 /* ARGSUSED */
247 int
248 stpaul_hw_rev_callback(picl_nodehdl_t pcih, void *args)
249 {
250 	int		err = PICL_SUCCESS;
251 	char		path[MAXSTRLEN];
252 	char		device_path[MAXSTRLEN];
253 	char		NAC[MAXSTRLEN];
254 	char		*compatible;
255 	int32_t		revision;
256 	int		device_found = 0;
257 	char		name[MAXSTRLEN];
258 	picl_nodehdl_t	nodeh;
259 
260 	err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, path,
261 	    sizeof (path));
262 	if (err != PICL_SUCCESS)
263 		return (err);
264 
265 	/* usb is special as a child of PCIE2PCI bridge */
266 	if (strcmp(path, SPL_PCIE2PCI) == 0) {
267 		err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh,
268 		    sizeof (picl_nodehdl_t));
269 		if (err != PICL_SUCCESS)
270 			return (err);
271 		err = picl_get_propval_by_name(nodeh, PICL_PROP_NAME, &name,
272 		    sizeof (name));
273 		if (err != PICL_SUCCESS)
274 			return (err);
275 		if (strcmp(name, USB) == 0) {
276 			err = stpaul_hw_rev_callback(nodeh, &nodeh);
277 			if (err != PICL_SUCCESS)
278 				return (err);
279 		}
280 	}
281 
282 	if ((strcmp(path, SPL_NETWORK_0_PATH) == 0) ||
283 	    (strcmp(path, SPL_NETWORK_1_PATH) == 0)) {
284 		device_found = 1;
285 		(void) snprintf(NAC, sizeof (NAC), "%s/%s%d", MOTHERBOARD,
286 		    OPHIR, 0);
287 		revision = stpaul_get_int_propval(pcih, OBP_PROP_REVISION_ID,
288 		    &err);
289 	}
290 
291 	if ((strcmp(path, SPL_USB0_PATH) == 0) ||
292 	    (strcmp(path, SPL_USB1_PATH) == 0) ||
293 	    (strcmp(path, SPL_USB2_PATH) == 0)) {
294 		device_found = 1;
295 		(void) snprintf(NAC, sizeof (NAC), "%s/%s%d", MOTHERBOARD,
296 		    USB_TAG, 0);
297 		revision = stpaul_get_int_propval(pcih, OBP_PROP_REVISION_ID,
298 		    &err);
299 	}
300 
301 	if ((strcmp(path, FIRE_PATH0) == 0) ||
302 	    (strcmp(path, FIRE_PATH1) == 0)) {
303 		device_found = 1;
304 		(void) snprintf(NAC, sizeof (NAC), "%s/%s", MOTHERBOARD,
305 		    "IO-BRIDGE");
306 		revision = stpaul_get_int_propval(pcih, OBP_PROP_VERSION_NUM,
307 		    &err);
308 	}
309 
310 	if (strcmp(path, SWITCH_A_PATH) == 0) {
311 		device_found = 1;
312 		(void) snprintf(NAC, sizeof (NAC), "%s/%s", MOTHERBOARD,
313 		    SWITCH_A);
314 		revision = stpaul_get_int_propval(pcih, OBP_PROP_REVISION_ID,
315 		    &err);
316 	}
317 
318 	if (strcmp(path, SWITCH_B_PATH) == 0) {
319 		device_found = 1;
320 		(void) snprintf(NAC, sizeof (NAC), "%s/%s", MOTHERBOARD,
321 		    SWITCH_B);
322 		revision = stpaul_get_int_propval(pcih, OBP_PROP_REVISION_ID,
323 		    &err);
324 	}
325 
326 	if (strcmp(path, SPL_LSI_PATH) == 0) {
327 		device_found = 1;
328 		(void) snprintf(NAC, sizeof (NAC), "%s/%s", MOTHERBOARD,
329 		    SPL_SAS_HBA);
330 		revision = stpaul_get_int_propval(pcih, OBP_PROP_REVISION_ID,
331 		    &err);
332 	}
333 
334 	if (strcmp(path, SPL_PCIE2PCI) == 0) {
335 		device_found = 1;
336 		(void) snprintf(NAC, sizeof (NAC), "%s/%s", MOTHERBOARD,
337 		    PCI_BRIDGE);
338 		revision = stpaul_get_int_propval(pcih, OBP_PROP_REVISION_ID,
339 		    &err);
340 	}
341 
342 	if (device_found == 1) {
343 
344 		(void) strlcpy(device_path, path, sizeof (device_path));
345 		err = stpaul_get_first_compatible_value(pcih, &compatible);
346 
347 		/* Print NAC name */
348 		log_printf("%-20s", NAC);
349 		/* Print Device Path */
350 		if (strlen(device_path) > 38)
351 			log_printf("%38.37s+", device_path);
352 		else
353 			log_printf("%39s", device_path);
354 		/* Print Compatible # */
355 		if (err == PICL_SUCCESS) {
356 			log_printf("%31s", compatible);
357 			free(compatible);
358 		} else
359 			log_printf("%31s", " ");
360 		/* Print Revision */
361 		log_printf("%6d", revision);
362 		log_printf("\n");
363 	}
364 
365 	return (PICL_WALK_CONTINUE);
366 }
367 
368 static void
369 get_bus_type(char *path, struct io_card *card)
370 {
371 	if (strncmp(path, SPL_PCIE_PEM0, strlen(SPL_PCIE_PEM0)) == 0) {
372 		(void) strlcpy(card->bus_type, "PCIE", sizeof (card->bus_type));
373 	} else if (strncmp(path, SPL_PCIE_PEM1, strlen(SPL_PCIE_PEM1)) == 0) {
374 		(void) strlcpy(card->bus_type, "PCIE", sizeof (card->bus_type));
375 	} else if (strncmp(path, SPL_PCIE_NEM0, strlen(SPL_PCIE_NEM0)) == 0) {
376 		(void) strlcpy(card->bus_type, "PCIE", sizeof (card->bus_type));
377 	} else if (strncmp(path, SPL_PCIE_NEM1, strlen(SPL_PCIE_NEM1)) == 0) {
378 		(void) strlcpy(card->bus_type, "PCIE", sizeof (card->bus_type));
379 	} else if (strncmp(path, SWITCH_A_PATH, strlen(SWITCH_A_PATH)) == 0) {
380 		(void) strlcpy(card->bus_type, "PCIE", sizeof (card->bus_type));
381 	} else if (strncmp(path, SWITCH_B_PATH, strlen(SWITCH_B_PATH)) == 0) {
382 		(void) strlcpy(card->bus_type, "PCIE", sizeof (card->bus_type));
383 	} else {
384 		(void) strlcpy(card->bus_type, "NONE", sizeof (card->bus_type));
385 	}
386 }
387 
388 static void
389 get_slot_number(char *path, struct io_card *card)
390 {
391 	if (strncmp(path, SPL_PCIE_PEM0, strlen(SPL_PCIE_PEM0)) == 0) {
392 		(void) strlcpy(card->slot_str, "0", sizeof (card->slot_str));
393 		card->slot = 0;
394 	} else if (strncmp(path, SPL_PCIE_NEM0, strlen(SPL_PCIE_NEM0)) == 0) {
395 		(void) strlcpy(card->slot_str, "0", sizeof (card->slot_str));
396 		card->slot = 0;
397 	} else if (strncmp(path, SPL_PCIE_PEM1, strlen(SPL_PCIE_PEM1)) == 0) {
398 		(void) strlcpy(card->slot_str, "1", sizeof (card->slot_str));
399 		card->slot = 1;
400 	} else if (strncmp(path, SPL_PCIE_NEM1, strlen(SPL_PCIE_NEM1)) == 0) {
401 		(void) strlcpy(card->slot_str, "1", sizeof (card->slot_str));
402 		card->slot = 1;
403 	} else {
404 		(void) strlcpy(card->slot_str, MOTHERBOARD,
405 		    sizeof (card->slot_str));
406 		card->slot = -1;
407 	}
408 }
409 
410 static int
411 stpaul_get_network_instance(char *path)
412 {
413 	if (strncmp(path, SPL_NETWORK_1_PATH,
414 	    strlen(SPL_NETWORK_1_PATH)) == 0)
415 		return (1);
416 	else if (strncmp(path, SPL_NETWORK_0_PATH,
417 	    strlen(SPL_NETWORK_0_PATH)) == 0)
418 		return (0);
419 	else
420 		return (-1);
421 }
422 
423 static int
424 stpaul_get_usb_instance(char *path)
425 {
426 	if (strncmp(path, SPL_USB2_PATH, strlen(SPL_USB2_PATH)) == 0)
427 		return (2);
428 	else if (strncmp(path, SPL_USB1_PATH, strlen(SPL_USB1_PATH)) == 0)
429 		return (1);
430 	else if (strncmp(path, SPL_USB0_PATH, strlen(path)) == 0)
431 		return (0);
432 	else
433 		return (-1);
434 }
435 
436 static int
437 stpaul_get_io_instance(char *path, char *type)
438 {
439 	if (strncmp(path, SPL_PCIE_PEM1, strlen(SPL_PCIE_PEM1)) == 0) {
440 		*type = SPL_PEM_TYPE;
441 		return (1);
442 	} else if (strncmp(path, SPL_PCIE_PEM0, strlen(SPL_PCIE_PEM0)) == 0) {
443 		*type = SPL_PEM_TYPE;
444 		return (0);
445 	} else if (strncmp(path, SPL_PCIE_NEM1, strlen(SPL_PCIE_NEM1)) == 0) {
446 		*type = SPL_NEM_TYPE;
447 		return (1);
448 	} else if (strncmp(path, SPL_PCIE_NEM0, strlen(SPL_PCIE_NEM0)) == 0) {
449 		*type = SPL_NEM_TYPE;
450 		return (0);
451 	} else
452 		return (-1);
453 }
454 /*
455  * return the first compatible value
456  */
457 static int
458 stpaul_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf)
459 {
460 	int		err;
461 	picl_prophdl_t	proph;
462 	picl_propinfo_t	pinfo;
463 	picl_prophdl_t	tblh;
464 	picl_prophdl_t	rowproph;
465 	char		*pval;
466 
467 	err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE,
468 	    &pinfo, &proph);
469 	if (err != PICL_SUCCESS)
470 		return (err);
471 
472 	if (pinfo.type == PICL_PTYPE_CHARSTRING) {
473 		pval = malloc(pinfo.size);
474 		if (pval == NULL)
475 			return (PICL_FAILURE);
476 		err = picl_get_propval(proph, pval, pinfo.size);
477 		if (err != PICL_SUCCESS) {
478 			free(pval);
479 			return (err);
480 		}
481 		*outbuf = pval;
482 		return (PICL_SUCCESS);
483 	}
484 
485 	if (pinfo.type != PICL_PTYPE_TABLE)
486 		return (PICL_FAILURE);
487 
488 	/* get first string from table */
489 	err = picl_get_propval(proph, &tblh, pinfo.size);
490 	if (err != PICL_SUCCESS)
491 		return (err);
492 
493 	err = picl_get_next_by_row(tblh, &rowproph);
494 	if (err != PICL_SUCCESS)
495 		return (err);
496 
497 	err = picl_get_propinfo(rowproph, &pinfo);
498 	if (err != PICL_SUCCESS)
499 		return (err);
500 
501 	pval = malloc(pinfo.size);
502 	if (pval == NULL)
503 		return (PICL_FAILURE);
504 
505 	err = picl_get_propval(rowproph, pval, pinfo.size);
506 	if (err != PICL_SUCCESS) {
507 		free(pval);
508 		return (err);
509 	}
510 
511 	*outbuf = pval;
512 	return (PICL_SUCCESS);
513 }
514 
515 static int64_t
516 stpaul_get_int_propval(picl_nodehdl_t modh, char *prop_name, int *ret)
517 {
518 	int		err;
519 	picl_prophdl_t	proph;
520 	picl_propinfo_t	pinfo;
521 	int8_t		int8v;
522 	int16_t		int16v;
523 	int32_t		int32v;
524 	int64_t		int64v;
525 
526 	err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph);
527 	if (err != PICL_SUCCESS) {
528 		*ret = err;
529 		return (0);
530 	}
531 
532 	/*
533 	 * If it is not an int, uint or byte array prop, return failure
534 	 */
535 	if ((pinfo.type != PICL_PTYPE_INT) &&
536 	    (pinfo.type != PICL_PTYPE_UNSIGNED_INT) &&
537 	    (pinfo.type != PICL_PTYPE_BYTEARRAY)) {
538 		*ret = PICL_FAILURE;
539 		return (0);
540 	}
541 
542 	switch (pinfo.size) {
543 	case sizeof (int8_t):
544 		err = picl_get_propval(proph, &int8v, sizeof (int8v));
545 		*ret = err;
546 		return (int8v);
547 	case sizeof (int16_t):
548 		err = picl_get_propval(proph, &int16v, sizeof (int16v));
549 		*ret = err;
550 		return (int16v);
551 	case sizeof (int32_t):
552 		err = picl_get_propval(proph, &int32v, sizeof (int32v));
553 		*ret = err;
554 		return (int32v);
555 	case sizeof (int64_t):
556 		err = picl_get_propval(proph, &int64v, sizeof (int64v));
557 		*ret = err;
558 		return (int64v);
559 	default:	/* not supported size */
560 		*ret = PICL_FAILURE;
561 		return (0);
562 	}
563 }
564