您的位置:首页 > 运维架构 > Linux

ARM Linux中断分析

2012-08-08 16:26 363 查看
首先,当然是进入start_kernel,调用setup_arch进行平台体系(处理器芯片)相关的初始化,然后复制中断向量表到内存中并对irq进行初始化

/* init/main.c */

asmlinkage void __init start_kernel(void)

{

    ……

    setup_arch(&command_line);

    ……

    trap_init();

    ……

    init_IRQ();

    ……

}

trap_init复制中断向量表到内存地址CONFIG_VECTORS_BASE

/* arch/arm/kernel/traps.c */

void __init trap_init(void)

{

    unsigned long vectors = CONFIG_VECTORS_BASE;

    extern char __stubs_start[], __stubs_end[];

    extern char __vectors_start[], __vectors_end[];

    extern char __kuser_helper_start[], __kuser_helper_end[];

    int kuser_sz = __kuser_helper_end - __kuser_helper_start;

    /*

     * Copy the vectors, stubs and kuser helpers (in entry-armv.S)

     * into the vector page, mapped at 0xffff0000, and ensure these

     * are visible to the instruction stream.

     */

    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

    /*

     * Copy signal return handlers into the vector page, and

     * set sigreturn to be a pointer to these.

     */

    memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,

     sizeof(sigreturn_codes));

    flush_icache_range(vectors, vectors + PAGE_SIZE);

    modify_domain(DOMAIN_USER, DOMAIN_CLIENT);

}

init_IRQ对irq进行初始化时,又调用了init_arch_irq对于具体平台体系的中断进行初始化。而init_arch_irq是在start_kernel里调用setup_arch函数设定的

/* arch/arm/kernel/irq.c n */

void __init init_IRQ(void)

{

    int irq;

    for (irq = 0; irq < NR_IRQS; irq++)

        irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;

#ifdef CONFIG_SMP

    bad_irq_desc.affinity = CPU_MASK_ALL;

    bad_irq_desc.cpu = smp_processor_id();

#endif

    init_arch_irq();

}

setup_arch里指定了具体平台体系的中断初始化函数,其中mdesc是一个machine_desc结构体指针

/* arch/arm/kernel/setup.c */

void __init setup_arch(char **cmdline_p)

{

    ……

    init_arch_irq = mdesc->init_irq;

    ……

}

machine_desc是用于描述平台体系(处理器芯片)的结构体,其定义如下: /* include/asm/mach/arch.h */

struct machine_desc {

    /*

     * Note! The first four elements are used

     * by assembler code in head-armv.S

     */

    unsigned int        nr;                /* architecture number    */

    unsigned int        phys_io;        /* start of physical io    */

    unsigned int        io_pg_offst;    /* byte offset for io

                         * page tabe entry    */

    const char            *name;            /* architecture name    */

    unsigned long        boot_params;    /* tagged list        */

    unsigned int        video_start;    /* start of video RAM    */

    unsigned int        video_end;        /* end of video RAM    */

    unsigned int        reserve_lp0 :1;    /* never has lp0    */

    unsigned int        reserve_lp1 :1;    /* never has lp1    */

    unsigned int        reserve_lp2 :1;    /* never has lp2    */

    unsigned int        soft_reboot :1;    /* soft reboot        */

    void            (*fixup)(struct machine_desc *,

                     struct tag *, char **,

                     struct meminfo *);

    void            (*map_io)(void);    /* IO mapping function    */

    void            (*init_irq)(void);

    struct sys_timer    *timer;            /* system tick timer    */

    void            (*init_machine)(void);

};

/*

* Set of macros to define architecture features. This is built into

* a table by the linker.

*/

#define MACHINE_START(_type,_name)            \

static const struct machine_desc __mach_desc_##_type    \

__used                            \

__attribute__((__section__(".arch.info.init"))) = {    \

    .nr        = MACH_TYPE_##_type,        \

    .name      = _name,

#define MACHINE_END                \

};

