基於設備樹的TQ2440的中斷(1)


作者

姓名:彭東林

E-mail:pengdonglin137@163.com

QQ:405728433

 

平台

板子:TQ2440

內核:Linux-4.9

u-boot: 2015.04

工具鏈: arm-none-linux-gnueabi-gcc 4.8.3

 

概述

在博文讓TQ2440也用上設備樹(1)將支持devicetree的Linux4.9移植到了tq2440上面,而在基於tiny4412的Linux內核移植 --- 實例學習中斷背后的知識(1)中介紹了最新的Linux下中斷的知識,下面我們再結合TQ2440來分析一下。

 

正文

一、基礎知識

關於這部分請參考S3C2440的芯片手冊或者博文TQ2440中斷系統

下面簡單介紹:

從圖中可以看到,中斷主要分為兩級,我們可以理解為是兩個中斷控制器的嵌套或者級聯。S3C2440總共支持60個中斷源,包含了主中斷源和子中斷源。

寄存器功能介紹:

1、SRCPND 地址: 0x4A000000 功能:每一位代表一個主中斷,置1表示有對應的主中斷請求,對應位寫入1可以清除中斷

2、INTMOD 地址: 0x4A000004 功能:設置對應的主中斷為IRQ還是FIQ, 置1表示FIQ

3、INTMSK 地址: 0x4A000008 功能:置1表示對應的主中斷被屏蔽(不會影響SRCPND)

4、INTPND 地址: 0x4A000010 功能:表示對應的主中斷被request,只可能有一位被置位,寫入1可以清除中斷

5、INTOFFSET 地址:0x4A000014 功能:存放的是發生中斷請求的主中斷號

6、SUBSRCPND 地址:0x4A000018 功能:每一位代表一個子中斷,置一表示對應子中斷請求,對應位寫入1清除子中斷請求

7、INTSUBMSK 地址:0x4A00001C 功能:置1表示對應的子中斷被屏蔽

32個主中斷:

圖一

15個子中斷:

圖二
 
圖三

外部中斷:

EINT0~7對應的GPIO是GPF0~7

EINT8~23對應的GPIO是GPG0~15

二、設備樹

1、中斷控制器

intc:interrupt-controller@4a000000 {
compatible = "samsung,s3c2410-irq";
reg = <0x4a000000 0x100>;
interrupt-controller;
#interrupt-cells = <4>;
};
2、引用
serial@50000000 {
compatible = "samsung,s3c2440-uart";
reg = <0x50000000 0x4000>;
interrupts = <1 28 0 4>, <1 28 1 4>;
status = "okay";
clock-names = "uart";
clocks = <&clock PCLK_UART0>;
pinctrl-names = "default";
pinctrl-0 = <0x3>;
};
 
