PCIe总线-RK3588 PCIe驱动设备树介绍(九)

1.简介

在Linux内核中设备树中,定义了一系列属性,用来描述PCIe总线。比如"bus-range"属性,描述PCIe某个domain的总线编号范围,比如"ranges"属性,描述PCIe地址转换。下面将分别介绍这些属性。

2.设备类型

由于PCIe总线有一些特有的属性,需要在驱动初始化的时候内核自动解析。因此需要在设备树定义设备类型,如下所示。当设备类型为"pci"时,内核就知道这是一个PCI Host Brage。

[arch/arm64/boot/dts/rockchip/rk3588.dtsi]
pcie3x4: pcie@fe150000 {
    ......
    device_type = "pci";
    ......
}

Linux内核调用__of_node_is_type函数解析设备类型。

[drivers/of/base.c]
static bool __of_node_is_type(const struct device_node *np, const char *type)
{
	const char *match = __of_get_property(np, "device_type", NULL);

	return np && match && type && !strcmp(match, type);
}

3.PCI域

在Linux内核中,一个PCI设备,通常使用domain number:bus number:device number.function number(比如0000:00:00.0)描述。domain number表示PCI域的编号,bus number表示总线编号,device number表示设备编号,function number表示功能编号,bus number、device number和function number也称之为BDF。PCI域用来给PCI Host Brage编号,有几个PCI Host Brage,就有几个PCI域。通常情况下,各个PCI域之间不能直接通信。在设备树中,PCI域使用"linux,pci-domain"属性描述。

[arch/arm64/boot/dts/rockchip/rk3588.dtsi]
pcie3x4: pcie@fe150000 {
    ......
    linux,pci-domain = <0>;
    ......
}

Linux内核调用of_get_pci_domain_nr函数解析PCI域编号。

[drivers/pci/of.c]
int of_get_pci_domain_nr(struct device_node *node)
{
	u32 domain;
	int error;

	error = of_property_read_u32(node, "linux,pci-domain", &domain);
	if (error)
		return error;

	return (u16)domain;
}

4.速度和Link Width

PCIe总线的最大速度由"max-link-speed"属性描述,Link Width由"num-lanes"描述。真实的速度和Link Width由RC、PCIe桥和EP协商决定。

[arch/arm64/boot/dts/rockchip/rk3588.dtsi]
pcie3x4: pcie@fe150000 {
    ......
    max-link-speed = <3>;
    num-lanes = <4>;
    ......
}

Linux内核中可使用下面的接口解析"max-link-speed""num-lanes"属性。

[drivers/pci/of.c]
int of_pci_get_max_link_speed(struct device_node *node)
{
	u32 max_link_speed;

	if (of_property_read_u32(node, "max-link-speed", &max_link_speed) ||
	    max_link_speed == 0 || max_link_speed > 4)
		return -EINVAL;

	return max_link_speed;
}

[drivers/pci/controller/pcie-rockchip.c]
err = of_property_read_u32(node, "num-lanes", &rockchip->lanes);

5.ranges

"ranges"属性定义了CPU地址到PCI地址的转换关系(outbound memory)。在PCI设备枚举的时候,PCI主机会根据ranges属性,设置对应的memory region,同时将PCI地址设置到PCI设备的寄存器中,当枚举完成后,CPU可以直接通过地址访问PCI设备。"dma-ranges"属性则与"ranges"属性相反,定义PCI地址到CPU地址的转换关系(inbound memory)。

"#address-cells"定于使用几个cell描述PCI地址,"#size-cells"定义使用几个cell描述地址长度。因此"ranges"属性和"dma-ranges"属性描述的地址意义如下所示。

[arch/arm64/boot/dts/rockchip/rk3588.dtsi]
pcie3x4: pcie@fe150000 {
    ......
    #address-cells = <3>
    #size-cells = <2>;
    /*           PCI地址标志  PCI地址高32位  PCI地址低32位 CPU地址高32位  CPU地址低32位 地址长度高32位 地址长度低32位 */
	ranges = <    0x00000800     0x0       0xf0000000     0x0        0xf0000000     0x0       0x100000
			      0x81000000     0x0       0xf0100000     0x0        0xf0100000     0x0       0x100000
			      0x82000000     0x0       0xf0200000     0x0        0xf0200000     0x0       0xe00000
			      0xc3000000     0x9       0x00000000     0x9        0x00000000     0x0       0x40000000>;
    ......
};

