From a0bc23f2886567f7d145b5877e85d96a6611cb22 Mon Sep 17 00:00:00 2001 From: David Reid Date: Thu, 25 Jul 2002 18:11:45 +0000 Subject: [PATCH] I've been trying to figure out how the division of work between config_manager and busses works, and this is a large correction based on my current working thoughts. I also think we need to have a new interface between the bus modules and the config_manager to allow them to work together. I have some ideas on it and will start fleshing it out next week. This update basically moves all PCI stuff back to the PCI module and cleans up config_manager.c. We now print a small amount of info for devices and more for bridges. My system only has a single bridge, but if anyone has a more exotic system and tries this can they let me know what sort of results they see? git-svn-id: file:///srv/svn/repos/haiku/trunk/current@444 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../core/addons/bus_managers/config_manager.c | 350 ---------- src/kernel/core/addons/bus_managers/pci/pci.c | 628 +++++++++++++++--- 2 files changed, 549 insertions(+), 429 deletions(-) diff --git a/src/kernel/core/addons/bus_managers/config_manager.c b/src/kernel/core/addons/bus_managers/config_manager.c index f2de68d233..d24b8528ff 100644 --- a/src/kernel/core/addons/bus_managers/config_manager.c +++ b/src/kernel/core/addons/bus_managers/config_manager.c @@ -13,17 +13,8 @@ #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 - * information is only useful if you're working on this code. - */ -#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, @@ -33,346 +24,7 @@ static status_t cfdm_get_next_device_info(bus_type bus, uint64 *cookie, bus, *cookie); return 0; } - -static char *decode_class(uint8 base, uint8 sub_class) -{ - switch(base) { - 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: - 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: - 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"; - case 0x10: return "encryption"; - case 0x11: return "signal processing"; - default: return "unknown"; - } -} - -static void show_pci_details(struct pci_info *p, pci_module_info *pcim) -{ -#if THE_FULL_MONTY - 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 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: %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 == 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("\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_info apci; - long index = 0; - - while (pcim->get_nth_pci_info(index++, &apci) == 0) { - show_pci_details(&apci, pcim); - } - - return 0; -} - /* device_modules */ static int cfdm_std_ops(int32 op, ...) { @@ -383,8 +35,6 @@ static int cfdm_std_ops(int32 op, ...) dprintf("config_manager: failed to load PCI module\n"); return -1; } - pci_scan_bus(0); - test_me(); break; case B_MODULE_UNINIT: dprintf( "config_manager: device modules: uninit\n" ); diff --git a/src/kernel/core/addons/bus_managers/pci/pci.c b/src/kernel/core/addons/bus_managers/pci/pci.c index d217d65af7..e8945425f5 100755 --- a/src/kernel/core/addons/bus_managers/pci/pci.c +++ b/src/kernel/core/addons/bus_managers/pci/pci.c @@ -26,20 +26,27 @@ /* Change to 1 to see more debugging :) */ #define THE_FULL_MONTY 0 -struct found_pci_device { - struct found_pci_device *next; - struct found_pci_device *prev; +struct pci_device { + struct pci_device *next; + int type; pci_info *info; }; -static struct found_pci_device pci_dev_list; - -struct pci_config { - struct pci_config *next; - char *full_path; - struct pci_cfg *cfg; +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 @@ -50,10 +57,28 @@ static int bus_max_devices = 32; /* max devices that any bus can support * 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_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 -struct pci_config *pci_list; /* Config space locking! * We need to make sure we only have one access at a time into the config space, @@ -73,6 +98,176 @@ struct pci_config *pci_list; restore_interrupts(cpu_status); \ } +/* 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 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: + 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: + 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"; + case 0x10: return "encryption"; + case 0x11: return "signal processing"; + default: return "unknown"; + } +} + +static void show_pci_details(struct pci_info *p) +{ +#if THE_FULL_MONTY + 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 THE_FULL_MONTY + 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; + } + 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 @@ -308,6 +503,63 @@ static int pci_set_power_state(uint8 bus, uint8 dev, uint8 func, int state) return 0; } + +/* 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 = 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; + } + } + } +} + +/* 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; +} /* set_pci_mechanism() * Try to determine which configuration mechanism the PCI Host Bridge @@ -499,6 +751,252 @@ static void print_pir_table(struct pir_table *tbl) } #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. + */ + +/* 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 + * + * 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 void pci_bridge(uint8 bus, uint8 dev, uint8 func) +{ + uint16 command = 0; + uint8 mybus = bus + 1; + struct pci_device *pcid; + struct pci_bus *pcib; + pci_info *pcii; + + command = read_pci_config(bus, dev, func, PCI_command, 2); + command &= ~ 0x03; + write_pci_config(bus, dev, func, PCI_command, 2, command); + + 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); + pci_max_bus = bus + 1; + + 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) + return; + pcid = (struct pci_device*)kmalloc(sizeof(struct pci_device)); + if (!pcid) { + kfree(pcii); + return; + } + pcib = (struct pci_bus *)kmalloc(sizeof(struct pci_bus)); + if (!pcib) { + kfree(pcii); + kfree(pcid); + return; + } + + pcid->info = pcii; + pcid->type = PCI_BRIDGE; + pcib->info = pcii; + 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); + 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 = read_pci_config(bus, dev, func, PCI_interrupt_line, 1); + pcii->u.h1.interrupt_pin = read_pci_config(bus, dev, func, PCI_interrupt_pin, 1) & PCI_pin_mask; + + { + struct pci_device *pd = pci_devices; + while (pd && pd->next) + pd = pd->next; + + if (pd) + pd->next = pcid; + else + pci_devices = pcid; + } + { + struct pci_bus *pb = pci_busses; + while (pb && pb->next) + pb = pb->next; + + if (pb) + pb->next = pcib; + else + pci_busses = pcib; + } + + command |= 0x03; + write_pci_config(mybus, dev, func, PCI_command, 2, command); + + show_pci_details(pcii); + return; +} + +/* This is a very, very brief 2 liner for a device... */ + +static void debug_show_device(pci_info *pcii) +{ + dprintf("device at %d:%d:%d => %s\n", pcii->bus, pcii->device, + pcii->function, + decode_class(pcii->class_base, pcii->class_sub)); + dprintf("Capabilities: "); + if (pci_get_capability(pcii->bus, pcii->device, pcii->function, PCI_cap_id_agp, NULL)) + dprintf("AGP "); + if (pci_get_capability(pcii->bus, pcii->device, pcii->function, PCI_cap_id_pm, NULL)) + dprintf("Pwr Mgmt "); + if (pci_get_capability(pcii->bus, pcii->device, pcii->function, PCI_cap_id_vpd, NULL)) + dprintf("VPD "); + if (pci_get_capability(pcii->bus, pcii->device, pcii->function, PCI_cap_id_slotid, NULL)) + dprintf("slotID "); + if (pci_get_capability(pcii->bus, pcii->device, pcii->function, PCI_cap_id_msi, NULL)) + dprintf("MSI "); + if (pci_get_capability(pcii->bus, pcii->device, pcii->function, PCI_cap_id_hotplug, NULL)) + dprintf("hotplug "); + dprintf("\n"); +} + +static void pci_device_probe(uint8 bus, uint8 dev, uint8 func) +{ + uint8 base_class, sub_class; + uint8 type; + pci_info *pcii; + struct pci_device *pcid; + + if (func > 0) { + uint16 vend = read_pci_config(bus, dev, func, PCI_vendor_id, 2); + if (vend == 0xffff) + return; + } + + type = read_pci_config(bus, dev, func, PCI_header_type, 1); + type &= PCI_header_type_mask; + base_class = read_pci_config(bus, dev, func, PCI_class_base, 1); + sub_class = 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; + } + } + /* If we get here then it's not a bridge, so we add it... */ + pcii = (pci_info*)kmalloc(sizeof(pci_info)); + if (!pcii) + return; + pcid = (struct pci_device*)kmalloc(sizeof(struct pci_device)); + if (!pcid) { + kfree(pcii); + return; + } + + pcid->info = pcii; + pcid->type = PCI_DEVICE; + pcii->bus = bus; + pcii->device = dev; + pcii->function = func; + + fill_basic_pci_structure(pcii); + + if (pcii->class_base == PCI_bridge && pcii->class_sub == PCI_host) + pcid->type = PCI_HOST_BUS; + + 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 = read_pci_config(bus, dev, func, PCI_interrupt_line, 1); + pcii->u.h0.interrupt_pin = read_pci_config(bus, dev, func, PCI_interrupt_pin, 1) & PCI_pin_mask; + + /* now add to list (at end) */ + { + struct pci_device *pd = pci_devices; + while (pd && pd->next) + pd = pd->next; + + if (pd) + pd->next = pcid; + else + pci_devices = pcid; + } + pci_set_power_state(bus, dev, func, PCI_pm_state_d0); + + debug_show_device(pcii); +} + +/* 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 = read_pci_config(bus, dev, 0, PCI_vendor_id, 2); + if (vend != 0xffff) { + uint16 device = read_pci_config(bus, dev, func, PCI_device_id, 2); + uint8 type = 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 void scan_pci(void) { int bus, dev, func; @@ -512,7 +1010,6 @@ static void scan_pci(void) for (func = 0; func < 8; func++) { pci_info *pcii = NULL; - struct found_pci_device *npcid = NULL; uint16 vendor_id = read_pci_config(bus, dev, func, 0, 2); uint16 device_id; uint8 offset; @@ -546,11 +1043,6 @@ static void scan_pci(void) dprintf("Failed to get memory for a pic_info structure in scan_pci\n"); return; } - if ((npcid = (struct found_pci_device*)kmalloc(sizeof(struct found_pci_device))) == NULL) { - kfree(pcii); - dprintf("scan_pci: failed to kmalloc memory for found_pci_device structure\n"); - return; - } if (pci_get_capability(bus, dev, func, PCI_cap_id_agp, &offset)) dprintf("Device @ %d:%d:%d has AGP capability\n", bus, dev, func); @@ -570,19 +1062,26 @@ static void scan_pci(void) 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); } } } } -static void fill_pci_structure(pci_info *pcii) +static void fill_basic_pci_structure(pci_info *pcii) { uint8 bus = pcii->bus, dev = pcii->device, func = pcii->function; uint8 int_line = 0, int_pin = 0; + + pcii->vendor_id = read_pci_config(bus, dev, func, PCI_vendor_id, 2); + pcii->device_id = read_pci_config(bus, dev, func, PCI_device_id, 2); + + 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->header_type = read_pci_config(bus, dev, func, PCI_header_type, 1); + pcii->header_type &= PCI_header_type_mask; pcii->latency = read_pci_config(bus, dev, func, PCI_latency, 1); pcii->bist = read_pci_config(bus, dev, func, PCI_bist, 1); @@ -591,43 +1090,6 @@ static void fill_pci_structure(pci_info *pcii) 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"); - /* ??? */ - } } /* XXX - check the return values from this function. */ @@ -639,7 +1101,7 @@ static long get_nth_pci_info(long index, pci_info *copyto) * XXX - see discussion below. */ long iter = index; - struct found_pci_device *fpd = pci_dev_list.prev; + struct pci_device *pd = pci_devices; /* iterate through the list until we have iter == 0 * NB we go in "reverse" as the devices are inserted into the @@ -652,8 +1114,8 @@ static long get_nth_pci_info(long index, pci_info *copyto) * XXX - if we do decide to reverse the order we need to be very * careful about which function # gets found first and returned. */ - while (iter-- > 0 && fpd && fpd != &pci_dev_list) - fpd = fpd->prev; + while (iter-- > 0 && pd) + pd = pd->next; /* 2 cases we bail here. * 1) we don't have enough devices to fulfill the request @@ -661,12 +1123,10 @@ static long get_nth_pci_info(long index, pci_info *copyto) * 2) The found_device_structure has a NULL pointer for * info, which would cause a segfault when we try to memcpy! */ - if (iter > 0 || !fpd->info) + if (iter > 0 || !pd->info) return B_DEV_ID_ERROR; - fill_pci_structure(fpd->info); - - memcpy(copyto, fpd->info, sizeof(pci_info)); + memcpy(copyto, pd->info, sizeof(pci_info)); return 0; } @@ -701,24 +1161,32 @@ static void pci_write_io_32(int mapped_io_addr, uint32 value) out32(value, mapped_io_addr); } +/* pci_module_init + * This is where we start all the PCI work, or not as the case may be. + * We start by trying to detect and set the correct configuration mechanism + * to use. We don't handle PCI BIOS's as that method seems to be falling out + * of fashion and is Intel specific. + * + * Next we try to check if we have working PCI on this machine. check_pci() + * isn't the smartest routine as it essentially just looks for a Host-PCI + * bridge, but it will show us if we can continue. + * NB Until this has been tested with a wider audience we don't consider + * success/failure here but spew a large debug message and try to continue. + * XXX - once we're happy with check_pci() make failing a criminal offense! + * + * Next we check to see if we can find a PIR table. + * NB This is Intel specific and currently included here as a test. + * + * Finally we scan for devices starting at bus 0. + */ static void pci_module_init(void) { struct pir_table *pirt = NULL; - /* init the double linked list */ - pci_dev_list.next = pci_dev_list.prev = &pci_dev_list; - - /* Determine how we communicate with the PCI Host Bridge */ if (set_pci_mechanism() == -1) { return; } - /* Check PCI is valid and we can use it. - * Presently we don't do anything with this other than run it and - * inform the user if we pass/fail. If we fail we print a big message - * asking the user to get in touch to let us know about their system so - * we can refine the test. - */ check_pci(); pci_region = vm_map_physical_memory(vm_get_kernel_aspace_id(), @@ -728,7 +1196,6 @@ static void pci_module_init(void) LOCK_RO | LOCK_KERNEL, (addr)0xf0000); - pirt = find_pir_table(); if (pirt) { dprintf("PCI IRQ Routing table found\n"); @@ -737,10 +1204,13 @@ static void pci_module_init(void) #endif } - /* Get our initial list of devices */ - scan_pci(); + pci_scan_bus(0); } +/* pci_module_uninit + * Finish the module by releasing the memory we grabbed for + * the pci_bios :) + */ static void pci_module_uninit(void) { vm_delete_region(vm_get_kernel_aspace_id(), pci_region);