apb.c revision 249119
150276Speter/*-
2166124Srafan * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
350276Speter * All rights reserved.
450276Speter *
550276Speter * Redistribution and use in source and binary forms, with or without
650276Speter * modification, are permitted provided that the following conditions
750276Speter * are met:
850276Speter * 1. Redistributions of source code must retain the above copyright
950276Speter *    notice unmodified, this list of conditions, and the following
1050276Speter *    disclaimer.
1150276Speter * 2. Redistributions in binary form must reproduce the above copyright
1250276Speter *    notice, this list of conditions and the following disclaimer in the
1350276Speter *    documentation and/or other materials provided with the distribution.
1450276Speter *
1550276Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1650276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1750276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1850276Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1950276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2050276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2150276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2250276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2350276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2450276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2550276Speter * SUCH DAMAGE.
2650276Speter */
2750276Speter
2850276Speter#include <sys/cdefs.h>
2950276Speter__FBSDID("$FreeBSD: head/sys/mips/atheros/apb.c 249119 2013-04-05 00:22:53Z adrian $");
30166124Srafan
3150276Speter#include <sys/param.h>
3250276Speter#include <sys/systm.h>
3350276Speter#include <sys/bus.h>
3450276Speter#include <sys/interrupt.h>
3550276Speter#include <sys/kernel.h>
3650276Speter#include <sys/module.h>
3750276Speter#include <sys/rman.h>
3850276Speter#include <sys/malloc.h>
3950276Speter#include <sys/pcpu.h>
40166124Srafan#include <sys/proc.h>
4150276Speter#include <sys/pmc.h>
42166124Srafan#include <sys/pmckern.h>
43166124Srafan
44166124Srafan#include <machine/bus.h>
45166124Srafan#include <machine/intr_machdep.h>
46166124Srafan
47166124Srafan#include <mips/atheros/apbvar.h>
4850276Speter#include <mips/atheros/ar71xxreg.h>
49166124Srafan#include <mips/atheros/ar71xx_setup.h>
50166124Srafan
51166124Srafan#define	APB_INTR_PMC	5
52166124Srafan
53166124Srafan#undef APB_DEBUG
54166124Srafan#ifdef APB_DEBUG
55166124Srafan#define dprintf printf
56166124Srafan#else
57166124Srafan#define dprintf(x, arg...)
58166124Srafan#endif  /* APB_DEBUG */
59166124Srafan
60166124Srafanstatic int	apb_activate_resource(device_t, device_t, int, int,
6150276Speter		    struct resource *);
62166124Srafanstatic device_t	apb_add_child(device_t, u_int, const char *, int);
63166124Srafanstatic struct resource *
64166124Srafan		apb_alloc_resource(device_t, device_t, int, int *, u_long,
65166124Srafan		    u_long, u_long, u_int);
66166124Srafanstatic int	apb_attach(device_t);
67166124Srafanstatic int	apb_deactivate_resource(device_t, device_t, int, int,
68166124Srafan		    struct resource *);
69166124Srafanstatic struct resource_list *
70166124Srafan		apb_get_resource_list(device_t, device_t);
71166124Srafanstatic void	apb_hinted_child(device_t, const char *, int);
72166124Srafanstatic int	apb_filter(void *);
73166124Srafanstatic int	apb_probe(device_t);
74166124Srafanstatic int	apb_release_resource(device_t, device_t, int, int,
75166124Srafan		    struct resource *);
7650276Speterstatic int	apb_setup_intr(device_t, device_t, struct resource *, int,
77166124Srafan		    driver_filter_t *, driver_intr_t *, void *, void **);
78166124Srafanstatic int	apb_teardown_intr(device_t, device_t, struct resource *,
79166124Srafan		    void *);
80166124Srafan
81166124Srafanstatic void
82166124Srafanapb_mask_irq(void *source)
83166124Srafan{
84166124Srafan	unsigned int irq = (unsigned int)source;
85166124Srafan	uint32_t reg;
86166124Srafan
87166124Srafan	reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK);
88166124Srafan	ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg & ~(1 << irq));
89166124Srafan
90166124Srafan}
91166124Srafan
92166124Srafanstatic void
93166124Srafanapb_unmask_irq(void *source)
94166124Srafan{
95166124Srafan	uint32_t reg;
96166124Srafan	unsigned int irq = (unsigned int)source;
97166124Srafan
98166124Srafan	reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK);
99166124Srafan	ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg | (1 << irq));
100166124Srafan}
10150276Speter
102166124Srafanstatic int
103166124Srafanapb_probe(device_t dev)
104166124Srafan{
10550276Speter
106166124Srafan	return (0);
10750276Speter}
10850276Speter
10950276Speterstatic int
11050276Speterapb_attach(device_t dev)
11150276Speter{
11250276Speter	struct apb_softc *sc = device_get_softc(dev);
11350276Speter	int rid = 0;
11450276Speter
11550276Speter	device_set_desc(dev, "APB Bus bridge");
11650276Speter
11750276Speter	sc->apb_mem_rman.rm_type = RMAN_ARRAY;
11876726Speter	sc->apb_mem_rman.rm_descr = "APB memory window";
119166124Srafan
12050276Speter	if (rman_init(&sc->apb_mem_rman) != 0 ||
121166124Srafan	    rman_manage_region(&sc->apb_mem_rman,
122166124Srafan			AR71XX_APB_BASE,
123166124Srafan			AR71XX_APB_BASE + AR71XX_APB_SIZE - 1) != 0)
12450276Speter		panic("apb_attach: failed to set up memory rman");
125166124Srafan
126166124Srafan	sc->apb_irq_rman.rm_type = RMAN_ARRAY;
12750276Speter	sc->apb_irq_rman.rm_descr = "APB IRQ";
12850276Speter
129166124Srafan	if (rman_init(&sc->apb_irq_rman) != 0 ||
13050276Speter	    rman_manage_region(&sc->apb_irq_rman,
13150276Speter			APB_IRQ_BASE, APB_IRQ_END) != 0)
13250276Speter		panic("apb_attach: failed to set up IRQ rman");
13350276Speter
13450276Speter	if ((sc->sc_misc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
13550276Speter	    RF_SHAREABLE | RF_ACTIVE)) == NULL) {
13650276Speter		device_printf(dev, "unable to allocate IRQ resource\n");
13750276Speter		return (ENXIO);
13850276Speter	}
13950276Speter
14050276Speter	if ((bus_setup_intr(dev, sc->sc_misc_irq, INTR_TYPE_MISC,
14176726Speter	    apb_filter, NULL, sc, &sc->sc_misc_ih))) {
142166124Srafan		device_printf(dev,
143166124Srafan		    "WARNING: unable to register interrupt handler\n");
14450276Speter		return (ENXIO);
14550276Speter	}
146166124Srafan
14750276Speter	bus_generic_probe(dev);
14850276Speter	bus_enumerate_hinted_children(dev);
149166124Srafan	bus_generic_attach(dev);
150166124Srafan
151166124Srafan	/*
15250276Speter	 * Unmask performance counter IRQ
15350276Speter	 */
154166124Srafan	apb_unmask_irq((void*)APB_INTR_PMC);
155166124Srafan	sc->sc_intr_counter[APB_INTR_PMC] = mips_intrcnt_create("apb irq5: pmc");
15650276Speter
15750276Speter	return (0);
15850276Speter}
15950276Speter
160166124Srafanstatic struct resource *
161166124Srafanapb_alloc_resource(device_t bus, device_t child, int type, int *rid,
16250276Speter    u_long start, u_long end, u_long count, u_int flags)
163166124Srafan{
164166124Srafan	struct apb_softc		*sc = device_get_softc(bus);
165166124Srafan	struct apb_ivar			*ivar = device_get_ivars(child);
16650276Speter	struct resource			*rv;
16750276Speter	struct resource_list_entry	*rle;
16850276Speter	struct rman			*rm;
16950276Speter	int				 isdefault, needactivate, passthrough;
17050276Speter
171	isdefault = (start == 0UL && end == ~0UL);
172	needactivate = flags & RF_ACTIVE;
173	/*
174	 * Pass memory requests to nexus device
175	 */
176	passthrough = (device_get_parent(child) != bus);
177	rle = NULL;
178
179	dprintf("%s: entry (%p, %p, %d, %d, %p, %p, %ld, %d)\n",
180	    __func__, bus, child, type, *rid, (void *)(intptr_t)start,
181	    (void *)(intptr_t)end, count, flags);
182
183	if (passthrough)
184		return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type,
185		    rid, start, end, count, flags));
186
187	/*
188	 * If this is an allocation of the "default" range for a given RID,
189	 * and we know what the resources for this device are (ie. they aren't
190	 * maintained by a child bus), then work out the start/end values.
191	 */
192
193	if (isdefault) {
194		rle = resource_list_find(&ivar->resources, type, *rid);
195		if (rle == NULL) {
196			return (NULL);
197		}
198
199		if (rle->res != NULL) {
200			panic("%s: resource entry is busy", __func__);
201		}
202		start = rle->start;
203		end = rle->end;
204		count = rle->count;
205
206		dprintf("%s: default resource (%p, %p, %ld)\n",
207		    __func__, (void *)(intptr_t)start,
208		    (void *)(intptr_t)end, count);
209	}
210
211	switch (type) {
212	case SYS_RES_IRQ:
213		rm = &sc->apb_irq_rman;
214		break;
215	case SYS_RES_MEMORY:
216		rm = &sc->apb_mem_rman;
217		break;
218	default:
219		printf("%s: unknown resource type %d\n", __func__, type);
220		return (0);
221	}
222
223	rv = rman_reserve_resource(rm, start, end, count, flags, child);
224	if (rv == 0) {
225		printf("%s: could not reserve resource\n", __func__);
226		return (0);
227	}
228
229	rman_set_rid(rv, *rid);
230
231	if (needactivate) {
232		if (bus_activate_resource(child, type, *rid, rv)) {
233			printf("%s: could not activate resource\n", __func__);
234			rman_release_resource(rv);
235			return (0);
236		}
237	}
238
239	return (rv);
240}
241
242static int
243apb_activate_resource(device_t bus, device_t child, int type, int rid,
244    struct resource *r)
245{
246
247	/* XXX: should we mask/unmask IRQ here? */
248	return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child,
249		type, rid, r));
250}
251
252static int
253apb_deactivate_resource(device_t bus, device_t child, int type, int rid,
254    struct resource *r)
255{
256
257	/* XXX: should we mask/unmask IRQ here? */
258	return (BUS_DEACTIVATE_RESOURCE(device_get_parent(bus), child,
259		type, rid, r));
260}
261
262static int
263apb_release_resource(device_t dev, device_t child, int type,
264    int rid, struct resource *r)
265{
266	struct resource_list *rl;
267	struct resource_list_entry *rle;
268
269	rl = apb_get_resource_list(dev, child);
270	if (rl == NULL)
271		return (EINVAL);
272	rle = resource_list_find(rl, type, rid);
273	if (rle == NULL)
274		return (EINVAL);
275	rman_release_resource(r);
276	rle->res = NULL;
277
278	return (0);
279}
280
281static int
282apb_setup_intr(device_t bus, device_t child, struct resource *ires,
283		int flags, driver_filter_t *filt, driver_intr_t *handler,
284		void *arg, void **cookiep)
285{
286	struct apb_softc *sc = device_get_softc(bus);
287	struct intr_event *event;
288	int irq, error;
289
290	irq = rman_get_start(ires);
291
292	if (irq > APB_IRQ_END)
293		panic("%s: bad irq %d", __func__, irq);
294
295	event = sc->sc_eventstab[irq];
296	if (event == NULL) {
297		error = intr_event_create(&event, (void *)irq, 0, irq,
298		    apb_mask_irq, apb_unmask_irq,
299		    NULL, NULL,
300		    "apb intr%d:", irq);
301
302		if (error == 0) {
303			sc->sc_eventstab[irq] = event;
304			sc->sc_intr_counter[irq] =
305			    mips_intrcnt_create(event->ie_name);
306		}
307		else
308			return (error);
309	}
310
311	intr_event_add_handler(event, device_get_nameunit(child), filt,
312	    handler, arg, intr_priority(flags), flags, cookiep);
313	mips_intrcnt_setname(sc->sc_intr_counter[irq], event->ie_fullname);
314
315	apb_unmask_irq((void*)irq);
316
317	return (0);
318}
319
320static int
321apb_teardown_intr(device_t dev, device_t child, struct resource *ires,
322    void *cookie)
323{
324	struct apb_softc *sc = device_get_softc(dev);
325	int irq, result;
326
327	irq = rman_get_start(ires);
328	if (irq > APB_IRQ_END)
329		panic("%s: bad irq %d", __func__, irq);
330
331	if (sc->sc_eventstab[irq] == NULL)
332		panic("Trying to teardown unoccupied IRQ");
333
334	apb_mask_irq((void*)irq);
335
336	result = intr_event_remove_handler(cookie);
337	if (!result)
338		sc->sc_eventstab[irq] = NULL;
339
340	return (result);
341}
342
343static int
344apb_filter(void *arg)
345{
346	struct apb_softc *sc = arg;
347	struct intr_event *event;
348	uint32_t reg, irq;
349	struct thread *td;
350	struct trapframe *tf;
351
352	reg = ATH_READ_REG(AR71XX_MISC_INTR_STATUS);
353	for (irq = 0; irq < APB_NIRQS; irq++) {
354		if (reg & (1 << irq)) {
355
356			switch (ar71xx_soc) {
357			case AR71XX_SOC_AR7240:
358			case AR71XX_SOC_AR7241:
359			case AR71XX_SOC_AR7242:
360			case AR71XX_SOC_AR9330:
361			case AR71XX_SOC_AR9331:
362				/* Ack/clear the irq on status register for AR724x */
363				ATH_WRITE_REG(AR71XX_MISC_INTR_STATUS,
364				    reg & ~(1 << irq));
365				break;
366			default:
367				/* fallthrough */
368				break;
369			}
370
371			event = sc->sc_eventstab[irq];
372			if (!event || TAILQ_EMPTY(&event->ie_handlers)) {
373				if (irq == APB_INTR_PMC) {
374					td = PCPU_GET(curthread);
375					tf = td->td_intr_frame;
376
377					if (pmc_intr)
378						(*pmc_intr)(PCPU_GET(cpuid), tf);
379
380					mips_intrcnt_inc(sc->sc_intr_counter[irq]);
381
382					continue;
383				}
384				/* Ignore timer interrupts */
385				if (irq != 0)
386					printf("Stray APB IRQ %d\n", irq);
387				continue;
388			}
389
390			intr_event_handle(event, PCPU_GET(curthread)->td_intr_frame);
391			mips_intrcnt_inc(sc->sc_intr_counter[irq]);
392		}
393	}
394
395	return (FILTER_HANDLED);
396}
397
398static void
399apb_hinted_child(device_t bus, const char *dname, int dunit)
400{
401	device_t		child;
402	long			maddr;
403	int			msize;
404	int			irq;
405	int			result;
406	int			mem_hints_count;
407
408	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
409
410	/*
411	 * Set hard-wired resources for hinted child using
412	 * specific RIDs.
413	 */
414	mem_hints_count = 0;
415	if (resource_long_value(dname, dunit, "maddr", &maddr) == 0)
416		mem_hints_count++;
417	if (resource_int_value(dname, dunit, "msize", &msize) == 0)
418		mem_hints_count++;
419
420	/* check if all info for mem resource has been provided */
421	if ((mem_hints_count > 0) && (mem_hints_count < 2)) {
422		printf("Either maddr or msize hint is missing for %s%d\n",
423		    dname, dunit);
424	} else if (mem_hints_count) {
425		result = bus_set_resource(child, SYS_RES_MEMORY, 0,
426		    maddr, msize);
427		if (result != 0)
428			device_printf(bus,
429			    "warning: bus_set_resource() failed\n");
430	}
431
432	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
433		result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
434		if (result != 0)
435			device_printf(bus,
436			    "warning: bus_set_resource() failed\n");
437	}
438}
439
440static device_t
441apb_add_child(device_t bus, u_int order, const char *name, int unit)
442{
443	device_t		child;
444	struct apb_ivar	*ivar;
445
446	ivar = malloc(sizeof(struct apb_ivar), M_DEVBUF, M_WAITOK | M_ZERO);
447	if (ivar == NULL) {
448		printf("Failed to allocate ivar\n");
449		return (0);
450	}
451	resource_list_init(&ivar->resources);
452
453	child = device_add_child_ordered(bus, order, name, unit);
454	if (child == NULL) {
455		printf("Can't add child %s%d ordered\n", name, unit);
456		return (0);
457	}
458
459	device_set_ivars(child, ivar);
460
461	return (child);
462}
463
464/*
465 * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource
466 * Provides pointer to resource_list for these routines
467 */
468static struct resource_list *
469apb_get_resource_list(device_t dev, device_t child)
470{
471	struct apb_ivar *ivar;
472
473	ivar = device_get_ivars(child);
474	return (&(ivar->resources));
475}
476
477static device_method_t apb_methods[] = {
478	DEVMETHOD(bus_activate_resource,	apb_activate_resource),
479	DEVMETHOD(bus_add_child,		apb_add_child),
480	DEVMETHOD(bus_alloc_resource,		apb_alloc_resource),
481	DEVMETHOD(bus_deactivate_resource,	apb_deactivate_resource),
482	DEVMETHOD(bus_get_resource_list,	apb_get_resource_list),
483	DEVMETHOD(bus_hinted_child,		apb_hinted_child),
484	DEVMETHOD(bus_release_resource,		apb_release_resource),
485	DEVMETHOD(bus_setup_intr,		apb_setup_intr),
486	DEVMETHOD(bus_teardown_intr,		apb_teardown_intr),
487	DEVMETHOD(device_attach,		apb_attach),
488	DEVMETHOD(device_probe,			apb_probe),
489	DEVMETHOD(bus_get_resource,		bus_generic_rl_get_resource),
490	DEVMETHOD(bus_set_resource,		bus_generic_rl_set_resource),
491
492	DEVMETHOD_END
493};
494
495static driver_t apb_driver = {
496	"apb",
497	apb_methods,
498	sizeof(struct apb_softc),
499};
500static devclass_t apb_devclass;
501
502DRIVER_MODULE(apb, nexus, apb_driver, apb_devclass, 0, 0);
503