1.简介
RK3588 PCIe RC的初始化涉及PCIe设备枚举、中断(INTx、MSI、MSI-X)配置、BAR配置、ATU配置、链路训练等,下面一一介绍。
2.初始化
当RC的模式为RK_PCIE_EP_TYPE
时,平台驱动调用rk_add_pcie_port
函数初始化RC,流程如下图所示,具体的工作有:
- 设置Host初始化回调函数结构体
rk_pcie_host_ops
,该结构体的实现和SoC硬件相关。 - 分配
pci_host_bridge
数据结构,并解析设备树中的"bus-range"
、"ranges"
、"dma-ranges"
属性,后面详细分析该函数。 - 初始化中断
- 设置MSI/MSI-X中断控制器的
irq_chip
,该结构体用来响应、屏蔽、解除屏蔽、设置MSI/MSI-X中断功能。 - 创建MSI/MSI-X中断的第一级
irq_domain
,对应的irq_domain_ops
为dw_pcie_msi_domain_ops
。 - 创建MSI/MSI-X中断的第二级
irq_domain
,对应的msi_domain_info
为dw_pcie_msi_domain_info
,第一级和第二级的irq_domain
级联。 - 映射MSI Data的地址。
- 设置MSI/MSI-X中断控制器的
- 设置Root Bus配置空间访问方法为
dw_pcie_ops
,其他Bus的配置空间访问方法为dw_child_pcie_ops
。 - 初始化RC Port相关的配置和建立link。
- 初始化RC Port相关的配置。
- 设置速度、lanes数量、lanes的编号、link width,使能fast link。
- 关闭和屏蔽MSI中断。
- 设置RC Inbound BAR为64位P-MEM。
- 设置INTx中断为INTA#。
- 设置Primary、Secondary、Subordinate总线编号分别为0、1、0xFF。
- 使能IO、MEM、Bus Master、SERR。
- 配置OUT Bound MEM类型的ATU。主要设置Region的方向和编号、CPU低32位地址、CPU高32位地址、Region的大小、PCIe低32位地址、PCIe高32位地址、匹配的Function Number和TLP类型,最后使能Region。
- 配置OUT Bound IO类型的ATU。配置内容和步骤8一样。
- 设置RC Port的类型为PCI_CLASS_BRIDGE_PCI。
- 关闭RC Inbound的BAR0和BAR1。
- 建立link。
- 拉低PERST#,复位总线上的所有设备。
- 先关闭LTSSM,清除link状态,使能client reset or link down中断,最后使能LTSSM。
- 延时,CEM要求PERST#的时间至少为100ms+100us,这里延时200ms。
- 拉高PERST#,释放复位。
- 等待PCIe链路硬件link训练完成,建立link。
- 初始化RC Port相关的配置。
- 枚举PCIe总线,后面详细分析该函数。
如下图所示,pci_host_bridge
数据结构的分配和桥地址资源的解析在devm_pci_alloc_host_bridge
函数中,具体的工作有:
- 首先分配
pci_host_bridge
数据结构。 - 设置中断相关的回调函数,
pci_common_swizzle
用于处理PCI设备INTx#中断引脚路由的函数,将PCI设备中断引脚INTx#通过旋转的方式接到PCI Host主桥上。of_irq_parse_and_map_pci
用于将INTx#中断号映射为Linux软件中断号。 - 请求资源。
- 从设备树中解析
"bus-range"
属性,即总线编号资源,同时转总线编号范围换成resource
,最后添加到pci_host_bridge
结构体中的windows
链表中。 - 从设备树中解析
"range"
属性,即Outbound地址资源(CPU地址、PCI地址、MEM和IO地址类型),同时将地址资源换成resource
,最后添加到pci_host_bridge
结构体中的windows
链表中。 - 从设备树中解析
"dma-ranges"
属性,即Inbound地址资源(CPU地址、PCI地址、MEM地址类型),同时将地址资源换成resource
,最后添加到pci_host_bridge
结构体中的windows
链表中。
- 从设备树中解析
桥地址资源使用下面的回调函数解析。
[drivers/of/address.c]
static struct of_bus of_busses[] = {
#ifdef CONFIG_PCI/* PCI */{.name = "pci",.addresses = "assigned-addresses",.match = of_bus_pci_match,.count_cells = of_bus_pci_count_cells,.map = of_bus_pci_map,.translate = of_bus_pci_translate,.has_flags = true,.get_flags = of_bus_pci_get_flags,},......
};static unsigned int of_bus_pci_get_flags(const __be32 *addr)
{unsigned int flags = 0;u32 w = be32_to_cpup(addr);if (!IS_ENABLED(CONFIG_PCI))return 0;switch((w >> 24) & 0x03) {case 0x01:flags |= IORESOURCE_IO; // IObreak;case 0x02: /* 32 bits */case 0x03: /* 64 bits */flags |= IORESOURCE_MEM; // MEMbreak;}if (w & 0x40000000)flags |= IORESOURCE_PREFETCH; // P-MEMreturn flags;
}
3.ATU
RC初始化的时候,会调用dw_pcie_prog_outbound_atu
函数配置Outbound ATU region。每个ATU region有7组寄存器,分别是1个VIEWPORT寄存器、3组CTRL寄存器、1组BASE寄存器、1组TARGET寄存器、1组LIMIT寄存器。
3.1.VIEWPORT寄存器
ATU region的7组寄存器地址相同,设置某个region,由IATU_VIEWPORT_OFF
寄存器设置。如下图所示,REGION_INDEX设置region的编号,REGION_DIR设置region的方向。
3.2.CTRL寄存器
下图是画出了两组CTRL寄存器,第三组用于配置Virtual Function,这里不介绍。第一组CTRL寄存器,用于配置Outbound/Inbound的传输的类型、TC、ATTR、Function编号等。第二组CTRL寄存器用于配置Outbound/Inbound的MSG_CODE、BAR编号、使能Region等。
3.3.BASE寄存器
如下图所示,BASE寄存器共有2两个,一个配置Outbound/Inbound的低32的BASE地址,另一个配置Outbound/Inbound的高32的BASE地址。LWR_BASE_HW为硬件设置,表示该region地址的最小范围(4kB、8kB、16kB、32kB、64kB,默认为64kB),Outbound方向时LWR_BASE_RW表示存储器域基地址,Inbound方向时LWR_BASE_RW表示PCIe域基地址,UPPER_BASE_RW表示对应基地址的高32位。
3.4.TARGET寄存器
如下图所示,TARGET寄存器共有2两个,一个配置Outbound/Inbound的低32的TARGET地址,另一个配置Outbound/Inbound的高32的TARGET地址。LWR_TARGET_HW为硬件设置,表示该region地址的最小范围(4kB、8kB、16kB、32kB、64kB,默认为64kB),Outbound方向时LWR_TARGET_HW表示PCIe域基地址,Inbound方向时LWR_TARGET_HW表示存储器域基地址,UPPER_TARGET_RW表示对应基地址的高32位。
3.5.LIMIT寄存器
如下图所示,LIMIT寄存器用于配置region的大小。
参考资料
- PCIEXPRESS体系结构导读
- PCI Express technology 3.0
- PCI Express® Base Specification Revision 5.0 Version 1.0
- Rockchip RK3588 TRM
- Linux kernel 5.10