kernel 里面的中断代码分析
Index :
1.1 涉及到的代码简单分析
1.2 中断处理函数是如何被 called ?(转载的,参考,了解流程即可,具体代
码不必深究)
1.3 s3c2410 里面的中断控制器的细节分析,以及 general 的中断原理
1.4 外部中断控制器的分析(与GPIO有关)
1.1 涉及到的代码简单分析:
大致分析:
以前看 IRQ 的部分, 都没有看到一个部分就是跟 chip 有关的代码。
通过看 request_irq () 还有 setup_irq() 都跟具体的 chip 有关的。
这个跟 chip 有关的代码可以到 arch/arm/mach-smdk2410/irq.c 里面查询。
比如: 跟具体的 cpu 有关的:
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int)", irqno);
set_irq_chip(irqno, &s3c_irq_eint0t4);
set_irq_handler(irqno, do_edge_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irqdbf("registering irq %d (extended s3c irq)", irqno);
set_irq_chip(irqno, &s3c_irqext_chip);
set_irq_handler(irqno, do_edge_IRQ);
set_irq_flags(irqno, IRQF_VALID);
}
还要注意 kernel/irq/chip.c 里面的函数。
比如 do_level_IRQ() 函数,
具体的大家可以分析
handle_level_irq() 和 handle_edge_irq()
硬件中断芯片描述苻
/**
* 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 {
。。。
}
对于所有的 cpu 和平台来讲, 通用的 “中断描述符表”
它记录了每个 irq 的所有信息, 包括 irq_handler ,status
/**
* struct irq_desc - interrupt descriptor
*
* @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()]
* @chip: low level interrupt hardware access
* @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
* @move_irq: need to re-target IRQ destination
* @dir: /proc/irq/ procfs entry
* @affinity_entry: /proc/irq/smp_affinity procfs entry on SMP
*
* Pad this out to 32 bytes for cache and indexing reasons.
*/
struct irq_desc {
void fastcall (*handle_irq)(unsigned int irq,
struct irq_desc *desc,
struct pt_regs *regs);
struct irq_chip *chip;
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;
unsigned int move_irq; /* need to re-target IRQ dest */
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
} ____cacheline_aligned;
重点分析 request_irq() 函数和 setup_irq() 函数 跟移植有关系的部分 。
Kernel/irq/manage.c =>request_irq()
int request_irq(unsigned int irq,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
int retval;
#ifdef CONFIG_LOCKDEP
/*
* Lockdep wants atomic interrupt handlers:
*/
irqflags |= SA_INTERRUPT;
#endif
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
if (irq >= NR_IRQS)
return -EINVAL;
if (irq_desc[irq].status & IRQ_NOREQUEST) //这个要初始化,否则无法 request_irq , 显然这里应该在 kernel 启动的
过程调用 irq 相关的函数进行初始化
return -EINVAL;
if (!handler)
return -EINVAL;
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
if (!action)
return -ENOMEM;
action->handler = handler;
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
select_smp_affinity(irq);
retval = setup_irq(irq, action);
if (retval)
kfree(action);
return retval;
}
EXPORT_SYMBOL(request_irq);
Kernel/irq/manage.c Î setup_irq()
/* 内部函数来注册 irqaction
* 典型的用于分配给特定的中断,这些中断体系结构的一部分
* 比如像 2410 手册里面定义的中断那样
/*
* Internal function to register an irqaction - typically used to
* allocate special interrupts that are part of the architecture.
*/
/* 共享中断的 irq , 应该有多个 irqaction
* 我们要做的就是在 irqaction 的链表的末尾
* 填上我们自己的 handler
*/
int setup_irq(unsigned int irq, struct irqaction *new)
{
struct irq_desc *desc = irq_desc + irq;
struct irqaction *old, **p;
unsigned long flags;
int shared = 0;