730xd pcie拆分 bios版本 pcie拆分改bios_PCI




PCI设备的扫描是基于深度优先搜索算法(DFS:Depth First Search),也就是说,下级分支最多的PCI桥将最先完成其子设备的扫描。下面我们以图片来具体说明,BIOS是如何一步步完成PCI 设备扫描的。


第一步:

PCI Host 主桥扫描Bus 0上的设备(在一个处理器系统中,一般将与HOST主桥直接相连的PCI总线被命名为PCI Bus 0),系统首先会忽略Bus 0上的D1,D2等不会挂接PCI桥的设备,主桥发现Bridge 1后,将Bridge1 下面的PCI Bus定为 Bus 1,系统将初始化Bridge 1的配置空间,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成0和1,以表明Bridge1 的上游总线是0,下游总线是1,由于还无法确定Bridge1下挂载设备的具体情况,系统先暂时将Subordinate Bus Number设为0xFF。如下图所示:

730xd pcie拆分 bios版本 pcie拆分改bios_linux驱动_02


第二步:

系统开始扫描Bus 1,将会发现Bridge 2。系统将Bridge 2下面的PCI Bus定为Bus 2,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成1和2,和上一步一样暂时把Bridge 2 的Subordinate Bus Number设为0xFF。如下图所示:

730xd pcie拆分 bios版本 pcie拆分改bios_寄存器_03


第三步:

系统继续扫描Bus 2,将会发现Bridge 4。系统将Bridge 4下面的PCI Bus定为Bus 3,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成2和3,此后

系统继续扫描后发现Bus 3 下面已经没有任何Bridge了,意味着该PCI总线下已经没有任何挂载下游总线了,因此Bridge 4的Subordinate Bus Number的值已经可以确定为3了。

如下图所示:

730xd pcie拆分 bios版本 pcie拆分改bios_寄存器_04


第四步:

完成Bus 3的扫描后,系统返回到Bus 2继续扫描,发现Bus 2下面已经没有其他Bridge了。此时Bridge 2的Subordinate Bus Number的值也已经可以确定为3了。如下图所示:

730xd pcie拆分 bios版本 pcie拆分改bios_730xd pcie拆分 bios版本_05


第五步:

完成Bus 2的扫描后,系统返回到Bus1继续扫描,会发现Bridge 3,系统将Bridge 3下面的PCI Bus定为Bus 4。并将Bridge 4的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成1和4,此后系统继续扫描后发现Bus 4 下面已经没有任何Bridge了,意味着该PCI总线下已经没有挂载任何下游总线了,因此Bridge 3 的Subordinate Bus Number的值已经可以确定为4了。如下图所示:

730xd pcie拆分 bios版本 pcie拆分改bios_linux驱动_06


第六步:

完成Bus 4的扫描后,系统返回到Bus 1继续扫描, 发现Bus 1下面已经没有其他Bridge了。此时Bridge 1的Subordinate Bus Number的值已经可以确定为4,系统返回Bus 0继续扫描(Bus 0下如果有其他它Bridge,将重复上述的步骤进行扫描)。至此,本例中的整个PCI的设备扫描已经完成了。最终的设备和总线的扫描结果如下图所示。

730xd pcie拆分 bios版本 pcie拆分改bios_寄存器_07


了解了上面PCI设备扫描的大概流程,我们接下来看看Bios代码中具体是如何实现这些扫描的。

一般来说,我们可以通过两个寄存器来访问PCI的配置空间(寄存器CONFIG_ADDRESS与CONFIG_DATA),在x86体系下,这两个寄存器分别对应0xCF8和0xCFC端口,对配置空间的访问都是通过对这两个寄存器的读写来实现先。CONFIG_ADDRESS寄存器的具体位组成如下图所示:


730xd pcie拆分 bios版本 pcie拆分改bios_linux驱动_08

Bus Number : 总线号(8 bit),范围0--255。

Device Number: 设备号(5 bit),范围0--31。

Function Number: 功能号(3 bit),范围0--7。

Register Number: 寄存器号(6 bit),范围0--63 (配置空间一共256个字节,分割成64个4字节的寄存器,从0--63编号)。

每个PCI设备可根据上图所示的四个信息:Bus Number, Device Number, Function Number,Register Number 来进行具体设备的定位并对其配置空间访问。当我们要访问PCI设备的配置空间时,先根据以上格式设置CONFIG_ADDRESS寄存器,然后再读取CONFIG_DATA寄存器即可得到相应的配置空间寄存器的值。


因此,BIOS中PCI配置空间的读写可以封装成下面的函数:


