summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c
blob: e6db76264511d5df4eb2b7f1246c96a94e725524 (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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
/**
 *@file interrupt.c
 *
 *@brief
 * - This file implements interrupt dispatcher. Most of the code is taken from
 *  the 533 implementation for blackfin. Since 52X supports 56 line and 2 ISR
 *  registers some portion is written twice.
 *
 * Target:   TLL6527v1-0
 * Compiler:
 *
 * COPYRIGHT (c) 2010 by ECE Northeastern University.
 *
 * 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
 *
 * @author Rohan Kangralkar, ECE, Northeastern University
 *         (kangralkar.r@husky.neu.edu)
 *
 * LastChange:
 */

#include <rtems.h>
#include <rtems/libio.h>

#include <bsp.h>
#include <libcpu/cecRegs.h>
#include <libcpu/sicRegs.h>
#include "interrupt.h"

#define SIC_IAR_COUNT_SET0		  4
#define SIC_IAR_BASE_ADDRESS_0  0xFFC00150

/**
 * There are two implementations for the interrupt handler.
 * 1. INTERRUPT_USE_TABLE: uses tables for finding the right ISR.
 * 2. Uses link list to find the user ISR.
 *
 *
 * 1. INTERRUPT_USE_TABLE
 * Space requirement:
 *  - Array to hold CEC masks size: CEC_INTERRUPT_COUNT(9)*(2*int).9*2*4= 72B
 *  - Array to hold isr function pointers IRQ_MAX(56)*sizeof(bfin_isr_t)= 896B
 *  - Array for bit twidlling 32 bytes.
 *  - Global Mask 8 bytes.
 *  - Total = 1008 Bytes Aprox
 *
 * Time requirements
 *    The worst case time is about the same for jumping to the user ISR. With a
 *    variance of one conditional statement.
 *
 * 2. Using link list.
 * Space requirement:
 *  - Array to hold CEC mask CEC_INTERRUPT_COUNT(9)*(sizeof(vectors)).
 *                                                                 9*3*4= 108B
 *  - Array to hold isr IRQ_MAX(56)*sizeof(bfin_isr_t) The structure has
 *    additional pointers                                         56*7*4=1568B
 *  - Global Mask 8 bytes.
 *    Total = 1684.
 * Time requirements
 *    In the worst case all the lines can be on one CEC line to 56 entries have
 *    to be traversed to find the right user ISR.
 *    But this implementation has benefit of being flexible, Providing
 *    additional user assigned priority. and may consume less space
 *    if all devices are not supported.
 */

/**
 * TODO: To place the dispatcher routine code in L1.
 */

#if INTERRUPT_USE_TABLE


/******************************************************************************
 * Static variables
 *****************************************************************************/
/**
 * @var sic_isr0_mask
 * @brief copy of the mask of SIC ISR. The SIC ISR is cleared by the device
 * the relevant SIC_ISRx bit is not cleared unless the interrupt
 * service routine clears the mechanism that generated interrupt
 */
static uint32_t sic_isr0_mask = 0;

/**
 * @var sic_isr0_mask
 * @brief copy of the mask of SIC ISR. The SIC ISR is cleared by the device
 * the relevant SIC_ISRx bit is not cleared unless the interrupt
 * service routine clears the mechanism that generated interrupt
 */
static uint32_t sic_isr1_mask = 0;


/**
 * @var sic_isr
 * @brief An array of sic register mask for each of the 16 core interrupt lines
 */
static struct {
  uint32_t mask0;
  uint32_t mask1;
} vectors[CEC_INTERRUPT_COUNT];

/**
 * @var ivt
 * @brief Contains a table of ISR and arguments. The ISR jumps directly to
 * these ISR.
 */
static bfin_isr_t ivt[IRQ_MAX];

/**
 * http://graphics.stanford.edu/~seander/bithacks.html for more details
 */
static const char clz_table[32] =
{
    0, 31, 9, 30, 3, 8, 18, 29, 2, 5, 7, 14, 12, 17,
    22, 28, 1, 10, 4, 19, 6, 15, 13, 23, 11, 20, 16,
    24, 21, 25, 26, 27
};

/**
 * finds the first bit set from the left. look at
 * http://graphics.stanford.edu/~seander/bithacks.html for more details
 * @param n
 * @return
 */
static unsigned long clz(unsigned long n)
{
  unsigned long c = 0x7dcd629;       /* magic constant... */

  n |= (n >> 1);
  n |= (n >> 2);
  n |= (n >> 4);
  n |= (n >> 8);
  n |= (n >> 16);
  if (n == 0) return 32;
  n = c + (c * n);
  return 31 - clz_table[n >> 27];       /* For little endian    */
}



/**
 * Centralized Interrupt dispatcher routine. This routine dispatches interrupts
 * to the user ISR. The priority is according to the blackfin SIC.
 * The first level of priority is handled in the hardware at the core event
 * controller. The second level of interrupt is handled according to the line
 * number that goes in to the SIC.
 * * SIC_0 has higher priority than SIC 1.
 * * Inside the SIC the priority is assigned according to the line number.
 *   Lower the line number higher the priority.
 *
 *   In order to change the interrupt priority we may
 *   1. change the SIC IAR registers or
 *   2. Assign priority and extract it inside this function and call the ISR
 *   according tot the priority.
 *
 * @param vector IVG number.
 * @return
 */
static rtems_isr interruptHandler(rtems_vector_number vector) {
  uint32_t mask = 0;
  int id = 0;
  /**
   * Enable for debugging
   *
   * static volatile uint32_t spurious_sic0    = 0;
   * static volatile uint32_t spurious_source  = 0;
   * static volatile uint32_t spurious_sic1    = 0;
   */

  /**
   * Extract the vector number relative to the SIC start line
   */
  vector -= CEC_INTERRUPT_BASE_VECTOR;

  /**
   * Check for bounds
   */
  if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {

    /**
     * Extract information and execute ISR from SIC 0
     */
    mask = *(uint32_t volatile *) SIC_ISR &
        *(uint32_t volatile *) SIC_IMASK & vectors[vector].mask0;
    id      = clz(mask);
    if ( SIC_ISR0_MAX > id ) {
      /** Parameter check */
      if( NULL != ivt[id].pFunc) {
        /** Call the relevant function with argument */
        ivt[id].pFunc( ivt[id].pArg );
      } else {
        /**
         * spurious interrupt we should not be getting this
         * spurious_sic0++;
         * spurious_source = id;
         */
      }
    } else {
      /**
       * we look at SIC 1
       */
    }


    /**
     * Extract information and execute ISR from SIC 1
     */
    mask    = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) &
        *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) &
        vectors[vector].mask1;
    id      = clz(mask)+SIC_ISR0_MAX;
    if ( IRQ_MAX > id ) {
      /** Parameter Check */
      if( NULL != ivt[id].pFunc ) {
        /** Call the relevant function with argument */
        ivt[id].pFunc( ivt[id].pArg );
      } else {
        /**
         * spurious interrupt we should not be getting this
         *
         * spurious_sic1++;
         * spurious_source = id;
         */
      }
    } else {
      /**
       * we continue
       */
    }

  }
}



