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
_topo_init(topo_mod_t * modhdl)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
_topo_fini(topo_mod_t * modhdl)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
opl_get_physical_board(topo_mod_t * mod,di_node_t n)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
opl_map_boards(topo_mod_t * mod,di_node_t opl_devtree,int lsb_to_psb[OPL_IOB_MAX])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 *
opl_iob_node_create(topo_mod_t * mp,tnode_t * parent,int inst,opl_model_t opl_model)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
opl_get_model(topo_mod_t * mp,di_node_t opl_devtree,char * model)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
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)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