(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模块的内容.
(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
http://www.wowotech.net/linux_kenrel/gic_driver.html
https://www.cnblogs.com/arnoldlu/p/8659981.html
