1139735Simp/*-
2129198Scognet * Copyright 1998 Massachusetts Institute of Technology
3129198Scognet *
4129198Scognet * Permission to use, copy, modify, and distribute this software and
5129198Scognet * its documentation for any purpose and without fee is hereby
6129198Scognet * granted, provided that both the above copyright notice and this
7129198Scognet * permission notice appear in all copies, that both the above
8129198Scognet * copyright notice and this permission notice appear in all
9129198Scognet * supporting documentation, and that the name of M.I.T. not be used
10129198Scognet * in advertising or publicity pertaining to distribution of the
11129198Scognet * software without specific, written prior permission.  M.I.T. makes
12129198Scognet * no representations about the suitability of this software for any
13129198Scognet * purpose.  It is provided "as is" without express or implied
14129198Scognet * warranty.
15182934Sraj *
16129198Scognet * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17129198Scognet * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18129198Scognet * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19129198Scognet * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20129198Scognet * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21129198Scognet * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22129198Scognet * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23129198Scognet * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24129198Scognet * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25129198Scognet * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26129198Scognet * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27129198Scognet * SUCH DAMAGE.
28129198Scognet *
29129198Scognet */
30129198Scognet
31129198Scognet/*
32129198Scognet * This code implements a `root nexus' for Arm Architecture
33129198Scognet * machines.  The function of the root nexus is to serve as an
34129198Scognet * attachment point for both processors and buses, and to manage
35129198Scognet * resources which are common to all of them.  In particular,
36129198Scognet * this code implements the core resource managers for interrupt
37129198Scognet * requests, DMA requests (which rightfully should be a part of the
38129198Scognet * ISA code but it's easier to do it here for now), I/O port addresses,
39129198Scognet * and I/O memory address space.
40129198Scognet */
41129198Scognet
42129198Scognet#include <sys/cdefs.h>
43129198Scognet__FBSDID("$FreeBSD$");
44129198Scognet
45129198Scognet#include <sys/param.h>
46129198Scognet#include <sys/systm.h>
47129198Scognet#include <sys/bus.h>
48129198Scognet#include <sys/kernel.h>
49129198Scognet#include <sys/malloc.h>
50129198Scognet#include <sys/module.h>
51129198Scognet#include <machine/bus.h>
52129198Scognet#include <sys/rman.h>
53129198Scognet#include <sys/interrupt.h>
54129198Scognet
55129198Scognet#include <machine/vmparam.h>
56129198Scognet#include <machine/pcb.h>
57129198Scognet#include <vm/vm.h>
58129198Scognet#include <vm/pmap.h>
59129198Scognet
60129198Scognet#include <machine/resource.h>
61129198Scognet#include <machine/intr.h>
62129198Scognet
63266000Sian#include "opt_platform.h"
64266000Sian
65266000Sian#ifdef FDT
66266079Sian#include <dev/fdt/fdt_common.h>
67266000Sian#include <machine/fdt.h>
68266079Sian#include "ofw_bus_if.h"
69266160Sian#endif
70266160Sian
71129198Scognetstatic MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device");
72129198Scognet
73129198Scognetstruct nexus_device {
74129198Scognet	struct resource_list	nx_resources;
75129198Scognet};
76129198Scognet
77129198Scognet#define DEVTONX(dev)	((struct nexus_device *)device_get_ivars(dev))
78129198Scognet
79129198Scognetstatic struct rman mem_rman;
80129198Scognet
81129198Scognetstatic	int nexus_probe(device_t);
82129198Scognetstatic	int nexus_attach(device_t);
83129198Scognetstatic	int nexus_print_child(device_t, device_t);
84212413Savgstatic	device_t nexus_add_child(device_t, u_int, const char *, int);
85129198Scognetstatic	struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
86182934Sraj    u_long, u_long, u_long, u_int);
87129198Scognetstatic	int nexus_activate_resource(device_t, device_t, int, int,
88182934Sraj    struct resource *);
89266070Sianstatic int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
90266070Sian    enum intr_polarity pol);
91266000Sianstatic	int nexus_deactivate_resource(device_t, device_t, int, int,
92266000Sian    struct resource *);
93266000Sian
94182934Srajstatic int nexus_setup_intr(device_t dev, device_t child, struct resource *res,
95182934Sraj    int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep);
96182934Srajstatic int nexus_teardown_intr(device_t, device_t, struct resource *, void *);
97147166Scognet
98266079Sian#ifdef FDT
99266079Sianstatic int nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent,
100266128Sian    int icells, pcell_t *intr);
101266079Sian#endif
102266079Sian
103129198Scognetstatic device_method_t nexus_methods[] = {
104129198Scognet	/* Device interface */
105129198Scognet	DEVMETHOD(device_probe,		nexus_probe),
106129198Scognet	DEVMETHOD(device_attach,	nexus_attach),
107129198Scognet	/* Bus interface */
108129198Scognet	DEVMETHOD(bus_print_child,	nexus_print_child),
109129198Scognet	DEVMETHOD(bus_add_child,	nexus_add_child),
110129198Scognet	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
111129198Scognet	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
112266070Sian	DEVMETHOD(bus_config_intr,	nexus_config_intr),
113266000Sian	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
114129198Scognet	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
115147166Scognet	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
116266079Sian#ifdef FDT
117266079Sian	DEVMETHOD(ofw_bus_map_intr,	nexus_ofw_map_intr),
118266079Sian#endif
119129198Scognet	{ 0, 0 }
120129198Scognet};
121129198Scognet
122266000Sianstatic devclass_t nexus_devclass;
123129198Scognetstatic driver_t nexus_driver = {
124129198Scognet	"nexus",
125129198Scognet	nexus_methods,
126129198Scognet	1			/* no softc */
127129198Scognet};
128270075SianEARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0,
129270075Sian    BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
130129198Scognet
131129198Scognetstatic int
132129198Scognetnexus_probe(device_t dev)
133129198Scognet{
134209129Sraj
135129198Scognet	device_quiet(dev);	/* suppress attach message for neatness */
136182934Sraj
137209129Sraj	return (BUS_PROBE_DEFAULT);
138129198Scognet}
139129198Scognet
140129198Scognetstatic int
141129198Scognetnexus_attach(device_t dev)
142129198Scognet{
143182934Sraj
144209232Sraj	mem_rman.rm_start = 0;
145221218Sjhb	mem_rman.rm_end = ~0ul;
146209232Sraj	mem_rman.rm_type = RMAN_ARRAY;
147209232Sraj	mem_rman.rm_descr = "I/O memory addresses";
148221218Sjhb	if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, ~0))
149209232Sraj		panic("nexus_probe mem_rman");
150209232Sraj
151129198Scognet	/*
152129198Scognet	 * First, deal with the children we know about already
153129198Scognet	 */
154129198Scognet	bus_generic_probe(dev);
155129198Scognet	bus_generic_attach(dev);
156182934Sraj
157182934Sraj	return (0);
158129198Scognet}
159129198Scognet
160129198Scognetstatic int
161129198Scognetnexus_print_child(device_t bus, device_t child)
162129198Scognet{
163129198Scognet	int retval = 0;
164182934Sraj
165129198Scognet	retval += bus_print_child_header(bus, child);
166235907Sgber	retval += printf("\n");
167182934Sraj
168129198Scognet	return (retval);
169129198Scognet}
170129198Scognet
171129198Scognetstatic device_t
172212413Savgnexus_add_child(device_t bus, u_int order, const char *name, int unit)
173129198Scognet{
174182934Sraj	device_t child;
175129198Scognet	struct nexus_device *ndev;
176182934Sraj
177129198Scognet	ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO);
178129198Scognet	if (!ndev)
179182934Sraj		return (0);
180129198Scognet	resource_list_init(&ndev->nx_resources);
181129198Scognet
182129198Scognet	child = device_add_child_ordered(bus, order, name, unit);
183182934Sraj
184129198Scognet	/* should we free this in nexus_child_detached? */
185129198Scognet	device_set_ivars(child, ndev);
186182934Sraj
187182934Sraj	return (child);
188129198Scognet}
189129198Scognet
190129198Scognet
191129198Scognet/*
192129198Scognet * Allocate a resource on behalf of child.  NB: child is usually going to be a
193129198Scognet * child of one of our descendants, not a direct child of nexus0.
194129198Scognet * (Exceptions include footbridge.)
195129198Scognet */
196129198Scognetstatic struct resource *
197129198Scognetnexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
198182934Sraj    u_long start, u_long end, u_long count, u_int flags)
199129198Scognet{
200129198Scognet	struct resource *rv;
201129198Scognet	struct rman *rm;
202129198Scognet	int needactivate = flags & RF_ACTIVE;
203129198Scognet
204129198Scognet	switch (type) {
205129198Scognet	case SYS_RES_MEMORY:
206266000Sian	case SYS_RES_IOPORT:
207129198Scognet		rm = &mem_rman;
208129198Scognet		break;
209182934Sraj
210129198Scognet	default:
211182934Sraj		return (0);
212129198Scognet	}
213129198Scognet
214129198Scognet	rv = rman_reserve_resource(rm, start, end, count, flags, child);
215129198Scognet	if (rv == 0)
216182934Sraj		return (0);
217129198Scognet
218157891Simp	rman_set_rid(rv, *rid);
219182934Sraj	rman_set_bushandle(rv, rman_get_start(rv));
220182934Sraj
221129198Scognet	if (needactivate) {
222129198Scognet		if (bus_activate_resource(child, type, *rid, rv)) {
223129198Scognet			rman_release_resource(rv);
224182934Sraj			return (0);
225129198Scognet		}
226129198Scognet	}
227182934Sraj
228182934Sraj	return (rv);
229129198Scognet}
230129198Scognet
231266000Sianstatic int
232266070Siannexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
233266070Sian    enum intr_polarity pol)
234266070Sian{
235266070Sian	int ret = ENODEV;
236266070Sian
237266070Sian	if (arm_config_irq)
238266070Sian		ret = (*arm_config_irq)(irq, trig, pol);
239266070Sian
240266070Sian	return (ret);
241266070Sian}
242266070Sian
243266070Sianstatic int
244266000Siannexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
245266000Sian    driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
246266000Sian{
247266000Sian	int irq;
248129198Scognet
249266000Sian	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
250266000Sian		flags |= INTR_EXCL;
251266000Sian
252266000Sian	for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
253266000Sian		arm_setup_irqhandler(device_get_nameunit(child),
254266000Sian		    filt, intr, arg, irq, flags, cookiep);
255266000Sian		arm_unmask_irq(irq);
256266000Sian	}
257266000Sian	return (0);
258266000Sian}
259266000Sian
260129198Scognetstatic int
261266000Siannexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
262266000Sian{
263266000Sian
264266000Sian	return (arm_remove_irqhandler(rman_get_start(r), ih));
265266000Sian}
266266000Sian
267266000Sian
268266000Sianstatic int
269129198Scognetnexus_activate_resource(device_t bus, device_t child, int type, int rid,
270182934Sraj    struct resource *r)
271129198Scognet{
272266079Sian	int err;
273266079Sian	bus_addr_t paddr;
274266079Sian	bus_size_t psize;
275266079Sian	bus_space_handle_t vaddr;
276266079Sian
277266079Sian	if ((err = rman_activate_resource(r)) != 0)
278266079Sian		return (err);
279266079Sian
280129198Scognet	/*
281129198Scognet	 * If this is a memory resource, map it into the kernel.
282129198Scognet	 */
283266000Sian	if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
284266079Sian		paddr = (bus_addr_t)rman_get_start(r);
285266079Sian		psize = (bus_size_t)rman_get_size(r);
286266000Sian#ifdef FDT
287266079Sian		err = bus_space_map(fdtbus_bs_tag, paddr, psize, 0, &vaddr);
288266079Sian		if (err != 0) {
289266079Sian			rman_deactivate_resource(r);
290266079Sian			return (err);
291266079Sian		}
292266000Sian		rman_set_bustag(r, fdtbus_bs_tag);
293266000Sian#else
294266079Sian		vaddr = (bus_space_handle_t)pmap_mapdev((vm_offset_t)paddr,
295266079Sian		    (vm_size_t)psize);
296266079Sian		if (vaddr == 0) {
297266079Sian			rman_deactivate_resource(r);
298266079Sian			return (ENOMEM);
299266079Sian		}
300266000Sian		rman_set_bustag(r, (void *)1);
301266000Sian#endif
302266079Sian		rman_set_virtual(r, (void *)vaddr);
303266079Sian		rman_set_bushandle(r, vaddr);
304129198Scognet	}
305266079Sian	return (0);
306129198Scognet}
307129198Scognet
308266000Sianstatic int
309266000Siannexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
310266079Sian    struct resource *r)
311266000Sian{
312266079Sian	bus_size_t psize;
313266079Sian	bus_space_handle_t vaddr;
314266000Sian
315266079Sian	psize = (bus_size_t)rman_get_size(r);
316266079Sian	vaddr = rman_get_bushandle(r);
317266079Sian
318266079Sian	if (vaddr != 0) {
319266079Sian#ifdef FDT
320266079Sian		bus_space_unmap(fdtbus_bs_tag, vaddr, psize);
321266079Sian#else
322266079Sian		pmap_unmapdev((vm_offset_t)vaddr, (vm_size_t)psize);
323266079Sian#endif
324266079Sian		rman_set_virtual(r, NULL);
325266079Sian		rman_set_bushandle(r, 0);
326266079Sian	}
327266079Sian
328266079Sian	return (rman_deactivate_resource(r));
329266000Sian}
330266000Sian
331266079Sian#ifdef FDT
332266079Sianstatic int
333266128Siannexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
334266128Sian    pcell_t *intr)
335266079Sian{
336266079Sian	fdt_pic_decode_t intr_decode;
337273671Sian	phandle_t intr_parent;
338266079Sian	int i, rv, interrupt, trig, pol;
339266079Sian
340273671Sian	intr_parent = OF_node_from_xref(iparent);
341266128Sian	for (i = 0; i < icells; i++)
342266128Sian		intr[i] = cpu_to_fdt32(intr[i]);
343266079Sian
344266079Sian	for (i = 0; fdt_pic_table[i] != NULL; i++) {
345266079Sian		intr_decode = fdt_pic_table[i];
346273671Sian		rv = intr_decode(intr_parent, intr, &interrupt, &trig, &pol);
347266079Sian
348266079Sian		if (rv == 0) {
349266079Sian			/* This was recognized as our PIC and decoded. */
350266079Sian			interrupt = FDT_MAP_IRQ(intr_parent, interrupt);
351266079Sian			return (interrupt);
352266079Sian		}
353266079Sian	}
354266079Sian
355266079Sian	/* Not in table, so guess */
356266128Sian	interrupt = FDT_MAP_IRQ(intr_parent, fdt32_to_cpu(intr[0]));
357266079Sian
358266079Sian	return (interrupt);
359266079Sian}
360266079Sian#endif
361266079Sian
362