summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/mips/mongoosev/vectorisrs/vectorisrs.c
blob: d0fc894f556e1587559a758d4f1d456715278ad4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/*
 *  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 <rtems.h>
#include <stdlib.h>
#include <libcpu/mongoose-v.h>

#include <rtems/mips/iregdef.h>
#include <rtems/mips/idtcpu.h>


#include <rtems/bspIo.h>  /* 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;
}