uboot中PCIe驱动程序说明

本文解析了U-Boot中PCIe配置的关键步骤和技术细节,包括初始化过程、配置空间设置、设备扫描等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

针对之前分析uboot中PCIe部分的代码,这里进行简要的PCIE体会说明,回头再整理一下格式
1.     概论
这边的说明只针对Uboot中P1020开发板的PCIE部分。简要说明功能和注意问题。
2.     PCIe相关基本概念
Host主桥:HOST 主桥与主存储器控制器在同一级总线上,其主要功能是隔离处理器系统的存储器域与处理器系统的PCI总线域。PCI 设备可以方便地通过 HOST 主桥访问主存储器。一个CPU可以有多个HOST主桥,一个主桥代表一个PCI总线域。
通过PCI桥(bridge)可以划分多个PCI总线域。而PCI设备(EP)则是挂载在各个PCI总线域的终结。
如下图:
uboot中PCIe驱动程序说明

uboot中PCIe驱动程序说明
PCI总线域:如同上图中列出的PCI域x,从上向下按树形一一级分。每个域可以通过bus号进行区分。
存储器域:对应PCI总线域,存储器域就是处理器系统的地址空间。
映射空间:由于隔离就存在将存储器域和PCI总线域映射的需要。在PPC中就采用Outbound和Inbound两个寄存器,负责保存这两个域之间的映射关系。
Outbound:将存储器域的地址转换为 PCI。用于host处理器对pci总线上设备进行读写。就是配置outbound映射关系后,host处理器就可以访问PCI总线设备。
Inbound:将 PCI 总线域的地址转换为存储器域的地址。用于PCI总线上的设备对host处理器的读写访问。要pci设备能访问host处理器的某段存储器空间,则需要配置inbound寄存器。
配置空间:按照PCI标准,任何PCI设备不管是桥还是Ep,都有一套标准的配置空间,配置空间中包含许多寄存器,这些是用来存放ID、指令、基地址等。其中桥和设备的配置空间结构不一样,需要区别对待。
访问配置空间:访问配置空间的方法每种HOST主桥会不一样。而P1020中的访问方式是通过往寄存器PEXx_PEX_CONFIG_ADDR写入要访问配置空间寄存器的偏移量,然后再对PEXx_PEX_CONFIG_DATA寄存器进行读写操作,来完成对配置空间中指定寄存器的读写操作。
PCIe总线则是串行总线,只是按照PCI标准指定的串行总线,所以它建立的是一对一的连接,也就是如果要负载一个设备,就需要host主桥和设备通过一个串行总线连接,根据负载能力可以分为X1、X2或X4等等。P1020中一个host最多连4个,也就是X4,但是这个跟硬件设计分配有关,首先就需要设置p1020中的serdes,分配lane通道来提供串行连接。
LAW:是p1020的本地访问窗口,用于做本地地址映射。在配置PCI总线时需要分配一段映射空间供给PCI设备的读写访问。
3.     说明
3.1.     主要文件说明
PCI的主文件:
fsl_pci_init.c文件是飞思卡尔PCI控制器的主要配置文件,PCI的初始化函数等就在该函数中。
pci.c文件是PCI配置的公用文件,用于所有PCI驱动,主要包括PCI设备的扫描、设备查找等。
pci_auto.c文件主要用于PCI自动扫描和PCI设备配置。包括飞思卡尔PCI控制扫描前和扫描后的配置函数等。
pci_indirect.c文件存放对PCI设备的配置空间中寄存器进行读写操作的函数。
cmd_pci.c文件是存放pci查看指令函数的文件,用户通过shell输入相关指令来查看PCIe设备信息。
PCI配置相关文件:
fsl_law.c文件用来设置LAW的文件,在PCIe配置时需要设置LAW,之前要进行law初始化,查看law的使用情况进行相关标志编号等。
p1021_serdes.c文件用来设置serde的文件,对相应的PCIe设置lane通道,在P1020开发板中对PCIe串口只提供了一个lane通道。
PCI头文件:
主要定义相关的宏定义、数据类型定义、大小端IO访问的函数等。
移植时用到的代码结构图
\PCIE
│  cmd_pci.c
│  command.h
│  e500.h
│  fsl_law.c
│  fsl_law.h
│  fsl_pci_init.c
│  law.c
│  mpc85xx.h
│  p1021_serdes.c
│  p1020rdb.h
│  pci.c
│  pci.h
│  pci_auto.c
│  pci_ids.h
│  pci_indirect.c
│ 
├─asm
│      bitops.h
│      byteorder.h
│      config.h
│      config_mpc85xx.h
│      fsl_law.h
│      fsl_pci.h
│      fsl_serdes.h
│      immap_85xx.h
│      io.h
│      processor.h
│      types.h
│ 
└─linux
        compiler-gcc.h
        compiler-gcc4.h
        compiler.h
        config.h
