obio.c revision 330897
1/*	$NetBSD: obio.c,v 1.11 2003/07/15 00:25:05 lukem Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-4-Clause
5 *
6 * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc.
7 * All rights reserved.
8 *
9 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed for the NetBSD Project by
22 *	Wasabi Systems, Inc.
23 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
24 *    or promote products derived from this software without specific prior
25 *    written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: stable/11/sys/mips/adm5120/obio.c 330897 2018-03-14 03:19:51Z eadler $");
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/bus.h>
46#include <sys/interrupt.h>
47#include <sys/kernel.h>
48#include <sys/module.h>
49#include <sys/rman.h>
50#include <sys/malloc.h>
51
52#include <machine/bus.h>
53
54#include <mips/adm5120/adm5120reg.h>
55#include <mips/adm5120/obiovar.h>
56
57/* MIPS HW interrupts of IRQ/FIQ respectively */
58#define ADM5120_INTR		0
59#define ADM5120_FAST_INTR	1
60
61/* Interrupt levels */
62#define INTR_IRQ 0
63#define INTR_FIQ 1
64
65int irq_priorities[NIRQS] = {
66	INTR_IRQ,	/* flash */
67	INTR_FIQ,	/* uart0 */
68	INTR_FIQ,	/* uart1 */
69	INTR_IRQ,	/* ahci  */
70	INTR_IRQ,	/* unknown */
71	INTR_IRQ,	/* unknown */
72	INTR_IRQ,	/* unknown */
73	INTR_IRQ,	/* unknown */
74	INTR_IRQ,	/* unknown */
75	INTR_IRQ,	/* admsw */
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	INTR_IRQ,	/* unknown */
97	INTR_IRQ,	/* unknown */
98};
99
100
101#define REG_READ(o) *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1(ADM5120_BASE_ICU + (o)))
102#define REG_WRITE(o,v) (REG_READ(o)) = (v)
103
104static int	obio_activate_resource(device_t, device_t, int, int,
105		    struct resource *);
106static device_t	obio_add_child(device_t, u_int, const char *, int);
107static struct resource *
108		obio_alloc_resource(device_t, device_t, int, int *, rman_res_t,
109		    rman_res_t, rman_res_t, u_int);
110static int	obio_attach(device_t);
111static int	obio_deactivate_resource(device_t, device_t, int, int,
112		    struct resource *);
113static struct resource_list *
114		obio_get_resource_list(device_t, device_t);
115static void	obio_hinted_child(device_t, const char *, int);
116static int	obio_intr(void *);
117static int	obio_probe(device_t);
118static int	obio_release_resource(device_t, device_t, int, int,
119		    struct resource *);
120static int	obio_setup_intr(device_t, device_t, struct resource *, int,
121		    driver_filter_t *, driver_intr_t *, void *, void **);
122static int	obio_teardown_intr(device_t, device_t, struct resource *,
123		    void *);
124
125
126static void
127obio_mask_irq(void *source)
128{
129	int irq;
130	uint32_t irqmask;
131	uint32_t reg;
132
133	irq = (int)source;
134	irqmask = 1 << irq;
135
136	/* disable IRQ */
137	reg = REG_READ(ICU_DISABLE_REG);
138	REG_WRITE(ICU_DISABLE_REG, (reg | irqmask));
139}
140
141static void
142obio_unmask_irq(void *source)
143{
144	int irq;
145	uint32_t irqmask;
146	uint32_t reg;
147
148	irq = (int)source;
149	irqmask = 1 << irq;
150
151	/* disable IRQ */
152	reg = REG_READ(ICU_DISABLE_REG);
153	REG_WRITE(ICU_DISABLE_REG, (reg & ~irqmask));
154
155}
156
157
158static int
159obio_probe(device_t dev)
160{
161
162	return (BUS_PROBE_NOWILDCARD);
163}
164
165static int
166obio_attach(device_t dev)
167{
168	struct obio_softc *sc = device_get_softc(dev);
169	int rid;
170
171	sc->oba_mem_rman.rm_type = RMAN_ARRAY;
172	sc->oba_mem_rman.rm_descr = "OBIO memeory";
173	if (rman_init(&sc->oba_mem_rman) != 0 ||
174	    rman_manage_region(&sc->oba_mem_rman, OBIO_MEM_START,
175	        OBIO_MEM_START + OBIO_MEM_SIZE) != 0)
176		panic("obio_attach: failed to set up I/O rman");
177
178	sc->oba_irq_rman.rm_type = RMAN_ARRAY;
179	sc->oba_irq_rman.rm_descr = "OBIO IRQ";
180
181	if (rman_init(&sc->oba_irq_rman) != 0 ||
182	    rman_manage_region(&sc->oba_irq_rman, 0, NIRQS-1) != 0)
183		panic("obio_attach: failed to set up IRQ rman");
184
185	/* Hook up our interrupt handler. */
186	if ((sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
187	    ADM5120_INTR, ADM5120_INTR, 1,
188	    RF_SHAREABLE | RF_ACTIVE)) == NULL) {
189		device_printf(dev, "unable to allocate IRQ resource\n");
190		return (ENXIO);
191	}
192
193	if ((bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC, obio_intr, NULL,
194	    sc, &sc->sc_ih))) {
195		device_printf(dev,
196		    "WARNING: unable to register interrupt handler\n");
197		return (ENXIO);
198	}
199
200	/* Hook up our FAST interrupt handler. */
201	if ((sc->sc_fast_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
202	    ADM5120_FAST_INTR, ADM5120_FAST_INTR, 1,
203	    RF_SHAREABLE | RF_ACTIVE)) == NULL) {
204		device_printf(dev, "unable to allocate IRQ resource\n");
205		return (ENXIO);
206	}
207
208	if ((bus_setup_intr(dev, sc->sc_fast_irq, INTR_TYPE_MISC, obio_intr,
209	    NULL, sc, &sc->sc_fast_ih))) {
210		device_printf(dev,
211		    "WARNING: unable to register interrupt handler\n");
212		return (ENXIO);
213	}
214
215	/* disable all interrupts */
216	REG_WRITE(ICU_ENABLE_REG, ICU_INT_MASK);
217
218	bus_generic_probe(dev);
219	bus_enumerate_hinted_children(dev);
220	bus_generic_attach(dev);
221
222	return (0);
223}
224
225static struct resource *
226obio_alloc_resource(device_t bus, device_t child, int type, int *rid,
227    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
228{
229	struct obio_softc		*sc = device_get_softc(bus);
230	struct obio_ivar		*ivar = device_get_ivars(child);
231	struct resource			*rv;
232	struct resource_list_entry	*rle;
233	struct rman			*rm;
234	int				 isdefault, needactivate, passthrough;
235
236	isdefault = (RMAN_IS_DEFAULT_RANGE(start, end) && count == 1);
237	needactivate = flags & RF_ACTIVE;
238	passthrough = (device_get_parent(child) != bus);
239	rle = NULL;
240
241	if (passthrough)
242		return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type,
243		    rid, start, end, count, flags));
244
245	/*
246	 * If this is an allocation of the "default" range for a given RID,
247	 * and we know what the resources for this device are (ie. they aren't
248	 * maintained by a child bus), then work out the start/end values.
249	 */
250	if (isdefault) {
251		rle = resource_list_find(&ivar->resources, type, *rid);
252		if (rle == NULL)
253			return (NULL);
254		if (rle->res != NULL) {
255			panic("%s: resource entry is busy", __func__);
256		}
257		start = rle->start;
258		end = rle->end;
259		count = rle->count;
260	}
261
262	switch (type) {
263	case SYS_RES_IRQ:
264		rm = &sc->oba_irq_rman;
265		break;
266	case SYS_RES_MEMORY:
267		rm = &sc->oba_mem_rman;
268		break;
269	default:
270		printf("%s: unknown resource type %d\n", __func__, type);
271		return (0);
272	}
273
274	rv = rman_reserve_resource(rm, start, end, count, flags, child);
275	if (rv == NULL) {
276		printf("%s: could not reserve resource\n", __func__);
277		return (0);
278	}
279
280	rman_set_rid(rv, *rid);
281
282	if (needactivate) {
283		if (bus_activate_resource(child, type, *rid, rv)) {
284			printf("%s: could not activate resource\n", __func__);
285			rman_release_resource(rv);
286			return (0);
287		}
288	}
289
290	return (rv);
291}
292
293static int
294obio_activate_resource(device_t bus, device_t child, int type, int rid,
295    struct resource *r)
296{
297
298	/*
299	 * If this is a memory resource, track the direct mapping
300	 * in the uncached MIPS KSEG1 segment.
301	 */
302	if (type == SYS_RES_MEMORY) {
303		void *vaddr;
304
305		vaddr = (void *)MIPS_PHYS_TO_KSEG1((intptr_t)rman_get_start(r));
306		rman_set_virtual(r, vaddr);
307		rman_set_bustag(r, mips_bus_space_generic);
308		rman_set_bushandle(r, (bus_space_handle_t)vaddr);
309	}
310
311	return (rman_activate_resource(r));
312}
313
314static int
315obio_deactivate_resource(device_t bus, device_t child, int type, int rid,
316    struct resource *r)
317{
318
319	return (rman_deactivate_resource(r));
320}
321
322static int
323obio_release_resource(device_t dev, device_t child, int type,
324    int rid, struct resource *r)
325{
326	struct resource_list *rl;
327	struct resource_list_entry *rle;
328
329	rl = obio_get_resource_list(dev, child);
330	if (rl == NULL)
331		return (EINVAL);
332	rle = resource_list_find(rl, type, rid);
333	if (rle == NULL)
334		return (EINVAL);
335	rman_release_resource(r);
336	rle->res = NULL;
337
338	return (0);
339}
340
341static int
342obio_setup_intr(device_t dev, device_t child, struct resource *ires,
343		int flags, driver_filter_t *filt, driver_intr_t *handler,
344		void *arg, void **cookiep)
345{
346	struct obio_softc *sc = device_get_softc(dev);
347	struct intr_event *event;
348	int irq, error, priority;
349	uint32_t irqmask;
350
351	irq = rman_get_start(ires);
352
353	if (irq >= NIRQS)
354		panic("%s: bad irq %d", __func__, irq);
355
356	event = sc->sc_eventstab[irq];
357	if (event == NULL) {
358		error = intr_event_create(&event, (void *)irq, 0, irq,
359		    obio_mask_irq, obio_unmask_irq,
360		    NULL, NULL, "obio intr%d:", irq);
361
362		sc->sc_eventstab[irq] = event;
363	}
364	else
365		panic("obio: Can't share IRQs");
366
367	intr_event_add_handler(event, device_get_nameunit(child), filt,
368	    handler, arg, intr_priority(flags), flags, cookiep);
369
370	irqmask = 1 << irq;
371	priority = irq_priorities[irq];
372
373	if (priority == INTR_FIQ)
374		REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) | irqmask);
375	else
376		REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) & ~irqmask);
377
378	/* enable */
379	REG_WRITE(ICU_ENABLE_REG, irqmask);
380
381	obio_unmask_irq((void*)irq);
382
383	return (0);
384}
385
386static int
387obio_teardown_intr(device_t dev, device_t child, struct resource *ires,
388    void *cookie)
389{
390	struct obio_softc *sc = device_get_softc(dev);
391	int irq, result, priority;
392	uint32_t irqmask;
393
394	irq = rman_get_start(ires);
395	if (irq >= NIRQS)
396		panic("%s: bad irq %d", __func__, irq);
397
398	if (sc->sc_eventstab[irq] == NULL)
399		panic("Trying to teardown unoccupied IRQ");
400
401	irqmask = (1 << irq);
402	priority = irq_priorities[irq];
403
404	if (priority == INTR_FIQ)
405		REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) & ~irqmask);
406	else
407		REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) | irqmask);
408
409	/* disable */
410	irqmask = REG_READ(ICU_ENABLE_REG);
411	irqmask &= ~(1 << irq);
412	REG_WRITE(ICU_ENABLE_REG, irqmask);
413
414	result = intr_event_remove_handler(cookie);
415	if (!result) {
416		sc->sc_eventstab[irq] = NULL;
417	}
418
419	return (result);
420}
421
422static int
423obio_intr(void *arg)
424{
425	struct obio_softc *sc = arg;
426	struct intr_event *event;
427	uint32_t irqstat;
428	int irq;
429
430	irqstat = REG_READ(ICU_FIQ_STATUS_REG);
431	irqstat |= REG_READ(ICU_STATUS_REG);
432
433	irq = 0;
434	while (irqstat != 0) {
435		if ((irqstat & 1) == 1) {
436			event = sc->sc_eventstab[irq];
437			if (!event || TAILQ_EMPTY(&event->ie_handlers))
438				continue;
439
440			/* TODO: pass frame as an argument*/
441			/* TODO: log stray interrupt */
442			intr_event_handle(event, NULL);
443		}
444
445		irq++;
446		irqstat >>= 1;
447	}
448
449	return (FILTER_HANDLED);
450}
451
452static void
453obio_hinted_child(device_t bus, const char *dname, int dunit)
454{
455	device_t		child;
456	long			maddr;
457	int			msize;
458	int			irq;
459	int			result;
460
461	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
462
463	/*
464	 * Set hard-wired resources for hinted child using
465	 * specific RIDs.
466	 */
467	resource_long_value(dname, dunit, "maddr", &maddr);
468	resource_int_value(dname, dunit, "msize", &msize);
469
470
471	result = bus_set_resource(child, SYS_RES_MEMORY, 0,
472	    maddr, msize);
473	if (result != 0)
474		device_printf(bus, "warning: bus_set_resource() failed\n");
475
476	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
477		result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
478		if (result != 0)
479			device_printf(bus,
480			    "warning: bus_set_resource() failed\n");
481	}
482}
483
484static device_t
485obio_add_child(device_t bus, u_int order, const char *name, int unit)
486{
487	device_t		child;
488	struct obio_ivar	*ivar;
489
490	ivar = malloc(sizeof(struct obio_ivar), M_DEVBUF, M_WAITOK | M_ZERO);
491	resource_list_init(&ivar->resources);
492
493	child = device_add_child_ordered(bus, order, name, unit);
494	if (child == NULL) {
495		printf("Can't add child %s%d ordered\n", name, unit);
496		return (0);
497	}
498
499	device_set_ivars(child, ivar);
500
501	return (child);
502}
503
504/*
505 * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource
506 * Provides pointer to resource_list for these routines
507 */
508static struct resource_list *
509obio_get_resource_list(device_t dev, device_t child)
510{
511	struct obio_ivar *ivar;
512
513	ivar = device_get_ivars(child);
514	return (&(ivar->resources));
515}
516
517static device_method_t obio_methods[] = {
518	DEVMETHOD(bus_activate_resource,	obio_activate_resource),
519	DEVMETHOD(bus_add_child,		obio_add_child),
520	DEVMETHOD(bus_alloc_resource,		obio_alloc_resource),
521	DEVMETHOD(bus_deactivate_resource,	obio_deactivate_resource),
522	DEVMETHOD(bus_get_resource_list,	obio_get_resource_list),
523	DEVMETHOD(bus_hinted_child,		obio_hinted_child),
524	DEVMETHOD(bus_release_resource,		obio_release_resource),
525	DEVMETHOD(bus_setup_intr,		obio_setup_intr),
526	DEVMETHOD(bus_teardown_intr,		obio_teardown_intr),
527	DEVMETHOD(device_attach,		obio_attach),
528	DEVMETHOD(device_probe,			obio_probe),
529        DEVMETHOD(bus_get_resource,		bus_generic_rl_get_resource),
530        DEVMETHOD(bus_set_resource,		bus_generic_rl_set_resource),
531
532	{0, 0},
533};
534
535static driver_t obio_driver = {
536	"obio",
537	obio_methods,
538	sizeof(struct obio_softc),
539};
540static devclass_t obio_devclass;
541
542DRIVER_MODULE(obio, nexus, obio_driver, obio_devclass, 0, 0);
543