歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux芯片級移植與底層驅動(基於3.7.4內核) 中斷控制器

Linux芯片級移植與底層驅動(基於3.7.4內核) 中斷控制器

日期:2017/3/3 16:21:57   编辑:關於Linux

3.中斷控制器驅動

在Linux內核中,各個設備驅動可以簡單地調用request_irq()、enable_irq()、disable_irq()、local_irq_disable()、local_irq_enable()等通用API完成中斷申請、使能、禁止等功能。在將Linux移植到新的SoC時,芯片供應商需要提供該部分API的底層支持。

local_irq_disable()、local_irq_enable()的實現與具體中斷控制器無關,對於ARMv6以上的體系架構而言,是直接調用CPSID/CPSIE指令進行,而對於ARMv6以前的體系結構,則是透過MRS、MSR指令來讀取和設置ARM的CPSR寄存器。由此可見,local_irq_disable()、local_irq_enable()針對的並不是外部的中斷控制器,而是直接讓CPU本身不響應中斷請求。相關的實現位於arch/arm/include/asm/irqflags.h:

11#if __LINUX_ARM_ARCH__ >= 6

12

13static inline unsigned long arch_local_irq_save(void)

14{

15 unsigned long flags;

16

17 asm volatile(

18 " mrs %0, cpsr @ arch_local_irq_save\n"

19 " cpsid i"

20 : "=r" (flags) : : "memory", "cc");

21 return flags;

22}

23

24static inline void arch_local_irq_enable(void)

25{

26 asm volatile(

27 " cpsie i @ arch_local_irq_enable"

28 :

29 :

30 : "memory", "cc");

31}

32

33static inline void arch_local_irq_disable(void)

34{

35 asm volatile(

36 " cpsid i @ arch_local_irq_disable"

37 :

38 :

39 : "memory", "cc");

40}

44#else

45

46/*

47 * Save the current interrupt enable state & disable IRQs

48 */

49static inline unsigned long arch_local_irq_save(void)

50{

51 unsigned long flags, temp;

52

53 asm volatile(

54 " mrs %0, cpsr @ arch_local_irq_save\n"

55 " orr %1, %0, #128\n"

56 " msr cpsr_c, %1"

57 : "=r" (flags), "=r" (temp)

58 :

59 : "memory", "cc");

60 return flags;

61}

62

63/*

64 * Enable IRQs

65 */

66static inline void arch_local_irq_enable(void)

67{

68 unsigned long temp;

69 asm volatile(

70 " mrs %0, cpsr @ arch_local_irq_enable\n"

71 " bic %0, %0, #128\n"

72 " msr cpsr_c, %0"

73 : "=r" (temp)

74 :

75 : "memory", "cc");

76}

77

78/*

79 * Disable IRQs

80 */

81static inline void arch_local_irq_disable(void)

82{

83 unsigned long temp;

84 asm volatile(

85 " mrs %0, cpsr @ arch_local_irq_disable\n"

86 " orr %0, %0, #128\n"

87 " msr cpsr_c, %0"

88 : "=r" (temp)

89 :

90 : "memory", "cc");

91}

92 #endif

與local_irq_disable()和local_irq_enable()不同,disable_irq()、enable_irq()針對的則是外部的中斷控制器。在內核中,透過irq_chip結構體來描述中斷控制器。該結構體內部封裝了中斷mask、unmask、ack等成員函數,其定義於include/linux/irq.h:

303struct irq_chip {

304 const char *name;

305 unsigned int (*irq_startup)(struct irq_data *data);

306 void (*irq_shutdown)(struct irq_data *data);

307 void (*irq_enable)(struct irq_data *data);

308 void (*irq_disable)(struct irq_data *data);

309

310 void (*irq_ack)(struct irq_data *data);

311 void (*irq_mask)(struct irq_data *data);

312 void (*irq_mask_ack)(struct irq_data *data);

313 void (*irq_unmask)(struct irq_data *data);

314 void (*irq_eoi)(struct irq_data *data);

315

316 int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);

317 int (*irq_retrigger)(struct irq_data *data);

318 int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);

319 int (*irq_set_wake)(struct irq_data *data, unsigned int on);

334};

各個芯片公司會將芯片內部的中斷控制器實現為irq_chip驅動的形式。受限於中斷控制器硬件的能力,這些成員函數並不一定需要全部實現,有時候只需要實現其中的部分函數即可。譬如drivers/pinctrl/pinctrl-sirf.c驅動中的

1438static struct irq_chip sirfsoc_irq_chip = {

1439 .name = "sirf-gpio-irq",

1440 .irq_ack = sirfsoc_gpio_irq_ack,

1441 .irq_mask = sirfsoc_gpio_irq_mask,

1442 .irq_unmask = sirfsoc_gpio_irq_unmask,

1443 .irq_set_type = sirfsoc_gpio_irq_type,

1444};

我們只實現了其中的ack、mask、unmask和set_type成員函數,ack函數用於清中斷,mask、unmask用於中斷屏蔽和取消中斷屏蔽、set_type則用於配置中斷的觸發方式,如高電平、低電平、上升沿、下降沿等。至於enable_irq()的時候,雖然沒有實現irq_enable成員函數,但是內核會間接調用到irq_unmask成員函數,這點從kernel/irq/chip.c可以看出:

192void irq_enable(struct irq_desc *desc)

