1. 程式人生 > >pci列舉初始化部分(2)

pci列舉初始化部分(2)

---恢復內容開始---

1.2.8判斷pcie裝置是否支援雷電技術

Intel具有一種基於Thunderbolt技術的PCIE變體,它結合了DisplayPort和PCIe協議,與Mini DisplayPort相容。
Thunderbolt技術融合兩種通訊方法或者說協議,其中PCI Express用於資料傳輸,可以連線幾乎任何型別的裝置,DisplayPort用於顯示,能同步傳輸1080p乃至超高清視訊和最多八聲道音訊。
因此程式碼只在intel生產的裝置中進行判別。
set_pcie_thunderbolt()

while ((vsec = pci_find_next_ext_capability(dev, vsec,
          PCI_EXT_CAP_ID_VNDR))) {
  pci_read_config_dword(dev, vsec + PCI_VNDR_HEADER, &header);

  /* Is the device part of a Thunderbolt controller? */
  //裝置是否具有雷電控制器
  if (dev->vendor == PCI_VENDOR_ID_INTEL &&
      PCI_VNDR_HEADER_ID(header) == PCI_VSEC_ID_INTEL_TBT) {
   dev->is_thunderbolt = 1;
   return;
  }
 }

其中有

#define PCI_VNDR_HEADER_ID(x)   ((x) & 0xffff)
#define PCI_VSEC_ID_INTEL_TBT   0x1234  //雷電介面

1.2.9修復某些特殊的bug

對於某些bug,只存在於特定體系或裝置,無法在此處進行列舉,因此提供一個hook用於修復特殊裝置的bug。而hook通過核心配置情況進行掛載。
pci_fixup_device()

//查詢是否存在廠商號裝置號相同的情況
for (; f < end; f++)
  if ((f->class == (u32) (dev->class >> f->class_shift) ||
       f->class == (u32) PCI_ANY_ID) &&
      (f->vendor == dev->vendor ||
       f->vendor == (u16) PCI_ANY_ID) &&
      (f->device == dev->device ||
       f->device == (u16) PCI_ANY_ID)) {
   void (*hook)(struct pci_dev *dev);

//獲取hook函式指標,用於修復特定bug
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
   hook = offset_to_ptr(&f->hook_offset);
#else
   hook = f->hook;
#endif
   calltime = fixup_debug_start(dev, hook);
   hook(dev);
   fixup_debug_report(dev, calltime, hook);
  }

其中hook可有由

#define DECLARE_PCI_FIXUP_CLASS_EARLY(vendor, device, class,    \
      class_shift, hook)    \
 DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early,    \
  hook, vendor, device, class, class_shift, hook)

等巨集進行掛載。

  • 舉個例子
    arch\x86\pci
static void pci_early_fixup_cyrix_5530(struct pci_dev *dev)
{
 u8 r;
 /* clear 'F4 Video Configuration Trap' bit */
 pci_read_config_byte(dev, 0x42, &r);
 r &= 0xfd;
 pci_write_config_byte(dev, 0x42, r);
}
//註冊回撥函式到pci_early_fixup段中,用於修復x86平臺下該裝置出現的bug。
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
   pci_early_fixup_cyrix_5530);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
   pci_early_fixup_cyrix_5530);

通過DECLARE_PCI_FIXUP_EARLY巨集將pci_early_fixup_cyrix_5530回撥函式註冊到pci_fixup_early段中,在通過hook函式指標呼叫並執行函式解決x86平臺下5530裝置特有的錯誤情況。

1.2.10 設定command暫存器

1.2.10.1 錯誤情況禁止IO空間和記憶體空間

//沒有識別正確的
 if (dev->non_compliant_bars) {
  pci_read_config_word(dev, PCI_COMMAND, &cmd);
  if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
   pci_info(dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
   cmd &= ~PCI_COMMAND_IO;
   cmd &= ~PCI_COMMAND_MEMORY;
   pci_write_config_word(dev, PCI_COMMAND, cmd);
  }
 }

COMMAND[0] :IO SPACE位
該位表示PCI裝置是否響應I/O請求,為1時響應,0時不響應。
COMMAND[1]: Memory Space位
該位表示PCI裝置是否響應儲存器請求,為1時響應,0時不響應。

