nexus.c revision 266000
1/*-
2 * Copyright 1998 Massachusetts Institute of Technology
3 *
4 * Permission to use, copy, modify, and distribute this software and
5 * its documentation for any purpose and without fee is hereby
6 * granted, provided that both the above copyright notice and this
7 * permission notice appear in all copies, that both the above
8 * copyright notice and this permission notice appear in all
9 * supporting documentation, and that the name of M.I.T. not be used
10 * in advertising or publicity pertaining to distribution of the
11 * software without specific, written prior permission.  M.I.T. makes
12 * no representations about the suitability of this software for any
13 * purpose.  It is provided "as is" without express or implied
14 * warranty.
15 *
16 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30
31/*
32 * This code implements a `root nexus' for MIPS Architecture
33 * machines.  The function of the root nexus is to serve as an
34 * attachment point for both processors and buses, and to manage
35 * resources which are common to all of them.  In particular,
36 * this code implements the core resource managers for interrupt
37 * requests and memory address space.
38 */
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: stable/10/sys/mips/mips/nexus.c 266000 2014-05-14 01:53:20Z ian $");
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/bus.h>
46#include <sys/kernel.h>
47#include <sys/malloc.h>
48#include <sys/module.h>
49#include <sys/rman.h>
50#include <sys/interrupt.h>
51
52#include <vm/vm.h>
53#include <vm/pmap.h>
54
55#include <machine/bus.h>
56#include <machine/intr_machdep.h>
57#include <machine/pmap.h>
58#include <machine/resource.h>
59#include <machine/vmparam.h>
60
61#include "opt_platform.h"
62
63#ifdef FDT
64#include <dev/ofw/ofw_nexus.h>
65#endif
66
67#undef NEXUS_DEBUG
68#ifdef NEXUS_DEBUG
69#define dprintf printf
70#else
71#define dprintf(x, arg...)
72#endif  /* NEXUS_DEBUG */
73
74#define NUM_MIPS_IRQS	6
75
76#ifndef FDT
77static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device");
78
79struct nexus_device {
80	struct resource_list	nx_resources;
81};
82
83#define DEVTONX(dev)	((struct nexus_device *)device_get_ivars(dev))
84
85static struct rman irq_rman;
86static struct rman mem_rman;
87
88static struct resource *
89		nexus_alloc_resource(device_t, device_t, int, int *, u_long,
90		    u_long, u_long, u_int);
91static device_t	nexus_add_child(device_t, u_int, const char *, int);
92static int	nexus_attach(device_t);
93static void	nexus_delete_resource(device_t, device_t, int, int);
94static struct resource_list *
95		nexus_get_reslist(device_t, device_t);
96static int	nexus_get_resource(device_t, device_t, int, int, u_long *,
97		    u_long *);
98static int	nexus_print_child(device_t, device_t);
99static int	nexus_print_all_resources(device_t dev);
100static int	nexus_probe(device_t);
101static int	nexus_release_resource(device_t, device_t, int, int,
102		    struct resource *);
103static int	nexus_set_resource(device_t, device_t, int, int, u_long,
104		    u_long);
105#endif
106static int	nexus_activate_resource(device_t, device_t, int, int,
107		    struct resource *);
108static int	nexus_deactivate_resource(device_t, device_t, int, int,
109		    struct resource *);
110static void	nexus_hinted_child(device_t, const char *, int);
111static int	nexus_setup_intr(device_t dev, device_t child,
112		    struct resource *res, int flags, driver_filter_t *filt,
113		    driver_intr_t *intr, void *arg, void **cookiep);
114static int	nexus_teardown_intr(device_t, device_t, struct resource *,
115		    void *);
116
117static device_method_t nexus_methods[] = {
118#ifndef FDT
119	/* Device interface */
120	DEVMETHOD(device_probe,		nexus_probe),
121	DEVMETHOD(device_attach,	nexus_attach),
122
123	/* Bus interface */
124	DEVMETHOD(bus_add_child,	nexus_add_child),
125	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
126	DEVMETHOD(bus_delete_resource,	nexus_delete_resource),
127	DEVMETHOD(bus_get_resource,	nexus_get_resource),
128	DEVMETHOD(bus_get_resource_list,	nexus_get_reslist),
129	DEVMETHOD(bus_print_child,	nexus_print_child),
130	DEVMETHOD(bus_release_resource,	nexus_release_resource),
131	DEVMETHOD(bus_set_resource,	nexus_set_resource),
132#endif
133	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
134	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
135	DEVMETHOD(bus_activate_resource,nexus_activate_resource),
136	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
137	DEVMETHOD(bus_hinted_child,	nexus_hinted_child),
138
139	{ 0, 0 }
140};
141
142#ifndef FDT
143static driver_t nexus_driver = {
144	"nexus",
145	nexus_methods,
146	1			/* no softc */
147};
148#else
149DEFINE_CLASS_1(nexus, nexus_driver, nexus_methods,
150    sizeof(struct ofw_nexus_softc), ofw_nexus_driver);
151#endif
152static devclass_t nexus_devclass;
153
154#ifndef FDT
155static int
156nexus_probe(device_t dev)
157{
158
159	device_set_desc(dev, "MIPS32 root nexus");
160
161	irq_rman.rm_start = 0;
162	irq_rman.rm_end = NUM_MIPS_IRQS - 1;
163	irq_rman.rm_type = RMAN_ARRAY;
164	irq_rman.rm_descr = "Hardware IRQs";
165	if (rman_init(&irq_rman) != 0 ||
166	    rman_manage_region(&irq_rman, 0, NUM_MIPS_IRQS - 1) != 0) {
167		panic("%s: irq_rman", __func__);
168	}
169
170	mem_rman.rm_start = 0;
171	mem_rman.rm_end = ~0ul;
172	mem_rman.rm_type = RMAN_ARRAY;
173	mem_rman.rm_descr = "Memory addresses";
174	if (rman_init(&mem_rman) != 0 ||
175	    rman_manage_region(&mem_rman, 0, ~0) != 0) {
176		panic("%s: mem_rman", __func__);
177	}
178
179	return (0);
180}
181
182static int
183nexus_attach(device_t dev)
184{
185
186	bus_generic_probe(dev);
187	bus_enumerate_hinted_children(dev);
188	bus_generic_attach(dev);
189
190	return (0);
191}
192
193static int
194nexus_print_child(device_t bus, device_t child)
195{
196	int retval = 0;
197
198	retval += bus_print_child_header(bus, child);
199	retval += nexus_print_all_resources(child);
200	if (device_get_flags(child))
201		retval += printf(" flags %#x", device_get_flags(child));
202	retval += printf(" on %s\n", device_get_nameunit(bus));
203
204	return (retval);
205}
206
207static int
208nexus_print_all_resources(device_t dev)
209{
210	struct nexus_device *ndev = DEVTONX(dev);
211	struct resource_list *rl = &ndev->nx_resources;
212	int retval = 0;
213
214	if (STAILQ_FIRST(rl))
215		retval += printf(" at");
216
217	retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
218	retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
219
220	return (retval);
221}
222
223static device_t
224nexus_add_child(device_t bus, u_int order, const char *name, int unit)
225{
226	device_t	child;
227	struct nexus_device *ndev;
228
229	ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO);
230	if (!ndev)
231		return (0);
232	resource_list_init(&ndev->nx_resources);
233
234	child = device_add_child_ordered(bus, order, name, unit);
235	if (child == NULL) {
236		device_printf(bus, "failed to add child: %s%d\n", name, unit);
237		return (0);
238	}
239
240	/* should we free this in nexus_child_detached? */
241	device_set_ivars(child, ndev);
242
243	return (child);
244}
245
246/*
247 * Allocate a resource on behalf of child.  NB: child is usually going to be a
248 * child of one of our descendants, not a direct child of nexus0.
249 * (Exceptions include footbridge.)
250 */
251static struct resource *
252nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
253	u_long start, u_long end, u_long count, u_int flags)
254{
255	struct nexus_device		*ndev = DEVTONX(child);
256	struct resource			*rv;
257	struct resource_list_entry	*rle;
258	struct rman			*rm;
259	int				 isdefault, needactivate, passthrough;
260
261	dprintf("%s: entry (%p, %p, %d, %p, %p, %p, %ld, %d)\n",
262	    __func__, bus, child, type, rid, (void *)(intptr_t)start,
263	    (void *)(intptr_t)end, count, flags);
264	dprintf("%s: requested rid is %d\n", __func__, *rid);
265
266	isdefault = (start == 0UL && end == ~0UL && count == 1);
267	needactivate = flags & RF_ACTIVE;
268	passthrough = (device_get_parent(child) != bus);
269	rle = NULL;
270
271	/*
272	 * If this is an allocation of the "default" range for a given RID,
273	 * and we know what the resources for this device are (ie. they aren't
274	 * maintained by a child bus), then work out the start/end values.
275	 */
276	if (isdefault) {
277		rle = resource_list_find(&ndev->nx_resources, type, *rid);
278		if (rle == NULL)
279			return (NULL);
280		if (rle->res != NULL) {
281			panic("%s: resource entry is busy", __func__);
282		}
283		start = rle->start;
284		end = rle->end;
285		count = rle->count;
286	}
287
288	switch (type) {
289	case SYS_RES_IRQ:
290		rm = &irq_rman;
291		break;
292	case SYS_RES_MEMORY:
293		rm = &mem_rman;
294		break;
295	default:
296		printf("%s: unknown resource type %d\n", __func__, type);
297		return (0);
298	}
299
300	rv = rman_reserve_resource(rm, start, end, count, flags, child);
301	if (rv == 0) {
302		printf("%s: could not reserve resource for %s\n", __func__,
303		    device_get_nameunit(child));
304		return (0);
305	}
306
307	rman_set_rid(rv, *rid);
308
309	if (needactivate) {
310		if (bus_activate_resource(child, type, *rid, rv)) {
311			printf("%s: could not activate resource\n", __func__);
312			rman_release_resource(rv);
313			return (0);
314		}
315	}
316
317	return (rv);
318}
319
320static struct resource_list *
321nexus_get_reslist(device_t dev, device_t child)
322{
323	struct nexus_device *ndev = DEVTONX(child);
324
325	return (&ndev->nx_resources);
326}
327
328static int
329nexus_set_resource(device_t dev, device_t child, int type, int rid,
330    u_long start, u_long count)
331{
332	struct nexus_device		*ndev = DEVTONX(child);
333	struct resource_list		*rl = &ndev->nx_resources;
334	struct resource_list_entry	*rle;
335
336	dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n",
337	    __func__, dev, child, type, rid, (void *)(intptr_t)start, count);
338
339	rle = resource_list_add(rl, type, rid, start, start + count - 1,
340	    count);
341	if (rle == NULL)
342		return (ENXIO);
343
344	return (0);
345}
346
347static int
348nexus_get_resource(device_t dev, device_t child, int type, int rid,
349    u_long *startp, u_long *countp)
350{
351	struct nexus_device		*ndev = DEVTONX(child);
352	struct resource_list		*rl = &ndev->nx_resources;
353	struct resource_list_entry	*rle;
354
355	rle = resource_list_find(rl, type, rid);
356	if (!rle)
357		return(ENOENT);
358	if (startp)
359		*startp = rle->start;
360	if (countp)
361		*countp = rle->count;
362	return (0);
363}
364
365static void
366nexus_delete_resource(device_t dev, device_t child, int type, int rid)
367{
368	struct nexus_device	*ndev = DEVTONX(child);
369	struct resource_list	*rl = &ndev->nx_resources;
370
371	dprintf("%s: entry\n", __func__);
372
373	resource_list_delete(rl, type, rid);
374}
375
376static int
377nexus_release_resource(device_t bus, device_t child, int type, int rid,
378		       struct resource *r)
379{
380	dprintf("%s: entry\n", __func__);
381
382	if (rman_get_flags(r) & RF_ACTIVE) {
383		int error = bus_deactivate_resource(child, type, rid, r);
384		if (error)
385			return error;
386	}
387
388	return (rman_release_resource(r));
389}
390#endif
391
392static int
393nexus_activate_resource(device_t bus, device_t child, int type, int rid,
394    struct resource *r)
395{
396	void *vaddr;
397	vm_paddr_t paddr;
398	vm_size_t psize;
399
400	/*
401	 * If this is a memory resource, use pmap_mapdev to map it.
402	 */
403	if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
404		paddr = rman_get_start(r);
405		psize = rman_get_size(r);
406		vaddr = pmap_mapdev(paddr, psize);
407
408		rman_set_virtual(r, vaddr);
409		rman_set_bustag(r, mips_bus_space_generic);
410		rman_set_bushandle(r, (bus_space_handle_t)(uintptr_t)vaddr);
411	}
412
413	return (rman_activate_resource(r));
414}
415
416static int
417nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
418			  struct resource *r)
419{
420	vm_offset_t va;
421
422	if (type == SYS_RES_MEMORY) {
423		va = (vm_offset_t)rman_get_virtual(r);
424		pmap_unmapdev(va, rman_get_size(r));
425	}
426
427	return (rman_deactivate_resource(r));
428}
429
430static int
431nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
432    driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
433{
434	register_t s;
435	int irq;
436
437	s = intr_disable();
438	irq = rman_get_start(res);
439	if (irq >= NUM_MIPS_IRQS) {
440		intr_restore(s);
441		return (0);
442	}
443
444	cpu_establish_hardintr(device_get_nameunit(child), filt, intr, arg,
445	    irq, flags, cookiep);
446	intr_restore(s);
447	return (0);
448}
449
450static int
451nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
452{
453
454	printf("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__);
455	return (0);
456}
457
458static void
459nexus_hinted_child(device_t bus, const char *dname, int dunit)
460{
461	device_t child;
462	long	maddr;
463	int	msize;
464	int	order;
465	int	result;
466	int	irq;
467	int	mem_hints_count;
468
469	if ((resource_int_value(dname, dunit, "order", &order)) != 0)
470		order = 1000;
471	child = BUS_ADD_CHILD(bus, order, dname, dunit);
472	if (child == NULL)
473		return;
474
475	/*
476	 * Set hard-wired resources for hinted child using
477	 * specific RIDs.
478	 */
479	mem_hints_count = 0;
480	if (resource_long_value(dname, dunit, "maddr", &maddr) == 0)
481		mem_hints_count++;
482	if (resource_int_value(dname, dunit, "msize", &msize) == 0)
483		mem_hints_count++;
484
485	/* check if all info for mem resource has been provided */
486	if ((mem_hints_count > 0) && (mem_hints_count < 2)) {
487		printf("Either maddr or msize hint is missing for %s%d\n",
488		    dname, dunit);
489	}
490	else if (mem_hints_count) {
491		dprintf("%s: discovered hinted child %s at maddr %p(%d)\n",
492		    __func__, device_get_nameunit(child),
493		    (void *)(intptr_t)maddr, msize);
494
495		result = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr,
496		    msize);
497		if (result != 0) {
498			device_printf(bus,
499			    "warning: bus_set_resource() failed\n");
500		}
501	}
502
503	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
504		result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
505		if (result != 0)
506			device_printf(bus,
507			    "warning: bus_set_resource() failed\n");
508	}
509}
510
511DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
512