对于s3c2410处理器芯片,其machine_desc变量的定义如下:

/* arch/arm/mach-s3c2410/mach-smdk2410.c */

MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch

                 * to SMDK2410 */

    /* Maintainer: Jonas Dietsche */

    .phys_io        = S3C2410_PA_UART,

    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

    .boot_params    = S3C2410_SDRAM_PA + 0x100,

    .map_io            = smdk2410_map_io,

    .init_irq        = s3c24xx_init_irq,

    .init_machine    = smdk2410_init,

    .timer            = &s3c24xx_timer,

MACHINE_END

接下来,我们看看s3c2410处理器芯片的irq初始化函数

/* arch/arm/plat-s3c24xx/irq.c */

/* s3c24xx_init_irq

*

* Initialise S3C2410 IRQ system

*/

/* s3c24xx系列芯片中断初始化函数 */

void __init s3c24xx_init_irq(void)

{

    unsigned long pend;

    unsigned long last;

    int irqno;

    int i;

    irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");

    /* first, clear all interrupts pending... */

    /* 清空各个中断请求信号 */

    last = 0;

    for (i = 0; i < 4; i++) {

        pend = __raw_readl(S3C24XX_EINTPEND);

        if (pend == 0 || pend == last)

            break;

        __raw_writel(pend, S3C24XX_EINTPEND);

        printk("irq: clearing pending ext status %08x\n", (int)pend);

        last = pend;

    }

    last = 0;

    for (i = 0; i < 4; i++) {

        pend = __raw_readl(S3C2410_INTPND);

        if (pend == 0 || pend == last)

            break;

        __raw_writel(pend, S3C2410_SRCPND);

        __raw_writel(pend, S3C2410_INTPND);

        printk("irq: clearing pending status %08x\n", (int)pend);

        last = pend;

    }

    last = 0;

    for (i = 0; i < 4; i++) {

        pend = __raw_readl(S3C2410_SUBSRCPND);

        if (pend == 0 || pend == last)

            break;

        printk("irq: clearing subpending status %08x\n", (int)pend);

        __raw_writel(pend, S3C2410_SUBSRCPND);

        last = pend;

    }

    /* register the main interrupts */

    /* 注册主要中断 */

    irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");

    for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {

        /* set all the s3c2410 internal irqs */

        switch (irqno) {

            /* deal with the special IRQs (cascaded) */

        case IRQ_EINT4t7:

        case IRQ_EINT8t23:

        case IRQ_UART0:

        case IRQ_UART1:

        case IRQ_UART2:

        case IRQ_ADCPARENT:

            set_irq_chip(irqno, &s3c_irq_level_chip);    /* 设置中断操作函数集(电平) */

            set_irq_handler(irqno, handle_level_irq);    /* 设置中断的高层流处理函数(电平) */

            break;

        case IRQ_RESERVED6:

        case IRQ_RESERVED24:

            /* no IRQ here */

            break;

        default:

            //irqdbf("registering irq %d (s3c irq)\n", irqno);

            set_irq_chip(irqno, &s3c_irq_chip);            /* 默认中断为边缘触发 */

            set_irq_handler(irqno, handle_edge_irq);

            set_irq_flags(irqno, IRQF_VALID);

        }

    }

    /* setup the cascade irq handlers */

    /* 设置中断的高层流处理函数 */

    set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);

    set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);

    set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);

    set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);

    set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);

    set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);

    /* external interrupts */

    for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {

        irqdbf("registering irq %d (ext int)\n", irqno);

        set_irq_chip(irqno, &s3c_irq_eint0t4);

        set_irq_handler(irqno, handle_edge_irq);

        set_irq_flags(irqno, IRQF_VALID);

    }

    for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {

        irqdbf("registering irq %d (extended s3c irq)\n", irqno);

        set_irq_chip(irqno, &s3c_irqext_chip);

        set_irq_handler(irqno, handle_edge_irq);

        set_irq_flags(irqno, IRQF_VALID);

    }

    /* register the uart interrupts */

    irqdbf("s3c2410: registering external interrupts\n");

    for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {

        irqdbf("registering irq %d (s3c uart0 irq)\n", irqno);

        set_irq_chip(irqno, &s3c_irq_uart0);

        set_irq_handler(irqno, handle_level_irq);

        set_irq_flags(irqno, IRQF_VALID);

    }

    for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {

        irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);

        set_irq_chip(irqno, &s3c_irq_uart1);

        set_irq_handler(irqno, handle_level_irq);

        set_irq_flags(irqno, IRQF_VALID);

    }

    for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {

        irqdbf("registering irq %d (s3c uart2 irq)\n", irqno);

        set_irq_chip(irqno, &s3c_irq_uart2);

        set_irq_handler(irqno, handle_level_irq);

        set_irq_flags(irqno, IRQF_VALID);

    }

    for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {

        irqdbf("registering irq %d (s3c adc irq)\n", irqno);

        set_irq_chip(irqno, &s3c_irq_adc);

        set_irq_handler(irqno, handle_edge_irq);

        set_irq_flags(irqno, IRQF_VALID);

    }

    irqdbf("s3c2410: registered interrupt handlers\n");

}

