xref: /titanic_50/usr/src/grub/grub-0.97/netboot/pic8259.c (revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809)
1*1b8adde7SWilliam Kucharski /*
2*1b8adde7SWilliam Kucharski  * Basic support for controlling the 8259 Programmable Interrupt Controllers.
3*1b8adde7SWilliam Kucharski  *
4*1b8adde7SWilliam Kucharski  * Initially written by Michael Brown (mcb30).
5*1b8adde7SWilliam Kucharski  */
6*1b8adde7SWilliam Kucharski 
7*1b8adde7SWilliam Kucharski #include <etherboot.h>
8*1b8adde7SWilliam Kucharski #include <pic8259.h>
9*1b8adde7SWilliam Kucharski 
10*1b8adde7SWilliam Kucharski #ifdef DEBUG_IRQ
11*1b8adde7SWilliam Kucharski #define DBG(...) printf ( __VA_ARGS__ )
12*1b8adde7SWilliam Kucharski #else
13*1b8adde7SWilliam Kucharski #define DBG(...)
14*1b8adde7SWilliam Kucharski #endif
15*1b8adde7SWilliam Kucharski 
16*1b8adde7SWilliam Kucharski /* Install a handler for the specified IRQ.  Address of previous
17*1b8adde7SWilliam Kucharski  * handler will be stored in previous_handler.  Enabled/disabled state
18*1b8adde7SWilliam Kucharski  * of IRQ will be preserved across call, therefore if the handler does
19*1b8adde7SWilliam Kucharski  * chaining, ensure that either (a) IRQ is disabled before call, or
20*1b8adde7SWilliam Kucharski  * (b) previous_handler points directly to the place that the handler
21*1b8adde7SWilliam Kucharski  * picks up its chain-to address.
22*1b8adde7SWilliam Kucharski  */
23*1b8adde7SWilliam Kucharski 
install_irq_handler(irq_t irq,segoff_t * handler,uint8_t * previously_enabled,segoff_t * previous_handler)24*1b8adde7SWilliam Kucharski int install_irq_handler ( irq_t irq, segoff_t *handler,
25*1b8adde7SWilliam Kucharski 			  uint8_t *previously_enabled,
26*1b8adde7SWilliam Kucharski 			  segoff_t *previous_handler ) {
27*1b8adde7SWilliam Kucharski 	segoff_t *irq_vector = IRQ_VECTOR ( irq );
28*1b8adde7SWilliam Kucharski 	*previously_enabled = irq_enabled ( irq );
29*1b8adde7SWilliam Kucharski 
30*1b8adde7SWilliam Kucharski 	if ( irq > IRQ_MAX ) {
31*1b8adde7SWilliam Kucharski 		DBG ( "Invalid IRQ number %d\n" );
32*1b8adde7SWilliam Kucharski 		return 0;
33*1b8adde7SWilliam Kucharski 	}
34*1b8adde7SWilliam Kucharski 
35*1b8adde7SWilliam Kucharski 	previous_handler->segment = irq_vector->segment;
36*1b8adde7SWilliam Kucharski 	previous_handler->offset = irq_vector->offset;
37*1b8adde7SWilliam Kucharski 	if ( *previously_enabled ) disable_irq ( irq );
38*1b8adde7SWilliam Kucharski 	DBG ( "Installing handler at %hx:%hx for IRQ %d, leaving %s\n",
39*1b8adde7SWilliam Kucharski 		  handler->segment, handler->offset, irq,
40*1b8adde7SWilliam Kucharski 		  ( *previously_enabled ? "enabled" : "disabled" ) );
41*1b8adde7SWilliam Kucharski 	DBG ( "...(previous handler at %hx:%hx)\n",
42*1b8adde7SWilliam Kucharski 		  previous_handler->segment, previous_handler->offset );
43*1b8adde7SWilliam Kucharski 	irq_vector->segment = handler->segment;
44*1b8adde7SWilliam Kucharski 	irq_vector->offset = handler->offset;
45*1b8adde7SWilliam Kucharski 	if ( *previously_enabled ) enable_irq ( irq );
46*1b8adde7SWilliam Kucharski 	return 1;
47*1b8adde7SWilliam Kucharski }
48*1b8adde7SWilliam Kucharski 
49*1b8adde7SWilliam Kucharski /* Remove handler for the specified IRQ.  Routine checks that another
50*1b8adde7SWilliam Kucharski  * handler has not been installed that chains to handler before
51*1b8adde7SWilliam Kucharski  * uninstalling handler.  Enabled/disabled state of the IRQ will be
52*1b8adde7SWilliam Kucharski  * restored to that specified by previously_enabled.
53*1b8adde7SWilliam Kucharski  */
54*1b8adde7SWilliam Kucharski 
remove_irq_handler(irq_t irq,segoff_t * handler,uint8_t * previously_enabled,segoff_t * previous_handler)55*1b8adde7SWilliam Kucharski int remove_irq_handler ( irq_t irq, segoff_t *handler,
56*1b8adde7SWilliam Kucharski 			 uint8_t *previously_enabled,
57*1b8adde7SWilliam Kucharski 			 segoff_t *previous_handler ) {
58*1b8adde7SWilliam Kucharski 	segoff_t *irq_vector = IRQ_VECTOR ( irq );
59*1b8adde7SWilliam Kucharski 
60*1b8adde7SWilliam Kucharski 	if ( irq > IRQ_MAX ) {
61*1b8adde7SWilliam Kucharski 		DBG ( "Invalid IRQ number %d\n" );
62*1b8adde7SWilliam Kucharski 		return 0;
63*1b8adde7SWilliam Kucharski 	}
64*1b8adde7SWilliam Kucharski 	if ( ( irq_vector->segment != handler->segment ) ||
65*1b8adde7SWilliam Kucharski 	     ( irq_vector->offset != handler->offset ) ) {
66*1b8adde7SWilliam Kucharski 		DBG ( "Cannot remove handler for IRQ %d\n" );
67*1b8adde7SWilliam Kucharski 		return 0;
68*1b8adde7SWilliam Kucharski 	}
69*1b8adde7SWilliam Kucharski 
70*1b8adde7SWilliam Kucharski 	DBG ( "Removing handler for IRQ %d\n", irq );
71*1b8adde7SWilliam Kucharski 	disable_irq ( irq );
72*1b8adde7SWilliam Kucharski 	irq_vector->segment = previous_handler->segment;
73*1b8adde7SWilliam Kucharski 	irq_vector->offset = previous_handler->offset;
74*1b8adde7SWilliam Kucharski 	if ( *previously_enabled ) enable_irq ( irq );
75*1b8adde7SWilliam Kucharski 	return 1;
76*1b8adde7SWilliam Kucharski }
77*1b8adde7SWilliam Kucharski 
78*1b8adde7SWilliam Kucharski /* Send specific EOI(s).
79*1b8adde7SWilliam Kucharski  */
80*1b8adde7SWilliam Kucharski 
send_specific_eoi(irq_t irq)81*1b8adde7SWilliam Kucharski void send_specific_eoi ( irq_t irq ) {
82*1b8adde7SWilliam Kucharski 	DBG ( "Sending specific EOI for IRQ %d\n", irq );
83*1b8adde7SWilliam Kucharski 	outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
84*1b8adde7SWilliam Kucharski 	if ( irq >= IRQ_PIC_CUTOFF ) {
85*1b8adde7SWilliam Kucharski 		outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
86*1b8adde7SWilliam Kucharski 		       ICR_REG(CHAINED_IRQ) );
87*1b8adde7SWilliam Kucharski 	}
88*1b8adde7SWilliam Kucharski }
89*1b8adde7SWilliam Kucharski 
90*1b8adde7SWilliam Kucharski /* Dump current 8259 status: enabled IRQs and handler addresses.
91*1b8adde7SWilliam Kucharski  */
92*1b8adde7SWilliam Kucharski 
93*1b8adde7SWilliam Kucharski #ifdef DEBUG_IRQ
dump_irq_status(void)94*1b8adde7SWilliam Kucharski void dump_irq_status (void) {
95*1b8adde7SWilliam Kucharski 	int irq = 0;
96*1b8adde7SWilliam Kucharski 
97*1b8adde7SWilliam Kucharski 	for ( irq = 0; irq < 16; irq++ ) {
98*1b8adde7SWilliam Kucharski 		if ( irq_enabled ( irq ) ) {
99*1b8adde7SWilliam Kucharski 			printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq,
100*1b8adde7SWilliam Kucharski 				 IRQ_VECTOR(irq)->segment,
101*1b8adde7SWilliam Kucharski 				 IRQ_VECTOR(irq)->offset );
102*1b8adde7SWilliam Kucharski 		}
103*1b8adde7SWilliam Kucharski 	}
104*1b8adde7SWilliam Kucharski }
105*1b8adde7SWilliam Kucharski #endif
106*1b8adde7SWilliam Kucharski 
107*1b8adde7SWilliam Kucharski /********************************************************************
108*1b8adde7SWilliam Kucharski  * UNDI interrupt handling
109*1b8adde7SWilliam Kucharski  * This essentially follows the defintion of the trivial interrupt
110*1b8adde7SWilliam Kucharski  * handler routines. The text is assumed to locate in base memory.
111*1b8adde7SWilliam Kucharski  */
112*1b8adde7SWilliam Kucharski void (*undi_irq_handler)P((void)) = _undi_irq_handler;
113*1b8adde7SWilliam Kucharski uint16_t volatile *undi_irq_trigger_count = &_undi_irq_trigger_count;
114*1b8adde7SWilliam Kucharski segoff_t *undi_irq_chain_to = &_undi_irq_chain_to;
115*1b8adde7SWilliam Kucharski uint8_t *undi_irq_chain = &_undi_irq_chain;
116*1b8adde7SWilliam Kucharski irq_t undi_irq_installed_on = IRQ_NONE;
117*1b8adde7SWilliam Kucharski 
118*1b8adde7SWilliam Kucharski /* UNDI entry point and irq, used by interrupt handler
119*1b8adde7SWilliam Kucharski  */
120*1b8adde7SWilliam Kucharski segoff_t *pxenv_undi_entrypointsp = &_pxenv_undi_entrypointsp;
121*1b8adde7SWilliam Kucharski uint8_t *pxenv_undi_irq = &_pxenv_undi_irq;
122*1b8adde7SWilliam Kucharski 
123*1b8adde7SWilliam Kucharski /* Previous trigger count for undi IRQ handler */
124*1b8adde7SWilliam Kucharski static uint16_t undi_irq_previous_trigger_count = 0;
125*1b8adde7SWilliam Kucharski 
126*1b8adde7SWilliam Kucharski /* Install the undi IRQ handler. Don't test as UNDI has not be opened.
127*1b8adde7SWilliam Kucharski  */
128*1b8adde7SWilliam Kucharski 
install_undi_irq_handler(irq_t irq,segoff_t entrypointsp)129*1b8adde7SWilliam Kucharski int install_undi_irq_handler ( irq_t irq, segoff_t entrypointsp ) {
130*1b8adde7SWilliam Kucharski 	segoff_t undi_irq_handler_segoff = SEGOFF(undi_irq_handler);
131*1b8adde7SWilliam Kucharski 
132*1b8adde7SWilliam Kucharski 	if ( undi_irq_installed_on != IRQ_NONE ) {
133*1b8adde7SWilliam Kucharski 		DBG ( "Can install undi IRQ handler only once\n" );
134*1b8adde7SWilliam Kucharski 		return 0;
135*1b8adde7SWilliam Kucharski 	}
136*1b8adde7SWilliam Kucharski 	if ( SEGMENT(undi_irq_handler) > 0xffff ) {
137*1b8adde7SWilliam Kucharski 		DBG ( "Trivial IRQ handler not in base memory\n" );
138*1b8adde7SWilliam Kucharski 		return 0;
139*1b8adde7SWilliam Kucharski 	}
140*1b8adde7SWilliam Kucharski 
141*1b8adde7SWilliam Kucharski 	DBG ( "Installing undi IRQ handler on IRQ %d\n", irq );
142*1b8adde7SWilliam Kucharski 	*pxenv_undi_entrypointsp = entrypointsp;
143*1b8adde7SWilliam Kucharski 	*pxenv_undi_irq = irq;
144*1b8adde7SWilliam Kucharski 	if ( ! install_irq_handler ( irq, &undi_irq_handler_segoff,
145*1b8adde7SWilliam Kucharski 				     undi_irq_chain,
146*1b8adde7SWilliam Kucharski 				     undi_irq_chain_to ) )
147*1b8adde7SWilliam Kucharski 		return 0;
148*1b8adde7SWilliam Kucharski 	undi_irq_installed_on = irq;
149*1b8adde7SWilliam Kucharski 
150*1b8adde7SWilliam Kucharski 	DBG ( "Disabling undi IRQ %d\n", irq );
151*1b8adde7SWilliam Kucharski 	disable_irq ( irq );
152*1b8adde7SWilliam Kucharski 	*undi_irq_trigger_count = 0;
153*1b8adde7SWilliam Kucharski 	undi_irq_previous_trigger_count = 0;
154*1b8adde7SWilliam Kucharski 	DBG ( "UNDI IRQ handler installed successfully\n" );
155*1b8adde7SWilliam Kucharski 	return 1;
156*1b8adde7SWilliam Kucharski }
157*1b8adde7SWilliam Kucharski 
158*1b8adde7SWilliam Kucharski /* Remove the undi IRQ handler.
159*1b8adde7SWilliam Kucharski  */
160*1b8adde7SWilliam Kucharski 
remove_undi_irq_handler(irq_t irq)161*1b8adde7SWilliam Kucharski int remove_undi_irq_handler ( irq_t irq ) {
162*1b8adde7SWilliam Kucharski 	segoff_t undi_irq_handler_segoff = SEGOFF(undi_irq_handler);
163*1b8adde7SWilliam Kucharski 
164*1b8adde7SWilliam Kucharski 	if ( undi_irq_installed_on == IRQ_NONE ) return 1;
165*1b8adde7SWilliam Kucharski 	if ( irq != undi_irq_installed_on ) {
166*1b8adde7SWilliam Kucharski 		DBG ( "Cannot uninstall undi IRQ handler from IRQ %d; "
167*1b8adde7SWilliam Kucharski 		      "is installed on IRQ %d\n", irq,
168*1b8adde7SWilliam Kucharski 		      undi_irq_installed_on );
169*1b8adde7SWilliam Kucharski 		return 0;
170*1b8adde7SWilliam Kucharski 	}
171*1b8adde7SWilliam Kucharski 
172*1b8adde7SWilliam Kucharski 	if ( ! remove_irq_handler ( irq, &undi_irq_handler_segoff,
173*1b8adde7SWilliam Kucharski 				    undi_irq_chain,
174*1b8adde7SWilliam Kucharski 				    undi_irq_chain_to ) )
175*1b8adde7SWilliam Kucharski 		return 0;
176*1b8adde7SWilliam Kucharski 
177*1b8adde7SWilliam Kucharski 	if ( undi_irq_triggered ( undi_irq_installed_on ) ) {
178*1b8adde7SWilliam Kucharski 		DBG ( "Sending EOI for unwanted undi IRQ\n" );
179*1b8adde7SWilliam Kucharski 		send_specific_eoi ( undi_irq_installed_on );
180*1b8adde7SWilliam Kucharski 	}
181*1b8adde7SWilliam Kucharski 
182*1b8adde7SWilliam Kucharski 	undi_irq_installed_on = IRQ_NONE;
183*1b8adde7SWilliam Kucharski 	return 1;
184*1b8adde7SWilliam Kucharski }
185*1b8adde7SWilliam Kucharski 
186*1b8adde7SWilliam Kucharski /* Safe method to detect whether or not undi IRQ has been
187*1b8adde7SWilliam Kucharski  * triggered.  Using this call avoids potential race conditions.  This
188*1b8adde7SWilliam Kucharski  * call will return success only once per trigger.
189*1b8adde7SWilliam Kucharski  */
190*1b8adde7SWilliam Kucharski 
undi_irq_triggered(irq_t irq)191*1b8adde7SWilliam Kucharski int undi_irq_triggered ( irq_t irq ) {
192*1b8adde7SWilliam Kucharski 	uint16_t undi_irq_this_trigger_count = *undi_irq_trigger_count;
193*1b8adde7SWilliam Kucharski 	int triggered = ( undi_irq_this_trigger_count -
194*1b8adde7SWilliam Kucharski 			  undi_irq_previous_trigger_count );
195*1b8adde7SWilliam Kucharski 
196*1b8adde7SWilliam Kucharski 	/* irq is not used at present, but we have it in the API for
197*1b8adde7SWilliam Kucharski 	 * future-proofing; in case we want the facility to have
198*1b8adde7SWilliam Kucharski 	 * multiple undi IRQ handlers installed simultaneously.
199*1b8adde7SWilliam Kucharski 	 *
200*1b8adde7SWilliam Kucharski 	 * Avoid compiler warning about unused variable.
201*1b8adde7SWilliam Kucharski 	 */
202*1b8adde7SWilliam Kucharski 	if ( irq == IRQ_NONE ) {};
203*1b8adde7SWilliam Kucharski 	undi_irq_previous_trigger_count = undi_irq_this_trigger_count;
204*1b8adde7SWilliam Kucharski 	return triggered ? 1 : 0;
205*1b8adde7SWilliam Kucharski }
206