xref: /minix3/minix/kernel/interrupt.c (revision 9624407e7addfd8b88486acfe3a0e056e2b92ee3)
1*433d6423SLionel Sambuc /*
2*433d6423SLionel Sambuc  *   The Minix hardware interrupt system.
3*433d6423SLionel Sambuc  *
4*433d6423SLionel Sambuc  *   This file contains routines for managing the interrupt
5*433d6423SLionel Sambuc  *   controller.
6*433d6423SLionel Sambuc  *
7*433d6423SLionel Sambuc  *   put_irq_handler: register an interrupt handler.
8*433d6423SLionel Sambuc  *   rm_irq_handler:  deregister an interrupt handler.
9*433d6423SLionel Sambuc  *   irq_handle:     handle a hardware interrupt.
10*433d6423SLionel Sambuc  *                    called by the system dependent part when an
11*433d6423SLionel Sambuc  *                    external interrupt occurs.
12*433d6423SLionel Sambuc  *   enable_irq:      enable hook for IRQ.
13*433d6423SLionel Sambuc  *   disable_irq:     disable hook for IRQ.
14*433d6423SLionel Sambuc  */
15*433d6423SLionel Sambuc 
16*433d6423SLionel Sambuc #include <assert.h>
17*433d6423SLionel Sambuc 
18*433d6423SLionel Sambuc #include "kernel/kernel.h"
19*433d6423SLionel Sambuc #include "hw_intr.h"
20*433d6423SLionel Sambuc 
21*433d6423SLionel Sambuc 
22*433d6423SLionel Sambuc /* number of lists of IRQ hooks, one list per supported line. */
23*433d6423SLionel Sambuc static irq_hook_t* irq_handlers[NR_IRQ_VECTORS] = {0};
24*433d6423SLionel Sambuc 
25*433d6423SLionel Sambuc /*===========================================================================*
26*433d6423SLionel Sambuc  *				put_irq_handler				     *
27*433d6423SLionel Sambuc  *===========================================================================*/
28*433d6423SLionel Sambuc /* Register an interrupt handler.  */
put_irq_handler(irq_hook_t * hook,int irq,const irq_handler_t handler)29*433d6423SLionel Sambuc void put_irq_handler( irq_hook_t* hook, int irq,
30*433d6423SLionel Sambuc   const irq_handler_t handler)
31*433d6423SLionel Sambuc {
32*433d6423SLionel Sambuc   int id;
33*433d6423SLionel Sambuc   irq_hook_t **line;
34*433d6423SLionel Sambuc   unsigned long bitmap;
35*433d6423SLionel Sambuc 
36*433d6423SLionel Sambuc   if( irq < 0 || irq >= NR_IRQ_VECTORS )
37*433d6423SLionel Sambuc 	panic("invalid call to put_irq_handler: %d",  irq);
38*433d6423SLionel Sambuc 
39*433d6423SLionel Sambuc   line = &irq_handlers[irq];
40*433d6423SLionel Sambuc 
41*433d6423SLionel Sambuc   bitmap = 0;
42*433d6423SLionel Sambuc   while ( *line != NULL ) {
43*433d6423SLionel Sambuc 	if(hook == *line) return; /* extra initialization */
44*433d6423SLionel Sambuc 	bitmap |= (*line)->id;	/* mark ids in use */
45*433d6423SLionel Sambuc 	line = &(*line)->next;
46*433d6423SLionel Sambuc   }
47*433d6423SLionel Sambuc 
48*433d6423SLionel Sambuc   /* find the lowest id not in use */
49*433d6423SLionel Sambuc   for (id = 1; id != 0; id <<= 1)
50*433d6423SLionel Sambuc   	if (!(bitmap & id)) break;
51*433d6423SLionel Sambuc 
52*433d6423SLionel Sambuc   if(id == 0)
53*433d6423SLionel Sambuc   	panic("Too many handlers for irq: %d",  irq);
54*433d6423SLionel Sambuc 
55*433d6423SLionel Sambuc   hook->next = NULL;
56*433d6423SLionel Sambuc   hook->handler = handler;
57*433d6423SLionel Sambuc   hook->irq = irq;
58*433d6423SLionel Sambuc   hook->id = id;
59*433d6423SLionel Sambuc   *line = hook;
60*433d6423SLionel Sambuc 
61*433d6423SLionel Sambuc   /* And as last enable the irq at the hardware.
62*433d6423SLionel Sambuc    *
63*433d6423SLionel Sambuc    * Internal this activates the line or source of the given interrupt.
64*433d6423SLionel Sambuc    */
65*433d6423SLionel Sambuc   if((irq_actids[hook->irq] &= ~hook->id) == 0) {
66*433d6423SLionel Sambuc 	  hw_intr_used(irq);
67*433d6423SLionel Sambuc 	  hw_intr_unmask(hook->irq);
68*433d6423SLionel Sambuc   }
69*433d6423SLionel Sambuc }
70*433d6423SLionel Sambuc 
71*433d6423SLionel Sambuc /*===========================================================================*
72*433d6423SLionel Sambuc  *				rm_irq_handler				     *
73*433d6423SLionel Sambuc  *===========================================================================*/
74*433d6423SLionel Sambuc /* Unregister an interrupt handler.  */
rm_irq_handler(const irq_hook_t * hook)75*433d6423SLionel Sambuc void rm_irq_handler( const irq_hook_t* hook ) {
76*433d6423SLionel Sambuc   const int irq = hook->irq;
77*433d6423SLionel Sambuc   const int id = hook->id;
78*433d6423SLionel Sambuc   irq_hook_t **line;
79*433d6423SLionel Sambuc 
80*433d6423SLionel Sambuc   if( irq < 0 || irq >= NR_IRQ_VECTORS )
81*433d6423SLionel Sambuc 	panic("invalid call to rm_irq_handler: %d",  irq);
82*433d6423SLionel Sambuc 
83*433d6423SLionel Sambuc   /* remove the hook  */
84*433d6423SLionel Sambuc   line = &irq_handlers[irq];
85*433d6423SLionel Sambuc   while( (*line) != NULL ) {
86*433d6423SLionel Sambuc 	if((*line)->id == id) {
87*433d6423SLionel Sambuc 		(*line) = (*line)->next;
88*433d6423SLionel Sambuc 		if (irq_actids[irq] & id)
89*433d6423SLionel Sambuc 			irq_actids[irq] &= ~id;
90*433d6423SLionel Sambuc     	}
91*433d6423SLionel Sambuc     	else {
92*433d6423SLionel Sambuc 		line = &(*line)->next;
93*433d6423SLionel Sambuc     	}
94*433d6423SLionel Sambuc   }
95*433d6423SLionel Sambuc 
96*433d6423SLionel Sambuc   /* Disable the irq if there are no other handlers registered.
97*433d6423SLionel Sambuc    * If the irq is shared, reenable it if there is no active handler.
98*433d6423SLionel Sambuc    */
99*433d6423SLionel Sambuc   if (irq_handlers[irq] == NULL) {
100*433d6423SLionel Sambuc 	hw_intr_mask(irq);
101*433d6423SLionel Sambuc 	hw_intr_not_used(irq);
102*433d6423SLionel Sambuc   }
103*433d6423SLionel Sambuc   else if (irq_actids[irq] == 0) {
104*433d6423SLionel Sambuc 	hw_intr_unmask(irq);
105*433d6423SLionel Sambuc   }
106*433d6423SLionel Sambuc }
107*433d6423SLionel Sambuc 
108*433d6423SLionel Sambuc /*===========================================================================*
109*433d6423SLionel Sambuc  *				irq_handle				     *
110*433d6423SLionel Sambuc  *===========================================================================*/
111*433d6423SLionel Sambuc /*
112*433d6423SLionel Sambuc  * The function first disables interrupt is need be and restores the state at
113*433d6423SLionel Sambuc  * the end. Before returning, it unmasks the IRQ if and only if all active ID
114*433d6423SLionel Sambuc  * bits are cleared, and restart a process.
115*433d6423SLionel Sambuc  */
irq_handle(int irq)116*433d6423SLionel Sambuc void irq_handle(int irq)
117*433d6423SLionel Sambuc {
118*433d6423SLionel Sambuc   irq_hook_t * hook;
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc   /* here we need not to get this IRQ until all the handlers had a say */
121*433d6423SLionel Sambuc   assert(irq >= 0 && irq < NR_IRQ_VECTORS);
122*433d6423SLionel Sambuc   hw_intr_mask(irq);
123*433d6423SLionel Sambuc   hook = irq_handlers[irq];
124*433d6423SLionel Sambuc 
125*433d6423SLionel Sambuc   /* Check for spurious interrupts. */
126*433d6423SLionel Sambuc   if(hook == NULL) {
127*433d6423SLionel Sambuc       static int nspurious[NR_IRQ_VECTORS], report_interval = 100;
128*433d6423SLionel Sambuc       nspurious[irq]++;
129*433d6423SLionel Sambuc       if(nspurious[irq] == 1 || !(nspurious[irq] % report_interval)) {
130*433d6423SLionel Sambuc       	printf("irq_handle: spurious irq %d (count: %d); keeping masked\n",
131*433d6423SLionel Sambuc 		irq, nspurious[irq]);
132*433d6423SLionel Sambuc 	if(report_interval < INT_MAX/2)
133*433d6423SLionel Sambuc 		report_interval *= 2;
134*433d6423SLionel Sambuc       }
135*433d6423SLionel Sambuc       return;
136*433d6423SLionel Sambuc   }
137*433d6423SLionel Sambuc 
138*433d6423SLionel Sambuc   /* Call list of handlers for an IRQ. */
139*433d6423SLionel Sambuc   while( hook != NULL ) {
140*433d6423SLionel Sambuc     /* For each handler in the list, mark it active by setting its ID bit,
141*433d6423SLionel Sambuc      * call the function, and unmark it if the function returns true.
142*433d6423SLionel Sambuc      */
143*433d6423SLionel Sambuc     irq_actids[irq] |= hook->id;
144*433d6423SLionel Sambuc 
145*433d6423SLionel Sambuc     /* Call the hooked function. */
146*433d6423SLionel Sambuc     if( (*hook->handler)(hook) )
147*433d6423SLionel Sambuc       irq_actids[hook->irq] &= ~hook->id;
148*433d6423SLionel Sambuc 
149*433d6423SLionel Sambuc     /* Next hooked function. */
150*433d6423SLionel Sambuc     hook = hook->next;
151*433d6423SLionel Sambuc   }
152*433d6423SLionel Sambuc 
153*433d6423SLionel Sambuc   /* reenable the IRQ only if there is no active handler */
154*433d6423SLionel Sambuc   if (irq_actids[irq] == 0)
155*433d6423SLionel Sambuc 	  hw_intr_unmask(irq);
156*433d6423SLionel Sambuc 
157*433d6423SLionel Sambuc   hw_intr_ack(irq);
158*433d6423SLionel Sambuc }
159*433d6423SLionel Sambuc 
160*433d6423SLionel Sambuc /* Enable/Disable a interrupt line.  */
enable_irq(const irq_hook_t * hook)161*433d6423SLionel Sambuc void enable_irq(const irq_hook_t *hook)
162*433d6423SLionel Sambuc {
163*433d6423SLionel Sambuc   if((irq_actids[hook->irq] &= ~hook->id) == 0) {
164*433d6423SLionel Sambuc     hw_intr_unmask(hook->irq);
165*433d6423SLionel Sambuc   }
166*433d6423SLionel Sambuc }
167*433d6423SLionel Sambuc 
168*433d6423SLionel Sambuc /* Return true if the interrupt was enabled before call.  */
disable_irq(const irq_hook_t * hook)169*433d6423SLionel Sambuc int disable_irq(const irq_hook_t *hook)
170*433d6423SLionel Sambuc {
171*433d6423SLionel Sambuc   if(irq_actids[hook->irq] & hook->id)  /* already disabled */
172*433d6423SLionel Sambuc     return 0;
173*433d6423SLionel Sambuc   irq_actids[hook->irq] |= hook->id;
174*433d6423SLionel Sambuc   hw_intr_mask(hook->irq);
175*433d6423SLionel Sambuc   return TRUE;
176*433d6423SLionel Sambuc }
177*433d6423SLionel Sambuc 
178