world leader in high performance signal processing
Trace: » interrupts

Interrupts

How the Hardware works

The Event Controller of the processor manages five types of activities or events or interrupts:

  • Emulation
  • Reset
  • Nonmaskable interrupts (NMI) - interrupt is an event that changes normal processor instruction flow and is asynchronous to program flow.
  • Exceptions - an exception is a software initiated event whose effects are synchronous to program flow.
  • Interrupts - interrupt is an event that changes normal processor instruction flow and is asynchronous to program flow.

Note the word event describes all five types of activities. The Event Controller manages fifteen different events in all: Emulation, Reset, NMI, Exception, and eleven interrupts. The event system is nested and prioritized. Consequently, several service routines may be active at any time, and a low priority event may be preempted by one of higher priority.

The Core Event Controller supports nine general-purpose interrupts (IVG7 – IVG15) in addition to the dedicated interrupt and exception events. When using the Linux kernel - the two lowest priority interrupts (IVG14 and IVG15) are reserved for system calls, and deferred interrupt handlers, leaving seven prioritized interrupt inputs (IVG7 – IVG13) to support the system level peripherals.

For reference here are the Interrupts mapped by the CEC

Priority Event Class Linux use EVT Entry
0 Emulation/Test Emulation/Test EMU
1 Reset Reset RST
2 Non Maskable Unused/User defined NMI
3 Exceptions Exceptions EVX
4 Global Enable Global Enable
5 Hardware Error Hardware Error/Deferred exceptions IVHW
6 Core Timer Kernel timer tick IVTMR
7 General Interrupt 7 Top half peripheral interrupt IVG7
8 General Interrupt 8 Top half peripheral interrupt IVG8
9 General Interrupt 9 Top half peripheral interrupt IVG9
10 General Interrupt 10 Top half peripheral interrupt IVG10
11 General Interrupt 11 Top half peripheral interrupt IVG11
12 General Interrupt 12 Top half peripheral interrupt IVG12
13 General Interrupt 13 Top half peripheral interrupt IVG13
14 General Interrupt 14 Bottom half interrupt processing IVG14
15 General Interrupt 15 Linux kernel system calls IVG15

Since there are normally many more system level peripheral interrupts than the 7 general purpose interrupts which are available as inputs to the CEC, the processor employs a two-level event control mechanism. The processor System Interrupt Controller (SIC) works with the Core Event Controller (CEC) to prioritize and control all system interrupts. The SIC provides mapping between the many peripheral interrupt sources and the prioritized general-purpose interrupt inputs of the core. This mapping is programmable, and individual interrupt sources can be masked in the SIC.

For reference here is the default mapping by the SIC for the BF526:

Event Event IDDefault Mapping
Real-Time Clock0 IVG7
Reserved 1
USB 2 IVG7
PCI Interrupt 3 IVG7
SPORT 0 RX DMA 4 IVG8
SPORT 0 TX DMA 5 IVG8
SPORT 1 RX DMA 6 IVG8
SPORT 1 TX DMA 7 IVG8
SPI 0 DMA 8 IVG9
SPI 1 DMA 9 IVG9
UART 0 Rx 10 IVG10
UART 0 Tx 11 IVG10
UART 1 Rx 12 IVG10
UART 1 Tx 13 IVG10
Timer 0 14 IVG11
Timer 1 15 IVG11
Timer 2 16 IVG11
GPIO Int A 17 IVG12
GPIO Int B 18 IVG12
MEM DMA 19 IVG13
SW Wdog 20 IVG13
Reserved 21-26
SWI 1 27 IVG14
SWI 2 28 IVG15

Interrupt Handling in Linux

Generic Interrupts Overview

An interrupt handler is associated with an interrupt “number” by the request_irq function. This is a normal “C” function. The function has the following parameters

  • irqnum: Interrupt number to allocate
  • handler: Function to be called when the IRQ occurs
  • irqflags: Interrupt type flags
  • devname: An ASCII name for the claiming device
  • dev_id: A cookie passed back to the handler function

This call allocates interrupt resources and enables the interrupt line and IRQ handling. From the point this call is made your handler function may be invoked. Since your handler function must clear any interrupt the board raises, you must take care both to initialize your hardware and to set up the interrupt handler in the right order.