i2c:i2c@54000000 {
compatible = "samsung,s3c2410-i2c";
reg = <0x54000000 0x100>;
interrupts = <0 0 27 3>;
#address-cells = <1>;
#size-cells = <0>;
};
上面的serial和i2c設備比較典型,一個引用的是主中斷,另一個引用的是子中斷。從中斷控制器的#interrupt-cells屬性知道,要描述一個中斷需要四個參數,每一個參數的含義需要由中斷控制器的驅動來解釋,具體是有中斷控制器的irq_domain_ops中的xlate來解釋,對於s3c2440就是drivers/irqchip/irq-s3c24xx.c中的s3c24xx_irq_xlate_of。
 1 /* Translate our of irq notation
 2  * format: <ctrl_num ctrl_irq parent_irq type>
 3  */
 4 static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
 5             const u32 *intspec, unsigned int intsize,
 6             irq_hw_number_t *out_hwirq, unsigned int *out_type)
 7 {
 8     struct s3c_irq_intc *intc;
 9     struct s3c_irq_intc *parent_intc;
10     struct s3c_irq_data *irq_data;
11     struct s3c_irq_data *parent_irq_data;
12     int irqno;
13 
14     if (WARN_ON(intsize < 4))  //如果參數個數不能小於4
15         return -EINVAL;
16         // 從這里知道,第一個參數不能大於2,只能是0和1, 0表示主中斷,2表示子中斷
17     if (intspec[0] > 2 || !s3c_intc[intspec[0]]) {
18         pr_err("controller number %d invalid\n", intspec[0]);
19         return -EINVAL;
20     }
21         // s3c_intc[0]表示主中斷控制器,s3c_intc[1]表示子中斷控制器
22     intc = s3c_intc[intspec[0]];
23 
24         // 第三個參數表示的是硬件中斷號,從這里知道,主中斷的硬件中斷是0~31,子中斷的硬件中斷是32及其以上
25     *out_hwirq = intspec[0] * 32 + intspec[2];
26         // 第四個參數表示的是中斷類型,可以查看IRQ_TYPE_SENSE_MASK定義,就知道含義:
27         // 1表示上升沿觸發,2表示下降沿觸發,3表示雙邊沿觸發,4表示高電平觸發,8表示低電平觸發,12表示高低電平觸發
28     *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
29         // 如果是主中斷,則intc->parent為NULL, 否則非空
30     parent_intc = intc->parent;
31     if (parent_intc) {  // 子中斷
32         irq_data = &intc->irqs[intspec[2]];
33                 // 對於子中斷,第二個參數才有意義,表示該子中斷所隸屬的主中斷的硬件中斷號
34         irq_data->parent_irq = intspec[1];
35         parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
36         parent_irq_data->sub_intc = intc;
37                 // sub_bits中記錄該主中斷下的子中斷被被使用的情況
38         parent_irq_data->sub_bits |= (1UL << intspec[2]);
39                 // 將主中斷號映射成虛擬中斷號
40         /* parent_intc is always s3c_intc[0], so no offset */
41         irqno = irq_create_mapping(parent_intc->domain, intspec[1]);
42         if (irqno < 0) {
43             pr_err("irq: could not map parent interrupt\n");
44             return irqno;
45         }
46                 // 這里設置irqno對應的irq_desc的handle_irq為s3c_irq_demux
47                 // 從函數名稱中就可以看出, 這個函數會再次進行檢測該主中斷的哪個子中斷被請求
48         irq_set_chained_handler(irqno, s3c_irq_demux);
49     }
50 
51     return 0;
52 }
View Code

從這里我們知道,interrupts屬性中的四個參數中的含義:

以串口的<1 28 0 4>和i2c的<0 0 27 3>為例:
<0 0 27 3>:第1個0表示的是主中斷,第2個數字0沒啥用,第3個數字27表示硬件中斷號,第4個數字3表示雙邊沿觸發。從上面的圖一中可以看到I2C的硬件中斷號是27
<1 28 0 4>:第1個數字1表示的是子中斷,第2個數字28表示的是uart0的主中斷,從上面的圖一中可以看到uart0的主中斷是28,第3個數字0表示的是子中斷的硬件中斷號,也就是圖二中INT_RXD0的位號0, 第4個數字4表示的是高電平觸發

三、中斷控制器驅動

中斷控制器驅動:drivers/irqchip/irq-s3c24xx.c
初始化入口:
 1 static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = {
 2     { // 主中斷
 3         .name = "intc",
 4         .offset = 0,
 5     }, { // 子中斷
 6         .name = "subintc",
 7         .offset = 0x18,  // 寄存器地址偏移
 8         .parent = &s3c_intc[0],
 9     }
10 };
11 
12 int __init s3c2410_init_intc_of(struct device_node *np,
13             struct device_node *interrupt_parent)
14 {
15     return s3c_init_intc_of(np, interrupt_parent,
16                 s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
17 }
18 IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);
View Code

在內核啟動的時候,函數s3c2410_init_intc_of會被調用。