1.2.10.2 判斷command暫存器中斷禁止位是否可寫

pci_intx_mask_broken()

 u16 orig, toggle, new;

 pci_read_config_word(dev, PCI_COMMAND, &orig);
 toggle = orig ^ PCI_COMMAND_INTX_DISABLE;
 pci_write_config_word(dev, PCI_COMMAND, toggle);
 pci_read_config_word(dev, PCI_COMMAND, &new);
 pci_write_config_word(dev, PCI_COMMAND, orig);
  //PCI_COMMAND_INTX_DISABLE是預留位並在PCIr2.3版本是隻讀的,因此嚴格輸出
  //如果他是不可寫的,則這個裝置沒有損壞。
 if (new != toggle)
  return 1;
 return 0;

COMMAND[10] :interrupt Disable位
復位值為0,該位為1時,PCI裝置不能通過INTx訊號向HOST主橋提交中斷請求,為0時可以使用INTx訊號提出請求。當PCI裝置使用MSI中斷方式提交中斷請求時,該位將被置為1。

1.2.11 根據不同頭型別做初始化

1.2.11.1標準頭

1.2.11.1.1獲取裝置資訊

獲取裝置中斷資訊和BAR空間資訊。

switch (dev->hdr_type) {    /* header type */
 case PCI_HEADER_TYPE_NORMAL:   /* standard header */
//錯誤情況
  if (class == PCI_CLASS_BRIDGE_PCI)
   goto bad;
//獲取中斷號和中斷引腳
  pci_read_irq(dev);        
//獲取基地址
  pci_read_bases(dev, 6, PCI_ROM_ADDRESS);

//獲取子廠商號裝置號
  pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);

(1)中斷資訊
包括中斷號,中斷引腳。
pci_read_irq()

 pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
 dev->pin = irq;
 if (irq)
  pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
 dev->irq = irq;

(2)BAR空間資訊
包括BAR空間大小,空間基地址,空間型別(IO/記憶體),空間位數(32位,64位)等。
pci_read_bases函式呼叫__pci_read_base函式來獲取這些資訊。
__pci_read_base()
(2.1)預設為 32位PCI時,獲取PCI空間大小

 pci_read_config_dword(dev, pos, &l);
 pci_write_config_dword(dev, pos, l | mask);
 pci_read_config_dword(dev, pos, &sz);
 pci_write_config_dword(dev, pos, l);
  • (2.2) 通過decode_bar()函式判別PCIBAR空間型別*
  • 判斷是否IO空間型別。
//判斷是否IO空間
 if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
  flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
  flags |= IORESOURCE_IO;
  return flags;
 }

通過BAR基地址的bit[0]可判別BAR空間型別為IO空間還是記憶體空間,1表示IO空間,0表示記憶體空間。
bit[1,2]則表示了BAR空間為32位還是64位。
bit[3]表示BAR是否支援預取。
其中bit[1,2]=01在linux4.x程式碼中代表1M這種型別。

  • 判斷記憶體空間位數
//判斷記憶體空間位數
 mem_type = bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
 switch (mem_type) {
 case PCI_BASE_ADDRESS_MEM_TYPE_32:
  break;
 case PCI_BASE_ADDRESS_MEM_TYPE_1M:
  /* 1M mem BAR treated as 32-bit BAR */
  break;
 case PCI_BASE_ADDRESS_MEM_TYPE_64:
  flags |= IORESOURCE_MEM_64;
  break;
 default:
  /* mem unknown type treated as 32-bit BAR */
  break;
 }
  • 64位的處理
//64位重新獲取裝置基地址和大小
  pci_read_config_dword(dev, pos + 4, &l);
  pci_write_config_dword(dev, pos + 4, ~0);
  pci_read_config_dword(dev, pos + 4, &sz);
  pci_write_config_dword(dev, pos + 4, l);

獲取地址和大小後還需判斷其是否超過體系所支援的位數,32位下是不能支援64位PCI裝置的,PCI空間大小也是不能超過4G的。

  • 測試BAR空間對映是否正確。
 pcibios_bus_to_resource(dev->bus, res, &region);
 pcibios_resource_to_bus(dev->bus, &inverted_region, res);

pcibios_bus_to_resource函式用於匯流排地址轉換到資源地址(用於CPU的實體地址)。
pcibios_resource_to_bus執行相反操作,如果,相互轉換的值不正確,則不能使用該裝置。

