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

http://www.wowotech.net/linux_kenrel/gic_driver.html

https://www.cnblogs.com/arnoldlu/p/8659981.html

Linux 中断 —— GIC 初始化相关推荐

  1. Linux 中断管理之ARM GIC V3 初始化

    1.ARM GIC V3中断控制器介绍 GIC(Generic Interrupt Controller)是一个通用的中断控制器,用来接收硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理.G ...

  2. Linux中断子系统(二)中断控制器GIC驱动分析

    Linux中断子系统(二)中断控制器GIC驱动分析 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一 ...

  3. Linux中断子系统(三)之GIC中断处理过程

    Linux中断子系统(三)之GIC中断处理过程 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一)中 ...

  4. Linux中断一网打尽(1) — 中断及其初始化

    女主宣言 通过本文您可以了解到:Linux 中断是什么,如何分类,能干什么?Linux 中断在计算机启动各阶段是如何初始化的? PS:丰富的一线技术.多元化的表现形式,尽在"360云计算&q ...

  5. Linux中断子系统(一)中断控制器GIC架构

    Linux中断子系统(一)中断控制器GIC架构 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一)中 ...

  6. linux中断初始化程序,Linux x86_64 内核中断初始化

    Linux x86_64 内核中断初始化 中断分类 Linux 系统中, 中断分为: 硬中断: 由外部设备或者执行异常产生的需要快速处理的中断如缺页中断定时器硬件中断 根据内部产生还是外部产生, 分为 ...

  7. Linux 中断 —— ARM GIC 中断控制器

    GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1-V4(V2最多支持 ...

  8. Linux中断(interrupt)子系统之一:中断系统基本原理【转】

    转自:http://blog.csdn.net/droidphone/article/details/7445825 这个中断系列文章主要针对移动设备中的Linux进行讨论,文中的例子基本都是基于AR ...

  9. arm Linux 中断管理机制

    关键词:GIC.IAR.EOI.SGI/PPI/SPI.中断映射.中断异常向量.中断上下文.内核中断线程.中断注册. 1.1 ARM支持中断类型 ARM GIC-v2支持三种类型的中断: SGI:软件 ...

最新文章

  1. Java Swing:JPanel添加边框
  2. python3 getopt用法
  3. python学习-代码调试(通过print调试、通过pdb调试、通过编译器调试(断点调试))
  4. HTML5 —— 属性
  5. 斗地主Java课程设计_Java课程设计---web版斗地主
  6. oracle创建哈希索引,ORACLE10g新特性——全局HASH分区索引
  7. iOS自动化测试---使用的相关工具
  8. vs2013连接access2016
  9. 斗地主牌型基本算法升级版本
  10. 华为内部转岗最好时间_华为博士类员工离职率21.8%:平均年薪110万,依然度日如年...
  11. 有限元:什么是有限元分析法
  12. JSON的生成和解析
  13. 如何用单片机控制可控硅c语言程序,可控硅的斩波控制的实现--C语言源代码
  14. FireStart教程:基于SharePoint的出差报销流程二
  15. mysql 增加时间字段
  16. 硬纪元干货|视+AR联合创始人涂意:AR将是下一代计算平台
  17. 前端跨域问题—解决Firefox浏览器显示“已阻止载入混合活动内容”的方法
  18. LTV预估与留存曲线拟合:指数函数还是幂函数?
  19. 在Go中构建区块链 第5部分:地址
  20. 项目经理修炼手册 2.1.1 程序员的基本功

热门文章

  1. 戴尔服务器r510怎么系统,DELLR510服务器上安系统.docx
  2. 一起来探索从双击程序到开始运行期间计算机经历了哪些过程
  3. 《算法新解》作者刘新宇:我只是想打开那些黑盒子,告诉人们里面有什么。
  4. CAD二次开发合并所有能合并的线
  5. MacOS 开发 — App多开
  6. docker镜像加速,docker更换为国内镜像
  7. python爬虫实战---爬取大众点评评论
  8. vue3 provide inject用法
  9. 【离散数学】编程练习:求关系的传递闭包
  10. java项目pm_项目中PO、PM的职责区分