xref: /freebsd-11-stable/sys/mips/adm5120/obio.c (revision 4ab2e064d7950be84256d671a7ae93f87cc6aa36)
1 /*	$NetBSD: obio.c,v 1.11 2003/07/15 00:25:05 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
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
23  *    written permission.
24  *
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.
36  */
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/bus.h>
44 #include <sys/interrupt.h>
45 #include <sys/kernel.h>
46 #include <sys/module.h>
47 #include <sys/rman.h>
48 #include <sys/malloc.h>
49 
50 #include <machine/bus.h>
51 
52 #include <mips/adm5120/adm5120reg.h>
53 #include <mips/adm5120/obiovar.h>
54 
55 /* MIPS HW interrupts of IRQ/FIQ respectively */
56 #define ADM5120_INTR		0
57 #define ADM5120_FAST_INTR	1
58 
59 /* Interrupt levels */
60 #define INTR_IRQ 0
61 #define INTR_FIQ 1
62 
63 int irq_priorities[NIRQS] = {
64 	INTR_IRQ,	/* flash */
65 	INTR_FIQ,	/* uart0 */
66 	INTR_FIQ,	/* uart1 */
67 	INTR_IRQ,	/* ahci  */
68 	INTR_IRQ,	/* unknown */
69 	INTR_IRQ,	/* unknown */
70 	INTR_IRQ,	/* unknown */
71 	INTR_IRQ,	/* unknown */
72 	INTR_IRQ,	/* unknown */
73 	INTR_IRQ,	/* admsw */
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 */
96 };
97 
98 
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)
101 
102 static int	obio_activate_resource(device_t, device_t, int, int,
103 		    struct resource *);
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,
110 		    struct resource *);
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,
117 		    struct resource *);
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 *,
121 		    void *);
122 
123 
124 static void
obio_mask_irq(void * source)125 obio_mask_irq(void *source)
126 {
127 	int irq;
128 	uint32_t irqmask;
129 	uint32_t reg;
130 
131 	irq = (int)source;
132 	irqmask = 1 << irq;
133 
134 	/* disable IRQ */
135 	reg = REG_READ(ICU_DISABLE_REG);
136 	REG_WRITE(ICU_DISABLE_REG, (reg | irqmask));
137 }
138 
139 static void
obio_unmask_irq(void * source)140 obio_unmask_irq(void *source)
141 {
142 	int irq;
143 	uint32_t irqmask;
144 	uint32_t reg;
145 
146 	irq = (int)source;
147 	irqmask = 1 << irq;
148 
149 	/* disable IRQ */
150 	reg = REG_READ(ICU_DISABLE_REG);
151 	REG_WRITE(ICU_DISABLE_REG, (reg & ~irqmask));
152 
153 }
154 
155 
156 static int
obio_probe(device_t dev)157 obio_probe(device_t dev)
158 {
159 
160 	return (BUS_PROBE_NOWILDCARD);
161 }
162 
163 static int
obio_attach(device_t dev)164 obio_attach(device_t dev)
165 {
166 	struct obio_softc *sc = device_get_softc(dev);
167 	int rid;
168 
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");
175 
176 	sc->oba_irq_rman.rm_type = RMAN_ARRAY;
177 	sc->oba_irq_rman.rm_descr = "OBIO IRQ";
178 
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");
182 
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");
188 		return (ENXIO);
189 	}
190 
191 	if ((bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC, obio_intr, NULL,
192 	    sc, &sc->sc_ih))) {
193 		device_printf(dev,
194 		    "WARNING: unable to register interrupt handler\n");
195 		return (ENXIO);
196 	}
197 
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");
203 		return (ENXIO);
204 	}
205 
206 	if ((bus_setup_intr(dev, sc->sc_fast_irq, INTR_TYPE_MISC, obio_intr,
207 	    NULL, sc, &sc->sc_fast_ih))) {
208 		device_printf(dev,
209 		    "WARNING: unable to register interrupt handler\n");
210 		return (ENXIO);
211 	}
212 
213 	/* disable all interrupts */
214 	REG_WRITE(ICU_ENABLE_REG, ICU_INT_MASK);
215 
216 	bus_generic_probe(dev);
217 	bus_enumerate_hinted_children(dev);
218 	bus_generic_attach(dev);
219 
220 	return (0);
221 }
222 
223 static struct resource *
obio_alloc_resource(device_t bus,device_t child,int type,int * rid,rman_res_t start,rman_res_t end,rman_res_t count,u_int flags)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)
226 {
227 	struct obio_softc		*sc = device_get_softc(bus);
228 	struct obio_ivar		*ivar = device_get_ivars(child);
229 	struct resource			*rv;
230 	struct resource_list_entry	*rle;
231 	struct rman			*rm;
232 	int				 isdefault, needactivate, passthrough;
233 
234 	isdefault = (RMAN_IS_DEFAULT_RANGE(start, end) && count == 1);
235 	needactivate = flags & RF_ACTIVE;
236 	passthrough = (device_get_parent(child) != bus);
237 	rle = NULL;
238 
239 	if (passthrough)
240 		return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type,
241 		    rid, start, end, count, flags));
242 
243 	/*
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.
247 	 */
248 	if (isdefault) {
249 		rle = resource_list_find(&ivar->resources, type, *rid);
250 		if (rle == NULL)
251 			return (NULL);
252 		if (rle->res != NULL) {
253 			panic("%s: resource entry is busy", __func__);
254 		}
255 		start = rle->start;
256 		end = rle->end;
257 		count = rle->count;
258 	}
259 
260 	switch (type) {
261 	case SYS_RES_IRQ:
262 		rm = &sc->oba_irq_rman;
263 		break;
264 	case SYS_RES_MEMORY:
265 		rm = &sc->oba_mem_rman;
266 		break;
267 	default:
268 		printf("%s: unknown resource type %d\n", __func__, type);
269 		return (0);
270 	}
271 
272 	rv = rman_reserve_resource(rm, start, end, count, flags, child);
273 	if (rv == NULL) {
274 		printf("%s: could not reserve resource\n", __func__);
275 		return (0);
276 	}
277 
278 	rman_set_rid(rv, *rid);
279 
280 	if (needactivate) {
281 		if (bus_activate_resource(child, type, *rid, rv)) {
282 			printf("%s: could not activate resource\n", __func__);
283 			rman_release_resource(rv);
284 			return (0);
285 		}
286 	}
287 
288 	return (rv);
289 }
290 
291 static int
obio_activate_resource(device_t bus,device_t child,int type,int rid,struct resource * r)292 obio_activate_resource(device_t bus, device_t child, int type, int rid,
293     struct resource *r)
294 {
295 
296 	/*
297 	 * If this is a memory resource, track the direct mapping
298 	 * in the uncached MIPS KSEG1 segment.
299 	 */
300 	if (type == SYS_RES_MEMORY) {
301 		void *vaddr;
302 
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);
307 	}
308 
309 	return (rman_activate_resource(r));
310 }
311 
312 static int
obio_deactivate_resource(device_t bus,device_t child,int type,int rid,struct resource * r)313 obio_deactivate_resource(device_t bus, device_t child, int type, int rid,
314     struct resource *r)
315 {
316 
317 	return (rman_deactivate_resource(r));
318 }
319 
320 static int
obio_release_resource(device_t dev,device_t child,int type,int rid,struct resource * r)321 obio_release_resource(device_t dev, device_t child, int type,
322     int rid, struct resource *r)
323 {
324 	struct resource_list *rl;
325 	struct resource_list_entry *rle;
326 
327 	rl = obio_get_resource_list(dev, child);
328 	if (rl == NULL)
329 		return (EINVAL);
330 	rle = resource_list_find(rl, type, rid);
331 	if (rle == NULL)
332 		return (EINVAL);
333 	rman_release_resource(r);
334 	rle->res = NULL;
335 
336 	return (0);
337 }
338 
339 static int
obio_setup_intr(device_t dev,device_t child,struct resource * ires,int flags,driver_filter_t * filt,driver_intr_t * handler,void * arg,void ** cookiep)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)
343 {
344 	struct obio_softc *sc = device_get_softc(dev);
345 	struct intr_event *event;
346 	int irq, error, priority;
347 	uint32_t irqmask;
348 
349 	irq = rman_get_start(ires);
350 
351 	if (irq >= NIRQS)
352 		panic("%s: bad irq %d", __func__, irq);
353 
354 	event = sc->sc_eventstab[irq];
355 	if (event == NULL) {
356 		error = intr_event_create(&event, (void *)irq, 0, irq,
357 		    obio_mask_irq, obio_unmask_irq,
358 		    NULL, NULL, "obio intr%d:", irq);
359 
360 		sc->sc_eventstab[irq] = event;
361 	}
362 	else
363 		panic("obio: Can't share IRQs");
364 
365 	intr_event_add_handler(event, device_get_nameunit(child), filt,
366 	    handler, arg, intr_priority(flags), flags, cookiep);
367 
368 	irqmask = 1 << irq;
369 	priority = irq_priorities[irq];
370 
371 	if (priority == INTR_FIQ)
372 		REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) | irqmask);
373 	else
374 		REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) & ~irqmask);
375 
376 	/* enable */
377 	REG_WRITE(ICU_ENABLE_REG, irqmask);
378 
379 	obio_unmask_irq((void*)irq);
380 
381 	return (0);
382 }
383 
384 static int
obio_teardown_intr(device_t dev,device_t child,struct resource * ires,void * cookie)385 obio_teardown_intr(device_t dev, device_t child, struct resource *ires,
386     void *cookie)
387 {
388 	struct obio_softc *sc = device_get_softc(dev);
389 	int irq, result, priority;
390 	uint32_t irqmask;
391 
392 	irq = rman_get_start(ires);
393 	if (irq >= NIRQS)
394 		panic("%s: bad irq %d", __func__, irq);
395 
396 	if (sc->sc_eventstab[irq] == NULL)
397 		panic("Trying to teardown unoccupied IRQ");
398 
399 	irqmask = (1 << irq);
400 	priority = irq_priorities[irq];
401 
402 	if (priority == INTR_FIQ)
403 		REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) & ~irqmask);
404 	else
405 		REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) | irqmask);
406 
407 	/* disable */
408 	irqmask = REG_READ(ICU_ENABLE_REG);
409 	irqmask &= ~(1 << irq);
410 	REG_WRITE(ICU_ENABLE_REG, irqmask);
411 
412 	result = intr_event_remove_handler(cookie);
413 	if (!result) {
414 		sc->sc_eventstab[irq] = NULL;
415 	}
416 
417 	return (result);
418 }
419 
420 static int
obio_intr(void * arg)421 obio_intr(void *arg)
422 {
423 	struct obio_softc *sc = arg;
424 	struct intr_event *event;
425 	uint32_t irqstat;
426 	int irq;
427 
428 	irqstat = REG_READ(ICU_FIQ_STATUS_REG);
429 	irqstat |= REG_READ(ICU_STATUS_REG);
430 
431 	irq = 0;
432 	while (irqstat != 0) {
433 		if ((irqstat & 1) == 1) {
434 			event = sc->sc_eventstab[irq];
435 			if (!event || TAILQ_EMPTY(&event->ie_handlers))
436 				continue;
437 
438 			/* TODO: pass frame as an argument*/
439 			/* TODO: log stray interrupt */
440 			intr_event_handle(event, NULL);
441 		}
442 
443 		irq++;
444 		irqstat >>= 1;
445 	}
446 
447 	return (FILTER_HANDLED);
448 }
449 
450 static void
obio_hinted_child(device_t bus,const char * dname,int dunit)451 obio_hinted_child(device_t bus, const char *dname, int dunit)
452 {
453 	device_t		child;
454 	long			maddr;
455 	int			msize;
456 	int			irq;
457 	int			result;
458 
459 	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
460 
461 	/*
462 	 * Set hard-wired resources for hinted child using
463 	 * specific RIDs.
464 	 */
465 	resource_long_value(dname, dunit, "maddr", &maddr);
466 	resource_int_value(dname, dunit, "msize", &msize);
467 
468 
469 	result = bus_set_resource(child, SYS_RES_MEMORY, 0,
470 	    maddr, msize);
471 	if (result != 0)
472 		device_printf(bus, "warning: bus_set_resource() failed\n");
473 
474 	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
475 		result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
476 		if (result != 0)
477 			device_printf(bus,
478 			    "warning: bus_set_resource() failed\n");
479 	}
480 }
481 
482 static device_t
obio_add_child(device_t bus,u_int order,const char * name,int unit)483 obio_add_child(device_t bus, u_int order, const char *name, int unit)
484 {
485 	device_t		child;
486 	struct obio_ivar	*ivar;
487 
488 	ivar = malloc(sizeof(struct obio_ivar), M_DEVBUF, M_WAITOK | M_ZERO);
489 	resource_list_init(&ivar->resources);
490 
491 	child = device_add_child_ordered(bus, order, name, unit);
492 	if (child == NULL) {
493 		printf("Can't add child %s%d ordered\n", name, unit);
494 		return (0);
495 	}
496 
497 	device_set_ivars(child, ivar);
498 
499 	return (child);
500 }
501 
502 /*
503  * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource
504  * Provides pointer to resource_list for these routines
505  */
506 static struct resource_list *
obio_get_resource_list(device_t dev,device_t child)507 obio_get_resource_list(device_t dev, device_t child)
508 {
509 	struct obio_ivar *ivar;
510 
511 	ivar = device_get_ivars(child);
512 	return (&(ivar->resources));
513 }
514 
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),
529 
530 	{0, 0},
531 };
532 
533 static driver_t obio_driver = {
534 	"obio",
535 	obio_methods,
536 	sizeof(struct obio_softc),
537 };
538 static devclass_t obio_devclass;
539 
540 DRIVER_MODULE(obio, nexus, obio_driver, obio_devclass, 0, 0);
541