1.2.11.1.2 對於ATA控制器的特殊設定

傳統模式ATA控制器具有固定地址。且BAR0-3的資料在某些情況下是無效的。
classCode暫存器用於判斷裝置類別。

#define PCI_CLASS_STORAGE_IDE   0x0101

通過base class=01,subclass =01,可以確定為IDE型別儲存器
605842281.png

606033218.png

對於IDE控制器型別的PCI裝置,又通過interface暫存器欄位進行了細分:
606501953.png

interface:
bit7:確定是否為主IDE裝置
bit3:可程式設計指示器(次通道)
bit2:操作模式(次通道)
bit1:可程式設計指示器(主通道)
bit0:操作模式(主通道)
根據PCI IDE Controller Specification Revision 1.0文件可知

607509671.png

對於主通道來說,命令暫存器被固定為1f0h-1f7h,其控制塊地址為3fh,
次通道命令暫存器地址170h-177h,控制暫存器376h.

  if (class == PCI_CLASS_STORAGE_IDE) {
   u8 progif;
   pci_read_config_byte(dev,, &progif);
//主通道IDE控制器 
  if ((progif & 1) == 0) {
    region.start = 0x1F0;
    region.end = 0x1F7;
    res = &dev->resource[0];
    res->flags = LEGACY_IO_RESOURCE;
    pcibios_bus_to_resource(dev->bus, res, &region);
    pci_info(dev, "legacy IDE quirk: reg 0x10: %pR\n",
      res);
    region.start = 0x3F6;
    region.end = 0x3F6;
    res = &dev->resource[1];
    res->flags = LEGACY_IO_RESOURCE;
    pcibios_bus_to_resource(dev->bus, res, &region);
    pci_info(dev, "legacy IDE quirk: reg 0x14: %pR\n",
      res);
   }
//次通道IDE控制器
if ((progif & 4) == 0) {
    region.start = 0x170;
    region.end = 0x177;
    res = &dev->resource[2];
    res->flags = LEGACY_IO_RESOURCE;
    pcibios_bus_to_resource(dev->bus, res, &region);
    pci_info(dev, "legacy IDE quirk: reg 0x18: %pR\n",
      res);
    region.start = 0x376;
    region.end = 0x376;
    res = &dev->resource[3];
    res->flags = LEGACY_IO_RESOURCE;
    pcibios_bus_to_resource(dev->bus, res, &region);
    pci_info(dev, "legacy IDE quirk: reg 0x1c: %pR\n",
      res);
   }
  }
  break;

1.2.11.2橋頭

PCI-PCI橋若要求譯碼(比如透明橋),橋的程式設計介面程式碼必須是0x01

case PCI_HEADER_TYPE_BRIDGE:    /* bridge header */
  if (class != PCI_CLASS_BRIDGE_PCI)
   goto bad;
  pci_read_irq(dev);
  dev->transparent = ((dev->class & 0xff) == 1);
  pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
//查詢是否支援熱插拔
  set_pcie_hotplug_bridge(dev);
//查詢capability中子系統的廠商號和裝置號
  pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
  if (pos) {
   pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
   pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
  }
  break;

對於熱插拔
set_pcie_hotplug_bridge()

 pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32);
 if (reg32 & PCI_EXP_SLTCAP_HPC)
  pdev->is_hotplug_bridge = 1;

其中

#define PCI_EXP_SLTCAP  20  /* Slot Capabilities */
#define PCI_EXP_SLTCAP_HPC  0x00000040 /* Hot-Plug Capable */

通過查詢pcie_3.0匯流排規範,可以看到偏移為14h的暫存器中有關於是否支援熱插拔的狀態位。
{{609199765.png(uploading...)}}

bit6 = 1時,表示裝置支援熱插拔。

1.2.11.2cardBus橋頭

 case PCI_HEADER_TYPE_CARDBUS:  /* CardBus bridge header */
  if (class != PCI_CLASS_BRIDGE_CARDBUS)
   goto bad;
  pci_read_irq(dev);
  pci_read_bases(dev, 1, 0);
  pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
  pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
  break;

結束

pci_setup_device()函式完成了對單個裝置的裝置和檢測,並將獲取的資訊存取裝置結構體中,用於後期具體裝置的使用。607509671.png

