PCI patch for 2.3.16

Martin Mares (mj@ucw.cz)
Wed, 1 Sep 1999 10:50:47 +0200


Hi Linus,

The PCI saga continues ... there is a small patch against 2.3.16:

o pci_dev->slot_name defined and used (I hope this variant is not confusing anymore)
o Use PCI BIOS IRQ routing table (if available) to find all the peer buses.
This should be a way more reliable than the peer bridge magic we were
using before (and still are using if there is no routing table).

The remaining things I'd like to solve before 2.4 (except for bug fixes, of course :)) :

o Introduce some mechanism for manual setting of the kernel view
of IRQ's, so that people having motherboards with broken BIOSes
can fix the things manually.
o Write a script creating devlist.h from the pci.ids file.
o Write helper functions for allocation/freeing of PCI resources.
As far as I remember, we didn't agree yet whether there should
be a single function allocating all the resources (and drivers
with special needs not using it) or a function for allocating
a single resource (called multiple times by each driver).
I prefer the first solution as it can be implemented as a
`pci_enable_device()' type function which could do things
like waking up powered down devices as well.

Have a nice day
Martin

--- include/linux/pci.h.mj Wed Sep 1 10:16:45 1999
+++ include/linux/pci.h Wed Sep 1 10:16:45 1999
@@ -1402,6 +1402,7 @@
struct resource irq_resource[DEVICE_COUNT_IRQ];

char name[48]; /* Device name */
+ char slot_name[8]; /* Slot name */

int (*prepare)(struct pci_dev *dev);
int (*activate)(struct pci_dev *dev);
--- drivers/pci/pci.c.mj Wed Sep 1 10:17:22 1999
+++ drivers/pci/pci.c Wed Sep 1 10:21:08 1999
@@ -193,13 +193,13 @@

pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (! (cmd & PCI_COMMAND_MASTER)) {
- printk("PCI: Enabling bus mastering for device %s\n", dev->name);
+ printk("PCI: Enabling bus mastering for device %s\n", dev->slot_name);
cmd |= PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
if (lat < 16) {
- printk("PCI: Increasing latency timer of device %s to 64\n", dev->name);
+ printk("PCI: Increasing latency timer of device %s to 64\n", dev->slot_name);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
}
}
@@ -267,7 +267,7 @@
res->end = res->start + (((unsigned long) ~l) << 32);
#else
if (l) {
- printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->name);
+ printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->slot_name);
res->start = 0;
res->flags = 0;
continue;
@@ -396,7 +396,7 @@
dev_cache = NULL;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
- sprintf(dev->name, "%02x:%02x.%d", bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ sprintf(dev->slot_name, "%02x:%02x.%d", bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
pci_name_device(dev);

pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
--- drivers/pci/quirks.c.mj Wed Sep 1 10:12:12 1999
+++ drivers/pci/quirks.c Wed Sep 1 10:21:39 1999
@@ -31,7 +31,7 @@
while ((d = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, d))) {
pci_read_config_byte(d, 0x82, &dlc);
if (!(dlc & 1<<1)) {
- printk("PCI: PIIX3: Enabling Passive Release on %s\n", d->name);
+ printk("PCI: PIIX3: Enabling Passive Release on %s\n", d->slot_name);
dlc |= 1<<1;
pci_write_config_byte(d, 0x82, dlc);
}
@@ -67,7 +67,7 @@
struct resource *r = &dev->resource[0];

if ((r->start & 0x3ffffff) || r->end != r->start + 0x3ffffff) {
- printk("PCI: Re-allocating buggy S3 card at %s: ", dev->name);
+ printk("PCI: Re-allocating buggy S3 card at %s: ", dev->slot_name);
r->start = 0;
r->end = 0x3ffffff;
if (pcibios_assign_resource(dev, 0))
@@ -103,7 +121,7 @@
(f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) &&
(f->device == dev->device || f->device == (u16) PCI_ANY_ID)) {
#ifdef DEBUG
- printk("PCI: Calling quirk %p for %s\n", f->hook, dev->name);
+ printk("PCI: Calling quirk %p for %s\n", f->hook, dev->slot_name);
#endif
f->hook(dev);
}
--- arch/i386/kernel/bios32.c.mj Wed Sep 1 10:00:24 1999
+++ arch/i386/kernel/bios32.c Wed Sep 1 10:19:00 1999
@@ -77,6 +77,9 @@
* Feb 8, 1999 : Added UM8886BF I/O address fixup. [mj]
*
* August 1999 : New resource management and configuration access stuff. [mj]
+ *
+ * Aug 31, 1999 : Use BIOS interrupt routing tables for detection of peer host
+ * bridges. Thanks to Chris Frantz <frantzc@insync.net> for the idea. [mj]
*/

#include <linux/config.h>
@@ -88,6 +91,7 @@
#include <linux/malloc.h>
#include <linux/smp_lock.h>
#include <linux/irq.h>
+#include <linux/mm.h>

#include <asm/page.h>
#include <asm/segment.h>
@@ -347,6 +351,8 @@
#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b
#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
+#define PCIBIOS_GET_ROUTING_OPTIONS 0xb10e
+#define PCIBIOS_SET_PCI_HW_INT 0xb10f

/* BIOS32 signature: "_32_" */
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
@@ -645,6 +651,81 @@
}

/*
+ * The only working way how to ask the BIOS about the list of buses
+ * is to query the IRQ routing options table.
+ */
+
+struct irq_routing_table {
+ u8 bus;
+ u8 dev;
+ struct {
+ u8 link;
+ u16 bitmap;
+ } __attribute__((packed)) irq[4];
+ u8 slot;
+ u8 rfu;
+} __attribute__((packed));
+
+struct irq_routing_options {
+ u16 size;
+ struct irq_routing_table *table;
+ u16 segment;
+} __attribute__((packed));
+
+static int __init pcibios_irq_peer_trick(void)
+{
+ char busmap[256];
+ int ret, i, map;
+ struct irq_routing_options opt;
+
+ opt.table = (void *) __get_free_page(GFP_KERNEL);
+ if (!opt.table)
+ return 0;
+ opt.size = PAGE_SIZE;
+ opt.segment = __KERNEL_DS;
+
+ DBG("PCI: Fetching IRQ routing table... ");
+ __asm__("push %%es\n\t"
+ "push %%ds\n\t"
+ "pop %%es\n\t"
+ "lcall (%%esi)\n\t"
+ "pop %%es\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret),
+ "=b" (map)
+ : "0" (PCIBIOS_GET_ROUTING_OPTIONS),
+ "1" (0),
+ "D" ((long) &opt),
+ "S" (&pci_indirect));
+ DBG("OK ret=%d, size=%d, map=%x\n", ret, opt.size, map);
+ if (ret & 0xff00) {
+ printk(KERN_ERR "PCI: Error %02x when fetching IRQ routing table.\n", (ret >> 8) & 0xff);
+ free_page((long) opt.table);
+ return 0;
+ }
+
+ memset(busmap, 0, sizeof(busmap));
+ for(i=0; i < opt.size/sizeof(struct irq_routing_table); i++) {
+ struct irq_routing_table *t = ((struct irq_routing_table *) opt.table) + i;
+ DBG("b=%02x d=%02x s=%02x\n", t->bus, t->dev, t->slot);
+ busmap[t->bus] = 1;
+ }
+ for(i=1; i<256; i++)
+ /*
+ * It might be a secondary bus, but in this case its parent is already
+ * known (ascending bus order) and therefore pci_scan_bus returns immediately.
+ */
+ if (busmap[i] && pci_scan_bus(i, pci_root->ops, NULL))
+ printk("PCI: Discovered primary peer bus %02x [IRQ]\n", i);
+
+ free_page((long) opt.table);
+ return 1;
+}
+
+
+/*
* Function table for BIOS32 access
*/

@@ -817,7 +898,7 @@
* ISA hardware, so we always want the addresses kilobyte aligned.
*/
if (!size || size > 256) {
- printk(KERN_ERR "PCI: Cannot assign I/O space to device %s, %d bytes are too much.\n", dev->name, size);
+ printk(KERN_ERR "PCI: Cannot assign I/O space to device %s, %d bytes are too much.\n", dev->slot_name, size);
return;
} else {
u32 try;
@@ -825,10 +906,10 @@
r->start = 0;
r->end = size - 1;
if (pcibios_assign_resource(dev, idx)) {
- printk(KERN_ERR "PCI: Unable to find free %d bytes of I/O space for device %s.\n", size, dev->name);
+ printk(KERN_ERR "PCI: Unable to find free %d bytes of I/O space for device %s.\n", size, dev->slot_name);
return;
}
- printk("PCI: Assigned I/O space %04lx-%04lx to device %s\n", r->start, r->end, dev->name);
+ printk("PCI: Assigned I/O space %04lx-%04lx to device %s\n", r->start, r->end, dev->slot_name);
pci_read_config_dword(dev, reg, &try);
if ((try & PCI_BASE_ADDRESS_IO_MASK) != r->start) {
r->start = 0;
@@ -854,9 +935,9 @@
r->end = rom_size - 1;
if (pcibios_assign_resource(dev, PCI_ROM_RESOURCE))
printk(KERN_ERR "PCI: Unable to find free space for expansion ROM of device %s (0x%lx bytes)\n",
- dev->name, rom_size);
+ dev->slot_name, rom_size);
else {
- DBG("PCI: Assigned address %08lx to expansion ROM of %s (0x%lx bytes)\n", r->start, dev->name, rom_size);
+ DBG("PCI: Assigned address %08lx to expansion ROM of %s (0x%lx bytes)\n", r->start, dev->slot_name, rom_size);
pci_write_config_dword(dev, reg, r->start | PCI_ROM_ADDRESS_ENABLE);
r->flags |= PCI_ROM_ADDRESS_ENABLE;
}
@@ -938,6 +1019,15 @@
return;
#endif

+#ifdef CONFIG_PCI_BIOS
+ /*
+ * If the BIOS supports `get irq routing options' call, it should
+ * give us enough information for peer bridge searching.
+ */
+ if (pci_bios_present && pcibios_irq_peer_trick())
+ return;
+#endif
+
for(d=b->devices; d; d=d->sibling)
if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
cnt++;
@@ -996,7 +1086,7 @@
*/
int pxb, reg;
u8 busno, suba, subb;
- printk("PCI: Searching for i450NX host bridges on %s\n", d->name);
+ printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name);
reg = 0xd0;
for(pxb=0; pxb<2; pxb++) {
pci_read_config_byte(d, reg++, &busno);
@@ -1019,7 +1109,7 @@
*/
int i;

- printk("PCI: Fixing base address flags for device %s\n", d->name);
+ printk("PCI: Fixing base address flags for device %s\n", d->slot_name);
for(i=0; i<4; i++)
d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
}
@@ -1049,7 +1139,7 @@
continue;
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0) {
- printk(KERN_ERR "PCI: Address space collision on region %d of device %s\n", idx, dev->name);
+ printk(KERN_ERR "PCI: Address space collision on region %d of device %s\n", idx, dev->slot_name);
/* We probably should disable the region, shouldn't we? */
}
}
@@ -1101,12 +1191,12 @@
((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)) {
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (has_io && !(cmd & PCI_COMMAND_IO)) {
- printk("PCI: Enabling I/O for device %s\n", dev->name);
+ printk("PCI: Enabling I/O for device %s\n", dev->slot_name);
cmd |= PCI_COMMAND_IO;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) {
- printk("PCI: Enabling memory for device %s\n", dev->name);
+ printk("PCI: Enabling memory for device %s\n", dev->slot_name);
cmd |= PCI_COMMAND_MEMORY;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/