The dev_id structure must (normally) be globally unique. Normally the address of the device data structure is used as the cookie. Since the handler receives this value it makes sense to use it. If your interrupt is shared you must pass a non NULL dev_id as this is required when freeing the interrupt.

The irqflags are normally defined as follows:

  • IRQF_DISABLED - keep irqs disabled when calling the action handler
  • IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
  • IRQF_SHARED - allow sharing the irq among several devices
  • IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
  • IRQF_TIMER - Flag to mark this interrupt as timer interrupt
  • IRQF_TRIGGER_NONE - already configured
  • IRQF_TRIGGER_RISING - EDGE sensitive: Rising Edge Triggered
  • IRQF_TRIGGER_FALLING - EDGE sensitive: Falling Edge Triggered
  • IRQF_TRIGGER_HIGH - LEVEL sensitive: Active High
  • IRQF_TRIGGER_LOW - LEVEL sensitive: Active Low

Some variation occurs with different target systems. With 2.6 The interrupt handler must return a 1 if the interrupt is handled. There is a special irqreturn_t defined in linux-2.6.x/include/linux/interrupt.h

To mix old-style and new-style irqs the handler returns.

  • IRQ_NONE means we didn't handle it.
  • IRQ_HANDLED means that we did have a valid interrupt and handled it.
  • IRQ_RETVAL(x) selects on the two depending on x being non-zero (for handled)

A typical interrupt service routine

static irqreturn_t
xxx_timer_interrupt(int irq, void *dev_id)
{
	timer_tick(regs);
	return IRQ_HANDLED;
}

Interrupts on Blackfin Systems

The Blackfin Interrupts work as follows.

  • Each interrupt is assigned to a level ( 0 - 15 )
  • A number of fixed interrupts are assigned to preset levels
  • System interrupts are vectored to a General interrupt level
  • The System Interface Controllers are examined to determine the interrupt source
  • The cpu starts executing from the vector address and arrives at the general Linux interrupt handler with a defined interrupt number

Blackfin Interrupts Overview

Architecture specific Linux code.

Interrupt priorities are all set up in the program_IAR() function in the cpu-specific ints-priority.c file. See the Linux Interrupt Overview belong for more information.

Interrupt Discovery

Each System interrupt is assigned to a number of possible uClinux interrupts. A vector table is set up for each possible system interrupt and the interrupt status register is checked to see if that interrupt has triggered. Following a successful match the correct linux interrupt can be determined.

file: arch/blackfin/mach-common/ints-priority.c

scm failed with exit code 1:
file does not exist in git

Blackfin Interrupt Setup

The following code sets up the initial vectors in the CEC.

Things get setup here:

file: arch/blackfin/mach-common/ints-priority.c

scm failed with exit code 1:
file does not exist in git

All of the interrupts are in the range 7-13 and are handled by dedicated service routines. evt_evtx where x ranges from 7 to 13.

The SIC is set up from:

file: arch/blackfin/mach-common/ints-priority.c

scm failed with exit code 1:
file does not exist in git

Blackfin System Timer

The system timer is set up using an inline statement

file: arch/blackfin/kernel/time.c

scm failed with exit code 1:
file does not exist in git

Blackfin Interrupt Service

When an interrupt goes off, the program counter is set to the interrupt vector, and execution starts here.

Here is an example

file: arch/blackfin/mach-common/interrupt.S

scm failed with exit code 1:
file does not exist in git

which uses the INTERRUPT_ENTRY macro:

file: include/asm-blackfin/entry.h

scm failed with exit code 1:
file does not exist in git

This code records the source of the interrupt and calls a common function to service the interrupt ''__common_int_entry'';

file: arch/blackfin/mach-common/interrupt.S

scm failed with exit code 1:
file does not exist in git

Linux Interrupt Overview

On the BF533 platforms the interrupt system uses a priority scheme to manage interrupts. A vector is provided for each priority.

The interrupt service routing has to monitor each interrupt associated with a given priority and detect the IRQ NUMBER for any given interrupt.

Once this number is detected the usual Linux interrupt handling can continue.