下面我們分析函數s3c_init_intc_of:
 1 static int __init s3c_init_intc_of(struct device_node *np,
 2             struct device_node *interrupt_parent,
 3             struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl)
 4 {
 5     struct s3c_irq_intc *intc;
 6     struct s3c24xx_irq_of_ctrl *ctrl;
 7     struct irq_domain *domain;
 8     void __iomem *reg_base;
 9     int i;
10 
11         // 對中斷控制器的reg屬性所表示地址空間進行映射
12     reg_base = of_iomap(np, 0);
13         
14         // 為該中斷控制器創建irq_domain,一共支持64個中斷,對應的irq_domain_ops是s3c24xx_irq_ops_of
15     domain = irq_domain_add_linear(np, num_ctrl * 32,
16                              &s3c24xx_irq_ops_of, NULL);
17         // 依次處理兩個中斷控制器
18     for (i = 0; i < num_ctrl; i++) {
19         ctrl = &s3c_ctrl[i];
20 
21         pr_debug("irq: found controller %s\n", ctrl->name);
22                 // 主和子中斷控制器各分配一個s3c_irq_intc
23         intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
24                 // 主和子中斷控制器共享一個irq_domain
25         intc->domain = domain;
26                 // 為主和子中斷控制器下的每個中斷各分配一個s3c_irq_data結構體
27         intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32,
28                      GFP_KERNEL);
29 
30         if (ctrl->parent) {  // 子中斷控制器
31             intc->reg_pending = reg_base + ctrl->offset; // SUBSRCPND
32             intc->reg_mask = reg_base + ctrl->offset + 0x4; // INTSUBMSK
33                         
34                         // 由於先處理的是s3c2410_ctrl[0],所以在處理子中斷的時候,*(ctrl->parent)非空
35                         // 是主中斷控制器對應的s3c_irq_intc
36             if (*(ctrl->parent)) {
37                 intc->parent = *(ctrl->parent);
38             } else {
39                 pr_warn("irq: parent of %s missing\n",
40                     ctrl->name);
41                 kfree(intc->irqs);
42                 kfree(intc);
43                 continue;
44             }
45         } else {  // 主中斷控制器
46             intc->reg_pending = reg_base + ctrl->offset;  //SRCPND
47             intc->reg_mask = reg_base + ctrl->offset + 0x08; //INTMSK
48             intc->reg_intpnd = reg_base + ctrl->offset + 0x10; //INTPND
49         }
50 
51         s3c24xx_clear_intc(intc);  // 清除中斷
52         s3c_intc[i] = intc;
53     }
54         // 將handle_arch_irq設置為s3c24xx_handle_irq, 這樣在發生中斷后,會從匯編entry-armv.S中
55         // 調轉到s3c24xx_handle_irq
56     set_handle_irq(s3c24xx_handle_irq);
57 
58     return 0;
59 }
View Code

中斷控制啟動的初始化,結構體s3c24xx_irq_ops_of中的map和xlate還沒有分析,放到下面分析。

四、interrupts屬性的解析

在內核啟動期間解析設備樹,將device_node轉換為platform_device的時候會處理節點中的interrupts屬性,將其轉換為irq resource,同時在所屬的irq domain中建立起hwirq到virq的映射,下面列出主要的函數調用,期間就會調用上面中斷控制器的irq_domain的s3c24xx_irq_ops_of中的xlate和map:
of_platform_default_populate_init
    ---> of_platform_default_populate
---> of_platform_populate
            ---> of_platform_bus_create
                ---> of_platform_device_create_pdata
                    ---> of_device_alloc
                        ---> of_irq_to_resource_table
                            ---> of_irq_to_resource

                                ---> irq_of_parse_and_map

                                    ---> of_irq_parse_one

                                    ---> irq_create_of_mapping

                                        ---> irq_create_fwspec_mapping

                                            ---> irq_domain_translate // 解析參數

                                                ---> s3c24xx_irq_xlate_of

                                            ---> irq_create_mapping // 創建hwirq到virq的映射

                                                ---> irq_domain_associate

                                                    ---> s3c24xx_irq_map_of

詳細分析請參考:基於tiny4412的Linux內核移植 --- 實例學習中斷背后的知識(1)

這里我們只看看s3c24xx_irq_map_of。

