韋東山:在Linux設(shè)備樹(DTS)中指定中斷_在代碼中獲得中斷

作者:韋東山
全文分為三大部分
1. 設(shè)備樹里中斷節(jié)點(diǎn)的語法
2. 設(shè)備樹里中斷節(jié)點(diǎn)的示例
3. 在代碼中獲得中斷
1 .設(shè)備樹里中斷節(jié)點(diǎn)的語法
參考文檔:
內(nèi)核Documentation\devicetree\bindings\interrupt-controller\interrupts.txt
1.1 設(shè)備樹里的中斷控制器
中斷的硬件框圖如下:

在硬件上,“中斷控制器”只有GIC這一個,但是我們在軟件上也可以把上圖中的“GPIO”稱為“中斷控制器”。很了芯片有多個GPIO模塊,比如GPIO1、GPIO2等等。所以軟件上的“中斷控制器”就有很多個:GIC、GPIO1、GPIO2等等。
GPIO1連接到GIC,GPIO2連接到GIC,所以GPIO1的父親是GIC,GPIO2的父親是GIC。
假設(shè)GPIO1有32個中斷源,但是它把其中的16個匯聚起來向GIC發(fā)出一個中斷,把另外16個匯聚起來向GIC發(fā)出另一個中斷。這就意味著GPIO1會用到GIC的兩個中斷,會涉及GIC里的2個hwirq。
這些層級關(guān)系、中斷號(hwirq),都會在設(shè)備樹中有所體現(xiàn)。
在設(shè)備樹中,中斷控制器節(jié)點(diǎn)中必須有一個屬性:interrupt-controller,表明它是“中斷控制器”。
還必須有一個屬性:#interrupt-cells,表明引用這個中斷控制器的話需要多少個cell。
#interrupt-cells的值一般有如下取值:
①
別的節(jié)點(diǎn)要使用這個中斷控制器時,只需要一個cell來表明使用“哪一個中斷”。
②
別的節(jié)點(diǎn)要使用這個中斷控制器時,需要一個cell來表明使用“哪一個中斷”;
還需要另一個cell來描述中斷,一般是表明觸發(fā)類型:
第2個cell的bits[3:0] 用來表示中斷觸發(fā)類型(trigger type and level flags):
1 = low-to-high edge triggered,上升沿觸發(fā)
2 = high-to-low edge triggered,下降沿觸發(fā)
4 = active high level-sensitive,高電平觸發(fā)
8 = active low level-sensitive,低電平觸發(fā)
示例如下:
vic: intc@10140000?
{
?
compatible = "arm,versatile-vic";
?
interrupt-controller;
?
#interrupt-cells = <1>;
?
reg = <0x10140000 0x1000>;
};
如果中斷控制器有級聯(lián)關(guān)系,下級的中斷控制器還需要表明它的“interrupt-parent”是誰,用了interrupt-parent”中的哪一個“interrupts”,請看下一小節(jié)。
1.2 設(shè)備樹里使用中斷
一個外設(shè),它的中斷信號接到哪個“中斷控制器”的哪個“中斷引腳”,這個中斷的觸發(fā)方式是怎樣的?
這3個問題,在設(shè)備樹里使用中斷時,都要有所體現(xiàn)。
① interrupt-parent=<&XXXX>
你要用哪一個中斷控制器里的中斷?
② interrupts
你要用哪一個中斷?
Interrupts里要用幾個cell,由interrupt-parent對應(yīng)的中斷控制器決定。在中斷控制器里有“#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”,也指定“interrupts”,比如:
interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;
2 .設(shè)備樹里中斷節(jié)點(diǎn)的示例
以100ASK_IMX6ULL開發(fā)板為例,在arch/arm/boot/dts目錄下可以看到2個文件:imx6ull.dtsi、100ask_imx6ull-14x14.dts,把里面有關(guān)中斷的部分內(nèi)容抽取出來。

從設(shè)備樹反推IMX6ULL的中斷體系,如下,比之前的框圖多了一個“GPC INTC”:

