三读内核中断处理(1):数据结构

本文详细介绍了uCLinux在Blackfin BF561处理器上的中断管理机制,包括irq_desc结构体及其成员介绍、irqaction和irq_chip的作用,以及ivg_table数组的应用。

 

快乐虾

http://blog.youkuaiyun.com/lights_joy/

lights@hb165.com

  

本文适用于

ADSP-BF561

优视BF561EVB开发板

uclinux-2008r1.5-rc3(smp patch)

Visual DSP++ 5.0(update 5)

 

欢迎转载,但请保留作者信息

 

1.1    irq_desc

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

 

对它的各个成员的描述其注释已经比较清楚了,最主要的是handle_irqchip这两个成员。这里需要注意的是NR_IRQS的定义。在bf561中,提供了64个外部中断和6个内部中断,其中60-634个外部中断保留。但是NR_IRQS的定义高达121。其构成为:

l         7个内部中断,EVT0EVT6

l         64个外部中断源,包括4bf561保留的外部中断。

l         软件中断14和系统调用中断15,这两个中断由系统保留,没有外部中断映射到这两个中断上,因此为它们单独留下描述信息。

l         PF口,这48PF口仅仅用了6个外部中断源,在内核中为这48PF口每个都留了中断描述。

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_INTAIRQ_PROG1_INTAIRQ_PROG2_INTA这三个由PF口共享的中断则使用bf561_demux_gpio_irq进行处理;对于其它的中断源则使用handle_simple_irq进行处理。

1.2    irqaction

这一结构体定义为:

struct irqaction {

     irq_handler_t handler;

     unsigned long flags;

     cpumask_t mask;

     const char *name;

     void *dev_id;

     struct irqaction *next;

     int irq;

     struct proc_dir_entry *dir;

};

当有代码请求设置一个中断的回调函数时,中断管理程序将创建一个irqaction对象,然后将之放到相应的irq_desc数组中去。

1.3    irq_chip

内核使用一个称为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)

uclinuxbf561的中断分为3类,使用了3个不同的irq_chip来对它们进行控制。

第一类是内部中断,即中断0到中断6,内核使用下述结构体进行控制:

static struct irq_chip bf561_core_irqchip = {

     .name         = "CORE",

     .ack = ack_noop,

     .mask = bf561_core_mask_irq,

     .unmask = bf561_core_unmask_irq,

};

对于这类中断,对它进行maskunmask操作时必须直接操作IMASK寄存器。

第二类中断是没有共用的外部中断,内核使用下述结构体进行控制:

static struct irq_chip bf561_internal_irqchip = {

     .name         = "INTN",

     .ack = ack_noop,

     .mask = bf561_internal_mask_irq,

     .unmask = bf561_internal_unmask_irq,

};

对于这类中断,直接操作SIC_IMASK

第三类中断是共用的外部中断,如PF0-PF7就共用一个中断源,此时内核使用下述结构体进行控制:

static struct irq_chip bf561_gpio_irqchip = {

     .name         = "GPIO",

     .ack = bf561_gpio_ack_irq,

     .mask = bf561_gpio_mask_irq,

     .mask_ack = bf561_gpio_mask_ack_irq,

     .unmask = bf561_gpio_unmask_irq,

     .set_type = bf561_gpio_irq_type,

     .startup = bf561_gpio_irq_startup,

     .shutdown = bf561_gpio_irq_shutdown

};

对这类中断,需要进行特殊处理,在后面再进行分析。

1.4    ivg_table

为了记录BF56164个外部中断的状态,内核使用了ivg_table的数组:

#define NR_PERI_INTS        64

 

struct ivgx {

     /* irq number for request_irq, available in mach-bf561/irq.h */

     int irqno;

     /* corresponding bit in the SICA_ISR0 register */

     int isrflag0;

     /* corresponding bit in the SICA_ISR1 register */

     int isrflag1;

} ivg_table[NR_PERI_INTS];

 

在这里,ivg_table的数组是使用内部中断号来进行排序的,即从IVG7排到IVG13。而其中的irqno这个值保存的是内核中的中断序号,使用这个序号即可访问irq_desc数组中对此中断的描述。isrflags则存放的是此中断对于于SICx_MASKSICx_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];

此结构体中的ifirstistop都指向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 &

                   bfin_read32((unsigned long *)SICA_IAR0 +

                        (irqn >> 3)) >> iar_shift)) {

                   ivg_table[irq_pos].irqno = IVG7 + irqn;

                   ivg_table[irq_pos].isrflag0 =

                       (irqn < 32 ? (1 << irqn) : 0);

                   ivg_table[irq_pos].isrflag1 =

                       (irqn < 32 ? 0 : (1 << (irqn - 32)));

                   ivg7_13[ivg].istop++;

                   irq_pos++;

              }

         }

     }

}

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

嵌云阁主

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

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

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

打赏作者

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

抵扣说明:

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

余额充值