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