[cpp] 
1. static inline
2. {  
3.     outl(dev | reg, 0xcf8);  
4. return
5. }  
6.   
7. static inline void
8. {  
9.     outl(dev | reg, 0xcf8);  
10.     outl(val, 0xcfc);  
11. }


static inline uint32_t pci_config_read32(pci_addr dev, uint8_t reg)
{
    outl(dev | reg, 0xcf8);
    return inl(0xcfc | reg);
}

static inline void pci_config_write32(pci_addr dev, uint8_t reg, uint32_t val)
{
    outl(dev | reg, 0xcf8);
    outl(val, 0xcfc);
}



总体来说。该BIOS扫描过程中调用如下几个主要的函数:

ob_pci_init  ----> ob_scan_pci_bus ----> pci_find_device ----> ob_pci_configure


下面我们来具体看看代码,首先BIOS执行ob_pci_init(void)函数


[cpp] 
1. int ob_pci_init(void)  
2. {  
3. int
4. long
5. char
6.   
7. #ifdef CONFIG_DEBUG_PCI 
8. "Initializing PCI devices...\n");  
9. #endif 
10.   
11. /* brute force bus scan */
12.   
13. /* Find all PCI bridges */
14.   
15. //获取系统指定的memeory与I/O空间的范围,分配给PCIe设备。 
16.     mem_base = arch->mem_base;  
17. /* I/O ports under 0x400 are used by devices mapped at fixed
18.            location. */
19.         io_base = arch->io_base + 0x400;  
20. "");  
21.   
22. /*遍历256条总线*/
23. for
24.         ob_scan_pci_bus(bus, &mem_base, &io_base, &path);  
25.     }  
26.     free(path);  
27. return
28. }


int ob_pci_init(void)
{
        int bus;
        unsigned long mem_base, io_base;
	char *path;

#ifdef CONFIG_DEBUG_PCI
	printk("Initializing PCI devices...\n");
#endif

	/* brute force bus scan */

	/* Find all PCI bridges */

        //获取系统指定的memeory与I/O空间的范围,分配给PCIe设备。
	mem_base = arch->mem_base;
        /* I/O ports under 0x400 are used by devices mapped at fixed
           location. */
        io_base = arch->io_base + 0x400;
	path = strdup("");

        /*遍历256条总线*/
        for (bus = 0; bus<0x100; bus++) {
		ob_scan_pci_bus(bus, &mem_base, &io_base, &path);
	}
	free(path);
	return 0;
}


总线扫描具体实现:


[cpp] 
1. static void ob_scan_pci_bus(int bus, unsigned long
2. long *io_base, char
3. {  
4. int
5. int
6.     pci_addr addr;  
7.     pci_config_t config;  
8. const
9.     uint32_t ccode;  
10. class, subclass, iface, rev;  
11.   
12. "/");  
13. for
14.         is_multi = 0;  
15. for
16. #ifdef CONFIG_XBOX 
17. if
18. continue;  
19. #endif 
20. /*获取设备配置空间地址*/
21. /*获取Vendor ID*/
22. /*获取Device ID*/
23.   
24. if
25. continue;  
26.   
27.             ccode = pci_config_read16(addr, PCI_CLASS_DEVICE);  
28. class
29.             subclass = ccode;  
30.             iface = pci_config_read8(addr, PCI_CLASS_PROG);  
31.             rev = pci_config_read8(addr, PCI_REVISION_ID);  
32.   
33. class, subclass, iface,/*具体设备查找以及初始化*/
34.                           vid, did);  
35.   
36. #ifdef CONFIG_DEBUG_PCI 
37. "%x:%x.%x - %x:%x - ", bus, devnum, fn,  
38.                     vid, did);  
39. #endif 
40.             htype = pci_config_read8(addr, PCI_HEADER_TYPE);  
41. if
42.                 is_multi = htype & 0x80;  
43.   
44. if
45. sizeof(config.path),  
46. "%s/pci%x,%x", *path, vid, did);  
47. else
48. sizeof(config.path),  
49. "%s/%s", *path, pci_dev->name);  
50. #ifdef CONFIG_DEBUG_PCI 
51. "%s - ", config.path);  
52. #endif 
53.             config.dev = addr & 0x00FFFFFF;  
54.   
55.             REGISTER_NAMED_NODE(ob_pci_node, config.path);  
56.   
57.             activate_device(config.path);  
58.   
59. /*配置设备的配置空间*/
60.             ob_pci_add_properties(addr, pci_dev, &config);  
61.   
62. if (class
63.                             (subclass == PCI_SUBCLASS_BRIDGE_HOST ||  
64.                              subclass == PCI_SUBCLASS_BRIDGE_PCI)) {  
65. /* host or bridge */
66.                 free(*path);  
67.                 *path = strdup(config.path);  
68.             }  
69.   
70.         }  
71.     }  
72.     device_end();  
73. }


