yiffer的个人空间 https://blog.eetop.cn/edesign [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

ARM Linux对中断的处理--中断管理系统的初始化

已有 5321 次阅读| 2010-7-20 23:03 |个人分类:Linux移植

中断管理系统的初始化

我们先来看一下Linux系统中,中断管理系统的初始化。中断系统的初始化主要由两个函数来完成。在系统初始化的start_kernel()函数 (在文件init/main.c中定义)中可以看到:

asmlinkage void __init start_kernel(void)

{

……

   trap_init();

……

   early_irq_init();

   init_IRQ();

……

}

start_kernel()函数调用early_irq_init()init_IRQ()两个函数来初始化中断管理系统。

early_irq_init()函数

start_kernel()函数中调用了early_irq_init()函数,这个函数在kernel/handle.c文件中定义。这个函数将用于管理中断的irq_desc[NR_IRQS]数组的每个元素的部分字段设置为确定的状态,它设置每一个成员的中断号,

int __init early_irq_init(void)

{

   struct irq_desc *desc;

   int count;

   int i;

 

   init_irq_default_affinity();

 

   printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);

 

   desc = irq_desc;

   count = ARRAY_SIZE(irq_desc);

 

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

      desc[i].irq = i;

      alloc_desc_masks(&desc[i], 0, true);

      init_desc_masks(&desc[i]);

      desc[i].kstat_irqs = kstat_irqs_all[i];

   }

   return arch_early_irq_init();

}

init_IRQ()函数

init_IRQ(void)函数是一个特定与体系结构的函数,对于ARM体系结构,在文件arch/arm/kernel/irq.c中定义,这个函数将用于管理中断的irq_desc[NR_IRQS]结构数组各成员的状态字段设置为IRQ_NOREQUEST | IRQ_NOPROBE,也就是未请求和未探测状态。然后调用特定平台的中断初始化的init_arch_irq()函数:

void __init init_IRQ(void)

{

   int irq;

 

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

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

   init_arch_irq();

}

init_arch_irq()实际上是一个函数指针,同样在文件arch/arm/kernel/irq.c中定义:

void (*init_arch_irq)(void) __initdata = NULL;

这个指针由函数setup_arch()初始化,而setup_arch()也是在start_kernel()中调用的。setup_arch()也是一个特定于体系架构的函数,在arch/arm/kernel/setup.c中定义:

void __init setup_arch(char **cmdline_p)

{

……

    init_arch_irq = mdesc->init_irq;

……

}

init_arch_irq指针被初始化为mdesc结构的init_irq成员,这是一个struct machine_desc类型的结构,mach_desc里定义了一些关键的体系架构相关的信息。以mini2440为例,其machine_desc结构的init_irq成员在文件arch/arm/mach-s3c2440/mach-mini2440.c中被赋值为s3c24xx_init_irq函数:

MACHINE_START(MINI2440, "MINI2440")

   /* Maintainer: Michel Pollet <buserror@gmail.com> */

   .phys_io = S3C2410_PA_UART,

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

   .boot_params = S3C2410_SDRAM_PA + 0x100,

   .map_io     = mini2440_map_io,

   .init_machine   = mini2440_init,

   .init_irq = s3c24xx_init_irq,

   .timer   = &s3c24xx_timer,

MACHINE_END

注:MACHINE_START宏的作用是对mach_desc结构体进行初始化。OK,终于找到了init_IRQ() 函数中调用的init_arch_irq()函数的真正内容,在arch/arm/plat-s3c24xx/irq.c中定义:

/* s3c24xx_init_irq

 *

 * Initialise S3C2410 IRQ system

*/

 

void __init s3c24xx_init_irq(void)

{

   unsigned long pend;

   unsigned long last;

   int irqno;

   int i;

 

#ifdef CONFIG_FIQ

   init_FIQ();

#endif

 

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

}

这个函数完成对于中断控制器的初始化,并且设置中断描述符的相应的函数指针的值,以在中断发生时发生时,调用这些函数来完成芯片级的处理。

这个函数又调用了许多相关的函数来初始化中断描述。先看set_irq_chip()函数,在文件kernel/irq/chip.c中定义:

/**

 *  set_irq_chip - set the irq chip for an irq

 *  @irq:  irq number

 *  @chip: pointer to irq chip description structure

 */