接着,我们来看一下中断处理过程 /* arch/arm/kernel/irq.c */

/*

* do_IRQ handles all hardware IRQ's. Decoded IRQs should not

* come via this function. Instead, they should provide their

* own 'handler'

*/

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

{

    struct pt_regs *old_regs = set_irq_regs(regs);    /* 保存寄存器中的内容 */

    struct irq_desc *desc = irq_desc + irq;           /* 获取中断源 */

    /*

     * Some hardware gives randomly wrong interrupts. Rather

     * than crashing, do something sensible.

     */

    if (irq >= NR_IRQS)

        desc = &bad_irq_desc;

    irq_enter();

    desc_handle_irq(irq, desc);            /* 调用已安装的中断高层流处理函数 */

    /* AT91 specific workaround */

    irq_finish(irq);

    irq_exit();

    set_irq_regs(old_regs);

}

接下来,我们以IRQ_UART2(s3c2410串口2中断)为例,看看中断是如何分流到我们通过request_irq注册的中断处理函数

在s3c24xx_init_irq里IRQ_S3CUART_TX2中断的中断高层流处理函数被设置为s3c_irq_demux_uart2。

s3c_irq_demux_uart2主要工作是调用s3c_irq_demux_uart对串口中断进行分流处理,因为串口中断包含Rx(接收)中断、Tx(发送)中断和Rx error(接收错误)中断

/* arch/arm/plat-s3c2410/irq.c */

static void s3c_irq_demux_uart2(unsigned int irq, struct irq_desc *desc)

{

    irq = irq;

    s3c_irq_demux_uart(IRQ_S3CUART_RX2);

}

s3c_irq_demux_uart通过副中断源引脚寄存器的值,判断中断源并执行相应的中断处理函数

/* arch/arm/plat-s3c2410/irq.c */

/* start: 标示发出该中断信号的是哪个uart,即其值应为

*            IRQ_S3CUART_RX0、IRQ_S3CUART_RX1或IRQ_S3CUART_RX2

*/

static void s3c_irq_demux_uart(unsigned int start)

{

    unsigned int subsrc, submsk;

    unsigned int offset = start - IRQ_S3CUART_RX0;    /* 获取该UART中断源在副中断屏蔽寄存器中的偏移量 */

    struct irq_desc *desc;

    /* read the current pending interrupts, and the mask

     * for what it is available */

    subsrc = __raw_readl(S3C2410_SUBSRCPND);        /* 读取副中断源引脚寄存器 */

    submsk = __raw_readl(S3C2410_INTSUBMSK);        /* 读取副中断屏蔽寄存器 */

    irqdbf2("s3c_irq_demux_uart: start=%d (%d), subsrc=0x%08x,0x%08x\n",

        start, offset, subsrc, submsk);

    subsrc &= ~submsk;        /* 只留下已使能(未屏蔽)的中断源 */

    subsrc >>= offset;        /* 截取发送中断的uart源到subsrc低3位 */

    subsrc &= 7;

    if (subsrc != 0)

    {

        desc = irq_desc + start;

        if (subsrc & 1)        /* Rx中断 */

            desc_handle_irq(start, desc);

        desc++;

        if (subsrc & 2)        /* Tx中断 */

            desc_handle_irq(start+1, desc);

        desc++;

        if (subsrc & 4)        /* Rx error中断 */

            desc_handle_irq(start+2, desc);

    }

}