在調用這個函數時,已經創建號hwirq對應的virq了:

 1 static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
 2                             irq_hw_number_t hw)
 3 {
 4     unsigned int ctrl_num = hw / 32; //判斷該hwirq屬於主還是子中斷控制器
 5     unsigned int intc_hw = hw % 32; // 得到跟寄存器對應的硬件中斷號
 6     struct s3c_irq_intc *intc = s3c_intc[ctrl_num]; // 獲得中斷控制器對應的結構體
 7     struct s3c_irq_intc *parent_intc = intc->parent;
 8     struct s3c_irq_data *irq_data = &intc->irqs[intc_hw]; // 每個中斷都會有一個s3c_irq_data結構體
 9 
10     /* attach controller pointer to irq_data */
11     irq_data->intc = intc;
12     irq_data->offset = intc_hw; //跟硬件寄存器對應的硬件中斷號,也就是對應的位號
13 
14         // 將virq對應的irq_desc的handle_irq初始化為handle_edge_irq,
15         // 下面在s3c_irq_type中會根據中斷觸發類型再次修改
16     if (!parent_intc) // 主中斷控制器
17         irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq);
18     else // 子中斷控制器
19         irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
20                      handle_edge_irq);
21 
22     irq_set_chip_data(virq, irq_data);
23 
24     return 0;
25 }
View Code

上面主和子中斷控制雖然使用的時同一個irq domain但是對應的irq_chip卻是各自的。

主irq_chip:
1 static struct irq_chip s3c_irq_chip = {
2 .name= "s3c",
3 .irq_ack= s3c_irq_ack,  // 清除中斷
4 .irq_mask= s3c_irq_mask, // 屏蔽中斷
5 .irq_unmask= s3c_irq_unmask, // 打開中斷
6 .irq_set_type= s3c_irq_type, //根據不同的觸發類型設置不同的處理函數
7 .irq_set_wake= s3c_irq_wake  // 
8 };

子irq_chip:

1 static struct irq_chip s3c_irq_level_chip = {
2 .name= "s3c-level",
3 .irq_mask= s3c_irq_mask,
4 .irq_unmask= s3c_irq_unmask,
5 .irq_ack= s3c_irq_ack,
6 .irq_set_type= s3c_irq_type,
7 };

 s3c_irq_mask中屏蔽中斷的時候,如果屏蔽的是子中斷,還需要判斷目前該子中斷所隸屬的主中斷是不是還有其他子中斷在使用,如果沒有的話,也會把該主中斷也給屏蔽了。

同樣,s3c_irq_unmask在打開子中斷時也會將其所隸屬的主中斷也打開。

五、子中斷分發s3c_irq_demux

在含有子中斷的主中斷被觸發后,會執行主中斷對應的virq的irq_desc的handle_irq,也就是這里的s3c_irq_demux:

 1 static void s3c_irq_demux(struct irq_desc *desc) // 這里的desc是主中斷的virq的irq_desc
 2 {
 3     struct irq_chip *chip = irq_desc_get_chip(desc); //獲得主中斷的irq_chip
 4     struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc);
 5     struct s3c_irq_intc *intc = irq_data->intc; // 參考s3c24xx_irq_xlate_of函數
 6     struct s3c_irq_intc *sub_intc = irq_data->sub_intc;  // 參考s3c24xx_irq_xlate_of函數
 7     unsigned int n, offset, irq;
 8     unsigned long src, msk;
 9 
10     /* we're using individual domains for the non-dt case
11      * and one big domain for the dt case where the subintc
12      * starts at hwirq number 32.
13      */
14         // 由於我們使用的是設備樹,所以這里的offset是32,也就是子中斷的起始號
15     offset = irq_domain_get_of_node(intc->domain) ? 32 : 0;
16 
17         // 屏蔽該主中斷,並清中斷
18     chained_irq_enter(chip, desc);
19         // 讀取子中斷的pending寄存器,看那些子中斷被觸發了
20     src = readl_relaxed(sub_intc->reg_pending);
21     msk = readl_relaxed(sub_intc->reg_mask);
22 
23     src &= ~msk; // 去掉被屏蔽的
24     src &= irq_data->sub_bits; // 去掉沒有初始化的
25         // 此時src中存放的就是將要被處理的子中斷,下面會一一處理
26     while (src) {
27         n = __ffs(src);
28         src &= ~(1 << n);
29         irq = irq_find_mapping(sub_intc->domain, offset + n); //根據hwirq找到virq
30         generic_handle_irq(irq); // 處理
31     }
32         // 打開該主中斷
33     chained_irq_exit(chip, desc);
34 }
View Code

