对技术执着
分类: LINUX
2015-03-14 14:15:31
原文地址:ARM Linux中断机制之中断的申请 作者:cainiao413
底层硬件操作方法
每一条中断线都有一个底层硬件操作函数集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()的包装而已
点击(此处)折叠或打开
中断卸载函数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));
}