GPC INTC的英文是:General Power Controller, Interrupt Controller。它提供中斷屏蔽、中斷狀態(tài)查詢功能,實(shí)際上這些功能在GIC里也實(shí)現(xiàn)了,個人覺得有點(diǎn)多余。除此之外,它還提供喚醒功能,這才是保留它的原因。
3 在代碼中獲得中斷
之前我們提到過,設(shè)備樹中的節(jié)點(diǎn)有些能被轉(zhuǎn)換為內(nèi)核里的platform_device,有些不能,回顧如下:
A. 根節(jié)點(diǎn)下含有compatile屬性的子節(jié)點(diǎn),會轉(zhuǎn)換為platform_device
B. 含有特定compatile屬性的節(jié)點(diǎn)的子節(jié)點(diǎn),會轉(zhuǎn)換為platform_device
如果一個節(jié)點(diǎn)的compatile屬性,它的值是這4者之一:"simple-bus","simple-mfd","isa","arm,amba-bus",
那么它的子結(jié)點(diǎn)(需含compatile屬性)也可以轉(zhuǎn)換為platform_device。
C. 總線I2C、SPI節(jié)點(diǎn)下的子節(jié)點(diǎn):不轉(zhuǎn)換為platform_device
某個總線下到子節(jié)點(diǎn),應(yīng)該交給對應(yīng)的總線驅(qū)動程序來處理, 它們不應(yīng)該被轉(zhuǎn)換為platform_device。
3.1 對于platform_device
一個節(jié)點(diǎn)能被轉(zhuǎn)換為platform_device,如果它的設(shè)備樹里指定了中斷屬性,那么可以從platform_device中獲得“中斷資源”,函數(shù)如下,可以使用下列函數(shù)獲得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);
3.2 對于I2C設(shè)備、SPI設(shè)備
對于I2C設(shè)備節(jié)點(diǎn),I2C總線驅(qū)動在處理設(shè)備樹里的I2C子節(jié)點(diǎn)時,也會處理其中的中斷信息。一個I2C設(shè)備會被轉(zhuǎn)換為一個i2c_client結(jié)構(gòu)體,中斷號會保存在i2c_client的irq成員里,代碼如下(drivers/i2c/i2c-core.c):

對于SPI設(shè)備節(jié)點(diǎn),SPI總線驅(qū)動在處理設(shè)備樹里的SPI子節(jié)點(diǎn)時,也會處理其中的中斷信息。一個SPI設(shè)備會被轉(zhuǎn)換為一個spi_device結(jié)構(gòu)體,中斷號會保存在spi_device的irq成員里,代碼如下(drivers/spi/spi.c):

3.3 調(diào)用of_irq_get獲得中斷號
如果你的設(shè)備節(jié)點(diǎn)既不能轉(zhuǎn)換為platform_device,它也不是I2C設(shè)備,不是SPI設(shè)備,那么在驅(qū)動程序中可以自行調(diào)用of_irq_get函數(shù)去解析設(shè)備樹,得到中斷號。
3.4 對于GPIO
參考:drivers/input/keyboard/gpio_keys.c
可以使用gpio_to_irq或gpiod_to_irq獲得中斷號。
舉例,假設(shè)在設(shè)備樹中有如下節(jié)點(diǎn):
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>;
? ?
?};
};
那么可以使用下面的函數(shù)獲得引腳和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);
本文配套視頻:
https://www.bilibili.com/video/BV1w4411B7a4?p=77

我是韋東山,10多年一直在研究linux+ARM,希望我的分享對你有幫助,歡迎進(jìn)店訂閱我的付費(fèi)內(nèi)容:http://100ask.taobao.com
聯(lián)系我們:
如果您沒有沒懂沒關(guān)系,可以加韋東山官方微信群進(jìn)行討論。
先加管理員微信:13163769879,若購買產(chǎn)品,拉您到對應(yīng)的售后群,否則拉入粉絲群。