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