193{

194 irq_state_clr_disabled(desc);

195 if (desc->irq_data.chip->irq_enable)

196 desc->irq_data.chip->irq_enable(&desc->irq_data);

197 else

198 desc->irq_data.chip->irq_unmask(&desc->irq_data);

199 irq_state_clr_masked(desc);

200}

在芯片內部,中斷控制器可能不止1個,多個中斷控制器之間還很可能是級聯的。舉個例子,假設芯片內部有一個中斷控制器,支持32個中斷源,其中有4個來源於GPIO控制器外圍的4組GPIO,每組GPIO上又有32個中斷(許多芯片的GPIO控制器也同時是一個中斷控制器),其關系如下圖:

那麼,一般來講,在實際操作中,gpio0_0——gpio0_31這些引腳本身在第1級會使用中斷號28,而這些引腳本身的中斷號在實現GPIO控制器對應的irq_chip驅動時,我們又會把它映射到Linux系統的32——63號中斷。同理,gpio1_0——gpio1_31這些引腳本身在第1級會使用中斷號29,而這些引腳本身的中斷號在實現GPIO控制器對應的irq_chip驅動時,我們又會把它映射到Linux系統的64——95號中斷,以此類推。對於中斷號的使用者而言,無需看到這種2級映射關系。如果某設備想申請gpio1_0這個引腳對應的中斷,它只需要申請64號中斷即可。這個關系圖看起來如下:

還是以drivers/pinctrl/pinctrl-sirf.c的irq_chip部分為例,我們對於每組GPIO都透過irq_domain_add_legacy()添加了相應的irq_domain,每組GPIO的中斷號開始於SIRFSOC_GPIO_IRQ_START + i * SIRFSOC_GPIO_BANK_SIZE,而每組GPIO本身占用的第1級中斷控制器的中斷號則為bank->parent_irq,我們透過irq_set_chained_handler()設置了第1級中斷發生的時候,會調用鏈式IRQ處理函數sirfsoc_gpio_handle_irq():

1689 bank->domain = irq_domain_add_legacy(np, SIRFSOC_GPIO_BANK_SIZE,

1690 SIRFSOC_GPIO_IRQ_START + i * SIRFSOC_GPIO_BANK_SIZE, 0,

1691 &sirfsoc_gpio_irq_simple_ops, bank);

1692

1693 if (!bank->domain) {

1694 pr_err("%s: Failed to create irqdomain\n", np->full_name);

1695 err = -ENOSYS;

1696 goto out;

1697 }

1698

1699 irq_set_chained_handler(bank->parent_irq, sirfsoc_gpio_handle_irq);

1700 irq_set_handler_data(bank->parent_irq, bank);

而在sirfsoc_gpio_handle_irq()函數的入口出調用chained_irq_enter()暗示自身進入鏈式IRQ處理,在函數體內判決具體的GPIO中斷,並透過generic_handle_irq()調用到最終的外設驅動中的中斷服務程序,最後調用chained_irq_exit()暗示自身退出鏈式IRQ處理:

1446static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc)

1447{

1448 …

1454 chained_irq_enter(chip, desc);

1456 …

1477 generic_handle_irq(first_irq + idx);

1478 …

1484 chained_irq_exit(chip, desc);

1485}

很多中斷控制器的寄存器定義呈現出簡單的規律,如有一個mask寄存器,其中每1位可屏蔽1個中斷等,這種情況下,我們無需實現1個完整的irq_chip驅動,可以使用內核提供的通用irq_chip驅動架構irq_chip_generic,這樣只需要實現極少量的代碼,如arch/arm/mach-prima2/irq.c中,注冊CSR SiRFprimaII內部中斷控制器的代碼僅為:

26static __init void

27sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)

28{

29 struct irq_chip_generic *gc;

30 struct irq_chip_type *ct;

31

32 gc = irq_alloc_generic_chip("SIRFINTC", 1, irq_start, base, handle_level_irq);

33 ct = gc->chip_types;

34

35 ct->chip.irq_mask = irq_gc_mask_clr_bit;

36 ct->chip.irq_unmask = irq_gc_mask_set_bit;

37 ct->regs.mask = SIRFSOC_INT_RISC_MASK0;

38

39 irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 0);

40}

特別值得一提的是,目前多數主流ARM芯片,內部的一級中斷控制器都使用了ARM公司的GIC,我們幾乎不需要實現任何代碼,只需要在Device Tree中添加相關的結點並將gic_handle_irq()填入MACHINE的handle_irq成員。

如在arch/arm/boot/dts/exynos5250.dtsi即含有:

36 gic:interrupt-controller@10481000 {

37 compatible = "arm,cortex-a9-gic";

38 #interrupt-cells = <3>;

39 interrupt-controller;

40 reg = <0x10481000 0x1000>, <0x10482000 0x2000>;

41 };

而在arch/arm/mach-exynos/mach-exynos5-dt.c中即含有:

95DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")

96 /* Maintainer: Kukjin Kim <[email protected]> */

97 .init_irq = exynos5_init_irq,

98 .smp = smp_ops(exynos_smp_ops),

99 .map_io = exynos5250_dt_map_io,

100 .handle_irq = gic_handle_irq,

101 .init_machine = exynos5250_dt_machine_init,

102 .init_late = exynos_init_late,

103 .timer = &exynos4_timer,

104 .dt_compat = exynos5250_dt_compat,

105 .restart = exynos5_restart,

106MACHINE_END

Copyright © Linux教程網 All Rights Reserved