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

GIC代码分析(7)

GIC代码分析(7)

2、电源管理的callback函数
        TODO

        3、irq chip回调函数分析
        (1)gic_mask_irq函数
        这个函数用来mask一个interrupt source。代码如下:
                        static void gic_mask_irq(struct irq_data *d)
{
    u32 mask = 1 << (gic_irq(d) % 32);       
                            raw_spin_lock(&irq_controller_lock);
    writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);
    if (gic_arch_extn.irq_mask)
        gic_arch_extn.irq_mask(d);
    raw_spin_unlock(&irq_controller_lock);
}       
        GIC有若干个叫做Interrupt Clear-Enable Registers(具体数目是和GIC支持的hw interrupt数目相关,我们前面说过的,GIC是一个高度可配置的interrupt controller)。这些Interrupt Clear-Enable Registers寄存器的每个bit可以控制一个interrupt source是否forward到CPU interface,写入1表示Distributor不再forward该interrupt,因此CPU也就感知不到该中断,也就是mask了该中断。特别需要注意的是:写入0无效,而不是unmask的操作。
        由于不同的SOC厂商在集成GIC的时候可能会修改,也就是说,也有可能mask的代码要微调,这是通过gic_arch_extn这个全局变量实现的。在gic-irq.c中这个变量的全部成员都设定为NULL,各个厂商在初始中断控制器的时候可以设定其特定的操作函数。
        (2)gic_unmask_irq函数
        这个函数用来unmask一个interrupt source。代码如下:
                        static void gic_unmask_irq(struct irq_data *d)
{
    u32 mask = 1 << (gic_irq(d) % 32);       
                            raw_spin_lock(&irq_controller_lock);
    if (gic_arch_extn.irq_unmask)
        gic_arch_extn.irq_unmask(d);
    writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
    raw_spin_unlock(&irq_controller_lock);
}       
        GIC有若干个叫做Interrupt Set-Enable Registers的寄存器。这些寄存器的每个bit可以控制一个interrupt source。当写入1的时候,表示Distributor会forward该interrupt到CPU interface,也就是意味这unmask了该中断。特别需要注意的是:写入0无效,而不是mask的操作。
        (3)gic_eoi_irq函数
        当processor处理中断的时候就会调用这个函数用来结束中断处理。代码如下:
                        static void gic_eoi_irq(struct irq_data *d)
{
    if (gic_arch_extn.irq_eoi) {
        raw_spin_lock(&irq_controller_lock);
        gic_arch_extn.irq_eoi(d);
        raw_spin_unlock(&irq_controller_lock);
    }       
                            writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
}       
        对于GIC而言,其中断状态有四种:
                                                               
                                中断状态                                                        描述                       
                                Inactive                                                        中断未触发状态,该中断即没有Pending也没有Active                       
                                Pending                                                        由于外设硬件产生了中断事件(或者软件触发)该中断事件已经通过硬件信号通知到GIC,等待GIC分配的那个CPU进行处理                       
                                Active                                                        CPU已经应答(acknowledge)了该interrupt请求,并且正在处理中                       
                                Active and Pending                                                        当一个中断源处于Active状态的时候,同一中断源又触发了中断,进入pending状态                       
        processor ack了一个中断后,该中断会被设定为active。当处理完成后,仍然要通知GIC,中断已经处理完毕了。这时候,如果没有pending的中断,GIC就会将该interrupt设定为inactive状态。操作GIC中的End of Interrupt Register可以完成end of interrupt事件通知。
        (4)gic_set_type函数
        这个函数用来设定一个interrupt source的type,例如是level sensitive还是edge triggered。代码如下:
                        static int gic_set_type(struct irq_data *d, unsigned int type)
{
    void __iomem *base = gic_dist_base(d);
    unsigned int gicirq = gic_irq(d);
    u32 enablemask = 1 << (gicirq % 32);
    u32 enableoff = (gicirq / 32) * 4;
    u32 confmask = 0x2 << ((gicirq % 16) * 2);
    u32 confoff = (gicirq / 16) * 4;
    bool enabled = false;
    u32 val;       
                            /* Interrupt configuration for SGIs can't be changed */
    if (gicirq < 16)
        return -EINVAL;       
                            if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
        return -EINVAL;       
                            raw_spin_lock(&irq_controller_lock);       
                            if (gic_arch_extn.irq_set_type)
        gic_arch_extn.irq_set_type(d, type);       
                            val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
    if (type == IRQ_TYPE_LEVEL_HIGH)
        val &= ~confmask;
    else if (type == IRQ_TYPE_EDGE_RISING)
        val |= confmask;       
                            /*
     * As recommended by the spec, disable the interrupt before changing
     * the configuration
     */
    if (readl_relaxed(base + GIC_DIST_ENABLE_SET + enableoff) & enablemask) {
        writel_relaxed(enablemask, base + GIC_DIST_ENABLE_CLEAR + enableoff);
        enabled = true;
    }       
                            writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);       
                            if (enabled)
        writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);       
                            raw_spin_unlock(&irq_controller_lock);       
                            return 0;
}       
        对于SGI类型的interrupt,是不能修改其type的,因为GIC中SGI固定就是edge-triggered。对于GIC,其type只支持高电平触发(IRQ_TYPE_LEVEL_HIGH)和上升沿触发(IRQ_TYPE_EDGE_RISING)的中断。另外需要注意的是,在更改其type的时候,先disable,然后修改type,然后再enable。
继承事业,薪火相传
返回列表