#include #include #include #include /* for pci_print_IRQ_route */ #include <../arch/i386/kernel/pci-i386.h> static int assign_irq=0; /* borrow from pci.c, I need it with no __initdev */ struct pci_dev * pci_scan_device(struct pci_dev *temp) { struct pci_dev *dev; u32 l; if (pci_read_config_dword(temp, PCI_VENDOR_ID, &l)) return NULL; /* some broken boards return 0 or ~0 if a slot is empty: */ if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) return NULL; dev = kmalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; memcpy(dev, temp, sizeof(*dev)); dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) set this higher, assuming the system even supports it. */ dev->dma_mask = 0xffffffff; if (pci_setup_device(dev) < 0) { kfree(dev); dev = NULL; } return dev; } #if 1 /* as in compaq hotplug driver, but print more info */ static int pci_print_IRQ_route (void) { struct irq_routing_table *routing_table; int len; int loop; u8 tbus, tdevice, tslot; routing_table = pcibios_get_irq_routing_table(); if (routing_table == NULL) { printk(KERN_ERR "No BIOS Routing Table??? Not good\n"); return -ENOMEM; } len = (routing_table->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); // Make sure I got at least one entry if (len == 0) { kfree(routing_table); return -1; } printk(KERN_INFO "IRQ router %2x:%2x.%d %04x:%04x\n", (unsigned int)routing_table->rtr_bus, (unsigned int)PCI_SLOT(routing_table->rtr_devfn), (unsigned int)PCI_FUNC(routing_table->rtr_devfn), (unsigned int)routing_table->rtr_vendor,(unsigned int)routing_table->rtr_device); printk(KERN_INFO "Exclusive IRQs %04x\n",(unsigned int)routing_table->exclusive_irqs); printk(KERN_INFO "bus dev func slot\n"); for (loop = 0; loop < len; ++loop) { tbus = routing_table->slots[loop].bus; tdevice = routing_table->slots[loop].devfn; tslot = routing_table->slots[loop].slot; printk(KERN_INFO "%d %d %d %d\n", (unsigned int)tbus, (unsigned int)PCI_SLOT(tdevice), (unsigned int)PCI_FUNC(tdevice), (unsigned int)tslot); { int i; for (i=0;i<4;i++) { printk(KERN_INFO " int%c %02x %04x\n", 'A'+i, (unsigned int)routing_table->slots[loop].irq[i].link, (unsigned int)routing_table->slots[loop].irq[i].bitmap); } } } kfree(routing_table); return 0; } #endif #if 0 struct irq_info { u8 bus, devfn; /* Bus, device and function */ struct { u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ u16 bitmap; /* Available IRQs */ } __attribute__((packed)) irq[4]; u8 slot; /* Slot number, 0=onboard */ u8 rfu; } __attribute__((packed)); struct irq_routing_table { u32 signature; /* PIRQ_SIGNATURE should be here */ u16 version; /* PIRQ_VERSION */ u16 size; /* Table size in bytes */ u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ u32 miniport_data; /* Crap */ u8 rfu[11]; u8 checksum; /* Modulo 256 checksum must give zero */ struct irq_info slots[0]; } __attribute__((packed)); #endif struct pci_dev* pci_rescan_new_dev(struct pci_dev *dev) { int i; for (i=0;iirq ) dev->irq = assign_irq; pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); } } /*pci_name_device(dev);*/ printk(KERN_INFO "Slot %s New <%s>\n", dev->slot_name, dev->name); /* Fix up broken headers */ #if 0 /* not exported by kernel, init time only */ pci_fixup_device(PCI_FIXUP_HEADER, dev); #endif /* FIXME insert new device */ if (pci_enable_device(dev)) { printk(KERN_ERR "pci_enable_device failed\n"); kfree(dev); return NULL; } pci_insert_device(dev,dev->bus); return dev; } /** * Rescan slot. * First, determine whether card in the slot has changed. It is * done by compare of old and actual devices for function 0. * If change detected, old device(s) removed, other functions * scanned if it is multi-function dev, new devices inserted. * * @param temp Device template. Should be set: bus and devfn. * @return Device for function 0 */ struct pci_dev* pci_rescan_slot(struct pci_dev *temp) { struct pci_bus *bus = temp->bus; struct pci_dev* old_dev=pci_find_slot(bus->number,temp->devfn); struct pci_dev *dev=NULL; struct pci_dev *first_dev = NULL; int func = 0; int new_multi = 0; /* new multifunction device */ u8 hdr_type; /* function 0 */ if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { temp->hdr_type = hdr_type & 0x7f; dev = pci_scan_device(temp); if (dev) { /* compare with old device to detect card change */ if (old_dev) { if (old_dev->vendor==dev->vendor && old_dev->device==dev->device) { kfree(dev); return old_dev; } else { printk(KERN_INFO "Slot %02x:%02x Device changed!\n", (unsigned int)bus->number,(unsigned int)PCI_SLOT(temp->devfn)); printk(KERN_INFO " Old one <%s>\n",old_dev->name); printk(KERN_INFO " New one <%s>\n",dev->name); for (func=0;func<8;func++) { old_dev=pci_find_slot(bus->number,temp->devfn+func); if (old_dev) { printk(KERN_INFO "Slot %s Removed <%s>\n", old_dev->slot_name,old_dev->name); pci_remove_device(old_dev); } } } } /* new device found */ printk(KERN_INFO "Slot %02x:%02x New <%s>\n", (unsigned int)bus->number,(unsigned int)PCI_SLOT(temp->devfn), dev->name); dev=pci_rescan_new_dev(dev); first_dev=dev; new_multi=hdr_type & 0x80; } } if ( !dev && old_dev ) { /* device removed */ for (func=0;func<8;func++) { old_dev=pci_find_slot(bus->number,temp->devfn+func); if (old_dev) { printk(KERN_INFO "Slot %s Removed <%s>\n", old_dev->slot_name,old_dev->name); pci_remove_device(old_dev); } } } if (new_multi) { /* continue scanning for other functions */ for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) { if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) continue; temp->hdr_type = hdr_type & 0x7f; dev = pci_scan_device(temp); if (dev) { dev=pci_rescan_new_dev(dev); } } } return first_dev; } /** * Rescan PCI bus. * Assumed, some slots may have changed its content. * Find old information about each slot and the new one. * If they match, do nothing. Otherwise, * remove/insert proper devices. * * @param bus */ static void pci_rescan_bus(const struct pci_bus *bus) { unsigned int devfn; struct pci_dev dev0; memset(&dev0, 0, sizeof(dev0)); dev0.bus = (struct pci_bus*)bus; dev0.sysdata = bus->sysdata; for (devfn = 0; devfn < 0x100; devfn += 8) { dev0.devfn = devfn; pci_rescan_slot(&dev0); } } static void pci_rescan_buses(const struct list_head *list) { const struct list_head *l; list_for_each(l,list) { const struct pci_bus *b = pci_bus_b(l); pci_rescan_bus(b); pci_rescan_buses(&b->children); } } static int pci_rescan_init(void) { pci_rescan_buses(&pci_root_buses); pci_print_IRQ_route(); return -ENODEV; } static void pci_rescan_exit(void) {} module_init(pci_rescan_init); module_exit(pci_rescan_exit); MODULE_PARM(assign_irq,"i"); MODULE_PARM_DESC(assign_irq,"IRQ to assign to new cards"); MODULE_DESCRIPTION("Force PCI bus rescan"); MODULE_AUTHOR("Vladimir Kondratiev, vladimir.kondratiev@intel.com");