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