[arch/arm64/boot/dts/renesas/r8a774b1.dtsi]
pciec0: pcie@fe000000 {
    ......
    #address-cells = <3>;
    #size-cells = <2>;
    /* Map all possible DDR as inbound ranges */
    /*          PCI地址标志  PCI地址高32位  PCI地址低32位 CPU地址高32位  CPU地址低32位 地址长度高32位 地址长度低32位 */
    dma-ranges = <0x42000000      0        0x40000000        0        0x40000000       0       0x80000000>;
    ......
};

PCI地址标志由8部分组成,其二进制位域的意义如下所示,PCIe总线中,只有pss有意义。

npt000ss bbbbbbbb dddddfff rrrrrrrr

n: relocatable region flag (doesn't play a role here)
p: prefetchable (cacheable) region flag
t: aliased address flag (doesn't play a role here)
ss: space code
    00: configuration space
    01: I/O space
    10: 32 bit memory space
    11: 64 bit memory space
bbbbbbbb: The PCI bus number. PCI may be structured hierarchically. 
          So we may have PCI/PCI bridges which will define sub busses.
ddddd: The device number, typically associated with IDSEL signal connections.
fff: The function number. Used for multifunction PCI devices.
rrrrrrrr: Register number; used for configuration cycles.

因此pcie3x4节点中"ranges"属性定义的4个地址段意义如下:

  1. 一个配置空间,从PCI地址0xf0000000开始,大小为1MB,将映射到Host CPU的0xf0000000地址处,具体的地址根据访问设备的BDF动态映射。
  2. 一个IO空间,从PCI地址0xf0100000开始,大小为1MB,将映射到Host CPU的0xf0100000地址处。
  3. 一个32位非预取内存空间,从PCI地址0xf0200000开始,大小为14MB,将映射到Host CPU的0xf0200000地址处。
  4. 一个64位预取内存空间,从PCI地址0x900000000开始,大小为14GB,将映射到Host CPU的0x900000000地址处。

因此pciec0节点中"dma-ranges"属性定义的地址段意义如下:

  1. 从Host CPU的角度看,一个32位非预取内存空间,从PCI地址0x40000000开始,大小为2GB,将映射到Host CPU内存0x40000000地址处。这样设置后,EP的DMA可以直接访问Host CPU的内存。

Linux内核中使用下面定义的函数解析"ranges"属性和"dma-ranges"属性。其中of_bus_pci_match匹配PCI总线,of_bus_pci_get_flags解析PCI地址标志。

[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,
	},
#endif /* CONFIG_PCI */
    ......
};

6.中断

PCIe总线中,涉及INTx、MSI、MSI-X中断。INTx中断需要在设备树中配置映射关系。MSI和MSI-X中断需要配置和ITS的映射关系。

6.1.INTx中断

由于很多PCI设备只使用INTA中断,若中断很频繁时,这些中断都将集中到INTA上,导致中断效率降低。因此需要将PCI插槽上的INTx中断以旋转(swizzling)方式连接到中断控制器上的不同中断引脚上。设备树需要一种将每个PCI中断信号映射到中断控制器输入的方法。"#interrupt-cells""interrupt-map""interrupt-map-mask"属性用于描述中断映射。"#interrupt-cells"属性表示描述中断需要几个cell,"interrupt-map-mask"表示PCI插槽(包含了device id信息)和INTx中断编号的掩码,"interrupt-map"表示INTx和中断控制器的映射关系。

如下图所示,phys.hi phys.mid phys.low分别表示PCI插槽位置信息,由于在PCIe总线中,INTx中断使用消息机制实现,不需要中断引脚,因此PCI插槽位置信息可以设置为0。INTx对应的一列分别表示INTA、INTB、INTC、INTD中断。"interrupt-map-mask""interrupt-map"的前4列相与得到最终的PCI插槽位置信息和INTx类型。因此下面分别将INTA、INTB、INTC、INTD映射到pcie3x4_intc中断控制器的0、1、2、3号中断上,实质上pcie3x4_intc是一个虚拟的中断控制器,父中断控制器为GIC,其使用260号中断向GIC提交中断。

[arch/arm64/boot/dts/rockchip/rk3588.dtsi]
pcie3x4: pcie@fe150000 {
    ......
    interrupts = <GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH>,
                <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>,
                <GIC_SPI 261 IRQ_TYPE_LEVEL_HIGH>,
                <GIC_SPI 260 IRQ_TYPE_LEVEL_HIGH>,
                <GIC_SPI 259 IRQ_TYPE_LEVEL_HIGH>;
    interrupt-names = "sys", "pmc", "msg", "legacy", "err";
    #interrupt-cells = <1>;
    /*                   phys.hi phys.mid phys.low  INTx */
    interrupt-map-mask = <  0        0        0      7>;
    /*            phys.hi phys.mid phys.low  INTx 映射的中断控制器  中断编号 */
    interrupt-map = <0        0        0      1    &pcie3x4_intc   0>,  /* INTA */
                    <0        0        0      2    &pcie3x4_intc   1>,  /* INTB */
                    <0        0        0      3    &pcie3x4_intc   2>,  /* INTC */
                    <0        0        0      4    &pcie3x4_intc   3>;  /* INTD */
    ......
    pcie3x4_intc: legacy-interrupt-controller {
        interrupt-controller;
        #address-cells = <0>;
        #interrupt-cells = <1>;
        interrupt-parent = <&gic>;
        interrupts = <GIC_SPI 260 IRQ_TYPE_EDGE_RISING>;
    };
    ......
}

6.2.MSI和MSI-X中断

GICv3及以上版本实现了ITS(Interrupt Translation Service)。因此在ARM架构上,可基于ITS实现MSI或MSI-X中断。设备树使用"msi-map"属性描述MSI或MSI-X中断和msi-controller的映射关系。"msi-map"属性的第一个数据表示MSI Data,即MSI中断向量起始编号,需要配置到PCIe设备的配置空间中,第二个数据引用msi-controller节点,msi-controller位于gic节点内,第三个数据表示PCIe设备起始16位的Requester ID(BDF),第四个数据表示中断数量,下面申请了4096个中断。

[arch/arm64/boot/dts/rockchip/rk3588.dtsi]
pcie3x4: pcie@fe150000 {
    ......
    /*        MSI Data  msi-controller  Requester ID  length  */
    msi-map = <0x0000      &its1          0x0000      0x1000>;
    ......
};

[arch/arm64/boot/dts/rockchip/rk3588s.dtsi]
gic: interrupt-controller@fe600000 {
    compatible = "arm,gic-v3";
    #interrupt-cells = <3>;
    #address-cells = <2>;
    #size-cells = <2>;
    ranges;
    interrupt-controller;

    reg = <0x0 0xfe600000 0 0x10000>, /* GICD */
            <0x0 0xfe680000 0 0x100000>; /* GICR */
    interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
    its0: msi-controller@fe640000 {
        compatible = "arm,gic-v3-its";
        msi-controller;
        #msi-cells = <1>;
        reg = <0x0 0xfe640000 0x0 0x20000>;
    };
    its1: msi-controller@fe660000 {
        compatible = "arm,gic-v3-its";
        msi-controller;
        #msi-cells = <1>;
        reg = <0x0 0xfe660000 0x0 0x20000>;
    };
};

参考资料

  1. PCIEXPRESS体系结构导读
  2. PCI Express technology 3.0
  3. PCI Express® Base Specification Revision 5.0 Version 1.0
  4. Rockchip RK3588 TRM
  5. https://elinux.org/Device_Tree_Usage#PCI_Host_Bridge

相关推荐

  1. PCIe总线-RK3588 PCIe驱动设备介绍

    2024-07-21 12:54:03       14 阅读
  2. pcie设备驱动无法工作排查

    2024-07-21 12:54:03       49 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 12:54:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 12:54:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 12:54:03       45 阅读
  4. Python语言-面向对象

    2024-07-21 12:54:03       55 阅读

热门阅读

  1. 我的创作纪念日——365天

    2024-07-21 12:54:03       17 阅读
  2. 掌握Perl的魔法:深入探索钩子(Hook)机制

    2024-07-21 12:54:03       18 阅读
  3. 【AI原理解析】—粒子群(PSO)原理

    2024-07-21 12:54:03       14 阅读
  4. 门控循环单元(GRU)及其预测和分类Python实现

    2024-07-21 12:54:03       16 阅读
  5. 【日常介绍】—pycharm2024.1变化

    2024-07-21 12:54:03       21 阅读
  6. 如何使用C++中的字符串类(如std::string)

    2024-07-21 12:54:03       15 阅读
  7. Ubuntu 20安装JDK17和MySQL8.0

    2024-07-21 12:54:03       17 阅读