The Event Controller of the processor manages five types of activities or events or interrupts:
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 ID | Default Mapping |
|---|---|---|
| Real-Time Clock | 0 | 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 |
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
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:
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.
A typical interrupt service routine
static irqreturn_t xxx_timer_interrupt(int irq, void *dev_id) { timer_tick(regs); return IRQ_HANDLED; }
The Blackfin Interrupts work as follows.
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.
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
void do_irq(int vec, struct pt_regs *fp) { int irq = vec_to_irq(vec); if (irq == -1) return; asm_do_IRQ(irq, fp); }
The following code sets up the initial vectors in the CEC.
Things get setup here:
file: arch/blackfin/mach-common/ints-priority.c
void __cpuinit init_exception_vectors(void) { /* cannot program in software: * evt0 - emulation (jtag) * evt1 - reset */ bfin_write_EVT2(evt_nmi); bfin_write_EVT3(trap); bfin_write_EVT5(evt_ivhw); bfin_write_EVT6(evt_timer); bfin_write_EVT7(evt_evt7); bfin_write_EVT8(evt_evt8); bfin_write_EVT9(evt_evt9); bfin_write_EVT10(evt_evt10); bfin_write_EVT11(evt_evt11); bfin_write_EVT12(evt_evt12); bfin_write_EVT13(evt_evt13); bfin_write_EVT14(evt_evt14); bfin_write_EVT15(evt_system_call); CSYNC(); }
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
int __init init_arch_irq(void) { int irq; unsigned long ilat = 0; /* Disable all the peripheral intrs - page 4-29 HW Ref manual */ #ifdef SIC_IMASK0 bfin_write_SIC_IMASK0(SIC_UNMASK_ALL); bfin_write_SIC_IMASK1(SIC_UNMASK_ALL); # ifdef SIC_IMASK2 bfin_write_SIC_IMASK2(SIC_UNMASK_ALL); # endif # if defined(CONFIG_SMP) || defined(CONFIG_ICC) bfin_write_SICB_IMASK0(SIC_UNMASK_ALL); bfin_write_SICB_IMASK1(SIC_UNMASK_ALL); # endif #else bfin_write_SIC_IMASK(SIC_UNMASK_ALL); #endif local_irq_disable(); for (irq = 0; irq <= SYS_IRQS; irq++) { if (irq <= IRQ_CORETMR) irq_set_chip(irq, &bfin_core_irqchip); else irq_set_chip(irq, &bfin_internal_irqchip); switch (irq) { #if !BFIN_GPIO_PINT #if defined(BF537_FAMILY) case IRQ_PH_INTA_MAC_RX: case IRQ_PF_INTA_PG_INTA: #elif defined(BF533_FAMILY) case IRQ_PROG_INTA: #elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x) case IRQ_PORTF_INTA: case IRQ_PORTG_INTA: case IRQ_PORTH_INTA: #elif defined(CONFIG_BF561) case IRQ_PROG0_INTA: case IRQ_PROG1_INTA: case IRQ_PROG2_INTA: #elif defined(BF538_FAMILY) case IRQ_PORTF_INTA: #endif irq_set_chained_handler(irq, bfin_demux_gpio_irq); break; #endif #if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE) case IRQ_MAC_ERROR: irq_set_chained_handler(irq, bfin_demux_mac_status_irq); break; #endif #if defined(CONFIG_SMP) || defined(CONFIG_ICC) case IRQ_SUPPLE_0: case IRQ_SUPPLE_1: irq_set_handler(irq, handle_percpu_irq); break; #endif #ifdef CONFIG_TICKSOURCE_CORETMR case IRQ_CORETMR: # ifdef CONFIG_SMP irq_set_handler(irq, handle_percpu_irq); # else irq_set_handler(irq, handle_simple_irq); # endif break; #endif #ifdef CONFIG_TICKSOURCE_GPTMR0 case IRQ_TIMER0: irq_set_handler(irq, handle_simple_irq); break; #endif default: #ifdef CONFIG_IPIPE irq_set_handler(irq, handle_level_irq); #else irq_set_handler(irq, handle_simple_irq); #endif break; } } init_mach_irq(); #if (defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) for (irq = IRQ_MAC_PHYINT; irq <= IRQ_MAC_STMDONE; irq++) irq_set_chip_and_handler(irq, &bfin_mac_status_irqchip, handle_level_irq); #endif /* if configured as edge, then will be changed to do_edge_IRQ */ #ifdef CONFIG_GPIO_ADI for (irq = GPIO_IRQ_BASE; irq < (GPIO_IRQ_BASE + MAX_BLACKFIN_GPIOS); irq++) irq_set_chip_and_handler(irq, &bfin_gpio_irqchip, handle_level_irq); #endif bfin_write_IMASK(0); CSYNC(); ilat = bfin_read_ILAT(); CSYNC(); bfin_write_ILAT(ilat); CSYNC(); printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); /* IMASK=xxx is equivalent to STI xx or bfin_irq_flags=xx, * local_irq_enable() */ program_IAR(); /* Therefore it's better to setup IARs before interrupts enabled */ search_IAR(); /* Enable interrupts IVG7-15 */ bfin_irq_flags |= IMASK_IVG15 | IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; /* This implicitly covers ANOMALY_05000171 * Boot-ROM code modifies SICA_IWRx wakeup registers */ #ifdef SIC_IWR0 bfin_write_SIC_IWR0(IWR_DISABLE_ALL); # ifdef SIC_IWR1 /* BF52x/BF51x system reset does not properly reset SIC_IWR1 which * will screw up the bootrom as it relies on MDMA0/1 waking it * up from IDLE instructions. See this report for more info: * http://blackfin.uclinux.org/gf/tracker/4323 */ if (ANOMALY_05000435) bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11)); else bfin_write_SIC_IWR1(IWR_DISABLE_ALL); # endif # ifdef SIC_IWR2 bfin_write_SIC_IWR2(IWR_DISABLE_ALL); # endif #else bfin_write_SIC_IWR(IWR_DISABLE_ALL); #endif return 0; }
The system timer is set up using an inline statement
file: arch/blackfin/kernel/time.c
time_sched_init(irqreturn_t(*timer_routine) (int, void *)) { #if defined(CONFIG_IPIPE) setup_system_timer0(); bfin_timer_irq.handler = timer_routine; setup_irq(IRQ_TIMER0, &bfin_timer_irq); #else setup_core_timer(); bfin_timer_irq.handler = timer_routine; setup_irq(IRQ_CORETMR, &bfin_timer_irq); #endif }
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
/* line 243 to 245 */ ENTRY(_evt_evt7) INTERRUPT_ENTRY(EVT_IVG7_P)
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
/* line 36 to 144 */ __common_int_entry: [--sp] = fp; [--sp] = usp; [--sp] = i0; [--sp] = i1; [--sp] = i2; [--sp] = i3; [--sp] = m0; [--sp] = m1; [--sp] = m2; [--sp] = m3; [--sp] = l0; [--sp] = l1; [--sp] = l2; [--sp] = l3; [--sp] = b0; [--sp] = b1; [--sp] = b2; [--sp] = b3; [--sp] = a0.x; [--sp] = a0.w; [--sp] = a1.x; [--sp] = a1.w; [--sp] = LC0; [--sp] = LC1; [--sp] = LT0; [--sp] = LT1; [--sp] = LB0; [--sp] = LB1; [--sp] = ASTAT; [--sp] = r0; /* Skip reserved */ [--sp] = RETS; r2 = RETI; [--sp] = r2; [--sp] = RETX; [--sp] = RETN; [--sp] = RETE; [--sp] = SEQSTAT; [--sp] = r1; /* IPEND - R1 may or may not be set up before jumping here. */ /* Switch to other method of keeping interrupts disabled. */ #ifdef CONFIG_DEBUG_HWERR r1 = 0x3f; sti r1; #else cli r1; #endif #ifdef CONFIG_TRACE_IRQFLAGS [--sp] = r0; sp += -12; call _trace_hardirqs_off; sp += 12; r0 = [sp++]; #endif [--sp] = RETI; /* orig_pc */ /* Clear all L registers. */ r1 = 0 (x); l0 = r1; l1 = r1; l2 = r1; l3 = r1; #ifdef CONFIG_FRAME_POINTER fp = 0; #endif ANOMALY_283_315_WORKAROUND(p5, r7) r1 = sp; SP += -12; #ifdef CONFIG_IPIPE call ___ipipe_grab_irq SP += 12; cc = r0 == 0; if cc jump .Lcommon_restore_context; #else /* CONFIG_IPIPE */ #ifdef CONFIG_PREEMPT r7 = sp; r4.l = lo(ALIGN_PAGE_MASK); r4.h = hi(ALIGN_PAGE_MASK); r7 = r7 & r4; p5 = r7; r7 = [p5 + TI_PREEMPT]; /* get preempt count */ r7 += 1; /* increment it */ [p5 + TI_PREEMPT] = r7; #endif pseudo_long_call _do_irq, p2; #ifdef CONFIG_PREEMPT r7 += -1; [p5 + TI_PREEMPT] = r7; /* restore preempt count */ #endif SP += 12; #endif /* CONFIG_IPIPE */ pseudo_long_call _return_from_int, p2; .Lcommon_restore_context: RESTORE_CONTEXT rti; /* interrupt routine for ivhw - 5 */
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
Here the priorities for each interrupt are set up
file: arch/blackfin/mach-bf537/ints-priority.c
void __init program_IAR(void) { /* Program the IAR0 Register with the configured priority */ bfin_write_SIC_IAR0(((CONFIG_IRQ_PLL_WAKEUP - 7) << IRQ_PLL_WAKEUP_POS) | ((CONFIG_IRQ_DMA_ERROR - 7) << IRQ_DMA_ERROR_POS) | ((CONFIG_IRQ_ERROR - 7) << IRQ_ERROR_POS) | ((CONFIG_IRQ_RTC - 7) << IRQ_RTC_POS) | ((CONFIG_IRQ_PPI - 7) << IRQ_PPI_POS) | ((CONFIG_IRQ_SPORT0_RX - 7) << IRQ_SPORT0_RX_POS) | ((CONFIG_IRQ_SPORT0_TX - 7) << IRQ_SPORT0_TX_POS) | ((CONFIG_IRQ_SPORT1_RX - 7) << IRQ_SPORT1_RX_POS)); bfin_write_SIC_IAR1(((CONFIG_IRQ_SPORT1_TX - 7) << IRQ_SPORT1_TX_POS) | ((CONFIG_IRQ_TWI - 7) << IRQ_TWI_POS) | ((CONFIG_IRQ_SPI - 7) << IRQ_SPI_POS) | ((CONFIG_IRQ_UART0_RX - 7) << IRQ_UART0_RX_POS) | ((CONFIG_IRQ_UART0_TX - 7) << IRQ_UART0_TX_POS) | ((CONFIG_IRQ_UART1_RX - 7) << IRQ_UART1_RX_POS) | ((CONFIG_IRQ_UART1_TX - 7) << IRQ_UART1_TX_POS) | ((CONFIG_IRQ_CAN_RX - 7) << IRQ_CAN_RX_POS)); bfin_write_SIC_IAR2(((CONFIG_IRQ_CAN_TX - 7) << IRQ_CAN_TX_POS) | ((CONFIG_IRQ_MAC_RX - 7) << IRQ_MAC_RX_POS) | ((CONFIG_IRQ_MAC_TX - 7) << IRQ_MAC_TX_POS) | ((CONFIG_IRQ_TIMER0 - 7) << IRQ_TIMER0_POS) | ((CONFIG_IRQ_TIMER1 - 7) << IRQ_TIMER1_POS) | ((CONFIG_IRQ_TIMER2 - 7) << IRQ_TIMER2_POS) | ((CONFIG_IRQ_TIMER3 - 7) << IRQ_TIMER3_POS) | ((CONFIG_IRQ_TIMER4 - 7) << IRQ_TIMER4_POS)); bfin_write_SIC_IAR3(((CONFIG_IRQ_TIMER5 - 7) << IRQ_TIMER5_POS) | ((CONFIG_IRQ_TIMER6 - 7) << IRQ_TIMER6_POS) | ((CONFIG_IRQ_TIMER7 - 7) << IRQ_TIMER7_POS) | ((CONFIG_IRQ_PROG_INTA - 7) << IRQ_PROG_INTA_POS) | ((CONFIG_IRQ_PORTG_INTB - 7) << IRQ_PORTG_INTB_POS) | ((CONFIG_IRQ_MEM_DMA0 - 7) << IRQ_MEM_DMA0_POS) | ((CONFIG_IRQ_MEM_DMA1 - 7) << IRQ_MEM_DMA1_POS) | ((CONFIG_IRQ_WATCH - 7) << IRQ_WATCH_POS)); SSYNC(); }
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
void do_irq(int vec, struct pt_regs *fp) { int irq = vec_to_irq(vec); if (irq == -1) return; asm_do_IRQ(irq, fp); }
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
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
irqreturn_t timer_interrupt(int irq, void *dummy) { xtime_update(1); #ifdef CONFIG_IPIPE update_root_process_times(get_irq_regs()); #else update_process_times(user_mode(get_irq_regs())); #endif profile_tick(CPU_PROFILING); return IRQ_HANDLED; }
The arguments for the interrupt handler are:
The interrupt handler must return a 1 to indicate that the interrupt has been processed.
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.
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.
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); }
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; }
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); }
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.
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.
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.
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);
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); }