17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Basic support for controlling the 8259 Programmable Interrupt Controllers.
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * Initially written by Michael Brown (mcb30).
57c478bd9Sstevel@tonic-gate  */
67c478bd9Sstevel@tonic-gate 
77c478bd9Sstevel@tonic-gate #include <etherboot.h>
87c478bd9Sstevel@tonic-gate #include <pic8259.h>
97c478bd9Sstevel@tonic-gate 
107c478bd9Sstevel@tonic-gate #ifdef DEBUG_IRQ
117c478bd9Sstevel@tonic-gate #define DBG(...) printf ( __VA_ARGS__ )
127c478bd9Sstevel@tonic-gate #else
137c478bd9Sstevel@tonic-gate #define DBG(...)
147c478bd9Sstevel@tonic-gate #endif
157c478bd9Sstevel@tonic-gate 
167c478bd9Sstevel@tonic-gate /* Install a handler for the specified IRQ.  Address of previous
177c478bd9Sstevel@tonic-gate  * handler will be stored in previous_handler.  Enabled/disabled state
187c478bd9Sstevel@tonic-gate  * of IRQ will be preserved across call, therefore if the handler does
197c478bd9Sstevel@tonic-gate  * chaining, ensure that either (a) IRQ is disabled before call, or
207c478bd9Sstevel@tonic-gate  * (b) previous_handler points directly to the place that the handler
217c478bd9Sstevel@tonic-gate  * picks up its chain-to address.
227c478bd9Sstevel@tonic-gate  */
237c478bd9Sstevel@tonic-gate 
install_irq_handler(irq_t irq,segoff_t * handler,uint8_t * previously_enabled,segoff_t * previous_handler)247c478bd9Sstevel@tonic-gate int install_irq_handler ( irq_t irq, segoff_t *handler,
257c478bd9Sstevel@tonic-gate 			  uint8_t *previously_enabled,
267c478bd9Sstevel@tonic-gate 			  segoff_t *previous_handler ) {
277c478bd9Sstevel@tonic-gate 	segoff_t *irq_vector = IRQ_VECTOR ( irq );
287c478bd9Sstevel@tonic-gate 	*previously_enabled = irq_enabled ( irq );
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate 	if ( irq > IRQ_MAX ) {
317c478bd9Sstevel@tonic-gate 		DBG ( "Invalid IRQ number %d\n" );
327c478bd9Sstevel@tonic-gate 		return 0;
337c478bd9Sstevel@tonic-gate 	}
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate 	previous_handler->segment = irq_vector->segment;
367c478bd9Sstevel@tonic-gate 	previous_handler->offset = irq_vector->offset;
377c478bd9Sstevel@tonic-gate 	if ( *previously_enabled ) disable_irq ( irq );
387c478bd9Sstevel@tonic-gate 	DBG ( "Installing handler at %hx:%hx for IRQ %d, leaving %s\n",
397c478bd9Sstevel@tonic-gate 		  handler->segment, handler->offset, irq,
407c478bd9Sstevel@tonic-gate 		  ( *previously_enabled ? "enabled" : "disabled" ) );
417c478bd9Sstevel@tonic-gate 	DBG ( "...(previous handler at %hx:%hx)\n",
427c478bd9Sstevel@tonic-gate 		  previous_handler->segment, previous_handler->offset );
437c478bd9Sstevel@tonic-gate 	irq_vector->segment = handler->segment;
447c478bd9Sstevel@tonic-gate 	irq_vector->offset = handler->offset;
457c478bd9Sstevel@tonic-gate 	if ( *previously_enabled ) enable_irq ( irq );
467c478bd9Sstevel@tonic-gate 	return 1;
477c478bd9Sstevel@tonic-gate }
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate /* Remove handler for the specified IRQ.  Routine checks that another
507c478bd9Sstevel@tonic-gate  * handler has not been installed that chains to handler before
517c478bd9Sstevel@tonic-gate  * uninstalling handler.  Enabled/disabled state of the IRQ will be
527c478bd9Sstevel@tonic-gate  * restored to that specified by previously_enabled.
537c478bd9Sstevel@tonic-gate  */
547c478bd9Sstevel@tonic-gate 
remove_irq_handler(irq_t irq,segoff_t * handler,uint8_t * previously_enabled,segoff_t * previous_handler)557c478bd9Sstevel@tonic-gate int remove_irq_handler ( irq_t irq, segoff_t *handler,
567c478bd9Sstevel@tonic-gate 			 uint8_t *previously_enabled,
577c478bd9Sstevel@tonic-gate 			 segoff_t *previous_handler ) {
587c478bd9Sstevel@tonic-gate 	segoff_t *irq_vector = IRQ_VECTOR ( irq );
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate 	if ( irq > IRQ_MAX ) {
617c478bd9Sstevel@tonic-gate 		DBG ( "Invalid IRQ number %d\n" );
627c478bd9Sstevel@tonic-gate 		return 0;
637c478bd9Sstevel@tonic-gate 	}
647c478bd9Sstevel@tonic-gate 	if ( ( irq_vector->segment != handler->segment ) ||
657c478bd9Sstevel@tonic-gate 	     ( irq_vector->offset != handler->offset ) ) {
667c478bd9Sstevel@tonic-gate 		DBG ( "Cannot remove handler for IRQ %d\n" );
677c478bd9Sstevel@tonic-gate 		return 0;
687c478bd9Sstevel@tonic-gate 	}
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 	DBG ( "Removing handler for IRQ %d\n", irq );
717c478bd9Sstevel@tonic-gate 	disable_irq ( irq );
727c478bd9Sstevel@tonic-gate 	irq_vector->segment = previous_handler->segment;
737c478bd9Sstevel@tonic-gate 	irq_vector->offset = previous_handler->offset;
747c478bd9Sstevel@tonic-gate 	if ( *previously_enabled ) enable_irq ( irq );
757c478bd9Sstevel@tonic-gate 	return 1;
767c478bd9Sstevel@tonic-gate }
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /* Send specific EOI(s).
797c478bd9Sstevel@tonic-gate  */
807c478bd9Sstevel@tonic-gate 
send_specific_eoi(irq_t irq)817c478bd9Sstevel@tonic-gate void send_specific_eoi ( irq_t irq ) {
827c478bd9Sstevel@tonic-gate 	DBG ( "Sending specific EOI for IRQ %d\n", irq );
837c478bd9Sstevel@tonic-gate 	outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
847c478bd9Sstevel@tonic-gate 	if ( irq >= IRQ_PIC_CUTOFF ) {
857c478bd9Sstevel@tonic-gate 		outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
867c478bd9Sstevel@tonic-gate 		       ICR_REG(CHAINED_IRQ) );
877c478bd9Sstevel@tonic-gate 	}
887c478bd9Sstevel@tonic-gate }
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate /* Dump current 8259 status: enabled IRQs and handler addresses.
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate #ifdef DEBUG_IRQ
dump_irq_status(void)947c478bd9Sstevel@tonic-gate void dump_irq_status (void) {
957c478bd9Sstevel@tonic-gate 	int irq = 0;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 	for ( irq = 0; irq < 16; irq++ ) {
987c478bd9Sstevel@tonic-gate 		if ( irq_enabled ( irq ) ) {
997c478bd9Sstevel@tonic-gate 			printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq,
1007c478bd9Sstevel@tonic-gate 				 IRQ_VECTOR(irq)->segment,
1017c478bd9Sstevel@tonic-gate 				 IRQ_VECTOR(irq)->offset );
1027c478bd9Sstevel@tonic-gate 		}
1037c478bd9Sstevel@tonic-gate 	}
1047c478bd9Sstevel@tonic-gate }
1057c478bd9Sstevel@tonic-gate #endif
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate /********************************************************************
1087c478bd9Sstevel@tonic-gate  * UNDI interrupt handling
1097c478bd9Sstevel@tonic-gate  * This essentially follows the defintion of the trivial interrupt
1107c478bd9Sstevel@tonic-gate  * handler routines. The text is assumed to locate in base memory.
1117c478bd9Sstevel@tonic-gate  */
1127c478bd9Sstevel@tonic-gate void (*undi_irq_handler)P((void)) = _undi_irq_handler;
1137c478bd9Sstevel@tonic-gate uint16_t volatile *undi_irq_trigger_count = &_undi_irq_trigger_count;
1147c478bd9Sstevel@tonic-gate segoff_t *undi_irq_chain_to = &_undi_irq_chain_to;
1157c478bd9Sstevel@tonic-gate uint8_t *undi_irq_chain = &_undi_irq_chain;
1167c478bd9Sstevel@tonic-gate irq_t undi_irq_installed_on = IRQ_NONE;
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate /* UNDI entry point and irq, used by interrupt handler
1197c478bd9Sstevel@tonic-gate  */
1207c478bd9Sstevel@tonic-gate segoff_t *pxenv_undi_entrypointsp = &_pxenv_undi_entrypointsp;
1217c478bd9Sstevel@tonic-gate uint8_t *pxenv_undi_irq = &_pxenv_undi_irq;
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate /* Previous trigger count for undi IRQ handler */
1247c478bd9Sstevel@tonic-gate static uint16_t undi_irq_previous_trigger_count = 0;
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate /* Install the undi IRQ handler. Don't test as UNDI has not be opened.
1277c478bd9Sstevel@tonic-gate  */
1287c478bd9Sstevel@tonic-gate 
install_undi_irq_handler(irq_t irq,segoff_t entrypointsp)1297c478bd9Sstevel@tonic-gate int install_undi_irq_handler ( irq_t irq, segoff_t entrypointsp ) {
1307c478bd9Sstevel@tonic-gate 	segoff_t undi_irq_handler_segoff = SEGOFF(undi_irq_handler);
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	if ( undi_irq_installed_on != IRQ_NONE ) {
1337c478bd9Sstevel@tonic-gate 		DBG ( "Can install undi IRQ handler only once\n" );
1347c478bd9Sstevel@tonic-gate 		return 0;
1357c478bd9Sstevel@tonic-gate 	}
1367c478bd9Sstevel@tonic-gate 	if ( SEGMENT(undi_irq_handler) > 0xffff ) {
1377c478bd9Sstevel@tonic-gate 		DBG ( "Trivial IRQ handler not in base memory\n" );
1387c478bd9Sstevel@tonic-gate 		return 0;
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	DBG ( "Installing undi IRQ handler on IRQ %d\n", irq );
1427c478bd9Sstevel@tonic-gate 	*pxenv_undi_entrypointsp = entrypointsp;
1437c478bd9Sstevel@tonic-gate 	*pxenv_undi_irq = irq;
1447c478bd9Sstevel@tonic-gate 	if ( ! install_irq_handler ( irq, &undi_irq_handler_segoff,
1457c478bd9Sstevel@tonic-gate 				     undi_irq_chain,
1467c478bd9Sstevel@tonic-gate 				     undi_irq_chain_to ) )
1477c478bd9Sstevel@tonic-gate 		return 0;
1487c478bd9Sstevel@tonic-gate 	undi_irq_installed_on = irq;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	DBG ( "Disabling undi IRQ %d\n", irq );
1517c478bd9Sstevel@tonic-gate 	disable_irq ( irq );
1527c478bd9Sstevel@tonic-gate 	*undi_irq_trigger_count = 0;
1537c478bd9Sstevel@tonic-gate 	undi_irq_previous_trigger_count = 0;
1547c478bd9Sstevel@tonic-gate 	DBG ( "UNDI IRQ handler installed successfully\n" );
1557c478bd9Sstevel@tonic-gate 	return 1;
1567c478bd9Sstevel@tonic-gate }
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate /* Remove the undi IRQ handler.
1597c478bd9Sstevel@tonic-gate  */
1607c478bd9Sstevel@tonic-gate 
remove_undi_irq_handler(irq_t irq)1617c478bd9Sstevel@tonic-gate int remove_undi_irq_handler ( irq_t irq ) {
1627c478bd9Sstevel@tonic-gate 	segoff_t undi_irq_handler_segoff = SEGOFF(undi_irq_handler);
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	if ( undi_irq_installed_on == IRQ_NONE ) return 1;
1657c478bd9Sstevel@tonic-gate 	if ( irq != undi_irq_installed_on ) {
1667c478bd9Sstevel@tonic-gate 		DBG ( "Cannot uninstall undi IRQ handler from IRQ %d; "
1677c478bd9Sstevel@tonic-gate 		      "is installed on IRQ %d\n", irq,
1687c478bd9Sstevel@tonic-gate 		      undi_irq_installed_on );
1697c478bd9Sstevel@tonic-gate 		return 0;
1707c478bd9Sstevel@tonic-gate 	}
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	if ( ! remove_irq_handler ( irq, &undi_irq_handler_segoff,
1737c478bd9Sstevel@tonic-gate 				    undi_irq_chain,
1747c478bd9Sstevel@tonic-gate 				    undi_irq_chain_to ) )
1757c478bd9Sstevel@tonic-gate 		return 0;
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 	if ( undi_irq_triggered ( undi_irq_installed_on ) ) {
1787c478bd9Sstevel@tonic-gate 		DBG ( "Sending EOI for unwanted undi IRQ\n" );
1797c478bd9Sstevel@tonic-gate 		send_specific_eoi ( undi_irq_installed_on );
1807c478bd9Sstevel@tonic-gate 	}
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	undi_irq_installed_on = IRQ_NONE;
1837c478bd9Sstevel@tonic-gate 	return 1;
1847c478bd9Sstevel@tonic-gate }
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate /* Safe method to detect whether or not undi IRQ has been
1877c478bd9Sstevel@tonic-gate  * triggered.  Using this call avoids potential race conditions.  This
1887c478bd9Sstevel@tonic-gate  * call will return success only once per trigger.
1897c478bd9Sstevel@tonic-gate  */
1907c478bd9Sstevel@tonic-gate 
undi_irq_triggered(irq_t irq)1917c478bd9Sstevel@tonic-gate int undi_irq_triggered ( irq_t irq ) {
1927c478bd9Sstevel@tonic-gate 	uint16_t undi_irq_this_trigger_count = *undi_irq_trigger_count;
1937c478bd9Sstevel@tonic-gate 	int triggered = ( undi_irq_this_trigger_count -
1947c478bd9Sstevel@tonic-gate 			  undi_irq_previous_trigger_count );
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	/* irq is not used at present, but we have it in the API for
1977c478bd9Sstevel@tonic-gate 	 * future-proofing; in case we want the facility to have
1987c478bd9Sstevel@tonic-gate 	 * multiple undi IRQ handlers installed simultaneously.
1997c478bd9Sstevel@tonic-gate 	 *
2007c478bd9Sstevel@tonic-gate 	 * Avoid compiler warning about unused variable.
2017c478bd9Sstevel@tonic-gate 	 */
2027c478bd9Sstevel@tonic-gate 	if ( irq == IRQ_NONE ) {};
2037c478bd9Sstevel@tonic-gate 	undi_irq_previous_trigger_count = undi_irq_this_trigger_count;
2047c478bd9Sstevel@tonic-gate 	return triggered ? 1 : 0;
2057c478bd9Sstevel@tonic-gate }
206