int set_irq_chip(unsigned int irq, struct irq_chip *chip)

{

   // 获取对应于中断号irq的中断描述符

   struct irq_desc *desc = irq_to_desc(irq);

   unsigned long flags;

 

   if (!desc) {

      WARN(1, KERN_ERR "Trying to install chip for IRQ%d\n", irq);

      return -EINVAL;

   }

 

   if (!chip)

      chip = &no_irq_chip;

 

   spin_lock_irqsave(&desc->lock, flags);

// chip设置一些默认的enable,disable函数

   irq_chip_set_defaults(chip);

   desc->chip = chip;

   spin_unlock_irqrestore(&desc->lock, flags);

 

   return 0;

}

设置中断描述符的chip,以在中断发生或者管理中断时可以完成对于芯片的操作。

IRQ_EINT4t7为例,它的chips3c_irq_level_chip,在文件arch/arm/plat-s3c24xx/irq.c中定义:

struct irq_chip s3c_irq_level_chip = {

   .name    = "s3c-level",

   .ack     = s3c_irq_maskack,

   .mask    = s3c_irq_mask,

   .unmask     = s3c_irq_unmask,

   .set_wake = s3c_irq_wake

};

再看irq_chip_set_defaults()函数,在kernel/irq/chip.c中定义:

/*

 * Fixup enable/disable function pointers

 */

void irq_chip_set_defaults(struct irq_chip *chip)

{

   if (!chip->enable)

      chip->enable = default_enable;

   if (!chip->disable)

      chip->disable = default_disable;

   if (!chip->startup)

      chip->startup = default_startup;

   /*

    * We use chip->disable, when the user provided its own. When

    * we have default_disable set for chip->disable, then we need

    * to use default_shutdown, otherwise the irq line is not

    * disabled on free_irq():

    */

   if (!chip->shutdown)

      chip->shutdown = chip->disable != default_disable ?

         chip->disable : default_shutdown;

   if (!chip->name)

      chip->name = chip->typename;

   if (!chip->end)

      chip->end = dummy_irq_chip.end;

}

如果chip没有相应的操作函数,则就给chip赋默认的操作函数。

 

接着看set_irq_handler()函数,在include/linux/irq.h中定义:

/*

 * Set a highlevel flow handler for a given IRQ:

 */

static inline void

set_irq_handler(unsigned int irq, irq_flow_handler_t handle)

{

   __set_irq_handler(irq, handle, 0, NULL);

}

 

然后来看__set_irq_handler()函数,在kernel/irq/chip.c中:

void

__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,

        const char *name)

{

   struct irq_desc *desc = irq_to_desc(irq);

   unsigned long flags;

 

   if (!desc) {

      printk(KERN_ERR

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

      return;

   }

 

   if (!handle)

      handle = handle_bad_irq;

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

   }

 

   chip_bus_lock(irq, desc);

   spin_lock_irqsave(&desc->lock, flags);

 

   /* Uninstall? */

   if (handle == handle_bad_irq) {

      if (desc->chip != &no_irq_chip)

         mask_ack_irq(desc, irq);

      desc->status |= IRQ_DISABLED;

      desc->depth = 1;

   }

   desc->handle_irq = handle;

   desc->name = name;

 

   if (handle != handle_bad_irq && is_chained) {

      desc->status &= ~IRQ_DISABLED;

      desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;

      desc->depth = 0;

      desc->chip->startup(irq);

   }

   spin_unlock_irqrestore(&desc->lock, flags);

   chip_bus_sync_unlock(irq, desc);

}

上面这个函数就是为特定的中断号设置好一个中断处理例程,这里的例程可不是我们request_irq注册的例程,Linux支持中断共享,共享同一个中断号的每一设备都可以有自己特定的中断处理程序,用来描述这些中断处理程序的结构会形成一个链表,这里设置的例程将会逐个调用特定中断号上的各设备的中断处理例程。

 

接着看set_irq_flags()函数,在


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 3

    粉丝
  • 0

    好友
  • 20

    获赞
  • 69

    评论
  • 3705

    访问数
关闭

站长推荐 上一条 /1 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-3-29 17:35 , Processed in 0.016330 second(s), 7 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网