天天看點

PCIe 學習筆記(二)

pci note 2

-v0.1 2014.11.18 *** draft

 本文繼續pci note 1, 介紹pci_create_root_bus函數, 核心版本為3.18-rc1

 調用關系:

 pci_scan_root_bus

     --> pci_create_root_bus

/*
 * device說明見下文,bus是根總線号,ops是配置空間讀寫函數的接口,需要驅動作者
 * 傳入回調函數, 會在pci_scan_child_bus->pci_scan_slot->pci_scan_single_device->
 * pci_scan_device->pci_bus_read_dev_vendor_id調用到該ops中的read函數。sysdata
 * 傳入私有資料。resources連結清單的元素是struct pci_host_bridge_window, 是dts上
 * 讀上來的總線号,mem空間,I/O空間的資訊, 一般一個pci_host_bridge_window對應
 * 一個資訊
 */
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
		struct pci_ops *ops, void *sysdata, struct list_head *resources)
{
	...
	/*
	 * 配置設定 struct pci_host_bridge, 初始化其中的windows連結清單
	 * windows連結清單上的存的結構是:struct pci_host_bridge_window
	 * struct pci_host_bridge_window {
	 * 	struct list_head list;
	 * 	struct resource *res;	/* host bridge aperture (CPU address) */
	 * 	resource_size_t offset;	/* bus address + offset = CPU address */
	 * };
	 */
	bridge = pci_alloc_host_bridge();

	/*
	 * 輸入參數parent來自pci host驅動中pci host核心結構的struct device *dev,
	 * dev來自 platform_device 中的dev。可以以drivers/pci/host下的pci-mvebu.c
	 * 作為例子, 其中所謂的pci host核心結構是:struct mvebu_pcie
	 */
	bridge->dev.parent = parent;

	/* 配置設定 struct pci_bus */
	b = pci_alloc_bus(NULL);

	b->sysdata = sysdata;
	b->ops = ops;
	b->number = b->busn_res.start = bus;
	/* 在pcie dts節點中找見domain字段, 加入pci_bus的domain_nr */
	pci_bus_assign_domain_nr(b, parent);
	/*
	 * 在pci_root_buses全局連結清單中找相應domain下的bus, 首次調用的時候傳回NULL
	 * 上面配置設定的pci_root_buses是在目前函數的最後才加入pci_root_buses中的,現在該
	 * 全局連結清單為空
	 */
	b2 = pci_find_bus(pci_domain_nr(b), bus);
	/*
	 * 上面兩行處理有關pci domain的資訊,kernel pci子系統怎麼處理pci domain
	 * 的呢? 首先資料結構是全局的連結清單:pci_root_buses, 局部連結清單:pci_domain_busn_res_list
	 * pci_root_buses中存放每個pci domain的根總線,根總線在pci_create_root_bus
	 * 函數的結尾被添加到pci_root_buses連結清單中。pci_domain_busn_res_list存放
	 * 各個domain的資訊, 包括domain号、domain包含的bus号範圍, 該連結清單上存放
	 * 存放的結構是:struct pci_domain_busn_res, 在函數get_pci_domain_busn_res
	 * 中查找相應domain号的pci_domain_busn_res, 如果沒有就配置設定一個新的
	 * pci_domain_busn_res, 然後加到pci_domain_busn_res_list上
	 */

	bridge->bus = b;
	dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
	error = pcibios_root_bridge_prepare(bridge);

	error = device_register(&bridge->dev);

	b->bridge = get_device(&bridge->dev);
	device_enable_async_suspend(b->bridge);
	pci_set_bus_of_node(b);

	if (!parent)
		set_dev_node(b->bridge, pcibus_to_node(b));

	b->dev.class = &pcibus_class;
	/* b->bridge 為對應pci_host_bridge中struct device dev的指針 */
	b->dev.parent = b->bridge;
	dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus);
	error = device_register(&b->dev);

	pcibios_add_bus(b);

	/* Create legacy_io and legacy_mem files for this bus */
	pci_create_legacy_files(b);

	...
	/*
	 * 取出pci_create_root_bus函數傳入的連結清單resources中的pci_host_bridge_window,
	 * 把每個pci_host_bridge_window加入pci_host_bridge中的window連結清單中
	 */
	list_for_each_entry_safe(window, n, resources, list) {
		list_move_tail(&window->list, &bridge->windows);
		res = window->res;
		offset = window->offset;
		if (res->flags & IORESOURCE_BUS)
		    	/*
			 * 一般的,resources連結清單上有bus number, MEM space, I/O
			 * space的節點,如果是bus number節點則調用以下函數。該
			 * 函數會找到目前pci_bus的父結構,生成pci_bus中的busn_res
			 * 并和父結構中的struct resource busn_res建立聯系。
			 * 如果父子在bus号上存在沖突,則傳回沖突的bus号[1]
			 */
			pci_bus_insert_busn_res(b, bus, res->end);
		else
			/*
			 * 向pci_bus中的連結清單resources中加入struct pci_bus_resource
			 * 記錄mem, io的資源
			 */
			pci_bus_add_resource(b, res, 0);
		if (offset) {
			if (resource_type(res) == IORESOURCE_IO)
				fmt = " (bus address [%#06llx-%#06llx])";
			else
				fmt = " (bus address [%#010llx-%#010llx])";
			snprintf(bus_addr, sizeof(bus_addr), fmt,
				 (unsigned long long) (res->start - offset),
				 (unsigned long long) (res->end - offset));
		} else
			bus_addr[0] = '\0';
		dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr);
	}

	down_write(&pci_bus_sem);
	/* 把建立的pci_bus加入全局連結清單pci_root_buses中 */
	list_add_tail(&b->node, &pci_root_buses);
	up_write(&pci_bus_sem);

	return b;
	...
}
           

[1] 關于linux中resource結構對資源的管理可以參看:

    http://www.linuxidc.com/Linux/2011-09/43708.htm

繼續閱讀