Compaq PCI woes

Jeff Lovell (jalovel@rs6000.cmp.ilstu.edu)
Fri, 14 Feb 1997 09:04:50 -0600


Recently, there were some patches posted for COMPAQ PCI problems.

These came just in time, as I have a machine that would benefit from these.
I applied the 2.0.29 patch, and it does indeed now see the PCI BIOS
(yippee!!). But the integrated pcnet32 card does not work still.

It gets detected, but it is found at 0x7000 irq 255. Somehow, that does not
seem right. I do not know where to look for an answer to this, so I thought
here might be a start.

Any info anyone can provide is VERY VERY Appreciated.

Jeff

Ps, The patch is below.

---------------------------* 2.0.29 *---------------------------------------
--- linux/arch/i386/kernel/bios32.c.orig Sat Jun 8 10:10:49 1996
+++ linux/arch/i386/kernel/bios32.c Mon Feb 3 13:16:37 1997
@@ -46,6 +46,11 @@
* Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the
code
* moved to drivers/pci/pci.c.
*
+ * Dec 7, 1996 : Added support for direct configuration access of boards
+ * with Intel compatible access schemes (tsbogend@alpha.franken.de)
+ *
+ * Feb 3, 1997 : Set internal functions to static, save/restore flags
+ * avoid dead locks reading broken PCI BIOS, werner@suse.de
*
*/

@@ -56,6 +61,8 @@
#include <linux/pci.h>

#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>

#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
@@ -101,6 +108,7 @@
char chars[16];
};

+#ifdef CONFIG_PCI
/*
* Physical address of the service directory. I don't know if we're
* allowed to have more than one of these or not, so just in case
@@ -114,7 +122,28 @@
unsigned short segment;
} bios32_indirect = { 0, KERNEL_CS };

-#ifdef CONFIG_PCI
+
+/*
+ * function table for accessing PCI configuration space
+ */
+struct pci_access {
+ int (*find_device)(unsigned short, unsigned short, unsigned short,
unsigned char *, unsigned char *);
+ int (*find_class)(unsigned int, unsigned short, unsigned char *,
unsigned char *);
+ int (*read_config_byte)(unsigned char, unsigned char, unsigned char,
unsigned char *);
+ int (*read_config_word)(unsigned char, unsigned char, unsigned char,
unsigned short *);
+ int (*read_config_dword)(unsigned char, unsigned char, unsigned char,
unsigned int *);
+ int (*write_config_byte)(unsigned char, unsigned char, unsigned char,
unsigned char);
+ int (*write_config_word)(unsigned char, unsigned char, unsigned char,
unsigned short);
+ int (*write_config_dword)(unsigned char, unsigned char, unsigned char,
unsigned int);
+};
+
+/*
+ * pointer to selected PCI access function table
+ */
+static struct pci_access *access_pci = NULL;
+
+
+
/*
* Returns the entry point for the given service, NULL on error
*/
@@ -125,7 +154,9 @@
unsigned long address; /* %ebx */
unsigned long length; /* %ecx */
unsigned long entry; /* %edx */
+ unsigned long flags;

+ save_flags(flags);
__asm__("lcall (%%edi)"
: "=a" (return_code),
"=b" (address),
@@ -134,6 +165,7 @@
: "0" (service),
"1" (0),
"D" (&bios32_indirect));
+ restore_flags(flags);

switch (return_code) {
case 0:
@@ -161,11 +193,13 @@
unsigned char present_status;
unsigned char major_revision;
unsigned char minor_revision;
+ unsigned long flags;
int pack;

if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
pci_indirect.address = pcibios_entry;

+ save_flags(flags);
__asm__("lcall (%%edi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -176,6 +210,7 @@
: "1" (PCIBIOS_PCI_BIOS_PRESENT),
"D" (&pci_indirect)
: "bx", "cx");
+ restore_flags(flags);

present_status = (pack >> 16) & 0xff;
major_revision = (pack >> 8) & 0xff;
@@ -200,17 +235,15 @@
return memory_start;
}

-int pcibios_present(void)
-{
- return pcibios_entry ? 1 : 0;
-}

