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