六、設備驅動申請中斷

1、串口驅動

drivers/tty/serial/samsung.c

函數調用:

s3c24xx_serial_probe

    ---> s3c24xx_serial_init_port

在函數s3c24xx_serial_init_port會申請中斷

ret = platform_get_irq(platdev, 0); // UART0 receive interrupt

ret = platform_get_irq(platdev, 1); // UART0 transmit interrupt

2、I2C控制器

drivers/i2c/busses/i2c-s3c2410.c

在函數s3c24xx_i2c_probe中會申請中斷:

i2c->irq = ret = platform_get_irq(pdev, 0)

七、查看系統信息

開機后,可以從/proc/interrupts中看到當前的中斷資源申請信息:

[root@tq2440 ]# cat /proc/interrupts 
           CPU0       
  7:        926  s3c-eint   7 Edge      eth0
  8:          0       s3c   8 Edge      s3c2410-rtc tick
 13:     567308       s3c  13 Edge      samsung_time_irq
 26:          0       s3c  26 Edge      ohci_hcd:usb1
 27:          4       s3c  27 Edge      54000000.i2c       //I2C
 30:          0       s3c  30 Edge      s3c2410-rtc alarm
 32:         53  s3c-level  32 Level     50000000.serial   //UART0 RXD
 33:        230  s3c-level  33 Level     50000000.serial   //UART0 TXD
 59:          0  s3c-level  59 Edge      53000000.watchdog
下面解釋一下上面這些參數的含義:
下面我們在中斷處理程序中加入log,看一下中斷處理程序的調用棧:
1、 以I2C代表的主中斷
[    1.851407] [<c035004c>] (s3c24xx_i2c_irq) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)
[    1.851457] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)
[    1.851490] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)
[    1.851526] [<c0044960>] (handle_irq_event) from [<c00476c0>] (handle_edge_irq+0xbc/0x190)
[    1.851558] [<c00476c0>] (handle_edge_irq) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)
[    1.851589] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)
[    1.851622] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)
2、 以串口代表的子中斷
TX:
[    2.393176] [<c0293410>] (s3c24xx_serial_tx_chars) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)
[    2.393218] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)
[    2.393249] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)
[    2.393283] [<c0044960>] (handle_irq_event) from [<c004740c>] (handle_level_irq+0x90/0x114)
[    2.393312] [<c004740c>] (handle_level_irq) from [<c0043fd8>] (generic_handle_irq+0x2c/0x40)
[    2.393379] [<c0043fd8>] (generic_handle_irq) from [<c0242868>] (s3c_irq_demux+0xc8/0x140)
[    2.393424] [<c0242868>] (s3c_irq_demux) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)
[    2.393460] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)
[    2.393492] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)
RX:
[    9.223769] [<c0294570>] (s3c24xx_serial_rx_chars) from [<c00447f0>] (__handle_irq_event_percpu+0x3c/0x130)
[    9.223817] [<c00447f0>] (__handle_irq_event_percpu) from [<c0044900>] (handle_irq_event_percpu+0x1c/0x54)
[    9.223851] [<c0044900>] (handle_irq_event_percpu) from [<c0044960>] (handle_irq_event+0x28/0x3c)
[    9.223883] [<c0044960>] (handle_irq_event) from [<c004740c>] (handle_level_irq+0x90/0x114)
[    9.223915] [<c004740c>] (handle_level_irq) from [<c0043fd8>] (generic_handle_irq+0x2c/0x40)
[    9.223978] [<c0043fd8>] (generic_handle_irq) from [<c0242868>] (s3c_irq_demux+0xc8/0x140)
[    9.224017] [<c0242868>] (s3c_irq_demux) from [<c00441c4>] (__handle_domain_irq+0x6c/0xcc)
[    9.224050] [<c00441c4>] (__handle_domain_irq) from [<c0009444>] (s3c24xx_handle_irq+0x6c/0x12c)
[    9.224081] [<c0009444>] (s3c24xx_handle_irq) from [<c000e5fc>] (__irq_svc+0x5c/0x78)

 

完。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM