龙芯2k1000-pmon(10)- _pci_businit---3(总结)

这篇博客详细解析了PCI设备的内存和I/O空间分配过程,从PCI配置数据结构到分配策略,特别是对PCI桥设备的处理。文章通过代码分析展示了如何将分配的空间写入设备的BAR寄存器,并探讨了地址空间的起始位置选择。内容涵盖了PCI设备的识别、地址分配、桥设备的特殊处理以及递归分配等关键步骤。
摘要由CSDN通过智能技术生成

好的,tgt_devinit();这一部分接近尾声。末尾做个简单的总结。(不保证完全正确)

入口:_setup_pcibuses(init);

_pci_hwreinit (); 这个是空函数,啥也没做。

 

_pci_setup_windows,主要还是这个函数。所以后面的分析基本就是这个函数中的部分。代码全部贴上来,后面再分块分析吧。

#ifndef PCI_BIGMEM_ADDRESS
#define PCI_BIGMEM_ADDRESS 0x40000000
#endif
static pci_bigmem_address=PCI_BIGMEM_ADDRESS;
static pci_bigio_address=0x10000;
static void
_pci_setup_windows (struct pci_device *dev)
{
    struct pci_win *pm;
    struct pci_win *next;
    struct pci_device *pd;
    unsigned int align;

    for(pm = dev->bridge.memspace; pm != NULL; pm = next) {

        pd = pm->device;
        next = pm->next;

        if (pd->bridge.child && pm->reg == PCI_MEMBASE_1) align = ~pd->bridge.mem_mask + 1;
        else if (pd->bridge.child &&  pm->reg == PCI_IOBASEL_1) align = ~pd->bridge.io_mask + 1;
        else align = 1<<(fls(pm->size)-1);

	pm->address = pci_alloc_fixmemio(pm);   //有固定的地址空间
	if(pm->address == -1)
		pm->address = _pci_allocate_mem (dev, pm->size, align);  //分配地址空间
        if (pm->address == -1) {
	        pci_bigmem_address = (pci_bigmem_address + pm->size-1) & ~(pm->size - 1);
		    pm->address = pci_bigmem_address;
	        pci_bigmem_address += pm->size;

#if 1
			if (_pciverbose >= 2)  //2022-03-06 添加if
           		 _pci_tagprintf (pd->pa.pa_tag, 
           	                 "not enough PCI mem space (%d requested)\n",
                            pm->size);
#endif
            //continue;
        }
        if (_pciverbose >= 2)
            _pci_tagprintf (pd->pa.pa_tag, "mem @%p, reg 0x%x %d bytes\n", pm->address, pm->reg, pm->size);
		//如果是pci桥的话,还需要设置
    	if (PCI_ISCLASS(pd->pa.pa_class, PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_PCI) && (pm->reg == PCI_MEMBASE_1)) {

            pcireg_t memory;
            
            pm->address = (pm->address + (~pd->bridge.mem_mask))& pd->bridge.mem_mask; //yang23 2013-11-26

            pd->bridge.secbus->minpcimemaddr = pm->address;
            pd->bridge.secbus->nextpcimemaddr = pm->address + pm->size;

            memory = (((pm->address+pm->size-1) >> 16) << 16) | (pm->address >> 16);
			printf("03-18 reg = 0x%x memory = %p pm->address=%p\n",pm->reg, memory,pm->address);
			_pci_conf_write(pd->pa.pa_tag, pm->reg, memory);
#ifdef LOONGSON_2K
	    _pci_conf_write(pd->pa.pa_tag,PCI_PMBASEL_1, memory);
#else
			/*set end memory bellow start memory to disable prefectable memory*/
            _pci_conf_write(pd->pa.pa_tag,PCI_PMBASEL_1,0x00000010);
#endif

        }      
        else if (pm->reg != PCI_MAPREG_ROM) {   //不是0x30的情况
            /* normal memory - expansion rom done below */
            pcireg_t base = _pci_conf_read(pd->pa.pa_tag, pm->reg);
            base = pm->address | (base & ~PCI_MAPREG_MEM_ADDR_MASK);
			printf("03-18 reg = 0x%x base = %p\n",pm->reg, base);
            _pci_conf_write(pd->pa.pa_tag, pm->reg, base);
        }
    }
	//end for(pm = )

	/* Program expansion rom address base after normal memory base,
       to keep DEC ethernet chip happy */
    for (pm = dev->bridge.memspace; pm != NULL; pm = next) 
	{

		pd = pm->device;
#ifdef LOONGSON_2G5536
		if (PCI_ISCLASS(((pd->pa.pa_class)&0xff00ffff), PCI_CLASS_DISPLAY, PCI_SUBCLASS_DISPLAY_VGA))
		{
			vga_dev = pd;
			pd->disable=0;
		}
#else
		if (PCI_ISCLASS(pd->pa.pa_class, PCI_CLASS_DISPLAY, PCI_SUBCLASS_DISPLAY_VGA))
		{
#if defined(USE_BMC)  /* USE_BMC_VGA */
	       if (PCI_VENDOR(pd->pa.pa_id) == 0x1a03)
			{
	            vga_dev = pd;
	            pd->disable=0;
	        }
#else
	        if ((PCI_VENDOR(pd->pa.pa_id) == 0x1002) && (PCI_PRODUCT(pd->pa.pa_id) == 0x9615))
	        {
				printf("vga_dev =:%x\n",vga_dev);
	            vga_dev = pd;
	            pd->disable=0;
	        }else{
				if (PCI_VENDOR(pd->pa.pa_id) == 0x1a03)
					;
				else if (PCI_VENDOR(pd->pa.pa_id) == 0x0014)
					;//ls7a vga
				else {
					printf("1.pcie_dev :%x vga_dev ==:%x\n",pcie_dev,vga_dev);
			        	pcie_dev = pd;
		            		pd->disable=0;
					vga_dev = NULL;
				}
	        }
#endif
			printf("2.pcie_dev :%x vga_dev ==:%x\n",pcie_dev,vga_dev);
		}
#endif
#if 0
	/*
	 * If this is the first VGA card we find, set the BIOS rom
	 * at address c0000 if PCI base address is 0x00000000.
	 */
	if (pm->reg == PCI_MAPREG_ROM && !have_vga &&
	    dev->bridge.secbus->minpcimemaddr == 0 &&
	    (PCI_ISCLASS(pd->pa.pa_class,
		PCI_CLASS_PREHISTORIC, PCI_SUBCLASS_PREHISTORIC_VGA) ||
	    PCI_ISCLASS(pd->pa.pa_class,
		PCI_CLASS_DISPLAY, PCI_SUBCLASS_DISPLAY_VGA))) {
		have_vga = pd->pa.pa_tag;
		pm->address = 0x000c0000;	/* XXX PCI MEM @ 0x000!!! */
	}
#endif
    	if ((PCI_ISCLASS(pd->pa.pa_class, PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_PCI) && (pm->reg == PCI_MAPREG_PPB_ROM)) || (pm->reg == PCI_MAPREG_ROM)) {
	    /* expansion rom */
		    if (_pciverbose >= 2){
		        _pci_tagprintf (pd->pa.pa_tag, "exp @%p, %d bytes\n", pm->address, pm->size);
            }
	        _pci_conf_write(pd->pa.pa_tag, pm->reg, pm->address | PCI_MAPREG_TYPE_ROM);
        }

        next = pm->next;
        dev->bridge.memspace = next;
        pfree(pm);
    }


	//end for(pm)    
    for(pm = dev->bridge.iospace; pm != NULL; pm = next) {

        pd = pm->device;
        next = pm->next;

        if(pd->bridge.child) align = ~pd->bridge.io_mask+1;
        else align = 1<<(fls(pm->size)-1);

	pm->address = pci_alloc_fixmemio(pm);
	if(pm->address == -1)
		pm->address = _pci_allocate_io (dev, pm->size, align);
        if (pm->address == -1) {
            _pci_tagprintf (pd->pa.pa_tag, 
                            "not enough PCI io space (%d requested)\n", 
                            pm->size);
	        pm->address = pci_bigio_address;
	        pci_bigio_address += pm->size;
        }
        if (_pciverbose >= 2)
		    _pci_tagprintf (pd->pa.pa_tag, "i/o @%p, reg 0x%x %d bytes\n", pm->address, pm->reg, pm->size);

	    if (PCI_ISCLASS(pd->pa.pa_class, PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_PCI) &&
           (pm->reg == PCI_IOBASEL_1)) {
	        pcireg_t tmp;

            pd->bridge.secbus->minpciioaddr = pm->address;
            pd->bridge.secbus->nextpciioaddr = pm->address + pm->size;

	        tmp = _pci_conf_read(pd->pa.pa_tag,PCI_IOBASEL_1);
	        tmp &= 0xffff0000;
	        tmp |= (pm->address >> 8) & 0xf0;
	        tmp |= ((pm->address + pm->size-1) & 0xf000);
	        _pci_conf_write(pd->pa.pa_tag,PCI_IOBASEL_1, tmp);

	        tmp = (pm->address >> 16) & 0xffff;
	        tmp |= ((pm->address + pm->size-1) & 0xffff0000);
	        _pci_conf_write(pd->pa.pa_tag,PCI_IOBASEH_1, tmp);

        }
        else {
            _pci_conf_write(pd->pa.pa_tag, pm->reg, pm->address | PCI_MAPREG_TYPE_IO);
        }
        dev->bridge.iospace = next;
        pfree(pm);
    }

	//end for
	/* Recursive allocate memory for secondary buses */
    for(pd = dev->bridge.child; pd != NULL; pd = pd->next) {
	    if (PCI_ISCLASS(pd->pa.pa_class, PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_PCI)) {
            _pci_setup_windows(pd);
        }
    }
}

1.先看看实际打印的空间分配,从大到小

1.1. 空间的分配是由大到小的顺序,地址是连续的。

1.2. pci桥的mem空间都是分配1MB

注意slot9 就是第一个桥的地址是0x4060,0000

桥下设备分配的空间就是以这个为首地址。

1.3 slot表示设备号。再次上图,可以对比看一下。

d3(对应slot3) - 两个功能网卡。

d4 - 三个功能usb接口

d5 - 显示设备

d6- vga显示

d7- 多媒体设备

d8- sata存储接口设备

d9 -d14 是桥,其中d9 下有一个设备,其他全部是空的。

d15 是系统设备

d16,d17是多媒体

 这是来自用户手册的说明部分。

2. 看看分配的代码。

在这个过程中,我看到的就是将分配的空间写入到对应的bar寄存器中去。

所以每个设备的对应的bar的内容(就是分配的地址)其实是不同的。

2.1 在看代码的时候,发现有一段固定的地址分配

pm->address = pci_alloc_fixmemio(pm);   //有固定的地址空间

 

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

struct pci_config_data pci_config_array[] = {
			/*		APB		*/
[0] = {
.bus = 0, .dev = 0x2, .func = 0, .interrupt = 0, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x1fe00000, .mem_end = 0x1fe0ffff, .type = PCI_DEV,
},
			/*		GMAC0	*/
[1] = {
.bus = 0, .dev = 0x3, .func = 0, .interrupt = 20, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x40040000, .mem_end = 0x4004ffff, .type = PCI_DEV,
},
			/*		GMAC1	*/
[2] = {
.bus = 0, .dev = 0x3, .func = 1, .interrupt = 22, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x40050000, .mem_end = 0x4005ffff, .type = PCI_DEV,
},
			/*		OTG		*/
[3] = {
.bus = 0, .dev = 0x4, .func = 0, .interrupt = 57, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x40000000, .mem_end = 0x4003ffff, .type = PCI_DEV,
},
			/*		EHCI	*/
[4] = {
.bus = 0, .dev = 0x4, .func = 1, .interrupt = 58, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x40060000, .mem_end = 0x4006ffff, .type = PCI_DEV,
},
			/*		OHCI	*/
[5] = {
.bus = 0, .dev = 0x4, .func = 2, .interrupt = 59, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x40070000, .mem_end = 0x4007ffff, .type = PCI_DEV,
},
			/*		GPU		*/
[6] = {
.bus = 0, .dev = 0x5, .func = 0, .interrupt = 37, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x40080000, .mem_end = 0x400bffff, .type = PCI_DEV,
},
			/*		DC		*/
[7] = {
.bus = 0, .dev = 0x6, .func = 0, .interrupt = 36, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x400c0000, .mem_end = 0x400cffff, .type = PCI_DEV,
},
			/*		HDA		*/
[8] = {
.bus = 0, .dev = 0x7, .func = 0, .interrupt = 12, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x400d0000, .mem_end = 0x400dffff, .type = PCI_DEV,
},
			/*		SATA	*/
[9] = {
.bus = 0, .dev = 0x8, .func = 0, .interrupt = 27, .primary = 0, .secondary = 0,
.subordinate = 0, .mem_start = 0x400e0000, .mem_end = 0x400effff, .type = PCI_DEV,
},
#if 0
			/*	PCIE0-PORT0	*/
[10] = {
.bus = 0, .dev = 0x9, .func = 0, .interrupt = 40, .primary = 0, .secondary = 1,
.subordinate = 1, .mem_start = 0x40100000, .mem_end = 0x4fffffff, .type = PCI_BRIDGE,
.io_start = 0x18000000, .io_end = 0x180fffff,
},
			/*	PCIE0-PORT1	*/
[11] = {
.bus = 0, .dev = 0xa, .func = 0, .interrupt = 41, .primary = 0, .secondary = 4,
.subordinate = 4, .mem_start = 0x50000000, .mem_end = 0x53ffffff, .type = PCI_BRIDGE,
.io_start = 0x18100000, .io_end = 0x181fffff,
},
			/*	PCIE0-PORT2	*/
[12] = {
.bus = 0, .dev = 0xb, .func = 0, .interrupt = 42, .primary = 0, .secondary = 8,
.subordinate = 8, .mem_start = 0x54000000, .mem_end = 0x57ffffff, .type = PCI_BRIDGE,
.io_start = 0x18200000, .io_end = 0x182fffff,
},
			/*	PCIE0-PORT3	*/
[13] = {
.bus = 0, .dev = 0xc, .func = 0, .interrupt = 43, .primary = 0, .secondary = 0xc,
.subordinate = 0xc, .mem_start = 0x58000000, .mem_end = 0x5fffffff, .type = PCI_BRIDGE,
.io_start = 0x18300000, .io_end = 0x183fffff,
},
			/*	PCIE1-PORT0	*/
[14] = {
.bus = 0, .dev = 0xd, .func = 0, .interrupt = 44, .primary = 0, .secondary = 0x10,
.subordinate = 0x10, .mem_start = 0x60000000, .mem_end = 0x77ffffff, .type = PCI_BRIDGE,
.io_start = 0x18400000, .io_end = 0x184fffff,
},
			/*	PCIE1-PORT1	*/
[15] = {
.bus = 0, .dev = 0xe, .func = 0, .interrupt = 45, .primary = 0, .secondary = 0x14,
.subordinate = 0x14, .mem_start = 0x78000000, .mem_end = 0x7fffffff, .type = PCI_BRIDGE,
.io_start = 0x18500000, .io_end = 0x185fffff,
},
#endif
};

对于桥片的地址全部注释掉了。

对于这些数据的结构体定义如下:

对比前面的打印出来的那个地址,就可以看到,对应得设备得地址确实是按照这个表格指定给出的。 

看得出来pci_alloc_fixmemio 就是用来分配固定给出的地址空间的。

第一行,先去看是否分配了固定地址空间,没有则返回-1,这时使用(第三行)分配内存及大小。 如果返回还是-1,说明动态分配失败。

仔细看,这里并不是alloc来动态分配,实际就是从指定的地址划分出来一个区间 。

返回-1是因为空间不足了。

这个空间的起始地址,在总线初始化的时候就指定了。_pci_hwinit函数中

 pci_probe_only 默认值是2,所以if是成立的。

这里分配的起始地址就是这个0x4010,0000,实际桥的起始地址正式从这开始的。

 

2.2 之后是分配成功后的处理:

 这一段有两个if

第一个if是对于pci桥的处理。

第二个else if 这里是排除寄存器为0x30的情况,之后还会在后面的程序继续处理。

一般的设备就都是在这个if中被处理了,我看到的就是对寄存器写入了实际的地址。

base 是对address的低4位进行了一些保留处理,因为低4位是有特定的含义的。

 2.3 需要注意的是,以上代码在for循环中,会连续执行很多次。

 

