首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

GIC代码分析(3)

GIC代码分析(3)

下面的表格按照时间轴来描述交互过程:
                                                                                                                                                                                                               
                                时间                                                        交互动作的描述                       
                                T0时刻                                                        Distributor检测到M这个interrupt source的有效触发电平                       
                                T2时刻                                                        Distributor将M这个interrupt source的状态设定为pending                       
                                T17时刻                                                        大约15个clock之后,CPU interface拉低nFIQCPU信号线,向CPU报告M外设的中断请求。这时候,CPU interface的ack寄存器(GICC_IAR)的内容会修改成M interrupt source对应的ID                       
                                T42时刻                                                        Distributor检测到N这个优先级更高的interrupt source的触发事件                       
                                T43时刻                                                        Distributor将N这个interrupt source的状态设定为pending。同时,由于N的优先级更高,因此Distributor会标记当前优先级最高的中断                       
                                T58时刻                                                        大约15个clock之后,CPU interface拉低nFIQCPU信号线,向CPU报告N外设的中断请求。当然,由于T17时刻已经assert CPU了,因此实际的电平信号仍然保持asserted。这时候,CPU interface的ack寄存器(GICC_IAR)的内容会被更新成N interrupt source的ID                       
                                T61时刻                                                        软件通过读取ack寄存器的内容,获取了当前优先级最高的,并且状态是pending的interrupt ID(也就是N interrupt source对应的ID),通过读该寄存器,CPU也就ack了该interrupt source N。这时候,Distributor将N这个interrupt source的状态设定为pending and active(因为是电平触发,只要外部仍然有asserted的电平信号,那么一定就是pending的,而该中断是正在被CPU处理的中断,因此状态是pending and active)
注意:T61标识CPU开始服务该中断                       
                                T64时刻                                                        3个clock之后,由于CPU已经ack了中断,因此GIC中CPU interface模块 deassert nFIQCPU信号线,解除发向该CPU的中断请求                       
                                T126时刻                                                        由于中断服务程序操作了N外设的控制寄存器(ack外设的中断),因此N外设deassert了其interrupt request signal                       
                                T128时刻                                                        Distributor解除N外设的pending状态,因此N这个interrupt source的状态设定为active                       
                                T131时刻                                                        软件操作End of Interrupt寄存器(向GICC_EOIR寄存器写入N对应的interrupt ID),标识中断处理结束。Distributor将N这个interrupt source的状态修改为idle
注意:T61~T131是CPU服务N外设中断的的时间区域,这个期间,如果有高优先级的中断pending,会发生中断的抢占(硬件意义的),这时候CPU interface会向CPU assert 新的中断。                       
                                T146时刻                                                        大约15个clock之后,Distributor向CPU interface报告当前pending且优先级最高的interrupt source,也就是M了。漫长的pending之后,M终于迎来了春天。CPU interface拉低nFIQCPU信号线,向CPU报告M外设的中断请求。这时候,CPU interface的ack寄存器(GICC_IAR)的内容会修改成M interrupt source对应的ID                       
                                T211时刻                                                        CPU ack M中断(通过读GICC_IAR寄存器),开始处理低优先级的中断。                       

        三、GIC-V2 irq chip driver的初始化过程
        在linux-3.17-rc3\drivers\irqchip目录下保存在各种不同的中断控制器的驱动代码,这个版本的内核支持了GICV3。irq-gic-common.c是通用的GIC的驱动代码,可以被各个版本的GIC使用。irq-gic.c是用于V2版本的GIC controller,而irq-gic-v3.c是用于V3版本的GIC controller。
        1、GIC的device node和GIC irq chip driver的匹配过程
        (1)irq chip driver中的声明
        在linux-3.17-rc3\drivers\irqchip目录下的irqchip.h文件中定义了IRQCHIP_DECLARE宏如下:
                        #define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)       
                        #define OF_DECLARE_2(table, name, compat, fn) \
        _OF_DECLARE(table, name, compat, fn, of_init_fn_2)       
                        #define _OF_DECLARE(table, name, compat, fn, fn_type)            \
    static const struct of_device_id __of_table_##name        \
        __used __section(__##table##_of_table)            \
         = { .compatible = compat,                \
             .data = (fn == (fn_type)NULL) ? fn : fn  }       
        这个宏其实就是初始化了一个struct of_device_id的静态常量,并放置在__irqchip_of_table section中。irq-gic.c文件中使用IRQCHIP_DECLARE来定义了若干个静态的struct of_device_id常量,如下:
                        IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);
IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);       
        兼容GIC-V2的GIC实现有很多,不过其初始化函数都是一个。在linux kernel编译的时候,你可以配置多个irq chip进入内核,编译系统会把所有的IRQCHIP_DECLARE宏定义的数据放入到一个特殊的section中(section name是__irqchip_of_table),我们称这个特殊的section叫做irq chip table。这个table也就保存了kernel支持的所有的中断控制器的ID信息(最重要的是驱动代码初始化函数和DT compatible string)。我们来看看struct of_device_id的定义:
                        struct of_device_id
{
    char    name[32];------要匹配的device node的名字
    char    type[32];-------要匹配的device node的类型
    char    compatible[128];---匹配字符串(DT compatible string),用来匹配适合的device node
    const void *data;--------对于GIC,这里是初始化函数指针
};       
        这个数据结构主要被用来进行Device node和driver模块进行匹配用的。从该数据结构的定义可以看出,在匹配过程中,device name、device type和DT compatible string都是考虑的因素。更细节的内容请参考__of_device_is_compatible函数。
        (2)device node
        不同的GIC-V2的实现总会有一些不同,这些信息可以通过Device tree的机制来传递。Device node中定义了各种属性,其中就包括了memory资源,IRQ描述等信息,这些信息需要在初始化的时候传递给具体的驱动,因此需要一个Device node和driver模块的匹配过程。在Device Tree模块中会包括系统中所有的device node,如果我们的系统使用了GIC-400,那么系统的device node数据库中会有一个node是GIC-400的,一个示例性的GIC-400的device node(我们以瑞芯微的RK3288处理器为例)定义如下:
                        gic: interrupt-controller@ffc01000 {
    compatible = "arm,gic-400";
    interrupt-controller;
    #interrupt-cells = <3>;
    #address-cells = <0>;       
                            reg = <0xffc01000 0x1000="">,----Distributor address range
          <0xffc02000 0x1000="">,-----CPU interface address range
          <0xffc04000 0x2000="">,-----Virtual interface control block
          <0xffc06000 0x2000="">;-----Virtual CPU interfaces
    interrupts = ;
};        

        (3)device node和irq chip driver的匹配
        在machine driver初始化的时候会调用irqchip_init函数进行irq chip driver的初始化。在driver/irqchip/irqchip.c文件中定义了irqchip_init函数,如下:
       
                        void __init irqchip_init(void)
{
    of_irq_init(__irqchip_begin);
}       
        __irqchip_begin就是内核irq chip table的首地址,这个table也就保存了kernel支持的所有的中断控制器的ID信息(用于和device node的匹配)。of_irq_init函数执行之前,系统已经完成了device tree的初始化,因此系统中的所有的设备节点都已经形成了一个树状结构,每个节点代表一个设备的device node。of_irq_init是在所有的device node中寻找中断控制器节点,形成树状结构(系统可以有多个interrupt controller,之所以形成中断控制器的树状结构,是为了让系统中所有的中断控制器驱动按照一定的顺序进行初始化)。之后,从root interrupt controller节点开始,对于每一个interrupt controller的device node,扫描irq chip table,进行匹配,一旦匹配到,就调用该interrupt controller的初始化函数,并把该中断控制器的device node以及parent中断控制器的device node作为参数传递给irq chip driver。。具体的匹配过程的代码属于Device Tree模块的内容,更详细的信息可以参考Device Tree代码分析文档

        2、GIC driver初始化代码分析
        (1)gic_of_init的代码如下:
                        int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
    void __iomem *cpu_base;
    void __iomem *dist_base;
    u32 percpu_offset;
    int irq;       
                            dist_base = of_iomap(node, 0);----------------映射GIC Distributor的寄存器地址空间       
                            cpu_base = of_iomap(node, 1);----------------映射GIC CPU interface的寄存器地址空间       
                            if (of_property_read_u32(node, "cpu-offset", &percpu_offset))--------处理cpu-offset属性。
        percpu_offset = 0;       
                            gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);))-----主处理过程,后面详述
    if (!gic_cnt)
        gic_init_physaddr(node); -----对于不支持big.LITTLE switcher(CONFIG_BL_SWITCHER)的系统,该函数为空。       
                            if (parent) {--------处理interrupt级联
        irq = irq_of_parse_and_map(node, 0); ---解析second GIC的interrupts属性,并进行mapping,返回IRQ number
        gic_cascade_irq(gic_cnt, irq);
    }
    gic_cnt++;
    return 0;
}       
继承事业,薪火相传
返回列表