static void ob_scan_pci_bus(int bus, unsigned long *mem_base,
                            unsigned long *io_base, char **path)
{
	int devnum, fn, is_multi, vid, did;
	unsigned int htype;
	pci_addr addr;
	pci_config_t config;
        const pci_dev_t *pci_dev;
	uint32_t ccode;
	uint8_t class, subclass, iface, rev;

	activate_device("/");
	for (devnum = 0; devnum < 32; devnum++) {
		is_multi = 0;
		for (fn = 0; fn==0 || (is_multi && fn<8); fn++) {
#ifdef CONFIG_XBOX
			if (pci_xbox_blacklisted (bus, devnum, fn))
				continue;
#endif
			addr = PCI_ADDR(bus, devnum, fn);       /*获取设备配置空间地址*/
			vid = pci_config_read16(addr, PCI_VENDOR_ID);  /*获取Vendor ID*/
			did = pci_config_read16(addr, PCI_DEVICE_ID);  /*获取Device ID*/

			if (vid==0xffff || vid==0)
				continue;

			ccode = pci_config_read16(addr, PCI_CLASS_DEVICE);
			class = ccode >> 8;
			subclass = ccode;
			iface = pci_config_read8(addr, PCI_CLASS_PROG);
			rev = pci_config_read8(addr, PCI_REVISION_ID);

			pci_dev = pci_find_device(class, subclass, iface,/*具体设备查找以及初始化*/
						  vid, did);

#ifdef CONFIG_DEBUG_PCI
			printk("%x:%x.%x - %x:%x - ", bus, devnum, fn,
					vid, did);
#endif
			htype = pci_config_read8(addr, PCI_HEADER_TYPE);
			if (fn == 0)
				is_multi = htype & 0x80;

			if (pci_dev == NULL || pci_dev->name == NULL)
                            snprintf(config.path, sizeof(config.path),
				     "%s/pci%x,%x", *path, vid, did);
			else
                            snprintf(config.path, sizeof(config.path),
				     "%s/%s", *path, pci_dev->name);
#ifdef CONFIG_DEBUG_PCI
			printk("%s - ", config.path);
#endif
			config.dev = addr & 0x00FFFFFF;

			REGISTER_NAMED_NODE(ob_pci_node, config.path);

			activate_device(config.path);

                        ob_pci_configure(addr, &config, mem_base, io_base); /*配置设备的配置空间*/
			ob_pci_add_properties(addr, pci_dev, &config);

                        if (class == PCI_BASE_CLASS_BRIDGE &&
                            (subclass == PCI_SUBCLASS_BRIDGE_HOST ||
                             subclass == PCI_SUBCLASS_BRIDGE_PCI)) {
				/* host or bridge */
				free(*path);
				*path = strdup(config.path);
			}

		}
	}
	device_end();
}


具体某条总线上的设备扫描由以下函数实现:


