Linux 中断 —— GIC 初始化
GIC-V2在 Linux 层的初始化分析1、GIC 的 device node和GIC irq chip driver的匹配过程(1)irq chip driver中的声明在drivers\irqchip\irqchip.h文件中定义了 IRQCHIP_DECLARE :#define IRQCHIP_DECLARE(name, compat, fn) OF_DECL...
GIC-V2 在 Linux 层的初始化分析
1、GIC 的 device node和GIC irq chip driver的匹配过程
(1)irq chip driver中的声明
在 drivers\irqchip\irqchip.h文件中定义了 IRQCHIP_DECLARE :
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
#define OF_DECLARE_2(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_2)
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__used __section(__##table##_of_table) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
这个宏其实就是初始化了一个struct of_device_id的静态常量,并放置在__irqchip_of_table section中。irq-gic.c 文件中使用IRQCHIP_DECLARE来定义了若干个静态的struct of_device_id常量,如下
IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);
IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
兼容GIC-V2的GIC实现有很多,不过其初始化函数都是一个。在linux kernel编译的时候,你可以配置多个irq chip进入内核,编译系统会把所有的IRQCHIP_DECLARE宏定义的数据放入到一个特殊的section中(section name是__irqchip_of_table),我们称这个特殊的section叫做irq chip table。这个table也就保存了kernel支持的所有的中断控制器的ID信息(最重要的是驱动代码初始化函数和DT compatible string)。我们来看看struct of_device_id的定义
struct of_device_id
{
char name[32];------要匹配的device node的名字
char type[32];-------要匹配的device node的类型
char compatible[128];---匹配字符串(DT compatible string),用来匹配适合的 device node
const void *data;--------对于GIC,这里是初始化函数指针
};
这个数据结构主要被用来进行Device node和driver模块进行匹配用的。从该数据结构的定义可以看出,在匹配过程中,device name、device type和DT compatible string都是考虑的因素。更细节的内容请参考__of_device_is_compatible函数。
(2)device node
不同的GIC-V2的实现总会有一些不同,这些信息可以通过Device tree的机制来传递。Device node中定义了各种属性,其中就包括了memory资源,IRQ描述等信息,这些信息需要在初始化的时候传递给具体的驱动,因此需要一个Device node和driver模块的匹配过程。在Device Tree模块中会包括系统中所有的device node,如果我们的系统使用了GIC-400,那么系统的device node数据库中会有一个node是GIC-400的,一个示例性的GIC-400的 device node:
gic: interrupt-controller@ffc01000 {
compatible = "arm,gic-400";
interrupt-controller;
#interrupt-cells = <3>;
#address-cells = <0>;
reg = <0xffc01000 0x1000="">,----Distributor address range
<0xffc02000 0x1000="">,-----CPU interface address range
<0xffc04000 0x2000="">,-----Virtual interface control block
<0xffc06000 0x2000="">;-----Virtual CPU interfaces
interrupts = ;
};
(3)device node和irq chip driver的匹配
在machine driver初始化的时候会调用irqchip_init函数进行irq chip driver的初始化。在driver/irqchip/irqchip.c文件中定义了irqchip_init函数,如下:
void __init irqchip_init(void)
{
of_irq_init(__irqchip_begin);
}
__irqchip_begin就是内核irq chip table的首地址,这个table也就保存了kernel支持的所有的中断控制器的ID信息(用于和device node的匹配)。of_irq_init函数执行之前,系统已经完成了device tree的初始化,因此系统中的所有的设备节点都已经形成了一个树状结构,每个节点代表一个设备的device node。of_irq_init是在所有的device node中寻找中断控制器节点,形成树状结构(系统可以有多个interrupt controller,之所以形成中断控制器的树状结构,是为了让系统中所有的中断控制器驱动按照一定的顺序进行初始化)。之后,从root interrupt controller节点开始,对于每一个interrupt controller的device node,扫描irq chip table,进行匹配,一旦匹配到,就调用该interrupt controller的初始化函数,并把该中断控制器的device node以及parent中断控制器的device node作为参数传递给irq chip driver。具体的匹配过程的代码属于Device Tree模块的内容.
2、GIC driver 初始化代码分析
(1)gic_of_init 的代码如下:
int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
void __iomem *cpu_base;
void __iomem *dist_base;
u32 percpu_offset;
int irq;
dist_base = of_iomap(node, 0);----------------映射GIC Distributor的寄存器地址空间
cpu_base = of_iomap(node, 1);----------------映射GIC CPU interface的寄存器地址空间
if (of_property_read_u32(node, "cpu-offset", &percpu_offset))--------处理cpu-offset属性。
percpu_offset = 0;
gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);))-----主处理过程,后面详述
if (!gic_cnt)
gic_init_physaddr(node); -----对于不支持big.LITTLE switcher(CONFIG_BL_SWITCHER)的系统,该函数为空。
if (parent) {---------------------------------处理interrupt级联
irq = irq_of_parse_and_map(node, 0); -----解析second GIC的interrupts属性,并进行mapping,返回IRQ number
gic_cascade_irq(gic_cnt, irq);
}
gic_cnt++;
return 0;
}
(2)gic_init_bases 的代码如下:
void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset, struct device_node *node)
{
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
int gic_irqs, irq_base, i;
gic = &gic_data[gic_nr];
gic->dist_base.common_base = dist_base;
gic->cpu_base.common_base = cpu_base;
gic_set_base_accessor(gic, gic_get_common_base);
for (i = 0; i < NR_GIC_CPU_IF; i++)
gic_cpu_map[i] = 0xff;
if (gic_nr == 0 && (irq_start & 31) > 0) {
hwirq_base = 16;
if (irq_start != -1)
irq_start = (irq_start & ~31) + 16;
} else {
hwirq_base = 32;
}
gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f; ----(a)
gic_irqs = (gic_irqs + 1) * 32;
if (gic_irqs > 1020)
gic_irqs = 1020;
gic->gic_irqs = gic_irqs;
gic_irqs -= hwirq_base;
if (of_property_read_u32(node, "arm,routable-irqs",
&nr_routable_irqs)) {
irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
if (IS_ERR_VALUE(irq_base)) {
WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
irq_start);
irq_base = irq_start;
}
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, -------(b)
hwirq_base, &gic_irq_domain_ops, gic);
} else {
gic->domain = irq_domain_add_linear(node, nr_routable_irqs, --------(b)
&gic_irq_domain_ops,
gic);
}
if (gic_nr == 0) { ---只对root GIC操作,因为设定callback、注册Notifier只需要一次就OK了
#ifdef CONFIG_SMP
set_smp_cross_call(gic_raise_softirq);
register_cpu_notifier(&gic_cpu_notifier);
#endif
set_handle_irq(gic_handle_irq); ---(c)
}
gic_chip.flags |= gic_arch_extn.flags;
gic_dist_init(gic);--------- GIC Distributer 部分初始化
gic_cpu_init(gic); --------- GIC CPU Interface 部分初始化
gic_pm_init(gic); --------- GIC PM 部分初始化
}
(a). 读取GIC 的最大支持的中断数目,从 GIC_DIST_CTR 寄存器(这是V1版本的寄存器名字,V2中是GICD_TYPER,Interrupt Controller Type Register,)的低五位ITLinesNumber获取的。如果ITLinesNumber等于N,那么最大支持的中断数目是32(N+1)。此外,GIC规范规定最大的中断数目不能超过1020,1020-1023是有特别用户的interrupt ID。
(b). 分配一个 irq_domain 的结构,一个 irq_domain 代表了一个 GIC 控制器
(c). 设置中断响应函数入口为:gic_handle_irq
(3). gic_init_bases->irq_domain_add_linear()->__irq_domain_add()
struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
GFP_KERNEL, of_node_to_nid(of_node));-------------domain大小为struct irq_domain加上gic_irqs个unsigned int。
if (WARN_ON(!domain))
return NULL;
/* Fill structure */
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
domain->ops = ops;
domain->host_data = host_data;
domain->of_node = of_node_get(of_node);
domain->hwirq_max = hwirq_max;
domain->revmap_size = size;
domain->revmap_direct_max_irq = direct_max;
irq_domain_check_hierarchy(domain);
mutex_lock(&irq_domain_mutex);
list_add(&domain->link, &irq_domain_list);----------------------将创建好的struct irq_domain加入全局链表irq_domain_list。
mutex_unlock(&irq_domain_mutex);
pr_debug("Added domain %s\n", domain->name);
return domain;
}
这里分配了一个 irq_domain 数据结构来表示 GIC,并继续进行一些结构的赋值,这里关系一下 ops 和 host_data 数据。
static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
.unmap = gic_irq_domain_unmap,
.xlate = gic_irq_domain_xlate,
};
host-data 传入的是 gic_chip_data 是:
struct gic_chip_data *gic;
int gic_irqs, irq_base, i;
gic = &gic_data[gic_nr];
irq_domain_add_linear(node, nr_routable_irqs,
&gic_irq_domain_ops,
gic);
最后把这个 irq domain 加入到了 irq domain list 链表。
在 gic_init_bases 函数最后,完成了 GIC 的 硬件初始化:
gic_dist_init(gic);--------- GIC Distributer 部分初始化
gic_cpu_init(gic); --------- GIC CPU Interface 部分初始化
gic_pm_init(gic); --------- GIC PM 部分初始化
总的来说:
参考文档:
http://www.wowotech.net/linux_kenrel/irq-domain.html
更多推荐
所有评论(0)