U-Boot 中增加 GIC-400中断服务程序
在 U-Boot 中为 GIC-400(GICv2 架构)添加中断服务程序需要以下关键步骤,这里提供完整实现指南:
步骤 1:启用 U-Boot 中断支持
在板级配置文件(如 include/configs/<board>.h
)中添加:
#define CONFIG_USE_IRQ // 启用中断支持
#define CONFIG_STACKSIZE_IRQ (8 * 1024) // IRQ 栈大小
#define CONFIG_STACKSIZE_FIQ (4 * 1024) // FIQ 栈大小
步骤 2:实现 GIC-400 驱动
创建 drivers/irq/gic400.c
:
#include <common.h>
#include <asm/io.h>
#include <asm/gic.h>// GIC-400 寄存器基址(根据 SoC 文档修改)
#define GICD_BASE 0x2C001000 // Distributor
#define GICC_BASE 0x2C002000 // CPU Interfacevoid gic400_init(void)
{// 1. 初始化 Distributorwritel(0x1, GICD_BASE + GICD_CTLR); // 使能全局分发// 设置所有中断到 CPU0for (int i = 0; i < 32; i += 4) {writel(0x01010101, GICD_BASE + GICD_ITARGETSR + i);}// 2. 初始化 CPU Interfacewritel(0x1, GICC_BASE + GICC_CTLR); // 使能 CPU 接口writel(0xF0, GICC_BASE + GICC_PMR); // 设置优先级阈值
}uint32_t gic400_read_iar(void)
{return readl(GICC_BASE + GICC_IAR);
}void gic400_write_eoir(uint32_t irq)
{writel(irq, GICC_BASE + GICC_EOIR);
}
步骤 3:实现中断处理框架
修改 arch/arm/lib/interrupts.c
:
#include <asm/gic.h>// 定义最大中断号
#define MAX_IRQS 160// 中断处理函数指针数组
static irq_handler_t irq_handlers[MAX_IRQS];// 注册中断处理函数
void irq_register_handler(int irq, irq_handler_t handler)
{if (irq < MAX_IRQS)irq_handlers[irq] = handler;
}// 主中断处理函数
void do_irq(struct pt_regs *pt_regs)
{uint32_t irq = gic400_read_iar();uint32_t irq_num = irq & 0x3FF; // 提取中断号if (irq_num < MAX_IRQS && irq_handlers[irq_num]) {irq_handlers[irq_num](); // 调用注册的处理函数} else {printf("Unhandled IRQ: %u\n", irq_num);}gic400_write_eoir(irq); // 结束中断
}
步骤 4:集成到启动流程
在板级初始化文件 board/<vendor>/<board>/<board>.c
中添加:
#include <asm/gic.h>void enable_interrupts(void)
{asm volatile("cpsie i"); // 使能 ARM 全局中断
}int interrupt_init(void)
{gic400_init(); // 初始化 GIC-400enable_interrupts(); // 使能中断return 0;
}
在启动序列中调用(修改 common/board_r.c
):
static int initr_interrupts(void)
{interrupt_init();return 0;
}INIT_FUNC_WATCHDOG_INIT(initr_interrupts, 0, int, 0)
步骤 5:注册具体中断服务程序
以 GPIO 中断为例(假设中断号=42):
// 在驱动文件中注册
void gpio_irq_handler(void)
{printf("GPIO interrupt triggered!\n");// 添加具体处理逻辑// 清除中断标志...
}void gpio_init(void)
{// 初始化 GPIO 控制器...irq_register_handler(42, gpio_irq_handler); // 注册中断// 在 GIC 中使能中断uint32_t reg_offset = 42 / 32;uint32_t bit_mask = 1 << (42 % 32);writel(bit_mask, GICD_BASE + GICD_ISENABLER + reg_offset * 4);
}
步骤 6:编译配置
修改
drivers/irq/Makefile
:obj-$(CONFIG_GIC_V2) += gic400.o
在 Kconfig 中添加配置项:
config GIC_V2bool "GIC-400 Interrupt Controller"depends on USE_IRQdefault y
关键验证步骤
中断触发测试:
=> mm 0xGPIO_REG_ADDR 0 0x1 # 模拟触发中断
观察控制台输出 "GPIO interrupt triggered!"
寄存器检查:
=> md.l 0x2C001000 10 # 查看 GICD 状态 => md.l 0x2C002000 10 # 查看 GICC 状态
栈溢出防护:
#define CONFIG_DEBUG_IRQ_STACK // 启用栈检查
常见问题解决
中断无法触发:
检查 GICD_CTLR[0] 和 GICC_CTLR[0] 是否置位
确认中断信号是否路由到 CPU0
验证向量表地址是否正确(
CONFIG_SYS_EXCEPTION_VECTORS_HIGH
)
中断丢失:
// 在 do_irq 开头添加 local_irq_disable(); // 防止嵌套 // ...处理中断... local_irq_enable(); // 退出前恢复
性能优化:
// 使用宏替代函数调用 #define QUICK_IRQ_HANDLER(name) \ static void __attribute__((naked)) name(void) \ { \asm volatile("b 1f"); \asm volatile("1: ..."); \ }
注意:U-Boot 中断处理应保持极简原则:
处理时间 < 100μs
避免内存分配/复杂函数调用
优先使用轮询模式处理复杂任务
关键操作完成后立即清除中断标志
完成上述步骤后,可通过 make menuconfig
启用 CONFIG_GIC_V2
并编译测试。实际中断号需根据 SoC 文档确认,典型值:
SPI 中断范围:32-1019
PPI 中断范围:16-31
SGI 中断范围:0-15