PCI/e配置空间
PCI/PCIe的配置空间Configuration Space是一个与Memory空间和IO空间并列的独立的空间。PCI Configuration Space有256 Bytes,对于PCIe Configuration Space,扩展为4096 Bytes,PCIe是在PCI基础上发展的协议,并且在此基础之上进行了扩展,其扩展形式是通过一种称为Capability的寄存器块来完成的。
访问方式
IO方式
CPU提供了两组I/O寄存器用于访问配置空间:
- 配置空间控制寄存器 CF8h-CFBh
- 配置空间数据寄存器 CFCh-CFFh
传统方式,写IO端口CFCh和CF8h。只能访问PCI/PCIe设备的开始256个字节(PCI设备的配置空间本来就只有256个字节......)
// for PCI
address = BIT31 | ((Bus & 0xFF) << 16) | ((Dev & 0x1F) << 11) | ((Fun & 0x7) << 8) | (Reg & 0xfffffffc);//BIT31=0x80000000
// write cfg register
IoWrite8(0xcf8, address);
// read data register
data8 = IoRead8(0xcfc);
data16 = IoRead16(0xcfc);
data32 = IoRead32(0xcfc);
Bit31代表enable bit。一定要置起来,否则不起作用。
MMIO方式
对于MMIO的访问,跟访问内存的方式一样,它从称为PCIEXBAR的基地址开始,有很大的一段空间,这个PCIEXBAR的值根据不同的平台可能不同,大致可能值有0xB0000000、0xC0000000等(飞腾用的就是0xB0000000)。
#define pcie_addr(m, b, d, f, o) (m + ((b & 0xff) << 20) + ((d & 0x1f) << 15) + ((f & 0x7) << 12) + (o & 0xfffffffc)
#define mmio_read8(addr) (*(volatile uint8 *)addr)
#define mmio_write8(addr, data8) *(volatile uint8 *)addr = data8
#define mmio_read16(addr) (*(volatile uint16 *)addr)
#define mmio_write16(addr, data16) *(volatile uint16 *)addr = data16
#define mmio_read32(addr) (*(volatile uint32 *)addr)
#define mmio_write32(addr, data32) *(volatile uint32 *)addr = data32
配置寄存器组内保留了对PCI设备的基本特性进行详尽说明的可读信息,CPU读取这些信息后,就可以为PCI设备设定符合需要的配置内容,从而实现自动配置。这些可读信息包括:
Vendor ID :设备供应商编号,由PCI SIG国际组织分配。
Device ID :特定设备编号,由设备供应商分配。
Revision ID :设备的特定版本号,由设备供应商分配。
Class Code :设备的功能类别编号。
Header Type :指示Header 中从地址10H到3FH区域的内容格式,同时指示该设备是否为多功能设备。
扫描PCI设备的时候,我们可以直接通过Vendor ID与Device ID是否为0xffff来判断设备是否存在,如果是,则设备不存在,不过也有一种情况,国产并不完备,在硬件设计的时候,很有可能这个不存在的值并不是0xffff,也有可能是0才不存在:
//
// Create PCI address map in terms of Bus, Device and Func
//
Address = EFI_PCI_ADDRESS (Bus, Device, Func, 0);
//
// Read the Vendor ID register
//
Status = PciRootBridgeIo->Pci.Read (
PciRootBridgeIo,
EfiPciWidthUint32,
Address,
1,
Pci
);
if (!EFI_ERROR (Status) && (Pci->Hdr).VendorId != 0xffff && (Pci->Hdr).VendorId != 0) {
//
// Read the entire config header for the device
//
Status = PciRootBridgeIo->Pci.Read (
PciRootBridgeIo,
EfiPciWidthUint32,
Address,
sizeof (PCI_TYPE00) / sizeof (UINT32),
Pci
);
return EFI_SUCCESS;
}
这部分代码在PciEnumeratorSupport.c文件中有定义