Linux芯片级移植与底层驱动(基于3.7.4内核) --中断控制器

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以前的体系结构,则是透过MRSMSR指令来读取和设置ARMCPSR寄存器。由此可见,local_irq_disable() local_irq_enable()针对的并不是外部的中断控制器,而是直接让CPU本身不响应中断请求。相关的实现位于arch/arm/include/asm/irqflags.h

 11#if__LINUX_ARM_ARCH__ >= 6

 12

 13static inline unsigned longarch_local_irq_save(void)

 14{

 15      unsigned long flags;

 16

 17      asm volatile(

 18             "      mrs    %0, cpsr       @ arch_local_irq_saven"

 19             "      cpsid  i"

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

 21      return flags;

 22}

 23

 24static inline voidarch_local_irq_enable(void)

 25{

 26      asm volatile(

 27             "      cpsie i              @ arch_local_irq_enable"

 28             :

 29             :

 30             : "memory", "cc");

 31}

 32

 33static inline voidarch_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

 49static inline unsigned longarch_local_irq_save(void)

 50{

 51      unsigned long flags, temp;

 52

 53      asm volatile(

 54             "      mrs    %0, cpsr       @ arch_local_irq_saven"

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

 56             "      msr    cpsr_c, %1"

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

 58             :

 59             : "memory", "cc");

 60      return flags;

 61}

 62

 63

 66static inline voidarch_local_irq_enable(void)

 67{

 68      unsigned long temp;

 69      asm volatile(

 70             "      mrs    %0, cpsr       @ arch_local_irq_enablen"

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

 72             "      msr    cpsr_c, %0"

 73             : "=r" (temp)

 74             :

 75             : "memory", "cc");

 76}

 77

 78

 81static inline voidarch_local_irq_disable(void)

 82{

 83      unsigned long temp;

 84      asm volatile(

 85             "      mrs    %0, cpsr       @ arch_local_irq_disablen"

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

 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结构体来描述中断控制器。该结构体内部封装了中断maskunmaskack等成员函数,其定义于include/linux/irq.h

303struct irq_chip {

304       const char     *name;

305       unsignedint    (*irq_startup)(structirq_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_chipsirfsoc_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};

我 们只实现了其中的ackmaskunmaskset_type成员函数,ack函数用于清中断,maskunmask用于中断屏蔽和取消中断屏 蔽、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控制器外围的4GPIO,每组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.cirq_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 createirqdomainn", 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 voidsirfsoc_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中,注册CSRSiRFprimaII内部中断控制器的代码仅为:

 26static __initvoid

 27sirfsoc_alloc_gc(void __iomem *base, unsignedint 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,我们几乎不需要实现任何代码,只需要在DeviceTree中添加相关的结点并将gic_handle_irq()填入MACHINEhandle_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 = <0x104810000x1000>, <0x10482000 0x2000>;

 41      };

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

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

 96      

 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

原文:http://tech.ddvip.com/2013-06/1370749911196976.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值