第20章 Linux芯片级移植及底层驱动之中断控制器驱动

20.3 中断控制器驱动

    在Linux内核中,各个设备驱动可以调用request_irq()、enable_irq()、disable_irq()、local_irq_disable()、local_irq_enable()等通用API来完成中断申请、使能、禁止等功能。在将Linux移

植到新的SoC时,芯片供应商需要提供该部分API的底层支持。

    local_irq_disable()、local_irq_enable()的实现与具体中断控制器无关,对于ARM v6以上的体系结

构,是直接调用CPSID/CPSIE指令进行,对于ARM v6以前的体系结构,则是通过MRS/MSR指令来读取和设置ARM的CPSR寄存器。由此可见,local_irq_disable()、local_irq_enable()针对的并不是外部的中断控制器,而是直接让CPU本身不响应中断请求。相关的实现位于arch/arm/include/asm/irqflags.h,如代码清单20.3所示。

代码清单20.3 ARM Linux local_irq_disable()/enable()底层实现(C内嵌汇编)

#if __LINUX_ARM_ARCH__ >= 6

static inline unsigned long arch_local_irq_save(void)
{
        unsigned long flags;

        asm volatile(
                "       mrs     %0, " IRQMASK_REG_NAME_R "      @ arch_local_irq_save\n"
                "       cpsid   i"
                : "=r" (flags) : : "memory", "cc");
        return flags;

}

static inline void arch_local_irq_enable(void)
{
        asm volatile(
                "       cpsie i                 @ arch_local_irq_enable"
                :
                :
                : "memory", "cc");
}

static inline void arch_local_irq_disable(void)
{
        asm volatile(
                "       cpsid i                 @ arch_local_irq_disable"
                :
                :
                : "memory", "cc");
}

#else

/*
 * Save the current interrupt enable state & disable IRQs

 */

static inline unsigned long arch_local_irq_save(void)
{
        unsigned long flags, temp;

        asm volatile(
                "       mrs     %0, cpsr        @ arch_local_irq_save\n"
                "       orr     %1, %0, #128\n"
                "       msr     cpsr_c, %1"
                : "=r" (flags), "=r" (temp)
                :
                : "memory", "cc");
        return flags;
}

/*
 * Enable IRQs
 */
static inline void arch_local_irq_enable(void)
{
        unsigned long temp;
        asm volatile(
                "       mrs     %0, cpsr        @ arch_local_irq_enable\n"
                "       bic     %0, %0, #128\n"
                "       msr     cpsr_c, %0"
                : "=r" (temp)
                :
                : "memory", "cc");
}

/*
 * Disable IRQs
 */
static inline void arch_local_irq_disable(void)
{
        unsigned long temp;
        asm volatile(
                "       mrs     %0, cpsr        @ arch_local_irq_disable\n"
                "       orr     %0, %0, #128\n"
                "       msr     cpsr_c, %0"
                : "=r" (temp)
                :
                : "memory", "cc");
}

#endif

        与local_irq_disable()和local_irq_enable()不同,disable_irq()和enable_irq()针对的则是中断控制器,因此disable_irq()和enable_irq()适用的对象是某个中断。disable_irq()的字面意思是暂时屏蔽掉某中断(其实在内核的实现层面上做了延后屏蔽),直到enable_irq()后再执行ISR。实际上,屏蔽中断可以发生在外设、中断控制器、CPU三个位置,如图20.3所示。


    对于外设端,是从源头上就不产生中断信号给中断控制器,由于外设端高度依赖于外设于本身,所以Linux不提供标准的API而是由外设的驱动直接读写自身的寄存器

    在内核中,使用irq_chip结构体来描述中断控制器。该结构体内部封装了中断mask、unmask、ack等成员函数,其定义于include/linux/irq.h中,如代码清单20.4所示。

代码清单20.4 中断控制器irq_chip结构体

struct irq_chip {
        struct device   *parent_device;
        const char      *name;
        unsigned int    (*irq_startup)(struct irq_data *data);
        void            (*irq_shutdown)(struct irq_data *data);
        void            (*irq_enable)(struct irq_data *data);
        void            (*irq_disable)(struct irq_data *data);

        void            (*irq_ack)(struct irq_data *data);// 清
        void            (*irq_mask)(struct irq_data *data);//中断屏蔽
        void            (*irq_mask_ack)(struct irq_data *data);//取消中断屏蔽
        void            (*irq_unmask)(struct i

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值