📄 pci.c
字号:
dprintf("ERROR: pci_get_capability: PCItoPCI bridge header type has no capabilities pointer???\n"); return 0; case PCI_header_type_cardbus: ofs = PCI_capabilities_ptr_2; break; default: dprintf("ERROR: pci_get_capability: unknown PCI header type\n"); return 0; } /* the 192 bytes vendor defined configuration space can * hold as maximum 48 times a 32 bit aligned capability, * we use this as abort condition to avoid lock up by * searching in a circular loop on bad hardware */ maxcount = 48; ofs = read_pci_config(bus, dev, func, ofs, 1); while (maxcount-- != 0 && ofs != 0) { /* mask off low two bits, demanded by PCI standard */ ofs &= ~3; /* PCI specification 2.2, section 6.8.1.1 and following * describe capability ID and next capability position as * two 8 bit values, the "capability ID" is at the 32 bit * aligned position, after it the "next pointer" follows. */ /* read the 8 bit capability id is at the 32bit aligned ofs position */ cap_data = read_pci_config(bus, dev, func, ofs, 1); if (cap_data == cap) { if (offs) *offs = ofs; return 1; } /* at ofs + 1, we can read the next capability position */ ofs = read_pci_config(bus, dev, func, ofs + 1, 1); } return 0;}/* pci_set_power_state * This attempts to set the device into one of the 4 power management * modes, where PCI_pm_state_d0 is "Full Power" and _d3 is the lowest. * The delay's at the end come from code I've seen but they really need * to be checked against the spec, which can be found at * PCI PM 1.1, section 5.6.1 table 18 * * There is a note on Linux to say that whilst we can jump straight to * D0 we can't move to D3 unless we're already at D1, though the code they * use to check this seems suspect. We may want to check against the spec. * * Returns * ERR_IO_ERROR if the device doesn't support the power management state requested * */static intpci_set_power_state(uint8 bus, uint8 dev, uint8 func, int state){ uint8 pm_reg, cur_state; uint16 cur_status; if (pci_get_capability(bus, dev, func, PCI_cap_id_pm, &pm_reg) == 0) return ERR_IO_ERROR; if (state > PCI_pm_state_d3) state = PCI_pm_state_d3; cur_status = read_pci_config(bus, dev, func, pm_reg + PCI_pm_status, 1); cur_state = cur_status & PCI_pm_mask; if (cur_state == state) return 0; if (state == PCI_pm_state_d1 || state == PCI_pm_state_d2) { uint16 pmc; pmc = read_pci_config(bus, dev, func, pm_reg + PCI_pm_ctrl, 2); if (state == PCI_pm_state_d1 && !(pmc & PCI_pm_d1supp)) return ERR_IO_ERROR; if (state == PCI_pm_state_d2 && !(pmc & PCI_pm_d2supp)) return ERR_IO_ERROR; } if (cur_state != PCI_pm_state_d3) { cur_status &= ~PCI_pm_mask; cur_status |= state; } write_pci_config(bus, dev, func, pm_reg + PCI_pm_status, 2, cur_status); if (state == PCI_pm_state_d3 || cur_state == PCI_pm_state_d3) { thread_snooze(10 * 1000); } else if (state == PCI_pm_state_d2 || cur_state == PCI_pm_state_d2) thread_snooze(200); return 0;}/* pci_set_bus_master * This sets the bus master bit in the command configuration register * if it isn't already set. */static voidpci_set_bus_master(uint8 bus, uint8 dev, uint8 func){ uint16 command; command = read_pci_config(bus, dev, func, PCI_command, 2); // if the bus master bit isn't already set, set it and write back. if ( (command & PCI_command_master) != PCI_command_master) write_pci_config(bus, dev, func, PCI_command, 2, command | PCI_command_master);}/* This used to be fixup_host_bridges, but some PCI-PCI bridges need * to be adjusted as well so I'll make it more general. * * Partially borrowed from NetBSD. */static voidfixup_bridge(uint8 bus, uint8 dev, uint8 func){ uint16 vendor, device; vendor = read_pci_config(bus, dev, func, PCI_vendor_id, 2); device = read_pci_config(bus, dev, func, PCI_device_id, 2); switch (vendor) { case PCI_VENDOR_INTEL: switch (device) { case PCI_PRODUCT_INTEL_82443BX_AGP: case PCI_PRODUCT_INTEL_82443BX_NOAGP: { /* BIOS bug workaround * While the datasheet indicates that the only valid setting * for "Idle/Pipeline DRAM Leadoff Timing (IPLDT)" is 01, * some BIOS's do not set these correctly, so we check and * correct if required. */ uint16 bcreg = read_pci_config(bus, dev, func, 0x76, 2); if ((bcreg & 0x0300) != 0x0100) { dprintf("Intel 82443BX Host Bridge: Fixing IPDLT setting\n"); bcreg &= ~0x0300; bcreg |= 0x100; write_pci_config(bus, dev, func, 0x76, 2, bcreg); } break; } case PCI_PRODUCT_INTEL_82845_AGP: { /* Foward accesses to VGA memory onto the AGP card * * This is very experimental! Added to see if this will * fix Marcus's problem with this device. */ uint8 ctrl = read_pci_config(bus, dev, func, 0x3e, 1); if ((ctrl & 0x04) != 0x04) { dprintf("Enabling VGA_EN1 for Intel 82845 AGP Bridge\n"); ctrl |= 0x04; write_pci_config(bus, dev, func, 0x3e, 1, ctrl); } } } }}/* Given a vendor/device pairing, do we need to scan through * the entire set of funtions? normally the return will be 0, * implying we don't need to, but for some it will be 1 which * means scan all functions. * This function may seem overkill but it prevents scanning * functions we don't need to and shoudl reduce the possibility of * duplicates being detected. */static intpci_quirk_multifunction(uint16 vendor, uint16 device){ switch (vendor) { case PCI_VENDOR_INTEL: switch (device) { case PCI_PRODUCT_INTEL_82371AB_ISA: case PCI_PRODUCT_INTEL_82371AB_IDE: case PCI_PRODUCT_INTEL_82371AB_USB: case PCI_PRODUCT_INTEL_82371AB_PMC: return 1; } } return 0;}/* set_pci_mechanism() * Try to determine which configuration mechanism the PCI Host Bridge * wants to deal with. * XXX - we really should add code to detect and use a PCI BIOS if one * exists, and this code then becomes the fallback. For now we'll * just use this. */static intset_pci_mechanism(void){ uint32 ckval = 0x80000000; /* Start by looking for the older and more limited mechanism 2 * as the test will probably work for mechanism 1 as well. * * This code copied/adapted from OpenBSD */#define PCI_MODE2_ENABLE 0x0cf8#define PCI_MODE2_FORWARD 0x0cfa out8(0, PCI_MODE2_ENABLE); out8(0, PCI_MODE2_FORWARD); if (in8(PCI_MODE2_ENABLE) == 0 && in8(PCI_MODE2_FORWARD) == 0) { dprintf("PCI_Mechanism 2 test passed\n"); bus_max_devices = 16; pci_mode = 2; return 0; } /* If we get here, the first test (for mechanism 2) failed, so there * is a good chance this one will pass. Basically enable then disable and * make sure we have the same values. */#define PCI_MODE1_ADDRESS 0x0cf8 out32(ckval, PCI_MODE1_ADDRESS); if (in32(PCI_MODE1_ADDRESS) == ckval) { out32(0, PCI_MODE1_ADDRESS); if (in32(PCI_MODE1_ADDRESS) == 0) { dprintf("PCI_Mechanism 1 test passed\n"); bus_max_devices = 32; pci_mode = 1; return 0; } } dprintf("PCI: Failed to find a valid PCI Configuration Mechanism!\n" "PCI: disabled\n"); pci_mode = 0; return -1;}/* check_pci() * Basically PCI bus #0 "should" contain a PCI Host Bridge. * This can be identified by the 8 bit pci class base of 0x06. * * XXX - this is pretty simplistic and needs improvement. In particular * some Intel & Compaq bridges don't have the correct class base set. * Need to review these. For the time being if this sanity check * fails it won't be a hanging offense, but it will generate a * message asking for the info to be sent to the kernel list so we * can refine this code. :) Assuming anyone ever looks at the * debug output! * * returns 0 if PCI seems to be OK * -1 if PCI fails the test */static intcheck_pci(void){ int dev = 0; /* Scan through the first 16 devices on bus 0 looking for * a PCI Host Bridge */ for (dev = 0; dev < bus_max_devices; dev++) { uint8 val = read_pci_config(0, dev, 0, PCI_class_base, 1); if (val == 0x06) return 0; } /* Bit wordy, but it needs to be :( */ dprintf("*** PCI Warning! ***\n" "The PCI sanity check appears to have failed on your system.\n" "This is probably due to the test being used, so please email\n" "\topen-beos-kernel-devel@lists.sourceforge.net\n" "Your assistance will help improve this test :)\n" "***\n" "PCI will attempt to continue normally.\n"); return -1;}/* Intel specific * * See http://www.microsoft.com/HWDEV/busbios/PCIIRQ.htm * * Intel boards have a PCI IRQ Routing table that contains details of * how things get routed, information we need :( */struct linkmap { uint8 pin; uint16 possible_irq;} _PACKED;struct pir_slot { uint8 bus; uint8 devfunc; struct linkmap linkmap[4]; uint8 slot; uint8 reserved;} _PACKED;#define PIR_HEADER "$PIR"struct pir_header { char signature[4]; /* always '$PIR' */ uint16 version; uint16 tbl_sz; uint8 router_bus; uint8 router_devfunc; uint16 exclusive_irq; uint32 compat_vend; uint32 miniport; uint8 rsvd[11]; uint8 cksum;} _PACKED;struct pir_table { struct pir_header hdr; struct pir_slot slot[1];} _PACKED;#define PIR_DEVICE(devfunc) (((devfunc) >> 3) & 0x1f)#define PIR_FUNCTION(devfunc) ((devfunc) & 0x07)/* find_pir_table * No real magic, just scan through the memory until we find it, * or not! * * When we check the size of the table, we need to satisfy that * size >= size of a pir_table with no pir_slots * size <= size of a pir_table with 32 pir_slots * If we find a table, and it's a suitable size we'll check that the * checksum is OK. This is done by simply adding up all the bytes and * checking that the final value == 0. If it is we return the pointer to the table. * * Returns NULL if we fail to find a suitable table. */static struct pir_table *find_pir_table(void){ uint32 mem_addr = (uint32)pci_bios_ptr; int range = 0x10000; for (; range > 0; range -= 16, mem_addr += 16) { if (memcmp((void*)mem_addr, PIR_HEADER, 4) == 0) { uint16 size = ((struct pir_header*)mem_addr)->tbl_sz; if ((size >= (sizeof(struct pir_header))) && (size <= (sizeof(struct pir_header) + sizeof(struct pir_slot) * 32))) { uint16 i; uint8 cksum = 0; for (i = 0; i < size; i++) cksum += ((uint8*)mem_addr)[i]; if (cksum == 0) return (struct pir_table *)mem_addr; } } } return NULL;}#if TRACE_PCI/* print_pir_table * Print out the table to debug output */static voidprint_pir_table(struct pir_table *tbl){ int i, j; int entries = (tbl->hdr.tbl_sz - sizeof(struct pir_header)) / sizeof(struct pir_slot); for (i = 0; i < entries; i++) { dprintf("PIR slot %d: bus %d, device %d\n", tbl->slot[i].slot, tbl->slot[i].bus, PIR_DEVICE(tbl->slot[i].devfunc)); for (j = 0; j < 4; j++) dprintf("\tINT%c: pin %d possible_irq's %04x\n", 'A'+j, tbl->slot[i].linkmap[j].pin, tbl->slot[i].linkmap[j].possible_irq); } dprintf("*** end of table\n");}#endif/* Scanning for Devices * ==================== * * http://www.tldp.org/LDP/tlk/dd/pci.html * * Although it refers to Linux it gives a good overview of the basic * methodology they use for scanning PCI. We've adopted a similar * approach here, using the "depthwise" algorithm. * * We start by scanning a bus, which treats every device it finds the * same and passes them onto pci_probe_device(). * pci_probe_device() sends pci_bridges to pci_bridge() to be setup * and deals with type 0 (generic) devices itself. * * NB presently we don't really cope with type 2 (Cardbus) devices * but will need to eventually. *//** The values passed in specify the "device" on the bus being searched * that has been identified as a PCI-PCI bridge. We now setup that bridge * and scan the bus it defines. * The bus is initially taken off-line, scanned and then put back on-line * * NB We increment the pci_max_bus value before we use mybus in the following * code and pci_max_bus is incremented before we recurse to preserve the * correct relationships with nubering. * * We initally set the subordinate_bus to 0xff and then adjust it to the max * once we've scanned the bus on the other side of the bridge. See the URL * above for information on why this is done. */static voidpci_bridge(uint8 bus, uint8 dev, uint8 func){ uint16 command; uint8 mybus; struct pci_device *pcid; struct pci_bus *pcib; pci_info *pcii; TRACE(("pci_bridge()\n")); command = read_pci_config(bus, dev, func, PCI_command, 2); command &= ~ 0x03; write_pci_config(bus, dev, func, PCI_command, 2, command); pci_max_bus += 1; mybus = pci_max_bus; write_pci_config(bus, dev, func, PCI_primary_bus, 1, bus); write_pci_config(bus, dev, func, PCI_secondary_bus, 1, mybus); write_pci_config(bus, dev, func, PCI_subordinate_bus, 1, 0xff); dprintf("PCI-PCI bridge at %d:%d:%d configured as bus %d\n", bus, dev, func, mybus); pci_scan_bus(mybus); write_pci_config(bus, dev, func, PCI_subordinate_bus, 1, pci_max_bus); pcii = (pci_info *)kmalloc(sizeof(pci_info)); if (!pcii) goto pci_bridge_skip_infolist; pcid = (struct pci_device *)kmalloc(sizeof(struct pci_device)); if (!pcid) { kfree(pcii); goto pci_bridge_skip_infolist; } pcib = (struct pci_bus *)kmalloc(sizeof(struct pci_bus)); if (!pcib) { kfree(pcii); kfree(pcid); goto pci_bridge_skip_infolist; } pcid->info = pcii; pcid->type = PCI_BRIDGE; pcid->next = NULL; pcib->info = pcii; pcib->next = NULL; pcii->bus = bus; pcii->device = dev; pcii->function = func; fill_basic_pci_structure(pcii); pcii->u.h1.rom_base_pci = read_pci_config(bus, dev, func, PCI_bridge_rom_base, 4); pcii->u.h1.primary_bus = read_pci_config(bus, dev, func, PCI_primary_bus, 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -