linux下中断子系统

在驱动程序初始化时,若使用到中断,通常调用函数reqeust_irq() 建立该驱动程序对应的irqaction结构体,并登记到irq_desc[irq_num]->action链表中去。

当发生中断后,首先获取触发中断的HW interrupt ID,然后通过irq domain翻译成IRQ number,(1.找到root interrupt controller对应的irq_domian;2.根据HW寄存器信息和irq_domain获取HW interrupt ID;3.调用irq_find_mapping找到对应的irq number)就可以获取对应的中断描述符。调用中断描述符中饿irq_events_handler来进行中断处理就OK了。

在device tree初始化的时候,形成了系统内所有的device node的树状结构(当然其中包括所有和中断外设的node)

在machine driver初始化的时候会调用of_irq_init函数,在该函数中会扫描所有interrupt controller的节点,并调用合适的interrupt controller driver进行初始化。


中断控制器是连接外设中断系统和CPU系统的桥梁。

驱动程序在请求中断服务时,会使用IRQ编号注册该中断,中断发生时,cpu通常会从中断控制器中获得相关信息,然后计算出相应的IRQ编号,然后把该IRQ编号传递给驱动程序。

对底层的封装主要包括:

实现不同i体系结构中断入口,通常用汇编实现

中断控制器进行封装和实现

系统启动阶段,中断子系统完成了必要的初始化操作,为驱动程序申请中断服务做好了准备。


实际上软中断更多的是在中断的退出阶段执行(irq_exit),以便达到更快的相应,加入守护进程机制,只是担心一旦有大量的软中断等待执行,会使得内核过长的停留在中断上下文中。


  几个比较关键的数据结构

struct irqaction //

 struct irqaction {
         irq_handler_t handler;  
         unsigned long flags;    
         void *dev_id;           
         struct irqaction *next; 
         int irq;
         irq_handler_t thread_fn;
         struct task_struct *thread;
         unsigned long thread_flags;
         unsigned long thread_mask;
         const char *name;
         struct proc_dir_entry *dir;
 } ____cacheline_internodealigned_in_smp;      

struct irq_desc //interrupt desciptor

  struct irq_desc {
          struct irq_data         irq_data;
          struct timer_rand_state *timer_rand_state;
          unsigned int __percpu   *kstat_irqs;
          irq_flow_handler_t      handle_irq;
  #ifdef CONFIG_IRQ_PREFLOW_FASTEOI
          irq_preflow_handler_t   preflow_handler;
  #endif   
          struct irqaction        *action;        /* IRQ action list */
          unsigned int            status_use_accessors;
          unsigned int            core_internal_state__do_not_mess_with_it;
          unsigned int            depth;          /* nested irq disables */
          unsigned int            wake_depth;     /* nested wake enables */
          unsigned int            irq_count;      /* For detecting broken IRQs */
          unsigned long           last_unhandled; /* Aging timer for unhandled count */
          unsigned int            irqs_unhandled;
          raw_spinlock_t          lock;
  #ifdef CONFIG_SMP
          const struct cpumask    *affinity_hint;
          struct irq_affinity_notify *affinity_notify;
  #ifdef CONFIG_GENERIC_PENDING_IRQ
          cpumask_var_t           pending_mask;
  #endif
  #endif
          unsigned long           threads_oneshot;
          atomic_t                threads_active;
          wait_queue_head_t       wait_for_threads;
  #ifdef CONFIG_PROC_FS
          struct proc_dir_entry   *dir;
  #endif
          const char              *name;
  } ____cacheline_internodealigned_in_smp;

