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