底层硬件操作办法
每一条中止线都有一个底层硬件操作函数集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 %d\n”, 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 IRQs\n”,
irq, devname);
}
#ifdef CONFIG_LOCKDEP
/*
* Lockdep wants atomic interrupt handlers:
*/
irqflags |= IRQF_DISABLED;
#endif
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise well 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;
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);
/*
函数__irq_set_trigger()的首要作业是调用函数chip->set_type(irq, flags);设置外部中止的触发方法。
中止触发方法在文件linux\include\irq.h中界说
#define IRQ_TYPE_NONE0x00000000/* Default, unspecified type */
#define IRQ_TYPE_EDGE_RISING0x00000001/* Edge rising type */
#define IRQ_TYPE_EDGE_FALLING0x00000002/* Edge falling type */
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
#define IRQ_TYPE_LEVEL_HIGH0x00000004/* Level high type */
#define IRQ_TYPE_LEVEL_LOW0x00000008/* Level low type */
#define IRQ_TYPE_SENSE_MASK0x0000000f/* Mask of the above */
IRQF_TRIGGER_MASK在文件interrupt.h中界说
#define IRQF_TRIGGER_NONE0x00000000
#define IRQF_TRIGGER_RISING0x00000001
#define IRQF_TRIGGER_FALLING0x00000002
#define IRQF_TRIGGER_HIGH0x00000004
#define IRQF_TRIGGER_LOW0x00000008
#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;
/*
依据 dev_id在中止处理例程单项链表中找出一个要卸载的中止例程。
在单项链表中假如卸载了中心的一个还得将前一个和后一个连接起来。在这里用了一个小技巧。
指针**action_ptr中存储了两个目标,一个是&action->next,另一个是action->next。
比方链表中有三个action,咱们要卸载的是第二个,此刻指针action_ptr中寄存的是第一个action中的成员next的地址&action->next。
*action_ptr寄存的是第一个action的action->next所指向的目标,即第二个action。此刻的action->next便是第三个action。
只需*action_ptr = action->next;就将第一个action的action->next指向了第三个action。
*/
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));
}