📄 pci.c
字号:
/*** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.** Distributed under the terms of the NewOS License.*/#include <kernel/kernel.h>#include <kernel/debug.h>#include <kernel/lock.h>#include <kernel/smp.h>#include <kernel/int.h>#include <kernel/heap.h>#include <kernel/thread.h>#include <kernel/module.h>#include <kernel/vm.h>#include <newos/errors.h>#include <string.h>#include <kernel/bus/pci/pci.h>//#include "pcihdr.h"#define TRACE_PCI 1#if TRACE_PCI# define TRACE(x) dprintf x#else# define TRACE(x) ;#endifstruct pci_device { struct pci_device *next; int type; pci_info *info;};struct pci_bus { struct pci_bus *next; pci_info *info;};enum { PCI_DEVICE = 0, PCI_HOST_BUS, PCI_BRIDGE, PCI_CARDBUS};static struct pci_device *pci_devices = NULL;static struct pci_bus *pci_busses = NULL;static spinlock_t pci_config_lock = 0; /* lock for config space access */static int pci_mode = 1; /* The pci config mechanism we're using. * NB defaults to 1 as this is more common, but * checked at runtime */static int bus_max_devices = 32; /* max devices that any bus can support * Yes, if we're using pci_mode == 2 then * this is only 16, instead of the 32 we * have with pci_mode == 1 */static int pci_max_bus = 0; /* maximum bus we've found/configured */static region_id pci_region; /* pci_bios region we map */static void * pci_bios_ptr = NULL; /* virtual address of memory we map */static void pci_scan_bus(uint8 bus);static void pci_bridge(uint8 bus, uint8 dev, uint8 func);static void fill_basic_pci_structure(pci_info *pcii);static uint32 read_pci_config(uchar, uchar, uchar, uchar, uchar);static void write_pci_config (uchar, uchar, uchar, uchar, uchar, uint32);/* XXX - move these to a header file */#define PCI_VENDOR_INTEL 0x8086#define PCI_PRODUCT_INTEL_82371AB_ISA 0x7110 /* PIIX4 ISA */#define PCI_PRODUCT_INTEL_82371AB_IDE 0x7111 /* PIIX4 IDE */#define PCI_PRODUCT_INTEL_82371AB_USB 0x7112 /* PIIX4 USB */#define PCI_PRODUCT_INTEL_82371AB_PMC 0x7113 /* PIIX4 Power Management */#define PCI_PRODUCT_INTEL_82443BX 0x7190#define PCI_PRODUCT_INTEL_82443BX_AGP 0x7191#define PCI_PRODUCT_INTEL_82443BX_NOAGP 0x7192#define PCI_PRODUCT_INTEL_82845_AGP 0x1a31/* Config space locking! * We need to make sure we only have one access at a time into the config space, * so we'll use a spinlock and also disbale interrupts so if we're smp we * won't have problems. */#define PCI_LOCK_CONFIG() \{ \ int_disable_interrupts(); \ acquire_spinlock(&pci_config_lock); \}#define PCI_UNLOCK_CONFIG() \{ \ release_spinlock(&pci_config_lock); \ int_restore_interrupts(); \}/* decode_class * DEBUG DEBUG DEBUG * Provide a string that describes the class and sub-class. * This could/should (?) be expanded to use the api as well. */static char *decode_class(uint8 base, uint8 sub_class){ switch(base) { case 0: switch (sub_class) { case 0: return "legacy (non VGA)"; case 1: return "legacy VGA"; } case 0x01: switch (sub_class) { case 0: return "mass storage: scsi"; case 1: return "mass storage: ide"; case 2: return "mass storage: floppy"; case 3: return "mass storage: ipi"; case 4: return "mass storage: raid"; case 5: return "mass storage: ata"; case 6: return "mass storage: sata"; case 0x80: return "mass storage: other"; } case 0x02: switch (sub_class) { case 0: return "network: ethernet"; case 1: return "network: token ring"; case 2: return "network: fddi"; case 3: return "network: atm"; case 4: return "network: isdn"; case 0x80: return "network: other"; } case 0x03: switch (sub_class) { case 0: return "display: vga"; case 1: return "display: xga"; case 2: return "display: 3d"; case 0x80: return "display: other"; } case 0x04: switch (sub_class) { case 0: return "multimedia device: video"; case 1: return "multimedia device: audio"; case 2: return "multimedia device: telephony"; case 0x80: return "multimedia device: other"; } case 0x05: switch (sub_class) { case 0: return "memory: ram"; case 1: return "memory: flash"; case 0x80: return "memory: other"; } case 0x06: switch (sub_class) { case 0: return "bridge: host bridge"; case 1: return "bridge: isa"; case 2: return "bridge: eisa"; case 3: return "bridge: microchannel"; case 4: return "bridge: PCI"; case 5: return "bridge: PC Card"; case 6: return "bridge: nubus"; case 7: return "bridge: CardBus"; case 8: return "bridge: raceway"; case 9: return "bridge: stpci"; case 10: return "bridge: infiniband"; case 0x80: return "bridge: other"; } case 0x07: return "simple comms controller"; case 0x08: return "base system peripheral"; case 0x09: return "input device"; case 0x0a: return "docking station"; case 0x0b: return "processor"; case 0x0c: switch (sub_class) { case 0: return "bus: IEEE1394 FireWire"; case 1: return "bus: ACCESS.bus"; case 2: return "bus: SSA"; case 3: return "bus: USB"; case 4: return "bus: fibre channel"; } case 0x0d: return "wireless"; case 0x0e: return "intelligent i/o ??"; case 0x0f: return "satellite"; case 0x10: return "encryption"; case 0x11: return "signal processing"; default: return "unknown"; }}static voidshow_pci_details(struct pci_info *p){#if TRACE_PCI uint16 ss_vend, ss_dev;#endif uint8 irq = 0; if (p->header_type == PCI_header_type_generic) irq = p->u.h0.interrupt_line; else irq = p->u.h1.interrupt_line; dprintf("0x%04x:0x%04x : ", p->vendor_id, p->device_id); if (irq > 0) dprintf("irq %d : ", irq); dprintf("location %d:%d:%d : ", p->bus, p->device, p->function); dprintf("class %02x:%02x:%02x", p->class_base, p->class_sub, p->class_api); dprintf(" => %s\n", decode_class(p->class_base, p->class_sub));#if TRACE_PCI dprintf("\trevision : %02x\n", p->revision); dprintf("\tstatus : %04x\n", read_pci_config(p->bus, p->device, p->function, PCI_status, 2)); dprintf("\tcommand : %04x\n", read_pci_config(p->bus, p->device, p->function, PCI_command, 2)); dprintf("\tbase address: %08x\n", read_pci_config(p->bus, p->device, p->function, PCI_base_registers, 4)); dprintf("\tline_size : %02x\n", p->line_size); dprintf("\theader_type : %02x\n", p->header_type); if (p->header_type == PCI_header_type_generic) { dprintf("Header Type 0 (Generic)\n"); dprintf("\tcardbus_cis : %08lx\n", p->u.h0.cardbus_cis); dprintf("\trom_base_pci : %08lx\n", p->u.h0.rom_base_pci); dprintf("\tinterrupt_line : %02x\n", p->u.h0.interrupt_line); dprintf("\tinterrupt_pin : %02x\n", p->u.h0.interrupt_pin); ss_vend = p->u.h0.subsystem_vendor_id; ss_dev = p->u.h0.subsystem_id; } else if (p->header_type == PCI_header_type_PCI_to_PCI_bridge) { dprintf("Header Type 1 (PCI-PCI bridge)\n"); dprintf("\trom_base_pci : %08lx\n", p->u.h1.rom_base_pci); dprintf("\tinterrupt_line : %02x\n", p->u.h1.interrupt_line); dprintf("\tinterrupt_pin : %02x\n", p->u.h1.interrupt_pin); dprintf("\t2ndry status : %04x\n", p->u.h1.secondary_status); dprintf("\tprimary_bus : %d\n", p->u.h1.primary_bus); dprintf("\tsecondary_bus : %d\n", p->u.h1.secondary_bus); dprintf("\tsubordinate_bus : %d\n", p->u.h1.subordinate_bus); ss_vend = p->u.h1.subsystem_vendor_id; ss_dev = p->u.h1.subsystem_id; } else { ss_vend = 0; ss_dev = 0; } dprintf("\tsubsystem_id : %04x\n", ss_dev); dprintf("\tsubsystem_vendor_id : %04x\n", ss_vend);#endif}/* PCI has 2 Configuration Mechanisms. We need to decide which one the * PCI Host Bridge is speaking and then speak to it correctly. This is decided * in set_pci_mechanism() where the pci_mode value is set to the appropriate * value and the bus_max_devices value is set correctly. * * Mechanism 1 * =========== * Mechanism 1 is the more common one found on modern computers, so presently * that's the one that has been tested and added. * * This has 2 ranges, one for addressing and for data. Basically we write in the details of * the configuration information we want (bus, device and function) and then either * read or write from the data port. * * Apparently most modern hardware no longer has Configuration Type #2. * * XXX - add code for Mechanism Two * * */#define CONFIG_REQ_PORT 0xCF8#define CONFIG_DATA_PORT 0xCFC#define CONFIG_ADDR_1(bus, device, func, reg) \ (0x80000000 | (bus << 16) | (device << 11) | (func << 8) | (reg & ~3))#define CONFIG_ADDR_2(dev, reg) \ (uint16)(0xC00 | (dev << 8) | reg)static uint32read_pci_config(uchar bus, uchar device, uchar function, uchar reg, uchar size){ uint32 val = 0; PCI_LOCK_CONFIG(); if (pci_mode == 1) { /* write request details */ out32(CONFIG_ADDR_1(bus, device, function, reg), CONFIG_REQ_PORT); /* Now read data back from the data port... * offset for 1 byte can be 1,2 or 3 * offset for 2 bytes can be 1 or 2 */ switch (size) { case 1: val = in8 (CONFIG_DATA_PORT + (reg & 3)); break; case 2: if ((reg & 3) != 3) val = in16(CONFIG_DATA_PORT + (reg & 3)); else { val = ERR_INVALID_ARGS; dprintf("ERROR: read_pci_config: can't read 2 bytes at reg %d (offset 3)!\n",reg); } break; case 4: if ((reg & 3) == 0) val = in32(CONFIG_DATA_PORT); else { val = ERR_INVALID_ARGS; dprintf("ERROR: read_pci_config: can't read 4 bytes at reg %d (offset != 0)!\n",reg); } break; default: dprintf("ERROR: mech #1: read_pci_config: called for %d bytes!!\n", size); } } else if (pci_mode == 2) { if (!(device & 0x10)) { out8((uint8)(0xF0 | (function << 1)), 0xCF8); out8(bus, 0xCFA); switch (size) { case 1: val = in8 (CONFIG_ADDR_2(device, reg)); break; case 2: val = in16(CONFIG_ADDR_2(device, reg)); break; case 4: val = in32(CONFIG_ADDR_2(device, reg)); break; default: dprintf("ERROR: mech #2: read_pci_config: called for %d bytes!!\n", size); } out8(0, 0xCF8); } else val = ERR_INVALID_ARGS; } else dprintf("PCI: Config Mechanism %d isn't known!\n", pci_mode); PCI_UNLOCK_CONFIG(); return val;}static voidwrite_pci_config(uchar bus, uchar device, uchar function, uchar reg, uchar size, uint32 value){ PCI_LOCK_CONFIG(); if (pci_mode == 1) { /* write request details */ out32(CONFIG_ADDR_1(bus, device, function, reg), CONFIG_REQ_PORT); /* Now read data back from the data port... * offset for 1 byte can be 1,2 or 3 * offset for 2 bytes can be 1 or 2 */ switch (size) { case 1: out8 (value, CONFIG_DATA_PORT + (reg & 3)); break; case 2: if ((reg & 3) != 3) out16(value, CONFIG_DATA_PORT + (reg & 3)); else dprintf("ERROR: write_pci_config: can't write 2 bytes at reg %d (offset 3)!\n",reg); break; case 4: if ((reg & 3) == 0) out32(value, CONFIG_DATA_PORT); else dprintf("ERROR: write_pci_config: can't write 4 bytes at reg %d (offset != 0)!\n",reg); break; default: dprintf("ERROR: write_pci_config: called for %d bytes!!\n", size); } } else if (pci_mode == 2) { if (!(device & 0x10)) { out8((uint8)(0xF0 | (function << 1)), 0xCF8); out8(bus, 0xCFA); switch (size) { case 1: out8 (value, CONFIG_ADDR_2(device, reg)); break; case 2: out16(value, CONFIG_ADDR_2(device, reg)); break; case 4: out32(value, CONFIG_ADDR_2(device, reg)); break; default: dprintf("ERROR: write_pci_config: called for %d bytes!!\n", size); } out8(0, 0xCF8); } } else dprintf("PCI: Config Mechanism %d isn't known!\n", pci_mode); PCI_UNLOCK_CONFIG(); return;}/* pci_get_capability * Try to get the offset and value of the specified capability from the * devices capability list. * returns 0 if unable, 1 if succesful */static intpci_get_capability(uint8 bus, uint8 dev, uint8 func, uint8 cap, uint8 *offs){ uint16 status; uint8 cap_data; uint8 hdr_type; uint8 ofs; int maxcount; status = read_pci_config(bus, dev, func, PCI_status, 2); if (!(status & PCI_status_capabilities)) return 0; hdr_type = read_pci_config(bus, dev, func, PCI_header_type, 1); switch (hdr_type & 0x7f) { /* mask off multi function device indicator bit */ case PCI_header_type_generic: ofs = PCI_capabilities_ptr; break; case PCI_header_type_PCI_to_PCI_bridge:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -