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
41busorrc_t *
42busorrc_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
84void
85busorrc_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
123int
124busorrc_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
140void
141busorrc_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
149tnode_t *
150hb_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
165tnode_t *
166rc_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 */
202int
203declare_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 */
295int
296declare_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