文章目录
- GIC 中断 Enable 和 Disable
- GICD_ISENABLER<n>
- GICD_ICENABLER<n>
- 参数 n
- 使用举例
- 代码实现
- 注意事项
GIC 中断 Enable 和 Disable
在ARMv8架构中,通用中断控制器(GIC)负责管理处理器的中断。为了控制和管理这些中断,GIC提供了一系列寄存器。其中,GICD_ISENABLER<n>
和 GICD_ICENABLER<n>
是两组关键的寄存器,用于启用和禁用中断。
Offset | Name | Description |
---|---|---|
0x0100-0x017C | GICD_ISENABLER<n> | Interrupt Set-Enable Registers |
0x0180-0x01FC | GICD_ICENABLER<n> | Interrupt Clear-Enable Registers |
GICD_ISENABLER
- 寄存器名称:Interrupt Set-Enable Registers
- 目的:用于启用中断。
- 描述:这些寄存器允许软件启用特定的中断。每个寄存器对应32个中断,每个位代表一个中断。写入
1
到某位会启用对应的中断,写入0
对该位没有影响(即,不能通过写入0
来禁用中断)。
GICD_ICENABLER
- 寄存器名称:Interrupt Clear-Enable Registers
- 目的:用于禁用中断。
- 描述:这些寄存器允许软件禁用特定的中断。与
GICD_ISENABLER<n>
类似,每个寄存器对应32个中断,但写入1
到某位会禁用对应的中断,写入0
对该位没有影响。
参数 n
参数<n>
代表寄存器的索引,根据系统支持的中断数量而变化。例如,如果GIC支持最多1020个中断,那么GICD_ISENABLER
和GICD_ICENABLER
寄存器会有32个(从GICD_ISENABLER0
到GICD_ISENABLER31
和从GICD_ICENABLER0
到GICD_ICENABLER31
),因为每个寄存器可以控制32个中断。
可以通过下面函数判断一个中断是否为SPI中断:
static uint32_t is_spi_irq(int irq)
{
return irq >= 32 && irq < 1020;
}
使用举例
假设我们要启用中断ID 68和禁用中断ID 95。
- 启用中断ID 68:
- 中断ID 68在
GICD_ISENABLER2
寄存器中,因为68/32 = 2
(整除)。 - 在
GICD_ISENABLER2
中,68对应的位是68 % 32 = 4
。 - 因此,通过向
GICD_ISENABLER2
写入1 << 4
(即,0x10
),可以启用中断ID 68。
- 中断ID 68在
GICD_ISENABLER2 |= (1 << 4);
- 禁用中断ID 95:
- 中断ID 95在
GICD_ICENABLER2
寄存器中,因为95/32 = 2
(整除)。 - 在
GICD_ICENABLER2
中,95对应的位是95 % 32 = 31
。 - 因此,通过向
GICD_ICENABLER2
写入1 << 31
(即,0x80000000
),可以禁用中断ID 95。
- 中断ID 95在
GICD_ICENABLER2 |= (1 << 31);
代码实现
下面代码展示了如何使用C代码实现 irq_enable
和 irq_disable
:
void irq_enable(int irq)
{
uint32_t addr;
if (!is_spi_irq(irq)) {
log_err("irq%d is not a spi irq\n", irq);
return;
}
/* Calculate enable register offset and bit position */
uint32_t reg_offset = irq / 32;
uint32_t reg_shift = irq % 32;
addr = GIC_ISENABLER_ADDRESS(GIC_DISTRIBUTOR_BASE, reg_offset);
write32(addr, (1 << reg_shift));
}
void irq_disable(int irq)
{
uint32_t addr;
if (!is_spi_irq(irq)) {
log_err("irq%d is not a spi irq\n", irq);
return;
}
uint32_t reg_offset = irq / 32;
uint32_t reg_shift = irq % 32;
addr = GIC_ICENABLER_ADDRESS(GIC_DISTRIBUTOR_BASE, reg_offset);
write32(addr, (1 << reg_shift));
}
注意事项
- 在实际编程中,对GIC寄存器的访问需要按照你的处理器和系统的特定要求来进行,这可能包拍使用特权指令或通过特定的内存映射地址访问这些寄存器。
- 操作这些寄存器时,需要确保不会意外地更改或启用/禁用其他中断,尤其是在对寄存器进行位操作时。