ofwbus.c revision 171805
1343181Sdim/*-
2343181Sdim * Copyright 1998 Massachusetts Institute of Technology
3353358Sdim *
4353358Sdim * Permission to use, copy, modify, and distribute this software and
5353358Sdim * its documentation for any purpose and without fee is hereby
6343181Sdim * granted, provided that both the above copyright notice and this
7343181Sdim * permission notice appear in all copies, that both the above
8343181Sdim * copyright notice and this permission notice appear in all
9343181Sdim * supporting documentation, and that the name of M.I.T. not be used
10343181Sdim * in advertising or publicity pertaining to distribution of the
11343181Sdim * software without specific, written prior permission.  M.I.T. makes
12343181Sdim * no representations about the suitability of this software for any
13343181Sdim * purpose.  It is provided "as is" without express or implied
14343181Sdim * warranty.
15343181Sdim *
16343181Sdim * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17343181Sdim * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18343181Sdim * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19343181Sdim * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20343181Sdim * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21343181Sdim * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22343181Sdim * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23343181Sdim * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24343181Sdim * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25343181Sdim * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26343181Sdim * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27343181Sdim * SUCH DAMAGE.
28343181Sdim */
29343181Sdim/*-
30343181Sdim * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.  All rights reserved.
31343181Sdim *
32353358Sdim * Redistribution and use in source and binary forms, with or without
33343181Sdim * modification, are permitted provided that the following conditions
34343181Sdim * are met:
35343181Sdim * 1. Redistributions of source code must retain the above copyright
36353358Sdim *    notice, this list of conditions and the following disclaimer.
37353358Sdim * 2. Redistributions in binary form must reproduce the above copyright
38353358Sdim *    notice, this list of conditions and the following disclaimer in the
39353358Sdim *    documentation and/or other materials provided with the distribution.
40343181Sdim *
41343181Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42353358Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43343181Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44343181Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45343181Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46343181Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47353358Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48343181Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49343181Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50343181Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51353358Sdim * SUCH DAMAGE.
52353358Sdim *
53353358Sdim * 	from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
54353358Sdim *
55343181Sdim * $FreeBSD: head/sys/powerpc/aim/nexus.c 171805 2007-08-11 19:25:32Z marcel $
56343181Sdim */
57343181Sdim#include "opt_psim.h"
58343181Sdim
59343181Sdim#include <sys/param.h>
60353358Sdim#include <sys/systm.h>
61353358Sdim#include <sys/module.h>
62353358Sdim#include <sys/bus.h>
63353358Sdim#include <sys/cons.h>
64343181Sdim#include <sys/kernel.h>
65343181Sdim#include <sys/malloc.h>
66343181Sdim
67343181Sdim#include <dev/ofw/openfirm.h>
68343181Sdim
69343181Sdim#include <machine/bus.h>
70343181Sdim#include <machine/frame.h>
71343181Sdim#include <machine/intr_machdep.h>
72343181Sdim#include <machine/nexusvar.h>
73343181Sdim#include <machine/resource.h>
74343181Sdim
75343181Sdim#include <sys/rman.h>
76343181Sdim
77343181Sdim#include "ofw_bus_if.h"
78343181Sdim#include "pic_if.h"
79343181Sdim
80343181Sdim/*
81343181Sdim * The nexus (which is a pseudo-bus actually) iterates over the nodes that
82343181Sdim * exist in Open Firmware and adds them as devices to this bus so that drivers
83343181Sdim * can be attached to them.
84353358Sdim *
85353358Sdim * Maybe this code should get into dev/ofw to some extent, as some of it should
86353358Sdim * work for all Open Firmware based machines...
87343181Sdim */
88343181Sdim
89343181Sdimstatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
90343181Sdim
91343181Sdimstruct nexus_devinfo {
92353358Sdim	phandle_t	ndi_node;
93343181Sdim	/* Some common properties. */
94343181Sdim	const char     	*ndi_name;
95353358Sdim	const char     	*ndi_device_type;
96343181Sdim	const char     	*ndi_compatible;
97353358Sdim};
98353358Sdim
99343181Sdimstruct nexus_softc {
100343181Sdim	struct rman	sc_rman;
101343181Sdim};
102343181Sdim
103343181Sdim/*
104343181Sdim * Device interface
105343181Sdim */
106343181Sdimstatic int	nexus_probe(device_t);
107343181Sdimstatic void	nexus_probe_nomatch(device_t, device_t);
108343181Sdim
109343181Sdim/*
110343181Sdim * Bus interface
111343181Sdim */
112343181Sdimstatic device_t nexus_add_child(device_t, int, const char *, int);
113343181Sdimstatic int	nexus_read_ivar(device_t, device_t, int, uintptr_t *);
114343181Sdimstatic int	nexus_write_ivar(device_t, device_t, int, uintptr_t);
115343181Sdimstatic int	nexus_setup_intr(device_t, device_t, struct resource *, int,
116343181Sdim		    driver_filter_t *, driver_intr_t *, void *, void **);
117343181Sdimstatic int	nexus_teardown_intr(device_t, device_t, struct resource *,
118343181Sdim		    void *);
119343181Sdimstatic struct	resource *nexus_alloc_resource(device_t, device_t, int, int *,
120343181Sdim		    u_long, u_long, u_long, u_int);
121353358Sdimstatic int	nexus_activate_resource(device_t, device_t, int, int,
122343181Sdim		    struct resource *);
123343181Sdimstatic int	nexus_deactivate_resource(device_t, device_t, int, int,
124343181Sdim		    struct resource *);
125343181Sdimstatic int	nexus_release_resource(device_t, device_t, int, int,
126353358Sdim		    struct resource *);
127343181Sdim
128343181Sdimstatic phandle_t	 nexus_ofw_get_node(device_t, device_t);
129343181Sdimstatic const char	*nexus_ofw_get_name(device_t, device_t);
130343181Sdimstatic const char	*nexus_ofw_get_type(device_t, device_t);
131343181Sdimstatic const char	*nexus_ofw_get_compat(device_t, device_t);
132343181Sdim
133343181Sdim/*
134343181Sdim * Local routines
135343181Sdim */
136353358Sdimstatic device_t	nexus_device_from_node(device_t, phandle_t);
137343181Sdim
138343181Sdimstatic device_method_t nexus_methods[] = {
139343181Sdim	/* Device interface */
140343181Sdim	DEVMETHOD(device_probe,		nexus_probe),
141353358Sdim	DEVMETHOD(device_attach,	bus_generic_attach),
142343181Sdim	DEVMETHOD(device_detach,	bus_generic_detach),
143343181Sdim	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
144343181Sdim	DEVMETHOD(device_suspend,	bus_generic_suspend),
145343181Sdim	DEVMETHOD(device_resume,	bus_generic_resume),
146343181Sdim
147343181Sdim	/* Bus interface. Resource management is business of the children... */
148343181Sdim	DEVMETHOD(bus_add_child,	nexus_add_child),
149343181Sdim	DEVMETHOD(bus_print_child,	bus_generic_print_child),
150343181Sdim	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
151353358Sdim	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
152353358Sdim	DEVMETHOD(bus_write_ivar,	nexus_write_ivar),
153353358Sdim	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
154343181Sdim	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
155343181Sdim	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
156343181Sdim	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
157343181Sdim	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
158353358Sdim	DEVMETHOD(bus_release_resource,	nexus_release_resource),
159343181Sdim
160343181Sdim	/* OFW bus interface */
161343181Sdim	DEVMETHOD(ofw_bus_get_node, nexus_ofw_get_node),
162343181Sdim	DEVMETHOD(ofw_bus_get_name, nexus_ofw_get_name),
163343181Sdim	DEVMETHOD(ofw_bus_get_type, nexus_ofw_get_type),
164343181Sdim	DEVMETHOD(ofw_bus_get_compat, nexus_ofw_get_compat),
165343181Sdim
166343181Sdim	{ 0, 0 }
167343181Sdim};
168343181Sdim
169343181Sdimstatic driver_t nexus_driver = {
170343181Sdim	"nexus",
171343181Sdim	nexus_methods,
172343181Sdim	sizeof(struct nexus_softc),
173353358Sdim};
174343181Sdim
175343181Sdimstatic devclass_t nexus_devclass;
176343181Sdim
177343181SdimDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
178353358Sdim
179343181Sdimstatic int
180343181Sdimnexus_probe(device_t dev)
181353358Sdim{
182353358Sdim	phandle_t	root;
183343181Sdim	phandle_t	child;
184343181Sdim	struct		nexus_softc *sc;
185343181Sdim	u_long		start, end;
186343181Sdim
187343181Sdim	if ((root = OF_peer(0)) == -1)
188343181Sdim		panic("nexus_probe: OF_peer failed.");
189343181Sdim
190343181Sdim	sc = device_get_softc(dev);
191343181Sdim
192343181Sdim	start = 0;
193343181Sdim	end = INTR_VECTORS - 1;
194343181Sdim
195343181Sdim	sc->sc_rman.rm_start = start;
196353358Sdim	sc->sc_rman.rm_end = end;
197343181Sdim	sc->sc_rman.rm_type = RMAN_ARRAY;
198343181Sdim	sc->sc_rman.rm_descr = "Interrupt request lines";
199353358Sdim	if (rman_init(&sc->sc_rman) ||
200353358Sdim	    rman_manage_region(&sc->sc_rman, start, end))
201343181Sdim		panic("nexus_probe IRQ rman");
202343181Sdim
203353358Sdim	/*
204343181Sdim	 * Allow devices to identify
205343181Sdim	 */
206353358Sdim	bus_generic_probe(dev);
207353358Sdim
208343181Sdim	/*
209343181Sdim	 * Now walk the OFW tree to locate top-level devices
210343181Sdim	 */
211343181Sdim	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
212343181Sdim		if (child == -1)
213343181Sdim			panic("nexus_probe(): OF_child failed.");
214343181Sdim		(void)nexus_device_from_node(dev, child);
215353358Sdim
216343181Sdim	}
217343181Sdim	device_set_desc(dev, "Open Firmware Nexus device");
218343181Sdim	return (0);
219343181Sdim}
220343181Sdim
221343181Sdimstatic void
222353358Sdimnexus_probe_nomatch(device_t dev, device_t child)
223343181Sdim{
224353358Sdim	char	*name, *type;
225343181Sdim
226343181Sdim	if (BUS_READ_IVAR(dev, child, NEXUS_IVAR_NAME,
227343181Sdim	    (uintptr_t *)&name) != 0 ||
228343181Sdim	    BUS_READ_IVAR(dev, child, NEXUS_IVAR_DEVICE_TYPE,
229343181Sdim	    (uintptr_t *)&type) != 0)
230343181Sdim		return;
231343181Sdim
232343181Sdim	if (type == NULL)
233343181Sdim		type = "(unknown)";
234343181Sdim
235343181Sdim	if (bootverbose)
236343181Sdim		device_printf(dev, "<%s>, type %s (no driver attached)\n",
237343181Sdim		    name, type);
238343181Sdim}
239343181Sdim
240343181Sdimstatic device_t
241343181Sdimnexus_add_child(device_t dev, int order, const char *name, int unit)
242343181Sdim{
243343181Sdim	device_t child;
244343181Sdim	struct nexus_devinfo *dinfo;
245343181Sdim
246343181Sdim	child = device_add_child_ordered(dev, order, name, unit);
247343181Sdim	if (child == NULL)
248343181Sdim		return (NULL);
249343181Sdim
250353358Sdim	dinfo = malloc(sizeof(struct nexus_devinfo), M_NEXUS, M_NOWAIT|M_ZERO);
251343181Sdim	if (dinfo == NULL)
252343181Sdim		return (NULL);
253343181Sdim
254343181Sdim	dinfo->ndi_node = -1;
255343181Sdim	dinfo->ndi_name = name;
256343181Sdim	device_set_ivars(child, dinfo);
257343181Sdim
258343181Sdim        return (child);
259343181Sdim}
260343181Sdim
261343181Sdim
262343181Sdimstatic int
263343181Sdimnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
264353358Sdim{
265343181Sdim	struct nexus_devinfo *dinfo;
266343181Sdim
267343181Sdim	if ((dinfo = device_get_ivars(child)) == 0)
268353358Sdim		return (ENOENT);
269343181Sdim	switch (which) {
270343181Sdim	case NEXUS_IVAR_NODE:
271343181Sdim		*result = dinfo->ndi_node;
272353358Sdim		break;
273343181Sdim	case NEXUS_IVAR_NAME:
274343181Sdim		*result = (uintptr_t)dinfo->ndi_name;
275343181Sdim		break;
276343181Sdim	case NEXUS_IVAR_DEVICE_TYPE:
277343181Sdim		*result = (uintptr_t)dinfo->ndi_device_type;
278343181Sdim		break;
279343181Sdim	case NEXUS_IVAR_COMPATIBLE:
280343181Sdim		*result = (uintptr_t)dinfo->ndi_compatible;
281343181Sdim		break;
282343181Sdim	default:
283343181Sdim		return (ENOENT);
284343181Sdim	}
285343181Sdim	return 0;
286343181Sdim}
287343181Sdim
288343181Sdimstatic int
289343181Sdimnexus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
290343181Sdim{
291343181Sdim	struct nexus_devinfo *dinfo;
292343181Sdim
293343181Sdim	if ((dinfo = device_get_ivars(child)) == 0)
294343181Sdim		return (ENOENT);
295343181Sdim
296343181Sdim	switch (which) {
297343181Sdim	case NEXUS_IVAR_NAME:
298343181Sdim		return (EINVAL);
299343181Sdim
300343181Sdim	/* Identified devices may want to set these */
301343181Sdim	case NEXUS_IVAR_NODE:
302343181Sdim		dinfo->ndi_node = (phandle_t)value;
303343181Sdim		break;
304343181Sdim
305343181Sdim	case NEXUS_IVAR_DEVICE_TYPE:
306343181Sdim		dinfo->ndi_device_type = (char *)value;
307343181Sdim		break;
308343181Sdim
309343181Sdim	default:
310343181Sdim		return (ENOENT);
311343181Sdim	}
312353358Sdim	return 0;
313343181Sdim}
314343181Sdim
315343181Sdimstatic int
316353358Sdimnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
317343181Sdim    driver_filter_t *filter, driver_intr_t *ihand, void *arg, void **cookiep)
318343181Sdim{
319343181Sdim	driver_t	*driver;
320353358Sdim	int		error;
321343181Sdim
322343181Sdim	/* somebody tried to setup an irq that failed to allocate! */
323343181Sdim	if (res == NULL)
324343181Sdim		panic("nexus_setup_intr: NULL irq resource!");
325343181Sdim
326343181Sdim	*cookiep = 0;
327343181Sdim	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
328343181Sdim		flags |= INTR_EXCL;
329353358Sdim
330343181Sdim	driver = device_get_driver(child);
331353358Sdim
332343181Sdim	/*
333343181Sdim	 * We depend here on rman_activate_resource() being idempotent.
334343181Sdim	 */
335353358Sdim	error = rman_activate_resource(res);
336343181Sdim	if (error)
337343181Sdim		return (error);
338353358Sdim
339343181Sdim	error = powerpc_setup_intr(device_get_nameunit(child),
340343181Sdim	    rman_get_start(res), filter, ihand, arg, flags, cookiep);
341343181Sdim
342343181Sdim	return (error);
343343181Sdim}
344343181Sdim
345343181Sdimstatic int
346343181Sdimnexus_teardown_intr(device_t dev, device_t child, struct resource *res,
347343181Sdim    void *cookie)
348353358Sdim{
349343181Sdim
350343181Sdim	return (powerpc_teardown_intr(cookie));
351343181Sdim}
352353358Sdim
353343181Sdim/*
354343181Sdim * Allocate resources at the behest of a child.  This only handles interrupts,
355343181Sdim * since I/O resources are handled by child busses.
356343181Sdim */
357343181Sdimstatic struct resource *
358343181Sdimnexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
359343181Sdim    u_long start, u_long end, u_long count, u_int flags)
360343181Sdim{
361343181Sdim	struct nexus_softc *sc;
362343181Sdim	struct resource *rv;
363343181Sdim
364343181Sdim	if (type != SYS_RES_IRQ) {
365343181Sdim		device_printf(bus, "unknown resource request from %s\n",
366343181Sdim		    device_get_nameunit(child));
367343181Sdim		return (NULL);
368343181Sdim	}
369343181Sdim
370343181Sdim	if (count == 0 || start + count - 1 != end) {
371353358Sdim		device_printf(bus, "invalid IRQ allocation from %s\n",
372343181Sdim		    device_get_nameunit(child));
373343181Sdim		return (NULL);
374353358Sdim	}
375343181Sdim
376343181Sdim	sc = device_get_softc(bus);
377353358Sdim
378343181Sdim	rv = rman_reserve_resource(&sc->sc_rman, start, end, count,
379343181Sdim	    flags, child);
380343181Sdim	if (rv == NULL) {
381353358Sdim		device_printf(bus, "IRQ allocation failed for %s\n",
382343181Sdim		    device_get_nameunit(child));
383343181Sdim	} else
384343181Sdim		rman_set_rid(rv, *rid);
385343181Sdim
386343181Sdim	return (rv);
387343181Sdim}
388343181Sdim
389343181Sdimstatic int
390343181Sdimnexus_activate_resource(device_t bus, device_t child, int type, int rid,
391343181Sdim    struct resource *res)
392343181Sdim{
393353358Sdim
394343181Sdim	/* Not much to be done yet... */
395343181Sdim	return (rman_activate_resource(res));
396343181Sdim}
397343181Sdim
398353358Sdimstatic int
399343181Sdimnexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
400343181Sdim    struct resource *res)
401353358Sdim{
402343181Sdim
403343181Sdim	/* Not much to be done yet... */
404353358Sdim	return (rman_deactivate_resource(res));
405343181Sdim}
406343181Sdim
407343181Sdimstatic int
408343181Sdimnexus_release_resource(device_t bus, device_t child, int type, int rid,
409343181Sdim    struct resource *res)
410343181Sdim{
411343181Sdim
412343181Sdim	if (type != SYS_RES_IRQ) {
413343181Sdim		device_printf(bus, "unknown resource request from %s\n",
414343181Sdim		    device_get_nameunit(child));
415343181Sdim		return (EINVAL);
416343181Sdim	}
417353358Sdim
418353358Sdim	return (rman_release_resource(res));
419353358Sdim}
420353358Sdim
421353358Sdimstatic device_t
422343181Sdimnexus_device_from_node(device_t parent, phandle_t node)
423343181Sdim{
424343181Sdim	device_t	cdev;
425343181Sdim	struct		nexus_devinfo *dinfo;
426343181Sdim	char		*name, *type, *compatible;
427353358Sdim
428353358Sdim	OF_getprop_alloc(node, "name", 1, (void **)&name);
429353358Sdim	OF_getprop_alloc(node, "device_type", 1, (void **)&type);
430353358Sdim	OF_getprop_alloc(node, "compatible", 1, (void **)&compatible);
431353358Sdim	cdev = device_add_child(parent, NULL, -1);
432353358Sdim	if (cdev != NULL) {
433353358Sdim		dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
434353358Sdim		dinfo->ndi_node = node;
435353358Sdim		dinfo->ndi_name = name;
436353358Sdim		dinfo->ndi_device_type = type;
437353358Sdim		dinfo->ndi_compatible = compatible;
438353358Sdim		device_set_ivars(cdev, dinfo);
439343181Sdim	} else
440343181Sdim		free(name, M_OFWPROP);
441343181Sdim
442343181Sdim	return (cdev);
443343181Sdim}
444343181Sdim
445343181Sdimstatic const char *
446343181Sdimnexus_ofw_get_name(device_t bus, device_t dev)
447343181Sdim{
448343181Sdim	struct nexus_devinfo *dinfo;
449343181Sdim
450343181Sdim	if ((dinfo = device_get_ivars(dev)) == NULL)
451343181Sdim		return (NULL);
452343181Sdim
453343181Sdim	return (dinfo->ndi_name);
454343181Sdim}
455343181Sdim
456343181Sdimstatic phandle_t
457343181Sdimnexus_ofw_get_node(device_t bus, device_t dev)
458343181Sdim{
459343181Sdim	struct nexus_devinfo *dinfo;
460343181Sdim
461343181Sdim	if ((dinfo = device_get_ivars(dev)) == NULL)
462343181Sdim		return (0);
463343181Sdim
464343181Sdim	return (dinfo->ndi_node);
465343181Sdim}
466343181Sdim
467343181Sdimstatic const char *
468343181Sdimnexus_ofw_get_type(device_t bus, device_t dev)
469343181Sdim{
470343181Sdim	struct nexus_devinfo *dinfo;
471343181Sdim
472343181Sdim	if ((dinfo = device_get_ivars(dev)) == NULL)
473343181Sdim		return (NULL);
474343181Sdim
475343181Sdim	return (dinfo->ndi_device_type);
476343181Sdim}
477343181Sdim
478343181Sdimstatic const char *
479343181Sdimnexus_ofw_get_compat(device_t bus, device_t dev)
480343181Sdim{
481343181Sdim	struct nexus_devinfo *dinfo;
482343181Sdim
483343181Sdim	if ((dinfo = device_get_ivars(dev)) == NULL)
484343181Sdim		return (NULL);
485343181Sdim
486343181Sdim	return (dinfo->ndi_compatible);
487343181Sdim}
488343181Sdim