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

GIC代码分析(8)

GIC代码分析(8)

(5)gic_retrigger
        这个接口用来resend一个IRQ到CPU。
                        static int gic_retrigger(struct irq_data *d)
{
    if (gic_arch_extn.irq_retrigger)
        return gic_arch_extn.irq_retrigger(d);       
                            /* the genirq layer expects 0 if we can't retrigger in hardware */
    return 0;
}       
        看起来这是功能不是通用GIC拥有的功能,各个厂家在集成GIC的时候,有可能进行功能扩展。
        (6)gic_set_affinity
        在多处理器的环境下,外部设备产生了一个中断就需要送到一个或者多个处理器去,这个设定是通过设定处理器的affinity进行的。具体代码如下:
                        static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,    bool force)
{
    void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
    unsigned int cpu, shift = (gic_irq(d) % 4) * 8;
    u32 val, mask, bit;       
                            if (!force)
        cpu = cpumask_any_and(mask_val, cpu_online_mask);---随机选取一个online的cpu
    else
        cpu = cpumask_first(mask_val); --------选取mask中的第一个cpu,不管是否online       
                            raw_spin_lock(&irq_controller_lock);
    mask = 0xff << shift;
    bit = gic_cpu_map[cpu] << shift;-------将CPU的逻辑ID转换成要设定的cpu mask
    val = readl_relaxed(reg) & ~mask;
    writel_relaxed(val | bit, reg);
    raw_spin_unlock(&irq_controller_lock);       
                            return IRQ_SET_MASK_OK;
}       
        GIC Distributor中有一个寄存器叫做Interrupt Processor Targets Registers,这个寄存器用来设定制定的中断送到哪个process去。由于GIC最大支持8个process,因此每个hw interrupt ID需要8个bit来表示送达的process。每一个Interrupt Processor Targets Registers由32个bit组成,因此每个Interrupt Processor Targets Registers可以表示4个HW interrupt ID的affinity,因此上面的代码中的shift就是计算该HW interrupt ID在寄存器中的偏移。
        (7)gic_set_wake
        这个接口用来设定唤醒CPU的interrupt source。对于GIC,代码如下:
                        static int gic_set_wake(struct irq_data *d, unsigned int on)
{
    int ret = -ENXIO;       
                            if (gic_arch_extn.irq_set_wake)
        ret = gic_arch_extn.irq_set_wake(d, on);       
                            return ret;
}       
        设定唤醒的interrupt和具体的厂商相关,这里不再赘述。

        4、BSP(bootstrap processor)之外,其他CPU的callback函数
        对于multi processor系统,不可能初始化代码在所有的processor上都执行一遍,实际上,系统的硬件会选取一个processor作为引导处理器,我们称之BSP。这个processor会首先执行,其他的CPU都是处于reset状态,等到BSP初始化完成之后,release所有的non-BSP,这时候,系统中的各种外设硬件条件和软件条件(例如per CPU变量)都准备好了,各个non-BSP执行自己CPU specific的初始化就OK了。
        上面描述的都是BSP的初始化过程,具体包括:
                        ……
    gic_dist_init(gic);------初始化GIC的Distributor
    gic_cpu_init(gic);------初始化BSP的CPU interface
    gic_pm_init(gic);------初始化GIC的Power management
……       
        对于GIC的Distributor和Power management,这两部分是全局性的,BSP执行初始化一次就OK了。对于CPU interface,每个processor负责初始化自己的连接的那个CPU interface HW block。我们用下面这个图片来描述这个过程:
       
          假设CPUx被选定为BSP,那么第三章描述的初始化过程在该CPU上欢畅的执行。这时候,被初始化的GIC硬件包括:root GIC的Distributor、root GIC CPU Interface x(连接BSP的那个CPU interface)以及其他的级联的非root GIC(上图中绿色block,当然,我偷懒,没有画non-root GIC)。
        BSP初始化完成之后,各个其他的CPU运行起来,会发送CPU_STARTING消息给关注该消息的模块。毫无疑问,GIC driver模块当然要关注这样的消息,在初始化过程中会注册callback函数如下:
                        register_cpu_notifier(&gic_cpu_notifier);       
        GIC相关的回调函数定义如下:
                        static struct notifier_block gic_cpu_notifier = {
    .notifier_call = gic_secondary_init,
    .priority = 100,
};       
                        static int gic_secondary_init(struct notifier_block *nfb, unsigned long action,  void *hcpu)
{
    if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
        gic_cpu_init(&gic_data[0]);---------初始化那些非BSP的CPU interface
    return NOTIFY_OK;
}       
        因此,当non-BSP booting up的时候,发送CPU_STARTING消息,调用GIC的callback函数,对上图中的紫色的CPU Interface HW block进行初始化,这样,就完成了全部GIC硬件的初始化过程。
继承事业,薪火相传
返回列表