Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2161159
  • 博文数量: 361
  • 博客积分: 10828
  • 博客等级: 上将
  • 技术积分: 4161
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-20 14:34
文章分类

全部博文(361)

文章存档

2011年(132)

2010年(229)

分类: LINUX

2010-01-27 10:24:47

1 中断初始化

参见  arch\arm\mach-s3c2410\Irq.c

void __init s3c24xx_init_irq(void)

{

   

    /* 1. 清除所有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;

    }

 

    /* 注册中断处理函数. */

    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) {

            /* 这几个中断是级联的,所以中断例程也不一样.*/

        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, do_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, do_edge_IRQ);  /*对于不是级联的中断,这个就是中断例程.*/

            set_irq_flags(irqno, IRQF_VALID);

        }

    }

 

    /* 重新设置级联的中断例程*/

    set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint); 

    set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint);

 

    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);

 

    /* 为外部中断设置中断例程*/

    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, do_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, do_edge_IRQ);

        set_irq_flags(irqno, IRQF_VALID);

    }

 

    /* UART设置中断例程,*/

    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, do_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, do_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, do_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, do_edge_IRQ);

        set_irq_flags(irqno, IRQF_VALID);

    }

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

}

所谓级联的中断, 即指有几个中断共用同一个中断线, 比如EINT4-EINT7就共用一个中断线:EINT4_7. 假如现在系统产生一个EINT4的中断, 大致的中断流程顺序是: …-> s3c_irq_demux_extint -> do_edge_IRQ -> 我们注册的中断例程. 后面我们会详细讲解.

接下来我们主要set_irq_handle()函数, set_irq_chip()主要用来设置对这个中断mask, unmask等操作的函数, 它们会在适当的地方被调用.

static inline void

set_irq_handler(unsigned int irq,

        void fastcall (*handle)(unsigned int, struct irq_desc *,

                   struct pt_regs *))

{

    __set_irq_handler(irq, handle, 0);

}

void

__set_irq_handler(unsigned int irq,

          void fastcall (*handle)(unsigned int, irq_desc_t *,

                     struct pt_regs *),

          int is_chained)

{

    struct irq_desc *desc;

    unsigned long flags;

 

    if (irq >= NR_IRQS) {

        printk(KERN_ERR

               "Trying to install type control for IRQ%d\n", irq);

        return;

    }

   

    /* irq_desc 是一个静态数组,存放每个中断信息*/

    desc = irq_desc + irq;

 

    if (!handle)

        handle = handle_bad_irq;

 

    if (desc->chip == &no_irq_chip) {

        printk(KERN_WARNING "Trying to install %sinterrupt handler "

               "for IRQ%d\n", is_chained ? "chained " : " ", irq);

        /*

         * Some ARM implementations install a handler for really dumb

         * interrupt hardware without setting an irq_chip. This worked

         * with the ARM no_irq_chip but the check in setup_irq would

         * prevent us to setup the interrupt at all. Switch it to

         * dummy_irq_chip for easy transition.

         */

        desc->chip = &dummy_irq_chip;

    }

 

    spin_lock_irqsave(&desc->lock, flags);

 

    /* Uninstall? */

    if (handle == handle_bad_irq) {

        if (desc->chip != &no_irq_chip) {

            desc->chip->mask(irq);

            desc->chip->ack(irq);

        }

        desc->status |= IRQ_DISABLED;

        desc->depth = 1;

    }

    desc->handle_irq = handle;  /*保存中断例程*/

 

    if (handle != handle_bad_irq && is_chained) {

        desc->status &= ~IRQ_DISABLED;

        desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;

        desc->depth = 0;

        desc->chip->unmask(irq);  /*对于级联的中断会先调用unmask来打开中断*/

    }

    spin_unlock_irqrestore(&desc->lock, flags);

}

每个中断都有一个irq_desc的对象来描述它,这里面保存了这个中断的状态,处理函数等信息.

当中断产生时比如EINT4, 大致的流程如下:

先调用在初始化里注册好的s3c_irq_demux_extint() (EINT4,EINT5,EINT6,EINT7都先调用它)

static void

s3c_irq_demux_extint(unsigned int irq,

             struct irqdesc *desc,

             struct pt_regs *regs)

{

    unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);  /*先获取具体是哪个EINT产生中断.*/

    unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);

 

    eintpnd &= ~eintmsk;  /*检查是否被屏蔽了*/

 

    if (eintpnd) {

        irq = fls(eintpnd);   /*获取相对于EINT0的中断号*/

        irq += (IRQ_EINT4 - (4 + 1));   /*算出在整个中断控制器下的中断号, 这里就是IRQ_EINT4*/

 

        desc_handle_irq(irq, irq_desc + irq, regs); /*IRQ_EINT4调用这个函数.*/

    }

}

Static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs)

{

      /*这里的desc就代表的是EINT4, 所以这里实际调用了do_edge_IRQ(在初始化时注册的) */

        desc->handle_irq(irq, desc, regs);

}

#define do_edge_IRQ  handle_edge_irq

void fastcall

handle_edge_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs)

{

    const unsigned int cpu = smp_processor_id();

 

    spin_lock(&desc->lock);

 

    desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);  /*修改状态*/

 

    /*

     * If we're currently running this IRQ, or its disabled,

     * we shouldn't process the IRQ. Mark it pending, handle

     * the necessary masking and go out

     */

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

            !desc->action)) {

        desc->status |= (IRQ_PENDING | IRQ_MASKED);

        mask_ack_irq(desc, irq);

        goto out_unlock;

    }

 

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

 

    /* Start handling the irq */

    desc->chip->ack(irq);  /*清除中断标记*/

 

    /* Mark the IRQ currently in progress.*/

    desc->status |= IRQ_INPROGRESS;  /*设置状态*/

 

    do {

        struct irqaction *action = desc->action;  /*这个action会在以后介绍*/

        irqreturn_t action_ret;

 

        if (unlikely(!action)) {

            desc->chip->mask(irq);

            goto out_unlock;

        }

 

        /*

         * When another irq arrived while we were handling

         * one, we could have masked the irq.

         * Renable it, if it was not disabled in meantime.

         */

        if (unlikely((desc->status &

                   (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==

                  (IRQ_PENDING | IRQ_MASKED))) {

            desc->chip->unmask(irq);

            desc->status &= ~IRQ_MASKED;

        }

 

        desc->status &= ~IRQ_PENDING;

        spin_unlock(&desc->lock);

        action_ret = handle_IRQ_event(irq, regs, action);  /*处理具体中断*/

        if (!noirqdebug)

            note_interrupt(irq, desc, action_ret, regs);

        spin_lock(&desc->lock);

 

    } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);

 

    desc->status &= ~IRQ_INPROGRESS;

out_unlock:

    spin_unlock(&desc->lock);

}

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

{

      ….

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

      ….

}

其中action->handler就是我们用request_irq()注册的我们自己的中断例程. 具体原理可以参考这个函数的实现.
阅读(741) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~