在设备树中指定中断_在代码中获得中断

设备树里中断节点的语法

参考文档:内核Documentation\devicetree\bindings\interrupt-controller\interrupts.txt

1. 设备树里的中断控制器

中断硬件框图如下:
在这里插入图片描述

需要三个属性来描述

  1. compatible
  2. interrupt-controller
  3. interrupt-cell
    在硬件上,中断控制器只有 GIC 这一个,但是我们在软件上也可以把上图中的 GPIO 称为中断控制器。很多芯片有多个GPIO 模块,比如GPIO1、GPIO2等等。所以软件上的中断控制器 就有很多个:GICGPIO1GPIO2 等等。

GPIO1 连接到GIC,GPIO2连接到GIC,所以GPIO1的父亲是GIC,GPIO2的父亲是GIC。

假设GPIO1有32 个中断源,但是它把其中的16个汇聚起来向GIC发出一个中断,把另外16个汇聚起来向GIC发出另一个中断。这就意味着GPIO1会用到GIC的两个中断,会涉及GIC里的 2 个hwirq。

这些层级关系、中断号(hwirq),都会在设备树中有所体现。

在设备树中,中断控制器节点中必须有一个属性:interrupt-controller,表明它是“中断控制器”。还必须有一个属性: #interrupt-cells,表明引用这个中断控制器的话需要多少个 cell。

#interrupt-cells 的值一般有如下的取值

  • #interrupt-cells = <1>
    别的节点要使用这个中断控制器时,只需要一个cell 来表明使用哪一个中断。
  • #interrupt-cells = <2>

别的节点要使用这个中断控制器时,需要一个 cell 来表明使用“哪一个中断”;还需要另一个 cell 来描述中断,一般是表明触发类型:

第 2 个 cell 的 bits[3:0] 用来表示中断触发类型(trigger type and level flags):
1 = low-to-high edge triggered,上升沿触发
2 = high-to-low edge triggered,下降沿触发
4 = active high level-sensitive,高电平触发
8 = active low level-sensitive,低电平触发

示例如下:

vic: intc@10140000 {
    compatible = "arm,versatile-vic";
    interrupt-controller;
    #interrupt-cells = <1>;
    reg = <0x10140000 0x1000>;
};

如果中断控制器有级联关系,下级的中断控制器还需要表明它的“interrupt-parent”是谁,用了interrupt-parent ” 中的哪一个“ interrupts”。

2 设备树里使用中断

一个外设,它的中断信号接到哪个“中断控制器”的哪个“中断引脚”,这个中断的触发方式是怎样的?这3 个问题,在设备树里使用中断时,都要有所体现。

  • interrupt-parent=<&xxxx>
    你要用哪一个中断器里的中断?
  • interrupts
    你要用哪一个中断?interrupts 里要用几个cell,由interrupt-parent 对应的中断控制器决定。在中断控制器里有 #interrupt-cells 属性,它指明了要用几个 cell 来描述中断
i2c@7000c000 {
    gpioext: gpio-adnp@41 {
        compatible = "ad,gpio-adnp";
 
        interrupt-parent = <&gpio>;
        interrupts = <160 1>;
 
        gpio-controller;
        #gpio-cells = <1>;
 
        interrupt-controller;
        #interrupt-cells = <2>;
    };
......
};
  • 新写法:interrupts-extended
    一个 interrupts-extended 属性就可以既指定 interrupt-parent ,也指定 interrupt-parent ,也指定 interrupts ,比如

interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;

&intc1和&intc2:这些是中断控制器的引用,它们是设备树中定义的节点,负责管理中断请求。
5和1:这些是中断号,它们是中断控制器分配给特定设备的中断请求号。
1和0:这些是中断的触发类型。

设备树里中断节点的示例

以100ASK_IMX6ULL开发板为例,在 arch/arm/boot/dts 目录下可以看到 2 个文件: imx6ull.dtsi、 100ask_imx6ull-14x14.dts,把里面有关中断的部分内容抽取出来。

在这里插入图片描述
从设备树反推 IMX6ULL 的中断体系,如下,比之前的框图多了一个“ GPC INTC”:
在这里插入图片描述
GPC INTC 的英文是:General Power Controller, interrupt Controller 。它提供中断屏蔽,中断状态查询功能,实际上这些功能在 GIC 里也实现了,个人觉得有点多余。除此之外,它还提供唤醒功能,这才是保留它的原因。