---恢復內容結束---

### 1.2.8判斷pcie裝置是否支援雷電技術

Intel具有一種基於Thunderbolt技術的PCIE變體,它結合了DisplayPort和PCIe協議,與Mini DisplayPort相容。
Thunderbolt技術融合兩種通訊方法或者說協議,其中PCI Express用於資料傳輸,可以連線幾乎任何型別的裝置,DisplayPort用於顯示,能同步傳輸1080p乃至超高清視訊和最多八聲道音訊。
因此程式碼只在intel生產的裝置中進行判別。
set_pcie_thunderbolt()

while ((vsec = pci_find_next_ext_capability(dev, vsec,
          PCI_EXT_CAP_ID_VNDR))) {
  pci_read_config_dword(dev, vsec + PCI_VNDR_HEADER, &header);

  /* Is the device part of a Thunderbolt controller? */
  //裝置是否具有雷電控制器
  if (dev->vendor == PCI_VENDOR_ID_INTEL &&
      PCI_VNDR_HEADER_ID(header) == PCI_VSEC_ID_INTEL_TBT) {
   dev->is_thunderbolt = 1;
   return;
  }
 }

其中有

#define PCI_VNDR_HEADER_ID(x)   ((x) & 0xffff)
#define PCI_VSEC_ID_INTEL_TBT   0x1234  //雷電介面

1.2.9修復某些特殊的bug

對於某些bug,只存在於特定體系或裝置,無法在此處進行列舉,因此提供一個hook用於修復特殊裝置的bug。而hook通過核心配置情況進行掛載。
pci_fixup_device()

//查詢是否存在廠商號裝置號相同的情況
for (; f < end; f++)
  if ((f->class == (u32) (dev->class >> f->class_shift) ||
       f->class == (u32) PCI_ANY_ID) &&
      (f->vendor == dev->vendor ||
       f->vendor == (u16) PCI_ANY_ID) &&
      (f->device == dev->device ||
       f->device == (u16) PCI_ANY_ID)) {
   void (*hook)(struct pci_dev *dev);

//獲取hook函式指標,用於修復特定bug
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
   hook = offset_to_ptr(&f->hook_offset);
#else
   hook = f->hook;
#endif
   calltime = fixup_debug_start(dev, hook);
   hook(dev);
   fixup_debug_report(dev, calltime, hook);
  }

其中hook可有由

#define DECLARE_PCI_FIXUP_CLASS_EARLY(vendor, device, class,    \
      class_shift, hook)    \
 DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early,    \
  hook, vendor, device, class, class_shift, hook)

等巨集進行掛載。

  • 舉個例子
    arch\x86\pci
static void pci_early_fixup_cyrix_5530(struct pci_dev *dev)
{
 u8 r;
 /* clear 'F4 Video Configuration Trap' bit */
 pci_read_config_byte(dev, 0x42, &r);
 r &= 0xfd;
 pci_write_config_byte(dev, 0x42, r);
}
//註冊回撥函式到pci_early_fixup段中,用於修復x86平臺下該裝置出現的bug。
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
   pci_early_fixup_cyrix_5530);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
   pci_early_fixup_cyrix_5530);

通過DECLARE_PCI_FIXUP_EARLY巨集將pci_early_fixup_cyrix_5530回撥函式註冊到pci_fixup_early段中,在通過hook函式指標呼叫並執行函式解決x86平臺下5530裝置特有的錯誤情況。

1.2.10 設定command暫存器

1.2.10.1 錯誤情況禁止IO空間和記憶體空間

//沒有識別正確的
 if (dev->non_compliant_bars) {
  pci_read_config_word(dev, PCI_COMMAND, &cmd);
  if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
   pci_info(dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
   cmd &= ~PCI_COMMAND_IO;
   cmd &= ~PCI_COMMAND_MEMORY;
   pci_write_config_word(dev, PCI_COMMAND, cmd);
  }
 }

COMMAND[0] :IO SPACE位
該位表示PCI裝置是否響應I/O請求,為1時響應,0時不響應。
COMMAND[1]: Memory Space位
該位表示PCI裝置是否響應儲存器請求,為1時響應,0時不響應。

1.2.10.2 判斷command暫存器中斷禁止位是否可寫

