Chinaunix首页 | 论坛 | 博客
  • 博客访问: 416992
  • 博文数量: 380
  • 博客积分: 75
  • 博客等级: 民兵
  • 技术积分: 1925
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-05 15:35
文章分类

全部博文(380)

文章存档

2014年(1)

2013年(2)

2012年(19)

2011年(358)

我的朋友

分类:

2011-09-11 11:19:33

首先,当然是进入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;
}


阅读(360) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~