再读uclinux-2008r1-rc8(bf561)内核的中断处理(1):数据结构

 
uclinux内核为每一个中断设置了一个称为irq_desc的结构体进行描述:
 
/**
 * struct irq_desc - interrupt descriptor
 *
 * @handle_irq:        highlevel irq-events handler [if NULL, __do_IRQ()]
 * @chip:     low level interrupt hardware access
 * @msi_desc:      MSI descriptor
 * @handler_data: per-IRQ data for the irq_chip methods
 * @chip_data:         platform-specific per-chip private data for the chip
 *            methods, to allow shared chip implementations
 * @action:        the irq action chain
 * @status:        status information
 * @depth:         disable-depth, for nested irq_disable() calls
 * @wake_depth:        enable depth, for multiple set_irq_wake() callers
 * @irq_count:         stats field to detect stalled irqs
 * @irqs_unhandled:    stats field for spurious unhandled interrupts
 * @lock:     locking for SMP
 * @affinity:      IRQ affinity on SMP
 * @cpu:      cpu index useful for balancing
 * @pending_mask: pending rebalanced interrupts
 * @dir:      /proc/irq/ procfs entry
 * @affinity_entry:    /proc/irq/smp_affinity procfs entry on SMP
 * @name:     flow handler name for /proc/interrupts output
 */
struct irq_desc {
     irq_flow_handler_t handle_irq;
     struct irq_chip        *chip;
     struct msi_desc        *msi_desc;
     void          *handler_data;
     void          *chip_data;
     struct irqaction   *action; /* IRQ action list */
     unsigned int       status;       /* IRQ status */
 
     unsigned int       depth;        /* nested irq disables */
     unsigned int       wake_depth;   /* nested wake enables */
     unsigned int       irq_count;    /* For detecting broken IRQs */
     unsigned int       irqs_unhandled;
     spinlock_t         lock;
#ifdef CONFIG_SMP
     cpumask_t     affinity;
     unsigned int       cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
     cpumask_t     pending_mask;
#endif
#ifdef CONFIG_PROC_FS
     struct proc_dir_entry *dir;
#endif
     const char         *name;
} ____cacheline_internodealigned_in_smp;
 
extern struct irq_desc irq_desc[NR_IRQS];
对它的各个成员的描述其注释已经比较清楚了,最主要的是irq_handle和chip这两个成员。这里需要注意的是NR_IRQS的定义。在bf561中,提供了64个外部中断和6个内部中断,其中60-63这4个外部中断保留。但是NR_IRQS的定义高达121。其构成为:
l         7个内部中断,EVT0到EVT6。
l         64个外部中断源,包括4个bf561保留的外部中断。
l         软件中断14和系统调用中断15,这两个中断由系统保留,没有外部中断映射到这两个中断上,因此为它们单独留下描述信息。
l         PF口,这48个PF口仅仅用了6个外部中断源,在内核中为这48个PF口每个都留了中断描述。
irq_desc这个数组的定义在kernel/irq/handle.c中:
/*
 * Linux has a controller-independent interrupt architecture.
 * Every controller has a 'controller-template', that is used
 * by the main code to do the right thing. Each driver-visible
 * interrupt source is transparently wired to the appropriate
 * controller. Thus drivers need not be aware of the
 * interrupt-controller.
 *
 * The code is designed to be easily extended with new/different
 * interrupt controllers, without having to do assembly magic or
 * having to touch the generic code.
 *
 * Controller mappings for all interrupt sources:
 */
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),
#ifdef CONFIG_SMP
         .affinity = CPU_MASK_ALL
#endif
     }
};
对于不同中断源的默认处理函数(irq_desc结构体中的handle_irq),内核将之分成了3类。对于PF0-PF47的中断,将调用 handle_level_irq进行处理;对于 IRQ_PROG0_INTA 、IRQ_PROG1_INTA、IRQ_PROG2_INTA这三个由PF口共享的中断则使用 bfin_demux_gpio_irq进行处理;对于其它的中断源则使用 handle_simple_irq进行处理。
 
内核使用一个称为irq_chip的结构体来描述中断控制器,对中断的操作(启用/禁用某个中断源)将通过irq_chip的回调函数进行,此结构体的定义位于include/linux/irq.h:
/**
 * struct irq_chip - hardware interrupt chip descriptor
 *
 * @name:     name for /proc/interrupts
 * @startup:       start up the interrupt (defaults to ->enable if NULL)
 * @shutdown:      shut down the interrupt (defaults to ->disable if NULL)
 * @enable:        enable the interrupt (defaults to chip->unmask if NULL)
 * @disable:       disable the interrupt (defaults to chip->mask if NULL)
 * @ack:      start of a new interrupt
 * @mask:     mask an interrupt source
 * @mask_ack:      ack and mask an interrupt source
 * @unmask:        unmask an interrupt source
 * @eoi:      end of interrupt - chip level
 * @end:      end of interrupt - flow level
 * @set_affinity: set the CPU affinity on SMP machines
 * @retrigger:         resend an IRQ to the CPU
 * @set_type:      set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
 * @set_wake:      enable/disable power-management wake-on of an IRQ
 *
 * @release:       release function solely used by UML
 * @typename:      obsoleted by name, kept as migration helper
 */
