summaryrefslogtreecommitdiffstats
path: root/pci.c
diff options
context:
space:
mode:
authorMichael Brown2009-09-18 05:35:05 +0200
committerMichael Brown2012-07-09 16:41:11 +0200
commitd4c04073958a6610720e205d7a3c6b6c139db96a (patch)
tree22c7aceb65addfceaf60c4e17f0bbe7c04b927b4 /pci.c
downloadmemtest86-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.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/pci.c b/pci.c
new file mode 100644
index 0000000..02ad6c2
--- /dev/null
+++ b/pci.c
@@ -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;
+}