/**
 * This routine registers a new ISR. It will write a new entry to the IVT table
 * @param isr contains a callback function and source
 * @return rtems status code
 */
rtems_status_code bfin_interrupt_register(bfin_isr_t *isr) {
  rtems_interrupt_level isrLevel;
  int               id        = 0;
  int               position  = 0;

  /**
   * Sanity Check
   */
  if ( NULL == isr ){
    return RTEMS_UNSATISFIED;
  }

  /**
   * Sanity check. The register function should at least provide callback func
   */
  if ( NULL == isr->pFunc ) {
    return RTEMS_UNSATISFIED;
  }

  id = isr->source;

  /**
   * Parameter Check. We already have a function registered here. First
   * unregister and then a new function can be allocated.
   */
  if ( NULL != ivt[id].pFunc ) {
    return RTEMS_UNSATISFIED;
  }

  rtems_interrupt_disable(isrLevel);
  /**
   * Assign the new function pointer to the ISR Dispatcher
   * */
  ivt[id].pFunc    = isr->pFunc;
  ivt[id].pArg     = isr->pArg;


  /** find out which isr mask has to be set to enable the interrupt */
  if ( SIC_ISR0_MAX > id ) {
    sic_isr0_mask |= 0x1<<id;
    *(uint32_t volatile *) SIC_IMASK  |= 0x1<<id;
  } else {
    position = id - SIC_ISR0_MAX;
    sic_isr1_mask |= 0x1<<position;
    *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH)  |= 0x1<<position;
  }

  rtems_interrupt_enable(isrLevel);

  return RTEMS_SUCCESSFUL;
}


/**
 * This function unregisters a registered interrupt handler.
 * @param isr
 */
rtems_status_code bfin_interrupt_unregister(bfin_isr_t *isr) {
  rtems_interrupt_level isrLevel;
  int               id        = 0;
  int               position  = 0;

  /**
   * Sanity Check
   */
  if ( NULL == isr ){
    return RTEMS_UNSATISFIED;
  }

  id = isr->source;

  rtems_interrupt_disable(isrLevel);
  /**
   * Assign the new function pointer to the ISR Dispatcher
   * */
  ivt[id].pFunc    = NULL;
  ivt[id].pArg     = NULL;


  /** find out which isr mask has to be set to enable the interrupt */
  if ( SIC_ISR0_MAX > id ) {
    sic_isr0_mask &= ~(0x1<<id);
    *(uint32_t volatile *) SIC_IMASK  &= ~(0x1<<id);
  } else {
    position = id - SIC_ISR0_MAX;
    sic_isr1_mask &= ~(0x1<<position);
    *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH)  &= ~(0x1<<position);
  }

  rtems_interrupt_enable(isrLevel);

  return RTEMS_SUCCESSFUL;
}




/**
 * blackfin interrupt initialization routine. It initializes the bfin ISR
 * dispatcher. It will also create SIC CEC map which will be used for
 * identifying the ISR.
 */
void bfin_interrupt_init(void) {
  int source;
  int vector;
  uint32_t r;
  int i;
  int j;

  *(uint32_t volatile *) SIC_IMASK = 0;
  *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) = 0;

  memset(vectors, 0, sizeof(vectors));
  /* build mask0 showing what SIC sources drive each CEC vector */
  source = 0;

  /**
   * The bf52x has 8 IAR registers but they do not have a constant pitch.
   *
   */
  for (i = 0; i < SIC_IAR_COUNT; i++) {
    if ( SIC_IAR_COUNT_SET0 > i ) {
      r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS + i * SIC_IAR_PITCH);
    } else {
      r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS_0 +
          ((i-SIC_IAR_COUNT_SET0) * SIC_IAR_PITCH));
    }

    for (j = 0; j < 8; j++) {
      vector = r & 0x0f;
      if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
        /* install our local handler */
        if (vectors[vector].mask0 == 0 && vectors[vector].mask1 == 0){
          set_vector(interruptHandler, vector + CEC_INTERRUPT_BASE_VECTOR, 1);
        }
        if ( SIC_ISR0_MAX > source ) {
          vectors[vector].mask0 |= (1 << source);
        } else {
          vectors[vector].mask1 |= (1 << (source - SIC_ISR0_MAX));
        }
      }
      r >>= 4;
      source++;
    }
  }
}





#else

static struct {
  uint32_t mask0;
  uint32_t mask1;
  bfin_isr_t *head;
} vectors[CEC_INTERRUPT_COUNT];

static uint32_t globalMask0;
static uint32_t globalMask1;

static rtems_isr interruptHandler(rtems_vector_number vector) {
  bfin_isr_t *isr = NULL;
  uint32_t sourceMask0 = 0;
  uint32_t sourceMask1 = 0;
  rtems_interrupt_level isrLevel;

  rtems_interrupt_disable(isrLevel);
  vector -= CEC_INTERRUPT_BASE_VECTOR;
  if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
    isr = vectors[vector].head;
    sourceMask0 = *(uint32_t volatile *) SIC_ISR &
        *(uint32_t volatile *) SIC_IMASK;
    sourceMask1 = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) &
        *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH);
    while (isr) {
      if ((sourceMask0 & isr->mask0) || (sourceMask1 & isr->mask1)) {
        isr->isr(isr->_arg);
        sourceMask0 = *(uint32_t volatile *) SIC_ISR &
            *(uint32_t volatile *) SIC_IMASK;
        sourceMask1 = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) &
            *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH);
      }
      isr = isr->next;
    }
  }
  rtems_interrupt_enable(isrLevel);
}

/**
 * Initializes the interrupt module
 */
void bfin_interrupt_init(void) {
  int source;
  int vector;
  uint32_t r;
  int i;
  int j;

  globalMask0 = ~(uint32_t) 0;
  globalMask1 = ~(uint32_t) 0;
  *(uint32_t volatile *) SIC_IMASK = 0;
  *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) = 0;

  memset(vectors, 0, sizeof(vectors));
  /* build mask0 showing what SIC sources drive each CEC vector */
  source = 0;

  /**
   * The bf52x has 8 IAR registers but they do not have a constant pitch.
   *
   */
  for (i = 0; i < SIC_IAR_COUNT; i++) {
    if ( SIC_IAR_COUNT_SET0 > i ) {
      r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS + i * SIC_IAR_PITCH);
    } else {
      r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS_0 +
          ((i-SIC_IAR_COUNT_SET0) * SIC_IAR_PITCH));
    }
    for (j = 0; j < 8; j++) {
      vector = r & 0x0f;
      if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
        /* install our local handler */
        if (vectors[vector].mask0 == 0 && vectors[vector].mask1 == 0){
          set_vector(interruptHandler, vector + CEC_INTERRUPT_BASE_VECTOR, 1);
        }
        if ( SIC_ISR0_MAX > source ) {
          vectors[vector].mask0 |= (1 << source);
        } else {
          vectors[vector].mask1 |= (1 << (source - SIC_ISR0_MAX));
        }
      }
      r >>= 4;
      source++;
    }
  }
}

/* modify SIC_IMASK based on ISR list for a particular CEC vector */
static void setMask(uint32_t vector) {
  bfin_isr_t *isr = NULL;
  uint32_t mask = 0;
  uint32_t r    = 0;

  mask = 0;
  isr = vectors[vector].head;
  while (isr) {
    mask |= isr->mask0;
    isr = isr->next;
  }
  r = *(uint32_t volatile *) SIC_IMASK;
  r &= ~vectors[vector].mask0;
  r |= mask;
  r &= globalMask0;
  *(uint32_t volatile *) SIC_IMASK = r;


  mask = 0;
  isr = vectors[vector].head;
  while (isr) {
    mask |= isr->mask1;
    isr = isr->next;
  }
  r = *(uint32_t volatile *) (SIC_IMASK+ SIC_IMASK_PITCH);
  r &= ~vectors[vector].mask1;
  r |= mask;
  r &= globalMask1;
  *(uint32_t volatile *) (SIC_IMASK+ SIC_IMASK_PITCH) = r;
}