在代码中获得中断

之前提到过,设备树中的节点有些能被转换为内核里的 platform_device,有些不能,回顾如下:

  1. 根节点下含有compatile属性的子节点,会转换为 platform_device
  2. 含有特定 compatile 属性的节点的子节点,会转换为 platform_device,如果一个节点的compatile 属性,它的值是这 4 者之一: “simplebus”,“simple-mfd”,“isa”,“arm,amba-bus”,那么它的子结点(需含 compatile 属性)也可以转换为 platform_device。
  3. 总线 I2C、 SPI 节点下的子节点: 不转换为 platform_device,某个总线下到子节点, 应该交给对应的总线驱动程序来处理, 它们不应该被转换为 platform_device。

1.对于platform_device

一个节点能被转换为 platform_device ,如果它的设备树里指定了中断属性,那么可以从 platform_device 中获得中断资源,函数如下,可以使用下列函数获得 IORESOURCE_IRQ 资源,即中断号:

/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type // 取哪类资源? IORESOURCE_MEM、 IORESOURCE_REG
* // IORESOURCE_IRQ 等
* @num: resource index // 这类资源中的哪一个?
*/
struct resource *platform_get_resource(struct platform_device *dev,
                                       unsigned int type,
                                       unsigned int num);

2. 对于I2C 设备、SPI 设备

对于I2C 设备节点,I2C 总线驱动在处理设备树里的 I2C子节点时,也会处理其中的中断信息。一个 I2C 设备会被转换为一个 i2c_client 结构体,中断号也会保存在 i2c_client 的irq 成员里,代码如下 (drivers/i2c/i2c-core-base.c):

static int i2c_device_probe(struct device *dev)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;
	int status;
 
	if (!client)
		return 0;
 
	driver = to_i2c_driver(dev->driver);
 
	client->irq = client->init_irq;
 
	if (!client->irq && !driver->disable_i2c_core_irq_mapping) {
		int irq = -ENOENT;
 
		if (client->flags & I2C_CLIENT_HOST_NOTIFY) {
			dev_dbg(dev, "Using Host Notify IRQ\n");
			/* Keep adapter active when Host Notify is required */
			pm_runtime_get_sync(&client->adapter->dev);
			irq = i2c_smbus_host_notify_to_irq(client);
		} else if (dev->of_node) {
			irq = of_irq_get_byname(dev->of_node, "irq");
			if (irq == -EINVAL || irq == -ENODATA)
				irq = of_irq_get(dev->of_node, 0);
		} else if (ACPI_COMPANION(dev)) {
			irq = i2c_acpi_get_irq(client);
		}
		if (irq == -EPROBE_DEFER)
			return irq;
 
		if (irq < 0)
			irq = 0;
 
		client->irq = irq;
	}
 
	/*
	 * An I2C ID table is not mandatory, if and only if, a suitable OF
	 * or ACPI ID table is supplied for the probing device.
	 */
	if (!driver->id_table &&
	    !i2c_acpi_match_device(dev->driver->acpi_match_table, client) &&
	    !i2c_of_match_device(dev->driver->of_match_table, client))
		return -ENODEV;
 
	if (client->flags & I2C_CLIENT_WAKE) {
		int wakeirq;
 
		wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
		if (wakeirq == -EPROBE_DEFER)
			return wakeirq;
 
		device_init_wakeup(&client->dev, true);
 
		if (wakeirq > 0 && wakeirq != client->irq)
			status = dev_pm_set_dedicated_wake_irq(dev, wakeirq);
		else if (client->irq > 0)
			status = dev_pm_set_wake_irq(dev, client->irq);
		else
			status = 0;
 
		if (status)
			dev_warn(&client->dev, "failed to set up wakeup irq\n");
	}
 
	dev_dbg(dev, "probe\n");
 
	status = of_clk_set_defaults(dev->of_node, false);
	if (status < 0)
		goto err_clear_wakeup_irq;
 
	status = dev_pm_domain_attach(&client->dev, true);
	if (status)
		goto err_clear_wakeup_irq;
 
	/*
	 * When there are no more users of probe(),
	 * rename probe_new to probe.
	 */
	if (driver->probe_new)
		status = driver->probe_new(client);
	else if (driver->probe)
		status = driver->probe(client,
				       i2c_match_id(driver->id_table, client));
	else
		status = -EINVAL;
 
	if (status)
		goto err_detach_pm_domain;
 
	return 0;
 
