📄 pci-irq.c
字号:
* 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1, * and 0x03 for SMBus. */static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq){ outb_p(pirq, 0xc00); return inb(0xc01) & 0xf;}static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq){ outb_p(pirq, 0xc00); outb_p(irq, 0xc01); return 1;}/* Support for AMD756 PCI IRQ Routing * Jhon H. Caicedo <jhcaiced@osso.org.co> * Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced) * Jun/19/2001 Alpha Release 0.1.0 (jhcaiced) * The AMD756 pirq rules are nibble-based * offset 0x56 0-3 PIRQA 4-7 PIRQB * offset 0x57 0-3 PIRQC 4-7 PIRQD */static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq){ u8 irq; irq = 0; if (pirq <= 4) { irq = read_config_nybble(router, 0x56, pirq - 1); } printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n", dev->vendor, dev->device, pirq, irq); return irq;}static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq){ printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n", dev->vendor, dev->device, pirq, irq); if (pirq <= 4) { write_config_nybble(router, 0x56, pirq - 1, irq); } return 1;}#ifdef CONFIG_PCI_BIOSstatic int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq){ struct pci_dev *bridge; int pin = pci_get_interrupt_pin(dev, &bridge); return pcibios_set_irq_routing(bridge, pin, irq);}static struct irq_router pirq_bios_router = { "BIOS", 0, 0, NULL, pirq_bios_set };#endifstatic struct irq_router pirq_routers[] = { { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, pirq_piix_get, pirq_piix_set }, { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set }, { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set }, { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set }, { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, pirq_via_get, pirq_via_set }, { "OPTI", PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700, pirq_opti_get, pirq_opti_set }, { "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set }, { "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set }, { "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set }, { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, pirq_serverworks_get, pirq_serverworks_set }, { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5, pirq_serverworks_get, pirq_serverworks_set }, { "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B, pirq_amd756_get, pirq_amd756_set }, { "default", 0, 0, NULL, NULL }};static struct irq_router *pirq_router;static struct pci_dev *pirq_router_dev;static void __init pirq_find_router(void){ struct irq_routing_table *rt = pirq_table; struct irq_router *r;#ifdef CONFIG_PCI_BIOS if (!rt->signature) { printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n"); pirq_router = &pirq_bios_router; return; }#endif DBG("PCI: Attempting to find IRQ router for %04x:%04x\n", rt->rtr_vendor, rt->rtr_device); /* fall back to default router if nothing else found */ pirq_router = &pirq_routers[ARRAY_SIZE(pirq_routers) - 1]; 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(r=pirq_routers; r->vendor; r++) { /* Exact match against router table entry? Use it! */ if (r->vendor == rt->rtr_vendor && r->device == rt->rtr_device) { pirq_router = r; break; } /* Match against router device entry? Use it as a fallback */ if (r->vendor == pirq_router_dev->vendor && r->device == pirq_router_dev->device) { pirq_router = r; } } printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n", pirq_router->name, pirq_router_dev->vendor, pirq_router_dev->device, pirq_router_dev->slot_name);}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 void pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs){}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; char *msg = NULL; if (!pirq_table) return 0; /* Find IRQ routing entry */ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if (!pin) { DBG(" -> no interrupt pin\n"); return 0; } pin = pin - 1; DBG("IRQ for %s:%d", dev->slot_name, 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; /* * Find the best IRQ to assign: use the one * reported by the device if possible. */ newirq = dev->irq; if (!newirq && assign) { for (i = 0; i < 16; i++) { if (!(mask & (1 << i))) continue; if (pirq_penalty[i] < pirq_penalty[newirq] && !request_irq(i, pcibios_test_irq_handler, SA_SHIRQ, "pci-test", dev)) { free_irq(i, dev); 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))) { 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, dev->slot_name); /* Update IRQ for all devices with the same pirq value */ pci_for_each_dev(dev2) { 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) { printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n", dev2->slot_name, dev2->irq, irq); continue; } dev2->irq = irq; pirq_penalty[irq]++; if (dev != dev2) printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, dev2->slot_name); } } return 1;}void __init pcibios_irq_init(void){ DBG("PCI: IRQ init\n"); 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(); 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; }}void __init pcibios_fixup_irqs(void){ struct pci_dev *dev; u8 pin; DBG("PCI: IRQ fixup\n"); pci_for_each_dev(dev) { /* * 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", dev->slot_name, 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]++; } pci_for_each_dev(dev) { 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) { 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); }}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;}void pcibios_enable_irq(struct pci_dev *dev){ u8 pin; pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { char *msg; if (io_apic_assign_pci_irqs) msg = " Probably buggy MP table."; else if (pci_probe & PCI_BIOS_IRQ_SCAN) msg = ""; else msg = " Please try using pci=biosirq."; printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", 'A' + pin - 1, dev->slot_name, msg); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -