/* * 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.org/license/LICENSE. */ #include #include #include #include #include #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; }