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
This commit is contained in:
David Reid 2002-07-25 18:11:45 +00:00
parent d6758977e7
commit a0bc23f288
2 changed files with 549 additions and 429 deletions

View File

@ -13,17 +13,8 @@
#include <string.h>
#include <memheap.h>
/* 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" );

View File

@ -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);