source: rtems/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c @ c499856

4.11
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on Mar 20, 2014 at 9:10:47 PM

Change all references of rtems.com to rtems.org.

  • Property mode set to 100644
File size: 16.9 KB
Line 
1/**
2 *@file interrupt.c
3 *
4 *@brief
5 * - This file implements interrupt dispatcher. Most of the code is taken from
6 *  the 533 implementation for blackfin. Since 52X supports 56 line and 2 ISR
7 *  registers some portion is written twice.
8 *
9 * Target:   TLL6527v1-0
10 * Compiler:
11 *
12 * COPYRIGHT (c) 2010 by ECE Northeastern University.
13 *
14 * The license and distribution terms for this file may be
15 * found in the file LICENSE in this distribution or at
16 * http://www.rtems.org/license
17 *
18 * @author Rohan Kangralkar, ECE, Northeastern University
19 *         (kangralkar.r@husky.neu.edu)
20 *
21 * LastChange:
22 */
23
24#include <rtems.h>
25#include <rtems/libio.h>
26
27#include <bsp.h>
28#include <libcpu/cecRegs.h>
29#include <libcpu/sicRegs.h>
30#include "interrupt.h"
31
32#define SIC_IAR_COUNT_SET0                4
33#define SIC_IAR_BASE_ADDRESS_0  0xFFC00150
34
35/**
36 * There are two implementations for the interrupt handler.
37 * 1. INTERRUPT_USE_TABLE: uses tables for finding the right ISR.
38 * 2. Uses link list to find the user ISR.
39 *
40 *
41 * 1. INTERRUPT_USE_TABLE
42 * Space requirement:
43 *  - Array to hold CEC masks size: CEC_INTERRUPT_COUNT(9)*(2*int).9*2*4= 72B
44 *  - Array to hold isr function pointers IRQ_MAX(56)*sizeof(bfin_isr_t)= 896B
45 *  - Array for bit twidlling 32 bytes.
46 *  - Global Mask 8 bytes.
47 *  - Total = 1008 Bytes Aprox
48 *
49 * Time requirements
50 *    The worst case time is about the same for jumping to the user ISR. With a
51 *    variance of one conditional statement.
52 *
53 * 2. Using link list.
54 * Space requirement:
55 *  - Array to hold CEC mask CEC_INTERRUPT_COUNT(9)*(sizeof(vectors)).
56 *                                                                 9*3*4= 108B
57 *  - Array to hold isr IRQ_MAX(56)*sizeof(bfin_isr_t) The structure has
58 *    additional pointers                                         56*7*4=1568B
59 *  - Global Mask 8 bytes.
60 *    Total = 1684.
61 * Time requirements
62 *    In the worst case all the lines can be on one CEC line to 56 entries have
63 *    to be traversed to find the right user ISR.
64 *    But this implementation has benefit of being flexible, Providing
65 *    additional user assigned priority. and may consume less space
66 *    if all devices are not supported.
67 */
68
69/**
70 * TODO: To place the dispatcher routine code in L1.
71 */
72
73#if INTERRUPT_USE_TABLE
74
75
76/******************************************************************************
77 * Static variables
78 *****************************************************************************/
79/**
80 * @var sic_isr0_mask
81 * @brief copy of the mask of SIC ISR. The SIC ISR is cleared by the device
82 * the relevant SIC_ISRx bit is not cleared unless the interrupt
83 * service routine clears the mechanism that generated interrupt
84 */
85static uint32_t sic_isr0_mask = 0;
86
87/**
88 * @var sic_isr0_mask
89 * @brief copy of the mask of SIC ISR. The SIC ISR is cleared by the device
90 * the relevant SIC_ISRx bit is not cleared unless the interrupt
91 * service routine clears the mechanism that generated interrupt
92 */
93static uint32_t sic_isr1_mask = 0;
94
95
96/**
97 * @var sic_isr
98 * @brief An array of sic register mask for each of the 16 core interrupt lines
99 */
100static struct {
101  uint32_t mask0;
102  uint32_t mask1;
103} vectors[CEC_INTERRUPT_COUNT];
104
105/**
106 * @var ivt
107 * @brief Contains a table of ISR and arguments. The ISR jumps directly to
108 * these ISR.
109 */
110static bfin_isr_t ivt[IRQ_MAX];
111
112/**
113 * http://graphics.stanford.edu/~seander/bithacks.html for more details
114 */
115static const char clz_table[32] =
116{
117    0, 31, 9, 30, 3, 8, 18, 29, 2, 5, 7, 14, 12, 17,
118    22, 28, 1, 10, 4, 19, 6, 15, 13, 23, 11, 20, 16,
119    24, 21, 25, 26, 27
120};
121
122/**
123 * finds the first bit set from the left. look at
124 * http://graphics.stanford.edu/~seander/bithacks.html for more details
125 * @param n
126 * @return
127 */
128static unsigned long clz(unsigned long n)
129{
130  unsigned long c = 0x7dcd629;       /* magic constant... */
131
132  n |= (n >> 1);
133  n |= (n >> 2);
134  n |= (n >> 4);
135  n |= (n >> 8);
136  n |= (n >> 16);
137  if (n == 0) return 32;
138  n = c + (c * n);
139  return 31 - clz_table[n >> 27];       /* For little endian    */
140}
141
142
143
144/**
145 * Centralized Interrupt dispatcher routine. This routine dispatches interrupts
146 * to the user ISR. The priority is according to the blackfin SIC.
147 * The first level of priority is handled in the hardware at the core event
148 * controller. The second level of interrupt is handled according to the line
149 * number that goes in to the SIC.
150 * * SIC_0 has higher priority than SIC 1.
151 * * Inside the SIC the priority is assigned according to the line number.
152 *   Lower the line number higher the priority.
153 *
154 *   In order to change the interrupt priority we may
155 *   1. change the SIC IAR registers or
156 *   2. Assign priority and extract it inside this function and call the ISR
157 *   according tot the priority.
158 *
159 * @param vector IVG number.
160 * @return
161 */
162static rtems_isr interruptHandler(rtems_vector_number vector) {
163  uint32_t mask = 0;
164  int id = 0;
165  /**
166   * Enable for debugging
167   *
168   * static volatile uint32_t spurious_sic0    = 0;
169   * static volatile uint32_t spurious_source  = 0;
170   * static volatile uint32_t spurious_sic1    = 0;
171   */
172
173  /**
174   * Extract the vector number relative to the SIC start line
175   */
176  vector -= CEC_INTERRUPT_BASE_VECTOR;
177
178  /**
179   * Check for bounds
180   */
181  if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
182
183    /**
184     * Extract information and execute ISR from SIC 0
185     */
186    mask = *(uint32_t volatile *) SIC_ISR &
187        *(uint32_t volatile *) SIC_IMASK & vectors[vector].mask0;
188    id      = clz(mask);
189    if ( SIC_ISR0_MAX > id ) {
190      /** Parameter check */
191      if( NULL != ivt[id].pFunc) {
192        /** Call the relevant function with argument */
193        ivt[id].pFunc( ivt[id].pArg );
194      } else {
195        /**
196         * spurious interrupt we should not be getting this
197         * spurious_sic0++;
198         * spurious_source = id;
199         */
200      }
201    } else {
202      /**
203       * we look at SIC 1
204       */
205    }
206
207
208    /**
209     * Extract information and execute ISR from SIC 1
210     */
211    mask    = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) &
212        *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) &
213        vectors[vector].mask1;
214    id      = clz(mask)+SIC_ISR0_MAX;
215    if ( IRQ_MAX > id ) {
216      /** Parameter Check */
217      if( NULL != ivt[id].pFunc ) {
218        /** Call the relevant function with argument */
219        ivt[id].pFunc( ivt[id].pArg );
220      } else {
221        /**
222         * spurious interrupt we should not be getting this
223         *
224         * spurious_sic1++;
225         * spurious_source = id;
226         */
227      }
228    } else {
229      /**
230       * we continue
231       */
232    }
233
234  }
235}
236
237
238
239/**
240 * This routine registers a new ISR. It will write a new entry to the IVT table
241 * @param isr contains a callback function and source
242 * @return rtems status code
243 */
244rtems_status_code bfin_interrupt_register(bfin_isr_t *isr) {
245  rtems_interrupt_level isrLevel;
246  int               id        = 0;
247  int               position  = 0;
248
249  /**
250   * Sanity Check
251   */
252  if ( NULL == isr ){
253    return RTEMS_UNSATISFIED;
254  }
255
256  /**
257   * Sanity check. The register function should at least provide callback func
258   */
259  if ( NULL == isr->pFunc ) {
260    return RTEMS_UNSATISFIED;
261  }
262
263  id = isr->source;
264
265  /**
266   * Parameter Check. We already have a function registered here. First
267   * unregister and then a new function can be allocated.
268   */
269  if ( NULL != ivt[id].pFunc ) {
270    return RTEMS_UNSATISFIED;
271  }
272
273  rtems_interrupt_disable(isrLevel);
274  /**
275   * Assign the new function pointer to the ISR Dispatcher
276   * */
277  ivt[id].pFunc    = isr->pFunc;
278  ivt[id].pArg     = isr->pArg;
279
280
281  /** find out which isr mask has to be set to enable the interrupt */
282  if ( SIC_ISR0_MAX > id ) {
283    sic_isr0_mask |= 0x1<<id;
284    *(uint32_t volatile *) SIC_IMASK  |= 0x1<<id;
285  } else {
286    position = id - SIC_ISR0_MAX;
287    sic_isr1_mask |= 0x1<<position;
288    *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH)  |= 0x1<<position;
289  }
290
291  rtems_interrupt_enable(isrLevel);
292
293  return RTEMS_SUCCESSFUL;
294}
295
296
297/**
298 * This function unregisters a registered interrupt handler.
299 * @param isr
300 */
301rtems_status_code bfin_interrupt_unregister(bfin_isr_t *isr) {
302  rtems_interrupt_level isrLevel;
303  int               id        = 0;
304  int               position  = 0;
305
306  /**
307   * Sanity Check
308   */
309  if ( NULL == isr ){
310    return RTEMS_UNSATISFIED;
311  }
312
313  id = isr->source;
314
315  rtems_interrupt_disable(isrLevel);
316  /**
317   * Assign the new function pointer to the ISR Dispatcher
318   * */
319  ivt[id].pFunc    = NULL;
320  ivt[id].pArg     = NULL;
321
322
323  /** find out which isr mask has to be set to enable the interrupt */
324  if ( SIC_ISR0_MAX > id ) {
325    sic_isr0_mask &= ~(0x1<<id);
326    *(uint32_t volatile *) SIC_IMASK  &= ~(0x1<<id);
327  } else {
328    position = id - SIC_ISR0_MAX;
329    sic_isr1_mask &= ~(0x1<<position);
330    *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH)  &= ~(0x1<<position);
331  }
332
333  rtems_interrupt_enable(isrLevel);
334
335  return RTEMS_SUCCESSFUL;
336}
337
338
339
340
341/**
342 * blackfin interrupt initialization routine. It initializes the bfin ISR
343 * dispatcher. It will also create SIC CEC map which will be used for
344 * identifying the ISR.
345 */
346void bfin_interrupt_init(void) {
347  int source;
348  int vector;
349  uint32_t r;
350  int i;
351  int j;
352
353  *(uint32_t volatile *) SIC_IMASK = 0;
354  *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) = 0;
355
356  memset(vectors, 0, sizeof(vectors));
357  /* build mask0 showing what SIC sources drive each CEC vector */
358  source = 0;
359
360  /**
361   * The bf52x has 8 IAR registers but they do not have a constant pitch.
362   *
363   */
364  for (i = 0; i < SIC_IAR_COUNT; i++) {
365    if ( SIC_IAR_COUNT_SET0 > i ) {
366      r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS + i * SIC_IAR_PITCH);
367    } else {
368      r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS_0 +
369          ((i-SIC_IAR_COUNT_SET0) * SIC_IAR_PITCH));
370    }
371
372    for (j = 0; j < 8; j++) {
373      vector = r & 0x0f;
374      if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
375        /* install our local handler */
376        if (vectors[vector].mask0 == 0 && vectors[vector].mask1 == 0){
377          set_vector(interruptHandler, vector + CEC_INTERRUPT_BASE_VECTOR, 1);
378        }
379        if ( SIC_ISR0_MAX > source ) {
380          vectors[vector].mask0 |= (1 << source);
381        } else {
382          vectors[vector].mask1 |= (1 << (source - SIC_ISR0_MAX));
383        }
384      }
385      r >>= 4;
386      source++;
387    }
388  }
389}
390
391
392
393
394
395#else
396
397static struct {
398  uint32_t mask0;
399  uint32_t mask1;
400  bfin_isr_t *head;
401} vectors[CEC_INTERRUPT_COUNT];
402
403static uint32_t globalMask0;
404static uint32_t globalMask1;
405
406static rtems_isr interruptHandler(rtems_vector_number vector) {
407  bfin_isr_t *isr = NULL;
408  uint32_t sourceMask0 = 0;
409  uint32_t sourceMask1 = 0;
410  rtems_interrupt_level isrLevel;
411
412  rtems_interrupt_disable(isrLevel);
413  vector -= CEC_INTERRUPT_BASE_VECTOR;
414  if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
415    isr = vectors[vector].head;
416    sourceMask0 = *(uint32_t volatile *) SIC_ISR &
417        *(uint32_t volatile *) SIC_IMASK;
418    sourceMask1 = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) &
419        *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH);
420    while (isr) {
421      if ((sourceMask0 & isr->mask0) || (sourceMask1 & isr->mask1)) {
422        isr->isr(isr->_arg);
423        sourceMask0 = *(uint32_t volatile *) SIC_ISR &
424            *(uint32_t volatile *) SIC_IMASK;
425        sourceMask1 = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) &
426            *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH);
427      }
428      isr = isr->next;
429    }
430  }
431  rtems_interrupt_enable(isrLevel);
432}
433
434/**
435 * Initializes the interrupt module
436 */
437void bfin_interrupt_init(void) {
438  int source;
439  int vector;
440  uint32_t r;
441  int i;
442  int j;
443
444  globalMask0 = ~(uint32_t) 0;
445  globalMask1 = ~(uint32_t) 0;
446  *(uint32_t volatile *) SIC_IMASK = 0;
447  *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) = 0;
448
449  memset(vectors, 0, sizeof(vectors));
450  /* build mask0 showing what SIC sources drive each CEC vector */
451  source = 0;
452
453  /**
454   * The bf52x has 8 IAR registers but they do not have a constant pitch.
455   *
456   */
457  for (i = 0; i < SIC_IAR_COUNT; i++) {
458    if ( SIC_IAR_COUNT_SET0 > i ) {
459      r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS + i * SIC_IAR_PITCH);
460    } else {
461      r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS_0 +
462          ((i-SIC_IAR_COUNT_SET0) * SIC_IAR_PITCH));
463    }
464    for (j = 0; j < 8; j++) {
465      vector = r & 0x0f;
466      if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
467        /* install our local handler */
468        if (vectors[vector].mask0 == 0 && vectors[vector].mask1 == 0){
469          set_vector(interruptHandler, vector + CEC_INTERRUPT_BASE_VECTOR, 1);
470        }
471        if ( SIC_ISR0_MAX > source ) {
472          vectors[vector].mask0 |= (1 << source);
473        } else {
474          vectors[vector].mask1 |= (1 << (source - SIC_ISR0_MAX));
475        }
476      }
477      r >>= 4;
478      source++;
479    }
480  }
481}
482
483/* modify SIC_IMASK based on ISR list for a particular CEC vector */
484static void setMask(uint32_t vector) {
485  bfin_isr_t *isr = NULL;
486  uint32_t mask = 0;
487  uint32_t r    = 0;
488
489  mask = 0;
490  isr = vectors[vector].head;
491  while (isr) {
492    mask |= isr->mask0;
493    isr = isr->next;
494  }
495  r = *(uint32_t volatile *) SIC_IMASK;
496  r &= ~vectors[vector].mask0;
497  r |= mask;
498  r &= globalMask0;
499  *(uint32_t volatile *) SIC_IMASK = r;
500
501
502  mask = 0;
503  isr = vectors[vector].head;
504  while (isr) {
505    mask |= isr->mask1;
506    isr = isr->next;
507  }
508  r = *(uint32_t volatile *) (SIC_IMASK+ SIC_IMASK_PITCH);
509  r &= ~vectors[vector].mask1;
510  r |= mask;
511  r &= globalMask1;
512  *(uint32_t volatile *) (SIC_IMASK+ SIC_IMASK_PITCH) = r;
513}
514
515/* add an ISR to the list for whichever vector it belongs to */
516rtems_status_code bfin_interrupt_register(bfin_isr_t *isr) {
517  bfin_isr_t *walk;
518  rtems_interrupt_level isrLevel;
519
520  /* find the appropriate vector */
521  for (isr->vector = 0; isr->vector < CEC_INTERRUPT_COUNT; isr->vector++)
522    if ( (vectors[isr->vector].mask0 & (1 << isr->source) ) || \
523        (vectors[isr->vector].mask1 & (1 << (isr->source - SIC_ISR0_MAX)) ))
524      break;
525  if (isr->vector < CEC_INTERRUPT_COUNT) {
526    isr->next = NULL;
527    isr->mask0 = 0;
528    isr->mask1 = 0;
529    rtems_interrupt_disable(isrLevel);
530    /* find the current end of the list */
531    walk = vectors[isr->vector].head;
532    while (walk && walk->next)
533      walk = walk->next;
534    /* append new isr to list */
535    if (walk)
536      walk->next = isr;
537    else
538      vectors[isr->vector].head = isr;
539    rtems_interrupt_enable(isrLevel);
540  } else
541    /* we failed, but make vector a legal value so other calls into
542               this module with this isr descriptor won't do anything bad */
543    isr->vector = 0;
544  return RTEMS_SUCCESSFUL;
545}
546
547rtems_status_code bfin_interrupt_unregister(bfin_isr_t *isr) {
548  bfin_isr_t *walk, *prev;
549  rtems_interrupt_level isrLevel;
550
551  rtems_interrupt_disable(isrLevel);
552  walk = vectors[isr->vector].head;
553  prev = NULL;
554  /* find this isr in our list */
555  while (walk && walk != isr) {
556    prev = walk;
557    walk = walk->next;
558  }
559  if (walk) {
560    /* if found, remove it */
561    if (prev)
562      prev->next = walk->next;
563    else
564      vectors[isr->vector].head = walk->next;
565    /* fix up SIC_IMASK if necessary */
566    setMask(isr->vector);
567  }
568  rtems_interrupt_enable(isrLevel);
569  return RTEMS_SUCCESSFUL;
570}
571
572void bfin_interrupt_enable(bfin_isr_t *isr, bool enable) {
573  rtems_interrupt_level isrLevel;
574
575  rtems_interrupt_disable(isrLevel);
576  if ( SIC_ISR0_MAX > isr->source ) {
577    isr->mask0 = enable ? (1 << isr->source) : 0;
578    *(uint32_t volatile *) SIC_IMASK |= isr->mask0;
579  }  else {
580    isr->mask1 = enable ? (1 << (isr->source - SIC_ISR0_MAX)) : 0;
581    *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) |= isr->mask1;
582  }
583
584  //setMask(isr->vector);
585  rtems_interrupt_enable(isrLevel);
586}
587
588void bfin_interrupt_enable_all(int source, bool enable) {
589  rtems_interrupt_level isrLevel;
590  int vector;
591  bfin_isr_t *walk;
592
593  for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++)
594    if ( (vectors[vector].mask0 & (1 << source) ) || \
595        (vectors[vector].mask1 & (1 << (source - SIC_ISR0_MAX)) ))
596      break;
597  if (vector < CEC_INTERRUPT_COUNT) {
598    rtems_interrupt_disable(isrLevel);
599    walk = vectors[vector].head;
600    while (walk) {
601      walk->mask0 = enable ? (1 << source) : 0;
602      walk = walk->next;
603    }
604
605    walk = vectors[vector].head;
606    while (walk) {
607      walk->mask1 = enable ? (1 << (source - SIC_ISR0_MAX)) : 0;
608      walk = walk->next;
609    }
610    setMask(vector);
611    rtems_interrupt_enable(isrLevel);
612  }
613}
614
615void bfin_interrupt_enable_global(int source, bool enable) {
616  int vector;
617  rtems_interrupt_level isrLevel;
618
619  for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++)
620    if ( (vectors[vector].mask0 & (1 << source) ) || \
621        (vectors[vector].mask1 & (1 << (source - SIC_ISR0_MAX)) ))
622      break;
623  if (vector < CEC_INTERRUPT_COUNT) {
624    rtems_interrupt_disable(isrLevel);
625    if ( SIC_ISR0_MAX > source ) {
626      if (enable)
627        globalMask0 |= 1 << source;
628      else
629        globalMask0 &= ~(1 << source);
630    }else {
631      if (enable)
632        globalMask1 |= 1 << (source - SIC_ISR0_MAX);
633      else
634        globalMask1 &= ~(1 << (source - SIC_ISR0_MAX));
635    }
636    setMask(vector);
637    rtems_interrupt_enable(isrLevel);
638  }
639}
640
641#endif
Note: See TracBrowser for help on using the repository browser.