pci_intx_mask_broken()

 u16 orig, toggle, new;

 pci_read_config_word(dev, PCI_COMMAND, &orig);
 toggle = orig ^ PCI_COMMAND_INTX_DISABLE;
 pci_write_config_word(dev, PCI_COMMAND, toggle);
 pci_read_config_word(dev, PCI_COMMAND, &new);
 pci_write_config_word(dev, PCI_COMMAND, orig);
  //PCI_COMMAND_INTX_DISABLE是預留位並在PCIr2.3版本是隻讀的,因此嚴格輸出
  //如果他是不可寫的,則這個裝置沒有損壞。
 if (new != toggle)
  return 1;
 return 0;

COMMAND[10] :interrupt Disable位
復位值為0,該位為1時,PCI裝置不能通過INTx訊號向HOST主橋提交中斷請求,為0時可以使用INTx訊號提出請求。當PCI裝置使用MSI中斷方式提交中斷請求時,該位將被置為1。

1.2.11 根據不同頭型別做初始化

1.2.11.1標準頭

1.2.11.1.1獲取裝置資訊

獲取裝置中斷資訊和BAR空間資訊。

switch (dev->hdr_type) {    /* header type */
 case PCI_HEADER_TYPE_NORMAL:   /* standard header */
//錯誤情況
  if (class == PCI_CLASS_BRIDGE_PCI)
   goto bad;
//獲取中斷號和中斷引腳
  pci_read_irq(dev);        
//獲取基地址
  pci_read_bases(dev, 6, PCI_ROM_ADDRESS);

//獲取子廠商號裝置號
  pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);

(1)中斷資訊
包括中斷號,中斷引腳。
pci_read_irq()

 pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
 dev->pin = irq;
 if (irq)
  pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
 dev->irq = irq;

(2)BAR空間資訊
包括BAR空間大小,空間基地址,空間型別(IO/記憶體),空間位數(32位,64位)等。
pci_read_bases函式呼叫__pci_read_base函式來獲取這些資訊。
__pci_read_base()
(2.1)預設為 32位PCI時,獲取PCI空間大小

 pci_read_config_dword(dev, pos, &l);
 pci_write_config_dword(dev, pos, l | mask);
 pci_read_config_dword(dev, pos, &sz);
 pci_write_config_dword(dev, pos, l);
  • (2.2) 通過decode_bar()函式判別PCIBAR空間型別*
  • 判斷是否IO空間型別。
//判斷是否IO空間
 if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
  flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
  flags |= IORESOURCE_IO;
  return flags;
 }

通過BAR基地址的bit[0]可判別BAR空間型別為IO空間還是記憶體空間,1表示IO空間,0表示記憶體空間。
bit[1,2]則表示了BAR空間為32位還是64位。
bit[3]表示BAR是否支援預取。
其中bit[1,2]=01在linux4.x程式碼中代表1M這種型別。

  • 判斷記憶體空間位數
//判斷記憶體空間位數
 mem_type = bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
 switch (mem_type) {
 case PCI_BASE_ADDRESS_MEM_TYPE_32:
  break;
 case PCI_BASE_ADDRESS_MEM_TYPE_1M:
  /* 1M mem BAR treated as 32-bit BAR */
  break;
 case PCI_BASE_ADDRESS_MEM_TYPE_64:
  flags |= IORESOURCE_MEM_64;
  break;
 default:
  /* mem unknown type treated as 32-bit BAR */
  break;
 }
  • 64位的處理
//64位重新獲取裝置基地址和大小
  pci_read_config_dword(dev, pos + 4, &l);
  pci_write_config_dword(dev, pos + 4, ~0);
  pci_read_config_dword(dev, pos + 4, &sz);
  pci_write_config_dword(dev, pos + 4, l);

獲取地址和大小後還需判斷其是否超過體系所支援的位數,32位下是不能支援64位PCI裝置的,PCI空間大小也是不能超過4G的。

  • 測試BAR空間對映是否正確。
 pcibios_bus_to_resource(dev->bus, res, &region);
 pcibios_resource_to_bus(dev->bus, &inverted_region, res);

pcibios_bus_to_resource函式用於匯流排地址轉換到資源地址(用於CPU的實體地址)。
pcibios_resource_to_bus執行相反操作,如果,相互轉換的值不正確,則不能使用該裝置。

