1. 程式人生 > >PCI裝置驅動之裝置

PCI裝置驅動之裝置

四、PCI裝置的列舉探測過程

在核心啟動過程中,PCI裝置的探測過程是完全自動的,核心已經整合好了方法,我們無需更改,在這裡還是分析一邊程式碼作為了解。

分析之前,先看一下全部的函式呼叫關係,大致瞭解一下

pci_arch_init /* 判斷host/pci橋的型別 */
	pci_direct_probe
		pci_check_type1
			pci_sanity_check
	
	pci_direct_init
		raw_pci_ops = &pci_direct_conf1;
		raw_pci_ext_ops = &pci_direct_conf1;  
 
/* 第二個過程,列舉各級總線上的裝置 */
pci_subsys_init pci_legacy_init pcibios_scan_root pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd); pci_create_bus(parent, bus, ops, sysdata); // 建立 0 級匯流排 pci_scan_child_bus(b); // 探測當前匯流排裝置以及子匯流排、子匯流排裝置 pci_scan_slot(bus, devfn); // 探測當前匯流排的裝置 pci_scan_single_device(
bus, devfn); // 探測單功能裝置 pci_scan_single_device(bus, devfn + fn); //探測多功能裝置 pci_scan_device(bus, devfn); //通過配置空間 列舉裝置 pci_setup_device //根據配置空間資訊,設定pci_dev pci_device_add(dev, bus); list_add_tail(&dev->bus_list, &bus->devices); // 將探測到的裝置加入到當前匯流排的裝置連結串列
pci_scan_bridge //此時已經完成當前匯流排裝置的探測,如果這些裝置裡有PCI橋,那麼進入下一級,探測橋下的裝置 child = pci_add_new_bus(bus, dev, busnr); pci_scan_child_bus(child); // 進入下一級探測 pci_bus_add_devices // 全部裝置探測完畢,註冊裝置。 pci_bus_add_device(dev); device_add // 將設備註冊到 pci_bus_type pci_bus_add_devices(child); //它最終也會呼叫到 device_add 將各個子總線上的設備註冊到 pci_bus_type

下面來看具體的探測過程。

static __init int pci_arch_init(void)
{
#ifdef CONFIG_PCI_DIRECT
	int type = 0;
	type = pci_direct_probe();
#endif
 
#ifdef CONFIG_PCI_BIOS
	pci_pcbios_init();
#endif
 
#ifdef CONFIG_PCI_DIRECT
	pci_direct_init(type);
#endif
 
	dmi_check_pciprobe();
 
	dmi_check_skip_isa_align();
 
	return 0;
}
arch_initcall(pci_arch_init);

這個函式是放在 init 段中,核心啟動時會呼叫。

int __init pci_direct_probe(void)
{
	struct resource *region, *region2;
	/* 申請IO資源 */
	region = request_region(0xCF8, 8, "PCI conf1");
	
	/* 探測那種型別 ,0型(PCI裝置)和1型(PCI橋) */
	if (pci_check_type1()) {
		raw_pci_ops = &pci_direct_conf1;
		port_cf9_safe = true;
		return 1;
	}
	release_resource(region);
 
 type2:
	if ((pci_probe & PCI_PROBE_CONF2) == 0)
		return 0;
	if (!request_region(0xCF8, 4, "PCI conf2"))
		return 0;
	if (!request_region(0xC000, 0x1000, "PCI conf2"))
		goto fail2;

	if (pci_check_type2()) {
		raw_pci_ops = &pci_direct_conf2;
		port_cf9_safe = true;
		return 2;
	}

	release_region(0xC000, 0x1000);
 fail2:
	release_region(0xCF8, 4);
	return 0;
}

在pci規範中,定義了兩種操作配置空間的方法,即type1 和type2.在新的設計中,type2的配置機制不會被採用,通常會使用type1.因此,在程式碼中pci_direct_probe()一般會返回1,即使用type1.

static int __init pci_check_type1(void)
{
	unsigned long flags;
	unsigned int tmp;
	int works = 0;
 
	local_irq_save(flags);
	
	/* i386 pci地址暫存器 0xcfb 寫 0x01 */
	outb(0x01, 0xCFB);
	tmp = inl(0xCF8);
	outl(0x80000000, 0xCF8);
	/* 判斷裝置型別 */
	if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&pci_direct_conf1)) {
		works = 1;
	}
	outl(tmp, 0xCF8);
	local_irq_restore(flags);
 
	return works;
}

static int __init pci_sanity_check(struct pci_raw_ops *o)
{
	u32 x = 0;
	int year, devfn;
 
	/* Assume Type 1 works for newer systems.
	   This handles machines that don't have anything on PCI Bus 0. */
	dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL);
	if (year >= 2001)
		return 1;
 
	for (devfn = 0; devfn < 0x100; devfn++) {
		/* 讀  CLASS_DEVICE ,PCI_CLASS_DEVICE 是片內偏移地址 */
		if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))    //pci_direct_conf1->read
			continue;
		/* 如果 CLASS_DEVICE 為 HOST-PCI橋(北橋),PCI-PCI橋,PCI-ISA橋(南橋)正確返回 */
		if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)
			return 1;
		/* 讀  VENDOR_ID 製造商ID */
		if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))
			continue;
		/* 如果 VENDOR_ID 為  INTEL 或 COMPAQ 正常返回 */
		if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)
			return 1;
	}
 
	DBG(KERN_WARNING "PCI: Sanity check failed\n");
	return 0;
}

