Linux驱动——PCI
小狼@http://blog.csdn.net/xiaolangyangyang
PCI设备枚举过程:
(PC系统中BIOS和OS均实现了枚举过程,linux可通过内核PCI access mode配置选择使用OS还是BIOS进行枚举,嵌入式系统中枚举过程由linux驱动实现)
1、PCI控制器是通过读取配置空间寄存器的VID检测是否存在bridge或device,读取值为0xFF则为空;
2、PCI控制器在bus上(从bus0-device0-func0开始)扫描bridge,如果有bridge,则初始化该配置空间,写入bus num到该配置空间寄存器,再跳转到对应的bus上扫描下一级bridge(注意:该处写入bus号到硬件后,访问对应的下一级bridge才有效,其跳转就是读取对应的地址偏移的配置寄存器);
3、CPU最初只能扫描到bus0上的bridge或device,初始化其下的bridge配置寄存器后(写入bus num),才能正常扫描对应的下一级bus;
4、递归式扫描整个配置寄存器形成树状结构;
5、依次在所有的bus上扫描device,通过向BAR空间写0xFFFFFFFF,读取BAR值确定BAR地址空间size和type,一个func包括6个BAR空间,每个有效的BAR都需要分配物理地址;
6、系统分配物理空间地址(该地址即为PCI空间映射到MEM空间的物理地址)写到BAR寄存器高bit位,这样在CPU访问到该部分物理空间地址时,就落入到了RC空间。
PCI MEM/IO映射过程:
(由Linux driver实现)
Linux驱动申请虚拟地址将BAR寄存器分配的物理地址进行MEM or IO映射,CPU发出一个物理地址,落入分配给RC的空间,这个地址进入PCIE的调度体系,就可以从RC开始,变成PCIE自己的消息(TLP)。
NVMe协议:
NVMe协议是基于PCIe接口的一种SDD标准协议,其在PCIe上支持65536个命令队列,Message和Data(core)支持乱序,支持的接口如下:
1、message:用于传输命令,命令可变数据长度(512b到2Mb),一般只有一个message接口;
2、data(core):用于传输数据,一般有多个data接口。
PCI设备驱动程序:
左边主要涉及的是PCI总线驱动,那么PCI设备驱动是什么呢。PCI设备驱动是通过PCI总线桥梁连接的硬件设备的驱动,例如基于PCI总线的网卡设备。以下代码是一个典型的PCI设备代码框架:
疑难问答:
1、在枚举之前访问左右的配置空间是什么效果?
枚举之前,没有将bus num写入配置寄存器,只能访问到root bridge下的全部配置寄存器,其他配置寄存器读出来全是0。
例如root bridge下挂载有3个bridge,分别是B_A、B_B、B_C,每个bridge下挂载一个dev,分别是D_A、D_B、D_C,这时读取bus 0下的配置寄存器,能读到B_A、B_B、B_C,但是在任何bus下都读不到D_A、D_B、D_C。
把B_A的bus num写1后,就能读取到bus 1下的D_A;
把B_B的bus num写2后,才能读取到bus 2下的D_B;
把B_C的bus num写3后,才能读取到bus 2下的D_C。
2、写入BAR寄存器的地址是什么地址?
写入BAR寄存器的地址是PCI空间地址,CPU要访问这个地址,就得有MEM空间到PCI空间的地址映射,各平台映射关系如下(一般为一一映射关系):
X86:一一映射关系,只要CPU向某个物理地址写数据,bridge都能获取,如果是BAR地址,就会将该数据经bridge传到PCI;
powerPC:Outbound和Inbound寄存器组;
ARM:AXI总线传递数据,ARM使用ECAM的方式访问pcie配置空间,BAR空间也是吗?
3、怎么读写配置寄存器?
X86:PCIe配置寄存器与MEM独立编址,使用CFG_ADDR、CFG_DATA寄存器读写;
ARM ECAM映射:ECAM把PCIe配置寄存器映射到MEM空间,统一编址;
ARM硬件支持:硬件直接将PCIe配置寄存器分配到MEM空间,统一编址。
4、PCI空间与MEM空间怎么映射?
X86:一一对应映射;
ARM/PowerPc;使用Outbound和Inbound寄存器组进行映射配置。