[cpp]
1. <PRE class=cpp name="code">const pci_dev_t *pci_find_device (uint8_t class, uint8_t subclass,  
2.                                   uint8_t iface, uint16_t vendor,  
3.                                   uint16_t product)  
4. {  
5. int (*config_cb)(const
6. const
7. const
8. const
9. const
10. const void *private;  
11. new;  
12. const char
13.   
14. "unknown";  
15. "unknown";  
16.     config_cb = NULL;  
17. private
18.   
19. if (class
20. /* Special hack for old style VGA devices */
21. class
22.         subclass = 0x00;  
23. else if (class
24. /* Special case for misc devices */
25.         dev = misc_pci;  
26. goto
27.     }  
28. if (class > (sizeof(pci_classes) / sizeof(pci_class_t))) {  
29. "invalid PCI device";  
30. "invalid";  
31. goto
32.     }  
33. class];  
34.     name = pclass->name;  
35.     type = pclass->type;  
36. for
37. if
38. goto
39. if
40. if
41.                 name = psubclass->name;  
42. if
43.                 type = psubclass->type;  
44. if
45.                 config_cb = psubclass->config_cb;  
46.             }  
47. if (psubclass->private
48. private = psubclass->private;  
49. if
50. break;  
51.             dev = psubclass->devices;  
52. goto
53.         }  
54.     }  
55. for
56. if
57.             dev = psubclass->devices;  
58. break;  
59.         }  
60. if
61. if
62.                 name = piface->name;  
63. if
64.                 type = piface->type;  
65. if
66.                 config_cb = piface->config_cb;  
67.             }  
68. if (piface->private
69. private = piface->private;  
70.             dev = piface->devices;  
71. break;  
72.         }  
73.     }  
74. find_device:  
75. if
76. goto
77. for
78. if
79. goto
80.         }  
81. if
82. if
83.                 name = dev->name;  
84. if
85.                 type = dev->type;  
86. if
87.                 config_cb = dev->config_cb;  
88.             }  
89. if (dev->private
90. private = dev->private;  
91. new = malloc(sizeof(pci_dev_t));  
92. if (new
93. return
94. new->vendor = vendor;  
95. new->product = product;  
96. new->type = type;  
97. new->name = name;  
98. new->model = dev->model;  
99. new->compat = dev->compat;  
100. new->acells = dev->acells;  
101. new->scells = dev->scells;  
102. new->icells = dev->icells;  
103. new->config_cb = config_cb;  
104. new->private = private;  
105.   
106. return new;  
107.         }  
108.     }  
109. bad_device:  
110. "Cannot manage '%s' PCI device type '%s':\n %x %x (%x %x %x)\n",  
111. class, subclass, iface);  
112.   
113. return
114. }  
115.   
116.   
117.   
118. 配置具体设备的配置空间  
119.   
120. static void ob_pci_configure(pci_addr addr, pci_config_t *config, unsigned long
121. long
122.   
123. {  
124.     uint32_t smask, omask, amask, size, reloc, min_align;  
125. long
126.     pci_addr config_addr;  
127. int
128.     uint8_t irq_pin, irq_line;  
129.           
130. /*配置中断引脚与中断编号*/
131.     irq_pin =  pci_config_read8(addr, PCI_INTERRUPT_PIN);  
132. if
133.         config->irq_pin = irq_pin;  
134.         irq_pin = (((config->dev >> 11) & 0x1F) + irq_pin - 1) & 3;  
135.         irq_line = arch->irqs[irq_pin];  
136.         pci_config_write8(addr, PCI_INTERRUPT_LINE, irq_line);  
137.         config->irq_line = irq_line;  
138. else
139.         config->irq_line = -1;  
140.   
141. /*配置memory空间和I/O空间*/
142.     omask = 0x00000000;  
143. for
144.   
145.         config->assigned[reg] = 0x00000000;  
146.         config->sizes[reg] = 0x00000000;  
147.   
148. if
149. /* 64 bits memory mapping */
150. continue;  
151.         }  
152.   
153. if
154.             config_addr = PCI_ROM_ADDRESS;  
155. else
156.             config_addr = PCI_BASE_ADDR_0 + reg * 4;  
157.   
158.         config->regions[reg] = pci_config_read32(addr, config_addr);  
159.   
160. /* get region size */
161.   
162.         pci_config_write32(addr, config_addr, 0xffffffff);  
163.         smask = pci_config_read32(addr, config_addr);  
164. if
165. continue;  
166.   
167. if
168. /* I/O space */
169.             base = *io_base;  
170.             min_align = 1 << 7;  
171.             amask = 0x00000001;  
172.             pci_config_write16(addr, PCI_COMMAND,  
173.                        pci_config_read16(addr,  
174.                                  PCI_COMMAND) |  
175.                                  PCI_COMMAND_IO);  
176. else
177. /* Memory Space */
178.             base = *mem_base;  
179.             min_align = 1 << 16;  
180.             amask = 0x0000000F;  
181. if
182. /* ROM */
183.             }  
184.             pci_config_write16(addr, PCI_COMMAND,  
185.                        pci_config_read16(addr,  
186.                                 PCI_COMMAND) |  
187.                                 PCI_COMMAND_MEMORY);  
188.         }  
189.         omask = smask & amask;  
190.         smask &= ~amask;  
191.         size = (~smask) + 1;  
192.         config->sizes[reg] = size;  
193.         reloc = base;  
194. if
195.             size = min_align;  
196.         reloc = (reloc + size -1) & ~(size - 1);  
197. if
198.             *io_base = reloc + size;  
199.             reloc -= arch->io_base;  
200. else
201.             *mem_base = reloc + size;  
202.         }  
203.         pci_config_write32(addr, config_addr, reloc | omask);  
204.         config->assigned[reg] = reloc | omask;  
205.     }  
206. }  
207.   
208. 通过以上这些步骤,Bios就完成了所有PCI设备的扫描,并且为每个设备分配好了系统资源。<BR>  
209. </PRE>  
210. <PRE></PRE>