struct irq_chip {
     const char    *name;
     unsigned int (*startup)(unsigned int irq);
     void     (*shutdown)(unsigned int irq);
     void     (*enable)(unsigned int irq);
     void     (*disable)(unsigned int irq);
 
     void     (*ack)(unsigned int irq);
     void     (*mask)(unsigned int irq);
     void     (*mask_ack)(unsigned int irq);
     void     (*unmask)(unsigned int irq);
     void     (*eoi)(unsigned int irq);
 
     void     (*end)(unsigned int irq);
     void     (*set_affinity)(unsigned int irq, cpumask_t dest);
     int      (*retrigger)(unsigned int irq);
     int      (*set_type)(unsigned int irq, unsigned int flow_type);
     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;
};
对系统中的每一个中断,都必须有一个与它相关的irq_chip进行操作(irq_desc->chip)。
uclinux将bf561的中断分为3类,使用了3个不同的irq_chip来对它们进行控制。
第一类是内部中断,即中断0到中断6,内核使用下述结构体进行控制:
static struct irq_chip bfin_core_irqchip = {
     .ack = ack_noop,
     .mask = bfin_core_mask_irq,
     .unmask = bfin_core_unmask_irq,
};
对于这类中断,对它进行mask和unmask操作时必须直接操作IMASK。
第二类中断是没有共用的外部中断,内核使用下述结构体进行控制:
static struct irq_chip bfin_internal_irqchip = {
     .ack = ack_noop,
     .mask = bfin_internal_mask_irq,
     .unmask = bfin_internal_unmask_irq,
     .mask_ack = bfin_internal_mask_irq,
     .disable = bfin_internal_mask_irq,
     .enable = bfin_internal_unmask_irq,
#ifdef CONFIG_PM
     .set_wake = bfin_internal_set_wake,
#endif
};
对于这类中断,直接操作SIC_IMASK。
第三类中断是共用的外部中断,如PF0-PF7就共用一个中断源,此时内核使用下述结构体进行控制:
static struct irq_chip bfin_gpio_irqchip = {
     .ack = bfin_gpio_ack_irq,
     .mask = bfin_gpio_mask_irq,
     .mask_ack = bfin_gpio_mask_ack_irq,
     .unmask = bfin_gpio_unmask_irq,
     .set_type = bfin_gpio_irq_type,
     .startup = bfin_gpio_irq_startup,
     .shutdown = bfin_gpio_irq_shutdown,
#ifdef CONFIG_PM
     .set_wake = bfin_gpio_set_wake,
#endif
};
对这类中断,需要进行特殊处理,在后面再进行分析。
为了记录BF561的64个外部中断的状态,内核使用了ivg_table的数组:
#define NR_PERI_INTS        64
 
struct ivgx {
     /* irq number for request_irq, available in mach-bf533/irq.h */
     unsigned int irqno;
     /* corresponding bit in the SIC_ISR register */
     unsigned int isrflag;
} ivg_table[NR_PERI_INTS];
在这里,ivg_table的数组是使用内部中断号来进行排序的,即从IVG7排到IVG13。而其中的irqno这个值保存的是内核中的中断序号,使用这个序号即可访问irq_desc数组中对此中断的描述。isrflags则存放的是此中断对于于SIC_MASK、SIC_ISR等的掩码。
为了快速访问同一个内部中断号所对应的所有外部中断,内核使用了另一个数组:
struct ivg_slice {
     /* position of first irq in ivg_table for given ivg */
     struct ivgx *ifirst;
     struct ivgx *istop;
} ivg7_13[IVG13 - IVG7 + 1];
此结构体中的ifirst和istop都指向ivg_table,由于ivg_table的元素是按内部中断号升序排列的,因而可以用ifirst指向相应内部中断号的起始元素,用istop指向结束元素。
在内核初始化的时候会调用一个称之为search_IAR的函数,从中可以看出这两个结构体之间的关系:
/*
 * Search SIC_IAR and fill tables with the irqvalues
 * and their positions in the SIC_ISR register.
 */
static void __init search_IAR(void)
{
     unsigned ivg, irq_pos = 0;
     for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) {
         int irqn;
 
         ivg7_13[ivg].istop = ivg7_13[ivg].ifirst = &ivg_table[irq_pos];
 
         for (irqn = 0; irqn < NR_PERI_INTS; irqn++) {
              int iar_shift = (irqn & 7) * 4;
                   if (ivg == (0xf &
#ifndef CONFIG_BF52x
                   bfin_read32((unsigned long *)SIC_IAR0 +
                        (irqn >> 3)) >> iar_shift)) {
#else
                   bfin_read32((unsigned long *)SIC_IAR0 +
                        ((irqn%32) >> 3) + ((irqn / 32) * 16)) >> iar_shift)) {
#endif
                   ivg_table[irq_pos].irqno = IVG7 + irqn;
                   ivg_table[irq_pos].isrflag = 1 << (irqn % 32);
                   ivg7_13[ivg].istop++;
                   irq_pos++;
              }
         }
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌云阁主

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值