-int pcibios_find_class (unsigned int class_code, unsigned short index,
+static int pci_bios_find_class (unsigned int class_code, unsigned short
index,
unsigned char *bus, unsigned char *device_fn)
{
unsigned long bx;
unsigned long ret;
+ unsigned long flags;

+ save_flags(flags);
__asm__ ("lcall (%%edi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -221,18 +254,21 @@
"c" (class_code),
"S" ((int) index),
"D" (&pci_indirect));
+ restore_flags(flags);
*bus = (bx >> 8) & 0xff;
*device_fn = bx & 0xff;
return (int) (ret & 0xff00) >> 8;
}


-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+static int pci_bios_find_device (unsigned short vendor, unsigned short
device_id,
unsigned short index, unsigned char *bus, unsigned char *device_fn)
{
unsigned short bx;
unsigned short ret;
+ unsigned long flags;

+ save_flags(flags);
__asm__("lcall (%%edi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -244,17 +280,20 @@
"d" (vendor),
"S" ((int) index),
"D" (&pci_indirect));
+ restore_flags(flags);
*bus = (bx >> 8) & 0xff;
*device_fn = bx & 0xff;
return (int) (ret & 0xff00) >> 8;
}

-int pcibios_read_config_byte(unsigned char bus,
+static int pci_bios_read_config_byte(unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned char *value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
+ unsigned long flags;

+ save_flags(flags);
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -265,15 +304,18 @@
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
+ restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}

-int pcibios_read_config_word (unsigned char bus,
+static int pci_bios_read_config_word (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned short *value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
+ unsigned long flags;

+ save_flags(flags);
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -284,15 +326,18 @@
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
+ restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}

-int pcibios_read_config_dword (unsigned char bus,
+static int pci_bios_read_config_dword (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned int *value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
+ unsigned long flags;

+ save_flags(flags);
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -303,15 +348,18 @@
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
+ restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}

-int pcibios_write_config_byte (unsigned char bus,
+static int pci_bios_write_config_byte (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned char value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
+ unsigned long flags;

+ save_flags(flags);
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -322,15 +370,18 @@
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
+ restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}

-int pcibios_write_config_word (unsigned char bus,
+static int pci_bios_write_config_word (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned short value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
+ unsigned long flags;

+ save_flags(flags);
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -341,15 +392,18 @@
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
+ restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}

-int pcibios_write_config_dword (unsigned char bus,
+static int pci_bios_write_config_dword (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned int value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
+ unsigned long flags;

+ save_flags(flags);
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
@@ -360,9 +414,421 @@
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
+ restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}

+/*
+ * function table for BIOS32 access
+ */
+static struct pci_access pci_bios_access = {
+ pci_bios_find_device,
+ pci_bios_find_class,
+ pci_bios_read_config_byte,
+ pci_bios_read_config_word,
+ pci_bios_read_config_dword,
+ pci_bios_write_config_byte,
+ pci_bios_write_config_word,
+ pci_bios_write_config_dword
+};
+
+
+
+/*
+ * Given the vendor and device ids, find the n'th instance of that device
+ * in the system.
+ */
+static int pci_direct_find_device (unsigned short vendor, unsigned short
device_id,
+ unsigned short index, unsigned char *bus,
+ unsigned char *devfn)
+{
+ unsigned int curr = 0;
+ struct pci_dev *dev;
+ unsigned long flags;
+
+ save_flags(flags);
+ for (dev = pci_devices; dev; dev = dev->next) {
+ if (dev->vendor == vendor && dev->device == device_id) {
+ if (curr == index) {
+ *devfn = dev->devfn;
+ *bus = dev->bus->number;
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++curr;
+ }
+ }
+ restore_flags(flags);
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+/*
+ * Given the class, find the n'th instance of that device
+ * in the system.
+ */
+static int pci_direct_find_class (unsigned int class_code, unsigned short
index,
+ unsigned char *bus, unsigned char *devfn)
+{
+ unsigned int curr = 0;
+ struct pci_dev *dev;
+ unsigned long flags;
+
+ save_flags(flags);
+ for (dev = pci_devices; dev; dev = dev->next) {
+ if (dev->class == class_code) {
+ if (curr == index) {
+ *devfn = dev->devfn;
+ *bus = dev->bus->number;
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++curr;
+ }
+ }
+ restore_flags(flags);
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+/*
+ * Functions for accessing PCI configuration space with type 1 accesses
+ */
+#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) |
(device_fn << 8) | (where & ~3))
+
+static int pci_conf1_read_config_byte(unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned char *value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ switch (where & 3) {
+ case 0: *value = inb(0xCFC);
+ break;
+ case 1: *value = inb(0xCFD);
+ break;
+ case 2: *value = inb(0xCFE);
+ break;
+ case 3: *value = inb(0xCFF);
+ break;
+ }
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_read_config_word (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned short *value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ if (where & 2)
+ *value = inw(0xCFE);
+ else
+ *value = inw(0xCFC);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_read_config_dword (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned int *value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ *value = inl(0xCFC);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_byte (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned char value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outb(value, 0xCFC);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_word (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned short value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outw(value, 0xCFC);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_dword (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned int value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outl(value, 0xCFC);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+#undef CONFIG_CMD
+
+/*
+ * functiontable for type 1
+ */
+static struct pci_access pci_direct_conf1 = {
+ pci_direct_find_device,
+ pci_direct_find_class,
+ pci_conf1_read_config_byte,
+ pci_conf1_read_config_word,
+ pci_conf1_read_config_dword,
+ pci_conf1_write_config_byte,
+ pci_conf1_write_config_word,
+ pci_conf1_write_config_dword
+};
+
+/*
+ * Functions for accessing PCI configuration space with type 2 accesses
+ */
+#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where)
+#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0)
+
+static int pci_conf2_read_config_byte(unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned char *value)
+{
+ unsigned long flags;
+
+ if (device_fn & 0x80)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ save_flags(flags);
+ outb (FUNC(device_fn), 0xCF8);
+ outb (bus, 0xCFA);
+ *value = inb(IOADDR(device_fn,where));
+ outb (0, 0xCF8);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_read_config_word (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned short *value)
+{
+ unsigned long flags;
+
+ if (device_fn & 0x80)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ save_flags(flags);
+ outb (FUNC(device_fn), 0xCF8);
+ outb (bus, 0xCFA);
+ *value = inw(IOADDR(device_fn,where));
+ outb (0, 0xCF8);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_read_config_dword (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned int *value)
+{
+ unsigned long flags;
+
+ if (device_fn & 0x80)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ save_flags(flags);
+ outb (FUNC(device_fn), 0xCF8);
+ outb (bus, 0xCFA);
+ *value = inl (IOADDR(device_fn,where));
+ outb (0, 0xCF8);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_byte (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned char value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outb (FUNC(device_fn), 0xCF8);
+ outb (bus, 0xCFA);
+ outb (value, IOADDR(device_fn,where));
+ outb (0, 0xCF8);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_word (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned short value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outb (FUNC(device_fn), 0xCF8);
+ outb (bus, 0xCFA);
+ outw (value, IOADDR(device_fn,where));
+ outb (0, 0xCF8);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_dword (unsigned char bus, unsigned char
device_fn,
+ unsigned char where, unsigned int value)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ outb (FUNC(device_fn), 0xCF8);
+ outb (bus, 0xCFA);
+ outl (value, IOADDR(device_fn,where));
+ outb (0, 0xCF8);
+ restore_flags(flags);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+#undef IOADDR
+#undef FUNC
+
+/*
+ * functiontable for type 2
+ */
+static struct pci_access pci_direct_conf2 = {
+ pci_direct_find_device,
+ pci_direct_find_class,
+ pci_conf2_read_config_byte,
+ pci_conf2_read_config_word,
+ pci_conf2_read_config_dword,
+ pci_conf2_write_config_byte,
+ pci_conf2_write_config_word,
+ pci_conf2_write_config_dword
+};
+
+
+static struct pci_access *check_direct_pci(void)
+{
+ unsigned int tmp;
+ unsigned long flags;
+
+ save_flags(flags);
+
+ /*
+ * check if configuration type 1 works
+ */
+ outb (0x01, 0xCFB);
+ tmp = inl (0xCF8);
+ outl (0x80000000, 0xCF8);
+ if (inl (0xCF8) == 0x80000000) {
+ outl (tmp, 0xCF8);
+ restore_flags(flags);
+ printk("pcibios_init: Using configuration type 1\n");
+ return &pci_direct_conf1;
+ }
+ outl (tmp, 0xCF8);
+
+ /*
+ * check if configuration type 2 works
+ */
+ outb (0x00, 0xCFB);
+ outb (0x00, 0xCF8);
+ outb (0x00, 0xCFA);
+ if (inb (0xCF8) == 0x00 && inb (0xCFC) == 0x00) {
+ restore_flags(flags);
+ printk("pcibios_init: Using configuration type 2\n");
+ return &pci_direct_conf2;
+ }
+ restore_flags(flags);
+ printk("pcibios_init: Not supported chipset for direct PCI access !\n");
+ return NULL;
+}
+
+
+/*
+ * access defined pcibios functions via
+ * the function table
+ */
+
+int pcibios_present(void)
+{
+ return access_pci ? 1 : 0;
+}
+
+int pcibios_find_class (unsigned int class_code, unsigned short index,
+ unsigned char *bus, unsigned char *device_fn)
+{
+ if (access_pci && access_pci->find_class)
+ return access_pci->find_class(class_code, index, bus, device_fn);
+
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+ unsigned short index, unsigned char *bus, unsigned char *device_fn)
+{
+ if (access_pci && access_pci->find_device)
+ return access_pci->find_device(vendor, device_id, index, bus,
device_fn);
+
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_byte (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned char *value)
+{
+ if (access_pci && access_pci->read_config_byte)
+ return access_pci->read_config_byte(bus, device_fn, where, value);
+
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_word (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned short *value)
+{
+ if (access_pci && access_pci->read_config_word)
+ return access_pci->read_config_word(bus, device_fn, where, value);
+
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_dword (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned int *value)
+{
+ if (access_pci && access_pci->read_config_dword)
+ return access_pci->read_config_dword(bus, device_fn, where, value);
+
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_write_config_byte (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned char value)
+{
+ if (access_pci && access_pci->write_config_byte)
+ return access_pci->write_config_byte(bus, device_fn, where, value);
+
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_write_config_word (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned short value)
+{
+ if (access_pci && access_pci->write_config_word)
+ return access_pci->write_config_word(bus, device_fn, where, value);
+
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_write_config_dword (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned int value)
+{
+ if (access_pci && access_pci->write_config_dword)
+ return access_pci->write_config_dword(bus, device_fn, where, value);
+
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
const char *pcibios_strerror (int error)
{
static char buf[80];
@@ -398,14 +864,14 @@

unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
{
-return mem_start;
+ return mem_start;
}

-
#endif

unsigned long pcibios_init(unsigned long memory_start, unsigned long
memory_end)
{
+#ifdef CONFIG_PCI
union bios32 *check;
unsigned char sum;
int i, length;
@@ -436,14 +902,16 @@
printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n",
check);
if (!bios32_entry) {
if (check->fields.entry >= 0x100000) {
- printk("pcibios_init: entry in high memory, unable to access\n");
+ printk("pcibios_init: entry in high memory, trying direct PCI access\n");
+ access_pci = check_direct_pci();
} else {
- bios32_indirect.address = bios32_entry = check->fields.entry;
+ bios32_entry = check->fields.entry;
printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n",
bios32_entry);
+ bios32_indirect.address = bios32_entry;
+ access_pci = &pci_bios_access;
}
}
}
-#ifdef CONFIG_PCI
if (bios32_entry) {
memory_start = check_pcibios (memory_start, memory_end);
}
---------------------------* 2.0.29 *---------------------------------------

--
How much net work could a network work, if a network could net work?

Jeff Lovell -- jalovel@rs6000.cmp.ilstu.edu -- Illinois State University