檢測完是“0型”還是“1型”裝置之後,在 raw_pci_ops 中指定對應的讀寫配置空間的方法。

/* 設定全域性的 配置空間讀寫函式 */
void __init pci_direct_init(int type)
{
	if (type == 1) {
		raw_pci_ops = &pci_direct_conf1;
 
		raw_pci_ext_ops = &pci_direct_conf1;
		return;
	}
}

/* 地址是由 匯流排編號、裝置號、片內地址 組成 */
#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
	(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
	| (devfn << 8) | (reg & 0xFC))
 
static int pci_conf1_read(unsigned int seg, unsigned int bus,
			  unsigned int devfn, int reg, int len, u32 *value)
{
	unsigned long flags;
	/* 最多256個匯流排 ,256個裝置 片內暫存器範圍 0~4095 */
	if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
		*value = -1;
		return -EINVAL;
	}
 
	spin_lock_irqsave(&pci_config_lock, flags);
	
	/* 向地址暫存器 寫要讀取的地址 */
	outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
	
	/* 從資料暫存器讀取資料 */
	switch (len) {
	case 1:
		*value = inb(0xCFC + (reg & 3));
		break;
	case 2:
		*value = inw(0xCFC + (reg & 2));
		break;
	case 4:
		*value = inl(0xCFC);
		break;
	}
 
	spin_unlock_irqrestore(&pci_config_lock, flags);
 
	return 0;
}
 
struct pci_raw_ops {
	int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
						int reg, int len, u32 *val);
	int (*write)(unsigned int domain, unsigned int bus, unsigned int devfn,
						int reg, int len, u32 val);
};
struct pci_raw_ops *raw_pci_ops;

五:pci裝置的列舉過程

int __init pci_subsys_init(void)
{
#ifdef CONFIG_X86_NUMAQ
	pci_numaq_init();
#endif
#ifdef CONFIG_ACPI
	pci_acpi_init();
#endif
#ifdef CONFIG_X86_VISWS
	pci_visws_init();
#endif
	pci_legacy_init();
	pcibios_fixup_peer_bridges();
	pcibios_irq_init();
	pcibios_init();
 
	return 0;
}
subsys_initcall(pci_subsys_init);

struct pci_bus *pci_root_bus;
static int __init pci_legacy_init(void)
{
	pci_root_bus = pcibios_scan_root(0);//建立0級匯流排
	if (pci_root_bus)
		pci_bus_add_devices(pci_root_bus);
 
	return 0;
}

extern struct list_head pci_root_buses;	/* list of all known PCI buses */
struct pci_bus * __devinit pcibios_scan_root(int busnum)
{
	struct pci_bus *bus = NULL;
	struct pci_sysdata *sd;
	/* 在全域性 pci_root_buses 連結串列尋找 匯流排編號為 busnum 的匯流排 */
	while ((bus = pci_find_next_bus(bus)) != NULL) {
		if (bus->number == busnum) {
			/* 如果已經存在,返回它 */
			return bus;
		}
	}
 
	/* 如果這個匯流排編號不存在, 那麼建立這個Bus */
	sd = kzalloc(sizeof(*sd), GFP_KERNEL);
	sd->node = get_mp_bus_to_node(busnum);
 
	bus = pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);
 
	return bus;
}

struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent,
		int bus, struct pci_ops *ops, void *sysdata)
{
	struct pci_bus *b;
	/* 為對應匯流排號構建pci_bus,然後將其掛入到pci_root_buses連結串列 */
	b = pci_create_bus(parent, bus, ops, sysdata);
	if (b)
		b->subordinate = pci_scan_child_bus(b);
	return b;
}

unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
{
	unsigned int devfn, pass, max = bus->secondary;
	struct pci_dev *dev;
 
	/* 探測總線上的裝置,按功能號掃描裝置號對應的pci 裝置 */
	for (devfn = 0; devfn < 0x100; devfn += 8)
		pci_scan_slot(bus, devfn);
 
	/* Reserve buses for SR-IOV capability. */
	max += pci_iov_bus_range(bus);
 
	/*
	 * After performing arch-dependent fixup of the bus, look behind
	 * all PCI-to-PCI bridges on this bus.
	 */
	if (!bus->is_added) {
		pr_debug("PCI: Fixups for bus %04x:%02x\n",
			 pci_domain_nr(bus), bus->number);
		pcibios_fixup_bus(bus);
		if (pci_is_root_bus(bus))
			bus->is_added = 1;
	}
	/* 探測 pci 橋上的裝置,建立子Bus,掛到父 bus->child */
	for (pass=0; pass < 2; pass++)
		list_for_each_entry(dev, &bus->devices, bus_list) {
			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
				max = pci_scan_bridge(bus, dev, max, pass);
		}
 
	/*
	 * We've scanned the bus and so we know all about what's on
	 * the other side of any bridges that may be on this bus plus
	 * any devices.
	 *
	 * Return how far we've got finding sub-buses.
	 */
	pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",
		pci_domain_nr(bus), bus->number, max);
	return max;
}

