Exynos4412裸机开发系列教程--中断管理

中断系统对于任何一款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,欢迎探讨。







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值