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 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 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 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 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 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 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 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