void __init gic_dist_config(void __iomem *base, int gic_irqs, void (*sync_access)(void))程序的注释已经非常清楚了,这里就不细述了。需要注意的是:这里设定的都是缺省值,实际上,在各种driver的初始化过程中,还是有可能改动这些设置的(例如触发方式)。
{
unsigned int i;
/* Set all global interrupts to be level triggered, active low. */
for (i = 32; i < gic_irqs; i += 16)
writel_relaxed(0, base + GIC_DIST_CONFIG + i / 4);
/* Set priority on all global interrupts. */
for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i);
/* Disable all interrupts. Leave the PPI and SGIs alone as they are enabled by redistributor registers. */
for (i = 32; i < gic_irqs; i += 32)
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i / 8);
if (sync_access)
sync_access();
}
static void gic_cpu_init(struct gic_chip_data *gic)(a)系统软件实际上是使用CPU 逻辑ID这个概念的,通过smp_processor_id可以获得本CPU的逻辑ID。gic_cpu_map这个全部lookup table就是用CPU 逻辑ID作为所以,去寻找其cpu mask,后续通过cpu mask值来控制中断是否送达该CPU。在gic_init_bases函数中,我们将该lookup table中的值都初始化为0xff,也就是说不进行mask,送达所有的CPU。这里,我们会进行重新修正。
{
void __iomem *dist_base = gic_data_dist_base(gic);-------Distributor的基地址空间
void __iomem *base = gic_data_cpu_base(gic);-------CPU interface的基地址空间
unsigned int cpu_mask, cpu = smp_processor_id();------获取CPU的逻辑ID
int i;
cpu_mask = gic_get_cpumask(gic);-------------(a)
gic_cpu_map[cpu] = cpu_mask;
for (i = 0; i < NR_GIC_CPU_IF; i++)
if (i != cpu)
gic_cpu_map &= ~cpu_mask; ------------(b)
gic_cpu_config(dist_base, NULL); --------------(c)
writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);-------(d)
writel_relaxed(1, base + GIC_CPU_CTRL);-----------(e)
}
void gic_cpu_config(void __iomem *base, void (*sync_access)(void))程序的注释已经非常清楚了,这里就不细述了。
{
int i;
/* Deal with the banked PPI and SGI interrupts - disable all
* PPI interrupts, ensure all SGI interrupts are enabled. */
writel_relaxed(0xffff0000, base + GIC_DIST_ENABLE_CLEAR);
writel_relaxed(0x0000ffff, base + GIC_DIST_ENABLE_SET);
/* Set priority on PPI and SGI interrupts */
for (i = 0; i < 32; i += 4)
writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
if (sync_access)
sync_access();
}
static void __init gic_pm_init(struct gic_chip_data *gic)这段代码前面主要是分配两个per cpu的内存。这些内存在系统进入sleep状态的时候保存PPI的寄存器状态信息,在resume的时候,写回寄存器。对于root GIC,需要注册一个和电源管理的事件通知callback函数。不得不吐槽一下gic_notifier_block和gic_notifier这两个符号的命名,看不出来和电源管理有任何关系。更优雅的名字应该包括pm这样的符号,以便让其他工程师看到名字就立刻知道是和电源管理相关的。
{
gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, sizeof(u32));
gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4, sizeof(u32));
if (gic == &gic_data[0])
cpu_pm_register_notifier(&gic_notifier_block);
}
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)(a)SGI或者PPI和SPI最大的不同是per cpu的,SPI是所有CPU共享的,因此需要分配per cpu的内存,设定一些per cpu的flag。
{
if (hw < 32) {------------------SGI或者PPI
irq_set_percpu_devid(irq);--------------------------(a)
irq_set_chip_and_handler(irq, &gic_chip, handle_percpu_devid_irq);-------(b)
set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);--------------(c)
} else {
irq_set_chip_and_handler(irq, &gic_chip, handle_fasteoi_irq);----------(d)
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
gic_routable_irq_domain_ops->map(d, irq, hw);----------------(e)
}
irq_set_chip_data(irq, d->host_data);-----设定irq chip的私有数据
return 0;
}
static int gic_irq_domain_xlate(struct irq_domain *d,(a)根据gic binding文档的描述,其interrupt specifier包括3个cell,分别是interrupt type(0 表示SPI,1表示PPI),interrupt number(对于PPI,范围是[0-15],对于SPI,范围是[0-987]),interrupt flag(触发方式)。GIC interrupt specifier中的interrupt number需要加上16(也就是加上SGI的那些ID号),才能转换成GIC的HW interrupt ID。
struct device_node *controller,
const u32 *intspec, unsigned int intsize,--------输入参数
unsigned long *out_hwirq, unsigned int *out_type)----输出参数
{
unsigned long ret = 0;
*out_hwirq = intspec[1] + 16; ---------------------(a)
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; -----------(b)
return ret;
}
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) | Powered by Discuz! 7.0.0 |