diff --git a/src/kernel/core/addons/bus_managers/config_manager.c b/src/kernel/core/addons/bus_managers/config_manager.c index 15aff78e9c..a5dea62a64 100644 --- a/src/kernel/core/addons/bus_managers/config_manager.c +++ b/src/kernel/core/addons/bus_managers/config_manager.c @@ -11,6 +11,7 @@ #include #include #include +#include /* This is normally turned off. Turning it on (1) will provide a more * verbose description of the PCI devices found, though most of the extra @@ -18,6 +19,11 @@ */ #define THE_FULL_MONTY 0 +static pci_module_info *pcim = NULL; + +static void pci_scan_bus(uint8 bus); +static void pci_bridge(uint8 bus, uint8 dev, uint8 func); + #define B_CONFIG_MANAGER_FOR_BUS_MODULE_NAME "bus_managers/config_manager/bus/v1" static status_t cfdm_get_next_device_info(bus_type bus, uint64 *cookie, @@ -28,22 +34,102 @@ static status_t cfdm_get_next_device_info(bus_type bus, uint64 *cookie, return 0; } -static char *decode_class_base(uint8 base) +static char *decode_class(uint8 base, uint8 sub_class) { switch(base) { - case 0x00: return "legacy"; - case 0x01: return "mass storage controller"; - case 0x02: return "network controller"; - case 0x03: return "display controller"; - case 0x04: return "multimedia device"; + case PCI_early: + switch (sub_class) { + case PCI_early_not_vga: + return "legacy (non VGA)"; + case PCI_early_vga: + return "legacy VGA"; + } + case 0x01: + switch (sub_class) { + case PCI_scsi: + return "mass storage controller: scsi"; + case PCI_ide: + return "mass storage controller: ide"; + case PCI_floppy: + return "mass storage controller: floppy"; + case PCI_ipi: + return "mass storage controller: ipi"; + case PCI_raid: + return "mass storage controller: raid"; + case PCI_mass_storage_other: + return "mass storage controller: other"; + } + case 0x02: + switch (sub_class) { + case PCI_ethernet: + return "network controller: ethernet"; + case PCI_token_ring: + return "network controller: token ring"; + case PCI_fddi: + return "network controller: fddi"; + case PCI_atm: + return "network controller: atm"; + case PCI_network_other: + return "network controller: other"; + } + case 0x03: + switch (sub_class) { + case PCI_vga: + return "display controller: vga"; + case PCI_xga: + return "display controller: xga"; + case PCI_display_other: + return "display controller: other"; + } + case 0x04: + switch (sub_class) { + case PCI_video: + return "multimedia device: video"; + case PCI_audio: + return "multimedia device: audio"; + case PCI_multimedia_other: + return "multimedia device: other"; + } case 0x05: return "memory controller"; - case 0x06: return "bridge controller"; + case 0x06: + switch (sub_class) { + case PCI_host: + return "bridge controller: host bridge"; + case PCI_isa: + return "bridge controller: isa"; + case PCI_eisa: + return "bridge controller: eisa"; + case PCI_microchannel: + return "bridge controller: microchannel"; + case PCI_pci: + return "bridge controller: PCI"; + case PCI_pcmcia: + return "bridge controller: PC Card"; + case PCI_nubus: + return "bridge controller: nubus"; + case PCI_cardbus: + return "bridge controller: CardBus"; + case PCI_bridge_other: + return "bridge controller: 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: return "serial bus controller"; + case 0x0c: + switch (sub_class) { + case PCI_firewire: + return "bus controller: IEEE1394 FireWire"; + case PCI_access: + return "bus controller: ACCESS.bus"; + case PCI_ssa: + return "bus controller: SSA"; + case PCI_usb: + return "bus controller: USB"; + case PCI_fibre_channel: + return "bus controller: fibre channel"; + } case 0x0d: return "wireless"; case 0x0e: return "intelligent i/o ??"; case 0x0f: return "satellite"; @@ -53,108 +139,237 @@ static char *decode_class_base(uint8 base) } } -/* XXX - these are only listed as they're the ones that my system has! - * I'm committing mainly so I don't have to keep adjusting this file each - * time I commit a change. I'm NOT suggesting others add their codes here. - * The level of detail offered is useful if you're working on this, otherwise - * it's just nice to have. Adding all the data we need will add over 270k to - * the build! - */ -static char *decode_vendor(uint16 vendor) -{ - switch(vendor) { - case 0x102b: return "Matrox"; - case 0x10b7: return "3Com"; - case 0x11ad: return "Lite-On"; - case 0x1385: return "NetGear"; - case 0x8086: return "Intel"; - default: return "unknown"; - } -} - -static char *decode_device(uint16 dev) -{ - switch(dev) { - case 0x0002: return "NGMC169B, 10/100 Ethernet (NetGear FA310TX)"; - case 0x051a: return "MGA 1064SG, Hurricane/Cyclone 64-bit graphics chip"; - case 0x7110: return "82371AB/EB/MB, PIIX4/4E/4M ISA Bridge"; - case 0x7111: return "82371AB/EB/MB, PIIX4/4E/4M IDE Controller"; - case 0x7112: return "82371AB/EB/MB, PIIX4/4E/4M USB Interface"; - case 0x7113: return "82371AB/EB/MB, PIIX4/4E/4M Power Management Controller"; - case 0x7190: return "82443BX/ZX, 440BX/ZX AGPset Host Bridge"; - case 0x7191: return "82443BX/ZX, 440BX/ZX AGPset PCI-to-PCI bridge"; - case 0x7192: return "82443BX/ZX, 440BX/ZX chipset Host-to-PCI Bridge"; - case 0x9055: return "3C905B-TX, Fast Etherlink 10/100 PCI TX NIC"; - default: return "unknown"; - } -} - static void show_pci_details(struct pci_info *p, pci_module_info *pcim) { - dprintf("PCI device found:\n"); - dprintf("\tvendor id : %02x [%s]\n", p->vendor_id, decode_vendor(p->vendor_id)); - dprintf("\tdevice id : %02x [%s]\n", p->device_id, decode_device(p->device_id)); - -#if THE_FULL_MONTY - dprintf("\tbus : %d\n", p->bus); - dprintf("\tdevice : %d\n", p->device); - dprintf("\tfunction : %d\n", p->function); - dprintf("\trevision : %02x\n", p->revision); - dprintf("\tclass_api : %02x\n", p->class_api); - dprintf("\tclass_sub : %02x\n", p->class_sub); +#ifdef THE_FULL_MONTY + uint16 ss_vend, ss_dev; #endif - dprintf("\tclass_base : %02x [%s]\n", p->class_base, decode_class_base(p->class_base)); + 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 THE_FULL_MONTY + dprintf("\trevision : %02x\n", p->revision); dprintf("\tstatus : %04x\n", pcim->read_pci_config(p->bus, p->device, p->function, PCI_status, 2)); dprintf("\tcommand : %04x\n", pcim->read_pci_config(p->bus, p->device, p->function, PCI_command, 2)); - dprintf("\tbase address: %08lx\n", + dprintf("\tbase address: %08x\n", pcim->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); dprintf("\tbist : %02x\n", p->bist); - if (p->header_type == 0) { - dprintf("Header Type 0\n"); + 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("\tsubsystem_id : %04x\n", p->u.h0.subsystem_id); - dprintf("\tsubsystem_vendor_id : %04x [%s]\n", p->u.h0.subsystem_vendor_id, - decode_vendor(p->u.h0.subsystem_vendor_id)); 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); - - } else if (p->header_type == 1) { + 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("\tmemory_base : %04x\n", p->u.h1.memory_base); + dprintf("\tprimary_bus : %d\n", p->u.h1.primary_bus); + dprintf("\tsecondary_bus : %d\n", p->u.h1.secondary_bus); dprintf("\tbridge control : %02x\n", p->u.h1.bridge_control); + ss_vend = p->u.h1.subsystem_vendor_id; + ss_dev = p->u.h1.subsystem_id; } + dprintf("\tsubsystem_id : %04x\n", ss_dev); + dprintf("\tsubsystem_vendor_id : %04x\n", ss_vend); #endif } + +/* 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 + + +/* Borrowed from NetBSD. + * Some Host bridges need to have fixes applied. + */ +static void fixup_host_bridge(uint8 bus, uint8 dev, uint8 func) +{ + uint16 vendor, device; + + vendor = pcim->read_pci_config(bus, dev, func, PCI_vendor_id, 2); + device = pcim->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 = pcim->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; + pcim->write_pci_config(bus, dev, func, 0x76, 2, bcreg); + } + break; + } + } + } +} + +/* 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 int pci_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; +} + +/* pci_bridge() + * 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 + */ +static void pci_bridge(uint8 bus, uint8 dev, uint8 func) +{ + uint16 command = 0; + uint8 mybus = bus + 1; + + command = pcim->read_pci_config(bus, dev, func, PCI_command, 2); + command &= ~ 0x03; + pcim->write_pci_config(bus, dev, func, PCI_command, 2, command); + + /* Bus is now off line */ + + pcim->write_pci_config(bus, dev, func, PCI_primary_bus, 1, bus); + pcim->write_pci_config(bus, dev, func, PCI_secondary_bus, 1, mybus); + pcim->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); + + /* Not strictly correct, but close enough... */ + command |= 0x03; + pcim->write_pci_config(bus, dev, func, PCI_command, 2, command); +} + +static void pci_device_probe(uint8 bus, uint8 dev, uint8 func) +{ + uint8 base_class, sub_class; + uint8 type; + + if (func > 0) { + uint16 vend = pcim->read_pci_config(bus, dev, func, PCI_vendor_id, 2); + if (vend == 0xffff) + return; + } + + type = pcim->read_pci_config(bus, dev, func, PCI_header_type, 1); + type &= PCI_header_type_mask; + base_class = pcim->read_pci_config(bus, dev, func, PCI_class_base, 1); + sub_class = pcim->read_pci_config(bus, dev, func, PCI_class_sub, 1); + + if (base_class == PCI_bridge) { + if (sub_class == PCI_host) + fixup_host_bridge(bus, dev, func); + if (sub_class == PCI_pci) { + pci_bridge(bus, dev, func); + return; + } + } + + dprintf("device at %d:%d:%d => %s\n", bus, dev, func, + decode_class(base_class, sub_class)); +} + +/* pci_bus() + * Scan a bus for PCI devices. For each device that's a possible + * we get the vendor_id. If it's 0xffff then it's not a valid + * device so we move on. + * Valid devices then have their header_type checked to detrmine how + * many functions we need to check. However, some devices that support + * multiple functions have a header_type of 0 (generic) so we also check + * for these using pci_quirk_multifunction(). + * If it's a multifunction device we scan all 8 possible functions, + * otherwise we just probe the first one. + */ +static void pci_scan_bus(uint8 bus) +{ + uint8 dev = 0, func = 0; + uint16 vend = 0; + + for (dev = 0; dev < 32; dev++) { + vend = pcim->read_pci_config(bus, dev, 0, PCI_vendor_id, 2); + if (vend != 0xffff) { + uint16 device = pcim->read_pci_config(bus, dev, func, PCI_device_id, 2); + uint8 type = pcim->read_pci_config(bus, dev, func, PCI_header_type, 1); + uint8 nfunc = 8; + + type &= PCI_header_type_mask; + if ((type & PCI_multifunction) == 0 && + !pci_quirk_multifunction(vend, device)) + nfunc = 1; + for (func = 0; func < nfunc; func++) + pci_device_probe(bus, dev, func); + } + } +} + static status_t test_me(void) { - pci_module_info *pcim; pci_info apci; long index = 0; - if (get_module(B_PCI_MODULE_NAME, (module_info**)&pcim) != 0) { - dprintf("config_manager: test_me: failed to load PCI module\n"); - return -1; - } - while (pcim->get_nth_pci_info(index++, &apci) == 0) { show_pci_details(&apci, pcim); } - put_module(B_PCI_MODULE_NAME); return 0; } @@ -164,6 +379,11 @@ static int cfdm_std_ops(int32 op, ...) switch(op) { case B_MODULE_INIT: dprintf( "config_manager: device modules: init\n" ); + if (get_module(B_PCI_MODULE_NAME, (module_info**)&pcim) != 0) { + dprintf("config_manager: failed to load PCI module\n"); + return -1; + } + pci_scan_bus(0); test_me(); break; case B_MODULE_UNINIT: diff --git a/src/kernel/core/addons/bus_managers/pci/pci.c b/src/kernel/core/addons/bus_managers/pci/pci.c index 479a48a2d6..10551bb2de 100755 --- a/src/kernel/core/addons/bus_managers/pci/pci.c +++ b/src/kernel/core/addons/bus_managers/pci/pci.c @@ -74,59 +74,105 @@ struct pci_config *pci_list; #define CONFIG_REQ_PORT 0xCF8 #define CONFIG_DATA_PORT 0xCFC -#define CONFIG_REQ(bus, device, func, offs) (0x80000000 | (bus << 16) | \ - (((device << 3) | (func & 0x07)) << 8) | \ - (offs & ~3)) -static uint32 read_pci_config(uchar bus, uchar device, uchar function, uchar offset, uchar size) +#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 uint32 read_pci_config(uchar bus, uchar device, uchar function, uchar reg, uchar size) { if (pci_mode == 1) { /* write request details */ - out32(CONFIG_REQ(bus, device, function, offset), 0xCF8); + 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: - return in8 (CONFIG_DATA_PORT + (offset & 3)); + return in8 (CONFIG_DATA_PORT + (reg & 3)); case 2: - return in16(CONFIG_DATA_PORT + (offset & 2)); + return in16(CONFIG_DATA_PORT + (reg & 2)); case 4: - return in32(CONFIG_DATA_PORT + offset); + return in32(CONFIG_DATA_PORT + reg); default: - dprintf("read_pci_config: called for %d bytes!!\n", size); + dprintf("ERROR: read_pci_config: called for %d bytes!!\n", size); } } else if (pci_mode == 2) { - dprintf("PCI: Config Mechanism 2 not yet supported!\n"); + uint32 rv = 0; + if (device & 0x10) + return EINVAL; + + out8((uint8)(0xF0 | (function << 1)), 0xCF8); + out8(bus, 0xCFA); + + switch (size) { + case 1: + rv = in8 (CONFIG_ADDR_2(device, reg)); + break; + case 2: + rv = in16(CONFIG_ADDR_2(device, reg)); + break; + case 4: + rv = in32(CONFIG_ADDR_2(device, reg)); + break; + default: + dprintf("ERROR: read_pci_config: called for %d bytes!!\n", size); + } + out8(0, 0xCF8); + return rv; } else dprintf("PCI: Config Mechanism %d isn't known!\n", pci_mode); return 0; } -static void write_pci_config(uchar bus, uchar device, uchar function, uchar offset, - uchar size, uint32 value) +static void write_pci_config(uchar bus, uchar device, uchar function, uchar reg, + uchar size, uint32 value) { if (pci_mode == 1) { /* write request details */ - out32(CONFIG_REQ(bus, device, function, offset), 0xCF8); + 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 + (offset & 3)); + out8 (value, CONFIG_DATA_PORT + (reg & 3)); + break; case 2: - out16(value, CONFIG_DATA_PORT + (offset & 2)); + out16(value, CONFIG_DATA_PORT + (reg & 2)); + break; case 4: - out32(value, CONFIG_DATA_PORT + offset); + out32(value, CONFIG_DATA_PORT + reg); + break; default: - dprintf("read_pci_config: called for %d bytes!!\n", size); + dprintf("ERROR: write_pci_config: called for %d bytes!!\n", size); } } else if (pci_mode == 2) { - dprintf("PCI: Config Mechanism 2 not yet supported\n"); + if (device & 0x10) + return; + + 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); @@ -221,7 +267,6 @@ static int check_pci(void) return -1; } - static void scan_pci(void) { int bus, dev, func; @@ -238,7 +283,6 @@ static void scan_pci(void) struct found_pci_device *npcid = NULL; uint16 vendor_id = read_pci_config(bus, dev, func, 0, 2); uint16 device_id; - uint8 int_line = 0, int_pin = 0; /* If we get 0xffff then there is no device here. As there can't * be any gaps in function allocation this tells us that we * can move onto the next device/bus @@ -276,61 +320,76 @@ static void scan_pci(void) } /* basic header */ - pcii->vendor_id = vendor_id; - pcii->device_id = device_id; pcii->bus = bus; pcii->device = dev; pcii->function = func; + pcii->vendor_id = vendor_id; + pcii->device_id = device_id; + pcii->revision = read_pci_config(bus, dev, func, PCI_revision, 1); pcii->class_api = read_pci_config(bus, dev, func, PCI_class_api, 1); pcii->class_sub = read_pci_config(bus, dev, func, PCI_class_sub, 1); pcii->class_base = read_pci_config(bus, dev, func, PCI_class_base, 1); - pcii->line_size = read_pci_config(bus, dev, func, PCI_line_size, 1); - pcii->latency = read_pci_config(bus, dev, func, PCI_latency, 1); - pcii->header_type = read_pci_config(bus, dev, func, PCI_header_type, 1); - pcii->bist = read_pci_config(bus, dev, func, PCI_bist, 1); - - int_line = read_pci_config(bus, dev, func, PCI_interrupt_line, 1); - int_pin = read_pci_config(bus, dev, func, PCI_interrupt_pin, 1); - - /* device type specific headers based on header_type declared */ - if (pcii->header_type == 0) { - /* header type 0 */ - pcii->u.h0.cardbus_cis = read_pci_config(bus, dev, func, PCI_cardbus_cis, 4); - pcii->u.h0.subsystem_id = read_pci_config(bus, dev, func, PCI_subsystem_id, 2); - pcii->u.h0.subsystem_vendor_id = read_pci_config(bus, dev, func, PCI_subsystem_vendor_id, 2); - pcii->u.h0.rom_base_pci = read_pci_config(bus, dev, func, PCI_rom_base, 4); - pcii->u.h0.interrupt_line = int_line; - pcii->u.h0.interrupt_pin = int_pin; - } else if (pcii->header_type == 1) { - /* header_type 1 */ - /* PCI-PCI bridge - may be used for AGP */ - 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); - pcii->u.h1.secondary_bus = read_pci_config(bus, dev, func, PCI_secondary_bus, 1); - pcii->u.h1.secondary_latency = read_pci_config(bus, dev, func, PCI_secondary_latency, 1); - pcii->u.h1.secondary_status = read_pci_config(bus, dev, func, PCI_secondary_status, 2); - pcii->u.h1.subordinate_bus = read_pci_config(bus, dev, func, PCI_subordinate_bus, 1); - pcii->u.h1.io_base = read_pci_config(bus, dev, func, PCI_io_base, 1); - pcii->u.h1.io_limit = read_pci_config(bus, dev, func, PCI_io_limit, 1); - pcii->u.h1.memory_base = read_pci_config(bus, dev, func, PCI_memory_base, 2); - pcii->u.h1.memory_limit = read_pci_config(bus, dev, func, PCI_memory_limit, 2); - pcii->u.h1.prefetchable_memory_base = read_pci_config(bus, dev, func, PCI_prefetchable_memory_base, 2); - pcii->u.h1.prefetchable_memory_limit = read_pci_config(bus, dev, func, PCI_prefetchable_memory_limit, 2); - pcii->u.h1.bridge_control = read_pci_config(bus, dev, func, PCI_bridge_control, 1); - pcii->u.h1.interrupt_line = int_line; - pcii->u.h1.interrupt_pin = int_pin; - } else if (pcii->header_type == 0x80) { - /* ??? */ - } + + pcii->header_type = read_pci_config(bus, dev, func, PCI_header_type, 1); + pcii->header_type &= PCI_header_type_mask; npcid->info = pcii; /* Add the device to the list */ insque(npcid, &pci_dev_list); } - /* next device */ } - /* next bus */ + } +} + +static void fill_pci_structure(pci_info *pcii) +{ + uint8 bus = pcii->bus, dev = pcii->device, func = pcii->function; + uint8 int_line = 0, int_pin = 0; + + pcii->latency = read_pci_config(bus, dev, func, PCI_latency, 1); + pcii->bist = read_pci_config(bus, dev, func, PCI_bist, 1); + pcii->line_size = read_pci_config(bus, dev, func, PCI_line_size, 1); + + int_line = read_pci_config(bus, dev, func, PCI_interrupt_line, 1); + int_pin = read_pci_config(bus, dev, func, PCI_interrupt_pin, 1); + int_pin &= PCI_pin_mask; + + /* device type specific headers based on header_type declared */ + if (pcii->header_type == PCI_header_type_generic) { + /* header type 0 */ + pcii->u.h0.cardbus_cis = read_pci_config(bus, dev, func, PCI_cardbus_cis, 4); + pcii->u.h0.subsystem_id = read_pci_config(bus, dev, func, PCI_subsystem_id, 2); + pcii->u.h0.subsystem_vendor_id = read_pci_config(bus, dev, func, PCI_subsystem_vendor_id, 2); + pcii->u.h0.rom_base_pci = read_pci_config(bus, dev, func, PCI_rom_base, 4); + pcii->u.h0.interrupt_line = int_line; + pcii->u.h0.interrupt_pin = int_pin; + } else if (pcii->header_type == PCI_header_type_PCI_to_PCI_bridge) { + /* header_type 1 */ + /* PCI-PCI bridge - may be used for AGP */ + 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); + pcii->u.h1.secondary_bus = read_pci_config(bus, dev, func, PCI_secondary_bus, 1); + pcii->u.h1.secondary_latency = read_pci_config(bus, dev, func, PCI_secondary_latency, 1); + pcii->u.h1.secondary_status = read_pci_config(bus, dev, func, PCI_secondary_status, 2); + pcii->u.h1.subordinate_bus = read_pci_config(bus, dev, func, PCI_subordinate_bus, 1); + pcii->u.h1.memory_base = read_pci_config(bus, dev, func, PCI_memory_base, 2); + pcii->u.h1.memory_limit = read_pci_config(bus, dev, func, PCI_memory_limit, 2); + pcii->u.h1.prefetchable_memory_base = read_pci_config(bus, dev, func, PCI_prefetchable_memory_base, 2); + pcii->u.h1.prefetchable_memory_limit = read_pci_config(bus, dev, func, PCI_prefetchable_memory_limit, 2); + pcii->u.h1.bridge_control = read_pci_config(bus, dev, func, PCI_bridge_control, 1); + pcii->u.h1.subsystem_vendor_id = read_pci_config(bus, dev, func, PCI_sub_vendor_id_1, 2); + pcii->u.h1.subsystem_id = read_pci_config(bus, dev, func, PCI_sub_device_id_1, 2); + pcii->u.h1.interrupt_line = int_line; + pcii->u.h1.interrupt_pin = int_pin; + + } else if (pcii->header_type == PCI_header_type_cardbus) { + pcii->u.h2.subsystem_vendor_id = read_pci_config(bus, dev, func, PCI_sub_vendor_id_2, 2); + pcii->u.h2.subsystem_id = read_pci_config(bus, dev, func, PCI_sub_device_id_2, 2); + + }else if (pcii->header_type == PCI_multifunction) { + dprintf("PCI: multifunction device detected\n"); + /* ??? */ } } @@ -367,6 +426,8 @@ static long get_nth_pci_info(long index, pci_info *copyto) */ if (iter > 0 || !fpd->info) return B_DEV_ID_ERROR; + + fill_pci_structure(fpd->info); memcpy(copyto, fpd->info, sizeof(pci_info)); return 0;