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 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <string.h>
28#include <fm/topo_mod.h>
29#include <fm/topo_hc.h>
30#include <libdevinfo.h>
31#include <limits.h>
32#include <sys/fm/protocol.h>
33#include <sys/param.h>
34#include <sys/systeminfo.h>
35#include <assert.h>
36#include <stdlib.h>
37
38/*
39 * niu.c
40 *	sun4v specific niu enumerators
41 */
42
43#ifdef __cplusplus
44extern "C" {
45#endif
46
47#define	NIU_VERSION	TOPO_VERSION
48#define	NIUFN_MAX	2
49#define	XAUI_MAX	1	/* max number of XAUIs per niufn */
50
51static int niu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
52		    topo_instance_t, void *, void *);
53
54static const topo_modops_t niu_ops =
55	{ niu_enum, NULL };
56
57const topo_modinfo_t niu_info =
58	{NIU, FM_FMRI_SCHEME_HC, NIU_VERSION, &niu_ops};
59
60static const topo_pgroup_info_t io_pgroup =
61	{ TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
62
63/*ARGSUSED*/
64void
65_topo_init(topo_mod_t *mod, topo_version_t version)
66{
67	/*
68	 * Turn on module debugging output
69	 */
70	if (getenv("TOPONIUDBG") != NULL)
71		topo_mod_setdebug(mod);
72	topo_mod_dprintf(mod, "initializing niu enumerator\n");
73
74	if (topo_mod_register(mod, &niu_info, TOPO_VERSION) < 0) {
75		topo_mod_dprintf(mod, "niu registration failed: %s\n",
76		    topo_mod_errmsg(mod));
77		return; /* mod errno already set */
78	}
79	topo_mod_dprintf(mod, "NIU enumr initd\n");
80}
81
82void
83_topo_fini(topo_mod_t *mod)
84{
85	topo_mod_unregister(mod);
86}
87static int
88devprop_set(tnode_t *tn, di_node_t dn,
89	const char *tpgrp, const char *tpnm, topo_mod_t *mod)
90{
91	char *path;
92	int err, e;
93
94	if ((path = di_devfs_path(dn)) == NULL) {
95		topo_mod_dprintf(mod, "NULL di_devfs_path.\n");
96		return (topo_mod_seterrno(mod, ETOPO_PROP_NOENT));
97	}
98	e = topo_prop_set_string(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE,
99	    path, &err);
100	di_devfs_path_free(path);
101	if (e != 0)
102		return (topo_mod_seterrno(mod, err));
103	return (0);
104}
105/*ARGSUSED*/
106static int
107driverprop_set(tnode_t *tn, di_node_t dn,
108	const char *tpgrp, const char *tpnm, topo_mod_t *mod)
109{
110	char *dnm;
111	int err;
112
113	if ((dnm = di_driver_name(dn)) == NULL)
114		return (0);
115	if (topo_prop_set_string(tn,
116	    tpgrp, tpnm, TOPO_PROP_IMMUTABLE, dnm, &err) < 0)
117		return (topo_mod_seterrno(mod, err));
118	return (0);
119}
120/*ARGSUSED*/
121static int
122moduleprop_set(tnode_t *tn, di_node_t dn,
123	const char *tpgrp, const char *tpnm, topo_mod_t *mod)
124{
125	nvlist_t *module;
126	char *dnm;
127	int err;
128
129	if ((dnm = di_driver_name(dn)) == NULL)
130		return (0);
131
132	if ((module = topo_mod_modfmri(mod, FM_MOD_SCHEME_VERSION, dnm))
133	    == NULL)
134		return (0); /* driver maybe detached, return success */
135
136	if (topo_prop_set_fmri(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE, module,
137	    &err) < 0) {
138		nvlist_free(module);
139		return (topo_mod_seterrno(mod, err));
140	}
141	nvlist_free(module);
142	return (0);
143}
144static tnode_t *
145niu_tnode_create(topo_mod_t *mod, tnode_t *parent,
146    const char *name, topo_instance_t i, void *priv)
147{
148	int err;
149	nvlist_t *fmri;
150	tnode_t *ntn;
151	nvlist_t *auth = topo_mod_auth(mod, parent);
152
153	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i,
154	    NULL, auth, NULL, NULL, NULL);
155	nvlist_free(auth);
156
157	if (fmri == NULL) {
158		topo_mod_dprintf(mod,
159		    "Unable to make nvlist for %s bind: %s.\n",
160		    name, topo_mod_errmsg(mod));
161		return (NULL);
162	}
163
164	ntn = topo_node_bind(mod, parent, name, i, fmri);
165	if (ntn == NULL) {
166		topo_mod_dprintf(mod,
167		    "topo_node_bind (%s%d/%s%d) failed: %s\n",
168		    topo_node_name(parent), topo_node_instance(parent),
169		    name, i,
170		    topo_strerror(topo_mod_errno(mod)));
171		nvlist_free(fmri);
172		return (NULL);
173	}
174	nvlist_free(fmri);
175	topo_node_setspecific(ntn, priv);
176
177	if (topo_pgroup_create(ntn, &io_pgroup, &err) == 0) {
178		(void) devprop_set(ntn, priv, TOPO_PGROUP_IO, TOPO_IO_DEV, mod);
179		(void) driverprop_set(ntn, priv, TOPO_PGROUP_IO, TOPO_IO_DRIVER,
180		    mod);
181		(void) moduleprop_set(ntn, priv, TOPO_PGROUP_IO, TOPO_IO_MODULE,
182		    mod);
183	}
184	return (ntn);
185}
186static int
187niu_asru_set(tnode_t *tn, di_node_t dn, topo_mod_t *mod)
188{
189	char *path;
190	nvlist_t *fmri;
191	int e;
192
193	if ((path = di_devfs_path(dn)) != NULL) {
194		fmri = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, path, NULL);
195		if (fmri == NULL) {
196			topo_mod_dprintf(mod,
197			    "dev:///%s fmri creation failed.\n", path);
198			di_devfs_path_free(path);
199			return (-1);
200		}
201		di_devfs_path_free(path);
202	} else {
203		topo_mod_dprintf(mod, "NULL di_devfs_path.\n");
204		if (topo_prop_get_fmri(tn, TOPO_PGROUP_PROTOCOL,
205		    TOPO_PROP_RESOURCE, &fmri, &e) < 0)
206			return (topo_mod_seterrno(mod, e));
207	}
208	if (topo_node_asru_set(tn, fmri, 0, &e) < 0) {
209		nvlist_free(fmri);
210		return (topo_mod_seterrno(mod, e));
211	}
212	nvlist_free(fmri);
213	return (0);
214}
215
216/*ARGSUSED*/
217static tnode_t *
218niu_declare(tnode_t *parent, const char *name, topo_instance_t i,
219	void *priv, topo_mod_t *mod)
220{
221	tnode_t *ntn;
222	int err;
223
224	if ((ntn = niu_tnode_create(mod, parent, name, 0, priv)) == NULL) {
225		topo_mod_dprintf(mod, "%s ntn = NULL\n", name);
226		return (NULL);
227	}
228
229	/* inherit FRU from parent */
230	(void) topo_node_fru_set(ntn, NULL, 0, &err);
231	/* inherit parent's label */
232	if (topo_node_label_set(ntn, NULL, &err) < 0) {
233		topo_mod_dprintf(mod, "niu label error %d\n", err);
234	}
235	/* set ASRU */
236	(void) niu_asru_set(ntn, priv, mod);
237
238	return (ntn);
239}
240
241
242/*ARGSUSED*/
243static tnode_t *
244niufn_declare(tnode_t *parent, const char *name, topo_instance_t i,
245	void *priv, topo_mod_t *mod)
246{
247	tnode_t *ntn;
248	int err;
249
250	if ((ntn = niu_tnode_create(mod, parent, name, i, priv)) == NULL)
251		return (NULL);
252
253	/* inherit FRU from parent */
254	(void) topo_node_fru_set(ntn, NULL, 0, &err);
255	/* inherit parent's label */
256	(void) topo_node_label_set(ntn, NULL, &err);
257
258	/* set ASRU */
259	(void) niu_asru_set(ntn, priv, mod);
260
261	if (topo_node_range_create(mod, ntn, XAUI, 0, XAUI_MAX) < 0) {
262		topo_node_unbind(ntn);
263		topo_mod_dprintf(mod, "child_range_add of XAUI"
264		    "failed: %s\n",
265		    topo_strerror(topo_mod_errno(mod)));
266		return (NULL); /* mod_errno already set */
267	}
268	return (ntn);
269}
270
271/*
272 * Get the NIU/Neptune ethernet function number from the reg property
273 */
274static int
275niufn_instance_get(topo_mod_t *mod, di_node_t node, topo_instance_t *inst)
276{
277	di_prom_handle_t phan;
278	int rval, *intp;
279
280	*inst = (topo_instance_t)0;
281	rval = -1;
282	if ((phan = topo_mod_prominfo(mod)) != DI_PROM_HANDLE_NIL) {
283		rval = di_prom_prop_lookup_ints(phan, node,
284		    DI_PROP_REG, &intp);
285	}
286	if (rval < 0) {
287		rval = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
288		    DI_PROP_REG, &intp);
289		if (rval < 0)
290			return (-1);
291	}
292	*inst = (topo_instance_t)intp[0];
293
294	return (0);
295}
296
297static int
298niufn_instantiate(tnode_t *parent, const char *name, di_node_t pnode,
299	topo_mod_t *mod)
300{
301	di_node_t sib;
302	tnode_t *ntn;
303	topo_instance_t inst;
304
305	if (strcmp(name, NIUFN) != 0) {
306		topo_mod_dprintf(mod,
307		    "Currently only know how to enumerate %s components.\n",
308		    NIUFN);
309		return (0);
310	}
311
312	sib = di_child_node(pnode);
313	while (sib != DI_NODE_NIL) {
314		if (niufn_instance_get(mod, sib, &inst) != 0) {
315			topo_mod_dprintf(mod, "Enumeration of %s "
316			    "instance failed.\n", NIUFN);
317			sib = di_sibling_node(sib);
318			continue;
319		}
320		if ((ntn = niufn_declare(parent, NIUFN, inst, sib, mod))
321		    == NULL) {
322			topo_mod_dprintf(mod, "Enumeration of %s=%d "
323			    "failed: %s\n", NIUFN, inst,
324			    topo_strerror(topo_mod_errno(mod)));
325			return (-1);
326		}
327		if (topo_mod_enumerate(mod,
328		    ntn, XAUI, XAUI, inst, inst, sib) != 0) {
329			return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
330		}
331		sib = di_sibling_node(sib);
332	}
333	return (0);
334}
335
336static topo_mod_t *
337xaui_enum_load(topo_mod_t *mp)
338{
339	topo_mod_t *rp = NULL;
340
341	if ((rp = topo_mod_load(mp, XAUI, TOPO_VERSION)) == NULL) {
342		topo_mod_dprintf(mp,
343		    "%s enumerator could not load %s enum.\n", NIU, XAUI);
344	}
345	return (rp);
346}
347/*ARGSUSED*/
348static int
349niu_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
350	topo_instance_t min, topo_instance_t max, void *arg, void *notused)
351{
352	tnode_t *niun;
353	di_node_t devtree;
354	di_node_t dnode;
355
356	if (strcmp(name, NIU) != 0) {
357		topo_mod_dprintf(mod,
358		    "Currently only know how to enumerate %s components.\n",
359		    NIU);
360		return (0);
361	}
362
363	if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
364		topo_mod_dprintf(mod, "devinfo init failed.");
365		return (-1);
366	}
367
368	/*
369	 * Load XAUI Enum
370	 */
371	if (xaui_enum_load(mod) == NULL)
372		return (-1);
373
374	dnode = di_drv_first_node("niumx", devtree);
375	if (dnode != DI_NODE_NIL) {
376		niun = niu_declare(rnode, name, 0, dnode, mod);
377		if (niun == NULL) {
378			topo_mod_dprintf(mod, "Enumeration of niu failed: %s\n",
379			    topo_strerror(topo_mod_errno(mod)));
380			return (-1); /* mod_errno already set */
381		}
382		if (topo_node_range_create(mod, niun, NIUFN,
383		    0, NIUFN_MAX) < 0) {
384			topo_node_unbind(niun);
385			topo_mod_dprintf(mod, "child_range_add of NIUFN"
386			    "failed: %s\n",
387			    topo_strerror(topo_mod_errno(mod)));
388			return (-1); /* mod_errno already set */
389		}
390		if (niufn_instantiate(niun, NIUFN, dnode, mod) < 0) {
391			topo_mod_dprintf(mod, "Enumeration of niufn "
392			    "failed %s\n",
393			    topo_strerror(topo_mod_errno(mod)));
394		}
395	}
396	if (di_drv_next_node(dnode) != DI_NODE_NIL)
397		topo_mod_dprintf(mod,
398		    "Currently only know how to enumerate one niu "
399		    "components.\n");
400
401	return (0);
402}
403