深度解析Linux内核—中断

中断

中断是外围设备通知处理器的一种机制。

1. 中断控制器

外围设备不是把中断请求直接发送给处理器,而是发给中断控制器,由中断控制器转发给处理器。

不同种类的中断控制器的访问方法存在差异,为了屏蔽差异,内核定义了中断控制器描述符irq_chip,每种中断控制器自定义各种操作函数。GIC v2控制器的描述符如下:

drivers/irqchip/irq-gic.c
 
tatic const struct irq_chip gic_chip = {
	.irq_mask		= gic_mask_irq,
	.irq_unmask		= gic_unmask_irq,
	.irq_eoi		= gic_eoi_irq,
	.irq_set_type		= gic_set_type,
	.irq_get_irqchip_state	= gic_irq_get_irqchip_state,
	.irq_set_irqchip_state	= gic_irq_set_irqchip_state,
	.flags			= IRQCHIP_SET_TYPE_MASKED |
				  IRQCHIP_SKIP_SET_WAKE |
				  IRQCHIP_MASK_ON_SUSPEND,
};

2. 中断域

一个大型系统可能有多个中断控制器,这些中断控制器可以级联,一个中断控制器作为中断源连接到另一个中断控制器,但只有一个中断控制器作为根控制器直接连接到处理器。为了把每个中断控制器本地的硬件中断映射到全局唯一的Linux中断号(也称为虚拟中断),内核定义了中断域irq_domain,每个中断控制器由自己的中断域。

2.1. 创建中断域

中断控制器的驱动程序使用分配函数irq_domain_add_*()创建和注册中断域。

2.2. 创建映射

创建中断域以后,需要向中断域添加硬件中断号到Linux中断号的映射,内核提供了函数irq_create_mapping:

unsigned int irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq);

输入参数是中断域和硬件中断号,返回Linux中断号。

该函数首先分配Linux中断号,然后把硬件中断号到Linux中断号的映射添加到中断域。

2.3. 查找映射

中断处理程序需要根据硬件中断号查找Linux中断号,内核提供了函数irq_find_mapping:

unsigned int irq_find_mapping(struct irq_domain *host, irq_hw_number_t hwirq);

输入参数是中断域和硬件中断号,返回Linux中断号。

【文章福利】小编推荐自己的Linux内核技术交流群:【977878001】整理一些个人觉得比较好得学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100进群领取,额外赠送一份价值699的内核资料包(含视频教程、电子书、实战项目及代码)

内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

3. 中断控制器驱动初始化

3.1. 设备树源文件

ARM64架构使用扁平设备树(Flattened Device Tree,FDT)描述板卡的硬件信息,好处是可以把板卡的特定的代码从内核中删除,编译生成通用的板卡无关的内核。

设备树源文件是文本文件,扩展名是“.dts”,需要在设备树源文件中描述中断的相关信息:

(1)中断控制器的信息

(2)对于作为中断源的外围设备,需要描述设备连接到哪个中断控制器,使用哪个硬件中断号

3.2. 中断控制器匹配表

在GIC v2控制器的驱动程序中,定义了多个类型为of_device_id的静态变量,成员compatible是驱动程序支持的设备的名称,成员data是初始化函数,编译器把这些静态变量放在专用的节“__irqchip_of_table”里面。

我们把节“__irqchip_of_table”称为中断控制器匹配表,里面每个表项的格式是结构体of_device_id。

drivers/irqchip/irq-gic.c
 
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展开以后是:

static const struct of_device_id __of_table_cortex_gic_400
	__section(__irqchip_of_table)
		= { .compatible = "arm,gic-g400",	
		    .data = gic_of_init  }
...
static const struct of_device_id __of_table_cortex_a15_gic
	__section(__irqchip_of_table)
		= { .compatible = "arm,cortex-a15-gic",	
		    .data = gic_of_init  }
static const struct of_device_id __of_table_cortex_a9_gic
	__section(__irqchip_of_table)
		= { .compatible = "arm,cortex-a9-gic",	
		    .data = gic_of_init  }
...

3.3. 初始化

在内核初始化的时候,匹配设备树文件中的中断控制器的属性“compatible”和内核的中断控制器匹配表,找到合适的中断控制器驱动程序,执行驱动程序的初始化函数。

start_kernel()  ->  init_IRQ()  ->  irqchip_init()
 
drivers/irqchip/irqchip.c
void __init irqchip_init(void)
{
	of_irq_init(__irqchip_of_table);    // 参数是中断控制器匹配表的起始地址__irqchip_of_table
	...
}

(1)函数of_irq_init

driver/of/irq.c
/**
 * of_irq_init - Scan and init matching interrupt controllers in DT
 * @matches: 0 terminated array of nodes to match and init function to call
 *
 * This function scans the device tree for matching interrupt controller nodes,
 * and calls their initialization functions in order with parents first.
 */
void __init of_irq_init(const struct of_device_id *matches)
{
	const struct of_device_id *match;
	struct device_node *np, *parent = NULL;
	struct of_intc_desc *desc, *temp_desc;
	struct list_head intc_desc_list, intc_parent_list;
 
	INIT_LIST_HEAD(&intc_desc_list);
	INIT_LIST_HEAD(&intc_parent_list);
 
	for_each_matching_node_and_match(np, matches, &match) {		/* 遍历设备树文件的设备节点。如果属性compatible和中断控制器匹配表中的任何一条表项的字段compatible匹配,处理如下 */
		if (!of_property_read_bool(np, "interrupt-controller") ||
				!of_device_is_available(np))		/* 如果没有节点属性interrupt-controller,说明设备不是中断控制器,忽略该设备 */
			continue;
 
		if (WARN(!match->data, "of_irq_init: no init function for %s\n",
			 match->compatible))
			continue;
 
		/*
		 * Here, we allocate and populate an of_intc_desc with the node
		 * pointer, interrupt-parent device_node etc.
		 */
		desc = kzalloc(sizeof(*desc), GFP_KERNEL);		/* 分配一个of_intc_desc实例 */
		if (WARN_ON(!desc)) {
			of_node_put(np);
			goto err;
		}
 
		desc->irq_init_cb = match->data;		/* 成员irq_init_cb保存初始化函数 */
		desc->dev = of_node_get(np);		/* 成员dev保存本设备的device_node */
		desc->interrupt_parent = of_irq_find_parent(np);		/* 成员inte
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值