1*38e52ce4Sthorpej /* $NetBSD: isa_irqhandler.c,v 1.29 2020/11/22 03:57:19 thorpej Exp $ */
280675955Sthorpej
380675955Sthorpej /*
480675955Sthorpej * Copyright 1997
580675955Sthorpej * Digital Equipment Corporation. All rights reserved.
680675955Sthorpej *
780675955Sthorpej * This software is furnished under license and may be used and
880675955Sthorpej * copied only in accordance with the following terms and conditions.
980675955Sthorpej * Subject to these conditions, you may download, copy, install,
1080675955Sthorpej * use, modify and distribute this software in source and/or binary
1180675955Sthorpej * form. No title or ownership is transferred hereby.
1280675955Sthorpej *
1380675955Sthorpej * 1) Any source code used, modified or distributed must reproduce
1480675955Sthorpej * and retain this copyright notice and list of conditions as
1580675955Sthorpej * they appear in the source file.
1680675955Sthorpej *
1780675955Sthorpej * 2) No right is granted to use any trade name, trademark, or logo of
1880675955Sthorpej * Digital Equipment Corporation. Neither the "Digital Equipment
1980675955Sthorpej * Corporation" name nor any trademark or logo of Digital Equipment
2080675955Sthorpej * Corporation may be used to endorse or promote products derived
2180675955Sthorpej * from this software without the prior written permission of
2280675955Sthorpej * Digital Equipment Corporation.
2380675955Sthorpej *
2480675955Sthorpej * 3) This software is provided "AS-IS" and any express or implied
2580675955Sthorpej * warranties, including but not limited to, any implied warranties
2680675955Sthorpej * of merchantability, fitness for a particular purpose, or
2780675955Sthorpej * non-infringement are disclaimed. In no event shall DIGITAL be
2880675955Sthorpej * liable for any damages whatsoever, and in particular, DIGITAL
2980675955Sthorpej * shall not be liable for special, indirect, consequential, or
3080675955Sthorpej * incidental damages or damages for lost profits, loss of
3180675955Sthorpej * revenue or loss of use, whether such damages arise in contract,
3280675955Sthorpej * negligence, tort, under statute, in equity, at law or otherwise,
3380675955Sthorpej * even if advised of the possibility of such damage.
3480675955Sthorpej */
3580675955Sthorpej
3680675955Sthorpej /*
3780675955Sthorpej * Copyright (c) 1994-1998 Mark Brinicombe.
3880675955Sthorpej * Copyright (c) 1994 Brini.
3980675955Sthorpej * All rights reserved.
4080675955Sthorpej *
4180675955Sthorpej * This code is derived from software written for Brini by Mark Brinicombe
4280675955Sthorpej *
4380675955Sthorpej * Redistribution and use in source and binary forms, with or without
4480675955Sthorpej * modification, are permitted provided that the following conditions
4580675955Sthorpej * are met:
4680675955Sthorpej * 1. Redistributions of source code must retain the above copyright
4780675955Sthorpej * notice, this list of conditions and the following disclaimer.
4880675955Sthorpej * 2. Redistributions in binary form must reproduce the above copyright
4980675955Sthorpej * notice, this list of conditions and the following disclaimer in the
5080675955Sthorpej * documentation and/or other materials provided with the distribution.
5180675955Sthorpej * 3. All advertising materials mentioning features or use of this software
5280675955Sthorpej * must display the following acknowledgement:
5380675955Sthorpej * This product includes software developed by Mark Brinicombe
5480675955Sthorpej * for the NetBSD Project.
5580675955Sthorpej * 4. The name of the company nor the name of the author may be used to
5680675955Sthorpej * endorse or promote products derived from this software without specific
5780675955Sthorpej * prior written permission.
5880675955Sthorpej *
5980675955Sthorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
6080675955Sthorpej * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
6180675955Sthorpej * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
6280675955Sthorpej * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
6380675955Sthorpej * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
6480675955Sthorpej * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
6580675955Sthorpej * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
6680675955Sthorpej * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
6780675955Sthorpej * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
6880675955Sthorpej * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6980675955Sthorpej *
7080675955Sthorpej * IRQ/FIQ initialisation, claim, release and handler routines
7180675955Sthorpej *
7280675955Sthorpej * from: irqhandler.c
7380675955Sthorpej *
7480675955Sthorpej * Created : 30/09/94
7580675955Sthorpej */
7680675955Sthorpej
77ed517291Slukem #include <sys/cdefs.h>
78*38e52ce4Sthorpej __KERNEL_RCSID(0, "$NetBSD: isa_irqhandler.c,v 1.29 2020/11/22 03:57:19 thorpej Exp $");
7980675955Sthorpej
8080675955Sthorpej #include <sys/param.h>
8180675955Sthorpej #include <sys/systm.h>
8280675955Sthorpej #include <sys/syslog.h>
83*38e52ce4Sthorpej #include <sys/kmem.h>
8453872253Smatt #include <sys/intr.h>
8580675955Sthorpej
8653872253Smatt #include <arm/locore.h>
8753872253Smatt
88d471ccf3Smatt #include <machine/irqhandler.h>
8980675955Sthorpej
9080675955Sthorpej irqhandler_t *irqhandlers[NIRQS];
9180675955Sthorpej
9280675955Sthorpej u_int current_mask;
9380675955Sthorpej u_int actual_mask;
9480675955Sthorpej u_int disabled_mask;
95825088edSmatt u_int irqmasks[NIPL];
9680675955Sthorpej
9780675955Sthorpej /* Prototypes */
9880675955Sthorpej
990b379cf8Schs extern void set_spl_masks(void);
1000b379cf8Schs void irq_calculatemasks(void);
1010b379cf8Schs void stray_irqhandler(u_int);
10280675955Sthorpej
10380675955Sthorpej #define WriteWord(a, b) *((volatile unsigned int *)(a)) = (b)
10480675955Sthorpej
10580675955Sthorpej /*
10680675955Sthorpej * void irq_init(void)
10780675955Sthorpej *
10880675955Sthorpej * Initialise the IRQ/FIQ sub system
10980675955Sthorpej */
11080675955Sthorpej
11180675955Sthorpej void
irq_init(void)112df7f595eScegger irq_init(void)
11380675955Sthorpej {
11480675955Sthorpej int loop;
11580675955Sthorpej
11680675955Sthorpej /* Clear all the IRQ handlers and the irq block masks */
11780675955Sthorpej for (loop = 0; loop < NIRQS; ++loop) {
11880675955Sthorpej irqhandlers[loop] = NULL;
11980675955Sthorpej }
12080675955Sthorpej
12180675955Sthorpej /*
12280675955Sthorpej * Setup the irqmasks for the different Interrupt Priority Levels
12380675955Sthorpej * We will start with no bits set and these will be updated as handlers
12480675955Sthorpej * are installed at different IPL's.
12580675955Sthorpej */
126825088edSmatt for (loop = 0; loop < NIPL; ++loop)
12780675955Sthorpej irqmasks[loop] = 0;
12880675955Sthorpej
12980675955Sthorpej current_mask = 0x00000000;
13080675955Sthorpej disabled_mask = 0x00000000;
13180675955Sthorpej actual_mask = 0x00000000;
13280675955Sthorpej
13380675955Sthorpej set_spl_masks();
13480675955Sthorpej
13580675955Sthorpej /* Enable IRQ's and FIQ's */
13680675955Sthorpej enable_interrupts(I32_bit | F32_bit);
13780675955Sthorpej }
13880675955Sthorpej
13980675955Sthorpej
14080675955Sthorpej /*
14180675955Sthorpej * int irq_claim(int irq, irqhandler_t *handler)
14280675955Sthorpej *
14380675955Sthorpej * Enable an IRQ and install a handler for it.
14480675955Sthorpej */
14580675955Sthorpej
14680675955Sthorpej int
irq_claim(int irq,irqhandler_t * handler,const char * group,const char * name)147454af1c0Sdsl irq_claim(int irq, irqhandler_t *handler, const char *group, const char *name)
14880675955Sthorpej {
14980675955Sthorpej
15080675955Sthorpej #ifdef DIAGNOSTIC
15180675955Sthorpej /* Sanity check */
15280675955Sthorpej if (handler == NULL)
1530f09ed48Sprovos panic("NULL interrupt handler");
15480675955Sthorpej if (handler->ih_func == NULL)
1550f09ed48Sprovos panic("Interrupt handler does not have a function");
15680675955Sthorpej #endif /* DIAGNOSTIC */
15780675955Sthorpej
15880675955Sthorpej /*
15980675955Sthorpej * IRQ_INSTRUCT indicates that we should get the irq number
16080675955Sthorpej * from the irq structure
16180675955Sthorpej */
16280675955Sthorpej if (irq == IRQ_INSTRUCT)
16380675955Sthorpej irq = handler->ih_num;
16480675955Sthorpej
16580675955Sthorpej /* Make sure the irq number is valid */
16680675955Sthorpej if (irq < 0 || irq >= NIRQS)
16780675955Sthorpej return(-1);
16880675955Sthorpej
16980675955Sthorpej /* Make sure the level is valid */
170825088edSmatt if (handler->ih_level < 0 || handler->ih_level >= NIPL)
17180675955Sthorpej return(-1);
17280675955Sthorpej
173d471ccf3Smatt /* Attach evcnt */
174d471ccf3Smatt evcnt_attach_dynamic(&handler->ih_ev, EVCNT_TYPE_INTR, NULL,
175d471ccf3Smatt group, name);
176d471ccf3Smatt
17780675955Sthorpej /* Attach handler at top of chain */
17880675955Sthorpej handler->ih_next = irqhandlers[irq];
17980675955Sthorpej irqhandlers[irq] = handler;
18080675955Sthorpej
18180675955Sthorpej /*
18280675955Sthorpej * Reset the flags for this handler.
18380675955Sthorpej * As the handler is now in the chain mark it as active.
18480675955Sthorpej */
18580675955Sthorpej handler->ih_flags = 0 | IRQ_FLAG_ACTIVE;
18680675955Sthorpej
18780675955Sthorpej /*
18880675955Sthorpej * Record the interrupt number for accounting.
18980675955Sthorpej * Done here as the accounting number may not be the same as the
19080675955Sthorpej * IRQ number though for the moment they are
19180675955Sthorpej */
19280675955Sthorpej handler->ih_num = irq;
19380675955Sthorpej
19480675955Sthorpej irq_calculatemasks();
19580675955Sthorpej
19680675955Sthorpej enable_irq(irq);
19780675955Sthorpej set_spl_masks();
19880675955Sthorpej return(0);
19980675955Sthorpej }
20080675955Sthorpej
20180675955Sthorpej
20280675955Sthorpej /*
20380675955Sthorpej * int irq_release(int irq, irqhandler_t *handler)
20480675955Sthorpej *
20580675955Sthorpej * Disable an IRQ and remove a handler for it.
20680675955Sthorpej */
20780675955Sthorpej
20880675955Sthorpej int
irq_release(int irq,irqhandler_t * handler)209454af1c0Sdsl irq_release(int irq, irqhandler_t *handler)
21080675955Sthorpej {
21180675955Sthorpej irqhandler_t *irqhand;
21280675955Sthorpej irqhandler_t **prehand;
21380675955Sthorpej
21480675955Sthorpej /*
21580675955Sthorpej * IRQ_INSTRUCT indicates that we should get the irq number
21680675955Sthorpej * from the irq structure
21780675955Sthorpej */
21880675955Sthorpej if (irq == IRQ_INSTRUCT)
21980675955Sthorpej irq = handler->ih_num;
22080675955Sthorpej
22180675955Sthorpej /* Make sure the irq number is valid */
22280675955Sthorpej if (irq < 0 || irq >= NIRQS)
22380675955Sthorpej return(-1);
22480675955Sthorpej
22580675955Sthorpej /* Locate the handler */
22680675955Sthorpej prehand = &irqhandlers[irq];
227d471ccf3Smatt irqhand = *prehand;
22880675955Sthorpej
22980675955Sthorpej while (irqhand && handler != irqhand) {
230d471ccf3Smatt prehand = &irqhand->ih_next;
231d471ccf3Smatt irqhand = *prehand;
23280675955Sthorpej }
23380675955Sthorpej
23480675955Sthorpej /* Remove the handler if located */
23580675955Sthorpej if (irqhand)
23680675955Sthorpej *prehand = irqhand->ih_next;
23780675955Sthorpej else
23880675955Sthorpej return(-1);
23980675955Sthorpej
240d471ccf3Smatt /* The handler has been removed from the chain so mark it as inactive */
24180675955Sthorpej irqhand->ih_flags &= ~IRQ_FLAG_ACTIVE;
24280675955Sthorpej
24380675955Sthorpej /* Make sure the head of the handler list is active */
24480675955Sthorpej if (irqhandlers[irq])
24580675955Sthorpej irqhandlers[irq]->ih_flags |= IRQ_FLAG_ACTIVE;
24680675955Sthorpej
247d471ccf3Smatt evcnt_detach(&irqhand->ih_ev);
248d471ccf3Smatt
24980675955Sthorpej irq_calculatemasks();
25080675955Sthorpej
25180675955Sthorpej /*
25280675955Sthorpej * Disable the appropriate mask bit if there are no handlers left for
25380675955Sthorpej * this IRQ.
25480675955Sthorpej */
25580675955Sthorpej if (irqhandlers[irq] == NULL)
25680675955Sthorpej disable_irq(irq);
25780675955Sthorpej
25880675955Sthorpej set_spl_masks();
25980675955Sthorpej
26080675955Sthorpej return(0);
26180675955Sthorpej }
26280675955Sthorpej
26380675955Sthorpej /* adapted from .../i386/isa/isa_machdep.c */
26480675955Sthorpej /*
26580675955Sthorpej * Recalculate the interrupt masks from scratch.
26680675955Sthorpej * We could code special registry and deregistry versions of this function that
26780675955Sthorpej * would be faster, but the code would be nastier, and we don't expect this to
26880675955Sthorpej * happen very much anyway.
26980675955Sthorpej */
27080675955Sthorpej void
irq_calculatemasks(void)271df7f595eScegger irq_calculatemasks(void)
27280675955Sthorpej {
27380675955Sthorpej int irq, level;
27480675955Sthorpej irqhandler_t *ptr;
27580675955Sthorpej int irqlevel[NIRQS];
27680675955Sthorpej
27780675955Sthorpej /* First, figure out which levels each IRQ uses. */
27880675955Sthorpej for (irq = 0; irq < NIRQS; irq++) {
27980675955Sthorpej int levels = 0;
28080675955Sthorpej for (ptr = irqhandlers[irq]; ptr; ptr = ptr->ih_next)
28180675955Sthorpej levels |= 1 << ptr->ih_level;
28280675955Sthorpej irqlevel[irq] = levels;
28380675955Sthorpej }
28480675955Sthorpej
28580675955Sthorpej /* Then figure out which IRQs use each level. */
286825088edSmatt for (level = 0; level < NIPL; level++) {
28780675955Sthorpej int irqs = 0;
28880675955Sthorpej for (irq = 0; irq < NIRQS; irq++)
28980675955Sthorpej if (irqlevel[irq] & (1 << level))
29080675955Sthorpej irqs |= 1 << irq;
29180675955Sthorpej irqmasks[level] = ~irqs;
29280675955Sthorpej }
29380675955Sthorpej
29480675955Sthorpej /*
29580675955Sthorpej * Enforce a hierarchy that gives slow devices a better chance at not
29680675955Sthorpej * dropping data.
29780675955Sthorpej */
298825088edSmatt KASSERT(irqmasks[IPL_NONE] == ~0);
2994b293a84Sad irqmasks[IPL_SOFTCLOCK] &= irqmasks[IPL_NONE];
3004b293a84Sad irqmasks[IPL_SOFTBIO] &= irqmasks[IPL_SOFTCLOCK];
3014b293a84Sad irqmasks[IPL_SOFTNET] &= irqmasks[IPL_SOFTBIO];
3024b293a84Sad irqmasks[IPL_SOFTSERIAL] &= irqmasks[IPL_SOFTNET];
3034b293a84Sad irqmasks[IPL_VM] &= irqmasks[IPL_SOFTSERIAL];
3044b293a84Sad irqmasks[IPL_CLOCK] &= irqmasks[IPL_VM];
3054b293a84Sad irqmasks[IPL_HIGH] &= irqmasks[IPL_CLOCK];
30680675955Sthorpej }
30780675955Sthorpej
30880675955Sthorpej
30980675955Sthorpej void *
intr_claim(int irq,int level,int (* ih_func)(void *),void * ih_arg,const char * group,const char * name)3107cc9af7dSdsl intr_claim(int irq, int level, int (*ih_func)(void *), void *ih_arg, const char *group, const char *name)
31180675955Sthorpej {
31280675955Sthorpej irqhandler_t *ih;
31380675955Sthorpej
314*38e52ce4Sthorpej ih = kmem_zalloc(sizeof(*ih), KM_SLEEP);
31580675955Sthorpej ih->ih_level = level;
316e7574fcbSmatt ih->ih_func = ih_func;
317e7574fcbSmatt ih->ih_arg = ih_arg;
31880675955Sthorpej ih->ih_flags = 0;
31980675955Sthorpej
32046923cbeSchristos if (irq_claim(irq, ih, group, name) != 0) {
321*38e52ce4Sthorpej kmem_free(ih, sizeof(*ih));
32280675955Sthorpej return(NULL);
32346923cbeSchristos }
324d471ccf3Smatt
32580675955Sthorpej return(ih);
32680675955Sthorpej }
32780675955Sthorpej
32880675955Sthorpej int
intr_release(void * arg)329454af1c0Sdsl intr_release(void *arg)
33080675955Sthorpej {
33180675955Sthorpej irqhandler_t *ih = (irqhandler_t *)arg;
33280675955Sthorpej
33380675955Sthorpej if (irq_release(ih->ih_num, ih) == 0) {
334*38e52ce4Sthorpej kmem_free(ih, sizeof(*ih));
33580675955Sthorpej return(0);
33680675955Sthorpej }
33780675955Sthorpej return(1);
33880675955Sthorpej }
33980675955Sthorpej
34080675955Sthorpej
34180675955Sthorpej /*
34280675955Sthorpej * void disable_irq(int irq)
34380675955Sthorpej *
34480675955Sthorpej * Disables a specific irq. The irq is removed from the master irq mask
34580675955Sthorpej */
34680675955Sthorpej
34780675955Sthorpej void
disable_irq(int irq)348454af1c0Sdsl disable_irq(int irq)
34980675955Sthorpej {
35080675955Sthorpej u_int oldirqstate;
35180675955Sthorpej
35280675955Sthorpej oldirqstate = disable_interrupts(I32_bit);
35380675955Sthorpej current_mask &= ~(1 << irq);
35480675955Sthorpej irq_setmasks();
35580675955Sthorpej restore_interrupts(oldirqstate);
35680675955Sthorpej }
35780675955Sthorpej
35880675955Sthorpej
35980675955Sthorpej /*
36080675955Sthorpej * void enable_irq(int irq)
36180675955Sthorpej *
36280675955Sthorpej * Enables a specific irq. The irq is added to the master irq mask
36380675955Sthorpej * This routine should be used with caution. A handler should already
36480675955Sthorpej * be installed.
36580675955Sthorpej */
36680675955Sthorpej
36780675955Sthorpej void
enable_irq(int irq)368454af1c0Sdsl enable_irq(int irq)
36980675955Sthorpej {
37080675955Sthorpej u_int oldirqstate;
37180675955Sthorpej
37280675955Sthorpej oldirqstate = disable_interrupts(I32_bit);
37380675955Sthorpej current_mask |= (1 << irq);
37480675955Sthorpej irq_setmasks();
37580675955Sthorpej restore_interrupts(oldirqstate);
37680675955Sthorpej }
37780675955Sthorpej
37880675955Sthorpej
37980675955Sthorpej /*
38080675955Sthorpej * void stray_irqhandler(u_int mask)
38180675955Sthorpej *
38280675955Sthorpej * Handler for stray interrupts. This gets called if a handler cannot be
38380675955Sthorpej * found for an interrupt.
38480675955Sthorpej */
38580675955Sthorpej
38680675955Sthorpej void
stray_irqhandler(u_int mask)387454af1c0Sdsl stray_irqhandler(u_int mask)
38880675955Sthorpej {
38980675955Sthorpej static u_int stray_irqs = 0;
39080675955Sthorpej
39180675955Sthorpej if (++stray_irqs <= 8)
39280675955Sthorpej log(LOG_ERR, "Stray interrupt %08x%s\n", mask,
39380675955Sthorpej stray_irqs >= 8 ? ": stopped logging" : "");
39480675955Sthorpej }
395