精华内容
下载资源
问答
  • CPU如何保证FIQ(快速中断模式)比IRQ(普通中断模式)响应更快的? 1.FIQ模式有专用的r8—r12寄存器,所以保护现场时不用保存到栈中。 2.FIQ中断中断向量表的最后一个异常向量入口,因此ISR无需跳转,可以直接写在...

    CPU如何保证FIQ(快速中断模式)比IRQ(普通中断模式)响应更快的?

    1.FIQ模式有专用的r8—r12寄存器,所以保护现场时不用保存到栈中在这里插入图片描述
    2.FIQ中断在中断向量表的最后一个异常向量入口,因此ISR无需跳转,可以直接写在原地,节省了跳转的时间
    在这里插入图片描述

    展开全文
  • LPC3250中的IRQ中断

    千次阅读 2011-05-05 22:11:00
    LPC3250中断控制器有3个,分别是主中断控制器(MIC_ER)、子中断控制1(SIC1)和子中断控制2(SIC2)。...3个控制器控制的中断源一共73个中断源(3*32-6-8-9)。 中断初始化,以SIC2IRQ中断为例: 

    LPC3250中断控制器有3个,分别是主中断控制器(MIC_ER)、子中断控制1(SIC1)和子中断控制2(SIC2)。

    对这三个中断控制器的理解:

          MIC_ER通过特定位来控制所有中断的使能。其中MIC_ER[31:30]控制Sub2FIQ、Sub1FIQ,MIC_ER[1:0]控制Sub2IRQ、Sub1IRQ。而MIC_ER、SIC1及SIC2分别控制着不同的中断源。3个控制器控制的中断源一共73个中断源(3*32-6-8-9)。

          中断初始化,以SIC2IRQ中断为例:

           MIC_ER=0X02   ;  //主中断控制器设置中断为SIC2IRQ中断

           SIC2_ER=(1<<10); //设置SIC2_ER[10]位中断

           MIC_APR=0X00;    //  低电平或者下降沿有效

           SIC2_APR=0X00;   //  低电平或者下降沿有效

           MIC_ATR=0X00;     //   电平触发

           SIC2_ATR=(1<<10);  // 边沿触发

           MIC_ITR=0X00;      //  设为IRQ中断

           SIC_ITR=0X00;      //  设为IRQ中断

           SIC_RSR=(1<<10);  //  清除中断,仅对边沿触发有效

     

    之后可以设置中断响应函数,比如蜂鸣器鸣叫函数:

          void  IRQ_fengming(void)

           {

              /*

                 *蜂鸣器鸣叫

             */

            }

    最后就是主函数

           int main()

           {

                 Init_IRQ();

                 while(1)

                  {

                       if(....)       //中断触发条件

                         {

                             IRQ_fengming();

                           }

                    }

           

              }

     

    展开全文
  • 其增强型的中断控制器(EIC)的EIC_IVR寄存器(中断向量寄存器)、EIC_SIRn寄存器(源中断寄存器)都是用于存储相关中断通道的中断服务程序在存储器中的地址,内核响应IRQ中断后,再根据当前对应的中断通道的中断向量进行...
  • 凌阳16位单片机之IRQ4中断

    千次阅读 2015-05-23 17:01:36
    IRQ4中断是由 b4 b5 b5三位来控制的 实现功能:利用IRQ4三个时基中断,即IRQ4_1kHz、IRQ4_2kHz和IRQ4_4kHz中断,在各自的中断服务程序中对相应的变量进行累加;并通过点亮、熄灭发光二极管来表示...

    IRQ4中断是由  b4  b5 b5三位来控制的



    实现功能:利用IRQ4三个时基中断,即IRQ4_1kHzIRQ4_2kHzIRQ4_4kHz中断,在各自的中断服务程序中对相应的变量进行累加;并通过点亮、熄灭发光二极管来表示相应变量计数器累加的速度; 

    实验现象:D4D5的亮灭周期为2sD6D7的亮灭周期为1sD8~D11的亮灭周期为0.5s



    这个需要有一个变量来计数,




    #include "SPCE061A.H"
    #define sec2 0x0003    //宏定义,低八位的低两个口
    #define sec1 0x000c
    #define sec5 0x00f0
    unsigned int i,j;

    void IRQ4(void)__attribute__((ISR));


    void IRQ4()
    {    
        static unsigned int IRQ_2=0,IRQ_1=0,IRQ_4=0;//设置三个计数器
        //unsigned int IRQ42,IRQ41,IRQ44;
        //unsigned int Output = 0x0000,i;
        //*P_IOA_Data=0x00ff;
        //asm("INT OFF");
        if(*P_INT_Ctrl & C_IRQ4_1KHz)//1024
        {
            if(++IRQ_1>=2048)
            {
                IRQ_1=0;
                if(((*P_IOA_Buffer)&sec2)==0)
                {
                    *P_IOA_Data+=sec2;
                }
                else
                {
                    *P_IOA_Data-=sec2;
                }
            }
            //通过判断亮暗来进行加减  下面两个都是类似的
            *P_INT_Clear = C_IRQ4_1KHz;
        }
        
        if(*P_INT_Ctrl & C_IRQ4_2KHz)//2048
        {
            if(++IRQ_2>=2048)
            {
                IRQ_2=0;
                if(((*P_IOA_Buffer)&sec1)==0)
                {
                    *P_IOA_Data+=sec1;
                }
                else
                {
                    *P_IOA_Data-=sec1;
                }
            }
            *P_INT_Clear = C_IRQ4_2KHz;     
        }
        
        
        if(*P_INT_Ctrl & C_IRQ4_4KHz)//4096
        {
            IRQ_4++;
            if(IRQ_4>=2048)
            {
                IRQ_4=0;
                if(((*P_IOA_Data)&sec5)==0)
                {
                    (*P_IOA_Data)=(*P_IOA_Data)+sec5;
                }
                else
                {
                    (*P_IOA_Data)=(*P_IOA_Data)-sec5;
                }
            }
            *P_INT_Clear =C_IRQ4_4KHz;
        }
        //asm("INT IRQ");
    }

    int main()
    {
        *P_IOA_Dir = 0x00ff;
        *P_IOA_Attrib = 0x00ff;
        *P_IOA_Data = 0x0000;
        
        *P_INT_Ctrl = C_IRQ4_2KHz+C_IRQ4_1KHz+C_IRQ4_4KHz;//01110000    开启了IRQ4的三种不同频率的中断
        asm("INT IRQ");
        
        while(1)
        {
            *P_Watchdog_Clear = 0x0001;
        }
        return 0;
    }

    展开全文
  • 作者:linuxer 发布于:2014-8-26 17:03 分类:中断子系统 一、前言 本文主要围绕IRQ number和中断描述符(interrupt descriptor)这两个概念描述通用中断处理过程。第二章主要描述基本概念,包括什么是IRQ ...

    linux kernel的中断子系统之(三):IRQ number和中断描述符

    作者:linuxer 发布于:2014-8-26 17:03 分类:中断子系统

    一、前言

    本文主要围绕IRQ number和中断描述符(interrupt descriptor)这两个概念描述通用中断处理过程。第二章主要描述基本概念,包括什么是IRQ number,什么是中断描述符等。第三章描述中断描述符数据结构的各个成员。第四章描述了初始化中断描述符相关的接口API。第五章描述中断描述符相关的接口API。

    二、基本概念

    1、通用中断的代码处理示意图

    一个关于通用中断处理的示意图如下:

    zhongduan

    在linux kernel中,对于每一个外设的IRQ都用struct irq_desc来描述,我们称之中断描述符(struct irq_desc)。linux kernel中会有一个数据结构保存了关于所有IRQ的中断描述符信息,我们称之中断描述符DB(上图中红色框图内)。当发生中断后,首先获取触发中断的HW interupt ID,然后通过irq domain翻译成IRQ nuber,然后通过IRQ number就可以获取对应的中断描述符。调用中断描述符中的highlevel irq-events handler来进行中断处理就OK了。而highlevel irq-events handler主要进行下面两个操作:

    (1)调用中断描述符的底层irq chip driver进行mask,ack等callback函数,进行interrupt flow control。

    (2)调用该中断描述符上的action list中的specific handler(我们用这个术语来区分具体中断handler和high level的handler)。这个步骤不一定会执行,这是和中断描述符的当前状态相关,实际上,interrupt flow control是软件(设定一些标志位,软件根据标志位进行处理)和硬件(mask或者unmask interrupt controller等)一起控制完成的。

     

    2、中断的打开和关闭

    我们再来看看整个通用中断处理过程中的开关中断情况,开关中断有两种:

    (1)开关local CPU的中断。对于UP,关闭CPU中断就关闭了一切,永远不会被抢占。对于SMP,实际上,没有关全局中断这一说,只能关闭local CPU(代码运行的那个CPU)

    (2)控制interrupt controller,关闭某个IRQ number对应的中断。更准确的术语是mask或者unmask一个 IRQ。

    本节主要描述的是第一种,也就是控制CPU的中断。当进入high level handler的时候,CPU的中断是关闭的(硬件在进入IRQ processor mode的时候设定的)。

    对于外设的specific handler,旧的内核(2.6.35版本之前)认为有两种:slow handler和fast handle。在request irq的时候,对于fast handler,需要传递IRQF_DISABLED的参数,确保其中断处理过程中是关闭CPU的中断,因为是fast handler,执行很快,即便是关闭CPU中断不会影响系统的性能。但是,并不是每一种外设中断的handler都是那么快(例如磁盘),因此就有slow handler的概念,说明其在中断处理过程中会耗时比较长。对于这种情况,如果在整个specific handler中关闭CPU中断,对系统的performance会有影响。因此,对于slow handler,在从high level handler转入specific handler中间会根据IRQF_DISABLED这个flag来决定是否打开中断,具体代码如下(来自2.6.23内核):

    irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) 

        ……

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

        …… 
    }

    如果没有设定IRQF_DISABLED(slow handler),则打开本CPU的中断。然而,随着软硬件技术的发展:

    (1)硬件方面,CPU越来越快,原来slow handler也可以很快执行完毕

    (2)软件方面,linux kernel提供了更多更好的bottom half的机制

    因此,在新的内核中,比如3.14,IRQF_DISABLED被废弃了。我们可以思考一下,为何要有slow handler?每一个handler不都是应该迅速执行完毕,返回中断现场吗?此外,任意中断可以打断slow handler执行,从而导致中断嵌套加深,对内核栈也是考验。因此,新的内核中在interrupt specific handler中是全程关闭CPU中断的。


    3、IRQ number

    从CPU的角度看,无论外部的Interrupt controller的结构是多么复杂,I do not care,我只关心发生了一个指定外设的中断,需要调用相应的外设中断的handler就OK了。更准确的说是通用中断处理模块不关心外部interrupt controller的组织细节(电源管理模块当然要关注具体的设备(interrupt controller也是设备)的拓扑结构)。一言以蔽之,通用中断处理模块可以用一个线性的table来管理一个个的外部中断,这个表的每个元素就是一个irq描述符,在kernel中定义如下:

    struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { 
        [0 ... NR_IRQS-1] = { 
            .handle_irq    = handle_bad_irq, 
            .depth        = 1, 
            .lock        = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock), 
        } 
    };

    系统中每一个连接外设的中断线(irq request line)用一个中断描述符来描述,每一个外设的interrupt request line分配一个中断号(irq number),系统中有多少个中断线(或者叫做中断源)就有多少个中断描述符(struct irq_desc)。NR_IRQS定义了该硬件平台IRQ的最大数目。

    总之,一个静态定义的表格,irq number作为index,每个描述符都是紧密的排在一起,一切看起来很美好,但是现实很残酷的。有些系统可能会定义一个很大的NR_IRQS,但是只是想用其中的若干个,换句话说,这个静态定义的表格不是每个entry都是有效的,有空洞,如果使用静态定义的表格就会导致了内存很大的浪费。为什么会有这种需求?我猜是和各个interrupt controller硬件的interrupt ID映射到irq number的算法有关。在这种情况下,静态表格不适合了,我们改用一个radix tree来保存中断描述符(HW interrupt作为索引)。这时候,每一个中断描述符都是动态分配,然后插入到radix tree中。如果你的系统采用这种策略,那么需要打开CONFIG_SPARSE_IRQ选项。上面的示意图描述的是静态表格的中断描述符DB,如果打开CONFIG_SPARSE_IRQ选项,系统使用Radix tree来保存中断描述符DB,不过概念和静态表格是类似的。

    此外,需要注意的是,在旧内核中,IRQ number和硬件的连接有一定的关系,但是,在引入irq domain后,IRQ number已经变成一个单纯的number,和硬件没有任何关系。

     

    三、中断描述符数据结构

    1、底层irq chip相关的数据结构

    中断描述符中应该会包括底层irq chip相关的数据结构,linux kernel中把这些数据组织在一起,形成struct irq_data,具体代码如下:

    struct irq_data { 
        u32            mask;----------TODO 
        unsigned int        irq;--------IRQ number 
        unsigned long        hwirq;-------HW interrupt ID 
        unsigned int        node;-------NUMA node index 
        unsigned int        state_use_accessors;--------底层状态,参考IRQD_xxxx 
        struct irq_chip        *chip;----------该中断描述符对应的irq chip数据结构 
        struct irq_domain    *domain;--------该中断描述符对应的irq domain数据结构 
        void            *handler_data;--------和外设specific handler相关的私有数据 
        void            *chip_data;---------和中断控制器相关的私有数据 
        struct msi_desc        *msi_desc; 
        cpumask_var_t        affinity;-------和irq affinity相关 
    };

    中断有两种形态,一种就是直接通过signal相连,用电平或者边缘触发。另外一种是基于消息的,被称为MSI (Message Signaled Interrupts)。msi_desc是MSI类型的中断相关,这里不再描述。

    node成员用来保存中断描述符的内存位于哪一个memory node上。 对于支持NUMA(Non Uniform Memory Access Architecture)的系统,其内存空间并不是均一的,而是被划分成不同的node,对于不同的memory node,CPU其访问速度是不一样的。如果一个IRQ大部分(或者固定)由某一个CPU处理,那么在动态分配中断描述符的时候,应该考虑将内存分配在该CPU访问速度比较快的memory node上。

     

    2、irq chip数据结构

    Interrupt controller描述符(struct irq_chip)包括了若干和具体Interrupt controller相关的callback函数,我们总结如下:

    成员名字 描述
    name 该中断控制器的名字,用于/proc/interrupts中的显示
    irq_startup start up 指定的irq domain上的HW interrupt ID。如果不设定的话,default会被设定为enable函数
    irq_shutdown shutdown 指定的irq domain上的HW interrupt ID。如果不设定的话,default会被设定为disable函数
    irq_enable enable指定的irq domain上的HW interrupt ID。如果不设定的话,default会被设定为unmask函数
    irq_disable disable指定的irq domain上的HW interrupt ID。
    irq_ack 和具体的硬件相关,有些中断控制器必须在Ack之后(清除pending的状态)才能接受到新的中断。
    irq_mask mask指定的irq domain上的HW interrupt ID
    irq_mask_ack mask并ack指定的irq domain上的HW interrupt ID。
    irq_unmask mask指定的irq domain上的HW interrupt ID
    irq_eoi 有些interrupt controler(例如GIC)提供了这样的寄存器接口,让CPU可以通知interrupt controller,它已经处理完一个中断
    irq_set_affinity 在SMP的情况下,可以通过该callback函数设定CPU affinity
    irq_retrigger 重新触发一次中断,一般用在中断丢失的场景下。如果硬件不支持retrigger,可以使用软件的方法。
    irq_set_type 设定指定的irq domain上的HW interrupt ID的触发方式,电平触发还是边缘触发
    irq_set_wake 和电源管理相关,用来enable/disable指定的interrupt source作为唤醒的条件。
    irq_bus_lock 有些interrupt controller是连接到慢速总线上(例如一个i2c接口的IO expander芯片),在访问这些芯片的时候需要lock住那个慢速bus(只能有一个client在使用I2C bus)
    irq_bus_sync_unlock unlock慢速总线
    irq_suspend 
    irq_resume 
    irq_pm_shutdown
    电源管理相关的callback函数
    irq_calc_mask TODO
    irq_print_chip /proc/interrupts中的信息显示

     

    3、中断描述符

    在linux kernel中,使用struct irq_desc来描述一个外设的中断,我们称之中断描述符,具体代码如下:

    struct irq_desc { 
        struct irq_data        irq_data; 
        unsigned int __percpu    *kstat_irqs;------IRQ的统计信息 
        irq_flow_handler_t    handle_irq;--------(1) 
        struct irqaction    *action; -----------(2) 
        unsigned int        status_use_accessors;-----中断描述符的状态,参考IRQ_xxxx 
        unsigned int        core_internal_state__do_not_mess_with_it;----(3) 
        unsigned int        depth;----------(4) 
        unsigned int        wake_depth;--------(5) 
        unsigned int        irq_count;  ---------(6) 
        unsigned long        last_unhandled;   
        unsigned int        irqs_unhandled; 
        raw_spinlock_t        lock;-----------(7) 
        struct cpumask        *percpu_enabled;-------(8) 
    #ifdef CONFIG_SMP 
        const struct cpumask    *affinity_hint;----和irq affinity相关,后续单独文档描述 
        struct irq_affinity_notify *affinity_notify; 
    #ifdef CONFIG_GENERIC_PENDING_IRQ 
        cpumask_var_t        pending_mask; 
    #endif 
    #endif 
        unsigned long        threads_oneshot; -----(9) 
        atomic_t        threads_active; 
        wait_queue_head_t       wait_for_threads; 
    #ifdef CONFIG_PROC_FS 
        struct proc_dir_entry    *dir;--------该IRQ对应的proc接口 
    #endif 
        int            parent_irq; 
        struct module        *owner; 
        const char        *name; 
    } ____cacheline_internodealigned_in_smp

    (1)handle_irq就是highlevel irq-events handler,何谓high level?站在高处自然看不到细节。我认为high level是和specific相对,specific handler处理具体的事务,例如处理一个按键中断、处理一个磁盘中断。而high level则是对处理各种中断交互过程的一个抽象,根据下列硬件的不同:

    (a)中断控制器

    (b)IRQ trigger type

    highlevel irq-events handler可以分成:

    (a)处理电平触发类型的中断handler(handle_level_irq)

    (b)处理边缘触发类型的中断handler(handle_edge_irq)

    (c)处理简单类型的中断handler(handle_simple_irq)

    (d)处理EOI类型的中断handler(handle_fasteoi_irq)

    会另外有一份文档对high level handler进行更详细的描述。

    (2)action指向一个struct irqaction的链表。如果一个interrupt request line允许共享,那么该链表中的成员可以是多个,否则,该链表只有一个节点。

    (3)这个有着很长名字的符号core_internal_state__do_not_mess_with_it在具体使用的时候被被简化成istate,表示internal state。就像这个名字定义的那样,我们最好不要直接修改它。

    #define istate core_internal_state__do_not_mess_with_it

    (4)我们可以通过enable和disable一个指定的IRQ来控制内核的并发,从而保护临界区的数据。对一个IRQ进行enable和disable的操作可以嵌套(当然一定要成对使用),depth是描述嵌套深度的信息。

    (5)wake_depth是和电源管理中的wake up source相关。通过irq_set_irq_wake接口可以enable或者disable一个IRQ中断是否可以把系统从suspend状态唤醒。同样的,对一个IRQ进行wakeup source的enable和disable的操作可以嵌套(当然一定要成对使用),wake_depth是描述嵌套深度的信息。

    (6)irq_count、last_unhandled和irqs_unhandled用于处理broken IRQ 的处理。所谓broken IRQ就是由于种种原因(例如错误firmware),IRQ handler没有定向到指定的IRQ上,当一个IRQ没有被处理的时候,kernel可以为这个没有被处理的handler启动scan过程,让系统中所有的handler来认领该IRQ。

    (7)保护该中断描述符的spin lock。

    (8)一个中断描述符可能会有两种情况,一种是该IRQ是global,一旦disable了该irq,那么对于所有的CPU而言都是disable的。还有一种情况,就是该IRQ是per CPU的,也就是说,在某个CPU上disable了该irq只是disable了本CPU的IRQ而已,其他的CPU仍然是enable的。percpu_enabled是一个描述该IRQ在各个CPU上是否enable成员。

    (9)threads_oneshot、threads_active和wait_for_threads是和IRQ thread相关,后续文档会专门描述。

     

    四、初始化相关的中断描述符的接口

    1、静态定义的中断描述符初始化

    int __init early_irq_init(void) 

        int count, i, node = first_online_node; 
        struct irq_desc *desc;

        init_irq_default_affinity();

        desc = irq_desc; 
        count = ARRAY_SIZE(irq_desc);

        for (i = 0; i < count; i++) {---遍历整个lookup table,对每一个entry进行初始化 
            desc[i].kstat_irqs = alloc_percpu(unsigned int);---分配per cpu的irq统计信息需要的内存 
            alloc_masks(&desc[i], GFP_KERNEL, node);---分配中断描述符中需要的cpu mask内存 
            raw_spin_lock_init(&desc[i].lock);---初始化spin lock 
            lockdep_set_class(&desc[i].lock, &irq_desc_lock_class); 
            desc_set_defaults(i, &desc[i], node, NULL);---设定default值 
        } 
        return arch_early_irq_init();---调用arch相关的初始化函数 
    }

    2、使用Radix tree的中断描述符初始化

    int __init early_irq_init(void) 
    {……

        initcnt = arch_probe_nr_irqs();---体系结构相关的代码来决定预先分配的中断描述符的个数

        if (initcnt > nr_irqs)---initcnt是需要在初始化的时候预分配的IRQ的个数 
            nr_irqs = initcnt; ---nr_irqs是当前系统中IRQ number的最大值

        for (i = 0; i < initcnt; i++) {--------预先分配initcnt个中断描述符 
            desc = alloc_desc(i, node, NULL);---分配中断描述符 
            set_bit(i, allocated_irqs);---设定已经alloc的flag 
            irq_insert_desc(i, desc);-----插入radix tree 
        } 
      …… 
    }

    即便是配置了CONFIG_SPARSE_IRQ选项,在中断描述符初始化的时候,也有机会预先分配一定数量的IRQ。这个数量由arch_probe_nr_irqs决定,对于ARM而言,其arch_probe_nr_irqs定义如下:

    int __init arch_probe_nr_irqs(void) 

        nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS; 
        return nr_irqs; 
    }

    3、分配和释放中断描述符

    对于使用Radix tree来保存中断描述符DB的linux kernel,其中断描述符是动态分配的,可以使用irq_alloc_descs和irq_free_descs来分配和释放中断描述符。alloc_desc函数也会对中断描述符进行初始化,初始化的内容和静态定义的中断描述符初始化过程是一样的。最大可以分配的ID是IRQ_BITMAP_BITS,定义如下:

    #ifdef CONFIG_SPARSE_IRQ 
    # define IRQ_BITMAP_BITS    (NR_IRQS + 8196)---对于Radix tree,除了预分配的,还可以动态
    #else                                                                             分配8196个中断描述符 
    # define IRQ_BITMAP_BITS    NR_IRQS---对于静态定义的,IRQ最大值就是NR_IRQS 
    #endif

     

    五、和中断控制器相关的中断描述符的接口

    这部分的接口主要有两类,irq_desc_get_xxx和irq_set_xxx,由于get接口API非常简单,这里不再描述,主要描述set类别的接口API。此外,还有一些locked版本的set接口API,定义为__irq_set_xxx,这些API的调用者应该已经持有保护irq desc的spinlock,因此,这些locked版本的接口没有中断描述符的spin lock进行操作。这些接口有自己特定的使用场合,这里也不详细描述了。

    1、接口调用时机

    kernel提供了若干的接口API可以让内核其他模块可以操作指定IRQ number的描述符结构。中断描述符中有很多的成员是和底层的中断控制器相关,例如:

    (1)该中断描述符对应的irq chip

    (2)该中断描述符对应的irq trigger type

    (3)high level handler

    在过去,系统中各个IRQ number是固定分配的,各个IRQ对应的中断控制器、触发类型等也都是清楚的,因此,一般都是在machine driver初始化的时候一次性的进行设定。machine driver的初始化过程会包括中断系统的初始化,在machine driver的中断初始化函数中,会调用本文定义的这些接口对各个IRQ number对应的中断描述符进行irq chip、触发类型的设定。

    在引入了device tree、动态分配IRQ number以及irq domain这些概念之后,这些接口的调用时机移到各个中断控制器的初始化以及各个具体硬件驱动初始化过程中,具体如下:

    (1)各个中断控制器定义好自己的struct irq_domain_ops callback函数,主要是map和translate函数。

    (2)在各个具体的硬件驱动初始化过程中,通过device tree系统可以知道自己的中断信息(连接到哪一个interrupt controler、使用该interrupt controller的那个HW interrupt ID,trigger type为何),调用对应的irq domain的translate进行翻译、解析。之后可以动态申请一个IRQ number并和该硬件外设的HW interrupt ID进行映射,调用irq domain对应的map函数。在map函数中,可以调用本节定义的接口进行中断描述符底层interrupt controller相关信息的设定。

     

    2、irq_set_chip

    这个接口函数用来设定中断描述符中desc->irq_data.chip成员,具体代码如下:

    int irq_set_chip(unsigned int irq, struct irq_chip *chip) 

        unsigned long flags; 
        struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); ----(1)

        desc->irq_data.chip = chip; 
        irq_put_desc_unlock(desc, flags);---------------(2) 

        irq_reserve_irq(irq);----------------------(3) 
        return 0; 
    }

    (1)获取irq number对应的中断描述符。这里用关闭中断+spin lock来保护中断描述符,flag中就是保存的关闭中断之前的状态flag,后面在(2)中会恢复中断flag。

    (3)前面我们说过,irq number有静态表格定义的,也有radix tree类型的。对于静态定义的中断描述符,没有alloc的概念。但是,对于radix tree类型,需要首先irq_alloc_desc或者irq_alloc_descs来分配一个或者一组IRQ number,在这些alloc函数值,就会set那些那些已经分配的IRQ。对于静态表格而言,其IRQ没有分配,因此,这里通过irq_reserve_irq函数标识该IRQ已经分配,虽然对于CONFIG_SPARSE_IRQ(使用radix tree)的配置而言,这个操作重复了(在alloc的时候已经设定了)。

     

    3、irq_set_irq_type

    这个函数是用来设定该irq number的trigger type的。

    int irq_set_irq_type(unsigned int irq, unsigned int type) 

        unsigned long flags; 
        struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); 
        int ret = 0;

        type &= IRQ_TYPE_SENSE_MASK; 
        ret = __irq_set_trigger(desc, irq, type); 
        irq_put_desc_busunlock(desc, flags); 
        return ret; 
    }

    来到这个接口函数,第一个问题就是:为何irq_set_chip接口函数使用irq_get_desc_lock来获取中断描述符,而irq_set_irq_type这个函数却需要irq_get_desc_buslock呢?其实也很简单,irq_set_chip不需要访问底层的irq chip(也就是interrupt controller),但是irq_set_irq_type需要。设定一个IRQ的trigger type最终要调用desc->irq_data.chip->irq_set_type函数对底层的interrupt controller进行设定。这时候,问题来了,对于嵌入SOC内部的interrupt controller,当然没有问题,因为访问这些中断控制器的寄存器memory map到了CPU的地址空间,访问非常的快,因此,关闭中断+spin lock来保护中断描述符当然没有问题,但是,如果该interrupt controller是一个I2C接口的IO expander芯片(这类芯片是扩展的IO,也可以提供中断功能),这时,让其他CPU进行spin操作太浪费CPU时间了(bus操作太慢了,会spin很久的)。肿么办?当然只能是用其他方法lock住bus了(例如mutex,具体实现是和irq chip中的irq_bus_lock实现相关)。一旦lock住了slow bus,然后就可以关闭中断了(中断状态保存在flag中)。

    解决了bus lock的疑问后,还有一个看起来奇奇怪怪的宏:IRQ_GET_DESC_CHECK_GLOBAL。为何在irq_set_chip函数中不设定检查(check的参数是0),而在irq_set_irq_type接口函数中要设定global的check,到底是什么意思呢?既然要检查,那么检查什么呢?和“global”对应的不是local而是“per CPU”,内核中的宏定义是:IRQ_GET_DESC_CHECK_PERCPU。SMP情况下,从系统角度看,中断有两种形态(或者叫mode):

    (1)1-N mode。只有1个processor处理中断

    (2)N-N mode。所有的processor都是独立的收到中断,如果有N个processor收到中断,那么就有N个处理器来处理该中断。

    听起来有些抽象,我们还是用GIC作为例子来具体描述。在GIC中,SPI使用1-N模型,而PPI和SGI使用N-N模型。对于SPI,由于采用了1-N模型,系统(硬件加上软件)必须保证一个中断被一个CPU处理。对于GIC,一个SPI的中断可以trigger多个CPU的interrupt line(如果Distributor中的Interrupt Processor Targets Registers有多个bit被设定),但是,该interrupt source和CPU的接口寄存器(例如ack register)只有一套,也就是说,这些寄存器接口是全局的,是global的,一旦一个CPU ack(读Interrupt Acknowledge Register,获取interrupt ID)了该中断,那么其他的CPU看到的该interupt source的状态也是已经ack的状态。在这种情况下,如果第二个CPU ack该中断的时候,将获取一个spurious interrupt ID。

    对于PPI或者SGI,使用N-N mode,其interrupt source的寄存器是per CPU的,也就是每个CPU都有自己的、针对该interrupt source的寄存器接口(这些寄存器叫做banked register)。一个CPU 清除了该interrupt source的pending状态,其他的CPU感知不到这个变化,它们仍然认为该中断是pending的。

    对于irq_set_irq_type这个接口函数,它是for 1-N mode的interrupt source使用的。如果底层设定该interrupt是per CPU的,那么irq_set_irq_type要返回错误。

    4、irq_set_chip_data

    每个irq chip总有自己私有的数据,我们称之chip data。具体设定chip data的代码如下:

    int irq_set_chip_data(unsigned int irq, void *data) 

        unsigned long flags; 
        struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);  
        desc->irq_data.chip_data = data; 
        irq_put_desc_unlock(desc, flags); 
        return 0; 
    }

    多么清晰、多么明了,需要文字继续描述吗?

    5、设定high level handler

    这是中断处理的核心内容,__irq_set_handler就是设定high level handler的接口函数,不过一般不会直接调用,而是通过irq_set_chip_and_handler_name或者irq_set_chip_and_handler来进行设定。具体代码如下:

    void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name) 

        unsigned long flags; 
        struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);

    …… 
        desc->handle_irq = handle; 
        desc->name = name;

        if (handle != handle_bad_irq && is_chained) { 
            irq_settings_set_noprobe(desc); 
            irq_settings_set_norequest(desc); 
            irq_settings_set_nothread(desc); 
            irq_startup(desc, true); 
        } 
    out: 
        irq_put_desc_busunlock(desc, flags); 
    }

    理解这个函数的关键是在is_chained这个参数。这个参数是用在interrupt级联的情况下。例如中断控制器B级联到中断控制器A的第x个interrupt source上。那么对于A上的x这个interrupt而言,在设定其IRQ handler参数的时候要设定is_chained参数等于1,由于这个interrupt source用于级联,因此不能probe、不能被request(已经被中断控制器B使用了),不能被threaded(具体中断线程化的概念在其他文档中描述)


    原创文章,转发请注明出处。蜗窝科技。http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html

    标签: irq 中断子系统 中断描述符

    展开全文
  • 凌阳16位单片机之IRQ6中断

    千次阅读 2015-05-23 17:18:48
    实现功能:利用IRQ6的两个时基中断,即IRQ6_TMB1和IRQ6_TMB2中断,在各自的中断服务程序中对相应的变量进行累加;并通过点亮、熄灭发光二极管来表示相应变量计数器累加的速度。  实验现象:D4~D7四个发光二极管...
  • IRQ

    2019-10-08 23:10:24
    IRQ 目录 简介用途 ... IRQ全称为Interrupt Request,即是“中断请求”的意思(以下使用IRQ称呼)。IRQ的作用就是在我们所用的电脑中,执行硬件中断请求的...在每台电脑的系统中,是由一个中断控制器8259或是8...
  • A: 从栈地址开始,栈顶为AT91SAM7S64的16K片内RAM尽头0x00204000IRQ_STACK_SIZE = 3*8*4FIQ_STACK_SIZE = 0x004ABT_STACK_SIZE = 0x004UND_STACK_SIZE = 0x004SVC_STACK_SIZE = ...0x400irq栈为什么用3*8*4=96B...
  • linux kernel的中断子系统之(四):High level irq event handler作者:linuxer 发布于:2014-8-28 20:00分类:中断子系统一、前言当外设触发一次中断后,一个大概的处理过程是:1、具体CPU architecture相关的模块会...
  • 其中IRQ中断包含了所有外部中断,采用中断号机制,每一个外部中断具有自己的中断号,通过给每一个中断号注册中断服务函数可以初始化中断服务函数; 编写cortex A系列mcu中断服务函数的时候遇到了这
  • NRF24L01 使用IRQ处理收发中断,六发一收 NRF24L01 使用IRQ处理收发中断,六发一收
  • 原文地址:https://mp.weixin.qq.com/s/vJCoUr8wHanF9Epvi1rKcw 一、前言 中断在驱动中是非常常用的,无论是外部的 GPIO 中断,还是 SPI,I2C 等发送或接收中断,都是必不可少的。所以今天来看看 Linux 中的中断...
  • 作者:linuxer 发布于:2014-8-19 18:46 分类:中断子系统 一、概述 在linux kernel中,我们使用下面两个ID来标识一个来自外设的中断: 1、IRQ number。CPU需要为每一个外设中断编号,我们称之IRQ Number。这个...
  • 1、ARMV8 SCR.FIQ/SCR.IRQ的bit位定义 意思就是说,如果SCR.IRQ=0,IRQ中断不会target到EL3,如果SCR.IRQ=1,则IRQ将会被target到EL3 同样FIQ也是如此,如果SCR.FIQ=0,FIQ中断不会target到EL3,如果SCR.FIQ=1,则...
  • 博主把今天学到的基于三星S5PV210核心板的简单IRQ中断框架代码加上个人的理解在这里总结了一下,如有错误理解,欢迎大家指正交流。本文以基于Cortex-A8的三星s5pv210实现外部中断16来点亮LED灯为例子。
  • 内核使用一个称为irq_chip的结构体来描述中断控制器,对中断的操作(启用/禁用某个中断源)将通过irq_chip的回调函数进行,此结构体的定义位于include/linux/irq.h:/** * struct irq_chip - hardware interrupt chip...
  • ARM异常处理(IRQ中断处理)总结1

    千次阅读 2019-02-26 19:46:34
    我们现在要从SVC转换模式到IRQ因此需要保存的寄存器是R0~R12因为是共用的这两个模式之间,还有一个就是SVC下的PC(在模式跳转的时候硬件会把SVC下的PC直接保存到IRQ下的lr里边)因为不保存这个就没办法返回了但是要...
  • 前面提到,当内核发生IVG6-IVG13这8中断时,最后都会调用do_irq函数进行处理,下面看看这个函数的实现:void do_irq(int vec, struct pt_regs *fp){ if (vec == EVT_IVTMR_P) { vec = IRQ_CORETMR; } else { ...
  • 作者:linuxer发布于:2014-8-19 18:46 分类:Linux内核...CPU需要为每一个外设中断编号,我们称之IRQ Number。这个IRQnumber是一个虚拟的interrupt ID,和硬件无关,仅仅是被CPU用来标识一个外设中断。 2、HW inter
  • linux kernel的中断子系统之(四):High level irq event handler 作者:linuxer 发布于:2014-8-28 20:00 分类:中断子系统 一、前言 当外设触发一次中断后,一个大概的处理过程是: 1、具体CPU ...
  • 因为ARM指令是三级流水线 就是说取指,译指,执行时同时执行的 这样说吧,现在PC指向的是正在取...当突然发生中断的时候,保存的是PC的地址 这样你就知道了,如果返回的时候返回PC,那么中间就有一个指令没有执行 所以
  • /* 设置定时器0中断IRQ */ VICIntSelect = 0x00; // 所有中断通道设置为IRQ中断 VICVectPri4 = 00; // 设置定时器0中断最高优先级 VICVectAddr4 = (uint32)IRQ_Timer0; // 设置中断服务程序地址 ...
  • ARM采用五级流水线技术(取码,译码,执行,缓冲,回写),对于ARM指令集而言,PC总是指向当前指令的下两条指令地址,即PC+8; 对异常的响应: 1)将下一条指令的地址存入相应连接寄存器LR,以便异常返回。若异常从...
  • Arm v8 中断处理

    千次阅读 2014-10-15 17:00:36
    ARM v8的工作模式 ARM v8支持的工作状态, EL0(用户态),EL1(内核态),EL2(虚拟机/可选),EL3(监控...异步中断有3种,irq、fiq、err. 外部中断通过irq告知CPU。 ARM v8的中断处理 基于ARM的SoC大都采用GI
  • 首先当然是操作cpsr寄存器来开启中断模式下的IRQ中断(因为在BL0中的程序将IRQ中断关闭了)2.设置中断模式下的栈地址3.中断前的环境保存和中断后的环境恢复代码如下():.global Reset Reset: @关闭看门狗省略,bl0...

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 331
精华内容 132
关键字:

中断irq8