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