/* add an ISR to the list for whichever vector it belongs to */
rtems_status_code bfin_interrupt_register(bfin_isr_t *isr) {
  bfin_isr_t *walk;
  rtems_interrupt_level isrLevel;

  /* find the appropriate vector */
  for (isr->vector = 0; isr->vector < CEC_INTERRUPT_COUNT; isr->vector++)
    if ( (vectors[isr->vector].mask0 & (1 << isr->source) ) || \
        (vectors[isr->vector].mask1 & (1 << (isr->source - SIC_ISR0_MAX)) ))
      break;
  if (isr->vector < CEC_INTERRUPT_COUNT) {
    isr->next = NULL;
    isr->mask0 = 0;
    isr->mask1 = 0;
    rtems_interrupt_disable(isrLevel);
    /* find the current end of the list */
    walk = vectors[isr->vector].head;
    while (walk && walk->next)
      walk = walk->next;
    /* append new isr to list */
    if (walk)
      walk->next = isr;
    else
      vectors[isr->vector].head = isr;
    rtems_interrupt_enable(isrLevel);
  } else
    /* we failed, but make vector a legal value so other calls into
	       this module with this isr descriptor won't do anything bad */
    isr->vector = 0;
  return RTEMS_SUCCESSFUL;
}

rtems_status_code bfin_interrupt_unregister(bfin_isr_t *isr) {
  bfin_isr_t *walk, *prev;
  rtems_interrupt_level isrLevel;

  rtems_interrupt_disable(isrLevel);
  walk = vectors[isr->vector].head;
  prev = NULL;
  /* find this isr in our list */
  while (walk && walk != isr) {
    prev = walk;
    walk = walk->next;
  }
  if (walk) {
    /* if found, remove it */
    if (prev)
      prev->next = walk->next;
    else
      vectors[isr->vector].head = walk->next;
    /* fix up SIC_IMASK if necessary */
    setMask(isr->vector);
  }
  rtems_interrupt_enable(isrLevel);
  return RTEMS_SUCCESSFUL;
}

void bfin_interrupt_enable(bfin_isr_t *isr, bool enable) {
  rtems_interrupt_level isrLevel;

  rtems_interrupt_disable(isrLevel);
  if ( SIC_ISR0_MAX > isr->source ) {
    isr->mask0 = enable ? (1 << isr->source) : 0;
    *(uint32_t volatile *) SIC_IMASK |= isr->mask0;
  }  else {
    isr->mask1 = enable ? (1 << (isr->source - SIC_ISR0_MAX)) : 0;
    *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) |= isr->mask1;
  }

  //setMask(isr->vector);
  rtems_interrupt_enable(isrLevel);
}

void bfin_interrupt_enable_all(int source, bool enable) {
  rtems_interrupt_level isrLevel;
  int vector;
  bfin_isr_t *walk;

  for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++)
    if ( (vectors[vector].mask0 & (1 << source) ) || \
        (vectors[vector].mask1 & (1 << (source - SIC_ISR0_MAX)) ))
      break;
  if (vector < CEC_INTERRUPT_COUNT) {
    rtems_interrupt_disable(isrLevel);
    walk = vectors[vector].head;
    while (walk) {
      walk->mask0 = enable ? (1 << source) : 0;
      walk = walk->next;
    }

    walk = vectors[vector].head;
    while (walk) {
      walk->mask1 = enable ? (1 << (source - SIC_ISR0_MAX)) : 0;
      walk = walk->next;
    }
    setMask(vector);
    rtems_interrupt_enable(isrLevel);
  }
}

void bfin_interrupt_enable_global(int source, bool enable) {
  int vector;
  rtems_interrupt_level isrLevel;

  for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++)
    if ( (vectors[vector].mask0 & (1 << source) ) || \
        (vectors[vector].mask1 & (1 << (source - SIC_ISR0_MAX)) ))
      break;
  if (vector < CEC_INTERRUPT_COUNT) {
    rtems_interrupt_disable(isrLevel);
    if ( SIC_ISR0_MAX > source ) {
      if (enable)
        globalMask0 |= 1 << source;
      else
        globalMask0 &= ~(1 << source);
    }else {
      if (enable)
        globalMask1 |= 1 << (source - SIC_ISR0_MAX);
      else
        globalMask1 &= ~(1 << (source - SIC_ISR0_MAX));
    }
    setMask(vector);
    rtems_interrupt_enable(isrLevel);
  }
}

#endif