The following files define and set up the interrupt system

  • linux-2.6.x/include/asm/irq.h
  • linux-2.6.x/arch/blackfin/mach-bf533/ints-priority.c
  • linux-2.6.x/arch/blackfin/mach-bf533/interrupt.S

Here the priorities for each interrupt are set up

file: arch/blackfin/mach-bf537/ints-priority.c

scm failed with exit code 1:
file does not exist in git

The actual interrupt service routines are here

The interrupt service routines are shown above

The final interrupt service routine

file: arch/blackfin/mach-common/ints-priority.c

scm failed with exit code 1:
file does not exist in git

The interrupt definitions are in this file linux-2.6.x/include/asm-blackfin/board/bf533_irq.h

file: include/asm-blackfin/mach-bf533/irq.h

scm failed with exit code 1:
file does not exist in git

Linux Interrupt Servicing

An interrupt service routine looks just like a regular function call. Linux takes care of any special handling required to get in and out of the interrupt context.

The following code is an extract from the file linux-2.6.x/arch/blackfin/kernel/time.c

Here the timer interrupt service is processed.

file: arch/blackfin/kernel/time.c

scm failed with exit code 1:
file does not exist in git

The arguments for the interrupt handler are:

  • Interrupt number
  • Private data structure (used for shared interrupts)
  • contents of stack before the interrupt occurred.

The interrupt handler must return a 1 to indicate that the interrupt has been processed.

Linux Interrupt Actions

This section deals with what you can do with an interrupt during the service call. The system is in an interrupt service state and normally only a limited number of options are available. If a fast interrupt is being serviced the interrupt routine is being run with interrupts disabled. This means that any lengthy processing should be avoided to prevent degrading the system.

Here is a short list of options available during an interrupt service routine.

  • Reset the interrupting device
  • Pend a bottom half
  • Wake up a sleeping task
  • Trigger a tasklet

Resetting the Source

This normally means resetting a timer or reading a register to remove the source of an interrupt. This is especially important in the case of a level triggered interrupt to prevent the system delivering on stop interrupts.

Pending a Bottom Half

The bottom half is a traditional way of splitting a service between fast service tasks done with interrupts disabled and slower tasks that need to be done in kernel context but before resuming “normal” processing. There are a fixed number of bottom halves slots (32) and just about all of them have been allocated to historical tasks. The one remaining bottom half task is the one related to running the immediate task queue. This queue is run on the return from interrupt, any return from a system call or any time the scheduler is called.

Before spending a lot of time on this please note that this service has been replaced by the tasklet system. A reference is included to allow you to understand any legacy code.

The following code example will show you how to trigger the immediate BH from an interrupt service routine.

  #include <linux/tqueue.h>                                           
  #include <linux/interrupt.h>                                        
 
  static struct tq_struct some_tq;                                    
 
  void some_handler ( unsigned long some_data )                       
  {                                                                   
     /* do whatever */                                                
  }                                                                   
  void some_interrupt(int irq , void * dev_id , struct pt_regs * regs)
  {                                                                   
 
    unsigned long some_data;                                          
 
        /* turn off the interrupt */                                  
    some_tq.routine = some_handler;                                   
    some_tq.data = some_data;                                         
    queue_task(&some_tq, &tq_immediate);                              
    mark_bh(IMMEDIATE_BH);                                            
  }                                                                   
 

The new (in Kernel 2.6) workqueue interface..

  #include <linux/workqueue.h>  // NEW for Kernel 2.6                               
  #include <linux/interrupt.h>                                               
 
 
 
  void my_work_fn ( unsigned long some_data )                                
  {                                                                          
     /* do whatever */                                                       
  }                                                                          
 
  unsigned long some_data;                                                   
 
  static DECLARE_WORK(my_work, my_work_fn, some_data);                       
 
  irqreturn_t some_interrupt(int irq, void *dev_id)
  {                                                                          
      schedule_work(&my_work);                                               
 
  }                                                                          

Wake up a Sleeping Task

Another option inside an interrupt service routine is to wake up a sleeping task. The task will enter an interruptible_sleep_on and wait to be woken up by the wake up call in the interrupt service routine.

