每一条中断线都有一个底层硬件操作函数集struct irq_chip 。大多数控制方法都是重复的 ,基本上只要有中断响应 、 中断屏蔽 、 中断开启 、 中断触发类型设置等方法就可以满足要求了。其他各种方法基本上和这些相同。
这些操作方法的实现在文件linux/arch/arm/plat-s3c24xx/irq.c中。
例如外部中断 IRQ_EINT0 ~ IRQ_EINT3都用以下操作函数集:
static struct irq_chip s3c_irq_eint0t4 = { .name = "s3c-ext0", .ack = s3c_irq_ack, .mask = s3c_irq_mask, .unmask = s3c_irq_unmask, .set_wake = s3c_irq_wake, .set_type = s3c_irqext_type, };
static inline void s3c_irq_ack(unsigned int irqno) { unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
__raw_writel(bitval, S3C2410_SRCPND); __raw_writel(bitval, S3C2410_INTPND); }
static void s3c_irq_mask(unsigned int irqno) { unsigned long mask;
irqno -= IRQ_EINT0;
mask = __raw_readl(S3C2410_INTMSK); mask |= 1UL << irqno; __raw_writel(mask, S3C2410_INTMSK); }
static void s3c_irq_unmask(unsigned int irqno) { unsigned long mask;
if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23) irqdbf2("s3c_irq_unmask %dn", irqno);
irqno -= IRQ_EINT0;
mask = __raw_readl(S3C2410_INTMSK); mask &= ~(1UL << irqno); __raw_writel(mask, S3C2410_INTMSK); }
int s3c_irqext_type(unsigned int irq, unsigned int type) { void __iomem *extint_reg; void __iomem *gpcon_reg; unsigned long gpcon_offset, extint_offset; unsigned long newvalue = 0, value;
if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) { gpcon_reg = S3C2410_GPFCON; extint_reg = S3C24XX_EXTINT0; gpcon_offset = (irq - IRQ_EINT0) * 2; extint_offset = (irq - IRQ_EINT0) * 4; } 。。。。。。 __raw_writel(value, gpcon_reg); //将对应管脚配置成中断功能
switch (type) { case IRQ_TYPE_NONE: printk(KERN_WARNING "No edge setting!n"); break; case IRQ_TYPE_EDGE_RISING: newvalue = S3C2410_EXTINT_RISEEDGE; break;
case IRQ_TYPE_EDGE_FALLING: newvalue = S3C2410_EXTINT_FALLEDGE; break;
case IRQ_TYPE_EDGE_BOTH: newvalue = S3C2410_EXTINT_BOTHEDGE; break;
case IRQ_TYPE_LEVEL_LOW: newvalue = S3C2410_EXTINT_LOWLEV; break;
case IRQ_TYPE_LEVEL_HIGH: newvalue = S3C2410_EXTINT_HILEV; break;
default: printk(KERN_ERR "No such irq type %d", type); return -1; }
value = __raw_readl(extint_reg); value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); __raw_writel(value, extint_reg); //设置中断的触发方式。
return 0; }
中断申请函数
//中断申请函数request_irq()只是函数request_threaded_irq()的包装而已 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) { return request_threaded_irq(irq, handler, NULL, flags, name, dev); }
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id) { struct irqaction *action; struct irq_desc *desc; int retval; //中断类型标识IRQF_SHARED和IRQF_DISABLED不应当被同时设置。
if ((irqflags & (IRQF_SHARED|IRQF_DISABLED)) == (IRQF_SHARED|IRQF_DISABLED)) { pr_warning( "IRQ %d/%s: IRQF_DISABLED is not guaranteed on shared IRQsn", irq, devname); } #ifdef CONFIG_LOCKDEP
irqflags |= IRQF_DISABLED; #endif if ((irqflags & IRQF_SHARED) && !dev_id) return -EINVAL; desc = irq_to_desc(irq); //根据中断号获取中断线描述符结构体 if (!desc) return -EINVAL;
if (desc->status & IRQ_NOREQUEST) return -EINVAL; if (!handler) return -EINVAL;
//分配一个中断服务例程结构体action并初始化它的各字段。
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); if (!action) return -ENOMEM;
action->handler = handler; action->thread_fn = thread_fn; //为NULL action->flags = irqflags; action->name = devname;
//对于共享中断 , 此特定值用来区分各中断,以便从共享中断线的诸多中断处理程序中删除指定的那一个。
action->dev_id = dev_id; //将该例程添加到单向链表desc->action上,并启动该例程。
retval = __setup_irq(irq, desc, action); if (retval) kfree(action);
。。。。。。
return retval; }
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) { struct irqaction *old, **old_ptr; const char *old_name = NULL; unsigned long flags; int shared = 0; int ret; 。。。。。。
old_ptr = &desc->action; //获取中断处理例程单向链表上的第一个例程,如果它为NULL说明该中断线是第一次被触发。 old = *old_ptr; if (old) { // 。。。。。。 //如果该中断线上存在中断服务例程则让old_ptr指向该例程链表的尾部,以便加入新的服务例程
do { old_ptr = &old->next; old = *old_ptr; } while (old); shared = 1; } if (!shared) {
//将操作函数集desc->chip的一些未设置的字段设为默认值。
irq_chip_set_defaults(desc->chip); init_waitqueue_head(&desc->wait_for_threads);
#define IRQ_TYPE_EDGE_RISING 0x00000001 #define IRQ_TYPE_EDGE_FALLING 0x00000002 #define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) #define IRQ_TYPE_LEVEL_HIGH 0x00000004 #define IRQ_TYPE_LEVEL_LOW 0x00000008 #define IRQ_TYPE_SENSE_MASK 0x0000000f
IRQF_TRIGGER_MASK在文件interrupt.h中定义
#define IRQF_TRIGGER_NONE 0x00000000 #define IRQF_TRIGGER_RISING 0x00000001 #define IRQF_TRIGGER_FALLING 0x00000002 #define IRQF_TRIGGER_HIGH 0x00000004 #define IRQF_TRIGGER_LOW 0x00000008 #define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
可以看出只要外部中断设置了触发方式函数__irq_set_trigger()就会执行。
*/
if (new->flags & IRQF_TRIGGER_MASK) ret = __irq_set_trigger(desc, irq, new->flags & IRQF_TRIGGER_MASK); if (ret) goto out_thread; } else compat_irq_chip_set_default_handler(desc);
// desc->status的标志IRQ_NOAUTOEN 在中断初始化函数 s3c24xx_init_irq()中调用函数set_irq_flags()设置。
if (!(desc->status & IRQ_NOAUTOEN)) { desc->depth = 0; desc->status &= ~IRQ_DISABLED; desc->chip->startup(irq); //开启中断线 } else desc->depth = 1;
。。。。。。
} *old_ptr = new; //将新加入的中断处理例程添加到链表中
。。。。。。
//在proc文件系统中创建目录。
new->irq = irq; register_irq_proc(irq, desc); new->dir = NULL; register_handler_proc(irq, new);
。。。。。。
return ret; }
中断卸载函数free_irq().。
如果指定的中断线不是共享的 , 那么 , 该函数删除处理程序的同时将禁用这条中断线 。 如果 中断线是共享的,则仅删除 dev_id 所对应的处理程序,而这条中断线本身只有在删除了最 后一个处理程序时才会被禁用。由此可以看出为什么惟一的 dev_ id 如此重要。对于共享的 中断线,需要一个惟一的信息来区分其上面的多个处理程序,并让 free_irq() 仅仅删除指定 的处理程序。如果 dev_id 非空,它都必须与需要删除的处理程序相匹配。非共享中断,该 域可以为空,但需要和注册时使用的指针一致。
static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{ struct irq_desc *desc = irq_to_desc(irq); struct irqaction *action, **action_ptr; struct task_struct *irqthread; unsigned long flags;
if (!desc) return NULL;
action_ptr = &desc->action; for (;;) { action = *action_ptr;
if (action->dev_id == dev_id) break; action_ptr = &action->next; }
*action_ptr = action->next;
。。。。。。
if (!desc->action) { // 无其他中断使用该中断线则禁止 desc->status |= IRQ_DISABLED; if (desc->chip->shutdown) desc->chip->shutdown(irq); else desc->chip->disable(irq); } 。。。。。。。
return action; //返回中断处理例程结构体 } //在函数free_irq中将函数__free_irq返回的中断处理例程结构体释放掉。 void free_irq(unsigned int irq, void *dev_id) { kfree(__free_irq(irq, dev_id));