3. 之后的一个for,仍然是对memspace的遍历。

 if内部对桥和0x30寄存器的情况都会处理。其实也是写寄存器。

之后会free释放pm的空间,看来之后就不会用到了。

 4. 第三个for循环

对iospace的同样的操作,这里就不截图说明了。

5. 第4个for循环

 对于桥下设备的一次递归调用。之前是bus0

这次就是bus1了。其他的bus下都没有挂载设备。

6. 总结

从这次tgt_devinit();函数出发。

6.1 首先是遍历pci总线(_pci_scan_dev),总线号0(因为cpu出来只有一条总线),设备号0-31,如果是多功能,还要遍历功能号0-7

遍历的时候,会建立两个主要链表,一个是总线的链表(_pci_bushead),一个是设备的链表(_pci_head)。

 

 6.2 bus上再遇到桥,就会生成新的总线

_pci_bus[_max_pci_bus++] ,这个全局数组会保存总线的地址,还有全局遍历_max_pci_bus保存总线数。这个应该不能反映出总线的树形结构。

6.3 通过classcode寄存器可以识别出来轮询到的设备是什么类型。(至少可以区分桥和普通设备,然后就分开处理)

6.4 还有一个很重要的就是对bar寄存器的处理,对其写入0xffff,ffff,返回值就可以知道它这个寄存器对应的是io空间还是mem空间,还可以知道空间的大小。

这些数据记录到struct pci_win 结构体中。

 这个链表是有序的,根据需要分配的字节数,由大到小排列,所以插入到链表时会进行比较。

 根据flags的不同,区分io链表和mem链表。

6.5 桥的mem空间是1MB。

桥下的设备的起始地址,从桥分配的空间起始地址开始分配。

 上图这是桥片分配到的1M的空间

上图是io的空间地址。

桥下设备的地址分配情况:

可以看到mem空间的地址,正好是与桥片的起始地址相同。

io的地址也同样是的。

 6.6 _setup_pcibuses(init); 是把实际分配到的地址空间(包括mem和io)写入到bar寄存器。

 每个设备都是唯一的地址了。

6.7 mem空间为什么起始地址是0x4000,0000开始。

手册上面确实指定了其中1G的空间,不过我觉得还要研究一下手册。 

6.8 至于后面加载驱动,识别厂家号这些就不多说了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大智兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值