Another code example helps:

  #include <linux/wait.h>                                                    
  #include <linux/interrupt.h>                                               
 
  static DECLARE_WAIT_QUEUE_HEAD(my_waitqueue);                              
 
  static int cond = 0;                                                       
 
 
  void some_task(unsigned long some_data)                                    
  {                                                                          
     int done = 0;   
 
     while(!done) {                                                          
 
        wait_event_interruptible(my_waitqueue,(cond == 0));                  
        /* do whatever */                                                    
 
     }                                                                       
  }                                                                          
 
  irqreturn_t some_interrupt(int irq , void * dev_id)
  {                                                                          
     /* turn off the interrupt */                                            
    cond = 1;                                                                
    wake_up_interruptible(&my_waitqueue);                                    
 
    return 1;                                                                
  }                                                                          
 

Run a Tasklet

This is the favored option with the 2.4 kernels. They can be scheduled many times and are guaranteed to be run once on the CPU they were first scheduled on. However several different tasklets may be scheduled to run at one time. Semaphores cannot be used to protect critical regions, you have to use spinlocks.

A Tasklet code example follows:

  #include <linux/tqueue.h>                                           
  #include <linux/interrupt.h>                                        
 
  void some_task(unsigned long);                                      
  unsigned long some_data;                                            
 
  DECLARE_TASKLET(some_name, some_task, some_data);                   
 
  void some_task ( unsigned long some_data )                          
  {                                                                   
        /* do whatever */                                             
  }                                                                   
 
  void some_interrupt(int irq , void * dev_id)
  {                                                                   
 
    /* turn off the interrupt */                                      
 
    tasklet_schedule(&some_name);                                     
  }                                 

Standalone Interrupt Handlers

For people writing small test applications, you may want to write your own IRQ handler. The gcc toolchain provides an easy way to do just that via the interrupt_handler attribute.

__attribute__ ((interrupt_handler))
void my_irq_handler(void)
{
	/* do the stuff needed to handle the IRQ properly */
}

Then just make sure you register the handler by updating all the relevant registers and masks.

Remember, this is for people running bare metal code. That means no Linux.

GPIO Interrupt Processing

Most Blackfin GPIOs can be configured to generate an interrupt. The processor can sense up to 48 (BF537/BF561) or 16 (BF533) asynchronous off-chip signals, requesting interrupts.

To make a pin function as an interrupt pin, the associated IRQ Number must be given to the request_irq function call.

These numbers differ between Blackfin processor derivatives.

This table is provided for reference only. The actual numerical value / IRQ # behind the define should never be utilized. You must always use the symbolic define such as IRQ_PF5, or use the gpio_to_irq() and irq_to_gpio() functions to translate the GPIO value to the IRQ value.

Note: this table is autogenerated by project/linux-kernel/scmsvn/scripts/make-irq-wiki-list -- do not edit!