desc_handle_irq实际上只是调用了desc->handle_irq来执行已安装的中断高层流处理函数。假设这里串口2发起的中断为Tx中断,即IRQ_S3CUART_TX2。IRQ_S3CUART_TX2的中断高层流处理函数已在s3c24xx_init_irq里通过set_irq_handler设置为handle_level_irq。

/* include/asm-arm/mach/irq.h */

/*

* Obsolete inline function for calling irq descriptor handlers.

*/

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)

{

    desc->handle_irq(irq, desc);

}

handle_level_irq主要工作是获取该中断的action链表(所有安装在该中断号上的中断处理函数其实就是一个action结构体,详见request_irq的实现),然后传递给handle_IRQ_event。

/* kernel/irq/chip.c */

/*

*    handle_level_irq - Level type irq handler

*    @irq:    the interrupt number

*    @desc:    the interrupt description structure for this irq

*

*    Level type interrupts are active as long as the hardware line has

*    the active level. This may require to mask the interrupt and unmask

*    it after the associated handler has acknowledged the device, so the

*    interrupt line is back to inactive.

*/

void fastcall handle_level_irq(unsigned int irq, struct irq_desc *desc)

{

    unsigned int cpu = smp_processor_id();

    struct irqaction *action;

    irqreturn_t action_ret;

    spin_lock(&desc->lock);

    mask_ack_irq(desc, irq);

    if (unlikely(desc->status & IRQ_INPROGRESS))

        goto out_unlock;

    desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);

    kstat_cpu(cpu).irqs[irq]++;

    /*

     * If its disabled or no action available

     * keep it masked and get out of here

     */

    /* 获取该中断的action链表 */

    action = desc->action;

    if (unlikely(!action || (desc->status & IRQ_DISABLED)))

        goto out_unlock;

    desc->status |= IRQ_INPROGRESS;

    spin_unlock(&desc->lock);

    /* 调用handle_IRQ_event,执行已安装在该中断号上的所有中断函数 */

    action_ret = handle_IRQ_event(irq, action);

    if (!noirqdebug)

        note_interrupt(irq, desc, action_ret);

    spin_lock(&desc->lock);

    desc->status &= ~IRQ_INPROGRESS;

    if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)

        desc->chip->unmask(irq);

out_unlock:

    spin_unlock(&desc->lock);

}

handle_IRQ_event主要作用就是执行那些已安装的中断处理函数action->handler /* kernel/irq/handle.c */

/*

* handle_IRQ_event - irq action chain handler

* @irq:    the interrupt number

* @action:    the interrupt action chain for this irq

*

* Handles the action chain of an irq event

*/

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)

{

    irqreturn_t ret, retval = IRQ_NONE;

    unsigned int status = 0;

    handle_dynamic_tick(action);

    if (!(action->flags & IRQF_DISABLED))

        local_irq_enable_in_hardirq();

    /* 循环调用安装在该中断号irq上的中断处理函数 */

    do

    {

        /* action->handler就是我们调用request_irq时,

         * 安装的中断处理函数 */

        ret = action->handler(irq, action->dev_id);   

        if (ret == IRQ_HANDLED)

            status |= action->flags;

        retval |= ret;

        action = action->next;

    } while (action);

    if (status & IRQF_SAMPLE_RANDOM)

        add_interrupt_randomness(irq);

    local_irq_disable();

    return retval;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息