⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pci.c

📁 newos is new operation system
💻 C
📖 第 1 页 / 共 3 页
字号:
			dprintf("ERROR: pci_get_capability: PCItoPCI bridge header type has no capabilities pointer???\n");			return 0;		case PCI_header_type_cardbus:			ofs = PCI_capabilities_ptr_2;			break;		default:			dprintf("ERROR: pci_get_capability: unknown PCI header type\n");			return 0;	}	/* the 192 bytes vendor defined configuration space can	 * hold as maximum 48 times a 32 bit aligned capability,	 * we use this as abort condition to avoid lock up by	 * searching in a circular loop on bad hardware	 */	maxcount = 48;	ofs = read_pci_config(bus, dev, func, ofs, 1);	while (maxcount-- != 0 && ofs != 0) {		/* mask off low two bits, demanded by PCI standard */		ofs &= ~3;		/* PCI specification 2.2, section 6.8.1.1 and following		 * describe capability ID and next capability position as		 * two 8 bit values, the "capability ID" is at the 32 bit		 * aligned position, after it the "next pointer" follows.		 */		/* read the 8 bit capability id is at the 32bit aligned ofs position */		cap_data = read_pci_config(bus, dev, func, ofs, 1);		if (cap_data == cap) {			if (offs)				*offs = ofs;			return 1;		}		/* at ofs + 1, we can read the next capability position */		ofs = read_pci_config(bus, dev, func, ofs + 1, 1);	}	return 0;}/* pci_set_power_state * This attempts to set the device into one of the 4 power management * modes, where PCI_pm_state_d0 is "Full Power" and _d3 is the lowest. * The delay's at the end come from code I've seen but they really need * to be checked against the spec, which can be found at * PCI PM 1.1, section 5.6.1 table 18 * * There is a note on Linux to say that whilst we can jump straight to * D0 we can't move to D3 unless we're already at D1, though the code they * use to check this seems suspect. We may want to check against the spec. * * Returns * ERR_IO_ERROR if the device doesn't support the power management state requested * */static intpci_set_power_state(uint8 bus, uint8 dev, uint8 func, int state){	uint8 pm_reg, cur_state;	uint16 cur_status;	if (pci_get_capability(bus, dev, func, PCI_cap_id_pm, &pm_reg) == 0)		return ERR_IO_ERROR;	if (state > PCI_pm_state_d3)		state = PCI_pm_state_d3;	cur_status = read_pci_config(bus, dev, func, pm_reg + PCI_pm_status, 1);	cur_state = cur_status & PCI_pm_mask;	if (cur_state == state)		return 0;	if (state == PCI_pm_state_d1 || state == PCI_pm_state_d2) {		uint16 pmc;		pmc = read_pci_config(bus, dev, func, pm_reg + PCI_pm_ctrl, 2);		if (state == PCI_pm_state_d1 && !(pmc & PCI_pm_d1supp))			return ERR_IO_ERROR;		if (state == PCI_pm_state_d2 && !(pmc & PCI_pm_d2supp))			return ERR_IO_ERROR;	}	if (cur_state != PCI_pm_state_d3) {		cur_status &= ~PCI_pm_mask;		cur_status |= state;	}	write_pci_config(bus, dev, func, pm_reg + PCI_pm_status, 2, cur_status);	if (state == PCI_pm_state_d3 || cur_state == PCI_pm_state_d3) {		thread_snooze(10 * 1000);	} else if (state == PCI_pm_state_d2 || cur_state == PCI_pm_state_d2)		thread_snooze(200);	return 0;}/* pci_set_bus_master * This sets the bus master bit in the command configuration register * if it isn't already set. */static voidpci_set_bus_master(uint8 bus, uint8 dev, uint8 func){        uint16 command;	command = read_pci_config(bus, dev, func, PCI_command, 2);	// if the bus master bit isn't already set, set it and write back.        if ( (command & PCI_command_master) != PCI_command_master)		write_pci_config(bus, dev, func, PCI_command, 2, 				 command | PCI_command_master);}/* This used to be fixup_host_bridges, but some PCI-PCI bridges need * to be adjusted as well so I'll make it more general. * * Partially borrowed from NetBSD. */static voidfixup_bridge(uint8 bus, uint8 dev, uint8 func){	uint16 vendor, device;	vendor = read_pci_config(bus, dev, func, PCI_vendor_id, 2);	device = read_pci_config(bus, dev, func, PCI_device_id, 2);	switch (vendor) {		case PCI_VENDOR_INTEL:			switch (device) {				case PCI_PRODUCT_INTEL_82443BX_AGP:				case PCI_PRODUCT_INTEL_82443BX_NOAGP: {					/* BIOS bug workaround					 * While the datasheet indicates that the only valid setting					 * for "Idle/Pipeline DRAM Leadoff Timing (IPLDT)" is 01,					 * some BIOS's do not set these correctly, so we check and					 * correct if required.					 */					uint16 bcreg = read_pci_config(bus, dev, func, 0x76, 2);					if ((bcreg & 0x0300) != 0x0100) {						dprintf("Intel 82443BX Host Bridge: Fixing IPDLT setting\n");						bcreg &= ~0x0300;						bcreg |= 0x100;						write_pci_config(bus, dev, func, 0x76, 2, bcreg);					}					break;				}				case PCI_PRODUCT_INTEL_82845_AGP: {					/* Foward accesses to VGA memory onto the AGP card					 *					 * This is very experimental! Added to see if this will					 * fix Marcus's problem with this device.					 */					uint8 ctrl = read_pci_config(bus, dev, func, 0x3e, 1);					if ((ctrl & 0x04) != 0x04) {						dprintf("Enabling VGA_EN1 for Intel 82845 AGP Bridge\n");						ctrl |= 0x04;						write_pci_config(bus, dev, func, 0x3e, 1, ctrl);					}				}			}	}}/* Given a vendor/device pairing, do we need to scan through * the entire set of funtions? normally the return will be 0, * implying we don't need to, but for some it will be 1 which * means scan all functions. * This function may seem overkill but it prevents scanning * functions we don't need to and shoudl reduce the possibility of * duplicates being detected. */static intpci_quirk_multifunction(uint16 vendor, uint16 device){	switch (vendor) {		case PCI_VENDOR_INTEL:			switch (device) {				case PCI_PRODUCT_INTEL_82371AB_ISA:				case PCI_PRODUCT_INTEL_82371AB_IDE:				case PCI_PRODUCT_INTEL_82371AB_USB:				case PCI_PRODUCT_INTEL_82371AB_PMC:					return 1;			}	}	return 0;}/* set_pci_mechanism() * Try to determine which configuration mechanism the PCI Host Bridge * wants to deal with. * XXX - we really should add code to detect and use a PCI BIOS if one *       exists, and this code then becomes the fallback. For now we'll *       just use this. */static intset_pci_mechanism(void){	uint32 ckval = 0x80000000;	/* Start by looking for the older and more limited mechanism 2	 * as the test will probably work for mechanism 1 as well.	 *	 * This code copied/adapted from OpenBSD	 */#define PCI_MODE2_ENABLE  0x0cf8#define PCI_MODE2_FORWARD 0x0cfa	out8(0, PCI_MODE2_ENABLE);	out8(0, PCI_MODE2_FORWARD);	if (in8(PCI_MODE2_ENABLE) == 0 &&	    in8(PCI_MODE2_FORWARD) == 0) {		dprintf("PCI_Mechanism 2 test passed\n");		bus_max_devices = 16;		pci_mode = 2;		return 0;	}	/* If we get here, the first test (for mechanism 2) failed, so there	 * is a good chance this one will pass. Basically enable then disable and	 * make sure we have the same values.	 */#define PCI_MODE1_ADDRESS 0x0cf8	out32(ckval, PCI_MODE1_ADDRESS);	if (in32(PCI_MODE1_ADDRESS) == ckval) {		out32(0, PCI_MODE1_ADDRESS);		if (in32(PCI_MODE1_ADDRESS) == 0) {			dprintf("PCI_Mechanism 1 test passed\n");			bus_max_devices = 32;			pci_mode = 1;			return 0;		}	}	dprintf("PCI: Failed to find a valid PCI Configuration Mechanism!\n"	        "PCI: disabled\n");	pci_mode = 0;	return -1;}/* check_pci() * Basically PCI bus #0 "should" contain a PCI Host Bridge. * This can be identified by the 8 bit pci class base of 0x06. * * XXX - this is pretty simplistic and needs improvement. In particular *       some Intel & Compaq bridges don't have the correct class base set. *       Need to review these. For the time being if this sanity check *       fails it won't be a hanging offense, but it will generate a *       message asking for the info to be sent to the kernel list so we *       can refine this code. :) Assuming anyone ever looks at the *       debug output! * * returns  0 if PCI seems to be OK *         -1 if PCI fails the test */static intcheck_pci(void){	int dev = 0;	/* Scan through the first 16 devices on bus 0 looking for	 * a PCI Host Bridge	 */	for (dev = 0; dev < bus_max_devices; dev++) {		uint8 val = read_pci_config(0, dev, 0, PCI_class_base, 1);		if (val == 0x06)			return 0;	}	/* Bit wordy, but it needs to be :( */	dprintf("*** PCI Warning! ***\n"	        "The PCI sanity check appears to have failed on your system.\n"	        "This is probably due to the test being used, so please email\n"	        "\topen-beos-kernel-devel@lists.sourceforge.net\n"	        "Your assistance will help improve this test :)\n"	        "***\n"	        "PCI will attempt to continue normally.\n");	return -1;}/* Intel specific * * See http://www.microsoft.com/HWDEV/busbios/PCIIRQ.htm * * Intel boards have a PCI IRQ Routing table that contains details of * how things get routed, information we need :( */struct linkmap {	uint8  pin;	uint16 possible_irq;} _PACKED;struct pir_slot {	uint8  bus;	uint8  devfunc;	struct linkmap linkmap[4];	uint8  slot;	uint8  reserved;} _PACKED;#define PIR_HEADER "$PIR"struct pir_header {	char   signature[4];     /* always '$PIR' */	uint16 version;	uint16 tbl_sz;	uint8  router_bus;	uint8  router_devfunc;	uint16 exclusive_irq;	uint32 compat_vend;	uint32 miniport;	uint8  rsvd[11];	uint8  cksum;} _PACKED;struct pir_table {	struct pir_header hdr;	struct pir_slot slot[1];} _PACKED;#define PIR_DEVICE(devfunc)     (((devfunc) >> 3) & 0x1f)#define PIR_FUNCTION(devfunc)   ((devfunc) & 0x07)/* find_pir_table * No real magic, just scan through the memory until we find it, * or not! * * When we check the size of the table, we need to satisfy that *  size >= size of a pir_table with no pir_slots *  size <= size of a pir_table with 32 pir_slots * If we find a table, and it's a suitable size we'll check that the * checksum is OK. This is done by simply adding up all the bytes and * checking that the final value == 0. If it is we return the pointer to the table. * * Returns NULL if we fail to find a suitable table. */static struct pir_table *find_pir_table(void){	uint32 mem_addr = (uint32)pci_bios_ptr;	int range = 0x10000;	for (; range > 0; range -= 16, mem_addr += 16) {		if (memcmp((void*)mem_addr, PIR_HEADER, 4) == 0) {			uint16 size = ((struct pir_header*)mem_addr)->tbl_sz;			if ((size >= (sizeof(struct pir_header))) &&			    (size <= (sizeof(struct pir_header) + sizeof(struct pir_slot) * 32))) {				uint16 i;				uint8 cksum = 0;				for (i = 0; i < size; i++)					cksum += ((uint8*)mem_addr)[i];				if (cksum == 0)					return (struct pir_table *)mem_addr;			}		}	}	return NULL;}#if TRACE_PCI/* print_pir_table * Print out the table to debug output */static voidprint_pir_table(struct pir_table *tbl){	int i, j;	int entries = (tbl->hdr.tbl_sz - sizeof(struct pir_header)) / sizeof(struct pir_slot);	for (i = 0; i < entries; i++) {		dprintf("PIR slot %d: bus %d, device %d\n",		        tbl->slot[i].slot, tbl->slot[i].bus,		        PIR_DEVICE(tbl->slot[i].devfunc));		for (j = 0; j < 4; j++)			dprintf("\tINT%c: pin %d possible_irq's %04x\n",			        'A'+j, tbl->slot[i].linkmap[j].pin,			        tbl->slot[i].linkmap[j].possible_irq);	}	dprintf("*** end of table\n");}#endif/* Scanning for Devices * ==================== * * http://www.tldp.org/LDP/tlk/dd/pci.html * * Although it refers to Linux it gives a good overview of the basic * methodology they use for scanning PCI. We've adopted a similar * approach here, using the "depthwise" algorithm. * * We start by scanning a bus, which treats every device it finds the * same and passes them onto pci_probe_device(). * pci_probe_device() sends pci_bridges to pci_bridge() to be setup * and deals with type 0 (generic) devices itself. * * NB presently we don't really cope with type 2 (Cardbus) devices *    but will need to eventually. *//**	The values passed in specify the "device" on the bus being searched *	that has been identified as a PCI-PCI bridge. We now setup that bridge *	and scan the bus it defines. *	The bus is initially taken off-line, scanned and then put back on-line * *	NB We increment the pci_max_bus value before we use mybus in the following *	  code and pci_max_bus is incremented before we recurse to preserve the *	  correct relationships with nubering. * *	We initally set the subordinate_bus to 0xff and then adjust it to the max *	once we've scanned the bus on the other side of the bridge. See the URL *	above for information on why this is done. */static voidpci_bridge(uint8 bus, uint8 dev, uint8 func){	uint16 command;	uint8 mybus;	struct pci_device *pcid;	struct pci_bus *pcib;	pci_info *pcii;	TRACE(("pci_bridge()\n"));	command = read_pci_config(bus, dev, func, PCI_command, 2);	command &= ~ 0x03;	write_pci_config(bus, dev, func, PCI_command, 2, command);	pci_max_bus += 1;	mybus = pci_max_bus;	write_pci_config(bus, dev, func, PCI_primary_bus, 1, bus);	write_pci_config(bus, dev, func, PCI_secondary_bus, 1, mybus);	write_pci_config(bus, dev, func, PCI_subordinate_bus, 1, 0xff);	dprintf("PCI-PCI bridge at %d:%d:%d configured as bus %d\n", bus, dev, func, mybus);	pci_scan_bus(mybus);	write_pci_config(bus, dev, func, PCI_subordinate_bus, 1, pci_max_bus);	pcii = (pci_info *)kmalloc(sizeof(pci_info));	if (!pcii)		goto pci_bridge_skip_infolist;	pcid = (struct pci_device *)kmalloc(sizeof(struct pci_device));	if (!pcid) {		kfree(pcii);		goto pci_bridge_skip_infolist;	}	pcib = (struct pci_bus *)kmalloc(sizeof(struct pci_bus));	if (!pcib) {		kfree(pcii);		kfree(pcid);		goto pci_bridge_skip_infolist;	}	pcid->info = pcii;	pcid->type = PCI_BRIDGE;	pcid->next = NULL;	pcib->info = pcii;	pcib->next = NULL;	pcii->bus = bus;	pcii->device = dev;	pcii->function = func;	fill_basic_pci_structure(pcii);	pcii->u.h1.rom_base_pci =        read_pci_config(bus, dev, func, PCI_bridge_rom_base, 4);	pcii->u.h1.primary_bus =         read_pci_config(bus, dev, func, PCI_primary_bus, 1);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -