Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1306572
  • 博文数量: 254
  • 博客积分: 1586
  • 博客等级: 上尉
  • 技术积分: 2295
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-15 16:38
个人简介

linux学习中

文章分类

全部博文(254)

文章存档

2016年(6)

2015年(2)

2014年(74)

2013年(93)

2012年(12)

2011年(2)

2010年(51)

2009年(14)

分类: LINUX

2013-11-15 10:41:40

简述MIPS中断  

1   MIPS中断处理概述
2   MIPS异常的初始化
3   AG7240中断初始化

 

疑问: 

1         irq_disable()in_irq()都在何时置位,以及两者的关系

irq_disable()只是在关中断的情况下返回1,就是说Status[IE] == 0,即返回irq_disable(),而in_irq(),表示是在中断上下文中,但它并不一定是irq_disable()

这两个函数完全没有关系。

2         为什么local_bh_disable()时要判断是否当前in_irq()

3         中断处理程序中,中断是开的还是关的,正在处理的irq能否嵌套,irq的优先级如何。

这个依赖于驱动,本身是开的,在进入直正的中断处理函数之前一刻开,但如果isr显式要求关中断,则是关中断的。而且对于linux来说,并没有硬件软件的中断优先级。

正在执行的irq,它还会触发吗?这个依赖于不同的中断控制器,如果使用per-cpu的中断控制器,则它会屏蔽本次中断。

 


 

一 MIPS中断处理概述


简述MIPS中断 - lyl19 - lyl19的博客
 

中断也属于异常,如A.1所示,当一个异常(中断)发生时,如下步骤会发生

简述MIPS中断 - lyl19 - lyl19的博客
 
简述MIPS中断 - lyl19 - lyl19的博客
 
简述MIPS中断 - lyl19 - lyl19的博客
 
简述MIPS中断 - lyl19 - lyl19的博客
 

 

1         EPC被置为被中断的PC

2         如果中断模式是兼容模式的话(普便情况),则vector offset = 0x180.

3         如果Status[EXL] == 1, offset = 0x180,并且EPC啥的都不会改变

4         如果是TLB refill, offset = 0x0000000,如果是其它异常,则是0x180。这里强调一下是,mips R2后中断有几种模式,不同的模式它的入口还不同。这里的0x180是与R1兼容的模式。

5         如果Status[BEV] == 1, BASE会到0xBFC00200,否则就BASE会是0x80000000,当然在MIPS32 R2后,我们设置了EBASE寄存器,这可以让用户选择中断后跳转的指令,这与R1就不同了,它固定在0x80000000处。为了向后兼容,EBASE默认情况下,它就是0x80000000处。

 

6         设置cause[extcode]为异常号,中断为0

 

7         Status[EXL] = 0,这个值会产生很大的影响,它会无视Status[IE, KSU]位,而强制处于内核态和关中断状态。这样做的原因是为了我们有足够的时间去保存现场。

8         最近PC会跑去base+offset的地方。

 

 

 


 

二,MIPS异常初始化

参照comcat的文档。


 



 AG7240的中断初始化

MIPS初始化过程中,它会export一些函数给目标板定义自己的驱动。对于驱动来说,它一般在arch/mips/board/irq.c

Export的函数为arch_init_irq,这个函数由各个目标板根据自己的中断处理器等定义中断处理函数。

对于中断来讲,这里有三个概念,一个是irq_chip, 还有一个是irq_desc,中断处理函数action

第一个表示中断控制器,

第二个表示一个中断号的描述。

第三个表示挂在这个中断号上的中断处理函数。

 

首先,linux预定义了NR_IRQS个中断irq_desc结构,NR_IRQS这个标志是板级相关的,在

Arch/mips/board/include/mach里面。

 

struct irq_desc {

unsigned int                       irq;

struct irq_chip                   *chip;

struct irqaction  *action;                /* IRQ action list */

}

这三个成员表示了这三个最基本的结构。

 

最开始,我们必须创建中断控制器的描述,并且把一个irq_desc与中断控制器联系起来。

 

 

 

 

set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip,

                                                 irq_flow_handler_t handle)

{

                set_irq_chip(irq, chip);

                __set_irq_handler(irq, handle, 0, NULL);

}

 

首先把descchip联系起来,再者handle挂在desc上,表示当该中断触发时,应该调用该handle

Desc需要chip,是因为每个中断控制器都不同,电平触发,边缘触发,或者其它啥的,都需要与硬件有关系,因此使用irq_chip挂上中断控制器自己的函数,这就在irq_desc层屏蔽了具体的硬件特性。

 

struct irq_chip {

                const char           *name;

                unsigned int       (*startup)(unsigned int irq);

                void                       (*shutdown)(unsigned int irq);

                void                       (*enable)(unsigned int irq);

                void                       (*disable)(unsigned int irq);

 

                void                       (*ack)(unsigned int irq);

                void                       (*mask)(unsigned int irq);

…………….

}

 

 

中断控制器虽然各有不同,但kernel已经为我们抽象了一些,它们在handle上可以一致,因此就以如下的handle可以现成使用

handle_edge_irq,    edge type IRQ handler

handle_fasteoi_irq    irq handler for transparent controllers

handle_level_irq                              Level type irq handler

handle_percpu_irq     Per CPU local irq handler

 

我们着重讲最后一种,它是与mips cpu 中断直接相连的,它比较特殊,因为不管什么中断控制器最终都要连到mips cpu上,因为我们这里专门为它设置了一个handle

mips cpu对应的中断控制器为

static struct irq_chip mips_cpu_irq_controller = {

                .name                   = "MIPS",

                .ack                        = mask_mips_irq,

                .mask                    = mask_mips_irq,

                .mask_ack           = mask_mips_irq,

                .unmask                               = unmask_mips_irq,

                .eoi                        = unmask_mips_irq,

};

 

/*

 * Almost all MIPS CPUs define 8 interrupt sources.  They are typically

 * level triggered (i.e., cannot be cleared from CPU; must be cleared from

 * device).  The first two are software interrupts which we don't really

 * use or support.  The last one is usually the CPU timer interrupt if

 * counter register is present or, for CPUs with an external FPU, by

 * convention it's the FPU exception interrupt.

 *

 * Don't even think about using this on SMP.  You have been warned.

 *

 * This file exports one global function:

 

好了,有了这些信息,我们来看AG7240是如何来注册自己的中断处理函数的。

 

void __init arch_init_irq(void)

{

                mips_cpu_irq_init();

                ar7240_misc_irq_init(AR7240_MISC_IRQ_BASE);

                ar7240_gpio_irq_init(AR7240_GPIO_IRQ_BASE);

                setup_irq(AR7240_CPU_IRQ_MISC,  &cascade);

                setup_irq(AR7240_MISC_IRQ_GPIO, &cascade);

}

 

首先设置mips自带的中断控制器和处理函数。这样就初始化了1-8号中断,ar7240_misc_irq_init又是初始化了10-23号中断。它使用ar7240_misc_irq_controller中断控制器,但还是使用handle_percpu_irq这个handle

设置CPU 6号中断为cascade,表示空,因为MISC下面连接着多个模块的中断信号,因此这个最高层并不需要一个action,它只要需要分发就行。

 

这只是建立了一个框架,我们还真正需要为它挂上处理函数,这里还有两个工作

1         plat_irq_dispatch

这个函数是从汇编的handle_irq中跳转过来的,用来处理板极的中断。

在这个函数中,我们需要

int pending = read_c0_status() & read_c0_cause();

然后根据中断号依次调用

do_IRQ(n)

当然,如果是MISC,我们要再读寄存器,来决定是下层的哪个中断号。

       2    request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

                    const char *name, void *dev)

我们要为这个中断号直接的挂一个action的函数。因为最终还是会调到这个真正的中断处理函数中来。

 

 

 

下面来看看整个流程

 

NESTED(except_vec3_r4000, 0, sp)           arch/mips/kernel/genex.S

                .set        push

                .set        mips3

                .set        noat

                mfc0      k1, CP0_CAUSE

                li              k0, 31<<2

                andi       k1, k1, 0x7c

                .set        push

                .set        noreorder

                .set        nomacro

                beq        k1, k0, handle_vced

                 li             k0, 14<<2

                beq        k1, k0, handle_vcei

#ifdef CONFIG_64BIT

                 dsll        k1, k1, 1

#endif

                .set        pop

                PTR_L    k0, exception_handlers(k1)

                jr             k0

这个很简单了,从cause中得到当前的异常号,然后跳转,对于中断来讲,这里是handle_int

 

 

NESTED(handle_int, PT_SIZE, sp)

                SAVE_ALL

                CLI

                TRACE_IRQS_OFF

 

                LONG_L               s0, TI_REGS($28)

                LONG_S               sp, TI_REGS($28)

                PTR_LA ra, ret_from_irq

                j               plat_irq_dispatch

                END(handle_int)

这个就很清楚了吧,先保持所有,然后CLI, 这个是清EXL, 然后关中断,并且进入kernel mode。 (并不是我们必须这样,vxworks就不是这样,这里面比较tricky)。

然后跳转到plat_irq_dispatch,这也就是为什么板极包要定义这个函数的原因。

 

当然还有另外一个方法是,在板级包初始函数中,直接使用set_vector把整个handle_int都替换掉,这样也就不用有plat_irq_dispatch了。

 

不过明确,进入plat_irq_dispatch中断是关的。

do_IRQ-à generic_handle_irq-à desc->handle_irq-à handle_percpu_irq

 

handle_percpu_irq(unsigned int irq, struct irq_desc *desc)

{

                irqreturn_t action_ret;

 

                kstat_incr_irqs_this_cpu(irq, desc);

 

                if (desc->chip->ack)

                                desc->chip->ack(irq);

 

                action_ret = handle_IRQ_event(irq, desc->action);

                if (!noirqdebug)

                                note_interrupt(irq, desc, action_ret);

 

                if (desc->chip->eoi)

                                desc->chip->eoi(irq);

}

这个函数重要,desc->chip->ack,这是给chip一个机会处理一些事情,比如说把中断信号拉低等。

对于CPU,和我们ag7240来说,有一个性质是中断信号是读过,它自动拉低,所以很简单。

那这个ack主要干什么事呢?

 

static inline void mask_mips_irq(unsigned int irq)

{

                clear_c0_status(0x100 << (irq - MIPS_CPU_IRQ_BASE));

                irq_disable_hazard();

}

 

表示把当前的irqmask,这就表示当前irq在执行时,这个irq是不能再被中断的。

 

handle_IRQ_event

{

                                if (!(action->flags & IRQF_DISABLED))

                                local_irq_enable_in_hardirq();

                                do {

                                                ret = action->handler(irq, action->dev_id);

}             

}

 

这就很明白了,在中断处理函数中,如果注册中断时,要求保持关中断,则我们保持关中断,否则中断处理程序中,我们是开中断。

而且我感觉对于mips cpu的几个中断来讲,硬件是没有优先级的,软件也没有做优先级。所以它是能够被其它中断打断的。

这个和 vxworsk不同了,vxworks在软件上是做了优先级的。

 


阅读(3487) | 评论(0) | 转发(0) |
0

上一篇:oprofile注意事项

下一篇:spin_lock/rw_lock

给主人留下些什么吧!~~