中断系统对于任何一款SOC都是最重要的,无论是微控制器还是应用处理器,没有中断,你将不能实时响应外部事件,不能精确计时,不能进行任务调度,所以,一个裸机程序不能操作中断,将没有任何实际使用价值,就是一个高级玩具,或者就是一为他人作嫁衣裳的bootloader,任何一套独立的适用系统都将包含中断功能,就像MMU对于安全操作系统而言,没有MMU就无法称之为安全操作系统,顶多算作一个多任务分配机,但你不能保证哪个应用在那儿使坏,对不。如果我是个SPY,没有那道门,我将变得无法也无天,哈哈。
ARM中断分为IRQ以及FIQ,这里我们只实现IRQ系统,FIQ与此类似,系统产生IRQ异常后,会根据异常向量表,进入相应的异常处理入口,在异常处理函数里,我们需要保存上下文,在处理完中段后,我们要恢复上下文,具体试下如下:
.global irq
irq:
/* get irq's sp */
ldr sp, _stack_irq_end
/* save user regs */
sub sp, sp, #72
stmia sp, {r0 - r12} /* calling r0-r12 */
add r8, sp, #60
stmdb r8, {sp, lr}^ /* calling sp, lr */
str lr, [r8, #0] /* save calling pc */
mrs r6, spsr
str r6, [r8, #4] /* save cpsr */
str r0, [r8, #8] /* save old_r0 */
mov r0, sp
/* do irqs routlines */
bl do_irqs
/* restore user regs */
ldmia sp, {r0 - lr}^ /* calling r0 - lr */
mov r0, r0
ldr lr, [sp, #60] /* get pc */
add sp, sp, #72
subs pc, lr, #4 /* return & move spsr_svc into cpsr */
在保护现场及恢复现场的中间,有一个调用C版的中断处理函数,而这就是我们今天的重点。为了能够实现中断管理,我们必须在函数里进行中断处理函数的回调,而这就必须通过框架来解决了,老规矩,先定义用户接口函数。
struct irq_handler_t {
void (*func)(void * data);
void * data;
};
typedef void (*interrupt_function_t)(void * data);
bool_t request_irq(const char * name, interrupt_function_t func, void * data);
bool_t free_irq(const char * name);
恩,貌似跟linux内核里申请中断差不多哈,没错,先进的思想都是同宗同源的,定义很漂亮,可又如何实现呢。跟时钟管理类似,我们用静态数组就可以实现注册中断了,动态的话,我们就需要通过链表来实现了,具体可参考xboot源码中断框架的实现。
bool_t request_irq(const char * name, interrupt_function_t func, void * data)
{
struct irq_t * irq = NULL;
int i;
if(!name || !func)
return FALSE;
for(i=0; i< ARRAY_SIZE(exynos4412_irqs); i++)
{
if(strcmp(exynos4412_irqs[i].name, name) == 0)
{
irq = &exynos4412_irqs[i];
break;
}
}
if(!irq)
return FALSE;
if(irq->handler->func != null_interrupt_function)
return FALSE;
irq->handler->func = func;
irq->handler->data = data;
if(irq->enable)
irq->enable(irq, TRUE);
return TRUE;
}
bool_t free_irq(const char * name)
{
struct irq_t * irq = NULL;
int i;
if(!name)
return FALSE;
for(i=0; i< ARRAY_SIZE(exynos4412_irqs); i++)
{
if(strcmp(exynos4412_irqs[i].name, name) == 0)
{
irq = &exynos4412_irqs[i];
break;
}
}
if(!irq)
return FALSE;
irq->handler->func = null_interrupt_function;
irq->handler->data = NULL;
if(irq->enable)
irq->enable(irq, FALSE);
return TRUE;
}
其实主体思想是申请中断时将原先的空函数,赋值为用户定义的中断处理函数,并绑定参数,释放时,再恢复为默认的空函数就可以,为了避免一格中断多次申请,在申请时检测是否已被占用,如果已占用则申请失败。那么我们又如何处理这么多中断呢。
每个中断控制器都有相应的寄存用于配置中断优先级,标识中断源,中断使能关闭,置中断pending标志等,Exynos4412采用的是ARM公司的GIC中断控制器PL390,其跟之前VIC中断控制器最大的区别是支持多核。
void do_irqs(struct pt_regs_t * regs)
{
u32_t irq;
/* Get irq's offset */
irq = readl(EXYNOS4412_GIC_CPU_BASE + GIC_CPU_INTACK) & 0x3ff;
/* Handle interrupt server function */
(exynos4412_irq_handler[irq - 32].func)(exynos4412_irq_handler[irq - 32].data);
/* Exit interrupt */
writel(EXYNOS4412_GIC_CPU_BASE + GIC_CPU_EOI, irq);
}
这里最关键的是读取当前的中断号,然后根据中断号,回调已注册的中断处理函数。
struct irq_t {
/* The irq name */
const char * name;
/* Interrupt number */
const int irq_no;
/* Irq handler */
struct irq_handler_t * handler;
/* Enable irq or not */
void (*enable)(struct irq_t * irq, bool_t enable);
};
static struct irq_handler_t exynos4412_irq_handler[160];
static void null_interrupt_function(void * data) { }
static void enable_irqs(struct irq_t * irq, bool_t enable)
{
u32_t mask = 1 << (irq->irq_no % 32);
if(enable)
writel(EXYNOS4412_GIC_DIST_BASE + GIC_DIST_ENABLE_SET + (irq->irq_no / 32) * 4, mask);
else
writel(EXYNOS4412_GIC_DIST_BASE + GIC_DIST_ENABLE_CLEAR + (irq->irq_no / 32) * 4, mask);
}
static struct irq_t exynos4412_irqs[] = {
{
.name = "INTG0",
.irq_no = 32,
.handler = &exynos4412_irq_handler[32 - 32],
.enable = enable_irqs,
}, {
.name = "INTG1",
.irq_no = 33,
.handler = &exynos4412_irq_handler[33 - 32],
.enable = enable_irqs,
}, {
.name = "INTG2",
.irq_no = 34,
.handler = &exynos4412_irq_handler[34 - 32],
.enable = enable_irqs,
}, {
.name = "INTG3",
.irq_no = 35,
.handler = &exynos4412_irq_handler[35 - 32],
.enable = enable_irqs,
}, {
.name = "INTG4",
.irq_no = 36,
.handler = &exynos4412_irq_handler[36 - 32],
.enable = enable_irqs,
}, {
.name = "INTG5",
.irq_no = 37,
.handler = &exynos4412_irq_handler[37 - 32],
.enable = enable_irqs,
}, {
.name = "INTG6",
.irq_no = 38,
.handler = &exynos4412_irq_handler[38 - 32],
.enable = enable_irqs,
}, {
.name = "INTG7",
.irq_no = 39,
.handler = &exynos4412_irq_handler[39 - 32],
.enable = enable_irqs,
}, {
.name = "INTG8",
.irq_no = 40,
.handler = &exynos4412_irq_handler[40 - 32],
.enable = enable_irqs,
}, {
.name = "INTG9",
.irq_no = 41,
.handler = &exynos4412_irq_handler[41 - 32],
.enable = enable_irqs,
}, {
.name = "INTG10",
.irq_no = 42,
.handler = &exynos4412_irq_handler[42 - 32],
.enable = enable_irqs,
}, {
.name = "INTG11",
.irq_no = 43,
.handler = &exynos4412_irq_handler[43 - 32],
.enable = enable_irqs,
}, {
.name = "INTG12",
.irq_no = 44,
.handler = &exynos4412_irq_handler[44 - 32],
.enable = enable_irqs,
}, {
.name = "INTG13",
.irq_no = 45,
.handler = &exynos4412_irq_handler[45 - 32],
.enable = enable_irqs,
}, {
.name = "INTG14",
.irq_no = 46,
.handler = &exynos4412_irq_handler[46 - 32],
.enable = enable_irqs,
}, {
.name = "INTG15",
.irq_no = 47,
.handler = &exynos4412_irq_handler[47 - 32],
.enable = enable_irqs,
}, {
.name = "EINT0",
.irq_no = 48,
.handler = &exynos4412_irq_handler[48 - 32],
.enable = enable_irqs,
}, {
.name = "EINT1",
.irq_no = 49,
.handler = &exynos4412_irq_handler[49 - 32],
.enable = enable_irqs,
}, {
.name = "EINT2",
.irq_no = 50,
.handler = &exynos4412_irq_handler[50 - 32],
.enable = enable_irqs,
}, {
.name = "EINT3",
.irq_no = 51,
.handler = &exynos4412_irq_handler[51 - 32],
.enable = enable_irqs,
}, {
.name = "EINT4",
.irq_no = 52,
.handler = &exynos4412_irq_handler[52 - 32],
.enable = enable_irqs,
}, {
.name = "EINT5",
.irq_no = 53,
.handler = &exynos4412_irq_handler[53 - 32],
.enable = enable_irqs,
}, {
.name = "EINT6",
.irq_no = 54,
.handler = &exynos4412_irq_handler[54 - 32],
.enable = enable_irqs,
}, {
.name = "EINT7",
.irq_no = 55,
.handler = &exynos4412_irq_handler[55 - 32],
.enable = enable_irqs,
}, {
.name = "EINT8",
.irq_no = 56,
.handler = &exynos4412_irq_handler[56 - 32],
.enable = enable_irqs,
}, {
.name = "EINT9",
.irq_no = 57,
.handler = &exynos4412_irq_handler[57 - 32],
.enable = enable_irqs,
}, {
.name = "EINT10",
.irq_no = 58,
.handler = &exynos4412_irq_handler[58 - 32],
.enable = enable_irqs,
}, {
.name = "EINT11",
.irq_no = 59,
.handler = &exynos4412_irq_handler[59 - 32],
.enable = enable_irqs,
}, {
.name = "EINT12",
.irq_no = 60,
.handler = &exynos4412_irq_handler[60 - 32],
.enable = enable_irqs,
}, {
.name = "EINT13",
.irq_no = 61,
.handler = &exynos4412_irq_handler[61 - 32],
.enable = enable_irqs,
}, {
.name = "EINT14",
.irq_no = 62,
.handler = &exynos4412_irq_handler[62 - 32],
.enable = enable_irqs,
}, {
.name = "EINT15",
.irq_no = 63,
.handler = &exynos4412_irq_handler[63 - 32],
.enable = enable_irqs,
}, {
.name = "EINT16_31",
.irq_no = 64,
.handler = &exynos4412_irq_handler[64 - 32],
.enable = enable_irqs,
}, {
.name = "C2C_SSCM0",
.irq_no = 65,
.handler = &exynos4412_irq_handler[65 - 32],
.enable = enable_irqs,
}, {
.name = "MDMA",
.irq_no = 66,
.handler = &exynos4412_irq_handler[66 - 32],
.enable = enable_irqs,
}, {
.name = "PDMA0",
.irq_no = 67,
.handler = &exynos4412_irq_handler[67 - 32],
.enable = enable_irqs,
}, {
.name = "PDMA1",
.irq_no = 68,
.handler = &exynos4412_irq_handler[68 - 32],
.enable = enable_irqs,
}, {
.name = "TIMER0",
.irq_no = 69,
.handler = &exynos4412_irq_handler[69 - 32],
.enable = enable_irqs,
}, {
.name = "TIMER1",
.irq_no = 70,
.handler = &exynos4412_irq_handler[70 - 32],
.enable = enable_irqs,
}, {
.name = "TIMER2",
.irq_no = 71,
.handler = &exynos4412_irq_handler[71 - 32],
.enable = enable_irqs,
}, {
.name = "TIMER3",
.irq_no = 72,
.handler = &exynos4412_irq_handler[72 - 32],
.enable = enable_irqs,
}, {
.name = "TIMER4",
.irq_no = 73,
.handler = &exynos4412_irq_handler[73 - 32],
.enable = enable_irqs,
}, {
.name = "INTG19",
.irq_no = 74,
.handler = &exynos4412_irq_handler[74 - 32],
.enable = enable_irqs,
}, {
.name = "WDT",
.irq_no = 75,
.handler = &exynos4412_irq_handler[75 - 32],
.enable = enable_irqs,
}, {
.name = "RTC_ALARM",
.irq_no = 76,
.handler = &exynos4412_irq_handler[76 - 32],
.enable = enable_irqs,
}, {
.name = "RTC_TIC",
.irq_no = 77,
.handler = &exynos4412_irq_handler[77 - 32],
.enable = enable_irqs,
}, {
.name = "GPIO_RT",
.irq_no = 78,
.handler = &exynos4412_irq_handler[78 - 32],
.enable = enable_irqs,
}, {
.name = "GPIO_LB",
.irq_no = 79,
.handler = &exynos4412_irq_handler[79 - 32],
.enable = enable_irqs,
}, {
.name = "INTG18",
.irq_no = 80,
.handler = &exynos4412_irq_handler[80 - 32],
.enable = enable_irqs,
}, {
.name = "IEM_APC",
.irq_no = 81,
.handler = &exynos4412_irq_handler[81 - 32],
.enable = enable_irqs,
}, {
.name = "IEM_IEC",
.irq_no = 82,
.handler = &exynos4412_irq_handler[82 - 32],
.enable = enable_irqs,
}, {
.name = "NFC",
.irq_no = 83,
.handler = &exynos4412_irq_handler[83 - 32],
.enable = enable_irqs,
}, {
.name = "UART0",
.irq_no = 84,
.handler = &exynos4412_irq_handler[84 - 32],
.enable = enable_irqs,
}, {
.name = "UART1",
.irq_no = 85,
.handler = &exynos4412_irq_handler[85 - 32],
.enable = enable_irqs,
}, {
.name = "UART2",
.irq_no = 86,
.handler = &exynos4412_irq_handler[86 - 32],
.enable = enable_irqs,
}, {
.name = "UART3",
.irq_no = 87,
.handler = &exynos4412_irq_handler[87 - 32],
.enable = enable_irqs,
}, {
.name = "G0_IRQ",
.irq_no = 89,
.handler = &exynos4412_irq_handler[89 - 32],
.enable = enable_irqs,
}, {
.name = "I2C0",
.irq_no = 90,
.handler = &exynos4412_irq_handler[90 - 32],
.enable = enable_irqs,
}, {
.name = "I2C1",
.irq_no = 91,
.handler = &exynos4412_irq_handler[91 - 32],
.enable = enable_irqs,
}, {
.name = "I2C2",
.irq_no = 92,
.handler = &exynos4412_irq_handler[92 - 32],
.enable = enable_irqs,
}, {
.name = "I2C3",
.irq_no = 93,
.handler = &exynos4412_irq_handler[93 - 32],
.enable = enable_irqs,
}, {
.name = "I2C4",
.irq_no = 94,
.handler = &exynos4412_irq_handler[94 - 32],
.enable = enable_irqs,
}, {
.name = "I2C5",
.irq_no = 95,
.handler = &exynos4412_irq_handler[95 - 32],
.enable = enable_irqs,
}, {
.name = "I2C6",
.irq_no = 96,
.handler = &exynos4412_irq_handler[96 - 32],
.enable = enable_irqs,
}, {
.name = "I2C7",
.irq_no = 97,
.handler = &exynos4412_irq_handler[97 - 32],
.enable = enable_irqs,
}, {
.name = "SPI0",
.irq_no = 98,
.handler = &exynos4412_irq_handler[98 - 32],
.enable = enable_irqs,
}, {
.name = "SPI1",
.irq_no = 99,
.handler = &exynos4412_irq_handler[99 - 32],
.enable = enable_irqs,
}, {
.name = "SPI2",
.irq_no = 100,
.handler = &exynos4412_irq_handler[100 - 32],
.enable = enable_irqs,
}, {
.name = "G1_IRQ",
.irq_no = 101,
.handler = &exynos4412_irq_handler[101 - 32],
.enable = enable_irqs,
}, {
.name = "UHOST",
.irq_no = 102,
.handler = &exynos4412_irq_handler[102 - 32],
.enable = enable_irqs,
}, {
.name = "HSOTG",
.irq_no = 103,
.handler = &exynos4412_irq_handler[103 - 32],
.enable = enable_irqs,
}, {
.name = "GPIO_C2C",
.irq_no = 104,
.handler = &exynos4412_irq_handler[104 - 32],
.enable = enable_irqs,
}, {
.name = "HSMMC0",
.irq_no = 105,
.handler = &exynos4412_irq_handler[105 - 32],
.enable = enable_irqs,
}, {
.name = "HSMMC1",
.irq_no = 106,
.handler = &exynos4412_irq_handler[106 - 32],
.enable = enable_irqs,
}, {
.name = "HSMMC2",
.irq_no = 107,
.handler = &exynos4412_irq_handler[107 - 32],
.enable = enable_irqs,
},{
.name = "HSMMC3",
.irq_no = 108,
.handler = &exynos4412_irq_handler[108 - 32],
.enable = enable_irqs,
}, {
.name = "SDMMC",
.irq_no = 109,
.handler = &exynos4412_irq_handler[109 - 32],
.enable = enable_irqs,
}, {
.name = "MIPI_CSI_4LANE",
.irq_no = 110,
.handler = &exynos4412_irq_handler[110 - 32],
.enable = enable_irqs,
}, {
.name = "MIPI_DSI_4LANE",
.irq_no = 111,
.handler = &exynos4412_irq_handler[111 - 32],
.enable = enable_irqs,
}, {
.name = "MIPI_CSI_2LANE",
.irq_no = 112,
.handler = &exynos4412_irq_handler[112 - 32],
.enable = enable_irqs,
}, {
.name = "ROTATOR",
.irq_no = 115,
.handler = &exynos4412_irq_handler[115 - 32],
.enable = enable_irqs,
}, {
.name = "FIMC0",
.irq_no = 116,
.handler = &exynos4412_irq_handler[116 - 32],
.enable = enable_irqs,
},{
.name = "FIMC1",
.irq_no = 117,
.handler = &exynos4412_irq_handler[117 - 32],
.enable = enable_irqs,
}, {
.name = "FIMC2",
.irq_no = 118,
.handler = &exynos4412_irq_handler[118 - 32],
.enable = enable_irqs,
}, {
.name = "FIMC3",
.irq_no = 119,
.handler = &exynos4412_irq_handler[119 - 32],
.enable = enable_irqs,
}, {
.name = "JPEG",
.irq_no = 120,
.handler = &exynos4412_irq_handler[120 - 32],
.enable = enable_irqs,
}, {
.name = "G2D",
.irq_no = 121,
.handler = &exynos4412_irq_handler[121 - 32],
.enable = enable_irqs,
}, {
.name = "ISP0",
.irq_no = 122,
.handler = &exynos4412_irq_handler[122 - 32],
.enable = enable_irqs,
}, {
.name = "MIXER",
.irq_no = 123,
.handler = &exynos4412_irq_handler[123 - 32],
.enable = enable_irqs,
},{
.name = "HDMI",
.irq_no = 124,
.handler = &exynos4412_irq_handler[124 - 32],
.enable = enable_irqs,
}, {
.name = "HDMI_I2C",
.irq_no = 125,
.handler = &exynos4412_irq_handler[125 - 32],
.enable = enable_irqs,
}, {
.name = "MFC",
.irq_no = 126,
.handler = &exynos4412_irq_handler[126 - 32],
.enable = enable_irqs,
}, {
.name = "ISP1",
.irq_no = 127,
.handler = &exynos4412_irq_handler[127 - 32],
.enable = enable_irqs,
}, {
.name = "AUDIO_SS",
.irq_no = 128,
.handler = &exynos4412_irq_handler[128 - 32],
.enable = enable_irqs,
}, {
.name = "I2S0",
.irq_no = 129,
.handler = &exynos4412_irq_handler[129 - 32],
.enable = enable_irqs,
}, {
.name = "I2S1",
.irq_no = 130,
.handler = &exynos4412_irq_handler[130 - 32],
.enable = enable_irqs,
},{
.name = "I2S2",
.irq_no = 131,
.handler = &exynos4412_irq_handler[131 - 32],
.enable = enable_irqs,
}, {
.name = "AC97",
.irq_no = 132,
.handler = &exynos4412_irq_handler[132 - 32],
.enable = enable_irqs,
}, {
.name = "PCM0",
.irq_no = 133,
.handler = &exynos4412_irq_handler[133 - 32],
.enable = enable_irqs,
}, {
.name = "PCM1",
.irq_no = 134,
.handler = &exynos4412_irq_handler[134 - 32],
.enable = enable_irqs,
}, {
.name = "PCM2",
.irq_no = 135,
.handler = &exynos4412_irq_handler[135 - 32],
.enable = enable_irqs,
}, {
.name = "SPDIF",
.irq_no = 136,
.handler = &exynos4412_irq_handler[136 - 32],
.enable = enable_irqs,
}, {
.name = "FIMC_LITE0",
.irq_no = 137,
.handler = &exynos4412_irq_handler[137 - 32],
.enable = enable_irqs,
},{
.name = "FIMC_LITE1",
.irq_no = 138,
.handler = &exynos4412_irq_handler[138 - 32],
.enable = enable_irqs,
}, {
.name = "INTG16",
.irq_no = 139,
.handler = &exynos4412_irq_handler[139 - 32],
.enable = enable_irqs,
}, {
.name = "INTG17",
.irq_no = 140,
.handler = &exynos4412_irq_handler[140 - 32],
.enable = enable_irqs,
}, {
.name = "KEYPAD",
.irq_no = 141,
.handler = &exynos4412_irq_handler[141 - 32],
.enable = enable_irqs,
}, {
.name = "PMU",
.irq_no = 142,
.handler = &exynos4412_irq_handler[142 - 32],
.enable = enable_irqs,
}, {
.name = "GPS",
.irq_no = 143,
.handler = &exynos4412_irq_handler[143 - 32],
.enable = enable_irqs,
}, {
.name = "SSS",
.irq_no = 144,
.handler = &exynos4412_irq_handler[144 - 32],
.enable = enable_irqs,
},{
.name = "SLIMBUS",
.irq_no = 145,
.handler = &exynos4412_irq_handler[145 - 32],
.enable = enable_irqs,
}, {
.name = "CEC",
.irq_no = 146,
.handler = &exynos4412_irq_handler[146 - 32],
.enable = enable_irqs,
}, {
.name = "TSI",
.irq_no = 147,
.handler = &exynos4412_irq_handler[147 - 32],
.enable = enable_irqs,
}, {
.name = "C2C_SSCM1",
.irq_no = 148,
.handler = &exynos4412_irq_handler[148 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQPMU",
.irq_no = 149,
.handler = &exynos4412_irq_handler[149 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQPPMMU0",
.irq_no = 150,
.handler = &exynos4412_irq_handler[150 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQPPMMU1",
.irq_no = 151,
.handler = &exynos4412_irq_handler[151 - 32],
.enable = enable_irqs,
},{
.name = "G3D_IRQPPMMU2",
.irq_no = 152,
.handler = &exynos4412_irq_handler[152 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQPPMMU3",
.irq_no = 153,
.handler = &exynos4412_irq_handler[153 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQGPMMU",
.irq_no = 154,
.handler = &exynos4412_irq_handler[154 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQPP0",
.irq_no = 155,
.handler = &exynos4412_irq_handler[155 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQPP1",
.irq_no = 156,
.handler = &exynos4412_irq_handler[156 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQPP2",
.irq_no = 157,
.handler = &exynos4412_irq_handler[157 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQPP3",
.irq_no = 158,
.handler = &exynos4412_irq_handler[158 - 32],
.enable = enable_irqs,
}, {
.name = "G3D_IRQGP",
.irq_no = 159,
.handler = &exynos4412_irq_handler[159 - 32],
.enable = enable_irqs,
},
};
当然为了要能够让中断控制器正常工作,首先得初始化。初始化代码如下:
static void gic_dist_init(physical_addr_t dist)
{
u32_t gic_irqs;
u32_t cpumask;
u32_t i;
writel(dist + GIC_DIST_CTRL,0);
/*
* Find out how many interrupts are supported.
* The GIC only supports up to 1020 interrupt sources.
*/
gic_irqs = readl(dist + GIC_DIST_CTR) & 0x1f;
gic_irqs = (gic_irqs + 1) * 32;
if(gic_irqs > 1020)
gic_irqs = 1020;
/*
* Set all global interrupts to be level triggered, active low.
*/
for(i = 32; i < gic_irqs; i += 16)
writel(dist + GIC_DIST_CONFIG + i * 4 / 16, 0);
/*
* Set all global interrupts to this CPU only.
*/
cpumask = 1 << get_cpuid();
cpumask |= cpumask << 8;
cpumask |= cpumask << 16;
for(i = 32; i < gic_irqs; i += 4)
writel(dist + GIC_DIST_TARGET + i * 4 / 4, cpumask);
/*
* Set priority on all global interrupts.
*/
for(i = 32; i < gic_irqs; i += 4)
writel(dist + GIC_DIST_PRI + i * 4 / 4, 0xa0a0a0a0);
/*
* Disable all interrupts. Leave the PPI and SGIs alone
* as these enables are banked registers.
*/
for(i = 32; i < gic_irqs; i += 32)
writel(dist + GIC_DIST_ENABLE_CLEAR + i * 4 / 32, 0xffffffff);
writel(dist + GIC_DIST_CTRL, 0x1);
}
static void gic_cpu_init(physical_addr_t dist, physical_addr_t cpu)
{
int i;
/*
* Deal with the banked PPI and SGI interrupts - disable all
* PPI interrupts, ensure all SGI interrupts are enabled.
*/
writel(dist + GIC_DIST_ENABLE_CLEAR, 0xffff0000);
writel(dist + GIC_DIST_ENABLE_SET, 0x0000ffff);
/*
* Set priority on PPI and SGI interrupts
*/
for(i = 0; i < 32; i += 4)
writel(dist + GIC_DIST_PRI + i * 4 / 4, 0xa0a0a0a0);
writel(cpu + GIC_CPU_PRIMASK, 0xf0);
writel(cpu + GIC_CPU_CTRL, 0x1);
}
static void combiner_init(physical_addr_t comb)
{
int i;
for(i = 0; i < 5; i++)
writel(comb + COMBINER_ENABLE_CLEAR + i * 0x10, 0xffffffff);
}
void exynos4412_irq_initial(void)
{
int i;
gic_dist_init(EXYNOS4412_GIC_DIST_BASE);
gic_cpu_init(EXYNOS4412_GIC_DIST_BASE, EXYNOS4412_GIC_CPU_BASE);
combiner_init(EXYNOS4412_COMBINER_BASE);
for(i = 0; i < ARRAY_SIZE(exynos4412_irqs); i++)
{
exynos4412_irqs[i].handler->func = null_interrupt_function;
exynos4412_irqs[i].handler->data = NULL;
}
/* Enable vector interrupt controller */
vic_enable();
/* Enable irq and fiq */
irq_enable();
fiq_enable();
}
具体的寄存器配置,代码中已有注释,可参考PL390手册进行分析,简要概括,分为三种类型中断,SGI,PPI以及SPI,裸机主要涉及到SPI。最后来个演示代码,怎么调用呢,以定时器为例,我们需要产生格100HZ的TICK中断,代码如下:
volatile u32_t jiffies = 0;
static u32_t tick_hz = 0;
static void timer_interrupt(void * data)
{
/* tick count */
jiffies++;
/* Clear interrupt status bit */
writel(EXYNOS4412_TINT_CSTAT, (readl(EXYNOS4412_TINT_CSTAT) & ~(0x1f<<5)) | (0x01<<9));
}
void exynos4412_tick_initial(void)
{
u64_t pclk;
if(!clk_get_rate("pclk", &pclk))
return;
if(!request_irq("TIMER4", timer_interrupt, 0))
return;
/* Using pwm timer 4, prescaler for timer 4 is 16 */
writel(EXYNOS4412_TCFG0, (readl(EXYNOS4412_TCFG0) & ~(0xff<<8)) | (0x0f<<8));
/* Select mux input for pwm timer4 is 1/2 */
writel(EXYNOS4412_TCFG1, (readl(EXYNOS4412_TCFG1) & ~(0xf<<16)) | (0x01<<16));
/* Load value for 10 ms timeout */
writel(EXYNOS4412_TCNTB4, (u32_t)(pclk / (2 * 16 * 100)));
/* Auto load, manaual update of timer 4 and stop timer4 */
writel(EXYNOS4412_TCON, (readl(EXYNOS4412_TCON) & ~(0x7<<20)) | (0x06<<20));
/* Enable timer4 interrupt and clear interrupt status bit */
writel(EXYNOS4412_TINT_CSTAT, (readl(EXYNOS4412_TINT_CSTAT) & ~(0x1<<4)) | (0x01<<4) | (0x01<<9));
/* Start timer4 */
writel(EXYNOS4412_TCON, (readl(EXYNOS4412_TCON) & ~(0x7<<20)) | (0x05<<20));
/* initial system tick */
tick_hz = 100;
jiffies = 0;
}
有了TICK中断实现,我们就可以实现精确延时,甚至定时器任务管理。此系列教程重点阐述各种实现思想以及用户接口,对于具体的寄存器的含义,各位可以参考用户手册,我想那个比谁都讲述得好而且准确全面。有任何疑问的朋友,可以留言,或者加QQ8192542,欢迎探讨。