3.2.     结构体说明
PCIE主桥控制器的配置结构体如下:
struct pci_controller {
     struct pci_controller *next;
     int first_busno;
     int last_busno;
。。。。。
     int indirect_type;
     struct pci_region regions[MAX_PCI_REGIONS];
     int region_count;
     struct pci_config_table *config_table;
     void (*fixup_irq)(struct pci_controller *, pci_dev_t);
     。。。。。。
    
     struct pci_region *pci_mem, *pci_io, *pci_prefetch;
。。。。。。};
first_busno和last_busno分别存储在该host主桥的bus号和所挂最下一级设备的bus号。pci_controller结构体中的struct pci_region regions用来存放IO空间、存储空间、预读空间等,在outbound和inbound 的配置时有用到。region_count指示配置的region个数。
而*pci_mem, *pci_io, *pci_prefetch结构体指针则是在PCI设备扫描时为了根据outbound自动配置PCI设备的BAR地址而定义的。在pciauto_config_init函数中会过去之前regions来设置各个特定区域的参数。
config_table在P1020中没有用到。其他不太重要的参数用省略号略过。
struct pci_region结构体如下:
struct pci_region {
     pci_addr_t bus_start;    
     phys_addr_t phys_start;    
     pci_size_t size;    
     unsigned long flags;    
     pci_addr_t bus_lower;};
因为在一个处理器系统中,并不是所有存储器空间都可以被 PCI 设备访问,只有在 PCI总线域中有映像的存储器空间才能被 PCI 设备访问。该结构体就是用来配置映射空间的。
bus_start是指CPU存储域的起始地址,phys_start是指PCI设备物理空间的起始地址。Size是指映射的空间大小。flags是用来指示映射空间的功能,是用于设备IO、MEM还是预读。bus_lower是在自动扫描配置时会用到,初始配置时等于bus_start,如果扫描设备用到了某空间,比如IO空间或MEM空间,则bus_lower=bus_start+used_size,used_size是扫描所有设备使用该空间的总大小。
3.3.     主程序流程图说明
graphic
Uboot中主流程如上图,下面将按照几个大模块分别进行说明。
3.4.     主程序详细说明
3.4.1.     PCIe初始化前奏
因为配置PCIe牵涉到LAW配置等,所以在初始化PCIe之前需要对LAW相关进行初始化。
serdes部分:执行fsl_serdes_init ()函数来初始化。其实serdes的配置主要跟硬件连接有关,看serdes的4个lane都分给了谁,根据I/O port selection中的设置,将serdes的分配结果映射到serdes1_prtcl_map变量中。这样在PCIe配置之前先判断serdes有没有分配一个lane给PCIe控制器,如果没有,就没有做后面PCIe配置的必要了。然后根据I/O port selection中的设置,对相应的serdes进行默认配置。
LAW部分:执行init_laws()函数来初始化,这里主要是查看12个LAW的使能情况,对已经使能的LAW寄存器在used_laws变量中进行标记。然后按照law.c文件中的law table对需要配置的相关law寄存器进行配置。
在执行PCIe配置前一定要先执行这两项的初始化。
3.4.2.     PCIe控制器的配置
3.4.2.1.     初始配置和bus号分配
P1020有两个独立的host主桥控制器,分管两个PCI域。在配置之前先判断serdes是否使能了PCIe,使能了才能继续。同时将系统提供的MEM基地址和空间大小,以及IO基地址和空间大小,放置到相应fsl_pci_info结构体中,用于配置对应PCIe控制器的LAW寄存器。
这边要注意的是对P1020中所有PCI的总线号的分配,这边对bus号分配是按从0开始顺序分配的(其实可以按照任意号开始,这边默认从0开始)。
比如P1020中包含pcie1和pcie2,其实属于两个host主桥(host-to-pci桥),相当于两个独立的域,而uboot中的pcie是从0开始编号的。如果按照连续配置两个PCIE1和PCIE2两个桥的时候,扫描后bus编号如下: uboot中PCIe驱动程序说明