err_detach_pm_domain:
	dev_pm_domain_detach(&client->dev, true);
err_clear_wakeup_irq:
	dev_pm_clear_wake_irq(&client->dev);
	device_init_wakeup(&client->dev, false);
	return status;
}

在这里插入图片描述
对于SPI设备节点, SPI总线驱动在处理设备树里的 SPI 子节点时,也会处理其中的中断信息。一个SPI 设备会被转换为一个 spi_device 结构体,中断号会保存在 spi_device 的 irq 成员里,代码如下(drivers/spi/spi.c):

static int spi_drv_probe(struct device *dev)
{
	const struct spi_driver		*sdrv = to_spi_driver(dev->driver);
	struct spi_device		*spi = to_spi_device(dev);
	int ret;
 
	ret = of_clk_set_defaults(dev->of_node, false);
	if (ret)
		return ret;
 
	if (dev->of_node) {
		spi->irq = of_irq_get(dev->of_node, 0);
		if (spi->irq == -EPROBE_DEFER)
			return -EPROBE_DEFER;
		if (spi->irq < 0)
			spi->irq = 0;
	}
 
	ret = dev_pm_domain_attach(dev, true);
	if (ret)
		return ret;
 
	ret = sdrv->probe(spi);
	if (ret)
		dev_pm_domain_detach(dev, true);
 
	return ret;
}

在这里插入图片描述

3.调用 of_irq_get 获得中断号

如果你的设备节点既不能转换为 platform_device,它也不是 I2C 设备,不是 SPI 设备,那么在驱动程序中可以自行调用 of_irq_get 函数去解析设备树,得到中断号。

4. 对于GPIO

参考: drivers/input/keyboard/gpio_keys.c
可以使用 gpio_to_irqgpiod_to_irq 获得中断号。

假如设备树中有如下节点:

gpio-keys {
    compatible = "gpio-keys";
    pinctrl-names = "default";
    user {
        label = "User Button";
        gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
        gpio-key,wakeup;
        linux,code = <KEY_1>;
    };
};

那么可以使用下面的函数获得引脚和 flag

button->gpio = of_get_gpio_flags(pp, 0, &flags);
bdata->gpiod = gpio_to_desc(button->gpio);

再去使用 gpiod_to_irq 获得中断号

irq = gpiod_to_irq(bdata->gpiod);
在STM32H7系列微控制器,启用特定的Timestamp (TIM) 中断通常涉及以下步骤: 1. **包含头文件**: 首先,在C代码包括`stm32h7xx_hal_tim.h`头文件,它包含了TIM模块的相关结构体和函数声明。 ```c #include "stm32h7xx_hal_tim.h" ``` 2. **初始化TIM外设**: 初始化你需要配置中断的TIM定时器,例如TIMx,通过`TIM_HandleTypeDef`类型的变量来表示: ```c TIM_HandleTypeDef htim; TIM_InitTypeDef.TIM_Prescaler = ...; // 设置预分频器值 TIM_InitTypeDef.TIM_Period = ...; // 设置计数周期 TIM_Init(&htim, &TIM_InitTypeDef); // 初始化TIM HAL_TIM_Base_Start_IT(&htim); // 启动定时器的基本定时功能 ``` 3. **设置中断**: 调用`HAL_TIM_IRQHandler()`函数,这会处理TIM的中断请求,并根据需要在相应的中断服务函数启用中断: ```c void TIMx_IRQHandler(TIM_HandleTypeDef *htim) { if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET) // 判断是否有UPDATE标志位 { __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_UPDATE); // 清除标志位 // 执行中断处理操作... // ... } } ``` 使用`__HAL_TIM_SET_IT_ENABLE(htim, TIM_IT_Update)`可以开启UPDATE中断。这里的`TIM_IT_Update`代表TIM更新中断。 4. **注册中断向量**: 将TIM中断关联到处理器的中断向量表,通常由系统库自动完成,但在某些情况下需要手动调整。 5. **启用中断**: 最后,调用`HAL_NVIC_EnableIRQ(TIMx_IRQn)`来激活TIMx的中断,其`TIMx_IRQn`对应TIMx中断向量名称(如`TIM1_UP_IRQn`、`TIM2_IRQn`等)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值