xref: /netbsd-src/sys/arch/mips/atheros/ar_intr.c (revision 9e26d3f35bec42a735e4f9727e8d4fd54e4716f5)
1*9e26d3f3Sthorpej /* $NetBSD: ar_intr.c,v 1.7 2021/01/04 17:42:29 thorpej Exp $ */
281d18a2fSmatt /*
381d18a2fSmatt  * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
481d18a2fSmatt  * Copyright (c) 2006 Garrett D'Amore.
581d18a2fSmatt  * All rights reserved.
681d18a2fSmatt  *
781d18a2fSmatt  * This code was written by Garrett D'Amore for the Champaign-Urbana
881d18a2fSmatt  * Community Wireless Network Project.
981d18a2fSmatt  *
1081d18a2fSmatt  * Redistribution and use in source and binary forms, with or
1181d18a2fSmatt  * without modification, are permitted provided that the following
1281d18a2fSmatt  * conditions are met:
1381d18a2fSmatt  * 1. Redistributions of source code must retain the above copyright
1481d18a2fSmatt  *    notice, this list of conditions and the following disclaimer.
1581d18a2fSmatt  * 2. Redistributions in binary form must reproduce the above
1681d18a2fSmatt  *    copyright notice, this list of conditions and the following
1781d18a2fSmatt  *    disclaimer in the documentation and/or other materials provided
1881d18a2fSmatt  *    with the distribution.
1981d18a2fSmatt  * 3. All advertising materials mentioning features or use of this
2081d18a2fSmatt  *    software must display the following acknowledgements:
2181d18a2fSmatt  *      This product includes software developed by the Urbana-Champaign
2281d18a2fSmatt  *      Independent Media Center.
2381d18a2fSmatt  *	This product includes software developed by Garrett D'Amore.
2481d18a2fSmatt  * 4. Urbana-Champaign Independent Media Center's name and Garrett
2581d18a2fSmatt  *    D'Amore's name may not be used to endorse or promote products
2681d18a2fSmatt  *    derived from this software without specific prior written permission.
2781d18a2fSmatt  *
2881d18a2fSmatt  * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
2981d18a2fSmatt  * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR
3081d18a2fSmatt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3181d18a2fSmatt  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3281d18a2fSmatt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
3381d18a2fSmatt  * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT,
3481d18a2fSmatt  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3581d18a2fSmatt  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3681d18a2fSmatt  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
3781d18a2fSmatt  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3881d18a2fSmatt  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3981d18a2fSmatt  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
4081d18a2fSmatt  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4181d18a2fSmatt  */
4281d18a2fSmatt 
4381d18a2fSmatt #include <sys/cdefs.h>
44*9e26d3f3Sthorpej __KERNEL_RCSID(0, "$NetBSD: ar_intr.c,v 1.7 2021/01/04 17:42:29 thorpej Exp $");
4581d18a2fSmatt 
4681d18a2fSmatt #define __INTR_PRIVATE
4781d18a2fSmatt 
4881d18a2fSmatt #include <sys/param.h>
49fa40faf6Smatt #include <sys/intr.h>
5097627a75Smatt #include <sys/cpu.h>
5181d18a2fSmatt #include <sys/kernel.h>
52*9e26d3f3Sthorpej #include <sys/kmem.h>
5381d18a2fSmatt 
5497627a75Smatt #include <mips/cpuregs.h>
5581d18a2fSmatt #include <mips/locore.h>
5681d18a2fSmatt #include <mips/atheros/include/platform.h>
5781d18a2fSmatt 
5881d18a2fSmatt #define	REGVAL(x)	*((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x))))
5981d18a2fSmatt 
6081d18a2fSmatt /*
6181d18a2fSmatt  * Only MISC interrupts are easily masked at the interrupt controller.
6281d18a2fSmatt  * The others have to be masked at the source.
6381d18a2fSmatt  */
6481d18a2fSmatt 
6581d18a2fSmatt #define	NINTRS	7	/* MIPS INT2-INT4 (7 is clock interrupt) */
6681d18a2fSmatt #define	NIRQS	32	/* bits in Miscellaneous Interrupt Status Register */
6781d18a2fSmatt 
6881d18a2fSmatt struct atheros_intrhand {
6981d18a2fSmatt 	LIST_ENTRY(atheros_intrhand) ih_q;
7081d18a2fSmatt 	int (*ih_func)(void *);
7181d18a2fSmatt 	void *ih_arg;
7281d18a2fSmatt 	int ih_irq;
7381d18a2fSmatt };
7481d18a2fSmatt 
7581d18a2fSmatt struct atheros_intr {
7681d18a2fSmatt 	LIST_HEAD(, atheros_intrhand) intr_qh;
7781d18a2fSmatt 	struct evcnt	intr_count;
7881d18a2fSmatt };
7981d18a2fSmatt 
8081d18a2fSmatt static struct atheros_intr cpu_intrs[NINTRS];
8181d18a2fSmatt static struct atheros_intr misc_intrs[NIRQS];
8281d18a2fSmatt 
8381d18a2fSmatt static uint32_t
misc_intstat_get(void)8481d18a2fSmatt misc_intstat_get(void)
8581d18a2fSmatt {
8681d18a2fSmatt 	return REGVAL(platformsw->apsw_misc_intstat);
8781d18a2fSmatt }
8881d18a2fSmatt 
8981d18a2fSmatt static void
misc_intstat_put(uint32_t v)9081d18a2fSmatt misc_intstat_put(uint32_t v)
9181d18a2fSmatt {
9281d18a2fSmatt 	REGVAL(platformsw->apsw_misc_intstat) = v;
9381d18a2fSmatt }
9481d18a2fSmatt 
9581d18a2fSmatt static uint32_t
misc_intmask_get(void)9681d18a2fSmatt misc_intmask_get(void)
9781d18a2fSmatt {
9881d18a2fSmatt 	return REGVAL(platformsw->apsw_misc_intmask);
9981d18a2fSmatt }
10081d18a2fSmatt 
10181d18a2fSmatt static void
misc_intmask_put(uint32_t v)10281d18a2fSmatt misc_intmask_put(uint32_t v)
10381d18a2fSmatt {
10481d18a2fSmatt 	REGVAL(platformsw->apsw_misc_intmask) = v;
10581d18a2fSmatt }
10681d18a2fSmatt 
10781d18a2fSmatt 
10881d18a2fSmatt static void *
genath_cpu_intr_establish(int intr,int (* func)(void *),void * arg)10981d18a2fSmatt genath_cpu_intr_establish(int intr, int (*func)(void *), void *arg)
11081d18a2fSmatt {
11181d18a2fSmatt 	struct atheros_intrhand	*ih;
11281d18a2fSmatt 
113*9e26d3f3Sthorpej 	ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
11481d18a2fSmatt 	ih->ih_func = func;
11581d18a2fSmatt 	ih->ih_arg = arg;
11681d18a2fSmatt 	ih->ih_irq = intr;
11781d18a2fSmatt 
11881d18a2fSmatt 	const int s = splhigh();
11981d18a2fSmatt 
12081d18a2fSmatt 	LIST_INSERT_HEAD(&cpu_intrs[intr].intr_qh, ih, ih_q);
12181d18a2fSmatt 
12281d18a2fSmatt 	/*
12381d18a2fSmatt 	 * The MIPS CPU interrupts are enabled at boot time, so they
12481d18a2fSmatt 	 * should pretty much always be ready to go.
12581d18a2fSmatt 	 */
12681d18a2fSmatt 
12781d18a2fSmatt 	splx(s);
12881d18a2fSmatt 	return (ih);
12981d18a2fSmatt }
13081d18a2fSmatt 
13181d18a2fSmatt static void
genath_cpu_intr_disestablish(void * arg)13281d18a2fSmatt genath_cpu_intr_disestablish(void *arg)
13381d18a2fSmatt {
13481d18a2fSmatt 	struct atheros_intrhand	* const ih = arg;
13581d18a2fSmatt 
13681d18a2fSmatt 	const int s = splhigh();
13781d18a2fSmatt 
13881d18a2fSmatt 	LIST_REMOVE(ih, ih_q);
13981d18a2fSmatt 
14081d18a2fSmatt 	splx(s);
141*9e26d3f3Sthorpej 	kmem_free(ih, sizeof(*ih));
14281d18a2fSmatt }
14381d18a2fSmatt 
14481d18a2fSmatt static void *
genath_misc_intr_establish(int irq,int (* func)(void *),void * arg)14581d18a2fSmatt genath_misc_intr_establish(int irq, int (*func)(void *), void *arg)
14681d18a2fSmatt {
14781d18a2fSmatt 	struct atheros_intr * const intr = &misc_intrs[irq];
14881d18a2fSmatt 	struct atheros_intrhand	*ih;
14981d18a2fSmatt 	bool first;
15081d18a2fSmatt 	int s;
15181d18a2fSmatt 
15281d18a2fSmatt 
153*9e26d3f3Sthorpej 	ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
15481d18a2fSmatt 	ih->ih_func = func;
15581d18a2fSmatt 	ih->ih_arg = arg;
15681d18a2fSmatt 	ih->ih_irq = irq;
15781d18a2fSmatt 
15881d18a2fSmatt 	s = splhigh();
15981d18a2fSmatt 
16081d18a2fSmatt 	first = LIST_EMPTY(&intr->intr_qh);
16181d18a2fSmatt 
16281d18a2fSmatt 	LIST_INSERT_HEAD(&intr->intr_qh, ih, ih_q);
16381d18a2fSmatt 
16481d18a2fSmatt 	if (first) {
16581d18a2fSmatt 		const uint32_t mask = misc_intmask_get() | __BIT(irq);
16681d18a2fSmatt 		misc_intmask_put(mask);
16781d18a2fSmatt 		(void) misc_intmask_get();	/* flush wbuffer */
16881d18a2fSmatt 	}
16981d18a2fSmatt 
17081d18a2fSmatt 	splx(s);
17181d18a2fSmatt 
17281d18a2fSmatt 	return ih;
17381d18a2fSmatt }
17481d18a2fSmatt 
17581d18a2fSmatt static void
genath_misc_intr_disestablish(void * arg)17681d18a2fSmatt genath_misc_intr_disestablish(void *arg)
17781d18a2fSmatt {
17881d18a2fSmatt 	struct atheros_intrhand	*ih = arg;
17981d18a2fSmatt 	struct atheros_intr * const intr = &misc_intrs[ih->ih_irq];
18081d18a2fSmatt 
18181d18a2fSmatt 	const int s = splhigh();
18281d18a2fSmatt 
18381d18a2fSmatt 	LIST_REMOVE(ih, ih_q);
18481d18a2fSmatt 	if (LIST_EMPTY(&intr->intr_qh)) {
18581d18a2fSmatt 		const uint32_t mask = misc_intmask_get() & ~__BIT(ih->ih_irq);
18681d18a2fSmatt 		misc_intmask_put(mask);
18781d18a2fSmatt 		(void) misc_intmask_get();	/* flush wbuffer */
18881d18a2fSmatt 	}
18981d18a2fSmatt 
19081d18a2fSmatt 	splx(s);
191*9e26d3f3Sthorpej 	kmem_free(ih, sizeof(*ih));
19281d18a2fSmatt }
19381d18a2fSmatt 
19481d18a2fSmatt 
19581d18a2fSmatt static int
genath_misc_intr(void * arg)19681d18a2fSmatt genath_misc_intr(void *arg)
19781d18a2fSmatt {
19881d18a2fSmatt 	uint32_t		isr;
19981d18a2fSmatt 	uint32_t		mask;
20081d18a2fSmatt 	int			rv = 0;
20181d18a2fSmatt 	struct atheros_intr	*intr = arg;
20281d18a2fSmatt 
20381d18a2fSmatt 	isr = misc_intstat_get();
20481d18a2fSmatt 	mask = misc_intmask_get();
20581d18a2fSmatt 
20681d18a2fSmatt 	misc_intstat_put(isr & ~mask);
20781d18a2fSmatt 
20881d18a2fSmatt 	isr &= mask;
20981d18a2fSmatt 	while (isr != 0) {
21081d18a2fSmatt 		struct atheros_intrhand	*ih;
21181d18a2fSmatt 		int index = 31 - __builtin_clz(isr & -isr); /* ffs */
21281d18a2fSmatt 		intr += index;
21381d18a2fSmatt 
21481d18a2fSmatt 		intr->intr_count.ev_count++;
21581d18a2fSmatt 		LIST_FOREACH(ih, &intr->intr_qh, ih_q) {
21681d18a2fSmatt 			rv |= (*ih->ih_func)(ih->ih_arg);
21781d18a2fSmatt 		}
21881d18a2fSmatt 		isr >>= index + 1;
21981d18a2fSmatt 		intr++;
22081d18a2fSmatt 	}
22181d18a2fSmatt 
22281d18a2fSmatt 	return rv;
22381d18a2fSmatt }
22481d18a2fSmatt 
22581d18a2fSmatt static void
genath_iointr(int cpl,vaddr_t pc,uint32_t ipending)22681d18a2fSmatt genath_iointr(int cpl, vaddr_t pc, uint32_t ipending)
22781d18a2fSmatt {
22881d18a2fSmatt 	struct atheros_intr *intr = &cpu_intrs[NINTRS-1];
22981d18a2fSmatt 
23081d18a2fSmatt 	/* move ipending to the most significant bits */
23181d18a2fSmatt 	ipending *= __BIT(31) / (MIPS_INT_MASK_0 << (NINTRS-1));
23281d18a2fSmatt 	while (ipending != 0) {
23381d18a2fSmatt 		struct atheros_intrhand	*ih;
23481d18a2fSmatt 		int index = __builtin_clz(ipending);
23581d18a2fSmatt 
23681d18a2fSmatt 		intr -= index;
23781d18a2fSmatt 		ipending <<= index;
23881d18a2fSmatt 		KASSERT(ipending & __BIT(31));
23981d18a2fSmatt 		KASSERT(intr >= cpu_intrs);
24081d18a2fSmatt 
24181d18a2fSmatt 		intr->intr_count.ev_count++;
24281d18a2fSmatt 		LIST_FOREACH(ih, &intr->intr_qh, ih_q) {
24381d18a2fSmatt 			(*ih->ih_func)(ih->ih_arg);
24481d18a2fSmatt 		}
24581d18a2fSmatt 		ipending <<= 1;
24681d18a2fSmatt 		intr--;
24781d18a2fSmatt 	}
24881d18a2fSmatt }
24981d18a2fSmatt 
25081d18a2fSmatt static void
genath_intr_init(void)25181d18a2fSmatt genath_intr_init(void)
25281d18a2fSmatt {
25381d18a2fSmatt 	const struct atheros_platformsw * const apsw = platformsw;
25481d18a2fSmatt 
25581d18a2fSmatt 	KASSERT(apsw->apsw_ipl_sr_map != NULL);
25681d18a2fSmatt 	ipl_sr_map = *apsw->apsw_ipl_sr_map;
25781d18a2fSmatt 
25881d18a2fSmatt 	for (size_t i = 0; i < apsw->apsw_cpu_nintrs; i++) {
25981d18a2fSmatt 		if (apsw->apsw_cpu_intrnames[i] != NULL) {
26081d18a2fSmatt 			LIST_INIT(&cpu_intrs[i].intr_qh);
26181d18a2fSmatt 			evcnt_attach_dynamic(&cpu_intrs[i].intr_count,
26281d18a2fSmatt 			    EVCNT_TYPE_INTR, NULL, "cpu",
26381d18a2fSmatt 			    apsw->apsw_cpu_intrnames[i]);
26481d18a2fSmatt 		}
26581d18a2fSmatt 	}
26681d18a2fSmatt 
26781d18a2fSmatt 	for (size_t i = 0; i < apsw->apsw_misc_nintrs; i++) {
26881d18a2fSmatt 		if (apsw->apsw_misc_intrnames[i] != NULL) {
26981d18a2fSmatt 			LIST_INIT(&misc_intrs[i].intr_qh);
27081d18a2fSmatt 			evcnt_attach_dynamic(&misc_intrs[i].intr_count,
27181d18a2fSmatt 			    EVCNT_TYPE_INTR, NULL, "misc",
27281d18a2fSmatt 			    apsw->apsw_misc_intrnames[i]);
27381d18a2fSmatt 		}
27481d18a2fSmatt 	}
27581d18a2fSmatt 
27681d18a2fSmatt 	/* make sure we start without any misc interrupts enabled */
27781d18a2fSmatt 	(void) misc_intstat_get();
27881d18a2fSmatt 	misc_intmask_put(0);
27981d18a2fSmatt 	misc_intstat_put(0);
28081d18a2fSmatt 
28181d18a2fSmatt 	/* make sure we register the MISC interrupt handler */
28281d18a2fSmatt 	genath_cpu_intr_establish(apsw->apsw_cpuirq_misc,
28381d18a2fSmatt 	    genath_misc_intr, misc_intrs);
28481d18a2fSmatt }
28581d18a2fSmatt 
28681d18a2fSmatt 
28781d18a2fSmatt const struct atheros_intrsw atheros_intrsw = {
28881d18a2fSmatt 	.aisw_init = genath_intr_init,
28981d18a2fSmatt 	.aisw_cpu_establish = genath_cpu_intr_establish,
29081d18a2fSmatt 	.aisw_cpu_disestablish = genath_cpu_intr_disestablish,
29181d18a2fSmatt 	.aisw_misc_establish = genath_misc_intr_establish,
29281d18a2fSmatt 	.aisw_misc_disestablish = genath_misc_intr_disestablish,
29381d18a2fSmatt 	.aisw_iointr = genath_iointr,
29481d18a2fSmatt };
295