| #include "pci/pci.h" |
| #include <com32.h> |
| #include <string.h> |
| |
| enum pci_config_type __pci_cfg_type; |
| |
| static int type1_ok(void) |
| { |
| uint32_t oldcf8, newcf8; |
| |
| /* Test for Configuration Method #1 */ |
| |
| /* Note: XFree86 writes ~0 and expects to read back 0x80fffffc. Linux |
| does this less severe test; go with Linux. */ |
| |
| cli(); |
| outb(1, 0xcfb); /* For old Intel chipsets */ |
| oldcf8 = inl(0xcf8); |
| outl(0x80000000, 0xcf8); |
| newcf8 = inl(0xcf8); |
| outl(oldcf8, 0xcf8); |
| sti(); |
| |
| return newcf8 == 0x80000000; |
| } |
| |
| static int type2_ok(void) |
| { |
| uint8_t oldcf8, oldcfa; |
| uint8_t cf8, cfa; |
| |
| /* Test for Configuration Method #2 */ |
| |
| /* CM#2 is hard to probe for, but let's do our best... */ |
| |
| cli(); |
| outb(0, 0xcfb); /* For old Intel chipsets */ |
| oldcf8 = inb(0xcf8); |
| outb(0, 0xcf8); |
| oldcfa = inb(0xcfa); |
| outb(0, 0xcfa); |
| |
| cf8 = inb(0xcf8); |
| cfa = inb(0xcfa); |
| |
| outb(oldcf8, 0xcf8); |
| outb(oldcfa, 0xcfa); |
| sti(); |
| |
| return cf8 == 0 && cfa == 0; |
| } |
| |
| int pci_set_config_type(enum pci_config_type type) |
| { |
| static const com32sys_t ireg = { |
| .eax.l = 0xb101, |
| .edi.l = 0, |
| .eflags.l = EFLAGS_CF, |
| }; |
| com32sys_t oreg; |
| |
| if (type == PCI_CFG_AUTO) { |
| type = PCI_CFG_NONE; |
| |
| /* Try to detect PCI BIOS */ |
| __intcall(0x1a, &ireg, &oreg); |
| |
| if (!(oreg.eflags.l & EFLAGS_CF) && |
| oreg.eax.b[1] == 0 && oreg.edx.l == 0x20494250) { |
| /* PCI BIOS present. Use direct access if we know how to do it. */ |
| |
| if ((oreg.eax.b[0] & 1) && type1_ok()) |
| type = PCI_CFG_TYPE1; |
| else if ((oreg.eax.b[0] & 2) && type2_ok()) |
| type = PCI_CFG_TYPE2; |
| else |
| type = PCI_CFG_BIOS; /* Use BIOS calls as fallback */ |
| |
| } else if (type1_ok()) { |
| type = PCI_CFG_TYPE1; |
| } else if (type2_ok()) { |
| type = PCI_CFG_TYPE2; |
| } else { |
| type = PCI_CFG_NONE; /* Badness... */ |
| } |
| } |
| |
| return (__pci_cfg_type = type); |
| } |