📄 pci_fixup.c
字号:
};#define ROUTER_COUNT (sizeof(router_table)/sizeof(router_table[0]))/* Global variables for current interrupt routing table */static struct routing_table *pirq = NULL;static struct pci_dev *router_dev = NULL;static struct router *router_info = NULL;#ifndef __va#define __va(x) (x)#endifstatic void scan_pirq_table(void){ struct routing_table *r; struct pci_dev *router, *dev; u8 pin, fn, *p; int i, j; struct slot_entry *e; /* Scan the BIOS for the routing table signature */ for (p = (u8 *)__va(0xf0000); p < (u8 *)__va(0xfffff); p += 16) if ((p[0] == '$') && (p[1] == 'P') && (p[2] == 'I') && (p[3] == 'R')) break; if (p >= (u8 *)__va(0xfffff)) return; pirq = r = (struct routing_table *)p; printk(KERN_INFO "PCI routing table version %d.%d at %#06x\n", r->major, r->minor, (u32)r & 0xfffff); for (i = j = 0; i < 16; i++) j += (r->pci_mask >> i) & 1; if (j > 4) printk(KERN_NOTICE " bogus PCI irq mask %#04x!\n", r->pci_mask); else pci_irq_mask |= r->pci_mask; router_dev = router = pci_find_slot(r->bus, r->devfn); if (router) { for (i = 0; i < ROUTER_COUNT; i++) { if ((router->vendor == router_table[i].vendor) && (router->device == router_table[i].device)) break; if (((r->compat & 0xffff) == router_table[i].vendor) && ((r->compat >> 16) == router_table[i].device)) break; } if (i == ROUTER_COUNT) printk(KERN_INFO " unknown PCI interrupt router %04x:%04x\n", router->vendor, router->device); else router_info = &router_table[i]; } for (e = r->entry; (u8 *)e < p+r->size; e++) { for (fn = 0; fn < 8; fn++) { dev = pci_find_slot(e->bus, e->devfn | fn); if ((dev == NULL) || (dev->irq != 0)) continue; pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if ((pin == 0) || (pin == 255)) continue; if (router_info) { dev->irq = router_info->xlate(router, e->pin[pin-1].link); } else { /* Fallback: see if only one irq possible */ int map = e->pin[pin-1].irq_map; if (map && (!(map & (map-1)))) dev->irq = ffs(map)-1; } if (dev->irq) { printk(KERN_INFO " %02x:%02x.%1x -> irq %d\n", e->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), dev->irq); pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); } } }}#endif /* (LINUX_VERSION_CODE < VERSION(2,3,24)) && defined(__i386__) *//*====================================================================== PCI device enabler This is not at all generic... it is mostly a hack to correctly configure CardBus bridges. ======================================================================*/#if (LINUX_VERSION_CODE < VERSION(2,3,24))static int check_cb_mapping(u_int phys){ /* A few sanity checks to validate the bridge mapping */ char *virt = ioremap(phys, 0x1000); int ret = ((readb(virt+0x800+I365_IDENT) & 0x70) || (readb(virt+0x800+I365_CSC) && readb(virt+0x800+I365_CSC) && readb(virt+0x800+I365_CSC))); int state = readl(virt+CB_SOCKET_STATE) >> 16; ret |= (state & ~0x3000) || !(state & 0x3000); ret |= readl(virt+CB_SOCKET_FORCE); iounmap(virt); return ret;}static void setup_cb_bridge(struct pci_dev *dev){ u8 bus, sub; u32 phys; int i; /* This is nasty, but where else can we put it? */ if (PCI_FUNC(dev->devfn) == 0) { struct pci_dev *sib; sib = pci_find_slot(dev->bus->number, dev->devfn+1); if (sib) { u8 a, b; u32 c, d; /* Check for bad PCI bus numbering */ pci_read_config_byte(dev, CB_CARDBUS_BUS, &a); pci_read_config_byte(sib, CB_CARDBUS_BUS, &b); if (a == b) { pci_write_config_byte(dev, CB_CARDBUS_BUS, 0); pci_write_config_byte(sib, CB_CARDBUS_BUS, 0); } /* check for bad register mapping */ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &c); pci_read_config_dword(sib, PCI_BASE_ADDRESS_0, &d); if ((c != 0) && (c == d)) { pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0); pci_write_config_dword(sib, PCI_BASE_ADDRESS_0, 0); } } } /* Assign PCI bus numbers, if needed */ pci_read_config_byte(dev, CB_CARDBUS_BUS, &bus); pci_read_config_byte(dev, CB_SUBORD_BUS, &sub); if ((cb_bus_base > 0) || (bus == 0)) { if (cb_bus_base <= 0) cb_bus_base = 0x20; bus = cb_bus_base; sub = cb_bus_base+cb_bus_step; cb_bus_base += cb_bus_step+1; pci_write_config_byte(dev, CB_CARDBUS_BUS, bus); pci_write_config_byte(dev, CB_SUBORD_BUS, sub); } /* Create pci_bus structure for the CardBus, if needed */ { struct pci_bus *child, *parent = dev->bus; for (child = parent->children; child; child = child->next) if (child->number == bus) break; if (!child) { child = kmalloc(sizeof(struct pci_bus), GFP_KERNEL); memset(child, 0, sizeof(struct pci_bus)); child->self = dev; child->primary = bus; child->number = child->secondary = bus; child->subordinate = sub; child->parent = parent;#if (LINUX_VERSION_CODE >= VERSION(2,3,15)) child->ops = parent->ops;#endif child->next = parent->children; parent->children = child; } } /* Map the CardBus bridge registers, if needed */ pci_write_config_dword(dev, CB_LEGACY_MODE_BASE, 0); pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &phys); if ((phys == 0) || (cb_mem_base[0] != 0)) { /* Make sure the bridge is awake so we can test it */ pci_set_power_state(dev, 0); for (i = 0; i < sizeof(cb_mem_base)/sizeof(u_int); i++) { phys = cb_mem_base[i]; if (phys == 0) continue; pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, phys); if ((i == 0) || (check_cb_mapping(phys) == 0)) break; } if (i == sizeof(cb_mem_base)/sizeof(u_int)) { pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0); } else { cb_mem_base[0] = cb_mem_base[i] + 0x1000; } }}#define CMD_DFLT (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | \ PCI_COMMAND_MASTER | PCI_COMMAND_WAIT)#ifdef __i386__static u8 pirq_init(struct pci_dev *router, struct pirq_pin *pin){ u16 map = pin->irq_map; u8 irq = 0; if (pirq->pci_mask) map &= pirq->pci_mask; if (cb_pci_irq) map = 1<<cb_pci_irq; /* Be conservative: only init irq if the mask is unambiguous */ if (map && (!(map & (map-1)))) { irq = ffs(map)-1; router_info->init(router, pin->link, irq); pci_irq_mask |= (1<<irq); } return irq;}static void setup_cb_bridge_irq(struct pci_dev *dev){ struct slot_entry *e; u8 pin; u32 phys; char *virt; pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &phys); if (!pin || !phys) return; virt = ioremap(phys, 0x1000); if (virt) { /* Disable any pending interrupt sources */ writel(0, virt+CB_SOCKET_MASK); writel(-1, virt+CB_SOCKET_EVENT); iounmap(virt); } for (e = pirq->entry; (u8 *)e < (u8 *)pirq + pirq->size; e++) { if ((e->bus != dev->bus->number) || (e->devfn != (dev->devfn & ~7))) continue; dev->irq = pirq_init(router_dev, &e->pin[pin-1]); pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); break; }}#endifint pci_enable_device(struct pci_dev *dev){ pci_write_config_word(dev, PCI_COMMAND, CMD_DFLT); if ((dev->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) { setup_cb_bridge(dev); }#ifdef __i386__ /* In certain cases, if the interrupt can be deduced, but was unrouted when the pirq table was scanned, we'll try to set it up now. */ if (!dev->irq && pirq && (router_info) && ((dev->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS)) { setup_cb_bridge_irq(dev); }#endif return 0;}int pci_set_power_state(struct pci_dev *dev, int state){ u16 tmp, cmd; u32 base, bus; u8 a, b, pmcs; pci_read_config_byte(dev, PCI_STATUS, &a); if (a & PCI_STATUS_CAPLIST) { pci_read_config_byte(dev, PCI_CB_CAPABILITY_POINTER, &b); while (b != 0) { pci_read_config_byte(dev, b+PCI_CAPABILITY_ID, &a); if (a == PCI_CAPABILITY_PM) { pmcs = b + PCI_PM_CONTROL_STATUS; /* Make sure we're in D0 state */ pci_read_config_word(dev, pmcs, &tmp); if (!(tmp & PCI_PMCS_PWR_STATE_MASK)) break; pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &base); pci_read_config_dword(dev, CB_PRIMARY_BUS, &bus); pci_read_config_word(dev, PCI_COMMAND, &cmd); pci_write_config_word(dev, pmcs, PCI_PMCS_PWR_STATE_D0); pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, base); pci_write_config_dword(dev, CB_PRIMARY_BUS, bus); pci_write_config_word(dev, PCI_COMMAND, cmd); break; } pci_read_config_byte(dev, b+PCI_NEXT_CAPABILITY, &b); } } return 0;}#endif /* (LINUX_VERSION_CODE < VERSION(2,3,24)) *//*====================================================================== General setup and cleanup entry points======================================================================*/void pci_fixup_init(void){ struct pci_dev *p;#if (LINUX_VERSION_CODE < VERSION(2,3,24)) && defined(__i386__) scan_pirq_table(); pci_for_each_dev(p) if (((p->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) && (p->irq == 0)) break; if (p && !pirq) printk(KERN_INFO "No PCI interrupt routing table!\n"); if (!pirq && cb_pci_irq) printk(KERN_INFO "cb_pci_irq will be ignored.\n");#endif pci_for_each_dev(p) pci_irq_mask |= (1<<p->irq);#ifdef __alpha__#define PIC 0x4d0 pci_irq_mask |= inb(PIC) | (inb(PIC+1) << 8);#endif}void pci_fixup_done(void){#if (LINUX_VERSION_CODE < VERSION(2,1,0)) struct pci_dev *d, *dn; struct pci_bus *b, *bn; for (d = pci_devices; d; d = dn) { dn = d->next; kfree(d); } for (b = pci_root.next; b; b = bn) { bn = b->next; kfree(b); }#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -