中断API之irq_set_irq_type

本文详细解析了irq_set_irq_type函数的实现原理及其在Linux内核中的使用方式。该函数用于设置中断触发类型,例如低电平触发,并介绍了如何通过中断描述符设置触发类型的具体流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

int irq_set_irq_type(unsigned int irq, unsigned int type)用于设置irq对应的中断
的触发类型.
其使用的例程如下:
static void __init meta_intc_init_cpu(struct meta_intc_priv *priv, int cpu)
{
	unsigned int thread = cpu_2_hwthread_id[cpu];
	unsigned int signum = TBID_SIGNUM_TR2(thread);
	int irq = tbisig_map(signum);

	/* Register the multiplexed IRQ handler */
	irq_set_chained_handler(irq, meta_intc_irq_demux);
	irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
}
这里就通过调用irq_set_irq_type 将中断类型设置为低电平触发.
其源码分析如下:
int irq_set_irq_type(unsigned int irq, unsigned int type)
{
	unsigned long flags;
	#通过irq number得到中断描述符,得到并操作中断描述符时需要通过irq_get_desc_buslock和irq_put_desc_busunlock的保护.
	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
	int ret = 0;
	#中断描述符为null,则退出
	if (!desc)
		return -EINVAL;
	#通过中断描述符设置中断触发的类型.
	ret = __irq_set_trigger(desc, type);
	irq_put_desc_busunlock(desc, flags);
	return ret;
}

int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
{
	struct irq_chip *chip = desc->irq_data.chip;
	int ret, unmask = 0;
	#中断描述符中的chip 或者chip对应的irq_set_type 为null,则退出
	#因为这里最终会调用chip->irq_set_type 来设置中断触发类型
	if (!chip || !chip->irq_set_type) {
		/*
		 * IRQF_TRIGGER_* but the PIC does not support multiple
		 * flow-types?
		 */
		pr_debug("No set_type function for IRQ %d (%s)\n",
			 irq_desc_get_irq(desc),
			 chip ? (chip->name ? : "unknown") : "unknown");
		return 0;
	}

	if (chip->flags & IRQCHIP_SET_TYPE_MASKED) {
		if (!irqd_irq_masked(&desc->irq_data))
			mask_irq(desc);
		if (!irqd_irq_disabled(&desc->irq_data))
			unmask = 1;
	}

	/* Mask all flags except trigger mode */
	flags &= IRQ_TYPE_SENSE_MASK;
	#核心代码设置irq的中断触发类型
	ret = chip->irq_set_type(&desc->irq_data, flags);
	
	switch (ret) {
	#注意这个case 没有break 语句,因此返回值IRQ_SET_MASK_OK/IRQ_SET_MASK_OK_DONE会比IRQ_SET_MASK_OK_NOCOPY 多调用两个函数.
	case IRQ_SET_MASK_OK:
	case IRQ_SET_MASK_OK_DONE:
		irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK);
		irqd_set(&desc->irq_data, flags);

	case IRQ_SET_MASK_OK_NOCOPY:
		flags = irqd_get_trigger_type(&desc->irq_data);
		irq_settings_set_trigger_mask(desc, flags);
		irqd_clear(&desc->irq_data, IRQD_LEVEL);
		irq_settings_clr_level(desc);
		if (flags & IRQ_TYPE_LEVEL_MASK) {
			irq_settings_set_level(desc);
			irqd_set(&desc->irq_data, IRQD_LEVEL);
		}

		ret = 0;
		break;
	default:
		pr_err("Setting trigger mode %lu for irq %u failed (%pF)\n",
		       flags, irq_desc_get_irq(desc), chip->irq_set_type);
	}
	if (unmask)
		unmask_irq(desc);
	return ret;
}

