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。 |