diff options
author | Michael Brown | 2009-09-18 05:35:05 +0200 |
---|---|---|
committer | Michael Brown | 2012-07-09 16:41:11 +0200 |
commit | d4c04073958a6610720e205d7a3c6b6c139db96a (patch) | |
tree | 22c7aceb65addfceaf60c4e17f0bbe7c04b927b4 /pci.c | |
download | memtest86-d4c04073958a6610720e205d7a3c6b6c139db96a.tar.gz memtest86-d4c04073958a6610720e205d7a3c6b6c139db96a.tar.xz memtest86-d4c04073958a6610720e205d7a3c6b6c139db96a.zip |
[import] Import version 1.15
http://www.memtest.org/download/1.15/memtest_source_v1.15.tar.gz
Diffstat (limited to 'pci.c')
-rw-r--r-- | pci.c | 135 |
1 files changed, 135 insertions, 0 deletions
@@ -0,0 +1,135 @@ +#include "io.h" +#include "pci.h" + +#define PCI_CONF_TYPE_NONE 0 +#define PCI_CONF_TYPE_1 1 +#define PCI_CONF_TYPE_2 2 + +static unsigned char pci_conf_type = PCI_CONF_TYPE_NONE; + +#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ + (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) + +#define PCI_CONF2_ADDRESS(dev, reg) (unsigned short)(0xC000 | (dev << 8) | reg) + +int pci_conf_read(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, unsigned long *value) +{ + int result; + + if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -1; + + result = -1; + switch(pci_conf_type) { + case PCI_CONF_TYPE_1: + outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); + switch(len) { + case 1: *value = inb(0xCFC + (reg & 3)); result = 0; break; + case 2: *value = inw(0xCFC + (reg & 2)); result = 0; break; + case 4: *value = inl(0xCFC); result = 0; break; + } + break; + case PCI_CONF_TYPE_2: + outb(0xF0 | (fn << 1), 0xCF8); + outb(bus, 0xCFA); + + switch(len) { + case 1: *value = inb(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; + case 2: *value = inw(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; + case 4: *value = inl(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; + } + outb(0, 0xCF8); + break; + } + return result; +} + +int pci_conf_write(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, unsigned long value) +{ + int result; + + if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -1; + + result = -1; + switch(pci_conf_type) { + case PCI_CONF_TYPE_1: + outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); + switch(len) { + case 1: outb(value, 0xCFC + (reg & 3)); result = 0; break; + case 2: outw(value, 0xCFC + (reg & 2)); result = 0; break; + case 4: outl(value, 0xCFC); result = 0; break; + } + break; + case PCI_CONF_TYPE_2: + outb(0xF0 | (fn << 1), 0xCF8); + outb(bus, 0xCFA); + + switch(len) { + case 1: outb(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; + case 2: outw(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; + case 4: outl(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; + } + outb(0, 0xCF8); + break; + } + return result; +} + +static int pci_sanity_check(void) +{ + unsigned long value; + int result; + /* Do a trivial check to make certain we can see a host bridge. + * There are reportedly some buggy chipsets from intel and + * compaq where this test does not work, I will worry about + * that when we support them. + */ + result = pci_conf_read(0, 0, 0, PCI_CLASS_DEVICE, 2, &value); + if (result == 0) { + result = -1; + if (value == PCI_CLASS_BRIDGE_HOST) { + result = 0; + } + } + return result; +} + +static int pci_check_direct(void) +{ + unsigned int tmp; + + /* Check if configuration type 1 works. */ + pci_conf_type = PCI_CONF_TYPE_1; + outb(0x01, 0xCFB); + tmp = inl(0xCF8); + outl(0x80000000, 0xCF8); + if ((inl(0xCF8) == 0x80000000) && (pci_sanity_check() == 0)) { + outl(tmp, 0xCF8); + return 0; + } + outl(tmp, 0xCF8); + + /* Check if configuration type 2 works. */ + pci_conf_type = PCI_CONF_TYPE_2; + outb(0x00, 0xCFB); + outb(0x00, 0xCF8); + outb(0x00, 0xCFA); + if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 && (pci_sanity_check() == 0)) { + return 0; + } + + /* Nothing worked return an error */ + pci_conf_type = PCI_CONF_TYPE_NONE; + return -1; +} + +int pci_init(void) +{ + int result; + /* For now just make certain we can directly + * use the pci functions. + */ + result = pci_check_direct(); + return result; +} |