<think>好的,我现在需要回答用户关于GPIO中断处理中两个函数gpiochip_irqchip_add_nested和irq_domain_add_legacy的区别的问题。首先,我得回忆一下这两个函数的作用和它们在Linux内核中的使用场景。 首先,用户提到这两个函数都是用来设置IRQ中断域的,但具体区别是什么。我记得在Linux内核中,GPIO控制器通常使用irqchip来处理中断,而这两个函数可能属于不同的层次或者用途。 让我先想一下gpiochip_irqchip_add_nested。这个函数应该是用于将GPIO控制器的中断控制器(irqchip)添加到系统中,特别是当这个GPIO控制器是一个嵌套的中断控制器时。嵌套的中断控制器意味着它位于另一个中断控制器的下游,比如级联的中断。这种情况下,中断处理可能需要特殊的处理,比如线程化处理或者嵌套的锁机制。这时候,使用nested类型的irqchip可以确保中断处理程序正确执行,避免死锁等问题。 然后是irq_domain_add_legacy。这个函数属于irqdomain的API,用于创建传统的中断域。传统的中断映射通常是指固定、静态的中断号分配,比如在设备树或ACPI表中已经定义好的中断号。当硬件的中断线是静态配置且不需要动态分配时,可以使用legacy方式。例如,一些老旧的硬件或者某些需要保持向后兼容性的情况会用到这种方法。 接下来,我需要明确它们的区别。首先,应用场景不同。gpiochip_irqchip_add_nested用于GPIO控制器作为嵌套的中断控制器的情况,而irq_domain_add_legacy则是用于传统的中断映射方式。其次,结构层次不同:gpiochip的函数属于GPIO子系统,而irq_domain属于中断子系统更底层的部分。再者,用途不同:nested处理的是中断控制器的层次结构,而legacy处理的是中断号的静态映射。 可能还需要考虑它们的参数和使用方式。gpiochip_irqchip_add_nested需要关联到具体的gpio_chip结构,设置父中断控制器,处理函数等,而irq_domain_add_legacy需要指定中断号的范围、操作函数以及设备节点等信息。 另外,错误处理方面可能也有区别。嵌套的中断控制器需要确保父控制器的正确设置,而legacy域则可能涉及更多的静态配置,比如固定的中断号映射,不需要动态分配。 需要验证一下是否这两个函数在代码中的调用位置和参数确实如我所想。例如,gpiochip的函数可能在GPIO驱动初始化时被调用,而irq_domain的函数可能在更早的中断控制器注册阶段使用。同时,是否legacy方法已经被逐渐淘汰,或者在某些特定硬件中仍然必需。 总结来说,主要区别在于应用场景、所属子系统和中断处理机制的不同。一个是处理嵌套中断的GPIO控制器注册,另一个是创建传统静态中断映射的域。可能需要进一步查阅内核代码或文档来确认这些细节,但根据现有的知识,这样的区分是合理的。</think>在Linux内核中断子系统中,`gpiochip_irqchip_add_nested`和`irq_domain_add_legacy`虽然都涉及中断域设置,但设计目标和应用场景有本质区别: --- ### 一、定位层级不同 1. **gpiochip_irqchip_add_nested** - **所属子系统**:GPIO子系统 - **作用**:将GPIO控制器注册为**嵌套中断控制器**(Nested IRQ Chip),通常用于GPIO引脚级联到父中断控制器(如GIC)的场景。 - **典型场景**:GPIO引脚作为中断源,通过父中断控制器(如`irq-gic`)传递中断信号。 - **关键特征**: - 需要显式指定父中断控制器(`parent_handler`) - 支持嵌套中断处理(避免中断重入导致的死锁) - 通过`gpiochip_set_nested_irqchip()`启用嵌套模式 2. **irq_domain_add_legacy** - **所属子系统**:通用中断子系统(Generic IRQ) - **作用**:创建**传统静态映射**的中断域,用于固定硬件中断号的旧设备。 - **典型场景**:硬件中断号与Linux虚拟中断号(`virq`)是1:1固定映射(如x86 PIC)。 - **关键特征**: - 不涉及硬件级联关系 - 中断号预定义,不可动态分配 - 常用于兼容旧驱动程序或固定中断拓扑的硬件 --- ### 二、中断处理流程差异 1. **gpiochip_irqchip_add_nested** - 中断触发流程: ```text HW中断 → 父中断控制器 → GPIO子控制器 → 具体GPIO引脚的中断处理 ``` - 需要实现`irq_chip`操作(如`.irq_mask`, `.irq_unmask`) - 通过`gpiochip_to_irq()`将GPIO编号转换为虚拟中断号 2. **irq_domain_add_legacy** - 中断触发流程: ```text HW中断 → 直接映射到固定virq → 调用对应中断处理函数 ``` - 不需要级联关系,中断号由`irq_domain`静态分配 --- ### 三、代码结构对比 #### 1. `gpiochip_irqchip_add_nested` 示例 ```c struct gpio_chip *gc; struct irq_chip *irqchip; // 初始化GPIO控制器的irqchip gpiochip_irqchip_add_nested(gc, irqchip, 0, handle_edge_irq, IRQ_TYPE_NONE); // 设置嵌套模式 gpiochip_set_nested_irqchip(gc, irqchip, parent_irq); // 为每个GPIO引脚设置中断 for (i = 0; i < gc->ngpio; i++) irq_set_chip_data(virq + i, gpiochip_get_data(gc)); ``` #### 2. `irq_domain_add_legacy` 示例 ```c struct device_node *np; struct irq_domain *domain; // 创建传统中断域 domain = irq_domain_add_legacy(np, NUM_IRQS, 0, 0, &irq_domain_simple_ops, NULL); // 直接通过固定偏移获取virq int virq = irq_create_mapping(domain, hwirq); ``` --- ### 四、选择依据 | **场景** | **推荐函数** | |---------------------------|------------------------------| | GPIO引脚作为级联中断源 | `gpiochip_irqchip_add_nested` | | 旧设备固定中断号映射 | `irq_domain_add_legacy` | --- ### 五、总结 - **`gpiochip_irqchip_add_nested`**:面向GPIO控制器作为子中断控制器的级联场景,强调嵌套安全和父子中断协作。 - **`irq_domain_add_legacy`**:面向传统静态中断映射,适用于无需动态分配中断号的旧硬件。 两者在中断拓扑中的位置和作用层级完全不同,需根据硬件中断设计选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值