BF518 BF527 BF533 BF537 BF538 BF548 BF561
GPIO # Define IRQ # Define IRQ # Define IRQ # Define IRQ # Define IRQ # Define IRQ # Define IRQ #
0 IRQ_PF0 71 IRQ_PF0 71 IRQ_PF0 33 IRQ_PF0 50 IRQ_PF0 71 IRQ_PA0 103 IRQ_PF0 73
1 IRQ_PF1 72 IRQ_PF1 72 IRQ_PF1 34 IRQ_PF1 51 IRQ_PF1 72 IRQ_PA1 104 IRQ_PF1 74
2 IRQ_PF2 73 IRQ_PF2 73 IRQ_PF2 35 IRQ_PF2 52 IRQ_PF2 73 IRQ_PA2 105 IRQ_PF2 75
3 IRQ_PF3 74 IRQ_PF3 74 IRQ_PF3 36 IRQ_PF3 53 IRQ_PF3 74 IRQ_PA3 106 IRQ_PF3 76
4 IRQ_PF4 75 IRQ_PF4 75 IRQ_PF4 37 IRQ_PF4 54 IRQ_PF4 75 IRQ_PA4 107 IRQ_PF4 77
5 IRQ_PF5 76 IRQ_PF5 76 IRQ_PF5 38 IRQ_PF5 55 IRQ_PF5 76 IRQ_PA5 108 IRQ_PF5 78
6 IRQ_PF6 77 IRQ_PF6 77 IRQ_PF6 39 IRQ_PF6 56 IRQ_PF6 77 IRQ_PA6 109 IRQ_PF6 79
7 IRQ_PF7 78 IRQ_PF7 78 IRQ_PF7 40 IRQ_PF7 57 IRQ_PF7 78 IRQ_PA7 110 IRQ_PF7 80
8 IRQ_PF8 79 IRQ_PF8 79 IRQ_PF8 41 IRQ_PF8 58 IRQ_PF8 79 IRQ_PA8 111 IRQ_PF8 81
9 IRQ_PF9 80 IRQ_PF9 80 IRQ_PF9 42 IRQ_PF9 59 IRQ_PF9 80 IRQ_PA9 112 IRQ_PF9 82
10 IRQ_PF10 81 IRQ_PF10 81 IRQ_PF10 43 IRQ_PF10 60 IRQ_PF10 81 IRQ_PA10 113 IRQ_PF10 83
11 IRQ_PF11 82 IRQ_PF11 82 IRQ_PF11 44 IRQ_PF11 61 IRQ_PF11 82 IRQ_PA11 114 IRQ_PF11 84
12 IRQ_PF12 83 IRQ_PF12 83 IRQ_PF12 45 IRQ_PF12 62 IRQ_PF12 83 IRQ_PA12 115 IRQ_PF12 85
13 IRQ_PF13 84 IRQ_PF13 84 IRQ_PF13 46 IRQ_PF13 63 IRQ_PF13 84 IRQ_PA13 116 IRQ_PF13 86
14 IRQ_PF14 85 IRQ_PF14 85 IRQ_PF14 47 IRQ_PF14 64 IRQ_PF14 85 IRQ_PA14 117 IRQ_PF14 87
15 IRQ_PF15 86 IRQ_PF15 86 IRQ_PF15 48 IRQ_PF15 65 IRQ_PF15 86 IRQ_PA15 118 IRQ_PF15 88
16 IRQ_PG0 87 IRQ_PG0 87 IRQ_PG0 66 IRQ_PB0 119 IRQ_PF16 89
17 IRQ_PG1 88 IRQ_PG1 88 IRQ_PG1 67 IRQ_PB1 120 IRQ_PF17 90
18 IRQ_PG2 89 IRQ_PG2 89 IRQ_PG2 68 IRQ_PB2 121 IRQ_PF18 91
19 IRQ_PG3 90 IRQ_PG3 90 IRQ_PG3 69 IRQ_PB3 122 IRQ_PF19 92
20 IRQ_PG4 91 IRQ_PG4 91 IRQ_PG4 70 IRQ_PB4 123 IRQ_PF20 93
21 IRQ_PG5 92 IRQ_PG5 92 IRQ_PG5 71 IRQ_PB5 124 IRQ_PF21 94
22 IRQ_PG6 93 IRQ_PG6 93 IRQ_PG6 72 IRQ_PB6 125 IRQ_PF22 95
23 IRQ_PG7 94 IRQ_PG7 94 IRQ_PG7 73 IRQ_PB7 126 IRQ_PF23 96
24 IRQ_PG8 95 IRQ_PG8 95 IRQ_PG8 74 IRQ_PB8 127 IRQ_PF24 97
25 IRQ_PG9 96 IRQ_PG9 96 IRQ_PG9 75 IRQ_PB9 128 IRQ_PF25 98
26 IRQ_PG10 97 IRQ_PG10 97 IRQ_PG10 76 IRQ_PB10 129 IRQ_PF26 99
27 IRQ_PG11 98 IRQ_PG11 98 IRQ_PG11 77 IRQ_PB11 130 IRQ_PF27 100
28 IRQ_PG12 99 IRQ_PG12 99 IRQ_PG12 78 IRQ_PB12 131 IRQ_PF28 101
29 IRQ_PG13 100 IRQ_PG13 100 IRQ_PG13 79 IRQ_PB13 132 IRQ_PF29 102
30 IRQ_PG14 101 IRQ_PG14 101 IRQ_PG14 80 IRQ_PB14 133 IRQ_PF30 103
31 IRQ_PG15 102 IRQ_PG15 102 IRQ_PG15 81 IRQ_PB15 134 IRQ_PF31 104
32 IRQ_PH0 103 IRQ_PH0 103 IRQ_PH0 82 IRQ_PC0 135 IRQ_PF32 105
33 IRQ_PH1 104 IRQ_PH1 104 IRQ_PH1 83 IRQ_PC1 136 IRQ_PF33 106
34 IRQ_PH2 105 IRQ_PH2 105 IRQ_PH2 84 IRQ_PC2 137 IRQ_PF34 107
35 IRQ_PH3 106 IRQ_PH3 106 IRQ_PH3 85 IRQ_PC3 138 IRQ_PF35 108
36 IRQ_PH4 107 IRQ_PH4 107 IRQ_PH4 86 IRQ_PC4 139 IRQ_PF36 109
37 IRQ_PH5 108 IRQ_PH5 108 IRQ_PH5 87 IRQ_PC5 140 IRQ_PF37 110
38 IRQ_PH6 109 IRQ_PH6 109 IRQ_PH6 88 IRQ_PC6 141 IRQ_PF38 111
39 IRQ_PH7 110 IRQ_PH7 110 IRQ_PH7 89 IRQ_PC7 142 IRQ_PF39 112
40 IRQ_PH8 111 IRQ_PH8 111 IRQ_PH8 90 IRQ_PC8 143 IRQ_PF40 113
41 IRQ_PH9 112 IRQ_PH9 112 IRQ_PH9 91 IRQ_PC9 144 IRQ_PF41 114
42 IRQ_PH10 113 IRQ_PH10 113 IRQ_PH10 92 IRQ_PC10 145 IRQ_PF42 115
43 IRQ_PH11 114 IRQ_PH11 114 IRQ_PH11 93 IRQ_PC11 146 IRQ_PF43 116
44 IRQ_PH12 115 IRQ_PH12 115 IRQ_PH12 94 IRQ_PC12 147 IRQ_PF44 117
45 IRQ_PH13 116 IRQ_PH13 116 IRQ_PH13 95 IRQ_PC13 148 IRQ_PF45 118
46 IRQ_PH14 117 IRQ_PH14 117 IRQ_PH14 96 IRQ_PC14 149 IRQ_PF46 119
47 IRQ_PH15 118 IRQ_PH15 118 IRQ_PH15 97 IRQ_PC15 150 IRQ_PF47 120
48 IRQ_PD0 151
49 IRQ_PD1 152
50 IRQ_PD2 153
51 IRQ_PD3 154
52 IRQ_PD4 155
53 IRQ_PD5 156
54 IRQ_PD6 157
55 IRQ_PD7 158
56 IRQ_PD8 159
57 IRQ_PD9 160
58 IRQ_PD10 161
59 IRQ_PD11 162
60 IRQ_PD12 163
61 IRQ_PD13 164
62 IRQ_PD14 165
63 IRQ_PD15 166
64 IRQ_PE0 167
65 IRQ_PE1 168
66 IRQ_PE2 169
67 IRQ_PE3 170
68 IRQ_PE4 171
69 IRQ_PE5 172
70 IRQ_PE6 173
71 IRQ_PE7 174
72 IRQ_PE8 175
73 IRQ_PE9 176
74 IRQ_PE10 177
75 IRQ_PE11 178
76 IRQ_PE12 179
77 IRQ_PE13 180
78 IRQ_PE14 181
79 IRQ_PE15 182
80 IRQ_PF0 183
81 IRQ_PF1 184
82 IRQ_PF2 185
83 IRQ_PF3 186
84 IRQ_PF4 187
85 IRQ_PF5 188
86 IRQ_PF6 189
87 IRQ_PF7 190
88 IRQ_PF8 191
89 IRQ_PF9 192
90 IRQ_PF10 193
91 IRQ_PF11 194
92 IRQ_PF12 195
93 IRQ_PF13 196
94 IRQ_PF14 197
95 IRQ_PF15 198
96 IRQ_PG0 199
97 IRQ_PG1 200
98 IRQ_PG2 201
99 IRQ_PG3 202
100 IRQ_PG4 203
101 IRQ_PG5 204
102 IRQ_PG6 205
103 IRQ_PG7 206
104 IRQ_PG8 207
105 IRQ_PG9 208
106 IRQ_PG10 209
107 IRQ_PG11 210
108 IRQ_PG12 211
109 IRQ_PG13 212
110 IRQ_PG14 213
111 IRQ_PG15 214
112 IRQ_PH0 215
113 IRQ_PH1 216
114 IRQ_PH2 217
115 IRQ_PH3 218
116 IRQ_PH4 219
117 IRQ_PH5 220
118 IRQ_PH6 221
119 IRQ_PH7 222
120 IRQ_PH8 223
121 IRQ_PH9 224
122 IRQ_PH10 225
123 IRQ_PH11 226
124 IRQ_PH12 227
125 IRQ_PH13 228
126 IRQ_PH14 229
127 IRQ_PH15 230
128 IRQ_PI0 231
129 IRQ_PI1 232
130 IRQ_PI2 233
131 IRQ_PI3 234
132 IRQ_PI4 235
133 IRQ_PI5 236
134 IRQ_PI6 237
135 IRQ_PI7 238
136 IRQ_PI8 239
137 IRQ_PI9 240
138 IRQ_PI10 241
139 IRQ_PI11 242
140 IRQ_PI12 243
141 IRQ_PI13 244
142 IRQ_PI14 245
143 IRQ_PI15 246
144 IRQ_PJ0 247
145 IRQ_PJ1 248
146 IRQ_PJ2 249
147 IRQ_PJ3 250
148 IRQ_PJ4 251
149 IRQ_PJ5 252
150 IRQ_PJ6 253
151 IRQ_PJ7 254
152 IRQ_PJ8 255
153 IRQ_PJ9 256
154 IRQ_PJ10 257
155 IRQ_PJ11 258
156 IRQ_PJ12 259
157 IRQ_PJ13 260
158 IRQ_PJ14 261
159 IRQ_PJ15 262
typedef irqreturn_t (*irq_handler_t)(int, void *);
int request_irq(unsigned int, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id);

