×
单片机 > 单片机程序设计 > 详情

ARM Linux中断机制之中断的初始化

发布时间:2020-06-05 发布时间:
|
一,认识几个重要结构体:

1.中断描述符

对于每一条中断线都由一个irq_desc结构来描述。

//在include/linux/irq.h中

struct irq_desc {
 unsigned int  irq;//中断号
 struct timer_rand_state *timer_rand_state;
 unsigned int            *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
 struct irq_2_iommu      *irq_2_iommu;
#endif

/*

在kernel/irq/chip.c中实现了5个函数:handle_simple_irq(),handle_level_irq(), 
handle_edge_irq(),handle_fasteoi_irq()以及handle_percpu_irq()。 handle_irq指针可
以指向这5个函数中的一个, 选择一种中断事件处理策略, 这是通过函数set_irq_handler()
完成的

*/
 irq_flow_handler_t handle_irq;//上层中断处理函数,
 struct irq_chip  *chip;//底层硬件操作
 struct msi_desc  *msi_desc;
 void   *handler_data;//附加参数,用于handle_irq
 void   *chip_data;//平台相关附加参数,用于chip
 struct irqaction *action; /* IRQ action list *///中断服务例程链表
 unsigned int  status;  /* IRQ status *///中断当前的状态

//中断关闭打开层数调用一次disable_irq( ) ,depth加1;调用一次enable_irq( )该值减1,

//如果depth等于0就开启这条中断线,如果depth大于0就关闭中断线。

 unsigned int  depth; 
 unsigned int  wake_depth; ////* 唤醒次数 */ 
 unsigned int  irq_count; /* 发生的中断次数 */ 
 unsigned long  last_unhandled; /* Aging timer for unhandled count */
 unsigned int  irqs_unhandled;
 spinlock_t  lock;
#ifdef CONFIG_SMP
 cpumask_var_t  affinity;
 unsigned int  cpu;
#ifdef CONFIG_GENERIC_PENDING_IRQ
 cpumask_var_t  pending_mask;
#endif
#endif
 atomic_t  threads_active;
 wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PROC_FS
 struct proc_dir_entry *dir;///proc/irq/ 入口
#endif
 const char  *name;///proc/interrupts 中显示的中断名称
} ____cacheline_internodealigned_in_smp;


/*在kernel/irq/handle.c中有个全局irq_desc数组,描述了系统中所有的中断线:

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
 [0 ... NR_IRQS-1] = {
  .status = IRQ_DISABLED,
  .chip = &no_irq_chip,
  .handle_irq = handle_bad_irq,
  .depth = 1,
  .lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
 }
};

NR_IRQS为最大中断数对于s3c2410芯片,在文件arch/arm/mach-s3c2410/include/mach/irqs.h中定义如下:

#ifdef CONFIG_CPU_S3C2443
#define NR_IRQS (IRQ_S3C2443_AC97+1)
#else
#define NR_IRQS (IRQ_S3C2440_AC97+1)//每一个中断源都对应一个irq_desc结构体。
#endif

*/

2. 中断硬件操作函数集

//在include/linux/irq.h中定义

//该结构体中各函数在文件linux/arch/arm/plat-s3c24xx/irq.c中实现

struct irq_chip {
 const char *name; //用于 /proc/interrupts
 unsigned int (*startup)(unsigned int irq); //默认为 enable 如果为NULL
 void  (*shutdown)(unsigned int irq); //默认为 disable 如果为NULL
 void  (*enable)(unsigned int irq); //允许中断,默认为 unmask 如果为NULL
 void  (*disable)(unsigned int irq); //禁止中断,默认为 mask 如果为 NULL

 void  (*ack)(unsigned int irq); //响应一个中断,清除中断标志
 void  (*mask)(unsigned int irq); //mask 一个中断源,通常是关闭中断
 void  (*mask_ack)(unsigned int irq); //响应并 mask 中断源
 void  (*unmask)(unsigned int irq); //unmask 中断源
 void  (*eoi)(unsigned int irq);

 void  (*end)(unsigned int irq);
 void  (*set_affinity)(unsigned int irq,
     const struct cpumask *dest);
 int  (*retrigger)(unsigned int irq);
 int  (*set_type)(unsigned int irq, unsigned int flow_type); //设置中断触发方式 IRQ_TYPE_LEVEL
 int  (*set_wake)(unsigned int irq, unsigned int on);

 /* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
 void  (*release)(unsigned int irq, void *dev_id);
#endif
 /*
  * For compatibility, ->typename is copied into ->name.
  * Will disappear.
  */
 const char *typename;
};

3.中断处理例程描述符

//在include/linux/interrupt.h中

struct irqaction {
 irq_handler_t handler; /* 具体的中断处理程序 */ 
 unsigned long flags; //用一组标志描述中断线与 I/O 设备之间的关系。
 cpumask_t mask;
 const char *name; /* 名称,会显示在/proc/interreupts中 */ 
 void *dev_id; /* 设备ID,用于区分共享一条中断线的多个处理程序 ,以便从共享中断线的诸多中断处理程序中删除指定的那一个*/ 
 struct irqaction *next; /* 指向下一个irq_action结构 */ 
 int irq;  /* 中断通道号 */ 
 struct proc_dir_entry *dir;  /* procfs目录 */
 irq_handler_t thread_fn;
 struct task_struct *thread;
 unsigned long thread_flags;
};

 

这三个结构体间的关系表示如下

 
二,中断初始化过程
 
