irq.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,096 行 · 第 1/2 页

C
1,096
字号
static __init int cyrix_router_probe(struct irq_router *r, struct pci_dev *router, u16 device){	switch(device)	{		case PCI_DEVICE_ID_CYRIX_5520:			r->name = "NatSemi";			r->get = pirq_cyrix_get;			r->set = pirq_cyrix_set;			return 1;	}	return 0;}static __init int opti_router_probe(struct irq_router *r, struct pci_dev *router, u16 device){	switch(device)	{		case PCI_DEVICE_ID_OPTI_82C700:			r->name = "OPTI";			r->get = pirq_opti_get;			r->set = pirq_opti_set;			return 1;	}	return 0;}static __init int ite_router_probe(struct irq_router *r, struct pci_dev *router, u16 device){	switch(device)	{		case PCI_DEVICE_ID_ITE_IT8330G_0:			r->name = "ITE";			r->get = pirq_ite_get;			r->set = pirq_ite_set;			return 1;	}	return 0;}static __init int ali_router_probe(struct irq_router *r, struct pci_dev *router, u16 device){	switch(device)	{	case PCI_DEVICE_ID_AL_M1533:	case PCI_DEVICE_ID_AL_M1563:		printk("PCI: Using ALI IRQ Router\n");			r->name = "ALI";			r->get = pirq_ali_get;			r->set = pirq_ali_set;			return 1;	}	return 0;}static __init int amd_router_probe(struct irq_router *r, struct pci_dev *router, u16 device){	switch(device)	{		case PCI_DEVICE_ID_AMD_VIPER_740B:			r->name = "AMD756";			break;		case PCI_DEVICE_ID_AMD_VIPER_7413:			r->name = "AMD766";			break;		case PCI_DEVICE_ID_AMD_VIPER_7443:			r->name = "AMD768";			break;		default:			return 0;	}	r->get = pirq_amd756_get;	r->set = pirq_amd756_set;	return 1;}		static __initdata struct irq_router_handler pirq_routers[] = {	{ PCI_VENDOR_ID_INTEL, intel_router_probe },	{ PCI_VENDOR_ID_AL, ali_router_probe },	{ PCI_VENDOR_ID_ITE, ite_router_probe },	{ PCI_VENDOR_ID_VIA, via_router_probe },	{ PCI_VENDOR_ID_OPTI, opti_router_probe },	{ PCI_VENDOR_ID_SI, sis_router_probe },	{ PCI_VENDOR_ID_CYRIX, cyrix_router_probe },	{ PCI_VENDOR_ID_VLSI, vlsi_router_probe },	{ PCI_VENDOR_ID_SERVERWORKS, serverworks_router_probe },	{ PCI_VENDOR_ID_AMD, amd_router_probe },	/* Someone with docs needs to add the ATI Radeon IGP */	{ 0, NULL }};static struct irq_router pirq_router;static struct pci_dev *pirq_router_dev;/* *	FIXME: should we have an option to say "generic for *	chipset" ? */ static void __init pirq_find_router(struct irq_router *r){	struct irq_routing_table *rt = pirq_table;	struct irq_router_handler *h;#ifdef CONFIG_PCI_BIOS	if (!rt->signature) {		printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n");		r->set = pirq_bios_set;		r->name = "BIOS";		return;	}#endif	/* Default unless a driver reloads it */	r->name = "default";	r->get = NULL;	r->set = NULL;		DBG("PCI: Attempting to find IRQ router for %04x:%04x\n",	    rt->rtr_vendor, rt->rtr_device);	pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn);	if (!pirq_router_dev) {		DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);		return;	}	for( h = pirq_routers; h->vendor; h++) {		/* First look for a router match */		if (rt->rtr_vendor == h->vendor && h->probe(r, pirq_router_dev, rt->rtr_device))			break;		/* Fall back to a device match */		if (pirq_router_dev->vendor == h->vendor && h->probe(r, pirq_router_dev, pirq_router_dev->device))			break;	}	printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n",		pirq_router.name,		pirq_router_dev->vendor,		pirq_router_dev->device,		pci_name(pirq_router_dev));}static struct irq_info *pirq_get_info(struct pci_dev *dev){	struct irq_routing_table *rt = pirq_table;	int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);	struct irq_info *info;	for (info = rt->slots; entries--; info++)		if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))			return info;	return NULL;}static int pcibios_lookup_irq(struct pci_dev *dev, int assign){	u8 pin;	struct irq_info *info;	int i, pirq, newirq;	int irq = 0;	u32 mask;	struct irq_router *r = &pirq_router;	struct pci_dev *dev2 = NULL;	char *msg = NULL;	/* Find IRQ pin */	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);	if (!pin) {		DBG(" -> no interrupt pin\n");		return 0;	}	pin = pin - 1;	/* Find IRQ routing entry */	if (!pirq_table)		return 0;		DBG("IRQ for %s:%d", pci_name(dev), pin);	info = pirq_get_info(dev);	if (!info) {		DBG(" -> not found in routing table\n");		return 0;	}	pirq = info->irq[pin].link;	mask = info->irq[pin].bitmap;	if (!pirq) {		DBG(" -> not routed\n");		return 0;	}	DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);	mask &= pcibios_irq_mask;	/* Work around broken HP Pavilion Notebooks which assign USB to	   IRQ 9 even though it is actually wired to IRQ 11 */	if (broken_hp_bios_irq9 && pirq == 0x59 && dev->irq == 9) {		dev->irq = 11;		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11);		r->set(pirq_router_dev, dev, pirq, 11);	}	/* same for Acer Travelmate 360, but with CB and irq 11 -> 10 */	if (acer_tm360_irqrouting && dev->irq == 11 && dev->vendor == PCI_VENDOR_ID_O2) {		pirq = 0x68;		mask = 0x400;		dev->irq = r->get(pirq_router_dev, dev, pirq);		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);	}	/*	 * Find the best IRQ to assign: use the one	 * reported by the device if possible.	 */	newirq = dev->irq;	if (!((1 << newirq) & mask)) {		if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0;		else printk(KERN_WARNING "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n", newirq, pci_name(dev));	}	if (!newirq && assign) {		for (i = 0; i < 16; i++) {			if (!(mask & (1 << i)))				continue;			if (pirq_penalty[i] < pirq_penalty[newirq] && can_request_irq(i, SA_SHIRQ))				newirq = i;		}	}	DBG(" -> newirq=%d", newirq);	/* Check if it is hardcoded */	if ((pirq & 0xf0) == 0xf0) {		irq = pirq & 0xf;		DBG(" -> hardcoded IRQ %d\n", irq);		msg = "Hardcoded";	} else if ( r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \	((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) {		DBG(" -> got IRQ %d\n", irq);		msg = "Found";	} else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {		DBG(" -> assigning IRQ %d", newirq);		if (r->set(pirq_router_dev, dev, pirq, newirq)) {			eisa_set_level_irq(newirq);			DBG(" ... OK\n");			msg = "Assigned";			irq = newirq;		}	}	if (!irq) {		DBG(" ... failed\n");		if (newirq && mask == (1 << newirq)) {			msg = "Guessed";			irq = newirq;		} else			return 0;	}	printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, pci_name(dev));	/* Update IRQ for all devices with the same pirq value */	while ((dev2 = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) {		pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);		if (!pin)			continue;		pin--;		info = pirq_get_info(dev2);		if (!info)			continue;		if (info->irq[pin].link == pirq) {			/* We refuse to override the dev->irq information. Give a warning! */		    	if ( dev2->irq && dev2->irq != irq && \			(!(pci_probe & PCI_USE_PIRQ_MASK) || \			((1 << dev2->irq) & mask)) ) {#ifndef CONFIG_PCI_MSI		    		printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n",				       pci_name(dev2), dev2->irq, irq);#endif		    		continue;		    	}			dev2->irq = irq;			pirq_penalty[irq]++;			if (dev != dev2)				printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, pci_name(dev2));		}	}	return 1;}static void __init pcibios_fixup_irqs(void){	struct pci_dev *dev = NULL;	u8 pin;	DBG("PCI: IRQ fixup\n");	while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {		/*		 * If the BIOS has set an out of range IRQ number, just ignore it.		 * Also keep track of which IRQ's are already in use.		 */		if (dev->irq >= 16) {			DBG("%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq);			dev->irq = 0;		}		/* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */		if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000)			pirq_penalty[dev->irq] = 0;		pirq_penalty[dev->irq]++;	}	dev = NULL;	while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {		pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);#ifdef CONFIG_X86_IO_APIC		/*		 * Recalculate IRQ numbers if we use the I/O APIC.		 */		if (io_apic_assign_pci_irqs)		{			int irq;			if (pin) {				pin--;		/* interrupt pins are numbered starting from 1 */				irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);	/*	 * Busses behind bridges are typically not listed in the MP-table.	 * In this case we have to look up the IRQ based on the parent bus,	 * parent slot, and pin number. The SMP code detects such bridged	 * busses itself so we should get into this branch reliably.	 */				if (irq < 0 && dev->bus->parent) { /* go back to the bridge */					struct pci_dev * bridge = dev->bus->self;					pin = (pin + PCI_SLOT(dev->devfn)) % 4;					irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, 							PCI_SLOT(bridge->devfn), pin);					if (irq >= 0)						printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", 							bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq);				}				if (irq >= 0) {					if (use_pci_vector() &&						!platform_legacy_irq(irq))						irq = IO_APIC_VECTOR(irq);					printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",						dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);					dev->irq = irq;				}			}		}#endif		/*		 * Still no IRQ? Try to lookup one...		 */		if (pin && !dev->irq)			pcibios_lookup_irq(dev, 0);	}}/* * Work around broken HP Pavilion Notebooks which assign USB to * IRQ 9 even though it is actually wired to IRQ 11 */static int __init fix_broken_hp_bios_irq9(struct dmi_system_id *d){	if (!broken_hp_bios_irq9) {		broken_hp_bios_irq9 = 1;		printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);	}	return 0;}/* * Work around broken Acer TravelMate 360 Notebooks which assign * Cardbus to IRQ 11 even though it is actually wired to IRQ 10 */static int __init fix_acer_tm360_irqrouting(struct dmi_system_id *d){	if (!acer_tm360_irqrouting) {		acer_tm360_irqrouting = 1;		printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);	}	return 0;}static struct dmi_system_id __initdata pciirq_dmi_table[] = {	{		.callback = fix_broken_hp_bios_irq9,		.ident = "HP Pavilion N5400 Series Laptop",		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),			DMI_MATCH(DMI_BIOS_VERSION, "GE.M1.03"),			DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook Model GE"),			DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"),		},	},	{		.callback = fix_acer_tm360_irqrouting,		.ident = "Acer TravelMate 36x Laptop",		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),		},	},	{ }};static int __init pcibios_irq_init(void){	DBG("PCI: IRQ init\n");	if (pcibios_enable_irq || raw_pci_ops == NULL)		return 0;	dmi_check_system(pciirq_dmi_table);	pirq_table = pirq_find_routing_table();#ifdef CONFIG_PCI_BIOS	if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN))		pirq_table = pcibios_get_irq_routing_table();#endif	if (pirq_table) {		pirq_peer_trick();		pirq_find_router(&pirq_router);		if (pirq_table->exclusive_irqs) {			int i;			for (i=0; i<16; i++)				if (!(pirq_table->exclusive_irqs & (1 << i)))					pirq_penalty[i] += 100;		}		/* If we're using the I/O APIC, avoid using the PCI IRQ routing table */		if (io_apic_assign_pci_irqs)			pirq_table = NULL;	}	pcibios_enable_irq = pirq_enable_irq;	pcibios_fixup_irqs();	return 0;}subsys_initcall(pcibios_irq_init);void pcibios_penalize_isa_irq(int irq){	/*	 *  If any ISAPnP device reports an IRQ in its list of possible	 *  IRQ's, we try to avoid assigning it to PCI devices.	 */	pirq_penalty[irq] += 100;}int pirq_enable_irq(struct pci_dev *dev){	u8 pin;	extern int interrupt_line_quirk;	struct pci_dev *temp_dev;	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);	if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {		char *msg;		msg = "";		if (io_apic_assign_pci_irqs) {			int irq;			if (pin) {				pin--;		/* interrupt pins are numbered starting from 1 */				irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);				/*				 * Busses behind bridges are typically not listed in the MP-table.				 * In this case we have to look up the IRQ based on the parent bus,				 * parent slot, and pin number. The SMP code detects such bridged				 * busses itself so we should get into this branch reliably.				 */				temp_dev = dev;				while (irq < 0 && dev->bus->parent) { /* go back to the bridge */					struct pci_dev * bridge = dev->bus->self;					pin = (pin + PCI_SLOT(dev->devfn)) % 4;					irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, 							PCI_SLOT(bridge->devfn), pin);					if (irq >= 0)						printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", 							bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq);					dev = bridge;				}				dev = temp_dev;				if (irq >= 0) {#ifdef CONFIG_PCI_MSI					if (!platform_legacy_irq(irq))						irq = IO_APIC_VECTOR(irq);#endif					printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",						dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);					dev->irq = irq;					return 0;				} else					msg = " Probably buggy MP table.";			}		} else if (pci_probe & PCI_BIOS_IRQ_SCAN)			msg = "";		else			msg = " Please try using pci=biosirq.";					/* With IDE legacy devices the IRQ lookup failure is not a problem.. */		if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5))			return 0;					printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",		       'A' + pin - 1, pci_name(dev), msg);	}	/* VIA bridges use interrupt line for apic/pci steering across	   the V-Link */	else if (interrupt_line_quirk)		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq & 15);	return 0;}int pci_vector_resources(int last, int nr_released){	int count = nr_released;	int next = last;	int offset = (last % 8);	while (next < FIRST_SYSTEM_VECTOR) {		next += 8;#ifdef CONFIG_X86_64		if (next == IA32_SYSCALL_VECTOR)			continue;#else		if (next == SYSCALL_VECTOR)			continue;#endif		count++;		if (next >= FIRST_SYSTEM_VECTOR) {			if (offset%8) {				next = FIRST_DEVICE_VECTOR + offset;				offset++;				continue;			}			count--;		}	}	return count;}

⌨️ 快捷键说明

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