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 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <string.h>
30 #include <fm/topo_mod.h>
31 #include <libdevinfo.h>
32 #include <sys/param.h>
33 #include <sys/systeminfo.h>
34
35 #include <hb_sun4.h>
36 #include <util.h>
37 #include <hostbridge.h>
38 #include <pcibus.h>
39 #include <did.h>
40
41 busorrc_t *
busorrc_new(topo_mod_t * mod,const char * bus_addr,di_node_t di)42 busorrc_new(topo_mod_t *mod, const char *bus_addr, di_node_t di)
43 {
44 busorrc_t *pp;
45 char *comma;
46 char *bac;
47 int e;
48
49 if ((pp = topo_mod_zalloc(mod, sizeof (busorrc_t))) == NULL)
50 return (NULL);
51 pp->br_din = di;
52 bac = topo_mod_strdup(mod, bus_addr);
53 if ((comma = strchr(bac, ',')) != NULL)
54 *comma = '\0';
55 pp->br_ba_bc = strtonum(mod, bac, &e);
56 if (e < 0) {
57 topo_mod_dprintf(mod,
58 "Trouble interpreting bus_addr before comma.\n");
59 if (comma != NULL)
60 *comma = ',';
61 topo_mod_strfree(mod, bac);
62 topo_mod_free(mod, pp, sizeof (busorrc_t));
63 return (NULL);
64 }
65 if (comma == NULL) {
66 pp->br_ba_ac = 0;
67 topo_mod_strfree(mod, bac);
68 return (pp);
69 }
70 pp->br_ba_ac = strtonum(mod, comma + 1, &e);
71 if (e < 0) {
72 topo_mod_dprintf(mod,
73 "Trouble interpreting bus_addr after comma.\n");
74 *comma = ',';
75 topo_mod_strfree(mod, bac);
76 topo_mod_free(mod, pp, sizeof (busorrc_t));
77 return (NULL);
78 }
79 *comma = ',';
80 topo_mod_strfree(mod, bac);
81 return (pp);
82 }
83
84 void
busorrc_insert(topo_mod_t * mod,busorrc_t ** head,busorrc_t * new)85 busorrc_insert(topo_mod_t *mod, busorrc_t **head, busorrc_t *new)
86 {
87 busorrc_t *ppci, *pci;
88
89 topo_mod_dprintf(mod,
90 "inserting (%x,%x)\n", new->br_ba_bc, new->br_ba_ac);
91
92 /* No entries yet? */
93 if (*head == NULL) {
94 *head = new;
95 return;
96 }
97
98 ppci = NULL;
99 pci = *head;
100
101 while (pci != NULL) {
102 if (new->br_ba_ac == pci->br_ba_ac)
103 if (new->br_ba_bc < pci->br_ba_bc)
104 break;
105 if (new->br_ba_ac < pci->br_ba_ac)
106 break;
107 ppci = pci;
108 pci = pci->br_nextbus;
109 }
110 if (ppci == NULL) {
111 new->br_nextbus = pci;
112 pci->br_prevbus = new;
113 *head = new;
114 } else {
115 new->br_nextbus = ppci->br_nextbus;
116 if (new->br_nextbus != NULL)
117 new->br_nextbus->br_prevbus = new;
118 ppci->br_nextbus = new;
119 new->br_prevbus = ppci;
120 }
121 }
122
123 int
busorrc_add(topo_mod_t * mod,busorrc_t ** list,di_node_t n)124 busorrc_add(topo_mod_t *mod, busorrc_t **list, di_node_t n)
125 {
126 busorrc_t *nb;
127 char *ba;
128
129 topo_mod_dprintf(mod, "busorrc_add\n");
130 ba = di_bus_addr(n);
131 if (ba == NULL ||
132 (nb = busorrc_new(mod, ba, n)) == NULL) {
133 topo_mod_dprintf(mod, "busorrc_new() failed.\n");
134 return (-1);
135 }
136 busorrc_insert(mod, list, nb);
137 return (0);
138 }
139
140 void
busorrc_free(topo_mod_t * mod,busorrc_t * pb)141 busorrc_free(topo_mod_t *mod, busorrc_t *pb)
142 {
143 if (pb == NULL)
144 return;
145 busorrc_free(mod, pb->br_nextbus);
146 topo_mod_free(mod, pb, sizeof (busorrc_t));
147 }
148
149 tnode_t *
hb_process(topo_mod_t * mod,tnode_t * ptn,topo_instance_t hbi,topo_instance_t bi,di_node_t bn,did_t * hbdid)150 hb_process(topo_mod_t *mod, tnode_t *ptn, topo_instance_t hbi,
151 topo_instance_t bi, di_node_t bn, did_t *hbdid)
152 {
153 tnode_t *hb;
154
155 if ((hb = pcihostbridge_declare(mod, ptn, bn, hbi)) == NULL)
156 return (NULL);
157 if (topo_mod_enumerate(mod, hb, PCI_BUS, PCI_BUS, bi, bi, hbdid) == 0)
158 return (hb);
159
160 topo_node_unbind(hb);
161
162 return (NULL);
163 }
164
165 tnode_t *
rc_process(topo_mod_t * mod,tnode_t * ptn,topo_instance_t rci,di_node_t bn)166 rc_process(topo_mod_t *mod, tnode_t *ptn, topo_instance_t rci, di_node_t bn)
167 {
168 tnode_t *rc;
169
170 if ((rc = pciexrc_declare(mod, ptn, bn, rci)) == NULL)
171 return (NULL);
172 if (topo_mod_enumerate(mod,
173 rc, PCI_BUS, PCIEX_BUS, 0, MAX_HB_BUSES, NULL) == 0)
174 return (rc);
175
176 topo_node_unbind(rc);
177
178 return (NULL);
179 }
180
181 /*
182 * declare_exbuses() assumes the elements in the provided busorrc list
183 * are sorted thusly:
184 *
185 * (Hostbridge #0, Root Complex #0, ExBus #0)
186 * (Hostbridge #0, Root Complex #0, ExBus #1)
187 * ...
188 * (Hostbridge #0, Root Complex #0, ExBus #(buses/rc))
189 * (Hostbridge #0, Root Complex #1, ExBus #0)
190 * ...
191 * (Hostbridge #0, Root Complex #1, ExBus #(buses/rc))
192 * ...
193 * ...
194 * (Hostbridge #0, Root Complex #(rcs/hostbridge), ExBus #(buses/rc))
195 * (Hostbridge #1, Root Complex #0, ExBus #0)
196 * ...
197 * ...
198 * ...
199 * ...
200 * (Hostbridge #nhb, Root Complex #(rcs/hostbridge), ExBus #(buses/rc))
201 */
202 int
declare_exbuses(topo_mod_t * mod,busorrc_t * list,tnode_t * ptn,int nhb,int nrc)203 declare_exbuses(topo_mod_t *mod, busorrc_t *list, tnode_t *ptn, int nhb,
204 int nrc)
205 {
206 int err = 0;
207 tnode_t **rcs;
208 tnode_t **hb;
209 busorrc_t *p;
210 int br, rc;
211
212 /*
213 * Allocate an array to point at the hostbridge tnode_t pointers.
214 */
215 if ((hb = topo_mod_zalloc(mod, nhb * sizeof (tnode_t *))) == NULL)
216 return (topo_mod_seterrno(mod, EMOD_NOMEM));
217
218 /*
219 * Allocate an array to point at the root complex tnode_t pointers.
220 */
221 if ((rcs = topo_mod_zalloc(mod, nrc * sizeof (tnode_t *))) == NULL) {
222 topo_mod_free(mod, hb, nhb * sizeof (tnode_t *));
223 return (topo_mod_seterrno(mod, EMOD_NOMEM));
224 }
225
226 br = rc = 0;
227 for (p = list; p != NULL; p = p->br_nextbus) {
228 topo_mod_dprintf(mod,
229 "declaring (%x,%x)\n", p->br_ba_bc, p->br_ba_ac);
230
231 if (did_create(mod, p->br_din, 0, br, rc, rc) == NULL) {
232 err = -1;
233 break;
234 }
235
236 if (hb[br] == NULL) {
237 hb[br] = pciexhostbridge_declare(mod, ptn, p->br_din,
238 br);
239 if (hb[br] == NULL) {
240 err = -1;
241 break;
242 }
243 }
244 if (rcs[rc] == NULL) {
245 rcs[rc] = rc_process(mod, hb[br], rc, p->br_din);
246 if (rcs[rc] == NULL) {
247 err = -1;
248 break;
249 }
250 } else {
251 if (topo_mod_enumerate(mod,
252 rcs[rc], PCI_BUS, PCIEX_BUS, 0, MAX_HB_BUSES,
253 NULL) < 0) {
254 err = -1;
255 break;
256 }
257 }
258 rc++;
259 if (rc == nrc) {
260 rc = 0;
261 br++;
262 if (br == nhb)
263 br = 0;
264 }
265 }
266
267 if (err != 0) {
268 int i;
269
270 for (i = 0; i < nhb; ++i)
271 topo_node_unbind(hb[br]);
272 for (i = 0; i < nrc; ++i)
273 topo_node_unbind(rcs[rc]);
274 }
275
276 topo_mod_free(mod, rcs, nrc * sizeof (tnode_t *));
277 topo_mod_free(mod, hb, nhb * sizeof (tnode_t *));
278
279 return (err);
280 }
281
282 /*
283 * declare_buses() assumes the elements in the provided busorrc list
284 * are sorted thusly:
285 *
286 * (Hostbridge #0, Bus #0)
287 * (Hostbridge #1, Bus #0)
288 * ...
289 * (Hostbridge #nhb, Bus #0)
290 * (Hostbridge #0, Bus #1)
291 * ...
292 * ...
293 * (Hostbridge #nhb, Bus #(buses/hostbridge))
294 */
295 int
declare_buses(topo_mod_t * mod,busorrc_t * list,tnode_t * ptn,int nhb)296 declare_buses(topo_mod_t *mod, busorrc_t *list, tnode_t *ptn, int nhb)
297 {
298 int err = 0;
299 busorrc_t *p;
300 tnode_t **hb;
301 did_t *link;
302 int br, bus;
303
304 /*
305 * Allocate an array to point at the hostbridge tnode_t pointers.
306 */
307 if ((hb = topo_mod_zalloc(mod, nhb * sizeof (tnode_t *))) == NULL)
308 return (topo_mod_seterrno(mod, EMOD_NOMEM));
309
310 br = bus = 0;
311 for (p = list; p != NULL; p = p->br_nextbus) {
312 topo_mod_dprintf(mod,
313 "declaring (%x,%x)\n", p->br_ba_bc, p->br_ba_ac);
314
315 if ((link =
316 did_create(mod, p->br_din, 0, br, NO_RC, bus)) == NULL) {
317 err = -1;
318 break;
319 }
320
321 if (hb[br] == NULL) {
322 hb[br] = hb_process(mod, ptn, br, bus, p->br_din, link);
323 if (hb[br] == NULL) {
324 err = -1;
325 break;
326 }
327 } else {
328 did_link_set(mod, hb[br], link);
329 if (topo_mod_enumerate(mod,
330 hb[br], PCI_BUS, PCI_BUS, bus, bus, link) < 0) {
331 err = -1;
332 break;
333 }
334 }
335 br++;
336 if (br == nhb) {
337 br = 0;
338 bus++;
339 }
340 }
341
342 if (err != 0) {
343 int i;
344
345 for (i = 0; i < nhb; ++i)
346 topo_node_unbind(hb[br]);
347 }
348
349 topo_mod_free(mod, hb, nhb * sizeof (tnode_t *));
350 return (err);
351 }
352