1.2.11.1.2 對於ATA控制器的特殊設定

傳統模式ATA控制器具有固定地址。且BAR0-3的資料在某些情況下是無效的。
classCode暫存器用於判斷裝置類別。

#define PCI_CLASS_STORAGE_IDE   0x0101

通過base class=01,subclass =01,可以確定為IDE型別儲存器
605842281.png

606033218.png

對於IDE控制器型別的PCI裝置,又通過interface暫存器欄位進行了細分:
606501953.png

interface:
bit7:確定是否為主IDE裝置
bit3:可程式設計指示器(次通道)
bit2:操作模式(次通道)
bit1:可程式設計指示器(主通道)
bit0:操作模式(主通道)
根據PCI IDE Controller Specification Revision 1.0文件可知

607509671.png

對於主通道來說,命令暫存器被固定為1f0h-1f7h,其控制塊地址為3fh,
次通道命令暫存器地址170h-177h,控制暫存器376h.

  if (class == PCI_CLASS_STORAGE_IDE) {
   u8 progif;
   pci_read_config_byte(dev,, &progif);
//主通道IDE控制器 
  if ((progif & 1) == 0) {
    region.start = 0x1F0;
    region.end = 0x1F7;
    res = &dev->resource[0];
    res->flags = LEGACY_IO_RESOURCE;
    pcibios_bus_to_resource(dev->bus, res, &region);
    pci_info(dev, "legacy IDE quirk: reg 0x10: %pR\n",
      res);
    region.start = 0x3F6;
    region.end = 0x3F6;
    res = &dev->resource[1];
    res->flags = LEGACY_IO_RESOURCE;
    pcibios_bus_to_resource(dev->bus, res, &region);
    pci_info(dev, "legacy IDE quirk: reg 0x14: %pR\n",
      res);
   }
//次通道IDE控制器
if ((progif & 4) == 0) {
    region.start = 0x170;
    region.end = 0x177;
    res = &dev->resource[2];
    res->flags = LEGACY_IO_RESOURCE;
    pcibios_bus_to_resource(dev->bus, res, &region);
    pci_info(dev, "legacy IDE quirk: reg 0x18: %pR\n",
      res);
    region.start = 0x376;
    region.end = 0x376;
    res = &dev->resource[3];
    res->flags = LEGACY_IO_RESOURCE;
    pcibios_bus_to_resource(dev->bus, res, &region);
    pci_info(dev, "legacy IDE quirk: reg 0x1c: %pR\n",
      res);
   }
  }
  break;

1.2.11.2橋頭

PCI-PCI橋若要求譯碼(比如透明橋),橋的程式設計介面程式碼必須是0x01

case PCI_HEADER_TYPE_BRIDGE:    /* bridge header */
  if (class != PCI_CLASS_BRIDGE_PCI)
   goto bad;
  pci_read_irq(dev);
  dev->transparent = ((dev->class & 0xff) == 1);
  pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
//查詢是否支援熱插拔
  set_pcie_hotplug_bridge(dev);
//查詢capability中子系統的廠商號和裝置號
  pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
  if (pos) {
   pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
   pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
  }
  break;

對於熱插拔
set_pcie_hotplug_bridge()

 pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32);
 if (reg32 & PCI_EXP_SLTCAP_HPC)
  pdev->is_hotplug_bridge = 1;

其中

#define PCI_EXP_SLTCAP  20  /* Slot Capabilities */
#define PCI_EXP_SLTCAP_HPC  0x00000040 /* Hot-Plug Capable */

通過查詢pcie_3.0匯流排規範,可以看到偏移為14h的暫存器中有關於是否支援熱插拔的狀態位。

bit6 = 1時,表示裝置支援熱插拔。

1.2.11.2cardBus橋頭

 case PCI_HEADER_TYPE_CARDBUS:  /* CardBus bridge header */
  if (class != PCI_CLASS_BRIDGE_CARDBUS)
   goto bad;
  pci_read_irq(dev);
  pci_read_bases(dev, 1, 0);
  pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
  pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
  break;

結束

pci_setup_device()函式完成了對單個裝置的裝置和檢測,並將獲取的資訊存取裝置結構體中,用於後期具體裝置的使用。607509671.png