這節的難點就是在這個地方了,從我們之前分析的pci裝置配置空間的讀寫方式可得知.對特定匯流排.下面最多個32個裝置號.每個裝置號又對應8 個功能號.我們可以將裝置號和功能號放到一起,即佔8~15位.在這面的程式碼中.對每個裝置號呼叫pci_scan_slot()去掃描它下面的8個功能號對應的裝置.總而言之,把該匯流排下面的所有裝置都要列舉完.

int pci_scan_slot(struct pci_bus *bus, int devfn)
{
	int fn, nr = 0;
	struct pci_dev *dev;
	
	dev = pci_scan_single_device(bus, devfn);
	
	/* 如果是多功能裝置 */
	if (dev && dev->multifunction) {
		for (fn = 1; fn < 8; fn++) {
			dev = pci_scan_single_device(bus, devfn + fn);
			if (dev) {
				if (!dev->is_added)
					nr++;
				dev->multifunction = 1;
			}
		}
	}
 
	return nr;
}

struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn)
{
	struct pci_dev *dev;
	/* 遍歷 bus->devices 裝置連結串列,查詢是否有 devfn 號裝置存在 */
	dev = pci_get_slot(bus, devfn);
	/* 如果已經存在,返回它 */
	if (dev) {
		pci_dev_put(dev);
		return dev;
	}
	/* 通過訪問配置空間,探測裝置 */
	dev = pci_scan_device(bus, devfn);
	/* 探測失敗 返回Null */
	if (!dev)
		return NULL;
	/* 探測成功 */
	pci_device_add(dev, bus);
 
	return dev;
}

呼叫pci_scan_device()執行掃描的過程,如果該裝置存在,就會將該裝置加入到所屬匯流排的devices連結串列上.這是在pci_device_add()函式中完成的,這個函式比較簡單.這裡不做詳細分析.我們把注意力集中到pci_scan_device(),這函式有點長,分段分析如下:

static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
{
	struct pci_dev *dev;
	u32 l;
	int delay = 1;
	
	/* 讀  PCI_VENDOR_ID 製造商ID */
	if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
		return NULL;
	
	/* id 等於這些值,認為探測失敗 ,返回 */
	if (l == 0xffffffff || l == 0x00000000 ||
	    l == 0x0000ffff || l == 0xffff0000)
		return NULL;
	....
	
	/* 探測成功,分配一個 pci_dev 結構 */
	dev = alloc_pci_dev();
 
	dev->bus = bus;
	dev->devfn = devfn;
	dev->vendor = l & 0xffff;
	dev->device = (l >> 16) & 0xffff;
	/* 讀取配置空間,更詳細的設定,指定 dev->bus 等 */
	if (pci_setup_device(dev)) {
		kfree(dev);
		return NULL;
	}
 
	return dev;
}

從配置空間中讀取該裝置對應的vendor id和device id.如果讀出來的值,有一個是空的,則說明該功能號對應的裝置不存在,或者是配置非法.

如果讀出來的是0xffff0001.則需要重新讀一次,如果重讀次數過多,也會退出

//對特定型別的裝置配置都行讀取操作
int pci_setup_device(struct pci_dev *dev)
{
	u32 class;
	u8 hdr_type;
	struct pci_slot *slot;
 
 
	dev->sysdata = dev->bus->sysdata;
	dev->dev.parent = dev->bus->bridge;
	
	/* 設定 dev 所屬的匯流排 */
	dev->dev.bus = &pci_bus_type;
	dev->hdr_type = hdr_type & 0x7f;
	dev->multifunction = !!(hdr_type & 0x80);
	dev->error_state = pci_channel_io_normal;
	set_pcie_port_type(dev);
 
 
	list_for_each_entry(slot, &dev->bus->slots, list)
		if (PCI_SLOT(dev->devfn) == slot->number)
			dev->slot = slot;
 
 
	dev->dma_mask = 0xffffffff;
	/* 裝置名 */
	dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
		     dev->bus->number, PCI_SLOT(dev->devfn),
		     PCI_FUNC(dev->devfn));
	/* 裝置型別 */
	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
	dev->revision = class & 0xff;
	class >>= 8;				    /* upper 3 bytes */
	dev->class = class;
	class >>= 8;
 
 
	/* need to have dev->class ready */
	dev->cfg_size = pci_cfg_space_size(dev);
 
 
	/* "Unknown power state" */
	dev->current_state = PCI_UNKNOWN;
 
 
	/* Early fixups, before probing the BARs */
	pci_fixup_device(pci_fixup_early, dev);
	/* device class may be changed after fixup */
	class = dev->class >> 8;
 
 
	switch (dev->hdr_type) {		    /* header type */
	case PCI_HEADER_TYPE_NORMAL:		    /* standard header */
		...
	case PCI_HEADER_TYPE_BRIDGE:		    /* bridge header */
		/* 設定 de