PCI 總線學習系列,參考自
技術大牛博客: PCIe 掃盲系列博文連載目錄篇
書籍:王齊老師的《PCI Express 體系結構導讀》
下面的文章中加入了自己的一些理解和實際使用中遇到的一些場景,供日后查詢和回憶使用
PCI 總線定義了兩類配置請求, 一類是 Type 00h 配置請求, 另一類是 Type 01h 配置請求。 PCI 總線使用這些配置請求訪問 PCI 總線樹上的設備配置空間, 包括 PCI 橋和 PCI Agent 設備的配置空間。
- Type 00h 配置請求:用于訪問與 HOST 主橋或者 PCI 橋直接相連的 PCI Agent 設備或者 PCI 橋(僅能傳遞給目標設備,而不會被進一步轉發)
- Type 01h 配置請求: HOST 主橋或者 PCI 橋使用 Type 01h 配置請求, 需要至少穿越一個 PCI 橋, 訪問沒有與其直接相連的 PCI Agent 設備或者 PCI 橋(配置請求可以被進一步轉發)
1、Type 01h 和Type 00h 配置請求
??在 PCI 總線中, 只有 PCI 橋能夠接收 Type 01h 配置請求。Type 01h 配置請求不能直接發向最終的 PCI Agent 設備, 而只能由 PCI 橋將其轉換為 Type 01h 繼續發向其他 PCI 橋, 或者轉換為 Type 00h 配置請求發向 PCI Agent 設備。
??以 x86 系統為例,在地址周期中, HOST 主橋使用配置讀寫總線事務, 將 CONFIG_ADDRESS 寄存器的內容復制到 PCI 總線的 AD [31∶0] 信號線中。 CONFIG_ADDRESS 寄存器與 Type 01h 配置請求的對應關系如下圖所示
??從上圖中可以發現, CONFIG_ADDRESS 寄存器的內容基本上是原封不動地復制到 PCI 總線的AD [31∶0] 信號線上的。 其中 CONFIG_ADDRESS 的 Enable 位不被復制, 而 AD 總線的第 0 位必須為 1, 表示當前配置請求是 Type 01h。
??當 PCI 總線接收到 Type01 配置請求時, 將尋找合適的 PCI 橋接收這個配置信息。 如果這個配置請求是直接發向 PCI 橋下的與之直接相連的 PCI 設備時, PCI 橋將接收這個 Type 01 配置請求,并將其轉換為 Type 00h 配置請求; 否則 PCI 橋將當前 Type 01h 配置請求原封不動地傳遞給下一級 PCI 總線。
??如果 HOST 主橋或者 PCI 橋發起的是 Type 00h 配置請求, CONFIG_ADDRESS 寄存器與 AD [31∶0] 的轉換如下圖所示:
??此時處理器對 CONFIG_DATA 寄存器進行讀寫時, 處理器將 CONFIG_ADDRESS 寄存器中的 Function Number 和 Register Number 字段復制到 PCI 的 AD 總線的第10 ~2 位; 將 AD 總線的第 1~0 位賦值為 0b00。 PCI 總線在配置請求總線事務的地址周期根據 AD [1∶0] 判斷當前配置請求是 Type 00h 還是 Type 01h。
如果 AD [1∶0] 等于 0b 00 表示是 Type 00h 配置請求, 如果 AD [1∶0] 等于 0b 01 表示是 Type 01h 配置請求
??而 AD [31∶11] 與 CONFIG_ADDRESS 的 Device Number 字段有關, 在 Type 00h 配置請求的地址周期中, AD [31∶11] 位有且只有一位為 1, 其中 AD [31∶11] 的每一位選通一個 PCI 設備的配置空間。 PCI 設備配置空間的片選信號是 IDSEL, 因此 AD[31∶ 11] 將與 PCI 設備的 IDSEL 信號對應相連。
從這里可以知道, PCI 協議中的設備號,是由 PCI 設備的 IDSEL 信號線,與 PCI 總線上的地址線 AD[0 : 31] 的硬件連接關系確定的
1.1、總結
??上面講解的東西,更多的是為了理解 PCI 協議,真正在 PCI 總線驅動開發上用到的不多。比較常用的就是配置 HOST 主橋發出的到底是 Type 00h 還是 Type 01h 配置請求。
??有些板卡,硬件上已經配置好 HOST 發出的到底是 Type 00h 還是 Type 01h 配置請求,我們無需關心。有些板卡更靈活,需要我們手動在軟件上去配置,例如:當訪問的 bus 號為當前 PCI 控制器起始 bus 號 + 1 時,使用 Type 00h 配置請求;否則就是 Type 01h 配置請求。了解了底層的原理,這里我們具體場景具體分析就行,不必拘泥。
1.2 代碼實例
以 rk3568 為例:
static void __iomem *rk_pcie_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, int where)
{
....../* 如果訪問的是 root bus,使用 Type 00h,不需要轉發;否則使用 Type 01h*/if (pci_is_root_bus(bus->parent))atu_type = PCIE_ATU_TYPE_CFG0;elseatu_type = PCIE_ATU_TYPE_CFG1;
......
}
以 Linux 源碼中,dw IP 核代碼為例:
static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,u32 devfn, int where, int size, u32 *val)
{
......if (bus->parent->number == pp->root_bus_nr) {type = PCIE_ATU_TYPE_CFG0;cpu_addr = pp->cfg0_base;cfg_size = pp->cfg0_size;va_cfg_base = pp->va_cfg0_base;} else {type = PCIE_ATU_TYPE_CFG1;cpu_addr = pp->cfg1_base;cfg_size = pp->cfg1_size;va_cfg_base = pp->va_cfg1_base;}
......
}
2、PCI 總線配置請求的轉換原則
??當 CONFIG_ADDRESS 寄存器的 Enable 位為 1, 系統軟件訪問 CONFIG_DATA 寄存器時,HOST 主橋可以產生兩類 PCI 總線配置讀寫總線事務, 分別為 Type 00h 和 Type 01h 配置讀寫總線事務。 在配置讀寫總線事務的地址周期和數據周期中,CONFIG_ADDRESS 和 CONFIG_DATA 寄存器中的數據將被放置到 PCI 總線的 AD 總線上。 其中 Type 00h 和 Type 01h 配置讀寫總線事務映射到 AD 總線的數據并不相同。
??其中 Type 00h 配置請求可以直接讀取 PCI Agent 設備的配置空間, 而 Type 01h 配置請求在通過 PCI 橋時, 最終將被轉換為 Type 00h 配置請求, 并讀取 PCI Agent 設備的配置寄存器。 本節重點講述 PCI 橋如何將 Type 01h 配置請求轉換為 Type 00h 配置請求。
??首先 Type 00h 配置請求不會被轉換成 Type 01h 配置請求, 因為 Type 00h 配置請求是發向最終 PCI Agent 設備, 這些 PCI Agent 設備不會轉發這些配置請求。當 CONFIG_ADDRESS 寄存器的 Bus Number 字段為 0 時, 處理器對 CONFIG_DATA 寄存器操作時, HOST 主橋將直接產生 Type 00h 配置請求, 掛接在 PCI 總線 0 上的某個設備將通過 ID 譯碼接收這個 Type 00h 配置請求, 并對配置寄存器進行讀寫操作。 如果 PCI 總線上沒有設備接收這個 Type 00h 配置請求, 將引發 Master Abort, 詳情見 PCI 總線規范, 本節對此不做進一步說明。
??如果 CONFIG_ADDRESS 寄存器的 Bus Number 字段為 n ( n≠ 0), 即訪問的 PCI 設備不是直接掛接在 PCI 總線 0 上的, 此時 HOST 主橋對 CONFIG_DATA 寄存器操作時, 將產生Type 01h 配置請求, PCI 總線 0 將遍歷所有在這條總線上的 PCI 橋, 確定由哪個 PCI 橋接收這個 Type 01h 配置請求。如果 n 大于或等于某個 PCI 橋的 Secondary Bus Number 寄存器, 而且小于或等于 Subordinate Bus number 寄存器, 那么這個 PCI 橋將接收在當前 PCI 總線上的 Type 01 配置請求,并采用以下規則進行遞歸處理:
- 開始。
- 遍歷當前 PCI 總線的所有 PCI 橋。
- 如果 n 等于某個 PCI 橋的 Secondary Bus Number 寄存器, 說明這個 Type 01 配置請求的目標設備直接連接在該 PCI 橋的 Secondary bus 上。 此時 PCI 橋將 Type 01 配置請求轉換為 Type 00h 配置請求, 并將這個配置請求發送到 PCI 橋的 Secondary Bus 上, Secondary Bus 上的某個設備將響應這個 Type 00h 配置請求, 并與 HOST 主橋進行配置信息的交換,轉 (5) 。
- 如果 n 大于 PCI 橋的 Secondary Bus Number 寄存器, 而且小于或等于 PCI 橋的 Subordinate Bus number 寄存器, 說明這個 Type 01 配置請求的目標設備不與該 PCI 橋的 Secondary Bus 直接相連, 但是由這個 PCI 橋下游總線上的某個 PCI 橋管理。 此時 PCI 橋將首先認領這個 Type 01 配置請求, 并將其轉發到 Secondary Bus, 轉 (2)。
- 結束。
3、PCI 總線樹 Bus 號的初始化
參考 PCI 總線學習筆記(三)
4、PCI 總線 Device 號的分配
??一條 PCI 總線會掛接各種各樣的 PCI 設備, 而每一個 PCI 設備在 PCI 總線下具有唯一的設備號。 系統軟件通過總線號和設備號定位一個 PCI 設備之后, 才能訪問這個 PCI 設備的配置寄存器。
??PCI 設備的 IDSEL 信號與 PCI 總線的 AD [31∶ 0] 信號的連接關系決定了該設備在這條 PCI 總線的設備號。 如上文所述, 每一個 PCI 設備都使用獨立的 IDSEL 信號, 該信號將與 PCI 總線的 AD [31∶ 0] 信號連接, IDSEL 信號的含義見第 PCI 總線學習筆記(一)。
??在此我們簡要回顧 PCI 的配置讀寫事務使用的時序。 如下圖所示, PCI 總線事務由一個地址周期加若干個數據周期組成。 在進行配置讀寫請求總線事務時, C / BE# 信號線的值在地址周期中為 0x1010 或者為 0x1011, 表示當前總線事務為配置讀或者配置寫請求。 此時出現在 AD [31∶ 0] 總線上的值并不是目標設備的 PCI 總線地址, 而是目標設備的 ID 號, 這與 PCI 總線進行 I / O 或者存儲器請求時不同, 因為 PCI 總線使用 ID 號而不是 PCI 總線地址對配置空間進行訪問。
??如圖 2-12 所示, 在配置讀寫總線事務的地址周期中, AD [10∶ 0] 信號已經被 Function Number 和 Register Number 使用, 因此 PCI 設備的 IDSEL 只能與 AD [31∶ 11] 信號連接。認真的讀者一定可以發現在 CONFIG_ADDRESS 寄存器中 Device Number 字段一共有 5 位可以表示 32 個設備, 而 AD [31∶ 11] 只有 21 位, 顯然在這兩者之間無法建立一一對應的映射關系。 因此在一條 PCI 總線上如果有 21 個以上的 PCI 設備, 那么總是有幾個設備無法與 AD [31∶ 11] 信號線連接, 從而 PCI 總線無法訪問這些設備。 因為 PCI 總線在配置請求的地址周期中, 只能使用第 31 ~ 11 這些 AD 信號, 所以在一條總線上最多也只能掛接 21 個 PCI 設備。 這 21 個設備可能是從 0 到 20, 也可能是從 11 到 31 排列。 從而系統軟件在遍歷 PCI 總線時, 還是需要從 0 到 31 遍歷整條 PCI 總線。
??在實際的應用中, 一條 PCI 總線能夠掛接 21 個設備已經足夠了, 實際上由于 PCI 總線的負載能力有限, 即便在總線頻率為 33 MHz 的情況下, 在一條 PCI 總線中最多也只能掛接 10 個負載, 一條 PCI 總線所能掛接的負載詳見表 1-1。 AD 信號線與 PCI 設備 IDSEL 線的連接關系如圖 2-14 所示
??PCI 總線推薦了一種 Device Number 字段與 AD [31∶ 16] 之間的映射關系。 其中 PCI 設備 0 與 Device Number 字段的 0b00000 對應; PCI 設備 1 與 Device Number 字段的 0b00001 對應, 并以此類推, PCI 設備 15 與 Device Number 字段的 0b01111 對應。
??在這種映射關系之下, 一條 PCI 總線中, 與信號線 AD16 相連的 PCI 設備的設備號為 0;與信號線 AD17 相連的 PCI 設備的設備號為 1; 以此類推, 與信號線 AD31 相連的 PCI 設備的設備號為 15。 在 Type 00h 配置請求中, 設備號并沒有像 Function Number 和 Register Number 那樣以編碼的形式出現在 AD 總線上, 而是與 AD 信號一一對應, 如圖 2-12 所示。
??這里有一個原則需要讀者注意, 就是對 PCI 設備的配置寄存器進行訪問時, 一定要有確定的 Bus Number、 Device Number、 Function Number 和 Register Number, 這 “四元組” 缺一不可。 在 Type 00h 配置請求中, Device Number 由 AD [31∶ 11] 信號線與 PCI 設備 IDSEL 信號的連接關系確定; Function Number 保存在 AD [10∶ 8] 字段中; 而 Register Number 保存在 AD [7∶ 2] 字段中; 在 Type 01h 配置請求中, 也有完整的四元組信息。