MPIC 与 PowerPC Linux 中断处理(2)
- UID
- 1066743
|
MPIC 与 PowerPC Linux 中断处理(2)
MPICMPIC 概述OpenPIC(OPEN Programmable Interrupt Controller) 是 AMD 和 Cyrix 联合开发的中断控制器规范。目前 PowerPC 系统多使用 MPIC (Multi-Processor Interrupt Controller)。MPIC 兼容 OpenPIC 并有所增强,主要包括增加了 #cint 和 #mcp 中断信号输出,更灵活的多处理器中断路由算法等。
PowerPC 处理器与 MPIC 的连接,中断信号的路由MPIC 的主要功能就是接受外设的中断信号或者 MPIC 本身产生的中断,按照一定的逻辑和次序,向各个处理器的管脚提交中断信号。
MPIC 本身也会产生中断,包括 IPI(Inter Processor Interrupt), MSI(Message Signaled Interrupt), MPIC Timer Interrupt 等。这些中断最后也是由 MPIC 送到处理器的 #int 引脚。对于处理器来说,它们也都是外部中断。关于这些中断及其使用,将另文介绍。
中断源编号无论是从 MPIC 外部引脚进来的中断,还是 MPIC 自身产生的中断,都有一个中断源编号,范围是 0~127。查阅芯片手册可以获得具体中断的中断源编号。
MPIC 编程接口中断源配置寄存器
每个中断源对应一套配置寄存器。它们的地址由中断源编号 n 决定。VPRn = VPR0 + 32 * n; DRn = VPRn + 16;
- VPR(Vector Priority Register), 中断向量优先级寄存器。包括 16 比特 Vector ( 即所谓硬件中断号,本文称为 MPIC 中断号 ),4 比特优先级和 1 比特屏蔽位,还有两个比特用来设置中断信号的极性与模式 ( 组合为上升缘,下降缘,高电平,低电平四种触发方式 )。中断源编号到 MPIC 中断号的映射,是通过对 VPR 编程来控制的。
- DR(Destination Register) 中断目的寄存器。P0~Pm 位用来指定该中断被定向到哪个处理器。
注意:对于 IPI 和 MPIC Timer 中断,DR 有多个比特被置 1,表示多播。一个中断被复制到多个处理器; 而对于来自外设的中断,DR 有多个比特被置 1,表示分布式分发,由 MPIC 进行路由选择,只将该中断发送给其中一个处理器。Freescale 的 QorIQ 系列平台的 MPIC, 都不支持分布式分发模式。
Per CPU 寄存器
每个处理器对应一套 Per CPU 寄存器。以下计算地址的公式中,n 代表处理器编号。
- CTPR(Current Task Priority Register)
地址为:CTPRn = MPIC_CPU_BASE+ 4096*n + 0x80
包含 4 比特优先级字段。OS 可以设置当前任务的优先级,只有中断源的优先级大于 CTPR,MPIC 才会向处理器提交中断信号。对于实时操作系统来说,这是一个非常友好的设计。不过 Linux 目前没有利用这一功能,所有 CTPR 都一直设为 0。
注意:中断源优先级等于 CTPR 时,不会被提交到处理器。CTPR 最小值是 0,所以把中断的优先级设为 0,相当于屏蔽该中断。
- IACK(Interrupt Ack Register)
地址为:IACKn = MPIC_CPU_BASE+ 4096*n + 0xa0
只读。处理器 n 响应一个外部中断,读 IACKn 就可以得到该中断的 MPIC 中断号 (Vector/Priority 寄存器的 Vector 字段 )。
地址为 EOIn = MPIC_CPU_BASE+ 4096*n + 0xb0
写 0 表示结束一个中断的处理。
全局寄存器
如 GCR 寄存器可以重启 MPIC, PIR 寄存器可以重启指定的处理器。
Linux 内核 MPIC 寄存器的定义
powerpc/include/asm/mpic.h 文件有以上寄存器地址偏移和各字段的比特掩码定义
MPIC 内部寄存器和流程另外还有几个 MPIC 内部的 Per CPU 寄存器,一般处理器无法访问它们,但是它们在 MPIC 内部处理流程中扮演了非常重要的角色。
- IPR(Interrupt Pending Register)
共 136 位,分别代表 136 个中断源。IPR 记录 MPIC 接收到中断信号,但还没有开始处理的中断。
- IRR(Interrupt Request Register)
IRR 只保存一个中断源编号。这个中断一定是 IPR 中优先级最高的,并且比处理器正在处理的中断 ( 如果有的话 ) 的优先级更高。
15 位。ISR 记录处理器开始处理 ( 读 IACK),但还没有处理完的中断 ( 写 EOI) 的优先级。MPIC 不允许优先级相同的中断互相嵌套,所以最多能嵌套的中断数就是有效优先级的数目 15。
MPIC 接收到一个中断信号以后,如果中断源寄存器的屏蔽位为 0,则根据 DR 的内容,将 IPR 对应比特置 1。例如,若 DR 的 P2 被置 1,表示该中断被定向到处理器 2,因此将 IPR2 的对应比特置 1。
IPR 中优先级最高的中断,如果其优先级大于 ISR 中的最高优先级,则将它的中断源编号,保存到 IRR,同时它的 MPIC 中断号被拷贝到 IACK 中。
如果 IRR 中断的优先级大于 CTPR,则向处理器 #int 引脚输出中断信号。( 注意此时有可能有其他中断正在处理中 )
如果处理器的 MSR[EE] 位为 1,则执行 ExternalInput 中断向量,读 IACK 得到 MPIC 中断号。对 IACK 的读操作,同时会翻转中断信号的电平极性;将 ISR 中的对应比特置 1,表示该级别中断正在被处理中;IPR 中对应的比特也会被清零。
中断处理程序执行完时,向 EOI 写 0。MPIC 将 ISR 中优先级最高的比特清零。
总结一下:
只有屏蔽位为零的中断,才能进入到 IPR。
只有更高优先级的中断都处理完之后,IPR 中的中断才能进入到 IRR。
只有 IRR 中断的优先级大于 CTPR 时,才能向处理器输出中断信号。
只有处理器的 MSR[EE] 位为 1,处理器才响应中断信号,执行外部中断向量。
一个中断存在于 ISR 中的时间范围是软件读 IACK 和写 EOI 之间。
ISR 的行为类似一个堆栈,并且压入堆栈的总是更高的优先级,弹出的总是当前最高优先级。实际上,在系统内存中的确有一个这样的堆栈跟 ISR 相对应,那就是中断处理程序嵌套而形成的堆栈。
要支持中断嵌套,需要在中断处理程序中把 MSR[EE] 设为 1。( 因为在进入中断向量之前,MSR[EE] 被处理器自动设置为 0)
对 PowerPC Linux 来说,在中断处理过程中,软件跟 MPIC 之间的交互就是读 IACK 和写 EOI。他们的实现请参考:
读 IACK1
2
3
| do_IRQ
ppc_md.get_irq()
==mpic.c::mpic_get_irq()
|
写 EOI1
2
3
4
5
6
| do_IRQ
handle_one_irq()
generic_handle_irq()
desc->handle_irq() = handle_xxxx_irq()
desc->irq_data.chip->irq_eoi()
==mpic.c::mpic_eoi()
|
中断号的映射有三种关于中断的序号:
由物理连接决定。它也是中断源配置寄存器的地址索引。
VPR 寄存器 Vector 字段的值,是由软件配置到 VPR 的,理论取值范围为 0~65535。Linux 配置 VPR 时,总是让 Vector 等于中断源编号。
在 Linux 中,一个外部中断用 irq_desc 结构来表示,用同名的数组 irq_desc[NR_IRQS] 来表示所有外部中断。这个数组的索引就是软件中断号。
软件中断号跟 MPIC 中断号往往并不相等。函数 irq_create_mapping 用来建立 MPIC 中断号到软件中断号的映射。软件中断号到 MPIC 中断号的映射保存在数组 irq_map 中。
virq_to_hw 宏用来从软件中断号得到 MPIC 中断号。
反向映射 ( 从 MPIC 中断号得到软件中断号 ) 有四种方式,分别是:
两者取值相同
两者并不相等,但是没有维护反向映射表。只能遍历 irq_map 来进行反向映射。
用数组 revmap 保存反向映射表。查询函数为 irq_linear_revmap。
用 radix tree 来保存反向映射表。查询函数为 irq_radix_revmap_lookup |
|
|
|
|
|