11898Shueston /*
21898Shueston  * CDDL HEADER START
31898Shueston  *
41898Shueston  * The contents of this file are subject to the terms of the
51898Shueston  * Common Development and Distribution License (the "License").
61898Shueston  * You may not use this file except in compliance with the License.
71898Shueston  *
81898Shueston  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91898Shueston  * or http://www.opensolaris.org/os/licensing.
101898Shueston  * See the License for the specific language governing permissions
111898Shueston  * and limitations under the License.
121898Shueston  *
131898Shueston  * When distributing Covered Code, include this CDDL HEADER in each
141898Shueston  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151898Shueston  * If applicable, add the following below this CDDL HEADER, with the
161898Shueston  * fields enclosed by brackets "[]" replaced with your own identifying
171898Shueston  * information: Portions Copyright [yyyy] [name of copyright owner]
181898Shueston  *
191898Shueston  * CDDL HEADER END
201898Shueston  */
211898Shueston 
221898Shueston /*
23*11583SSurya.Prakki@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
241898Shueston  * Use is subject to license terms.
251898Shueston  */
261898Shueston 
271898Shueston /*
281898Shueston  * SUNW,OPL-Enterprise platform ioboard topology enumerator
291898Shueston  */
301898Shueston #include <string.h>
311898Shueston #include <strings.h>
321898Shueston #include <libdevinfo.h>
331898Shueston #include <fm/topo_mod.h>
343062Scindi #include <fm/topo_hc.h>
351898Shueston #include <sys/fm/protocol.h>
361898Shueston #include "opl_topo.h"
371898Shueston 
381898Shueston #define	IOB_ENUMR_VERS	1
391898Shueston #define	FRUNAME		"iou"
401898Shueston #define	LABEL		FRUNAME "#%d"
411898Shueston #define	IOBDFRU		"hc:///component=" LABEL
421898Shueston 
436297Sjl139090 #define	IKKAKU_FRUNAME	"MBU_A"
446297Sjl139090 #define	IKKAKU_LABEL	IKKAKU_FRUNAME
456297Sjl139090 #define	IKKAKU_IOBDFRU	"hc:///component=" IKKAKU_LABEL
466297Sjl139090 
471898Shueston static int opl_iob_enum(topo_mod_t *hdl, tnode_t *parent, const char *name,
483062Scindi     topo_instance_t imin, topo_instance_t imax, void *notused1, void *notused2);
491898Shueston 
503062Scindi static const topo_modops_t Iobops =
513062Scindi 	{ opl_iob_enum, NULL };
523062Scindi 
533062Scindi static const topo_modinfo_t IobInfo = {
541898Shueston 	IOBOARD,
553062Scindi 	FM_FMRI_SCHEME_HC,
561898Shueston 	IOB_ENUMR_VERS,
573062Scindi 	&Iobops};
581898Shueston 
596297Sjl139090 /* OPL model type */
606297Sjl139090 typedef enum {
616297Sjl139090 	MODEL_FF,
626297Sjl139090 	MODEL_DC,
636297Sjl139090 	MODEL_IKKAKU
646297Sjl139090 } opl_model_t;
656297Sjl139090 
661898Shueston void
_topo_init(topo_mod_t * modhdl)671898Shueston _topo_init(topo_mod_t *modhdl)
681898Shueston {
691898Shueston 	/*
701898Shueston 	 * Turn on module debugging output
711898Shueston 	 */
721898Shueston 	if (getenv("TOPOIOBDBG") != NULL)
733062Scindi 		topo_mod_setdebug(modhdl);
741898Shueston 	topo_mod_dprintf(modhdl, "initializing ioboard enumerator\n");
751898Shueston 
76*11583SSurya.Prakki@Sun.COM 	(void) topo_mod_register(modhdl, &IobInfo, TOPO_VERSION);
771898Shueston }
781898Shueston 
791898Shueston void
_topo_fini(topo_mod_t * modhdl)801898Shueston _topo_fini(topo_mod_t *modhdl)
811898Shueston {
821898Shueston 	topo_mod_unregister(modhdl);
831898Shueston }
841898Shueston 
851898Shueston /*
861898Shueston  * Checks to see if there's a physical board number property on this
871898Shueston  * device node.
881898Shueston  */
891898Shueston static int
opl_get_physical_board(topo_mod_t * mod,di_node_t n)903062Scindi opl_get_physical_board(topo_mod_t *mod, di_node_t n)
911898Shueston {
923062Scindi 	di_prom_handle_t ptp = DI_PROM_HANDLE_NIL;
931898Shueston 	di_prom_prop_t pp = DI_PROM_PROP_NIL;
941898Shueston 	uchar_t *buf;
951898Shueston 	int val;
961898Shueston 
973062Scindi 	if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL)
983062Scindi 		return (-1);
992499Shueston 
1003062Scindi 	for (pp = di_prom_prop_next(ptp, n, pp);
1011898Shueston 	    pp != DI_PROM_PROP_NIL;
1023062Scindi 	    pp = di_prom_prop_next(ptp, n, pp)) {
1031898Shueston 		if (strcmp(di_prom_prop_name(pp), OPL_PHYSICAL_BD) == 0) {
1041898Shueston 			if (di_prom_prop_data(pp, &buf) < sizeof (val))
1051898Shueston 				continue;
1061898Shueston 			bcopy(buf, &val, sizeof (val));
1071898Shueston 			return (val);
1081898Shueston 		}
1091898Shueston 	}
1101898Shueston 	return (-1);
1111898Shueston }
1121898Shueston 
1131898Shueston /*
1141898Shueston  * Creates a map of logical boards to physical location.
1151898Shueston  */
1161898Shueston static void
opl_map_boards(topo_mod_t * mod,di_node_t opl_devtree,int lsb_to_psb[OPL_IOB_MAX])1173062Scindi opl_map_boards(topo_mod_t *mod, di_node_t opl_devtree,
1183062Scindi     int lsb_to_psb[OPL_IOB_MAX])
1191898Shueston {
1201898Shueston 	di_node_t n;
1211898Shueston 	int i;
1221898Shueston 
1231898Shueston 	/* Initialize all entries to no mapping */
1241898Shueston 	for (i = 0; i < OPL_IOB_MAX; i++) {
1251898Shueston 		lsb_to_psb[i] = i;
1261898Shueston 	}
1271898Shueston 	/*
1281898Shueston 	 * Get LSB-to-PSB (logical-to-physical board) mapping by finding the
1291898Shueston 	 * memory controller driver per LSB. The MC driver will have a
1301898Shueston 	 * physical-board# property.
1311898Shueston 	 */
1321898Shueston 	for (n = di_drv_first_node(OPL_MC_DRV, opl_devtree);
1331898Shueston 	    n != DI_NODE_NIL;
1341898Shueston 	    n = di_drv_next_node(n)) {
1352499Shueston 		int a, lsb, psb;
1361898Shueston 		char *ba = di_bus_addr(n);
1372499Shueston 		if (ba == NULL) {
1382499Shueston 			/*
1392499Shueston 			 * di_bus_addr returned NULL. This can happen during
1402499Shueston 			 * DR attach/detach of the mc driver. Just skip this
1412499Shueston 			 * node for now.
1422499Shueston 			 */
1432499Shueston 			continue;
1442499Shueston 		}
1452499Shueston 		a = OPL_MC_STR2BA(ba);
1462499Shueston 		lsb = OPL_MC_LSB(a);
1471898Shueston 
1483062Scindi 		psb = opl_get_physical_board(mod, n);
1491898Shueston 		if (psb < 0 || psb >= OPL_IOB_MAX) {
1501898Shueston 			/* psb mapping is out of range, skip */
1511898Shueston 			continue;
1521898Shueston 		}
1531898Shueston 		lsb_to_psb[lsb] = psb;
1541898Shueston 	}
1551898Shueston }
1561898Shueston 
1571898Shueston /*
1581898Shueston  * Create the ioboard node. Add fru and label properties, and create room
1591898Shueston  * for child hostbridge nodes.
1606297Sjl139090  *
1616297Sjl139090  * Only IKKAKU model has different IO topology.
1621898Shueston  */
1631898Shueston static tnode_t *
opl_iob_node_create(topo_mod_t * mp,tnode_t * parent,int inst,opl_model_t opl_model)1646297Sjl139090 opl_iob_node_create(topo_mod_t *mp, tnode_t *parent, int inst,
1656297Sjl139090     opl_model_t opl_model)
1661898Shueston {
1671898Shueston 	int err;
1681898Shueston 	tnode_t *ion;
1691898Shueston 	nvlist_t *fmri;
1701898Shueston 	char label[8];
1711898Shueston 	char fmri_str[32];
1723062Scindi 	nvlist_t *auth = topo_mod_auth(mp, parent);
1731898Shueston 
1741898Shueston 	if (parent == NULL || inst < 0) {
1751898Shueston 		return (NULL);
1761898Shueston 	}
1771898Shueston 
1781898Shueston 	/* Create ioboard FMRI */
1793062Scindi 	if ((fmri = topo_mod_hcfmri(mp, parent, FM_HC_SCHEME_VERSION, IOBOARD,
1803062Scindi 	    inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
1813062Scindi 		nvlist_free(auth);
1822499Shueston 		topo_mod_dprintf(mp, "create of tnode for ioboard failed: %s\n",
1831898Shueston 		    topo_strerror(topo_mod_errno(mp)));
1841898Shueston 		return (NULL);
1851898Shueston 	}
1863062Scindi 	nvlist_free(auth);
1871898Shueston 	/* Create node for this ioboard */
1883062Scindi 	ion = topo_node_bind(mp, parent, IOBOARD, inst, fmri);
1891898Shueston 	if (ion == NULL) {
1901898Shueston 		nvlist_free(fmri);
1912499Shueston 		topo_mod_dprintf(mp, "unable to bind ioboard: %s\n",
1921898Shueston 		    topo_strerror(topo_mod_errno(mp)));
1931898Shueston 		return (NULL); /* mod_errno already set */
1941898Shueston 	}
1951898Shueston 	nvlist_free(fmri);
1961898Shueston 	/* Create and add FRU fmri for this ioboard */
1976297Sjl139090 	if (opl_model == MODEL_IKKAKU)
1986297Sjl139090 		(void) snprintf(fmri_str, sizeof (fmri_str), IKKAKU_IOBDFRU);
1996297Sjl139090 	else
2006297Sjl139090 		(void) snprintf(fmri_str, sizeof (fmri_str), IOBDFRU, inst);
2013062Scindi 	if (topo_mod_str2nvl(mp, fmri_str, &fmri) == 0) {
2021898Shueston 		(void) topo_node_fru_set(ion, fmri, 0, &err);
2031898Shueston 		nvlist_free(fmri);
2041898Shueston 	}
2051898Shueston 	/* Add label for this ioboard */
2066297Sjl139090 	if (opl_model == MODEL_IKKAKU)
2076297Sjl139090 		(void) snprintf(label, sizeof (label), IKKAKU_LABEL);
2086297Sjl139090 	else
2096297Sjl139090 		(void) snprintf(label, sizeof (label), LABEL, inst);
2101898Shueston 	(void) topo_node_label_set(ion, label, &err);
2111898Shueston 
2121898Shueston 	/* Create range of hostbridges on this ioboard */
2131898Shueston 	if (topo_node_range_create(mp, ion, HOSTBRIDGE, 0, OPL_HB_MAX) != 0) {
2142499Shueston 		topo_mod_dprintf(mp, "topo_node_range_create failed: %s\n",
2151898Shueston 		    topo_strerror(topo_mod_errno(mp)));
2161898Shueston 		return (NULL);
2171898Shueston 	}
2181898Shueston 
2191898Shueston 	return (ion);
2201898Shueston }
2211898Shueston 
2226297Sjl139090 /*
2236297Sjl139090  * get the OPL model name from rootnode property "model"
2246297Sjl139090  */
2256297Sjl139090 static int
opl_get_model(topo_mod_t * mp,di_node_t opl_devtree,char * model)2266297Sjl139090 opl_get_model(topo_mod_t *mp, di_node_t opl_devtree, char *model)
2276297Sjl139090 {
2286297Sjl139090 	char *bufp;
2296297Sjl139090 	di_prom_handle_t promh = DI_PROM_HANDLE_NIL;
2306297Sjl139090 
2316297Sjl139090 	if (opl_devtree == DI_NODE_NIL ||
2326297Sjl139090 	    (promh = topo_mod_prominfo(mp)) == DI_PROM_HANDLE_NIL)
2336297Sjl139090 		return (-1);
2346297Sjl139090 
2356297Sjl139090 	if (di_prom_prop_lookup_bytes(promh, opl_devtree, "model",
2366297Sjl139090 	    (unsigned char **)&bufp) != -1) {
2376297Sjl139090 		(void) strlcpy(model, bufp, MAXNAMELEN);
2386297Sjl139090 		return (0);
2396297Sjl139090 	} else {
2406297Sjl139090 		return (-1);
2416297Sjl139090 	}
2426297Sjl139090 
2436297Sjl139090 }
2446297Sjl139090 
2451898Shueston /*ARGSUSED*/
2461898Shueston static int
opl_iob_enum(topo_mod_t * mp,tnode_t * parent,const char * name,topo_instance_t imin,topo_instance_t imax,void * notused1,void * notused2)2471898Shueston opl_iob_enum(topo_mod_t *mp, tnode_t *parent, const char *name,
2483062Scindi     topo_instance_t imin, topo_instance_t imax, void *notused1, void *notused2)
2491898Shueston {
2503062Scindi 	di_node_t opl_devtree;
2511898Shueston 	di_node_t pnode;
2521898Shueston 	tnode_t *ion;
2531898Shueston 	topo_instance_t inst;
2541898Shueston 	int lsb_to_psb[OPL_IOB_MAX];
2551898Shueston 	ioboard_contents_t ioboard_list[OPL_IOB_MAX];
2561898Shueston 	int retval = 0;
2576297Sjl139090 	char model[MAXNAMELEN];
2586297Sjl139090 	opl_model_t opl_model = MODEL_FF;
2591898Shueston 
2601898Shueston 	/* Validate the name is correct */
2611898Shueston 	if (strcmp(name, "ioboard") != 0) {
2621898Shueston 		return (-1);
2631898Shueston 	}
2641898Shueston 	/* Make sure we don't exceed OPL_IOB_MAX */
2651898Shueston 	if (imax >= OPL_IOB_MAX) {
2661898Shueston 		imax = OPL_IOB_MAX;
2671898Shueston 	}
2681898Shueston 
2691898Shueston 	bzero(ioboard_list, sizeof (ioboard_list));
2701898Shueston 
2713062Scindi 	opl_devtree = topo_mod_devinfo(mp);
2721898Shueston 	if (opl_devtree == DI_NODE_NIL) {
2731898Shueston 		(void) topo_mod_seterrno(mp, errno);
2742499Shueston 		topo_mod_dprintf(mp, "devinfo init failed.\n");
2751898Shueston 		return (-1);
2761898Shueston 	}
2771898Shueston 
2786297Sjl139090 	if (opl_get_model(mp, opl_devtree, model) == -1) {
2796297Sjl139090 		topo_mod_dprintf(mp, "opl_get_model failed.\n");
2806297Sjl139090 	} else {
2816297Sjl139090 		if (strncmp(model, "FF", 2) == 0)
2826297Sjl139090 			opl_model = MODEL_FF;
2836297Sjl139090 		else if (strncmp(model, "DC", 2) == 0)
2846297Sjl139090 			opl_model = MODEL_DC;
2856297Sjl139090 		else if (strcmp(model, "IKKAKU") == 0)
2866297Sjl139090 			opl_model = MODEL_IKKAKU;
2876297Sjl139090 
2886297Sjl139090 		topo_mod_dprintf(mp, "opl_get_model %s found.\n", model);
2896297Sjl139090 	}
2906297Sjl139090 
2911898Shueston 	/*
2921898Shueston 	 * Create a mapping from logical board numbers (which are part of
2931898Shueston 	 * the device node bus address) to physical board numbers, so we
2941898Shueston 	 * can create meaningful fru labels.
2951898Shueston 	 */
2963062Scindi 	opl_map_boards(mp, opl_devtree, lsb_to_psb);
2971898Shueston 
2981898Shueston 	/*
2991898Shueston 	 * Figure out which boards are installed by finding hostbridges
3001898Shueston 	 * with matching bus addresses.
3011898Shueston 	 */
3021898Shueston 	for (pnode = di_drv_first_node(OPL_PX_DRV, opl_devtree);
3031898Shueston 	    pnode != DI_NODE_NIL;
3041898Shueston 	    pnode = di_drv_next_node(pnode)) {
3051898Shueston 		int psb = -1;
3066216Skd93003 		int a, lsb, hb, rc;
3076216Skd93003 
3086216Skd93003 		/* Get the bus address */
3091898Shueston 		char *ba = di_bus_addr(pnode);
3106216Skd93003 		if (ba == NULL || (*ba == '\0')) {
3116216Skd93003 			return (-1); /* Return if it's not assigned */
3126216Skd93003 		}
3136216Skd93003 
3146216Skd93003 		a = OPL_PX_STR2BA(ba);
3156216Skd93003 		lsb = OPL_PX_LSB(a);
3166216Skd93003 		hb = OPL_PX_HB(a);
3176216Skd93003 		rc = OPL_PX_RC(a);
3181898Shueston 		/* Map logical system board to physical system board */
3191898Shueston 		if (lsb >= 0 && lsb <= OPL_IOB_MAX) {
3201898Shueston 			psb = lsb_to_psb[lsb];
3211898Shueston 		}
3221898Shueston 		/* If valid psb, note that this board exists */
3231898Shueston 		if (psb >= 0 && psb < OPL_IOB_MAX) {
3241898Shueston 			ioboard_list[psb].count++;
3251898Shueston 			ioboard_list[psb].rcs[hb][rc] = pnode;
3261898Shueston 		}
3271898Shueston 	}
3281898Shueston 
3291898Shueston 	/*
3301898Shueston 	 * Now enumerate each existing board  Exit loop if retval is
3311898Shueston 	 * ever set to non-zero.
3321898Shueston 	 */
3331898Shueston 	for (inst = imin; inst <= imax && retval == 0; inst++) {
3341898Shueston 		/* If this board doesn't contain any hostbridges, skip it */
3351898Shueston 		if (ioboard_list[inst].count == 0) {
3361898Shueston 			continue;
3371898Shueston 		}
3381898Shueston 		/* Create node for this ioboard */
3396297Sjl139090 		ion = opl_iob_node_create(mp, parent, inst, opl_model);
3401898Shueston 		if (ion == NULL) {
3411898Shueston 			topo_mod_dprintf(mp,
3422499Shueston 			    "enumeration of ioboard failed: %s\n",
3431898Shueston 			    topo_strerror(topo_mod_errno(mp)));
3441898Shueston 			retval = -1;
3451898Shueston 			break;
3461898Shueston 		}
3471898Shueston 		/* Enumerate hostbridges on this ioboard, sets errno */
3483062Scindi 		retval = opl_hb_enum(mp, &ioboard_list[inst], ion, inst);
3491898Shueston 	}
3501898Shueston 	return (retval);
3511898Shueston }
352