中断机制的初始化通过 两个函数完成:early_trap_init()和init_IRQ(),在此我们先讨论函数init_IRQ()。
//函数init_IRQ在文件linux/arch/arm/kernel/irq.c中实现。
void __init init_IRQ(void)
{
 int irq;
/*  设置 irq_desc 数组的 status 为 IRQ_NOREQUEST | IRQ_NOPROBE(没有请求,没有检测)  */
 for (irq = 0; irq < NR_IRQS; irq++)
  irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
#ifdef CONFIG_SMP
 cpumask_setall(bad_irq_desc.affinity);
 bad_irq_desc.cpu = smp_processor_id();
#endif
/*
init_arch_irq在文件linux/arch/arm/kernel/irq.c中定义如下
void (*init_arch_irq)(void) __initdata = NULL;
该函数指针 在 setup_arch()中被赋值,
init_arch_irq = mdesc->init_irq;
指向 machine_desc 中定义的 init_irq 函数。
在平台smdk2440中,该函数在文件linux/arch/arm/plat-s3c24xx/irq.c中实现。
*/
 init_arch_irq();
}
//函数s3c24xx_init_irq在文件linux/arch/arm/plat-s3c24xx/irq.c中实现
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); //清除外部中断寄存器EINTPEND中的请求标志,
  printk("irq: clearing pending ext status %08x\n", (int)pend);
  last = pend;
 }
 last = 0;

。。。。。。
 
 //设置各中断的底层硬件操作函数集desc->chip,中断上层处理函数desc->handle_irq
 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);
  }
 }
 //以下几个中断都有多个中断源,每一个中断源也都有各自的中断号,它们的多个中断源中任意一个产生中断
//该中断都会被触发,而不是直接出发子中断。这几个中断并不处理中断函数,它们的中作是计算子中断的中断号,
//并根据子中断的中断号在数组irq_desc[NR_IRQS]中去找出该中断号对应的irq_desc结构,并调用该结构中的中断处理函数。
 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);
 
。。。。。。
 
 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);
 }
 
。。。。。。
 
}

 比如此时外部中断10产生了中断,中断号为IRQ_EINT8t23的中断被触发,执行函数s3c_irq_demux_extint8()。

static void
s3c_irq_demux_extint8(unsigned int irq,
        struct irq_desc *desc)
{
 unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);
 unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);

 eintpnd &= ~eintmsk;
 eintpnd &= ~0xff; /* ignore lower irqs */

 /* we may as well handle all the pending IRQs here */

 while (eintpnd) {
  irq = __ffs(eintpnd);  //计算该中断在外部中断寄存器EINTPEND中的偏移量
  eintpnd &= ~(1<

  irq += (IRQ_EINT4 - 4); //根据这个偏移量重新计算中断号
  generic_handle_irq(irq); //根据重新计算的中断号获取对应的结构体irq_desc,并调用它的上层中断处理函数。
 }

}

 

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
 desc->handle_irq(irq, desc); //直接调用上层中断处理函数
#else
 if (likely(desc->handle_irq))
  desc->handle_irq(irq, desc);
 else
  __do_IRQ(irq); //通用中断处理函数,该函数最终调用desc->handle_irq(irq, desc); 
#endif
}

 

上层中断处理函数

上层中断处理函数有5个分别为:handle_simple_irq(),handle_level_irq(), 
handle_edge_irq(),handle_fasteoi_irq()以及handle_percpu_irq()。

这几个函数在文件kernel/irq/chip.c中实现。常用的有两个handle_level_irq(),和handle_edge_irq()。

这5个上层中断处理函数都是通过调用函数handle_IRQ_event()来做进一步处理。

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
 irqreturn_t ret, retval = IRQ_NONE;
 unsigned int status = 0;

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

 do {


。。。。。。


  ret = action->handler(irq, action->dev_id); //执行中断处理函数。


。。。。。。

 

  retval |= ret;
  action = action->next;
 } while (action); //调用该中断线上的所有例程

 if (status & IRQF_SAMPLE_RANDOM)
  add_interrupt_randomness(irq);
 local_irq_disable();

 return retval;
}

 

void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
 struct irqaction *action;
 irqreturn_t action_ret;

。。。。。。


 desc = irq_remap_to_desc(irq, desc);

。。。。。。


 action = desc->action;

 action_ret = handle_IRQ_event(irq, action);


。。。。。。


}

 

void
handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
 spin_lock(&desc->lock);

。。。。。。
  desc = irq_remap_to_desc(irq, desc);
。。。。。。
 desc->status |= IRQ_INPROGRESS;

 do {
  struct irqaction *action = desc->action;
 。。。。。。

 

  desc->status &= ~IRQ_PENDING;
  spin_unlock(&desc->lock);
  action_ret = handle_IRQ_event(irq, action);
  if (!noirqdebug)
   note_interrupt(irq, desc, action_ret);
  spin_lock(&desc->lock);

//该函数与函数handle_level_irq不太一样的是,该函数多了一个循环。即如果在本次中断

//的处理过程中该中断线上又有中断产生,则再次执行该中断线上的处理例程

/*

以下是5个常用的中断线状态。

#define IRQ_INPROGRESS 1 /* 正在执行这个 IRQ 的一个处理程序 */
#define IRQ_DISABLED 2 /* 由设备驱动程序已经禁用了这条 IRQ 中断线 */

#define IRQ_PENDING 4 /* 一个 IRQ 已经出现在中断线上,且被应答,但还没有
为它提供服务 */
#define IRQ_REPLAY 8 /* 当 Linux 重新发送一个已被删除的 IRQ 时 */
#define IRQ_WAITING 32 /* 当对硬件设备进行探测时,设置这个状态以标记正在被
测试的 irq */

*/

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

 desc->status &= ~IRQ_INPROGRESS;
out_unlock:
 spin_unlock(&desc->lock);
}




『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
51单片机CO2检测显示程序解析