Home | History | Annotate | Download | only in ioboard
      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 2010 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * SUNW,OPL-Enterprise platform ioboard topology enumerator
     29  */
     30 #include <string.h>
     31 #include <strings.h>
     32 #include <libdevinfo.h>
     33 #include <fm/topo_mod.h>
     34 #include <fm/topo_hc.h>
     35 #include <sys/fm/protocol.h>
     36 #include "opl_topo.h"
     37 
     38 #define	IOB_ENUMR_VERS	1
     39 #define	FRUNAME		"iou"
     40 #define	LABEL		FRUNAME "#%d"
     41 #define	IOBDFRU		"hc:///component=" LABEL
     42 
     43 #define	IKKAKU_FRUNAME	"MBU_A"
     44 #define	IKKAKU_LABEL	IKKAKU_FRUNAME
     45 #define	IKKAKU_IOBDFRU	"hc:///component=" IKKAKU_LABEL
     46 
     47 static int opl_iob_enum(topo_mod_t *hdl, tnode_t *parent, const char *name,
     48     topo_instance_t imin, topo_instance_t imax, void *notused1, void *notused2);
     49 
     50 static const topo_modops_t Iobops =
     51 	{ opl_iob_enum, NULL };
     52 
     53 static const topo_modinfo_t IobInfo = {
     54 	IOBOARD,
     55 	FM_FMRI_SCHEME_HC,
     56 	IOB_ENUMR_VERS,
     57 	&Iobops};
     58 
     59 /* OPL model type */
     60 typedef enum {
     61 	MODEL_FF,
     62 	MODEL_DC,
     63 	MODEL_IKKAKU
     64 } opl_model_t;
     65 
     66 void
     67 _topo_init(topo_mod_t *modhdl)
     68 {
     69 	/*
     70 	 * Turn on module debugging output
     71 	 */
     72 	if (getenv("TOPOIOBDBG") != NULL)
     73 		topo_mod_setdebug(modhdl);
     74 	topo_mod_dprintf(modhdl, "initializing ioboard enumerator\n");
     75 
     76 	(void) topo_mod_register(modhdl, &IobInfo, TOPO_VERSION);
     77 }
     78 
     79 void
     80 _topo_fini(topo_mod_t *modhdl)
     81 {
     82 	topo_mod_unregister(modhdl);
     83 }
     84 
     85 /*
     86  * Checks to see if there's a physical board number property on this
     87  * device node.
     88  */
     89 static int
     90 opl_get_physical_board(topo_mod_t *mod, di_node_t n)
     91 {
     92 	di_prom_handle_t ptp = DI_PROM_HANDLE_NIL;
     93 	di_prom_prop_t pp = DI_PROM_PROP_NIL;
     94 	uchar_t *buf;
     95 	int val;
     96 
     97 	if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL)
     98 		return (-1);
     99 
    100 	for (pp = di_prom_prop_next(ptp, n, pp);
    101 	    pp != DI_PROM_PROP_NIL;
    102 	    pp = di_prom_prop_next(ptp, n, pp)) {
    103 		if (strcmp(di_prom_prop_name(pp), OPL_PHYSICAL_BD) == 0) {
    104 			if (di_prom_prop_data(pp, &buf) < sizeof (val))
    105 				continue;
    106 			bcopy(buf, &val, sizeof (val));
    107 			return (val);
    108 		}
    109 	}
    110 	return (-1);
    111 }
    112 
    113 /*
    114  * Creates a map of logical boards to physical location.
    115  */
    116 static void
    117 opl_map_boards(topo_mod_t *mod, di_node_t opl_devtree,
    118     int lsb_to_psb[OPL_IOB_MAX])
    119 {
    120 	di_node_t n;
    121 	int i;
    122 
    123 	/* Initialize all entries to no mapping */
    124 	for (i = 0; i < OPL_IOB_MAX; i++) {
    125 		lsb_to_psb[i] = i;
    126 	}
    127 	/*
    128 	 * Get LSB-to-PSB (logical-to-physical board) mapping by finding the
    129 	 * memory controller driver per LSB. The MC driver will have a
    130 	 * physical-board# property.
    131 	 */
    132 	for (n = di_drv_first_node(OPL_MC_DRV, opl_devtree);
    133 	    n != DI_NODE_NIL;
    134 	    n = di_drv_next_node(n)) {
    135 		int a, lsb, psb;
    136 		char *ba = di_bus_addr(n);
    137 		if (ba == NULL) {
    138 			/*
    139 			 * di_bus_addr returned NULL. This can happen during
    140 			 * DR attach/detach of the mc driver. Just skip this
    141 			 * node for now.
    142 			 */
    143 			continue;
    144 		}
    145 		a = OPL_MC_STR2BA(ba);
    146 		lsb = OPL_MC_LSB(a);
    147 
    148 		psb = opl_get_physical_board(mod, n);
    149 		if (psb < 0 || psb >= OPL_IOB_MAX) {
    150 			/* psb mapping is out of range, skip */
    151 			continue;
    152 		}
    153 		lsb_to_psb[lsb] = psb;
    154 	}
    155 }
    156 
    157 /*
    158  * Create the ioboard node. Add fru and label properties, and create room
    159  * for child hostbridge nodes.
    160  *
    161  * Only IKKAKU model has different IO topology.
    162  */
    163 static tnode_t *
    164 opl_iob_node_create(topo_mod_t *mp, tnode_t *parent, int inst,
    165     opl_model_t opl_model)
    166 {
    167 	int err;
    168 	tnode_t *ion;
    169 	nvlist_t *fmri;
    170 	char label[8];
    171 	char fmri_str[32];
    172 	nvlist_t *auth = topo_mod_auth(mp, parent);
    173 
    174 	if (parent == NULL || inst < 0) {
    175 		return (NULL);
    176 	}
    177 
    178 	/* Create ioboard FMRI */
    179 	if ((fmri = topo_mod_hcfmri(mp, parent, FM_HC_SCHEME_VERSION, IOBOARD,
    180 	    inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
    181 		nvlist_free(auth);
    182 		topo_mod_dprintf(mp, "create of tnode for ioboard failed: %s\n",
    183 		    topo_strerror(topo_mod_errno(mp)));
    184 		return (NULL);
    185 	}
    186 	nvlist_free(auth);
    187 	/* Create node for this ioboard */
    188 	ion = topo_node_bind(mp, parent, IOBOARD, inst, fmri);
    189 	if (ion == NULL) {
    190 		nvlist_free(fmri);
    191 		topo_mod_dprintf(mp, "unable to bind ioboard: %s\n",
    192 		    topo_strerror(topo_mod_errno(mp)));
    193 		return (NULL); /* mod_errno already set */
    194 	}
    195 	nvlist_free(fmri);
    196 	/* Create and add FRU fmri for this ioboard */
    197 	if (opl_model == MODEL_IKKAKU)
    198 		(void) snprintf(fmri_str, sizeof (fmri_str), IKKAKU_IOBDFRU);
    199 	else
    200 		(void) snprintf(fmri_str, sizeof (fmri_str), IOBDFRU, inst);
    201 	if (topo_mod_str2nvl(mp, fmri_str, &fmri) == 0) {
    202 		(void) topo_node_fru_set(ion, fmri, 0, &err);
    203 		nvlist_free(fmri);
    204 	}
    205 	/* Add label for this ioboard */
    206 	if (opl_model == MODEL_IKKAKU)
    207 		(void) snprintf(label, sizeof (label), IKKAKU_LABEL);
    208 	else
    209 		(void) snprintf(label, sizeof (label), LABEL, inst);
    210 	(void) topo_node_label_set(ion, label, &err);
    211 
    212 	/* Create range of hostbridges on this ioboard */
    213 	if (topo_node_range_create(mp, ion, HOSTBRIDGE, 0, OPL_HB_MAX) != 0) {
    214 		topo_mod_dprintf(mp, "topo_node_range_create failed: %s\n",
    215 		    topo_strerror(topo_mod_errno(mp)));
    216 		return (NULL);
    217 	}
    218 
    219 	return (ion);
    220 }
    221 
    222 /*
    223  * get the OPL model name from rootnode property "model"
    224  */
    225 static int
    226 opl_get_model(topo_mod_t *mp, di_node_t opl_devtree, char *model)
    227 {
    228 	char *bufp;
    229 	di_prom_handle_t promh = DI_PROM_HANDLE_NIL;
    230 
    231 	if (opl_devtree == DI_NODE_NIL ||
    232 	    (promh = topo_mod_prominfo(mp)) == DI_PROM_HANDLE_NIL)
    233 		return (-1);
    234 
    235 	if (di_prom_prop_lookup_bytes(promh, opl_devtree, "model",
    236 	    (unsigned char **)&bufp) != -1) {
    237 		(void) strlcpy(model, bufp, MAXNAMELEN);
    238 		return (0);
    239 	} else {
    240 		return (-1);
    241 	}
    242 
    243 }
    244 
    245 /*ARGSUSED*/
    246 static int
    247 opl_iob_enum(topo_mod_t *mp, tnode_t *parent, const char *name,
    248     topo_instance_t imin, topo_instance_t imax, void *notused1, void *notused2)
    249 {
    250 	di_node_t opl_devtree;
    251 	di_node_t pnode;
    252 	tnode_t *ion;
    253 	topo_instance_t inst;
    254 	int lsb_to_psb[OPL_IOB_MAX];
    255 	ioboard_contents_t ioboard_list[OPL_IOB_MAX];
    256 	int retval = 0;
    257 	char model[MAXNAMELEN];
    258 	opl_model_t opl_model = MODEL_FF;
    259 
    260 	/* Validate the name is correct */
    261 	if (strcmp(name, "ioboard") != 0) {
    262 		return (-1);
    263 	}
    264 	/* Make sure we don't exceed OPL_IOB_MAX */
    265 	if (imax >= OPL_IOB_MAX) {
    266 		imax = OPL_IOB_MAX;
    267 	}
    268 
    269 	bzero(ioboard_list, sizeof (ioboard_list));
    270 
    271 	opl_devtree = topo_mod_devinfo(mp);
    272 	if (opl_devtree == DI_NODE_NIL) {
    273 		(void) topo_mod_seterrno(mp, errno);
    274 		topo_mod_dprintf(mp, "devinfo init failed.\n");
    275 		return (-1);
    276 	}
    277 
    278 	if (opl_get_model(mp, opl_devtree, model) == -1) {
    279 		topo_mod_dprintf(mp, "opl_get_model failed.\n");
    280 	} else {
    281 		if (strncmp(model, "FF", 2) == 0)
    282 			opl_model = MODEL_FF;
    283 		else if (strncmp(model, "DC", 2) == 0)
    284 			opl_model = MODEL_DC;
    285 		else if (strcmp(model, "IKKAKU") == 0)
    286 			opl_model = MODEL_IKKAKU;
    287 
    288 		topo_mod_dprintf(mp, "opl_get_model %s found.\n", model);
    289 	}
    290 
    291 	/*
    292 	 * Create a mapping from logical board numbers (which are part of
    293 	 * the device node bus address) to physical board numbers, so we
    294 	 * can create meaningful fru labels.
    295 	 */
    296 	opl_map_boards(mp, opl_devtree, lsb_to_psb);
    297 
    298 	/*
    299 	 * Figure out which boards are installed by finding hostbridges
    300 	 * with matching bus addresses.
    301 	 */
    302 	for (pnode = di_drv_first_node(OPL_PX_DRV, opl_devtree);
    303 	    pnode != DI_NODE_NIL;
    304 	    pnode = di_drv_next_node(pnode)) {
    305 		int psb = -1;
    306 		int a, lsb, hb, rc;
    307 
    308 		/* Get the bus address */
    309 		char *ba = di_bus_addr(pnode);
    310 		if (ba == NULL || (*ba == '\0')) {
    311 			return (-1); /* Return if it's not assigned */
    312 		}
    313 
    314 		a = OPL_PX_STR2BA(ba);
    315 		lsb = OPL_PX_LSB(a);
    316 		hb = OPL_PX_HB(a);
    317 		rc = OPL_PX_RC(a);
    318 		/* Map logical system board to physical system board */
    319 		if (lsb >= 0 && lsb <= OPL_IOB_MAX) {
    320 			psb = lsb_to_psb[lsb];
    321 		}
    322 		/* If valid psb, note that this board exists */
    323 		if (psb >= 0 && psb < OPL_IOB_MAX) {
    324 			ioboard_list[psb].count++;
    325 			ioboard_list[psb].rcs[hb][rc] = pnode;
    326 		}
    327 	}
    328 
    329 	/*
    330 	 * Now enumerate each existing board  Exit loop if retval is
    331 	 * ever set to non-zero.
    332 	 */
    333 	for (inst = imin; inst <= imax && retval == 0; inst++) {
    334 		/* If this board doesn't contain any hostbridges, skip it */
    335 		if (ioboard_list[inst].count == 0) {
    336 			continue;
    337 		}
    338 		/* Create node for this ioboard */
    339 		ion = opl_iob_node_create(mp, parent, inst, opl_model);
    340 		if (ion == NULL) {
    341 			topo_mod_dprintf(mp,
    342 			    "enumeration of ioboard failed: %s\n",
    343 			    topo_strerror(topo_mod_errno(mp)));
    344 			retval = -1;
    345 			break;
    346 		}
    347 		/* Enumerate hostbridges on this ioboard, sets errno */
    348 		retval = opl_hb_enum(mp, &ioboard_list[inst], ion, inst);
    349 	}
    350 	return (retval);
    351 }
    352