PCIe驱动

PCIE设备驱动与Platform设备驱动的对比学习

1、驱动模块结构

1) PCIE设备注册:module_pci_driver(xxxx_driver);

展开之后对应于

module_init(xxxx_driver)---->pci_register_drive(xxxx_driver)

module_exit(xxxx_driver)---->pci_unregister_drive(xxxx_driver)

因此也可以采用自己编写module_init(),module_exit()方式使用。

2) Platform设备注册:module_platform_driver(gpio_led_driver);

2、设备驱动结构体

1)pcie设备

static struct pci_driver xxxx_driver = {
.name = xxx, //名称
.id_table = xxxx_table,    //匹配的设备列表
.probe = xxxx_probe,
.remove  = xxxx_remove,

....
};

2)Platform设备

static struct platform_driver xxxx_driver = {
.probe = xxxx_probe,
.remove = xxxx_remove,
.driver = {
.name = "xxxx",
.of_match_table = of_xxxx
_match, //一般与设备树中的设备匹配
},
};

3、匹配设备表

1) pcie设备

static const struct pci_device_id xxxx_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID, 0xnnnn) },

//内容为设备的号、厂商号,用于判别设备,与总线上扫描到的设备匹配
{ }
};
MODULE_DEVICE_TABLE(pci, xxxx_table);

2)Platform设备

static const struct of_device_id of_gpio_leds_match[] = {
{ .compatible = "gpio-leds", },
//主要用于和设备树进行匹配

{},
};
MODULE_DEVICE_TABLE(of, of_gpio_leds_match);

4、Probe函数

1) pcie设备

static int xxxx_probe(struct pci_dev *pdev,
  const struct pci_device_id *id)

//pcie设备id:厂商号、设备号、类等信息

2)Platform设备

static int xxxx_probe(struct platform_device *pdev)

总结:

pcie设备驱动是注册在pcie总线的,对应的设备是通过pcie总线控制器管理,因此其设备与驱动的匹配方式与platform总线上不同;同样USB总线、I2C总线等与pcie总线相似,由总线负责匹配、驱动管理等工作。

1 PCIe中断
- PCI/PCIe设备中断都是level触发
- PCI总线一般只有INTA#到INTD#的4个中断引脚,所以PCI多功能设备的func一般不会超过4个,但是共享中断除外

2 IOMMU和SMMU
2.1 x86 IOMMU
(1)Native时将PCIe总线地址(ARM叫IPA地址)转换成存储器物理地址
(2)Hypervisor时根据来的BDF(DMA源或目的ID)将DMA GPA转换成HPA(DRAM物理地址)
(3)Hypervisor时物理中断投送给pCPU的LAPIC(读作ei pik),然后pCPU根据BDF将物理中断翻译成虚拟中断,再注入(inject)在其上运行的vCPU的vLAPIC
(4)获取vm-exit的退出原因:exit_qualification = vmcs_readl(EXIT_QUALIFICATION)
Figure 2-1 IOMMU

2.2 ARM中断虚拟化
(1)在虚拟化系统中,physical CPU interface和hypervisor interface属于VMM控制
(2)VMM通过physical CPU inteface接收物理中断
(3)VMM通过hypervisor interface配置virtual CPU interface向虚拟机注入(inject)虚拟中断
(4)GuestOS通过virtual CPU interface接收虚拟中断并处理
Figure 2-2 SMMU

3 Linux x86 PCIe调试
3.1 PCIe设备分类
- RC,BDF为00:00.0
- bridge就像hub,一般是个多功能的设备,传递数据需要仲裁,比较慢
- switch就像交换机,PCIe规范中引入,比较快
- endpoint,x86主板上内置设备的总线号一般为0,而外挂EP的总线号一般从1开始

Figure 3-1 Type0 Header

Figure 3-2 Type1 Header

3.2 基本概念
- PCIe QOS:TC(Traffic Class),TC的值从0到7,值越大,优先级越高,类似于支持AVB的EtherSwitch,因为PCIe设计之初主要是针对于音视频应用;一个TC对应一个VC buffer(Virtual Channel),如果只有一个VC buffer,那么设置的TC值无效
- PCIe超过256字节的配置空间需要找到基地址,在MMCFG中,偏移44字节(0x2c),长度为8个字节,而MCFG可以通过acpidump找到
- PCIe的domain在内核代码中叫segment,可以通过pci_domain_nr()获得
- dev号(也叫slot)和func号一般通过宏PCI_DEVFN()合并成一个字节
- 因为PCI规范允许单个系统拥有高达256个总线,所以总线编号是8位。但对于大型系统而言,这是不够的,所以,引入了域的概念,每个PCI域可以拥有最多256个总线,每个总线上可支持32个设备,所以设备号是5位,而每个设备上最多可有8种功能,所以功能号是3位
- I210一般连接在pcieport的Lane0

3.3 LTSSM状态的查询
- PCIESTS1 offset:328h
- PCIe的LTSSM控制寄存器一般位于bridge的配置空间中(x86或者synopsys)或者RC的私有的寄存器(qcom)
- 读取EP的上一级bridge的config space的0x328(假如EP直接连在RC的port上,读取RC私有的寄存器),就可以获得下一级EP的LTSSM状态。譬如读取bridge(00:13.0)的下一级EP状态:peeknpoke b r 0x00 13 0 328

CONFIG_PCI_MMCONFIG=y
arch/i386/pci/init.c
arch/i386/pci/mmconfig.c
pci_access_init()

QNX读取桥配置空间0x328的方法:
pci-tool -D 0x5ada -vvvvv
pci-tool -d 0:19:2 --read=CFG:0x328

3.4 LTSSM链路训练结果
通过访问PCIe桥的配置寄存器获得
Link Capabilities:配置空间0x4c
Link Control;Link Status:配置空间0x50

3.5 re-enumeration
echo 1 > /sys/bus/pci/devices/0000:00:00.0/remove
echo 1 > /sys/bus/pci/rescan
echo 1 > /sys/bus/pci/drivers/pcieport/0000:00:13.0/rescan

3.6 procfs
proc_create()
remove_proc_entry()

4 ARM PCIe
4.1 MSM RC
drivers/pci/host/pci-msm.c
qcom平台上每个RC属于一个domain(PCIe规范叫segment),并且每个RC只连接一个EP

Figure 4-1 qcom RC拓扑图

4.2 MSM ep_pcie
msm/ep_pcie
ep_pcie_enumeration()

5 x86 MIPI60
- Blackhawk USB560v2

6 PCI用户空间编程 - libpci
6.1 Android libpci库
external/pciutils

6.2 libpci判断一个PCI设备是不是PCIe
capability ID参考:include/uapi/linux/pci_regs.h
参数ptr是配置空间偏移0x34地址指向的一个字节。
static bool pci_is_pcie(struct pci_dev *pdev, unsigned char ptr)
{
    unsigned int value;
    unsigned int next_ptr;
    unsigned int cap_id;

    next_ptr = ptr;
    if (0 == ptr) {
        return false;
    }
    do {
        value = pci_read_long(pdev, next_ptr);
        next_ptr = (value >> 8) & 0xff;
        cap_id = value & 0xff;
        /* PCI Express Capability Structure */
        if (cap_id == 0x10) {
            return true;
        }
    } while (next_ptr);

    return false;
}

7 x86 P2SB
PCH(Platform Controller Hub)上大部分设备可以通过PCIe或IO方式访问,但PCH上部分设备需要访问PCH的私有空间,这部分空间通过P2SB的SBREG_BAR寄存器映射到内存空间,这段空间被称为PCR(PCH Private Configuration Space Register)。每个设备对应一个PortID,PortID表示设备在PCR空间的偏移量,在加上寄存器偏移就可以获取寄存器的地址。

x86下GPIO配置位于PCH的私有空间。GPIO被分组,每组对应一个PCR的PortID。GPIO community和PortID的对应关系如下所示。
SouthWest: 0xC0
NorthWest: 0xC4
North: 0xC5
West: 0xC7

8 Windows PCIe工具软件
Mindshare的Arbor
Teledyne LeCroy的TeleScan PE

9 Abbreviations
ATU:Address Translation Unit
BDF:Bus,Device,Function
MEI:Intel Management Engine Interface;一个独立的子系统,使用ARC处理器,OS是Minix 3,固件整合到BIOS中,通过PCI桥片在x86端访问ARC的local端
overhead:开销,包头包尾等由协议层而不是应用层添加的字节,也就是说,一个PCIe包中除了payload之外的附加字节(ACK、CRC等)都叫overhead
P2SB:x86 Primary to Sideband
PCIe bifurcation:分叉
RC:Root Complex,执行存储器域地址到PCIe域地址的翻译,ATU被配置好后,CPU将要访问的地址发给ATU,ATU翻译后生成TLP包发给对应的Endpoint
TLP:Transaction Layer Packet,TLP中包含BDF号或者要寻址的内存和IO地址及其范围
VMCS:Virtual Machine Control Structure

猜你喜欢

转载自blog.csdn.net/star871016/article/details/112996967