想使用中断,节点中至少有有2个属性:
interrupts // 表示要使用哪一个中断, 中断的触发类型等等。
interrupt-parent // 这个中断要接到哪一个设备去? 即父中断控制器是谁
父中断控制器有两种指定方法:
1)只有一个中断父设备
interrupt-parent = <&父设备标号>;
interrupts = <... ...>, <... ...>;
ethernet@20000000 {
compatible = "davicom,dm9000";
reg = <0x20000000 0x2 0x20000004 0x2>;
interrupt-parent = <&gpf>; //父中断控制器为gpf
interrupts = <7 IRQ_TYPE_EDGE_RISING>;
local-mac-address = [00 00 de ad be ef];
davicom,no-eeprom;
};
2)有多个中断父设备
interrupts-extended = <&父设备标号 .....>, <... ... ...>;
buttons {
compatible = "jz2440_button";
eint-pins = <&gpf 0 0>, <&gpf 2 0>, <&gpg 3 0>, <&gpg 11 0>;
interrupts-extended = <&intc 0 0 0 3>,
<&intc 0 0 2 3>,
<&gpg 3 3>,
<&gpg 11 3>;
};
由它的父中断控制器来描述,在父中断控制器中, 至少有2个属性:
interrupt-controller; // 表示自己是一个中断控制器
#interrupt-cells // 表示自己的子设备里应该用几个U32的数据来描述中断
例如:
gpf {
gpio-controller;
#gpio-cells = <0x2>;
interrupt-controller;
#interrupt-cells = <0x2>; //使用两个u32数据来描述中断
phandle = <0x6>;
};
一般在描述子中断节点中都会有一个属性interrupt-parent,由此属性描述。
如果子中断节点中没有此属性,需要查看此节点的父节点,一级一级往上直到父节点中出现interrupt-parent。
interrupts属性的具体含义由各自的父中断控制器来解释
s3c2440中有一个主中断控制器,一个子中断控制器,以及一个外部中断控制器(EINTPEND)。下面截取了这些中断控制器的一些图,用来说明每个bit代表哪个中断。
主中断控制器
副中断控制器
外部中断控制器
设备树中的ctrl_irq实际上指的就是各个中断控制器上的对应位。
一个例子:
jz2440ts@5800000 {
compatible = "jz2440,ts";
reg = <0x58000000 0x100>;
reg-names = "adc_ts_physical";
<连接到副中断控制器 父中断号31 副中断中的bit9 bit10 触发类型为3>
interrupts = <1 31 9 3>, <1 31 10 3>;
interrupt-names = "int_ts", "int_adc_s";
clocks = <&clocks PCLK_ADC>;
clock-names = "adc";
};
例子中是一个描述触摸设备的节点,节点内并没有指定interrupt-parent,我们可以使用之前提到的方法去找。实际上此节点的父中断控制器为主中断控制器。
关于interrupts属性的描述可以在linux-4.19-rc3\Documentation\devicetree\bindings\interrupt-controller\samsung,s3c24xx-irq.txt中找到。
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. The value shall be 4 and interrupt descriptor shall
have the following format:
<连接的中断控制器 父中断号 中断所占位 中断触发类型>
ctrl_num contains the controller to use:
- 0 ... main controller
- 1 ... sub controller
- 2 ... second main controller on s3c2416 and s3c2450
parent_irq contains the parent bit in the main controller and will be
ignored in main controllers
ctrl_irq contains the interrupt bit of the controller
type contains the trigger type to use
结合着上述文档内的描述,我们可以知道触摸屏节点里的对应的中断是连接到副中断控制器,父中断号31,属于副中断中的bit9 bit10,触发类型为3。查看芯片手册后可知这两个中断为INT_TC,INT_ADC_S。
另一个例子:
ethernet@20000000 {
compatible = "davicom,dm9000";
reg = <0x20000000 0x2 0x20000004 0x2>;
interrupt-parent = <&gpf>; //父中断控制器为gpf
//中断位bit7
interrupts = <7 IRQ_TYPE_EDGE_RISING>;
local-mac-address = [00 00 de ad be ef];
davicom,no-eeprom;
};
这是一个描述网卡的设备树节点,它的父节点为gpf。GPF是一个外部中断,关于它子节点的中断描述在linux-4.19-rc3\Documentation\devicetree\bindings\pinctrl\samsung-pinctrl.txt中找到。
- interrupt-controller: identifies the controller node as interrupt-parent.
- #interrupt-cells: the value of this property should be 2.
- First Cell: represents the external gpio interrupt number local to the
external gpio interrupt space of the controller.
- Second Cell: flags to identify the type of the interrupt
- 1 = rising edge triggered
- 2 = falling edge triggered
- 3 = rising and falling edge triggered
- 4 = high level triggered
- 8 = low level triggered
#define IRQ_TYPE_NONE 0
#define IRQ_TYPE_EDGE_RISING 1
#define IRQ_TYPE_EDGE_FALLING 2
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
#define IRQ_TYPE_LEVEL_HIGH 4
#define IRQ_TYPE_LEVEL_LOW 8
结合文档可知,中断引脚为GPF7,使用的是EINT7,并且触摸沿是上升沿触发。
IRQ_TYPE_EDGE_RISING定义在linux-4.19-rc3\include\dt-bindings\interrupt-controller\irq.h内。
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节点:
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client->irq && dev->of_node) {
int irq = of_irq_get(dev->of_node, 0); //得到节点中的中断号
if (irq == -EPROBE_DEFER)
return irq;
if (irq < 0)
irq = 0;
client->irq = irq;
}
....
}
获取节点中的中断号保存在client->irq中
对于spi节点:
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;
}
驱动程序可直接在probe函数中直接使用这些中断。
假如使用的中断对应接到了io上,则可以使用此方法定义和使用中断。
首先在设备树中定义一个io
device{
...
irq-gpio = <&gpio4 21 1>; //声明使用的引脚
..
};
在驱动代码中获取中断
irq_gpio = of_get_named_gpio ( np, "irq-gpio", 0 ); //获取属性irq-gpio的引脚
if ( irq_gpio < 0 )
{
ret = irq_gpio;
if ( ret != -ENOENT )
{
if ( ret != -EPROBE_DEFER )
dev_err ( dev,
"Failed to get gpio flags, error: %d\n",
ret );
return ret ;
}
}
irq = gpiod_to_irq ( gpio_to_desc ( irq_gpio ) ); // 获取中断号
if ( irq < 0 )
{
ret = irq;
dev_err ( dev,
"Unable to get irq number for GPIO %d, error %d\n",
irq_gpio, ret );
return ret;
}
之后即可使用此中断号去申请中断。