It is possible to add flags to the request_irq call to configure the interrupt as EDGE or LEVEL triggered interrupts pin.

  • IRQF_TRIGGER_NONE - already configured
  • IRQF_TRIGGER_RISING - EDGE sensitive: Rising Edge Triggered
  • IRQF_TRIGGER_FALLING - EDGE sensitive: Falling Edge Triggered
  • IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING - EDGE sensitive: Both Edge Triggered
  • IRQF_TRIGGER_HIGH - LEVEL sensitive: Active High
  • IRQF_TRIGGER_LOW - LEVEL sensitive: Active Low

If you want to change the IRQ flags on the fly (say switch from triggering on active high to triggering on active low), then you can use the set_irq_type function.

int set_irq_type(unsigned int irq, unsigned int type);

Blackfin GPIO interrupt processing uses only MASK_A interrupts.

When debugging interrupt timing, preferably use GPIOs. Set a GPIO when you enter the ISR and clear it upon return. On a scope watch the IRQ and the GPIO. Use complex/advanced triggers to determine when you possibly lost an edge. Don't put printks into ISR handlers that go out the serial port. UART is damn slow - this is going to hold off interrupt processing.

If you don't have enough spare GPIOs - you may also do dummy read accesses to different Asynchronous Memory banks, and watch the /AMSx strobe wiggle.

Short example:

#include <linux/interrupt.h>
 
#define CONFIG_YOURDRIVERS_IRQ_PFX IRQ_PF5
 
static irqreturn_t your_irq_handler(int irq, void *dev_id)
{
	printk(KERN_INFO "your_irq_handler\n");
 
	/* ack the irq here so it stops generating */
 
	return IRQ_HANDLED;
}
 
static int __init your_drivers_init_function(void)
{
	int ret;
 
	/*  ... initialize your hardware here ...  */
 
	ret = request_irq(CONFIG_YOURDRIVERS_IRQ_PFX, your_irq_handler, IRQF_TRIGGER_LOW, "Your IRQ",
	                  your_pointer_passed_to_the_irq_handler);
	if (ret) {
		printk(KERN_WARNING "IRQ %d is not free.\n", CONFIG_YOURDRIVERS_IRQ_PFX);
		return ret;
	}
 
	return 0;
}
 
void __exit your_drivers_exit_function(void)
{
	free_irq(CONFIG_YOURDRIVERS_IRQ_PFX, your_pointer_passed_to_the_irq_handler);
}