1 /* $NetBSD: obio.c,v 1.11 2003/07/15 00:25:05 lukem Exp $ */
4 * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
41 #include <sys/param.h>
42 #include <sys/systm.h>
44 #include <sys/interrupt.h>
45 #include <sys/kernel.h>
46 #include <sys/module.h>
48 #include <sys/malloc.h>
50 #include <machine/bus.h>
52 #include <mips/adm5120/adm5120reg.h>
53 #include <mips/adm5120/obiovar.h>
55 /* MIPS HW interrupts of IRQ/FIQ respectively */
56 #define ADM5120_INTR 0
57 #define ADM5120_FAST_INTR 1
59 /* Interrupt levels */
63 int irq_priorities
[NIRQS
] = {
68 INTR_IRQ
, /* unknown */
69 INTR_IRQ
, /* unknown */
70 INTR_IRQ
, /* unknown */
71 INTR_IRQ
, /* unknown */
72 INTR_IRQ
, /* unknown */
74 INTR_IRQ
, /* unknown */
75 INTR_IRQ
, /* unknown */
76 INTR_IRQ
, /* unknown */
77 INTR_IRQ
, /* unknown */
78 INTR_IRQ
, /* unknown */
79 INTR_IRQ
, /* unknown */
80 INTR_IRQ
, /* unknown */
81 INTR_IRQ
, /* unknown */
82 INTR_IRQ
, /* unknown */
83 INTR_IRQ
, /* unknown */
84 INTR_IRQ
, /* unknown */
85 INTR_IRQ
, /* unknown */
86 INTR_IRQ
, /* unknown */
87 INTR_IRQ
, /* unknown */
88 INTR_IRQ
, /* unknown */
89 INTR_IRQ
, /* unknown */
90 INTR_IRQ
, /* unknown */
91 INTR_IRQ
, /* unknown */
92 INTR_IRQ
, /* unknown */
93 INTR_IRQ
, /* unknown */
94 INTR_IRQ
, /* unknown */
95 INTR_IRQ
, /* unknown */
99 #define REG_READ(o) *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1(ADM5120_BASE_ICU + (o)))
100 #define REG_WRITE(o,v) (REG_READ(o)) = (v)
102 static int obio_activate_resource(device_t
, device_t
, int, int,
104 static device_t
obio_add_child(device_t
, u_int
, const char *, int);
105 static struct resource
*
106 obio_alloc_resource(device_t
, device_t
, int, int *, rman_res_t
,
107 rman_res_t
, rman_res_t
, u_int
);
108 static int obio_attach(device_t
);
109 static int obio_deactivate_resource(device_t
, device_t
, int, int,
111 static struct resource_list
*
112 obio_get_resource_list(device_t
, device_t
);
113 static void obio_hinted_child(device_t
, const char *, int);
114 static int obio_intr(void *);
115 static int obio_probe(device_t
);
116 static int obio_release_resource(device_t
, device_t
, int, int,
118 static int obio_setup_intr(device_t
, device_t
, struct resource
*, int,
119 driver_filter_t
*, driver_intr_t
*, void *, void **);
120 static int obio_teardown_intr(device_t
, device_t
, struct resource
*,
125 obio_mask_irq(void *source
)
135 reg
= REG_READ(ICU_DISABLE_REG
);
136 REG_WRITE(ICU_DISABLE_REG
, (reg
| irqmask
));
140 obio_unmask_irq(void *source
)
150 reg
= REG_READ(ICU_DISABLE_REG
);
151 REG_WRITE(ICU_DISABLE_REG
, (reg
& ~irqmask
));
157 obio_probe(device_t dev
)
160 return (BUS_PROBE_NOWILDCARD
);
164 obio_attach(device_t dev
)
166 struct obio_softc
*sc
= device_get_softc(dev
);
169 sc
->oba_mem_rman
.rm_type
= RMAN_ARRAY
;
170 sc
->oba_mem_rman
.rm_descr
= "OBIO memeory";
171 if (rman_init(&sc
->oba_mem_rman
) != 0 ||
172 rman_manage_region(&sc
->oba_mem_rman
, OBIO_MEM_START
,
173 OBIO_MEM_START
+ OBIO_MEM_SIZE
) != 0)
174 panic("obio_attach: failed to set up I/O rman");
176 sc
->oba_irq_rman
.rm_type
= RMAN_ARRAY
;
177 sc
->oba_irq_rman
.rm_descr
= "OBIO IRQ";
179 if (rman_init(&sc
->oba_irq_rman
) != 0 ||
180 rman_manage_region(&sc
->oba_irq_rman
, 0, NIRQS
-1) != 0)
181 panic("obio_attach: failed to set up IRQ rman");
183 /* Hook up our interrupt handler. */
184 if ((sc
->sc_irq
= bus_alloc_resource(dev
, SYS_RES_IRQ
, &rid
,
185 ADM5120_INTR
, ADM5120_INTR
, 1,
186 RF_SHAREABLE
| RF_ACTIVE
)) == NULL
) {
187 device_printf(dev
, "unable to allocate IRQ resource\n");
191 if ((bus_setup_intr(dev
, sc
->sc_irq
, INTR_TYPE_MISC
, obio_intr
, NULL
,
194 "WARNING: unable to register interrupt handler\n");
198 /* Hook up our FAST interrupt handler. */
199 if ((sc
->sc_fast_irq
= bus_alloc_resource(dev
, SYS_RES_IRQ
, &rid
,
200 ADM5120_FAST_INTR
, ADM5120_FAST_INTR
, 1,
201 RF_SHAREABLE
| RF_ACTIVE
)) == NULL
) {
202 device_printf(dev
, "unable to allocate IRQ resource\n");
206 if ((bus_setup_intr(dev
, sc
->sc_fast_irq
, INTR_TYPE_MISC
, obio_intr
,
207 NULL
, sc
, &sc
->sc_fast_ih
))) {
209 "WARNING: unable to register interrupt handler\n");
213 /* disable all interrupts */
214 REG_WRITE(ICU_ENABLE_REG
, ICU_INT_MASK
);
216 bus_generic_probe(dev
);
217 bus_enumerate_hinted_children(dev
);
218 bus_generic_attach(dev
);
223 static struct resource
*
224 obio_alloc_resource(device_t bus
, device_t child
, int type
, int *rid
,
225 rman_res_t start
, rman_res_t end
, rman_res_t count
, u_int flags
)
227 struct obio_softc
*sc
= device_get_softc(bus
);
228 struct obio_ivar
*ivar
= device_get_ivars(child
);
230 struct resource_list_entry
*rle
;
232 int isdefault
, needactivate
, passthrough
;
234 isdefault
= (RMAN_IS_DEFAULT_RANGE(start
, end
) && count
== 1);
235 needactivate
= flags
& RF_ACTIVE
;
236 passthrough
= (device_get_parent(child
) != bus
);
240 return (BUS_ALLOC_RESOURCE(device_get_parent(bus
), child
, type
,
241 rid
, start
, end
, count
, flags
));
244 * If this is an allocation of the "default" range for a given RID,
245 * and we know what the resources for this device are (ie. they aren't
246 * maintained by a child bus), then work out the start/end values.
249 rle
= resource_list_find(&ivar
->resources
, type
, *rid
);
252 if (rle
->res
!= NULL
) {
253 panic("%s: resource entry is busy", __func__
);
262 rm
= &sc
->oba_irq_rman
;
265 rm
= &sc
->oba_mem_rman
;
268 printf("%s: unknown resource type %d\n", __func__
, type
);
272 rv
= rman_reserve_resource(rm
, start
, end
, count
, flags
, child
);
274 printf("%s: could not reserve resource\n", __func__
);
278 rman_set_rid(rv
, *rid
);
281 if (bus_activate_resource(child
, type
, *rid
, rv
)) {
282 printf("%s: could not activate resource\n", __func__
);
283 rman_release_resource(rv
);
292 obio_activate_resource(device_t bus
, device_t child
, int type
, int rid
,
297 * If this is a memory resource, track the direct mapping
298 * in the uncached MIPS KSEG1 segment.
300 if (type
== SYS_RES_MEMORY
) {
303 vaddr
= (void *)MIPS_PHYS_TO_KSEG1((intptr_t)rman_get_start(r
));
304 rman_set_virtual(r
, vaddr
);
305 rman_set_bustag(r
, mips_bus_space_generic
);
306 rman_set_bushandle(r
, (bus_space_handle_t
)vaddr
);
309 return (rman_activate_resource(r
));
313 obio_deactivate_resource(device_t bus
, device_t child
, int type
, int rid
,
317 return (rman_deactivate_resource(r
));
321 obio_release_resource(device_t dev
, device_t child
, int type
,
322 int rid
, struct resource
*r
)
324 struct resource_list
*rl
;
325 struct resource_list_entry
*rle
;
327 rl
= obio_get_resource_list(dev
, child
);
330 rle
= resource_list_find(rl
, type
, rid
);
333 rman_release_resource(r
);
340 obio_setup_intr(device_t dev
, device_t child
, struct resource
*ires
,
341 int flags
, driver_filter_t
*filt
, driver_intr_t
*handler
,
342 void *arg
, void **cookiep
)
344 struct obio_softc
*sc
= device_get_softc(dev
);
345 struct intr_event
*event
;
346 int irq
, error
, priority
;
349 irq
= rman_get_start(ires
);
352 panic("%s: bad irq %d", __func__
, irq
);
354 event
= sc
->sc_eventstab
[irq
];
356 error
= intr_event_create(&event
, (void *)irq
, 0, irq
,
357 obio_mask_irq
, obio_unmask_irq
,
358 NULL
, NULL
, "obio intr%d:", irq
);
360 sc
->sc_eventstab
[irq
] = event
;
363 panic("obio: Can't share IRQs");
365 intr_event_add_handler(event
, device_get_nameunit(child
), filt
,
366 handler
, arg
, intr_priority(flags
), flags
, cookiep
);
369 priority
= irq_priorities
[irq
];
371 if (priority
== INTR_FIQ
)
372 REG_WRITE(ICU_MODE_REG
, REG_READ(ICU_MODE_REG
) | irqmask
);
374 REG_WRITE(ICU_MODE_REG
, REG_READ(ICU_MODE_REG
) & ~irqmask
);
377 REG_WRITE(ICU_ENABLE_REG
, irqmask
);
379 obio_unmask_irq((void*)irq
);
385 obio_teardown_intr(device_t dev
, device_t child
, struct resource
*ires
,
388 struct obio_softc
*sc
= device_get_softc(dev
);
389 int irq
, result
, priority
;
392 irq
= rman_get_start(ires
);
394 panic("%s: bad irq %d", __func__
, irq
);
396 if (sc
->sc_eventstab
[irq
] == NULL
)
397 panic("Trying to teardown unoccupied IRQ");
399 irqmask
= (1 << irq
);
400 priority
= irq_priorities
[irq
];
402 if (priority
== INTR_FIQ
)
403 REG_WRITE(ICU_MODE_REG
, REG_READ(ICU_MODE_REG
) & ~irqmask
);
405 REG_WRITE(ICU_MODE_REG
, REG_READ(ICU_MODE_REG
) | irqmask
);
408 irqmask
= REG_READ(ICU_ENABLE_REG
);
409 irqmask
&= ~(1 << irq
);
410 REG_WRITE(ICU_ENABLE_REG
, irqmask
);
412 result
= intr_event_remove_handler(cookie
);
414 sc
->sc_eventstab
[irq
] = NULL
;
423 struct obio_softc
*sc
= arg
;
424 struct intr_event
*event
;
428 irqstat
= REG_READ(ICU_FIQ_STATUS_REG
);
429 irqstat
|= REG_READ(ICU_STATUS_REG
);
432 while (irqstat
!= 0) {
433 if ((irqstat
& 1) == 1) {
434 event
= sc
->sc_eventstab
[irq
];
435 if (!event
|| TAILQ_EMPTY(&event
->ie_handlers
))
438 /* TODO: pass frame as an argument*/
439 /* TODO: log stray interrupt */
440 intr_event_handle(event
, NULL
);
447 return (FILTER_HANDLED
);
451 obio_hinted_child(device_t bus
, const char *dname
, int dunit
)
459 child
= BUS_ADD_CHILD(bus
, 0, dname
, dunit
);
462 * Set hard-wired resources for hinted child using
465 resource_long_value(dname
, dunit
, "maddr", &maddr
);
466 resource_int_value(dname
, dunit
, "msize", &msize
);
469 result
= bus_set_resource(child
, SYS_RES_MEMORY
, 0,
472 device_printf(bus
, "warning: bus_set_resource() failed\n");
474 if (resource_int_value(dname
, dunit
, "irq", &irq
) == 0) {
475 result
= bus_set_resource(child
, SYS_RES_IRQ
, 0, irq
, 1);
478 "warning: bus_set_resource() failed\n");
483 obio_add_child(device_t bus
, u_int order
, const char *name
, int unit
)
486 struct obio_ivar
*ivar
;
488 ivar
= malloc(sizeof(struct obio_ivar
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
489 resource_list_init(&ivar
->resources
);
491 child
= device_add_child_ordered(bus
, order
, name
, unit
);
493 printf("Can't add child %s%d ordered\n", name
, unit
);
497 device_set_ivars(child
, ivar
);
503 * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource
504 * Provides pointer to resource_list for these routines
506 static struct resource_list
*
507 obio_get_resource_list(device_t dev
, device_t child
)
509 struct obio_ivar
*ivar
;
511 ivar
= device_get_ivars(child
);
512 return (&(ivar
->resources
));
515 static device_method_t obio_methods
[] = {
516 DEVMETHOD(bus_activate_resource
, obio_activate_resource
),
517 DEVMETHOD(bus_add_child
, obio_add_child
),
518 DEVMETHOD(bus_alloc_resource
, obio_alloc_resource
),
519 DEVMETHOD(bus_deactivate_resource
, obio_deactivate_resource
),
520 DEVMETHOD(bus_get_resource_list
, obio_get_resource_list
),
521 DEVMETHOD(bus_hinted_child
, obio_hinted_child
),
522 DEVMETHOD(bus_release_resource
, obio_release_resource
),
523 DEVMETHOD(bus_setup_intr
, obio_setup_intr
),
524 DEVMETHOD(bus_teardown_intr
, obio_teardown_intr
),
525 DEVMETHOD(device_attach
, obio_attach
),
526 DEVMETHOD(device_probe
, obio_probe
),
527 DEVMETHOD(bus_get_resource
, bus_generic_rl_get_resource
),
528 DEVMETHOD(bus_set_resource
, bus_generic_rl_set_resource
),
533 static driver_t obio_driver
= {
536 sizeof(struct obio_softc
),
538 static devclass_t obio_devclass
;
540 DRIVER_MODULE(obio
, nexus
, obio_driver
, obio_devclass
, 0, 0);