nexus.c revision 204152
1/*-
2 * Copyright 1998 Massachusetts Institute of Technology
3 * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.
4 * Copyright 2006 by Marius Strobl <marius@FreeBSD.org>.
5 * All rights reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software and
8 * its documentation for any purpose and without fee is hereby
9 * granted, provided that both the above copyright notice and this
10 * permission notice appear in all copies, that both the above
11 * copyright notice and this permission notice appear in all
12 * supporting documentation, and that the name of M.I.T. not be used
13 * in advertising or publicity pertaining to distribution of the
14 * software without specific, written prior permission.  M.I.T. makes
15 * no representations about the suitability of this software for any
16 * purpose.  It is provided "as is" without express or implied
17 * warranty.
18 *
19 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
20 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
21 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
23 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * 	from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/nexus.c 204152 2010-02-20 23:24:19Z marius $");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/bus.h>
41#include <sys/kernel.h>
42#include <sys/malloc.h>
43#include <sys/module.h>
44#include <sys/pcpu.h>
45
46#include <dev/ofw/ofw_bus.h>
47#include <dev/ofw/ofw_bus_subr.h>
48#include <dev/ofw/openfirm.h>
49
50#include <machine/bus.h>
51#include <machine/bus_common.h>
52#include <machine/intr_machdep.h>
53#include <machine/nexusvar.h>
54#include <machine/ofw_nexus.h>
55#include <machine/resource.h>
56#include <machine/ver.h>
57
58#include <sys/rman.h>
59
60/*
61 * The nexus (which is a pseudo-bus actually) iterates over the nodes that
62 * hang from the Open Firmware root node and adds them as devices to this bus
63 * (except some special nodes which are excluded) so that drivers can be
64 * attached to them.
65 *
66 * Additionally, interrupt setup/teardown and some resource management are
67 * done at this level.
68 *
69 * Maybe this code should get into dev/ofw to some extent, as some of it should
70 * work for all Open Firmware based machines...
71 */
72
73struct nexus_devinfo {
74	struct ofw_bus_devinfo	ndi_obdinfo;
75	struct resource_list	ndi_rl;
76};
77
78struct nexus_softc {
79	struct rman	sc_intr_rman;
80	struct rman	sc_mem_rman;
81};
82
83static device_probe_t nexus_probe;
84static device_attach_t nexus_attach;
85static bus_print_child_t nexus_print_child;
86static bus_add_child_t nexus_add_child;
87static bus_probe_nomatch_t nexus_probe_nomatch;
88static bus_setup_intr_t nexus_setup_intr;
89static bus_teardown_intr_t nexus_teardown_intr;
90static bus_alloc_resource_t nexus_alloc_resource;
91static bus_activate_resource_t nexus_activate_resource;
92static bus_deactivate_resource_t nexus_deactivate_resource;
93static bus_release_resource_t nexus_release_resource;
94static bus_get_resource_list_t nexus_get_resource_list;
95#ifdef SMP
96static bus_bind_intr_t nexus_bind_intr;
97#endif
98static bus_describe_intr_t nexus_describe_intr;
99static bus_get_dma_tag_t nexus_get_dma_tag;
100static ofw_bus_get_devinfo_t nexus_get_devinfo;
101
102static int nexus_inlist(const char *, const char *const *);
103static struct nexus_devinfo * nexus_setup_dinfo(device_t, phandle_t);
104static void nexus_destroy_dinfo(struct nexus_devinfo *);
105static int nexus_print_res(struct nexus_devinfo *);
106
107static device_method_t nexus_methods[] = {
108	/* Device interface */
109	DEVMETHOD(device_probe,		nexus_probe),
110	DEVMETHOD(device_attach,	nexus_attach),
111	DEVMETHOD(device_detach,	bus_generic_detach),
112	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
113	DEVMETHOD(device_suspend,	bus_generic_suspend),
114	DEVMETHOD(device_resume,	bus_generic_resume),
115
116	/* Bus interface */
117	DEVMETHOD(bus_print_child,	nexus_print_child),
118	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
119	DEVMETHOD(bus_read_ivar,	bus_generic_read_ivar),
120	DEVMETHOD(bus_write_ivar,	bus_generic_write_ivar),
121	DEVMETHOD(bus_add_child,	nexus_add_child),
122	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
123	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
124	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
125	DEVMETHOD(bus_release_resource,	nexus_release_resource),
126	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
127	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
128	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
129	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
130	DEVMETHOD(bus_get_resource_list, nexus_get_resource_list),
131#ifdef SMP
132	DEVMETHOD(bus_bind_intr,	nexus_bind_intr),
133#endif
134	DEVMETHOD(bus_describe_intr,	nexus_describe_intr),
135	DEVMETHOD(bus_get_dma_tag,	nexus_get_dma_tag),
136
137	/* ofw_bus interface */
138	DEVMETHOD(ofw_bus_get_devinfo,	nexus_get_devinfo),
139	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
140	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
141	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
142	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
143	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
144
145	KOBJMETHOD_END
146};
147
148static devclass_t nexus_devclass;
149
150DEFINE_CLASS_0(nexus, nexus_driver, nexus_methods, sizeof(struct nexus_softc));
151EARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0,
152    BUS_PASS_BUS);
153MODULE_VERSION(nexus, 1);
154
155static const char *const nexus_excl_name[] = {
156	"aliases",
157	"associations",
158	"chosen",
159	"cmp",
160	"counter-timer",	/* No separate device; handled by psycho/sbus */
161	"failsafe",
162	"memory",
163	"openprom",
164	"options",
165	"packages",
166	"rsc",
167	"sgcn",
168	"todsg",
169	"virtual-memory",
170	NULL
171};
172
173static const char *const nexus_excl_type[] = {
174	"core",
175	"cpu",
176	NULL
177};
178
179extern struct bus_space_tag nexus_bustag;
180extern struct bus_dma_tag nexus_dmatag;
181
182static int
183nexus_inlist(const char *name, const char *const *list)
184{
185	int i;
186
187	if (name == NULL)
188		return (0);
189	for (i = 0; list[i] != NULL; i++)
190		if (strcmp(name, list[i]) == 0)
191			return (1);
192	return (0);
193}
194
195#define	NEXUS_EXCLUDED(name, type)					\
196	(nexus_inlist((name), nexus_excl_name) ||			\
197	((type) != NULL && nexus_inlist((type), nexus_excl_type)))
198
199static int
200nexus_probe(device_t dev)
201{
202
203	/* Nexus does always match. */
204	device_set_desc(dev, "Open Firmware Nexus device");
205	return (0);
206}
207
208static int
209nexus_attach(device_t dev)
210{
211	struct nexus_devinfo *ndi;
212	struct nexus_softc *sc;
213	device_t cdev;
214	phandle_t node;
215
216	if (strcmp(device_get_name(device_get_parent(dev)), "root") == 0) {
217		node = OF_peer(0);
218		if (node == -1)
219			panic("%s: OF_peer failed.", __func__);
220
221		sc = device_get_softc(dev);
222		sc->sc_intr_rman.rm_type = RMAN_ARRAY;
223		sc->sc_intr_rman.rm_descr = "Interrupts";
224		sc->sc_mem_rman.rm_type = RMAN_ARRAY;
225		sc->sc_mem_rman.rm_descr = "Device Memory";
226		if (rman_init(&sc->sc_intr_rman) != 0 ||
227		    rman_init(&sc->sc_mem_rman) != 0 ||
228		    rman_manage_region(&sc->sc_intr_rman, 0,
229		    IV_MAX - 1) != 0 ||
230		    rman_manage_region(&sc->sc_mem_rman, 0ULL, ~0ULL) != 0)
231			panic("%s: failed to set up rmans.", __func__);
232	} else
233		node = ofw_bus_get_node(dev);
234
235	/*
236	 * Allow devices to identify.
237	 */
238	bus_generic_probe(dev);
239
240	/*
241	 * Now walk the OFW tree and attach top-level devices.
242	 */
243	for (node = OF_child(node); node > 0; node = OF_peer(node)) {
244		if ((ndi = nexus_setup_dinfo(dev, node)) == NULL)
245			continue;
246		cdev = device_add_child(dev, NULL, -1);
247		if (cdev == NULL) {
248			device_printf(dev, "<%s>: device_add_child failed\n",
249			    ndi->ndi_obdinfo.obd_name);
250			nexus_destroy_dinfo(ndi);
251			continue;
252		}
253		device_set_ivars(cdev, ndi);
254	}
255	return (bus_generic_attach(dev));
256}
257
258static device_t
259nexus_add_child(device_t dev, int order, const char *name, int unit)
260{
261	device_t cdev;
262	struct nexus_devinfo *ndi;
263
264	cdev = device_add_child_ordered(dev, order, name, unit);
265	if (cdev == NULL)
266		return (NULL);
267
268	ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
269	ndi->ndi_obdinfo.obd_node = -1;
270	ndi->ndi_obdinfo.obd_name = strdup(name, M_OFWPROP);
271	resource_list_init(&ndi->ndi_rl);
272	device_set_ivars(cdev, ndi);
273
274	return (cdev);
275}
276
277static int
278nexus_print_child(device_t dev, device_t child)
279{
280	int rv;
281
282	rv = bus_print_child_header(dev, child);
283	rv += nexus_print_res(device_get_ivars(child));
284	rv += bus_print_child_footer(dev, child);
285	return (rv);
286}
287
288static void
289nexus_probe_nomatch(device_t dev, device_t child)
290{
291	const char *type;
292
293	device_printf(dev, "<%s>", ofw_bus_get_name(child));
294	nexus_print_res(device_get_ivars(child));
295	type = ofw_bus_get_type(child);
296	printf(" type %s (no driver attached)\n",
297	    type != NULL ? type : "unknown");
298}
299
300static int
301nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
302    driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
303{
304	int error;
305
306	if (res == NULL)
307		panic("%s: NULL interrupt resource!", __func__);
308
309	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
310		flags |= INTR_EXCL;
311
312	/* We depend here on rman_activate_resource() being idempotent. */
313	error = rman_activate_resource(res);
314	if (error)
315		return (error);
316
317	error = inthand_add(device_get_nameunit(child), rman_get_start(res),
318	    filt, intr, arg, flags, cookiep);
319
320	/*
321	 * XXX in case of the AFB/FFB interrupt and a Psycho, Sabre or U2S
322	 * bridge enable the interrupt in the respective bridge.
323	 */
324
325	return (error);
326}
327
328static int
329nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
330{
331
332	inthand_remove(rman_get_start(r), ih);
333	return (0);
334}
335
336#ifdef SMP
337static int
338nexus_bind_intr(device_t dev, device_t child, struct resource *r, int cpu)
339{
340
341	return (intr_bind(rman_get_start(r), cpu));
342}
343#endif
344
345static int
346nexus_describe_intr(device_t dev, device_t child, struct resource *r,
347    void *cookie, const char *descr)
348{
349
350	return (intr_describe(rman_get_start(r), cookie, descr));
351}
352
353static struct resource *
354nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
355    u_long start, u_long end, u_long count, u_int flags)
356{
357	struct nexus_softc *sc;
358	struct rman *rm;
359	struct resource *rv;
360	struct resource_list_entry *rle;
361	device_t nexus;
362	int isdefault, needactivate, passthrough;
363
364	isdefault = (start == 0UL && end == ~0UL);
365	needactivate = flags & RF_ACTIVE;
366	passthrough = (device_get_parent(child) != bus);
367	nexus = bus;
368	while (strcmp(device_get_name(device_get_parent(nexus)), "root") != 0)
369		nexus = device_get_parent(nexus);
370	sc = device_get_softc(nexus);
371	rle = NULL;
372
373	if (!passthrough) {
374		rle = resource_list_find(BUS_GET_RESOURCE_LIST(bus, child),
375		    type, *rid);
376		if (rle == NULL)
377			return (NULL);
378		if (rle->res != NULL)
379			panic("%s: resource entry is busy", __func__);
380		if (isdefault) {
381			start = rle->start;
382			count = ulmax(count, rle->count);
383			end = ulmax(rle->end, start + count - 1);
384		}
385	}
386
387	switch (type) {
388	case SYS_RES_IRQ:
389		rm = &sc->sc_intr_rman;
390		break;
391	case SYS_RES_MEMORY:
392		rm = &sc->sc_mem_rman;
393		break;
394	default:
395		return (NULL);
396	}
397
398	flags &= ~RF_ACTIVE;
399	rv = rman_reserve_resource(rm, start, end, count, flags, child);
400	if (rv == NULL)
401		return (NULL);
402	rman_set_rid(rv, *rid);
403	if (type == SYS_RES_MEMORY) {
404		rman_set_bustag(rv, &nexus_bustag);
405		rman_set_bushandle(rv, rman_get_start(rv));
406	}
407
408	if (needactivate) {
409		if (bus_activate_resource(child, type, *rid, rv) != 0) {
410			rman_release_resource(rv);
411			return (NULL);
412		}
413	}
414
415	if (!passthrough) {
416		rle->res = rv;
417		rle->start = rman_get_start(rv);
418		rle->end = rman_get_end(rv);
419		rle->count = rle->end - rle->start + 1;
420	}
421
422	return (rv);
423}
424
425static int
426nexus_activate_resource(device_t bus, device_t child, int type, int rid,
427    struct resource *r)
428{
429
430	/* Not much to be done yet... */
431	return (rman_activate_resource(r));
432}
433
434static int
435nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
436    struct resource *r)
437{
438
439	/* Not much to be done yet... */
440	return (rman_deactivate_resource(r));
441}
442
443static int
444nexus_release_resource(device_t bus, device_t child, int type, int rid,
445    struct resource *r)
446{
447	int error;
448
449	if (rman_get_flags(r) & RF_ACTIVE) {
450		error = bus_deactivate_resource(child, type, rid, r);
451		if (error)
452			return (error);
453	}
454	return (rman_release_resource(r));
455}
456
457static struct resource_list *
458nexus_get_resource_list(device_t dev, device_t child)
459{
460	struct nexus_devinfo *ndi;
461
462	ndi = device_get_ivars(child);
463	return (&ndi->ndi_rl);
464}
465
466static bus_dma_tag_t
467nexus_get_dma_tag(device_t bus, device_t child)
468{
469
470	return (&nexus_dmatag);
471}
472
473static const struct ofw_bus_devinfo *
474nexus_get_devinfo(device_t dev, device_t child)
475{
476	struct nexus_devinfo *ndi;
477
478	ndi = device_get_ivars(child);
479	return (&ndi->ndi_obdinfo);
480}
481
482static struct nexus_devinfo *
483nexus_setup_dinfo(device_t dev, phandle_t node)
484{
485	struct nexus_devinfo *ndi;
486	struct nexus_regs *reg;
487	bus_addr_t phys;
488	bus_size_t size;
489	uint32_t ign;
490	uint32_t *intr;
491	int i;
492	int nintr;
493	int nreg;
494
495	ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
496	if (ofw_bus_gen_setup_devinfo(&ndi->ndi_obdinfo, node) != 0) {
497		free(ndi, M_DEVBUF);
498		return (NULL);
499	}
500	if (NEXUS_EXCLUDED(ndi->ndi_obdinfo.obd_name,
501	    ndi->ndi_obdinfo.obd_type)) {
502		ofw_bus_gen_destroy_devinfo(&ndi->ndi_obdinfo);
503		free(ndi, M_DEVBUF);
504		return (NULL);
505	}
506	resource_list_init(&ndi->ndi_rl);
507	nreg = OF_getprop_alloc(node, "reg", sizeof(*reg), (void **)&reg);
508	if (nreg == -1) {
509		device_printf(dev, "<%s>: incomplete\n",
510		    ndi->ndi_obdinfo.obd_name);
511		goto fail;
512	}
513	for (i = 0; i < nreg; i++) {
514		phys = NEXUS_REG_PHYS(&reg[i]);
515		size = NEXUS_REG_SIZE(&reg[i]);
516		/* Skip the dummy reg property of glue devices like ssm(4). */
517		if (size != 0)
518			resource_list_add(&ndi->ndi_rl, SYS_RES_MEMORY, i,
519			    phys, phys + size - 1, size);
520	}
521	free(reg, M_OFWPROP);
522
523	nintr = OF_getprop_alloc(node, "interrupts",  sizeof(*intr),
524	    (void **)&intr);
525	if (nintr > 0) {
526		if (OF_getprop(node, PCPU_GET(impl) < CPU_IMPL_ULTRASPARCIII ?
527		    "upa-portid" : "portid", &ign, sizeof(ign)) <= 0) {
528			device_printf(dev, "<%s>: could not determine portid\n",
529			    ndi->ndi_obdinfo.obd_name);
530			free(intr, M_OFWPROP);
531			goto fail;
532		}
533
534		/* XXX 7-bit MID on Starfire */
535		ign = (ign << INTMAP_IGN_SHIFT) & INTMAP_IGN_MASK;
536		for (i = 0; i < nintr; i++) {
537			intr[i] |= ign;
538			resource_list_add(&ndi->ndi_rl, SYS_RES_IRQ, i, intr[i],
539			    intr[i], 1);
540		}
541		free(intr, M_OFWPROP);
542	}
543
544	return (ndi);
545
546 fail:
547	nexus_destroy_dinfo(ndi);
548	return (NULL);
549}
550
551static void
552nexus_destroy_dinfo(struct nexus_devinfo *ndi)
553{
554
555	resource_list_free(&ndi->ndi_rl);
556	ofw_bus_gen_destroy_devinfo(&ndi->ndi_obdinfo);
557	free(ndi, M_DEVBUF);
558}
559
560static int
561nexus_print_res(struct nexus_devinfo *ndi)
562{
563	int rv;
564
565	rv = 0;
566	rv += resource_list_print_type(&ndi->ndi_rl, "mem", SYS_RES_MEMORY,
567	    "%#lx");
568	rv += resource_list_print_type(&ndi->ndi_rl, "irq", SYS_RES_IRQ,
569	    "%ld");
570	return (rv);
571}
572