μT-Kernel for Raspberry Pi おぼえがき(3)

IRQ ハンドラ

ARM の割込み(昔からの TRON の分類で言う「EIT の I」)は、コアでは外部の割込み要因の識別などをおこなったりせず、単一のハンドラを呼ぶようになっている。
AT91 では外部の割込みコントローラを使用しているのでハンドラのコードは簡単だが、Raspberry Pi はソフトウェアで処理しなければならないため、コードが多くなっている。
やはり kernel/sysdepend/device/app_raspib/icrt0.S の、最後に部分にコードがある。

	.global knl_irq_handler
knl_irq_handler:
	sub    lr, lr, #4
	stmfd  sp!, {lr}  /* sp-> lr_xxx */
	stmfd  sp!, {ip} /* sp-> ip, lr_xxx */
	mrs    ip, spsr
	stmfd  sp!, {ip} /* sp-> spsr_xxx, ip, lr_xxx */
	stmfd  sp!, {r3} /* sp-> r3, spsr_xxx, ip, lr_xxx */

	/*
	 * based on Broadcom BCM2835 Peripherals datasheet p. 111
	 */

#define BCM2708_PERI_BASE 0x20000000
#define BCM2708_ARM_BASE (BCM2708_PERI_BASE + 0xB000)
#define BCM2708_ARMCTRL_IC_BASE (BCM2708_ARM_BASE + 0x200)
#define BCM2708_ARM_IRQ_PEND0 (BCM2708_ARMCTRL_IC_BASE + 0x0)
#define BCM2708_ARM_IRQ_PEND1 (BCM2708_ARMCTRL_IC_BASE + 0x4)
#define BCM2708_ARM_IRQ_PEND2 (BCM2708_ARMCTRL_IC_BASE + 0x8)
#define BCM2708_ARM_IRQ0_BASE 64
#define BCM2708_ARM_IRQ1_BASE 0
#define BCM2708_ARM_IRQ2_BASE 32

	.macro	get_irqnr_preamble base, tmp
	ldr	\base, =ARMCTRL_IC_BASE
	.endm

	.macro	get_irqnr_and_base irqnr, irqstat, base, tmp
	ldr	\irqstat, [\base, #(ARM_IRQ_PEND0 - ARMCTRL_IC_BASE)] @ get masked status
	mov	\irqnr, #(ARM_IRQ0_BASE + 31)
	and	\tmp, \irqstat, #0x300		@ save bits 8 and 9
	bics	\irqstat, \irqstat, #0x300	@ clear bits 8 and 9, and test
	bne	1010f
	tst	\tmp, #0x100
	ldrne	\irqstat, [\base, #(ARM_IRQ_PEND1 - ARMCTRL_IC_BASE)]
	movne	\irqnr, #(ARM_IRQ1_BASE + 31)
	@ Mask out the interrupts also present in PEND0 - see SW-5809
	bicne	\irqstat, #((1<<7) | (1<<9) | (1<<10))
	bicne	\irqstat, #((1<<18) | (1<<19))
	bne	1010f
	tst	\tmp, #0x200
	ldrne	\irqstat, [\base, #(ARM_IRQ_PEND2 - ARMCTRL_IC_BASE)]
	movne	\irqnr, #(ARM_IRQ2_BASE + 31)
	@ Mask out the interrupts also present in PEND0 - see SW-5809
	bicne	\irqstat, #((1<<21) | (1<<22) | (1<<23) | (1<<24) | (1<<25))
	bicne	\irqstat, #((1<<30))
	beq	1020f
1010:
	@ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1))
	@ N.B. CLZ is an ARM5 instruction.
	sub	\tmp, \irqstat, #1
	eor	\irqstat, \irqstat, \tmp
	clz	\tmp, \irqstat
	sub	\irqnr, \tmp
1020: @ EQ will be set if no irqs pending
	.endm

	stmfd  sp!, {r0-r1}

	get_irqnr_preamble r0, r1
	get_irqnr_and_base lr, r3, r0, r1

	ldmfd	sp!, {r0-r1}

	ldr    ip, =Csym(knl_intvec) /* exception vector table */
	add    ip, ip, lr, LSL #2 /* ip := &vector[IRQ No.] */
	ldr    r3, [ip] /* r3 := vector[IRQ No.] */
	mov    lr, pc
	bx     r3

一旦マクロを定義してから使っているが、コメントにもある通りデータシートに示されているコードである。knl_intvec は機種共通部の kernel/sysdepend/device/app_raspib/devinit.c に実体がある。定数の定義については Raspberry Pi 用 Linuxソースコードlinux/arch/arm/mach-bcm2708/include/mach/platform.h も参考にした。