CMP EMBEDDED.COM

Login | Register     Welcome Guest  
HOME DESIGN PRODUCTS COLUMNS E-LEARNING CONFERENCES CODE FORUMS/BLOGS NEWSLETTERS CONTACT FEATURES RSS RSS

Linux Porting Guide



Embedded Systems Design
Interrupt handling

The trap_init() function copies the top-level exception handlers to the KSEG0 vector location based on the CPU type. The interrupt handling code is contained in $(TOPDIR)/arch/MY_ARCH/MY_PLATFORM/irq.c and int-handler.S. Most systems use a dedicated interrupt controller to handle the interrupts in the system. The interrupt controller is hooked to one of the external interrupt lines in the processor. The architecture-dependent code has to be modified to fit the interrupt controller into the kernel interrupt handling.

Listing 7 shows the platform-specific interrupt initialization code. The topmost interrupt handler has to be installed using set_except_vector(). The interrupt controller that is used in the platform has to be initialized next. If remote debugging is enabled, a call to set_debug_traps() has to be made to allow any breakpoints or error conditions to be properly intercepted and reported to the debugger. In addition, a breakpoint needs to be generated to begin communication with the debugger running on the host.

Listing 7: Platform-specific interrupt initialization

static void __init myplatform_irq_setup (void) {     set_except_vector (0, myplatform_handle_int);

    // Initialize InterruptController

    InterruptController_Init(IsrTable);

#ifdef CONFIG_REMOTE_DEBUG
    printk (*Setting debug traps - please connect the remote debugger.\n*);
    set_debug_traps ();
    breakpoint ();
#endif
}

The top-level interrupt handler (Listing 8) first saves all the registers and then disables further interrupts. The CAUSE register is examined to find the source of the interrupt. If it is a timer interrupt, the corresponding ISR is called. In case it is not a timer interrupt, it checks whether an interrupt has occurred on the line connected to the interrupt controller.

The interrupt handler for the interrupt controller (Listing 9) has to get the pending interrupt vector that caused the interrupt and then execute the handler for the particular interrupt source.

Listing 8: Top-level interrupt handler

NESTED(myplatform_handle_int, PT_SIZE, ra)
    .set    .noat
    SAVE_ALL
    CLI
    .set    .at

    mfc0    .s0, CP0_CAUSE    .# get irq mask

    /* First, we check for counter/timer IRQ. */
    andi    .a0, s0, CAUSEF_IP5
    beq    .a0, zero, 1f
    andi    .a0, s0, CAUSEF_IP2    # delay slot, check hw0 interrupt

    /* Wheee, a timer interrupt. */
    move    .a0, sp
    jal    .timer_interrupt
    nop    .    .# delay slot

    j ret_from_irq
    nop    .    .# delay slot

1:
    beq    .a0, zero, 1f
    nop

    /* Wheee, combined hardware
    level zero interrupt. */
    jal    .InterruptController_InterruptHandler
    move    .a0, sp    .# delay slot

    j    .ret_from_irq
    nop    .    .# delay slot

1:
    /* Here by mistake? This is possible,
    *what can happen is that by the time we
    *take the exception the IRQ pin goes low, so
     *just leave if this is the case.
     */
    j    .ret_from_irq
    nop
    END(myplatform_handle_int)

Listing 9: Interrupt handler for the interrupt controller

void
InterruptController_InterruptHandler (
    struct pt_regs *regs
    )

{
    IntVector intvector;
    struct irqaction *action;
    int irq, cpu = smp_processor_id();

    InterruptControllerGetPendingIntVector(&intvector);
    InterruptControllerGetPendingIntSrc((&irq);

  action = (struct irqaction *)intvector;

      if ( action == NULL ) {
      printk(*No handler for hw0 irq: %i\n*, irq);
        return;
      }

    hardirq_enter(cpu);

      action->handler(irq, action->dev_id, regs);
      kstat.irqs[0]irq++;
      hardirq_exit(cpu);

} // InterruptController_InterruptHandler ()

The functions request_irq(), free_irq(), enable_irq() and disable_irq() have to be implemented for your target platform. request_irq() is used to install an interrupt handler for a given interrupt source. free_irq() needs to free the memory allocated for the given interrupt. enable_irq() needs to make a call to the interrupt controller function that enables the given interrupt line and disable_irq() needs to disable the given interrupt line.

Timer interrupt File $(TOPDIR)/arch/MY_ARCH/MY_PLATFORM/time.c contains the platform-dependent timer code. The Linux kernel on MIPS requires a 100Hz timer interrupt. In the MIPS, one of the timers in coprocessor 0 is programmed to generate 100Hz interrupts. The count register and the compare register together make up the timer. When active, the count register contains a free running counter. On each processor clock-tick, the value in the register increments by one. The register is reset and an external hardware interrupt is generated when the values in the count register and compare register match. After the count register is reset, it restarts to count on the next processor clock-tick. The timer interrupt service routine (ISR) needs to call the do_timer() routine. Performing a write to the compare register clears the timer interrupt.

Serial console driver

The console runs on top of a serial driver. A polled serial driver can be used for printk() (kernel debug message) functionality. The minimum functions that this driver needs to provide are the following:

  • serial_console_init()-for registering the console printing procedure for kernel printk() functionality, before the console driver is properly initialized
  • serial_console_setup()-for initializing the serial port
  • serial_console_write(struct console *console, const char *string, int count)-for writing "count" characters

1 | 2 | 3

Rate this article: Low High
Current rating
  • .
Embedded.com Career Center
Looking for a new job?
SEARCH JOBS

Browse all jobs

SPONSOR
RECENT JOB POSTINGS





 :