新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM Linux中斷機(jī)制之中斷的申請(qǐng)

ARM Linux中斷機(jī)制之中斷的申請(qǐng)

作者: 時(shí)間:2016-11-10 來(lái)源:網(wǎng)絡(luò) 收藏
底層硬件操作方法

每一條中斷線都有一個(gè)底層硬件操作函數(shù)集struct irq_chip 。大多數(shù)控制方法都是重復(fù)的 ,基本上只要有中斷響應(yīng) 、 中斷屏蔽 、 中斷開啟 、 中斷觸發(fā)類型設(shè)置等方法就可以滿足要求了。其他各種方法基本上和這些相同。

本文引用地址:http://butianyuan.cn/article/201611/317088.htm

這些操作方法的實(shí)現(xiàn)在文件linux/arch/arm/plat-s3c24xx/irq.c中。

例如外部中斷 IRQ_EINT0 ~ IRQ_EINT3都用以下操作函數(shù)集:

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,
};

/********************中斷響應(yīng)******************************/

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);
}

/********************中斷觸發(fā)類型設(shè)置******************************/

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);//將對(duì)應(yīng)管腳配置成中斷功能


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);//設(shè)置中斷的觸發(fā)方式。

return 0;
}

中斷申請(qǐng)函數(shù)

//中斷申請(qǐng)函數(shù)request_irq()只是函數(shù)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;

//中斷類型標(biāo)識(shí)IRQF_SHARED和IRQF_DISABLED不應(yīng)當(dāng)被同時(shí)設(shè)置。
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
/*
* 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);//根據(jù)中斷號(hào)獲取中斷線描述符結(jié)構(gòu)體
if (!desc)
return -EINVAL;

if (desc->status & IRQ_NOREQUEST)
return -EINVAL;
if (!handler)
return -EINVAL;

//分配一個(gè)中斷服務(wù)例程結(jié)構(gòu)體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;

//對(duì)于共享中斷 , 此特定值用來(lái)區(qū)分各中斷,以便從共享中斷線的諸多中斷處理程序中刪除指定的那一個(gè)。
action->dev_id = dev_id;

//將該例程添加到單向鏈表desc->action上,并啟動(dòng)該例程。

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;//獲取中斷處理例程單向鏈表上的第一個(gè)例程,如果它為NULL說(shuō)明該中斷線是第一次被觸發(fā)。
old = *old_ptr;
if (old) { //


。。。。。。

//如果該中斷線上存在中斷服務(wù)例程則讓old_ptr指向該例程鏈表的尾部,以便加入新的服務(wù)例程
do {
old_ptr = &old->next;
old = *old_ptr;
} while (old);
shared = 1;
}

if (!shared) {

//將操作函數(shù)集desc->chip的一些未設(shè)置的字段設(shè)為默認(rèn)值。
irq_chip_set_defaults(desc->chip);

init_waitqueue_head(&desc->wait_for_threads);

/*

函數(shù)__irq_set_trigger()的主要工作是調(diào)用函數(shù)chip->set_type(irq, flags);設(shè)置外部中斷的觸發(fā)方式。

中斷觸發(fā)方式在文件linuxincludeirq.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)


可以看出只要外部中斷設(shè)置了觸發(fā)方式函數(shù)__irq_set_trigger()就會(huì)執(zhí)行。

*/
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的標(biāo)志IRQ_NOAUTOEN 在中斷初始化函數(shù) s3c24xx_init_irq()中調(diào)用函數(shù)set_irq_flags()設(shè)置。

if (!(desc->status & IRQ_NOAUTOEN)) {
desc->depth = 0;
desc->status &= ~IRQ_DISABLED;
desc->chip->startup(irq);//開啟中斷線
} else
desc->depth = 1;

。。。。。。
}

*old_ptr = new; //將新加入的中斷處理例程添加到鏈表中

。。。。。。

//在proc文件系統(tǒng)中創(chuàng)建目錄。

new->irq = irq;
register_irq_proc(irq, desc);
new->dir = NULL;
register_handler_proc(irq, new);

。。。。。。
return ret;
}

中斷卸載函數(shù)free_irq().。

如果指定的中斷線不是共享的 , 那么 , 該函數(shù)刪除處理程序的同時(shí)將禁用這條中斷線 。 如果
中斷線是共享的,則僅刪除 dev_id 所對(duì)應(yīng)的處理程序,而這條中斷線本身只有在刪除了最
后一個(gè)處理程序時(shí)才會(huì)被禁用。由此可以看出為什么惟一的 dev_ id 如此重要。對(duì)于共享的
中斷線,需要一個(gè)惟一的信息來(lái)區(qū)分其上面的多個(gè)處理程序,并讓 free_irq() 僅僅刪除指定
的處理程序。如果 dev_id 非空,它都必須與需要?jiǎng)h除的處理程序相匹配。非共享中斷,該
域可以為空,但需要和注冊(cè)時(shí)使用的指針一致。

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;

/*

根據(jù) dev_id在中斷處理例程單項(xiàng)鏈表中找出一個(gè)要卸載的中斷例程。

在單項(xiàng)鏈表中如果卸載了中間的一個(gè)還得將前一個(gè)和后一個(gè)連接起來(lái)。在這里用了一個(gè)小技巧。

指針**action_ptr中存儲(chǔ)了兩個(gè)對(duì)象,一個(gè)是&action->next,另一個(gè)是action->next。

比如鏈表中有三個(gè)action,我們要卸載的是第二個(gè),此時(shí)指針action_ptr中存放的是第一個(gè)action中的成員next的地址&action->next。

*action_ptr存放的是第一個(gè)action的action->next所指向的對(duì)象,即第二個(gè)action。此時(shí)的action->next即是第三個(gè)action。

只要*action_ptr = action->next;就將第一個(gè)action的action->next指向了第三個(gè)action。

*/

if (action->dev_id == dev_id)
break;
action_ptr = &action->next;
}

*action_ptr = action->next;

。。。。。。
if (!desc->action) {// 無(wú)其他中斷使用該中斷線則禁止
desc->status |= IRQ_DISABLED;
if (desc->chip->shutdown)
desc->chip->shutdown(irq);
else
desc->chip->disable(irq);
}

。。。。。。。


return action;//返回中斷處理例程結(jié)構(gòu)體
}
//在函數(shù)free_irq中將函數(shù)__free_irq返回的中斷處理例程結(jié)構(gòu)體釋放掉。

void free_irq(unsigned int irq, void *dev_id)
{
kfree(__free_irq(irq, dev_id));
}



關(guān)鍵詞: ARMLinux中斷機(jī)制申

評(píng)論


技術(shù)專區(qū)

關(guān)閉