这边只考虑每个host控制器配置X1的情况,好像Uboot中只支持单分支的扫描,实际在X1中扫描的结果也和上图类似。
对于Host主桥通过pci_controller结构体保存信息,在结构体中的first_busno记录RC的bus号。
由于P1020的PCIe控制器可以配置成RC或EP(这种配置是由host/agent引脚配置信号LWE_B[1]/LA[18:19]确定哪种模式,说白了就是硬件决定是什么模式),所以在UBoot的驱动中调用fsl_is_pci_agent函数判断该控制器被配置成那种类型。在该函数中通过判断Cap ID寄存器来判断是否是PCIe接口,然后再读相关寄存器看是设置为何种模式。这里Cap ID寄存器在配置空间的0x4C位置,P1020中PCIE控制器有两个capabilities寄存器,先cap point指向0x44,然后链表指向0x4C。
3.4.2.2.     outbound和inbound配置
在PCIe控制器中的初始化主要在fsl_pci_init函数中。首先是outbound和inbound的配置。Outbound的地址转换寄存器配置时,由于POWBAR0 寄存器作为缺省窗口,所以从POWBAR1寄存器开始配置,然而Inbound的地址转换寄存器配置时,则是从PIWBAR2或PIWBAR3倒序配置的。
而在uboot中对地址、空间大小等配置都是先放置到pci_region结构体中,然后由pci_region设置到对应寄存器中。在开发板中测试得到一般的pci_region配置如下图:
graphic

注意PCSRBAR设置中,配置1M空间,做了防止outbound和inbound重合的操作。包括在fsl_pci_setup_inbound_windows中设置inbound也是这样,虽然pcie本身可以支持outbound和inbound重合,不过该程序是针对pci/pcie,所以做了防止inbound重合的措施。
Uboot中pci_region结构体里,phy_start和bus_start一般设成一样,作为一一映射。在inbound配置中,这两个地址都默认为0。
3.4.2.3.     查看链路状态和扫描前配置空间设置
配置完outbound和inbound后,就是清除error寄存器然后使能错误中断异常寄存器。
如果定义CONFIG_FSL_PCIE_DISABLE_ASPM宏,则设置寄存器关闭ASPM(激活状态电源管理)功能。
之后查看link传输状态机观察设备连接的状态,只有在L0状态以上才表示有正常连接,如果有设备连接则继续,如果没有则返回,完成初始化配置。同时初始化时如果检测link状态机在active状态0,表示errata,需要reset(不过在P1020中这个pdb_stat软件复位没有该寄存器,所以段reset代码无效)。
有设备连接的话,还将对PCIe控制器进行prescan配置(调用pciauto_prescan_setup_bridge函数),对HOST主桥的配置空间进行设置,主要包括PCI_COMMAND,主总线、二级总线、下级总线数目等,以及RC主桥的IO、MEM等基地址。
3.4.2.4.     PCIe设备扫描和自动配置
初始化时可以通过定义CONFIG_PCI_NOSCAN宏,不进行PCI扫描和自动配置。
这里默认使用初始化时扫描。
在fsl_pci_init函数中调用pci_hose_scan_bus,首先从HOST主桥的下一级bus号开始扫描。如果是桥,则配置完cmd、BAR0~1等参数后,再递归调用pci_hose_scan_bus,继续向下扫描。Busno加1。通过判断vendorID和HeaderType来判断是否有设备挂载。
PCIe下级设备是从主总线一级级向下扫,一级级增加bus号。参考上节的图。
如果添加或删除设备后重新扫描busno可能变化。热插拔后需要重新调用pci_hose_scan(struct pci_controller *hose)函数重新扫描分配BAR地址。
从代码可以看出,uboot中pcie的自动扫描只能进行单分支扫描,就是针对X1的情况,若X2或X4之类的情况,则不行。uboot中的pci_hose_scan用嵌套扫描,如果是桥再在桥下进行扫描。同一桥下的设备通过device号进行区分。
扫描后再调用pciauto_postscan_setup_bridge函数进行配置,这里主要是根据挂接的PCIe设备得到使用的地址空间大小并最大bus号。IO、MEM当前使用的高位地址放置在对应pci_region的bus_lower中。
3.5.     结束及测试
扫描配置完后,重新清除error寄存器位。再设置配置ready等寄存器之后,整个PCIe初始化基本就完成了。
这时候可以通过pciinfo指令查看总线号下的设备配置参数等,还可以获得对应设备的BAR,对该地址进行读写。
uboot中PCIe驱动程序说明 
在测试时可以通过CPU对设备映射的地址进行读写操作,在FPGA开发板的PCIE设备中通过CPU反复读写测试,发现正常。不过对于PCI设备访问CPU的测试没能进行。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值