nexus.c revision 308333
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#include "opt_platform.h"
40
41#include <sys/cdefs.h>
42__FBSDID("$FreeBSD: stable/11/sys/mips/mips/nexus.c 308333 2016-11-05 10:23:02Z mmel $");
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/bus.h>
47#include <sys/kernel.h>
48#include <sys/malloc.h>
49#include <sys/module.h>
50#include <sys/rman.h>
51#include <sys/interrupt.h>
52
53#include <vm/vm.h>
54#include <vm/pmap.h>
55
56#include <machine/bus.h>
57#include <machine/resource.h>
58#include <machine/vmparam.h>
59
60#ifdef INTRNG
61#include <machine/intr.h>
62#else
63#include <machine/intr_machdep.h>
64#endif
65
66#ifdef FDT
67#include <dev/ofw/ofw_bus_subr.h>
68#include <dev/ofw/openfirm.h>
69#include "ofw_bus_if.h"
70#endif
71
72#undef NEXUS_DEBUG
73#ifdef NEXUS_DEBUG
74#define dprintf printf
75#else
76#define dprintf(x, arg...)
77#endif  /* NEXUS_DEBUG */
78
79#define NUM_MIPS_IRQS	6
80
81static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device");
82
83struct nexus_device {
84	struct resource_list	nx_resources;
85};
86
87#define DEVTONX(dev)	((struct nexus_device *)device_get_ivars(dev))
88
89static struct rman irq_rman;
90static struct rman mem_rman;
91
92static struct resource *
93		nexus_alloc_resource(device_t, device_t, int, int *, rman_res_t,
94		    rman_res_t, rman_res_t, u_int);
95static device_t	nexus_add_child(device_t, u_int, const char *, int);
96static int	nexus_attach(device_t);
97static void	nexus_delete_resource(device_t, device_t, int, int);
98static struct resource_list *
99		nexus_get_reslist(device_t, device_t);
100static int	nexus_get_resource(device_t, device_t, int, int, rman_res_t *,
101		    rman_res_t *);
102static int	nexus_print_child(device_t, device_t);
103static int	nexus_print_all_resources(device_t dev);
104static int	nexus_probe(device_t);
105static int	nexus_release_resource(device_t, device_t, int, int,
106		    struct resource *);
107static int	nexus_set_resource(device_t, device_t, int, int, rman_res_t,
108		    rman_res_t);
109static int	nexus_activate_resource(device_t, device_t, int, int,
110		    struct resource *);
111static int	nexus_deactivate_resource(device_t, device_t, int, int,
112		    struct resource *);
113static void	nexus_hinted_child(device_t, const char *, int);
114static int	nexus_setup_intr(device_t dev, device_t child,
115		    struct resource *res, int flags, driver_filter_t *filt,
116		    driver_intr_t *intr, void *arg, void **cookiep);
117static int	nexus_teardown_intr(device_t, device_t, struct resource *,
118		    void *);
119#ifdef INTRNG
120#ifdef SMP
121static int	nexus_bind_intr(device_t, device_t, struct resource *, int);
122#endif
123#ifdef FDT
124static int	nexus_ofw_map_intr(device_t dev, device_t child,
125		    phandle_t iparent, int icells, pcell_t *intr);
126#endif
127static int	nexus_describe_intr(device_t dev, device_t child,
128		    struct resource *irq, void *cookie, const char *descr);
129static int	nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
130		    enum intr_polarity pol);
131#endif
132
133static device_method_t nexus_methods[] = {
134	/* Device interface */
135	DEVMETHOD(device_probe,		nexus_probe),
136	DEVMETHOD(device_attach,	nexus_attach),
137
138	/* Bus interface */
139	DEVMETHOD(bus_add_child,	nexus_add_child),
140	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
141	DEVMETHOD(bus_delete_resource,	nexus_delete_resource),
142	DEVMETHOD(bus_get_resource,	nexus_get_resource),
143	DEVMETHOD(bus_get_resource_list,	nexus_get_reslist),
144	DEVMETHOD(bus_print_child,	nexus_print_child),
145	DEVMETHOD(bus_release_resource,	nexus_release_resource),
146	DEVMETHOD(bus_set_resource,	nexus_set_resource),
147	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
148	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
149	DEVMETHOD(bus_activate_resource,nexus_activate_resource),
150	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
151	DEVMETHOD(bus_hinted_child,	nexus_hinted_child),
152#ifdef INTRNG
153	DEVMETHOD(bus_config_intr,	nexus_config_intr),
154	DEVMETHOD(bus_describe_intr,	nexus_describe_intr),
155#ifdef SMP
156	DEVMETHOD(bus_bind_intr,	nexus_bind_intr),
157#endif
158#ifdef FDT
159	DEVMETHOD(ofw_bus_map_intr,	nexus_ofw_map_intr),
160#endif
161#endif
162
163	{ 0, 0 }
164};
165
166static driver_t nexus_driver = {
167	"nexus",
168	nexus_methods,
169	1			/* no softc */
170};
171static devclass_t nexus_devclass;
172
173static int
174nexus_probe(device_t dev)
175{
176
177	device_set_desc(dev, "MIPS32 root nexus");
178
179	irq_rman.rm_start = 0;
180	irq_rman.rm_end = NUM_MIPS_IRQS - 1;
181	irq_rman.rm_type = RMAN_ARRAY;
182	irq_rman.rm_descr = "Hardware IRQs";
183	if (rman_init(&irq_rman) != 0 ||
184	    rman_manage_region(&irq_rman, 0, NUM_MIPS_IRQS - 1) != 0) {
185		panic("%s: irq_rman", __func__);
186	}
187
188	mem_rman.rm_start = 0;
189	mem_rman.rm_end = BUS_SPACE_MAXADDR;
190	mem_rman.rm_type = RMAN_ARRAY;
191	mem_rman.rm_descr = "Memory addresses";
192	if (rman_init(&mem_rman) != 0 ||
193	    rman_manage_region(&mem_rman, 0, BUS_SPACE_MAXADDR) != 0) {
194		panic("%s: mem_rman", __func__);
195	}
196
197	return (0);
198}
199
200static int
201nexus_attach(device_t dev)
202{
203
204	bus_generic_probe(dev);
205	bus_enumerate_hinted_children(dev);
206	bus_generic_attach(dev);
207
208	return (0);
209}
210
211static int
212nexus_print_child(device_t bus, device_t child)
213{
214	int retval = 0;
215
216	retval += bus_print_child_header(bus, child);
217	retval += nexus_print_all_resources(child);
218	if (device_get_flags(child))
219		retval += printf(" flags %#x", device_get_flags(child));
220	retval += printf(" on %s\n", device_get_nameunit(bus));
221
222	return (retval);
223}
224
225static int
226nexus_print_all_resources(device_t dev)
227{
228	struct nexus_device *ndev = DEVTONX(dev);
229	struct resource_list *rl = &ndev->nx_resources;
230	int retval = 0;
231
232	if (STAILQ_FIRST(rl))
233		retval += printf(" at");
234
235	retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx");
236	retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
237
238	return (retval);
239}
240
241static device_t
242nexus_add_child(device_t bus, u_int order, const char *name, int unit)
243{
244	device_t	child;
245	struct nexus_device *ndev;
246
247	ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO);
248	if (!ndev)
249		return (0);
250	resource_list_init(&ndev->nx_resources);
251
252	child = device_add_child_ordered(bus, order, name, unit);
253	if (child == NULL) {
254		device_printf(bus, "failed to add child: %s%d\n", name, unit);
255		return (0);
256	}
257
258	/* should we free this in nexus_child_detached? */
259	device_set_ivars(child, ndev);
260
261	return (child);
262}
263
264/*
265 * Allocate a resource on behalf of child.  NB: child is usually going to be a
266 * child of one of our descendants, not a direct child of nexus0.
267 * (Exceptions include footbridge.)
268 */
269static struct resource *
270nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
271	rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
272{
273	struct nexus_device		*ndev = DEVTONX(child);
274	struct resource			*rv;
275	struct resource_list_entry	*rle;
276	struct rman			*rm;
277	int				 isdefault, needactivate, passthrough;
278
279	dprintf("%s: entry (%p, %p, %d, %p, %p, %p, %jd, %d)\n",
280	    __func__, bus, child, type, rid, (void *)(intptr_t)start,
281	    (void *)(intptr_t)end, count, flags);
282	dprintf("%s: requested rid is %d\n", __func__, *rid);
283
284	isdefault = (RMAN_IS_DEFAULT_RANGE(start, end) && count == 1);
285	needactivate = flags & RF_ACTIVE;
286	passthrough = (device_get_parent(child) != bus);
287	rle = NULL;
288
289	/*
290	 * If this is an allocation of the "default" range for a given RID,
291	 * and we know what the resources for this device are (ie. they aren't
292	 * maintained by a child bus), then work out the start/end values.
293	 */
294	if (isdefault) {
295		rle = resource_list_find(&ndev->nx_resources, type, *rid);
296		if (rle == NULL)
297			return (NULL);
298		if (rle->res != NULL) {
299			panic("%s: resource entry is busy", __func__);
300		}
301		start = rle->start;
302		end = rle->end;
303		count = rle->count;
304	}
305
306	switch (type) {
307	case SYS_RES_IRQ:
308		rm = &irq_rman;
309		break;
310	case SYS_RES_MEMORY:
311		rm = &mem_rman;
312		break;
313	default:
314		printf("%s: unknown resource type %d\n", __func__, type);
315		return (0);
316	}
317
318	rv = rman_reserve_resource(rm, start, end, count, flags, child);
319	if (rv == NULL) {
320		printf("%s: could not reserve resource for %s\n", __func__,
321		    device_get_nameunit(child));
322		return (0);
323	}
324
325	rman_set_rid(rv, *rid);
326
327	if (needactivate) {
328		if (bus_activate_resource(child, type, *rid, rv)) {
329			printf("%s: could not activate resource\n", __func__);
330			rman_release_resource(rv);
331			return (0);
332		}
333	}
334
335	return (rv);
336}
337
338static struct resource_list *
339nexus_get_reslist(device_t dev, device_t child)
340{
341	struct nexus_device *ndev = DEVTONX(child);
342
343	return (&ndev->nx_resources);
344}
345
346static int
347nexus_set_resource(device_t dev, device_t child, int type, int rid,
348    rman_res_t start, rman_res_t count)
349{
350	struct nexus_device		*ndev = DEVTONX(child);
351	struct resource_list		*rl = &ndev->nx_resources;
352	struct resource_list_entry	*rle;
353
354	dprintf("%s: entry (%p, %p, %d, %d, %p, %jd)\n",
355	    __func__, dev, child, type, rid, (void *)(intptr_t)start, count);
356
357	rle = resource_list_add(rl, type, rid, start, start + count - 1,
358	    count);
359	if (rle == NULL)
360		return (ENXIO);
361
362	return (0);
363}
364
365static int
366nexus_get_resource(device_t dev, device_t child, int type, int rid,
367    rman_res_t *startp, rman_res_t *countp)
368{
369	struct nexus_device		*ndev = DEVTONX(child);
370	struct resource_list		*rl = &ndev->nx_resources;
371	struct resource_list_entry	*rle;
372
373	rle = resource_list_find(rl, type, rid);
374	if (!rle)
375		return(ENOENT);
376	if (startp)
377		*startp = rle->start;
378	if (countp)
379		*countp = rle->count;
380	return (0);
381}
382
383static void
384nexus_delete_resource(device_t dev, device_t child, int type, int rid)
385{
386	struct nexus_device	*ndev = DEVTONX(child);
387	struct resource_list	*rl = &ndev->nx_resources;
388
389	dprintf("%s: entry\n", __func__);
390
391	resource_list_delete(rl, type, rid);
392}
393
394static int
395nexus_release_resource(device_t bus, device_t child, int type, int rid,
396		       struct resource *r)
397{
398	dprintf("%s: entry\n", __func__);
399
400	if (rman_get_flags(r) & RF_ACTIVE) {
401		int error = bus_deactivate_resource(child, type, rid, r);
402		if (error)
403			return error;
404	}
405
406	return (rman_release_resource(r));
407}
408
409static int
410nexus_activate_resource(device_t bus, device_t child, int type, int rid,
411    struct resource *r)
412{
413	void *vaddr;
414	vm_paddr_t paddr;
415	vm_size_t psize;
416	int err;
417
418	/*
419	 * If this is a memory resource, use pmap_mapdev to map it.
420	 */
421	if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
422		paddr = rman_get_start(r);
423		psize = rman_get_size(r);
424		rman_set_bustag(r, mips_bus_space_generic);
425		err = bus_space_map(rman_get_bustag(r), paddr, psize, 0,
426		    (bus_space_handle_t *)&vaddr);
427		if (err != 0) {
428			rman_deactivate_resource(r);
429			return (err);
430		}
431		rman_set_virtual(r, vaddr);
432		rman_set_bushandle(r, (bus_space_handle_t)(uintptr_t)vaddr);
433	} else if (type == SYS_RES_IRQ) {
434#ifdef INTRNG
435#ifdef FDT
436		intr_activate_irq(child, r);
437#else
438		/*
439		 * INTRNG without FDT needs to have the interrupt properly
440		 * mapped first. cpu_create_intr_map() will do that and
441		 * call intr_activate_irq() at the end.
442		 */
443		cpu_create_intr_map(rman_get_start(r));
444#endif
445#endif
446	}
447
448	return (rman_activate_resource(r));
449}
450
451static int
452nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
453			  struct resource *r)
454{
455	bus_space_handle_t vaddr;
456	bus_size_t psize;
457
458	vaddr = rman_get_bushandle(r);
459
460	if (type == SYS_RES_MEMORY && vaddr != 0) {
461		psize = (bus_size_t)rman_get_size(r);
462		bus_space_unmap(rman_get_bustag(r), vaddr, psize);
463		rman_set_virtual(r, NULL);
464		rman_set_bushandle(r, 0);
465	} else if (type == SYS_RES_IRQ) {
466#ifdef INTRNG
467		intr_deactivate_irq(child, r);
468#endif
469	}
470
471	return (rman_deactivate_resource(r));
472}
473
474static int
475nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
476    driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
477{
478#ifdef INTRNG
479	struct resource *r = res;
480
481#ifndef FDT
482	r = cpu_get_irq_resource(rman_get_start(r));
483#endif
484	return (intr_setup_irq(child, r, filt, intr, arg, flags, cookiep));
485#else
486	int irq;
487	register_t s;
488
489	s = intr_disable();
490	irq = rman_get_start(res);
491	if (irq >= NUM_MIPS_IRQS) {
492		intr_restore(s);
493		return (0);
494	}
495
496	cpu_establish_hardintr(device_get_nameunit(child), filt, intr, arg,
497	    irq, flags, cookiep);
498	intr_restore(s);
499
500	return (0);
501#endif
502}
503
504static int
505nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
506{
507
508#ifdef INTRNG
509	return (intr_teardown_irq(child, r, ih));
510#else
511	printf("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__);
512	return (0);
513#endif
514}
515
516#ifdef INTRNG
517static int
518nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
519    enum intr_polarity pol)
520{
521
522	device_printf(dev, "bus_config_intr is obsolete and not supported!\n");
523	return (EOPNOTSUPP);
524}
525
526static int
527nexus_describe_intr(device_t dev, device_t child, struct resource *irq,
528    void *cookie, const char *descr)
529{
530
531	return (intr_describe_irq(child, irq, cookie, descr));
532}
533
534#ifdef SMP
535static int
536nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
537{
538
539	return (intr_bind_irq(child, irq, cpu));
540}
541#endif
542
543#ifdef FDT
544static int
545nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
546    pcell_t *intr)
547{
548	u_int irq;
549	struct intr_map_data_fdt *fdt_data;
550	size_t len;
551
552	len = sizeof(*fdt_data) + icells * sizeof(pcell_t);
553	fdt_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
554	    INTR_MAP_DATA_FDT, len, M_WAITOK | M_ZERO);
555	fdt_data->iparent = iparent;
556	fdt_data->ncells = icells;
557	memcpy(fdt_data->cells, intr, icells * sizeof(pcell_t));
558	irq = intr_map_irq(NULL, iparent, (struct intr_map_data *)fdt_data);
559	return (irq);
560}
561#endif
562#endif /* INTRNG */
563
564static void
565nexus_hinted_child(device_t bus, const char *dname, int dunit)
566{
567	device_t child;
568	long	maddr;
569	int	msize;
570	int	order;
571	int	result;
572	int	irq;
573	int	mem_hints_count;
574
575	if ((resource_int_value(dname, dunit, "order", &order)) != 0)
576		order = 1000;
577	child = BUS_ADD_CHILD(bus, order, dname, dunit);
578	if (child == NULL)
579		return;
580
581	/*
582	 * Set hard-wired resources for hinted child using
583	 * specific RIDs.
584	 */
585	mem_hints_count = 0;
586	if (resource_long_value(dname, dunit, "maddr", &maddr) == 0)
587		mem_hints_count++;
588	if (resource_int_value(dname, dunit, "msize", &msize) == 0)
589		mem_hints_count++;
590
591	/* check if all info for mem resource has been provided */
592	if ((mem_hints_count > 0) && (mem_hints_count < 2)) {
593		printf("Either maddr or msize hint is missing for %s%d\n",
594		    dname, dunit);
595	}
596	else if (mem_hints_count) {
597		dprintf("%s: discovered hinted child %s at maddr %p(%d)\n",
598		    __func__, device_get_nameunit(child),
599		    (void *)(intptr_t)maddr, msize);
600
601		result = bus_set_resource(child, SYS_RES_MEMORY, 0,
602		    (u_long) maddr, msize);
603		if (result != 0) {
604			device_printf(bus,
605			    "warning: bus_set_resource() failed\n");
606		}
607	}
608
609	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
610		result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
611		if (result != 0)
612			device_printf(bus,
613			    "warning: bus_set_resource() failed\n");
614	}
615}
616
617EARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0,
618    BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
619