/* * ISR Vectoring support for the Synova Mongoose-V. * * COPYRIGHT (c) 1989-2001. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * * $Id$ */ #include #include #include #include "iregdef.h" #include "idtcpu.h" #include /* for printk */ int mips_default_isr( int vector ) { unsigned int sr, sr2; unsigned int cause; mips_get_sr( sr ); mips_get_cause( cause ); sr2 = sr & ~0xffff; mips_set_sr(sr2); printk( "Unhandled isr exception: vector 0x%02x, cause 0x%08X, sr 0x%08X\n", vector, cause, sr ); rtems_fatal_error_occurred(1); return 0; } /* userspace routine to assert either software interrupt */ int assertSoftwareInterrupt( uint32_t n ) { if( n<2 ) { uint32_t c; mips_get_cause(c); c = ((n+1) << CAUSE_IPSHIFT); mips_set_cause(c); return n; } else return -1; } #define CALL_ISR(_vector,_frame) \ do { \ if( _ISR_Vector_table[_vector] ) \ (_ISR_Vector_table[_vector])(_vector,_frame); \ else \ mips_default_isr(_vector); \ } while (0) // // Instrumentation tweaks for isr timing measurement, turning them off // via this #if will remove the code entirely from the RTEMS kernel. // #if 0 #define SET_ISR_FLAG( offset ) *((uint32_t *)(0x8001e000+offset)) = 1; #define CLR_ISR_FLAG( offset ) *((uint32_t *)(0x8001e000+offset)) = 0; #else #define SET_ISR_FLAG( offset ) #define CLR_ISR_FLAG( offset ) #endif static volatile uint32_t _ivcause, _ivsr; static uint32_t READ_CAUSE(void) { mips_get_cause( _ivcause ); _ivcause &= SR_IMASK; // mask off everything other than the interrupt bits return ((_ivcause & (_ivsr & SR_IMASK)) >> CAUSE_IPSHIFT); } // // This rather strangely coded routine enforces an interrupt priority // scheme. As it runs thru finding whichever interrupt caused it to get // here, it test for other interrupts arriving in the meantime (maybe it // occured while the vector code is executing for instance). Each new // interrupt will be served in order of its priority. In an effort to // minimize overhead, the cause register is only fetched after an // interrupt is serviced. Because of the intvect goto's, this routine // will only exit when all interrupts have been serviced and no more // have arrived, this improves interrupt latency at the cost of // increasing scheduling jitter; though scheduling jitter should only // become apparent in high interrupt load conditions. // void mips_vector_isr_handlers( CPU_Interrupt_frame *frame ) { uint32_t cshifted; /* mips_get_sr( sr ); */ _ivsr = frame->c0_sr; cshifted = READ_CAUSE(); intvect: if( cshifted & 0x3 ) { // making the software interrupt the highest priority is kind of // stupid, but it makes the bit testing lots easier. On the other // hand, these ints are infrequently used and the testing overhead // is minimal. Who knows, high-priority software ints might be // handy in some situation. /* unset both software int cause bits */ mips_set_cause( _ivcause & ~(3 << CAUSE_IPSHIFT) ); if ( cshifted & 0x01 ) /* SW[0] */ { CALL_ISR( MONGOOSEV_IRQ_SOFTWARE_1, frame ); } if ( cshifted & 0x02 ) /* SW[1] */ { CALL_ISR( MONGOOSEV_IRQ_SOFTWARE_2, frame ); } cshifted = READ_CAUSE(); } if ( cshifted & 0x04 ) /* IP[0] ==> INT0 == TIMER1 */ { SET_ISR_FLAG( 0x4 ); CALL_ISR( MONGOOSEV_IRQ_TIMER1, frame ); CLR_ISR_FLAG( 0x4 ); if( (cshifted = READ_CAUSE()) & 0x3 ) goto intvect; } if ( cshifted & 0x08 ) /* IP[1] ==> INT1 == TIMER2*/ { SET_ISR_FLAG( 0x8 ); CALL_ISR( MONGOOSEV_IRQ_TIMER2, frame ); CLR_ISR_FLAG( 0x8 ); if( (cshifted = READ_CAUSE()) & 0x7 ) goto intvect; } if ( cshifted & 0x10 ) /* IP[2] ==> INT2 */ { SET_ISR_FLAG( 0x10 ); CALL_ISR( MONGOOSEV_IRQ_INT2, frame ); CLR_ISR_FLAG( 0x10 ); if( (cshifted = READ_CAUSE()) & 0xf ) goto intvect; } if ( cshifted & 0x20 ) /* IP[3] ==> INT3 == FPU interrupt */ { SET_ISR_FLAG( 0x20 ); CALL_ISR( MONGOOSEV_IRQ_INT3, frame ); CLR_ISR_FLAG( 0x20 ); if( (cshifted = READ_CAUSE()) & 0x1f ) goto intvect; } if ( cshifted & 0x40 ) /* IP[4] ==> INT4, external interrupt */ { SET_ISR_FLAG( 0x40 ); CALL_ISR( MONGOOSEV_IRQ_INT4, frame ); CLR_ISR_FLAG( 0x40 ); if( (cshifted = READ_CAUSE()) & 0x3f ) goto intvect; } if ( cshifted & 0x80 ) /* IP[5] ==> INT5, peripheral interrupt */ { uint32_t bit; uint32_t pf_icr, pf_mask, pf_reset = 0; uint32_t i, m; pf_icr = MONGOOSEV_READ( MONGOOSEV_PERIPHERAL_FUNCTION_INTERRUPT_CAUSE_REGISTER ); /* for (bit=0, pf_mask = 1; bit < 32; bit++, pf_mask <<= 1 ) { if ( pf_icr & pf_mask ) { SET_ISR_FLAG( 0x80 + (bit*4) ); CALL_ISR( MONGOOSEV_IRQ_PERIPHERAL_BASE + bit, frame ); CLR_ISR_FLAG( 0x80 + (bit*4) ); pf_reset |= pf_mask; if( (cshifted = READ_CAUSE()) & 0xff ) break; } } */ // // iterate thru 32 bits in 4 chunks of 8 bits each. This lets us // quickly get past unasserted interrupts instead of flogging our // way thru a full 32 bits. pf_mask shifts left 8 bits at a time // to serve as a interrupt cause test mask. // for( bit=0, pf_mask = 0xff; (bit < 32 && pf_icr); (bit+=8, pf_mask <<= 8) ) { if ( pf_icr & pf_mask ) { // one or more of the 8 bits we're testing is high m = (1 << bit); // iterate thru the 8 bits, servicing any of the interrupts for(i=0; (i<8 && pf_icr); (i++, m <<= 1)) { if( pf_icr & m ) { SET_ISR_FLAG( 0x80 + ((bit + i) * 4) ); CALL_ISR( MONGOOSEV_IRQ_PERIPHERAL_BASE + bit + i, frame ); CLR_ISR_FLAG( 0x80 + ((bit + i) * 4) ); // or each serviced interrupt into our interrupt clear // mask pf_reset |= m; // xor off each int we service so we can immediately // exit once we get the last one pf_icr %= m; // if another interrupt has arrived, jump out right // away but be sure to reset all the interrupts we've // already serviced if( READ_CAUSE() & 0xff ) goto pfexit; } } } } pfexit: MONGOOSEV_WRITE( MONGOOSEV_PERIPHERAL_STATUS_REGISTER, pf_reset ); } // // this is a last ditch interrupt check, if an interrupt arrives // after this step, servicing it will incur the entire interrupt // overhead cost. // if( (cshifted = READ_CAUSE()) & 0xff ) goto intvect; } // eof