往期内容
本文章相关专栏往期内容,PCI/PCIe子系统专栏:
- 嵌入式系统的内存访问和总线通信机制解析、PCI/PCIe引入
- 深入解析非桥PCI设备的访问和配置方法
- PCI桥设备的访问方法、软件角度讲解PCIe设备的硬件结构
- 深入解析PCIe设备事务层与配置过程
- PCIe的三种路由方式
- PCI驱动与AXI总线框架解析(RK3399)
- 深入解析PCIe地址空间与寄存器机制:从地址映射到TLP生成的完整流程
- PCIe_Host驱动分析_地址映射
- PCIe_Host驱动分析_设备枚举
Uart子系统专栏:
- 专栏地址:Uart子系统
- Linux内核早期打印机制与RS485通信技术
-- 末片,有专栏内容观看顺序interrupt子系统专栏:
- 专栏地址:interrupt子系统
- Linux 链式与层级中断控制器讲解:原理与驱动开发
-- 末片,有专栏内容观看顺序pinctrl和gpio子系统专栏:
专栏地址:pinctrl和gpio子系统
编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用
-- 末片,有专栏内容观看顺序
input子系统专栏:
- 专栏地址:input子系统
- input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
-- 末片,有专栏内容观看顺序I2C子系统专栏:
- 专栏地址:IIC子系统
- 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
-- 末篇,有专栏内容观看顺序总线和设备树专栏:
- 专栏地址:总线和设备树
- 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
-- 末篇,有专栏内容观看顺序
目录
- 往期内容
- 资料
- 1.PCI设备的INTx中断机制
- 2.PCIe设备的INTx中断机制
- 3.MSI中断机制
-
- [3.1 Capabilities Pointer](#3.1 Capabilities Pointer)
- [3.2 MSI Capability](#3.2 MSI Capability)
资料
参考资料:
- 《PCI_SPEV_V3_0.pdf》6.8节
- PCIe中MSI和MSI-X中断机制
- PCIe学习笔记之MSI/MSI-x中断及代码分析
- msix中断分析
- MSI中断与Linux实现
- ARM GICv3中断控制器
开发板资料:
分析的文件:
linux-4.4_rk3399\drivers\pci\host\pcie-rockchip.c
1.PCI设备的INTx中断机制
PCI设备是可以通过INTA#引脚喝PCIe设备进行连接的,有PCIe硬件来向控制器传递中断,这种和GPIO引脚的中断传递方式差不多。PCI总线上有INTA#~INTD#这些真实存在的引脚。
在PCI设备的配置空间,它声明:通过INTA#、INTB#、INTC#还是INTD#发出中断。
配置空间有2个寄存器:Interrupt Pin、Interrupt Line,作用如下:
- Interrupt Pin:用来表示本设备通过哪条引脚发出中断信号,取值如下
Interrupt Pin取值 | 含义 |
---|---|
0 | 不需要中断引脚 |
1 | 通过INTA#发出中断 |
2 | 通过INTB#发出中断 |
3 | 通过INTC#发出中断 |
4 | 通过INTD#发出中断 |
5~0xff | 保留 |
- Interrupt Line:给软件使用的,PCI设备本身不使用该寄存器。软件可以写入中断相关的信息,比如在Linux系统中,可以把分配的virq(虚拟中断号)写入此寄存器。软件完全可以自己记录中断信息,没必要依赖这个寄存器。
INTx中断是电平触发,处理过程如下:
- PCI设备发出中断:让INTx引脚变低
- 软件处理中断,清除中断:写PCI设备的某个寄存器,导致PCI设备取消中断
- PCI设备取消中断:让INTx引脚变高
2.PCIe设备的INTx中断机制
看接口图可以看出,PCIe设备是没有像PCI设备那样用INTx# 引脚(PCIE总线上并没有这些引脚)来和PCIe控制器进行连接从而达到传递中断的目的,但是一般情况下PCIe设备的驱动程序一般都是兼容PCI设备的,那么是怎么兼容的? --- 发出特殊的TPL
PCIe 是完全向后兼容 PCI 的,因此即使没有物理的 INTx# 引脚 ,PCIe 仍然可以通过软件和消息的方式模拟这些传统中断,以支持那些依赖于 INTx# 中断的 PCI 驱动程序。具体来说,PCIe 使用的是消息传递而非物理中断引脚。
TLP头部中,Message Code被用来区分发出的是哪类TLP包,为例"INTx模拟",有两类TLP包:
- Assert_INTx -- 发出
- Deassert_INTx -- 取消
跟传统PCI设备类似,这个"INTx模拟"的处理过程也是一样的:
- PCIe设备发出中断:发出Assert_INTx的TLP包
- 软件处理中断,清除中断:写PCIe设备的某个寄存器,导致PCIe设备取消中断
- PCIe设备取消中断:发出Deassert_INTx的TLP包
硬件框图如下:
对于软件开发人员来说,他感觉不到变化:
- PCI设备通过真实的引脚传输中断
- PCIe设备通过TLP实现虚拟的引脚传输中断
3.MSI中断机制
从上图可以看出,传统的INTx#中断,比如PCI系统,由于是并行连接,因此如果有多个PCI设备的话,他们就得共享这四条INTA#~INTD#这四条真实的中断引脚才能连接同一个PCI控制器,中断效率很低;同样对于PCIe系统,PCIe设备只能发出 INTA#~INTD# 四种TPL包。为此引入了新的中断机制:MSI,Message Signaled Interrupts。
在初始PCI设备时,可以告诉它一个地址(主控芯片的地址)、一个数据:
- PCI设备想发出中断时,往这个地址写入这个数据就可以触发中断
- 软件读到这个数据,就知道是哪个设备发出中断了
流程及硬件框图如下:
设备发出 MSI/MSI-X 中断时,会通过**向一个特定的地址(pci_addr > cpu_addr)写入数据(value)**来触发中断。
-
cpu_addr 是内存中的某个地址,通常是一个分配给中断控制器(例如 GIC,Generic Interrupt Controller)的地址,或是 PCI/PCIe 控制器的某个特定寄存器地址,当然,分配给它们肯定是需要映射为pci_addr
-
当设备触发中断时,会将 value 数据写入 pci_addr ,pci_addr又是cpu_addr的映射,会写入到cpu_addr,这使得 CPU 中的中断控制器(如 GIC)检测到这个写操作,并生成中断。
-
- cpu_addr:可以是PCI/PCIe控制器也可以是GIC的地址。往前者写入value,其会进一步上报给GIC;后者则可以直接往GIC写入value,触发中断
-
而pci_addr 和value的地址范围 在初始化设备时就写入了设备配置空间中Capabilities Pointer 相应的Capability 结构(比如 MSI Capability)
3.1 Capabilities Pointer
capability的意思是"能力",PCI/PCIe设备可以提供各种能力,所以在配置空间里有寄存器来描述这些capability:
- 配置空间里有第1个capability的位置:Capabilities Pointer
- 它指向第1个capability的多个寄存器,这些寄存器也是在配置空间里
- 第1个capability的寄存器里,也会指示第2个capability在哪里
Capability示例图如下:
-
配置空间0x34位置,存放的是第1个capability的位置:假设是 A4H
-
在配置空间0xA4位置,找到第1个capability,capability的寄存器有如下约定
- 第1个字节表示ID,每类capability都有自己的ID
- 第2个字节表示下一个capability的位置,如果等于0表示这是最后一个capability
- 其他寄存器由capability决定,所占据的寄存器数量由capability决定
-
第1个capability里面,它表示下一个capability在5CH
-
在配置空间0x5C位置,找到第2个capability
- 第1个字节表示ID,第2个字节表示下一个capability的位置(图里是E0H)
- 其他字节由capability本身决定
-
在配置空间0xE0位置,找到第3个capability
- 第1个字节表示ID
- 第2个字节表示下一个capability的位置,这里是00H表示没有更多的capability了
- 其他字节由capability本身决定
假设Capability X就是MSI Capability类型的结构,那么pci_addr和value就存在这里面,同时也代表了这个设备支持MSI
- Message Address:设备触发 MSI 中断时要写入的地址。
- Message Data:设备触发中断时写入的数据。
\1. MSI Capability (Message Signaled Interrupts)
- ID: 0x05
- 用于支持 Message Signaled Interrupts (MSI),它允许设备通过写内存的方式来触发中断,而不是传统的 INTx 引脚。
\2. MSI-X Capability
- ID: 0x11
- 是 MSI 的扩展版本,支持多个中断向量,可以为不同的事件触发不同的中断向量,从而提高系统中断处理的并行性和性能。
\3. PCI Power Management Capability
- ID: 0x01
- 提供设备的电源管理功能,支持各种电源状态(如 D0、D1、D2、D3),允许操作系统和驱动程序控制设备的功耗,进入低功耗模式或唤醒。
\4. AGP Capability (Accelerated Graphics Port)
- ID: 0x02
- 用于 AGP 设备,主要出现在老式图形处理器中,定义了设备如何通过 AGP 接口进行内存访问和加速图形处理。
\5. PCI Express Capability
- ID: 0x10
- 用于 PCI Express 设备,描述了设备的 PCIe 相关特性和能力。它是 PCIe 设备的一个重要 Capability,包含有关链路状态、链路速度、设备功能等信息。
\6. Vital Product Data (VPD) Capability
- ID: 0x03
- 允许存储设备的产品相关数据,如序列号、制造商信息等。这些信息通常是不可变的,用于识别设备。
\7. Hot-Plug Capability
- ID: 0x0F
- 用于支持热插拔功能的 PCIe 设备,允许在不关闭系统的情况下动态地插入和移除设备。这个 Capability 通常会出现在支持热插拔功能的 PCIe 插槽或设备中。
\8. Vendor-Specific Capability
- ID: 0x09
- 一些厂商定义的特定功能。每个厂商可以根据需求定制自己的 Capability,用于实现特定的硬件功能或扩展特性。
\9. SATA Capability
- ID: 0x12
- 出现在支持 SATA 接口的设备中,提供了有关 SATA 控制器的扩展特性,包含 SATA 端口的信息。
\10. Advanced Error Reporting (AER) Capability
- ID: 0x13
- 用于 PCI Express 设备,提供高级错误报告功能,允许设备报告和记录错误事件,支持更强的错误检测和错误处理机制。
\11. Slot Identification Capability
- ID: 0x04
- 用于表示设备所在的物理插槽,尤其是在多插槽的系统中有用,可以帮助操作系统识别设备所在的具体位置。
\12. HyperTransport Capability
- ID: 0x08
- 用于支持 HyperTransport 总线的设备,HyperTransport 是一种高速、低延迟的点对点链路技术,主要用于 CPU 和其他高速外设之间的通信。
\13. PCI-X Capability
- ID: 0x07
- 出现在支持 PCI-X 标准的设备中,描述了设备的 PCI-X 特性,主要用于服务器和工作站等需要更高带宽的场景。
\14. Latency Tolerance Reporting (LTR) Capability
- ID: 0x18
- 用于报告设备的延迟容忍度信息,帮助系统优化功耗和性能之间的平衡,尤其在低功耗系统中比较常见。
\15. Resizable BAR Capability
- ID: 0x15
- 允许设备调整其基址寄存器(BAR)的大小,支持更大内存映射区域,这在高性能 GPU 或需要大量内存映射的设备中很有用。
\16. Access Control Services (ACS) Capability
- ID: 0x0D
- 提供设备对 I/O 请求路径的控制,主要用于 PCIe 设备隔离和安全性,确保 I/O 传输不会受到未授权的访问影响。
\17. Alternative Routing-ID Interpretation (ARI) Capability
- ID: 0x0E
- 用于支持 PCIe ARI 扩展,允许一个 PCIe 设备具有多个功能域(Function),从而增加设备的功能数量(例如多功能网卡等)。
3.2 MSI Capability
- pci_addr是32位、还是64位?
- 能触发几个中断?通过地址来分辨,还是通过数据来分辨?
- 这些中断能否屏蔽?
- 能否读出中断状态?
- 这些问题,都由capability里面的"Message Control"决定。
MSI Capability格式的含义如下:
- Capability ID:对于MSI capability,它的ID为05H
- Next Pointer:下一个capability的位置,00H表示这是最后一个capability
- Message Control
位 | 域 | 描述 |
---|---|---|
8 | Per-vector masking capable | 是否支持屏蔽单个中断(vector): 1: 支持 0: 不支持 这是只读位。 |
7 | 64 bit address capable | 是否支持64位地址: 1: 支持 0: 不支持 这是只读位。 |
6:4 | Multiple Message Enable | 系统软件可以支持多少个MSI中断? PCI设备可以表明自己想发出多少个中断, 但是到底能发出几个中断? 由系统软件决定,它会写这些位,表示能支持多少个中断: 000: 系统分配了1个中断 001: 系统分配了2个中断 010: 系统分配了4个中断 011: 系统分配了8个中断 100: 系统分配了16个中断 101: 系统分配了32个中断 110: 保留值 111: 保留值 这些位是可读可写的。 |
3:1 | Multiple Message Capable | PCI设备可以表明自己想发出多少个中断: 000: 设备申请1个中断 001: 设备申请2个中断 010: 设备申请4个中断 011: 设备申请8个中断 100: 设备申请16个中断 101: 设备申请32个中断 110: 保留值 111: 保留值 这些位是只读的。 |
0 | MSI Enable | 使能MSI: 1: 使能 0: 禁止 |
-
Message Address/Message Uper Address:地址
-
- 32位地址保存在Message Address中
- 64位地址:低32位地址保存在Message Address中,高32位地址保存在Message Uper Address中
- 这些地址是系统软件初始化PCI设备时分配的,系统软件把分配的地址写入这些寄存器
- 这些地址属于PCI地址空间
-
Message Data:数据
-
这个寄存器只有15位,PCI设备发出中断时数据是32位的,其中高16位数据为0
-
这个寄存器的数值是系统软件初始化设备时写入的
-
当PCI设备想发出中断是会发起一次写传输:
- 往Message Address寄存器表示的地址,写入Message Data寄存器的数据
- 如果可以发出多个中断的话,发出的数据中低位可以改变
- 比如"Multiple Message Enable"被设置为"010"表示可以发出4个中断
- 那么PCI设备发出的数据中bit1,0可以修改
-
-
Mask Bits/Pending Bits: 屏蔽位/挂起位,这是可选的功能,PCI设备不一定实现它
- Mask Bits:每一位用来屏蔽一个中断,被系统软件设置为1表示禁止对应的中断
- Pending Bits:每一位用来表示一个中断的状态,这是软件只读位,它的值为1表示对应中断发生了,待处理