卷一 内核源代码分析 第二章 异常 2.2.3 Exynos4的中断体系 图书试读版-请勿转载

作者 [email protected]


2.2.3 Exynos4的中断体系


三星,ARM处理器界的新王者,近年来抢先实现每一代ARM处理器,而且通过手机处理器与其庞大硬件产业链的有机整合成为Apple的最有力对手。
第一次将三星与高性能手机处理器联系起来的是其S5PV210,凭借超出同级处理器一倍的L2 Cache,S5PV210成为当时跑的最快的CA8 ARM。紧接着三星开始抢跑ARM界,在CA9时期,三星已经成为ARM处理器界的第一阵营,在CA15时期三星成为第一家先进ARM架构的实现者。
三星的ARM处理器成功背后的很大原因在于三星强大的产业链整合能力。在前端,三星手机的攻城略地保证了三星处理器出货量。在后端,三星的半导体工厂有比肩Intel的能力,总是可以以更先进的工艺的来降低功耗、节省成本。在市场大门开启的时候,三星的对手却要为处理器代工厂的工艺和良率伤透脑筋,为炫酷的LCD屏幕供货周期头疼,为了内存和EMMC的价格波动心惊胆战,而所有这些,三星集团的各个工厂数月之前都已经协调完毕,正按计划批量出货了。


本节分析exynos4的中断体系。


CPU0的GIC初始化工作从“void __init exynos4_init_irq(void)”开始:
void __init exynos4_init_irq(void)
{
int irq;


gic_bank_offset = soc_is_exynos4412() ? 0x4000 : 0x8000;
// S5P_VA_GIC_DIST是CPU0的GIC DIST基地址,S5P_VA_GIC_CPU是CPU0的GIC CPU interface 基地址
gic_init(0, IRQ_PPI_MCT_L, S5P_VA_GIC_DIST, S5P_VA_GIC_CPU);



}


void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
void __iomem *dist_base, void __iomem *cpu_base)
{
struct gic_chip_data *gic;
int cpu;



    //GIC描述结构:struct gic_chip_data
gic = &gic_data[gic_nr];
//为每颗CPU分配放置DIST和CPU interface基地址的内存地址。
gic->dist_base = alloc_percpu(void __iomem *);
gic->cpu_base = alloc_percpu(void __iomem *);

    //先初始化CPU0的DIST和CPU interface基地址
for_each_possible_cpu(cpu) {
*per_cpu_ptr(gic->dist_base, cpu) = dist_base;
*per_cpu_ptr(gic->cpu_base, cpu) = cpu_base;
}


gic->irq_offset = (irq_start - 1) & ~31;


if (gic_nr == 0)
gic_cpu_base_addr = cpu_base;


    //DIST的初始化
gic_dist_init(gic, irq_start);
//CPU0的CPU interface基地址的内存
gic_cpu_init(gic);
}


/*所谓GIC dist的初始化,系统里只在CPU0进行,核心工作是配置GIC SPI中断(通常外围设备产生的中断)。*/
static void __init gic_dist_init(struct gic_chip_data *gic,
unsigned int irq_start)
{
unsigned int gic_irqs, irq_limit, i;
void __iomem *base = gic_data_dist_base(gic);
u32 cpumask = 1 << smp_processor_id();


//cpumask:每个8位的最低位都置1


cpumask |= cpumask << 8;
cpumask |= cpumask << 16;


writel_relaxed(0, base + GIC_DIST_CTRL);


/*
* Find out how many interrupts are supported.
* The GIC only supports up to 1020 interrupt sources.
*/
gic_irqs = readl_relaxed(base + GIC_DIST_CTR) & 0x1f;
gic_irqs = (gic_irqs + 1) * 32;
if (gic_irqs > 1020)
gic_irqs = 1020;


/*
* Set all global interrupts to be level triggered, active low.
*/
//先将SPI设置为电平触发


for (i = 32; i < gic_irqs; i += 16)
writel_relaxed(0, base + GIC_DIST_CONFIG + i * 4 / 16);


/*
* Set all global interrupts to this CPU only.
*/
//偏移量0x800-0x81C空间的每个BYTE指出了对应的中断分发到哪颗CPU上
//这里把所有的全局中断的分发方向都指向自己,这时只有CPU0可用。以后还可以使用
struct irq_chip的“int (*set_affinity)(unsigned int irq,const struct cpumask *dest);”函数将中断绑定到另外的CPU上。


for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);


/*
* Set priority on all global interrupts.
*/
//0x400-0x7F8


for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);


/*
关闭除PPI 和SGIs 之外的中断
*/
for (i = 32; i < gic_irqs; i += 32)
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);


/*
* Limit number of interrupts registered to the platform maximum
*/
irq_limit = gic->irq_offset + gic_irqs;
if (WARN_ON(irq_limit > NR_IRQS))
irq_limit = NR_IRQS;


/*
* Setup the Linux IRQ subsystem.
*/
//填充linux里generic中断分发数组
for (i = irq_start; i < irq_limit; i++) {
irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq);
irq_set_chip_data(i, gic);
set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
}


writel_relaxed(1, base + GIC_DIST_CTRL);
}


//CPU0的GIC cpu interface中断初始化,主要是PPI和SGI初始化,SGI是处理器间通信的重要手段
static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
{
void __iomem *dist_base = gic_data_dist_base(gic);
void __iomem *base = gic_data_cpu_base(gic);
int i;


/*
* Deal with the banked PPI and SGI interrupts - disable all
* PPI interrupts, ensure all SGI interrupts are enabled.
*/
//值得注意的是,这里PPI中断是被屏蔽的
writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR);
writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET);


/*
* Set priority on PPI and SGI interrupts
*/
for (i = 0; i < 32; i += 4)
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);


writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
writel_relaxed(1, base + GIC_CPU_CTRL);
}


CPUX相关的GIC配置
CPUX不需要初始化GIC的SPI中断,所以CPUX相关的GIC初始化工作完成自身的GIC cpu interface中断初始化即可。值得关注是其执行流程:


asmlinkage void __cpuinit secondary_start_kernel(void)
{…
platform_secondary_init(cpu);
…}




void __cpuinit platform_secondary_init(unsigned int cpu)
{
//显然对于Exynos4x12处理器,CPUX的GIC DIST和CPU interface基地址的计算是:
CPU0的对应基地址+ 前面CPU占用的长度
void __iomem *dist_base = S5P_VA_GIC_DIST +
(gic_bank_offset * cpu);
void __iomem *cpu_base = S5P_VA_GIC_CPU +
(gic_bank_offset * cpu);



gic_secondary_init_base(0, dist_base, cpu_base);



}


void __cpuinit gic_secondary_init_base(unsigned int gic_nr,
      void __iomem *dist_base,
      void __iomem *cpu_base)
{
//先把基地址放到GIC描述结构里的数组里
if (dist_base)
*__this_cpu_ptr(gic_data[gic_nr].dist_base) = dist_base;
if (cpu_base)
*__this_cpu_ptr(gic_data[gic_nr].cpu_base) = cpu_base;
//CPUX的PPI SGI中断初始化,参见前面小节,不赘述
gic_cpu_init(&gic_data[gic_nr]);
}

你可能感兴趣的:(android,linux内核,arm-linux,smp,exynos4)