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};
128270075Sian#ifdef ARM_DEVICE_MULTIPASS
129270075SianEARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0,
130270075Sian    BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
131270075Sian#else
132266000SianDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
133270075Sian#endif
134129198Scognet
135129198Scognetstatic int
136129198Scognetnexus_probe(device_t dev)
137129198Scognet{
138209129Sraj
139129198Scognet	device_quiet(dev);	/* suppress attach message for neatness */
140182934Sraj
141209129Sraj	return (BUS_PROBE_DEFAULT);
142129198Scognet}
143129198Scognet
144129198Scognetstatic int
145129198Scognetnexus_attach(device_t dev)
146129198Scognet{
147182934Sraj
148209232Sraj	mem_rman.rm_start = 0;
149221218Sjhb	mem_rman.rm_end = ~0ul;
150209232Sraj	mem_rman.rm_type = RMAN_ARRAY;
151209232Sraj	mem_rman.rm_descr = "I/O memory addresses";
152221218Sjhb	if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, ~0))
153209232Sraj		panic("nexus_probe mem_rman");
154209232Sraj
155129198Scognet	/*
156129198Scognet	 * First, deal with the children we know about already
157129198Scognet	 */
158129198Scognet	bus_generic_probe(dev);
159129198Scognet	bus_generic_attach(dev);
160182934Sraj
161182934Sraj	return (0);
162129198Scognet}
163129198Scognet
164129198Scognetstatic int
165129198Scognetnexus_print_child(device_t bus, device_t child)
166129198Scognet{
167129198Scognet	int retval = 0;
168182934Sraj
169129198Scognet	retval += bus_print_child_header(bus, child);
170235907Sgber	retval += printf("\n");
171182934Sraj
172129198Scognet	return (retval);
173129198Scognet}
174129198Scognet
175129198Scognetstatic device_t
176212413Savgnexus_add_child(device_t bus, u_int order, const char *name, int unit)
177129198Scognet{
178182934Sraj	device_t child;
179129198Scognet	struct nexus_device *ndev;
180182934Sraj
181129198Scognet	ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO);
182129198Scognet	if (!ndev)
183182934Sraj		return (0);
184129198Scognet	resource_list_init(&ndev->nx_resources);
185129198Scognet
186129198Scognet	child = device_add_child_ordered(bus, order, name, unit);
187182934Sraj
188129198Scognet	/* should we free this in nexus_child_detached? */
189129198Scognet	device_set_ivars(child, ndev);
190182934Sraj
191182934Sraj	return (child);
192129198Scognet}
193129198Scognet
194129198Scognet
195129198Scognet/*
196129198Scognet * Allocate a resource on behalf of child.  NB: child is usually going to be a
197129198Scognet * child of one of our descendants, not a direct child of nexus0.
198129198Scognet * (Exceptions include footbridge.)
199129198Scognet */
200129198Scognetstatic struct resource *
201129198Scognetnexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
202182934Sraj    u_long start, u_long end, u_long count, u_int flags)
203129198Scognet{
204129198Scognet	struct resource *rv;
205129198Scognet	struct rman *rm;
206129198Scognet	int needactivate = flags & RF_ACTIVE;
207129198Scognet
208129198Scognet	switch (type) {
209129198Scognet	case SYS_RES_MEMORY:
210266000Sian	case SYS_RES_IOPORT:
211129198Scognet		rm = &mem_rman;
212129198Scognet		break;
213182934Sraj
214129198Scognet	default:
215182934Sraj		return (0);
216129198Scognet	}
217129198Scognet
218129198Scognet	rv = rman_reserve_resource(rm, start, end, count, flags, child);
219129198Scognet	if (rv == 0)
220182934Sraj		return (0);
221129198Scognet
222157891Simp	rman_set_rid(rv, *rid);
223182934Sraj	rman_set_bushandle(rv, rman_get_start(rv));
224182934Sraj
225129198Scognet	if (needactivate) {
226129198Scognet		if (bus_activate_resource(child, type, *rid, rv)) {
227129198Scognet			rman_release_resource(rv);
228182934Sraj			return (0);
229129198Scognet		}
230129198Scognet	}
231182934Sraj
232182934Sraj	return (rv);
233129198Scognet}
234129198Scognet
235266000Sianstatic int
236266070Siannexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
237266070Sian    enum intr_polarity pol)
238266070Sian{
239266070Sian	int ret = ENODEV;
240266070Sian
241266070Sian	if (arm_config_irq)
242266070Sian		ret = (*arm_config_irq)(irq, trig, pol);
243266070Sian
244266070Sian	return (ret);
245266070Sian}
246266070Sian
247266070Sianstatic int
248266000Siannexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
249266000Sian    driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
250266000Sian{
251266000Sian	int irq;
252129198Scognet
253266000Sian	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
254266000Sian		flags |= INTR_EXCL;
255266000Sian
256266000Sian	for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
257266000Sian		arm_setup_irqhandler(device_get_nameunit(child),
258266000Sian		    filt, intr, arg, irq, flags, cookiep);
259266000Sian		arm_unmask_irq(irq);
260266000Sian	}
261266000Sian	return (0);
262266000Sian}
263266000Sian
264129198Scognetstatic int
265266000Siannexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
266266000Sian{
267266000Sian
268266000Sian	return (arm_remove_irqhandler(rman_get_start(r), ih));
269266000Sian}
270266000Sian
271266000Sian
272266000Sianstatic int
273129198Scognetnexus_activate_resource(device_t bus, device_t child, int type, int rid,
274182934Sraj    struct resource *r)
275129198Scognet{
276266079Sian	int err;
277266079Sian	bus_addr_t paddr;
278266079Sian	bus_size_t psize;
279266079Sian	bus_space_handle_t vaddr;
280266079Sian
281266079Sian	if ((err = rman_activate_resource(r)) != 0)
282266079Sian		return (err);
283266079Sian
284129198Scognet	/*
285129198Scognet	 * If this is a memory resource, map it into the kernel.
286129198Scognet	 */
287266000Sian	if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
288266079Sian		paddr = (bus_addr_t)rman_get_start(r);
289266079Sian		psize = (bus_size_t)rman_get_size(r);
290266000Sian#ifdef FDT
291266079Sian		err = bus_space_map(fdtbus_bs_tag, paddr, psize, 0, &vaddr);
292266079Sian		if (err != 0) {
293266079Sian			rman_deactivate_resource(r);
294266079Sian			return (err);
295266079Sian		}
296266000Sian		rman_set_bustag(r, fdtbus_bs_tag);
297266000Sian#else
298266079Sian		vaddr = (bus_space_handle_t)pmap_mapdev((vm_offset_t)paddr,
299266079Sian		    (vm_size_t)psize);
300266079Sian		if (vaddr == 0) {
301266079Sian			rman_deactivate_resource(r);
302266079Sian			return (ENOMEM);
303266079Sian		}
304266000Sian		rman_set_bustag(r, (void *)1);
305266000Sian#endif
306266079Sian		rman_set_virtual(r, (void *)vaddr);
307266079Sian		rman_set_bushandle(r, vaddr);
308129198Scognet	}
309266079Sian	return (0);
310129198Scognet}
311129198Scognet
312266000Sianstatic int
313266000Siannexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
314266079Sian    struct resource *r)
315266000Sian{
316266079Sian	bus_size_t psize;
317266079Sian	bus_space_handle_t vaddr;
318266000Sian
319266079Sian	psize = (bus_size_t)rman_get_size(r);
320266079Sian	vaddr = rman_get_bushandle(r);
321266079Sian
322266079Sian	if (vaddr != 0) {
323266079Sian#ifdef FDT
324266079Sian		bus_space_unmap(fdtbus_bs_tag, vaddr, psize);
325266079Sian#else
326266079Sian		pmap_unmapdev((vm_offset_t)vaddr, (vm_size_t)psize);
327266079Sian#endif
328266079Sian		rman_set_virtual(r, NULL);
329266079Sian		rman_set_bushandle(r, 0);
330266079Sian	}
331266079Sian
332266079Sian	return (rman_deactivate_resource(r));
333266000Sian}
334266000Sian
335266079Sian#ifdef FDT
336266079Sianstatic int
337266128Siannexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
338266128Sian    pcell_t *intr)
339266079Sian{
340266079Sian	fdt_pic_decode_t intr_decode;
341266079Sian	phandle_t intr_offset;
342266079Sian	int i, rv, interrupt, trig, pol;
343266079Sian
344266079Sian	intr_offset = OF_xref_phandle(iparent);
345266128Sian	for (i = 0; i < icells; i++)
346266128Sian		intr[i] = cpu_to_fdt32(intr[i]);
347266079Sian
348266079Sian	for (i = 0; fdt_pic_table[i] != NULL; i++) {
349266079Sian		intr_decode = fdt_pic_table[i];
350266079Sian		rv = intr_decode(intr_offset, intr, &interrupt, &trig, &pol);
351266079Sian
352266079Sian		if (rv == 0) {
353266079Sian			/* This was recognized as our PIC and decoded. */
354266079Sian			interrupt = FDT_MAP_IRQ(intr_parent, interrupt);
355266079Sian			return (interrupt);
356266079Sian		}
357266079Sian	}
358266079Sian
359266079Sian	/* Not in table, so guess */
360266128Sian	interrupt = FDT_MAP_IRQ(intr_parent, fdt32_to_cpu(intr[0]));
361266079Sian
362266079Sian	return (interrupt);
363266079Sian}
364266079Sian#endif
365266079Sian
366