struct irq_chip //hardware interrupt chip descriptor中断控制器

 struct irq_chip {
         const char      *name;
         unsigned int    (*irq_startup)(struct irq_data *data);
         void            (*irq_shutdown)(struct irq_data *data);
         void            (*irq_enable)(struct irq_data *data);
         void            (*irq_disable)(struct irq_data *data);
 
         void            (*irq_ack)(struct irq_data *data);
         void            (*irq_mask)(struct irq_data *data);
         void            (*irq_mask_ack)(struct irq_data *data);
         void            (*irq_unmask)(struct irq_data *data);
         void            (*irq_eoi)(struct irq_data *data);
         
         int             (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
         int             (*irq_retrigger)(struct irq_data *data);
         int             (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
         int             (*irq_set_wake)(struct irq_data *data, unsigned int on);
                    
         void            (*irq_bus_lock)(struct irq_data *data);
         void            (*irq_bus_sync_unlock)(struct irq_data *data);
         
         void            (*irq_cpu_online)(struct irq_data *data);
         void            (*irq_cpu_offline)(struct irq_data *data);
                 
         void            (*irq_suspend)(struct irq_data *data);
         void            (*irq_resume)(struct irq_data *data);
         void            (*irq_pm_shutdown)(struct irq_data *data);
         
         void            (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
 
         unsigned long   flags;
 
         /* Currently used only by UML, might disappear one day.*/
 #ifdef CONFIG_IRQ_RELEASE_METHOD
         void            (*release)(unsigned int irq, void *dev_id);
 #endif
 };
  request_irq().

  request_thread_irq().//分配中断资源,使能中断线和中断处理函数。

irq_to_desc();//将中断号变成中断描述符指针,主要是通过1.radix_tree_lookup(&irq_desc_tree, irq)获得 2.irq_desc[irq]获得。两者的区别在是否配置CONFIG_SPARSE_IRQ

之后进行一系列的参数赋值。

最终调用_setup_irq()去注册中断处理函数。

对不同的irq flags做对应的处理

register_irq_proc();

register_handler_proc();


在init/main.c中的strat_kernel函数中有early_init_irq()和init_IRQ(),softirq_init().

init_IRQ().是cpu厂商自己编写的,不同的cpu代码不同。

先将每个中断向量号设置为noprobe。(irq_set_noprobe)

再调用硬件相关代码,mach_init_IRQ(),该函数值之前setup_arch()中的config_BSP()已经被注册回调函数,

  void __init config_BSP(void)
  {
          printk("C-SKY Silan IVS2 Board\n");
  
          mach_time_init = xxx_timer_init;
          mach_hwclk = xxx_hwclk;
          mach_init_IRQ = xxx_init_IRQ;
          mach_get_auto_irqno = xxx_get_auto_irqno;
          mach_reset = xxx_machine_restart;
  #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
          mach_tick = xxx_tick;
          mach_gettimeoffset = xxx_timer_offset;
  #endif  
  #ifdef CONFIG_CPU_USE_FIQ
          mach_init_FIQ = xxx_init_FIQ;
  #endif
          prom_meminit();
  }

最后调用在arch/xxx_cpu/xxx_sys/irq.c下的xxx_init_IRQ()

  irq_set_chip_and_handler(i, xxx_irq_chip, handle_level_irq).

参数中有一个函数指针handle_level_irq表示是电平中断流控.

 void    
 irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
                               irq_flow_handler_t handle, const char *name)
 {       
         irq_set_chip(irq, chip);
         __irq_set_handler(irq, handle, 0, name);
 }    

最终调用两个函数

irq_set_chip().将中断控制器irq_chip赋给irq_desc->irq_data.chip;

__irq_set_handler().将中断触发方式赋给irq_desc->handle_irq.

软中断更多的是在中断的退出阶段执行,以便达到更快的响应,加入守护进程机制,只是担心一旦有大量的软中断等待执行,会使得内核过长地留在中断上下文中。

softirq_init().中关键的是两个函数

open_softirq(TASKLET_SOFTIRQ, tasklet_action);

open_softirq(HI_SOFTIRQ, tasklet_hi_action);

想要说明一点,tasklet是建立在软中断上的一种延迟执行机制,它的实现基于TASKLET_SOFTIRQ和HI_SOFTIRQ这两种软中断类型。

 中断向量号在arcj/xxx_cpu/xxx_sys/include/mach/xxx_irq.h中



你可能感兴趣的:(linux下中断子系统)