in pci/irq.c [1148:1297]
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) {
dev_dbg(&dev->dev, "no interrupt pin\n");
return 0;
}
if (io_apic_assign_pci_irqs)
return 0;
/* Find IRQ routing entry */
if (!pirq_table)
return 0;
info = pirq_get_info(dev);
if (!info) {
dev_dbg(&dev->dev, "PCI INT %c not found in routing table\n",
'A' + pin - 1);
return 0;
}
pirq = info->irq[pin - 1].link;
mask = info->irq[pin - 1].bitmap;
if (!pirq) {
dev_dbg(&dev->dev, "PCI INT %c not routed\n", 'A' + pin - 1);
return 0;
}
dev_dbg(&dev->dev, "PCI INT %c -> PIRQ %02x, mask %04x, excl %04x",
'A' + pin - 1, 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 (newirq && !((1 << newirq) & mask)) {
if (pci_probe & PCI_USE_PIRQ_MASK)
newirq = 0;
else
dev_warn(&dev->dev, "IRQ %d doesn't match PIRQ mask "
"%#x; try pci=usepirqmask\n", newirq, mask);
}
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, IRQF_SHARED))
newirq = i;
}
}
dev_dbg(&dev->dev, "PCI INT %c -> newirq %d", 'A' + pin - 1, newirq);
/* Check if it is hardcoded */
if ((pirq & 0xf0) == 0xf0) {
irq = pirq & 0xf;
msg = "hardcoded";
} else if (r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask))) {
msg = "found";
if (r->lvl)
r->lvl(pirq_router_dev, dev, pirq, irq);
else
elcr_set_level_irq(irq);
} else if (newirq && r->set &&
(dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
if (r->set(pirq_router_dev, dev, pirq, newirq)) {
if (r->lvl)
r->lvl(pirq_router_dev, dev, pirq, newirq);
else
elcr_set_level_irq(newirq);
msg = "assigned";
irq = newirq;
}
}
if (!irq) {
if (newirq && mask == (1 << newirq)) {
msg = "guessed";
irq = newirq;
} else {
dev_dbg(&dev->dev, "can't route interrupt\n");
return 0;
}
}
dev_info(&dev->dev, "%s PCI INT %c -> IRQ %d\n", msg, 'A' + pin - 1, irq);
/* Update IRQ for all devices with the same pirq value */
for_each_pci_dev(dev2) {
pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
if (!pin)
continue;
info = pirq_get_info(dev2);
if (!info)
continue;
if (info->irq[pin - 1].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
dev_info(&dev2->dev, "IRQ routing conflict: "
"have IRQ %d, want IRQ %d\n",
dev2->irq, irq);
#endif
continue;
}
dev2->irq = irq;
pirq_penalty[irq]++;
if (dev != dev2)
dev_info(&dev->dev, "sharing IRQ %d with %s\n",
irq, pci_name(dev2));
}
}
return 1;
}