xenarm irq routine

原创文章,转载请注明出处.转载自: Li Haifeng's Blog
本文链接地址: xenarm irq routine

/*
 当一个异常出现以后,ARM微处理器会执行以下几步操作: (这些是中断发生时,自动处理的)

 1、 将下一条指令的地址存入相应连接寄存器LR,以便程序在处理异常返回时能从正确的位置重新开始执行。若异常是从ARM状态进入,LR寄存器中保存的是下一 条指令的地址(当前PC+4或PC+8,与异常的类型有关);若异常是从Thumb状态进入,则在LR寄存器中保存当前 PC的偏移量,这样,异常处理程序就不需要确定异常是从何种状态进入的。例如:在软件中断异常SWI,指令MOV PC,R14_svc总是返回到下一条指令,不管SWI是在ARM状态执行,还是在Thumb状态执行。

 2、将CPSR复制到相应的SPSR中。

 3、根据异常类型,强制设置CPSR的运行模式位。

 4、 强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。还可以设置中断禁止位,以禁止中断发生.当有异常发生时,处理器会跳转 到对应的0xffff0000起始的向量处取指令,然后,通过b指令散转到异常处理代码.因为ARM中b指令是相对跳转,而 且只有+/-32MB的寻址范围,所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处.这 里可直接用b指令跳转过去,这样比使用绝对跳转(ldr)效率高。
 */

        b       vector_undefined_instruction + stubs_offset
        ldr     pc, .LCvswi + stubs_offset
        b       vector_prefetch_abort + stubs_offset
        b       vector_data_abort + stubs_offset
        b       vector_addrexcptn + stubs_offset
        b       vector_irq + stubs_offset
        b       vector_FIQ + stubs_offset

#define INSTALL_VECTOR_STUB(name, offset, mode, correction, branch_table)      
        vector_##name:                                                 
                vector_stub     offset, mode, correction, branch_table
        .macro vector_stub      offset, mode, correction, branch_table
        sub     sp, sp, #16
        sub     lr, lr, #correction @修正返回地址
        stmia   sp!, {r0, lr}

        mrs     r0, spsr @spsr保存的是cpsr的值
        mov     lr, #offset
        stmia   sp!, {r0, lr}

        mrs     lr, cpsr
        eor     lr, lr, #(mode ^ PSR_MODE_SVC)
        msr     spsr_cxsf, lr   @改变spsr以重启irq       @ switch to SVC_32 mode

        and     r0, r0, #15
        adr     lr, branch_table
        ldr     lr, [lr, r0, lsl #2]    @根据spsr内容来决定进入哪一个irq_
        sub     r0, sp, #16
        movs    pc, lr                          @ Changes mode and branches
        .endm

__irq_svc:
        save_svc_context
#ifdef  CONFIG_MACHINE_VERSATILE
        get_irqnr_preamble r5, lr @ minsung
#endif
1:      get_irqnr_and_base r0, r6, r5, lr
        movne   r1, sp  

        @
        @ routine called with r0 = irq number, r1 = struct pt_regs *
        @
        adrne   lr, 1b
        bne     asm_do_IRQ

        /* restore svc mode register */
        ldr     r0, [sp, #S_PSR]                @ irqs are already disabled
        msr     spsr_cxsf, r0
        ldmia   sp, {r0 – pc}^                  @ load r0 – pc, cpsr

        .macro save_svc_context
        sub     sp, sp, #S_FRAME_SIZE
SPFIX(  tst     sp, #4                 )
SPFIX(  bicne   sp, sp, #4             )
        stmib   sp, {r1 – r12}

        ldmia   r0, {r1 – r4}
        add     r5, sp, #S_SP
        add     r0, sp, #S_FRAME_SIZE
SPFIX(  addne   r0, r0, #4  )
        str     r1, [sp]                @ Save real R0

        mov     r1, lr

        stmia   r5, {r0 – r4}
        .endm
/*
 从下面这段程序可以看出来,
        如何获得goldfish中断号,
        如何获得goldfish状态

        如果没有中断号,还可以设置条件描述符
 */
        .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
        ldr     base, =IO_ADDRESS(GOLDFISH_INTERRUPT_BASE)
        ldr     irqnr, [base, #GOLDFISH_INTERRUPT_NUMBER]
        ldr     irqstat, [base, #GOLDFISH_INTERRUPT_STATUS]
        teq     irqstat, #0
        /* EQ will be set if no irqs pending */
        .endm

/*
         时间中断来了,仅仅是打开了一个软中断而已。
*/
irqreturn_t goldfish_timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
{
    uint32_t timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE);

    timer_tick(regs);

    raise_softirq(TIMER_SOFTIRQ);

    __raw_writel(1, timer_base + TIMER_CLEAR_INTERRUPT);

    goldfish_timer_set_next_event(LATCH);

    return IRQ_HANDLED;
}
//软中断,跟定时器又联系上了。
static void timer_softirq_action(void)
{
                int           cpu = smp_processor_id();
                struct timer *t, **heap;
                s_time_t      now;
                void        (*fn)(void *);
                void         *data;

                DPRINTK(5, %s:%dn, __FUNCTION__, __LINE__);
                spin_lock_irq(&timers[cpu].lock);  

                do {
                        heap = timers[cpu].heap;
                        now  = NOW();

                        while ( (GET_HEAP_SIZE(heap) != 0) && ( (t = heap[1])->expires < (now + (s_time_t)TIMER_SLOP) ) )
                        {
                                remove_entry(heap, t);
                                timers[cpu].running = t;
                                fn   = t->function;
                                data = t->data;
//      printk(“fn:%x”, fn);
                                spin_unlock_irq(&timers[cpu].lock);
                                (*fn)(data);//执行定时器routine

                                DPRINTK(5, %s:%dn, __FUNCTION__, __LINE__);
                                spin_lock_irq(&timers[cpu].lock);

                                /* Heap may have grown while the lock was released. */
                                heap = timers[cpu].heap;
                        }

                        timers[cpu].running = NULL;
                }
                while ( !reprogram_timer(GET_HEAP_SIZE(heap) ? heap[1]->expires : 0) );

                DPRINTK(5, %s:%dn, __FUNCTION__, __LINE__);

                spin_unlock_irq(&timers[cpu].lock);
}
/*
 从异常返回

 异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回:

 1、将连接寄存器LR的值减去相应的偏移量后送到PC中。

 2、将SPSR复制回CPSR中。

 3、若在进入异常处理时设置了中断禁止位,要在此清除。
 */

From Li Haifeng's Blog, post xenarm irq routine

Post Footer automatically generated by wp-posturl plugin for wordpress.

分享到: