5.14.2.5. PCI/PCI-E
PCI/PCI-E本质上就是一个代理了很多设备的设备。所以它才有那些BDF的复杂概念,好像很灵活,但如果我们从地址分配这个角度看,每个PCI/PCIE根桥就是一个平台设备,这个平台设备有自己的MMIO空间,它的所有动态协议,不过是对这个空间的重新分配(基于设备对应的Bus-Device-Function)。
所以PCI/PCIE根桥的创建本质上分配一个MMIO空间,并用PCI/PCIE作为这个MR的管理设备而已。一般套路是:
创建一个PCI/PCIE设备作为Root Bridge,比如TYPE_GPEX_HOST(General PCI EXpress)。
创建ECAM空间(配置空间)* mb = sysbus_mmio_get_region() * mem_region_init_alias(alias...) * memory_region_add_subregion(system_memory, addr, alias)
创建BAR空间* 同2
剩下的事情就是TYPE_GPEX_HOST驱动的问题了。
备注
我们这里快速补充一下PCI/PCIE上的基本概念:
在谈PCI/PCIE的时候,Host表示CPU子系统,Host Bridge表示把CPU和PCI/PCIE总线连起来的那个IP。这个IP包括三个功能:Bus Master,Bus Target,以及Configure Access Generation。
TYPE_GPEX_HOST的继承树结构::
DEVICE <- SYS_BUS_DEVICE <- PCI_HOST_BRIDAGE <- PCIE_HOST_BRIDGE <- GPEX_HOST
中断的行为类似,先为整个RP分配中断,然后用gpex_set_irq_num()建立PCIE局部irq和全局irq的关系即可。
5.14.2.5.1. PCI/PCI-E驱动
前面是全系统的PCI桥的概念,我们用一个PCI设备的backend来看具体的backend的写法:
static void my_class_init(ObjectClass *oc, void *data) {
PCIDeviceClass *k = PCI_DEVICE_CLASS(oc);
k->realize = my_realize;
k->vendor_id = MY_VENDOR_ID;
k->device_id = MY_DEVICE_ID;
k->revision = MY_REVISION;
k->class_id = PCI_CLASS_XXXX;
}
static const TypeInfo my_pci_device_info = {
.name = "my-pci-device"
.parent = TYPE_PCI_DEVICE,
.class_init = my_class_init,
.interfaces = {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
};
};
这个和其他类没有什么区别,只是父类设置成了TYPE_PCI_DEVICE,在类初始化的时候把父类的基本属性都设置类(vendor id等),realize中可以调用pci模块提供的比如:
pci_config_set_interrupt_pin()/msi_init(),
pci_register_bar()
这些函数,创建相应的pci资源,剩下的工作,留给父类去做就可以了。