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

Last change on this file since b2ed712 was b2ed712, checked in by Sebastian Huber <sebastian.huber@…>, on Aug 25, 2017 at 8:58:58 AM

Include missing <string.h>

Update #2133.

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