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#include <string.h>
28#include <strings.h>
29#include <libdevinfo.h>
30#include <fm/topo_mod.h>
31#include <fm/topo_hc.h>
32#include <sys/fm/protocol.h>
33#include "cpuboard_topo.h"
34
35static const topo_pgroup_info_t io_pgroup =
36	{ TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
37static const topo_pgroup_info_t pci_pgroup =
38	{ TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
39
40static tnode_t *
41cpuboard_node_create(topo_mod_t *mp, tnode_t *parent, const char *name,
42    int inst, void *priv)
43{
44	tnode_t *node;
45	nvlist_t *fmri;
46	nvlist_t *auth = topo_mod_auth(mp, parent);
47
48	topo_mod_dprintf(mp, "cpuboard_node_create:\n");
49
50	if (parent == NULL || inst < 0) {
51		return (NULL);
52	}
53
54	/* Create FMRI */
55	if ((fmri = topo_mod_hcfmri(mp, parent, FM_HC_SCHEME_VERSION, name,
56	    inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
57		topo_mod_dprintf(mp, "create of tnode for %s failed: %s",
58		    name, topo_strerror(topo_mod_errno(mp)));
59		nvlist_free(auth);
60		return (NULL);
61	}
62	nvlist_free(auth);
63
64	/* Create and bind node  */
65	node = topo_node_bind(mp, parent, name, inst, fmri);
66	if (node == NULL) {
67		nvlist_free(fmri);
68		topo_mod_dprintf(mp, "unable to bind root complex: %s\n",
69		    topo_strerror(topo_mod_errno(mp)));
70		return (NULL); /* mod_errno already set */
71	}
72
73	nvlist_free(fmri);
74	topo_node_setspecific(node, priv);
75
76	return (node);
77}
78
79/*
80 * cpuboard_rc_node_create()
81 * Description:
82 *     Create a root complex node pciexrc
83 * Parameters:
84 *     mp: topo module pointer
85 *     parent: topo parent node of the newly created pciexrc node
86 *     dnode: Solaris device node of the root complex
87 *     rcpath: Used to populated the dev property of the topo pciexrc node if
88 *          the local host does not own the root complex.
89 */
90static tnode_t *
91cpuboard_rc_node_create(topo_mod_t *mp, tnode_t *parent, di_node_t dnode,
92    char *rcpath, int inst)
93{
94	int err;
95	tnode_t *rcn;
96	char *dnpath;
97	nvlist_t *mod;
98
99	topo_mod_dprintf(mp, "cpuboard_rc_node_create:\n");
100
101	rcn = cpuboard_node_create(mp, parent, PCIEX_ROOT, inst, (void *)dnode);
102	if (rcn == NULL) {
103		return (NULL);
104	}
105
106	/* Inherit parent FRU's label */
107	(void) topo_node_fru_set(rcn, NULL, 0, &err);
108	(void) topo_node_label_set(rcn, NULL, &err);
109
110	/*
111	 * Set ASRU to be the dev-scheme ASRU
112	 */
113	if ((dnpath = di_devfs_path(dnode)) != NULL) {
114		nvlist_t *fmri;
115
116		/*
117		 * The local host owns the root complex, so use the dev path
118		 * from the di_devfs_path(), instead of the passed in rcpath,
119		 * to populate the dev property.
120		 */
121		rcpath = dnpath;
122		fmri = topo_mod_devfmri(mp, FM_DEV_SCHEME_VERSION,
123		    dnpath, NULL);
124		if (fmri == NULL) {
125			topo_mod_dprintf(mp,
126			    "dev:///%s fmri creation failed.\n",
127			    dnpath);
128			(void) topo_mod_seterrno(mp, err);
129			di_devfs_path_free(dnpath);
130			return (NULL);
131		}
132		if (topo_node_asru_set(rcn, fmri, 0, &err) < 0) {
133			topo_mod_dprintf(mp, "topo_node_asru_set failed\n");
134			(void) topo_mod_seterrno(mp, err);
135			nvlist_free(fmri);
136			di_devfs_path_free(dnpath);
137			return (NULL);
138		}
139		nvlist_free(fmri);
140	} else {
141		topo_mod_dprintf(mp, "NULL di_devfs_path.\n");
142	}
143
144	/*
145	 * Set pciexrc properties for root complex nodes
146	 */
147
148	/* Add the io and pci property groups */
149	if (topo_pgroup_create(rcn, &io_pgroup, &err) < 0) {
150		topo_mod_dprintf(mp, "topo_pgroup_create failed\n");
151		di_devfs_path_free(dnpath);
152		(void) topo_mod_seterrno(mp, err);
153		return (NULL);
154	}
155	if (topo_pgroup_create(rcn, &pci_pgroup, &err) < 0) {
156		topo_mod_dprintf(mp, "topo_pgroup_create failed\n");
157		di_devfs_path_free(dnpath);
158		(void) topo_mod_seterrno(mp, err);
159		return (NULL);
160	}
161	/* Add the devfs path property */
162	if (rcpath) {
163		if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEV,
164		    TOPO_PROP_IMMUTABLE, rcpath, &err) != 0) {
165			topo_mod_dprintf(mp, "Failed to set DEV property\n");
166			(void) topo_mod_seterrno(mp, err);
167		}
168	}
169	if (dnpath) {
170		di_devfs_path_free(dnpath);
171	}
172	/* T5440 device type is always "pciex" */
173	if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEVTYPE,
174	    TOPO_PROP_IMMUTABLE, CPUBOARD_PX_DEVTYPE, &err) != 0) {
175		topo_mod_dprintf(mp, "Failed to set DEVTYPE property\n");
176	}
177	/* T5440 driver is always "px" */
178	if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DRIVER,
179	    TOPO_PROP_IMMUTABLE, CPUBOARD_PX_DRV, &err) != 0) {
180		topo_mod_dprintf(mp, "Failed to set DRIVER property\n");
181	}
182	if ((mod = topo_mod_modfmri(mp, FM_MOD_SCHEME_VERSION, CPUBOARD_PX_DRV))
183	    == NULL || topo_prop_set_fmri(rcn, TOPO_PGROUP_IO,
184	    TOPO_IO_MODULE, TOPO_PROP_IMMUTABLE, mod,  &err) != 0) {
185		topo_mod_dprintf(mp, "Failed to set MODULE property\n");
186	}
187	nvlist_free(mod);
188
189	/* This is a PCIEX Root Complex */
190	if (topo_prop_set_string(rcn, TOPO_PGROUP_PCI, TOPO_PCI_EXCAP,
191	    TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err) != 0) {
192		topo_mod_dprintf(mp, "Failed to set EXCAP property\n");
193	}
194	/* BDF of T5440 root complex is constant */
195	if (topo_prop_set_string(rcn, TOPO_PGROUP_PCI,
196	    TOPO_PCI_BDF, TOPO_PROP_IMMUTABLE, CPUBOARD_PX_BDF, &err) != 0) {
197		topo_mod_dprintf(mp, "Failed to set EXCAP property\n");
198	}
199
200	/* Make room for children */
201	(void) topo_node_range_create(mp, rcn, PCIEX_BUS, 0, CPUBOARD_MAX);
202	return (rcn);
203}
204
205/*
206 * Create a hostbridge node.
207 */
208static tnode_t *
209cpuboard_hb_node_create(topo_mod_t *mp, tnode_t *parent, int inst)
210{
211	int err;
212	tnode_t *hbn;
213
214	topo_mod_dprintf(mp, "cpuboard_hb_node_create: parent=%p, inst=%d\n",
215	    parent, inst);
216
217	hbn = cpuboard_node_create(mp, parent, HOSTBRIDGE, inst, NULL);
218	if (hbn == NULL) {
219		topo_mod_dprintf(mp, "cpuboard_hb_node_create: "
220		    "cpuboard_node_create() failed.\n");
221		return (NULL);
222	}
223
224	/* Inherit parent FRU's label */
225	(void) topo_node_fru_set(hbn, NULL, 0, &err);
226	(void) topo_node_label_set(hbn, NULL, &err);
227
228	/* Make room for children */
229	(void) topo_node_range_create(mp, hbn, PCIEX_ROOT, 0, CPUBOARD_MAX);
230
231	topo_mod_dprintf(mp, "cpuboard_hb_node_create: EXIT hbn=%p\n", hbn);
232
233	return (hbn);
234}
235
236/*
237 * Enumerate hostbridge on the cpuboard.  Hostbridge and root complex instances
238 * match the cpuboard instance.
239 */
240int
241cpuboard_hb_enum(topo_mod_t *mp, di_node_t dnode, char *rcpath,
242    tnode_t *cpubn, int brd)
243{
244	int hb;
245	int rc;
246	tnode_t *hbnode;
247	tnode_t *rcnode;
248	topo_mod_t *pcimod;
249
250	topo_mod_dprintf(mp, "cpuboard_hb_enum: brd: %d, cpubn=%p\n",
251	    brd, cpubn);
252
253	/* Load the pcibus module. We'll need it later. */
254	pcimod = topo_mod_load(mp, PCI_BUS, PCI_BUS_VERS);
255	if (pcimod == NULL) {
256		topo_mod_dprintf(mp, "can't load pcibus module: %s\n",
257		    topo_strerror(topo_mod_errno(mp)));
258		return (-1);
259	}
260	hb = rc = brd;
261
262	/* The root complex exists! */
263	topo_mod_dprintf(mp, "declaring "
264	    "/motherboard=0/cpuboard=%d/hostbridge=%d/"
265	    "pciexrc=%d\n", brd, hb, rc);
266
267	/* Create the hostbridge node */
268	hbnode = cpuboard_hb_node_create(mp, cpubn, hb);
269	if (hbnode == NULL) {
270		topo_mod_dprintf(mp,
271		    "unable to create hbnode: %s\n",
272		    topo_strerror(topo_mod_errno(mp)));
273		topo_mod_unload(pcimod);
274		return (-1);
275	}
276	/* Create the root complex node */
277	rcnode = cpuboard_rc_node_create(mp, hbnode, dnode, rcpath, rc);
278	if (rcnode == NULL) {
279		topo_mod_dprintf(mp,
280		    "unable to create rcnode: %s\n",
281		    topo_strerror(topo_mod_errno(mp)));
282		topo_mod_unload(pcimod);
283		return (-1);
284	}
285	/*
286	 * If dnode not NULL, enumerate pcibus nodes under the root complex.
287	 * If dnode NULL, skip enumeration.  Condition could occur if the RC
288	 * is assigned to non-control domain.
289	 */
290	if ((dnode != NULL) && topo_mod_enumerate(pcimod, rcnode,
291	    PCI_BUS, PCIEX_BUS, 0, 255, NULL) != 0) {
292		topo_mod_dprintf(mp,
293		    "error enumerating pcibus: %s\n",
294		    topo_strerror(topo_mod_errno(mp)));
295		topo_mod_unload(pcimod);
296		return (-1);
297	}
298	topo_mod_unload(pcimod);
299	return (0);
300}
301