lkml.org 
[lkml]   [1998]   [May]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectMore PCI patches (peer host bridges etc.)
Hi Linus,

Here is the next round of PCI patches...

- The PCI quirk code now uses the new pci_... interface instead
of the old pcibios_... routines.

- Added bridge optimization for yet another old Intel chipset
(as suggested by Aaron Tiensivu).

- Peer host bridges are now recognized and handled. This is a bit
tricky as the bus numbers of secondary host bridges are hard-wired
(to 1 for all cases I've ever seen), but it makes Linux see the
second PCI bus on dual-PCI motherboards.

- pci_(read|write)_config_(byte,word,dword) made functions instead
of macros [space saving].

- 64-bit base addresses are now handled properly (i.e., warned about
on 32-bit archs, used on 64-bit ones).

- Device chain for each bus is now also kept sorted.

- Ignoring bogus devices with vendor/device ID 0000 or FFFF.

- Backward-compatible /proc/pci uses info in pci_dev structs instead
of re-reading it from the devices.

Martin


--- /usr/src/linux-2.1/drivers/pci/quirks.c Fri May 1 10:43:36 1998
+++ linux/drivers/pci/quirks.c Sat May 2 22:00:27 1998
@@ -1,5 +1,5 @@
/*
- * $Id: quirks.c,v 1.3 1998/02/06 19:51:42 mj Exp $
+ * $Id: quirks.c,v 1.5 1998/05/02 19:24:14 mj Exp $
*
* PCI Chipset-Specific Quirks
*
@@ -7,8 +7,7 @@
*
* This is the right place for all special fixups for on-board
* devices not depending on system architecture -- for example
- * bus bridges. The only thing implemented in this release is
- * the bridge optimization, but others might appear later.
+ * bus bridges.
*/

#include <linux/config.h>
@@ -88,21 +87,19 @@
printk(" %s: ", bridge_optimization[i].type);
bmap = &bridge_mapping[pos + i];
if (!bmap->addr) {
- printk("Not supported.");
+ printk("Not supported.\n");
} else {
- pcibios_read_config_byte(dev->bus->number, dev->devfn, bmap->addr, &val);
+ pci_read_config_byte(dev, bmap->addr, &val);
if ((val & bmap->mask) == bmap->value)
- printk("%s.", bridge_optimization[i].on);
+ printk("%s.\n", bridge_optimization[i].on);
else {
- printk("%s.", bridge_optimization[i].off);
- pcibios_write_config_byte(dev->bus->number, dev->devfn,
- bmap->addr,
- (val & (0xff - bmap->mask))
- + bmap->value);
- printk("Changed! Now %s.", bridge_optimization[i].on);
+ printk("%s", bridge_optimization[i].off);
+ pci_write_config_byte(dev,
+ bmap->addr,
+ (val & (0xff - bmap->mask)) + bmap->value);
+ printk(" -> %s.\n", bridge_optimization[i].on);
}
}
- printk("\n");
}
}

@@ -113,27 +110,18 @@
which can cause problems in combination with the 82441FX/PPro MTRRs */
__initfunc(static void quirk_passive_release(struct pci_dev *dev, int arg))
{
- struct pci_dev *piix3;
+ struct pci_dev *d = NULL;
unsigned char dlc;

/* We have to make sure a particular bit is set in the PIIX3
ISA bridge, so we have to go out and find it. */
- for (piix3 = pci_devices; ; piix3 = piix3->next) {
- if (!piix3)
- return;
-
- if (piix3->vendor == PCI_VENDOR_ID_INTEL
- && piix3->device == PCI_DEVICE_ID_INTEL_82371SB_0)
- break;
- }
-
- pcibios_read_config_byte(piix3->bus->number, piix3->devfn, 0x82, &dlc);
-
- if (!(dlc & 1<<1)) {
- printk("PIIX3: Enabling Passive Release\n");
- dlc |= 1<<1;
- pcibios_write_config_byte(piix3->bus->number, piix3->devfn,
- 0x82, dlc);
+ 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("PIIX3: Enabling Passive Release\n");
+ dlc |= 1<<1;
+ pci_write_config_byte(d, 0x82, dlc);
+ }
}
}

@@ -141,7 +129,7 @@
typedef void (*quirk_handler)(struct pci_dev *, int);

/*
- * Mpping from quirk handler functions to names.
+ * Mapping from quirk handler functions to names.
*/

struct quirk_name {
@@ -185,6 +173,7 @@
{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8891A, quirk_bridge, 0x01 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424, quirk_bridge, 0x00 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82434, quirk_bridge, 0x00 },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82430, quirk_bridge, 0x00 },
#endif
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, quirk_passive_release, 0x00 },
};
--- /usr/src/linux-2.1/drivers/pci/pci.c Fri May 1 10:43:36 1998
+++ linux/drivers/pci/pci.c Sat May 2 21:58:18 1998
@@ -1,5 +1,5 @@
/*
- * $Id: pci.c,v 1.79 1998/04/17 16:25:24 mj Exp $
+ * $Id: pci.c,v 1.84 1998/05/02 19:22:06 mj Exp $
*
* PCI Bus Services
*
@@ -68,11 +68,48 @@
}


+int
+pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val)
+{
+ return pcibios_read_config_byte(dev->bus->number, dev->devfn, where, val);
+}
+
+int
+pci_read_config_word(struct pci_dev *dev, u8 where, u16 *val)
+{
+ return pcibios_read_config_word(dev->bus->number, dev->devfn, where, val);
+}
+
+int
+pci_read_config_dword(struct pci_dev *dev, u8 where, u32 *val)
+{
+ return pcibios_read_config_dword(dev->bus->number, dev->devfn, where, val);
+}
+
+int
+pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val)
+{
+ return pcibios_write_config_byte(dev->bus->number, dev->devfn, where, val);
+}
+
+int
+pci_write_config_word(struct pci_dev *dev, u8 where, u16 val)
+{
+ return pcibios_write_config_word(dev->bus->number, dev->devfn, where, val);
+}
+
+int
+pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val)
+{
+ return pcibios_write_config_dword(dev->bus->number, dev->devfn, where, val);
+}
+
+
void
pci_set_master(struct pci_dev *dev)
{
- unsigned short cmd;
- unsigned char lat;
+ u16 cmd;
+ u8 lat;

pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (! (cmd & PCI_COMMAND_MASTER)) {
@@ -89,16 +126,43 @@
}
}

+__initfunc(void pci_read_bases(struct pci_dev *dev, unsigned int howmany))
+{
+ unsigned int reg;
+ u32 l;
+
+ for(reg=0; reg<howmany; reg++) {
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &l);
+ if (l == 0xffffffff)
+ continue;
+ dev->base_address[reg] = l;
+ if ((l & PCI_MEMORY_RANGE_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ reg++;
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &l);
+ if (l) {
+#if BITS_PER_LONG == 64
+ dev->base_address[reg-1] |= ((unsigned long) l) << 32;
+#else
+ printk("PCI: Unable to handle 64-bit address for device %02x:%02x\n",
+ dev->bus->number, dev->devfn);
+ dev->base_address[reg-1] = 0;
+#endif
+ }
+ }
+ }
+}
+

__initfunc(unsigned int pci_scan_bus(struct pci_bus *bus))
{
unsigned int devfn, l, max, class;
unsigned char cmd, irq, tmp, hdr_type, is_multi = 0;
- struct pci_dev *dev;
+ struct pci_dev *dev, **bus_last;
struct pci_bus *child;
int reg;

DBG("pci_scan_bus for bus %d\n", bus->number);
+ bus_last = &bus->devices;
max = bus->secondary;
for (devfn = 0; devfn < 0xff; ++devfn) {
if (PCI_FUNC(devfn) && !is_multi) {
@@ -111,8 +175,8 @@

pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l);
/* some broken boards return 0 if a slot is empty: */
- if (l == 0xffffffff || l == 0x00000000) {
- hdr_type = 0;
+ if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) {
+ is_multi = 0;
continue;
}

@@ -133,11 +197,12 @@
pcibios_read_config_dword(bus->number, devfn, PCI_CLASS_REVISION, &class);
class >>= 8; /* upper 3 bytes */
dev->class = class;
+ class >>= 8;
dev->hdr_type = hdr_type;

switch (hdr_type & 0x7f) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
- if (class >> 8 == PCI_CLASS_BRIDGE_PCI)
+ if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
/*
* If the card generates interrupts, read IRQ number
@@ -151,25 +216,19 @@
* read base address registers, again pcibios_fixup() can
* tweak these
*/
- for (reg = 0; reg < 6; reg++) {
- pcibios_read_config_dword(bus->number, devfn, PCI_BASE_ADDRESS_0 + (reg << 2), &l);
- dev->base_address[reg] = (l == 0xffffffff) ? 0 : l;
- }
+ pci_read_bases(dev, 6);
pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &l);
dev->rom_address = (l == 0xffffffff) ? 0 : l;
break;
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
- if (class >> 8 != PCI_CLASS_BRIDGE_PCI)
+ if (class != PCI_CLASS_BRIDGE_PCI)
goto bad;
- for (reg = 0; reg < 2; reg++) {
- pcibios_read_config_dword(bus->number, devfn, PCI_BASE_ADDRESS_0 + (reg << 2), &l);
- dev->base_address[reg] = (l == 0xffffffff) ? 0 : l;
- }
+ pci_read_bases(dev, 2);
pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS1, &l);
dev->rom_address = (l == 0xffffffff) ? 0 : l;
break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
- if (class >> 16 != PCI_BASE_CLASS_BRIDGE)
+ if (class != PCI_CLASS_BRIDGE_CARDBUS)
goto bad;
for (reg = 0; reg < 2; reg++) {
pcibios_read_config_dword(bus->number, devfn, PCI_CB_MEMORY_BASE_0 + (reg << 3), &l);
@@ -201,8 +260,8 @@
* Now insert it into the list of devices held
* by the parent bus.
*/
- dev->sibling = bus->devices;
- bus->devices = dev;
+ *bus_last = dev;
+ bus_last = &dev->sibling;

#if 0
/*
@@ -217,7 +276,7 @@
/*
* If it's a bridge, scan the bus behind it.
*/
- if (class >> 8 == PCI_CLASS_BRIDGE_PCI) {
+ if (class == PCI_CLASS_BRIDGE_PCI) {
unsigned int buses;
unsigned short cr;

--- /usr/src/linux-2.1/drivers/pci/pcisyms.c Sun Apr 19 10:14:51 1998
+++ linux/drivers/pci/pcisyms.c Sat May 2 21:56:18 1998
@@ -1,5 +1,5 @@
/*
- * $Id: pcisyms.c,v 1.4 1998/04/17 16:34:19 mj Exp $
+ * $Id: pcisyms.c,v 1.7 1998/05/02 19:20:06 mj Exp $
*
* PCI Bus Services -- Exported Symbols
*
@@ -16,6 +16,12 @@
EXPORT_SYMBOL(pcibios_write_config_byte);
EXPORT_SYMBOL(pcibios_write_config_word);
EXPORT_SYMBOL(pcibios_write_config_dword);
+EXPORT_SYMBOL(pci_read_config_byte);
+EXPORT_SYMBOL(pci_read_config_word);
+EXPORT_SYMBOL(pci_read_config_dword);
+EXPORT_SYMBOL(pci_write_config_byte);
+EXPORT_SYMBOL(pci_write_config_word);
+EXPORT_SYMBOL(pci_write_config_dword);
EXPORT_SYMBOL(pci_devices);
EXPORT_SYMBOL(pci_root);
EXPORT_SYMBOL(pci_find_class);
--- /usr/src/linux-2.1/drivers/pci/oldproc.c Sun Apr 19 10:10:55 1998
+++ linux/drivers/pci/oldproc.c Fri May 1 13:26:47 1998
@@ -1,5 +1,5 @@
/*
- * $Id: oldproc.c,v 1.10 1998/03/15 13:50:11 ecd Exp $
+ * $Id: oldproc.c,v 1.12 1998/05/01 10:58:21 mj Exp $
*
* Backward-compatible procfs interface for PCI.
*
@@ -744,7 +744,7 @@
static int sprint_dev_config(struct pci_dev *dev, char *buf, int size)
{
unsigned long base;
- unsigned int l, class_rev, bus, devfn;
+ unsigned int class_rev, bus, devfn;
unsigned short vendor, device, status;
unsigned char bist, latency, min_gnt, max_lat;
int reg, len = 0;
@@ -835,12 +835,7 @@
if (len + 40 > size) {
return -1;
}
- pcibios_read_config_dword(bus, devfn,
- PCI_BASE_ADDRESS_0 + (reg << 2), &l);
- if (l == 0xffffffff)
- base = 0;
- else
- base = l;
+ base = dev->base_address[reg];
if (!base)
continue;

@@ -863,12 +858,7 @@
case PCI_BASE_ADDRESS_MEM_TYPE_1M:
type = "20 bit"; break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
- type = "64 bit";
- /* read top 32 bit address of base addr: */
- reg += 4;
- pcibios_read_config_dword(bus, devfn, reg, &l);
- base |= ((u64) l) << 32;
- break;
+ type = "64 bit"; break;
}
len += sprintf(buf + len,
"\n %srefetchable %s memory at "
--- /usr/src/linux-2.1/include/linux/pci.h Sun Apr 19 10:15:05 1998
+++ linux/include/linux/pci.h Sat May 2 21:56:17 1998
@@ -1,5 +1,5 @@
/*
- * $Id: pci.h,v 1.67 1998/04/16 20:48:33 mj Exp $
+ * $Id: pci.h,v 1.70 1998/05/02 19:20:03 mj Exp $
*
* PCI defines and function prototypes
* Copyright 1994, Drew Eckhardt
@@ -978,6 +978,8 @@

#ifdef __KERNEL__

+#include <linux/types.h>
+
/*
* Error values that may be returned by the PCI bios.
*/
@@ -1084,13 +1086,13 @@
struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);

#define pci_present pcibios_present
-#define pci_read_config_byte(dev, where, val) pcibios_read_config_byte(dev->bus->number, dev->devfn, where, val)
-#define pci_read_config_word(dev, where, val) pcibios_read_config_word(dev->bus->number, dev->devfn, where, val)
-#define pci_read_config_dword(dev, where, val) pcibios_read_config_dword(dev->bus->number, dev->devfn, where, val)
-#define pci_write_config_byte(dev, where, val) pcibios_write_config_byte(dev->bus->number, dev->devfn, where, val)
-#define pci_write_config_word(dev, where, val) pcibios_write_config_word(dev->bus->number, dev->devfn, where, val)
-#define pci_write_config_dword(dev, where, val) pcibios_write_config_dword(dev->bus->number, dev->devfn, where, val)
-void pci_set_master(struct pci_dev *);
+int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val);
+int pci_read_config_word(struct pci_dev *dev, u8 where, u16 *val);
+int pci_read_config_dword(struct pci_dev *dev, u8 where, u32 *val);
+int pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val);
+int pci_write_config_word(struct pci_dev *dev, u8 where, u16 val);
+int pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val);
+void pci_set_master(struct pci_dev *dev);

#endif /* __KERNEL__ */
#endif /* LINUX_PCI_H */
--- /usr/src/linux-2.1/arch/i386/kernel/bios32.c Sun Apr 19 10:14:44 1998
+++ linux/arch/i386/kernel/bios32.c Sat May 2 21:24:01 1998
@@ -1,7 +1,7 @@
/*
* bios32.c - Low-Level PCI Access
*
- * $Id: bios32.c,v 1.29 1998/04/17 16:31:15 mj Exp $
+ * $Id: bios32.c,v 1.32 1998/05/02 12:03:05 davem Exp $
*
* Sponsored by
* iX Multiuser Multitasking Magazine
@@ -66,6 +66,8 @@
* and cleaned it up... Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*
* Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj]
+ *
+ * May 1, 1998 : Support for peer host bridges. [mj]
*/

#include <linux/config.h>
@@ -74,6 +76,7 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ioport.h>
+#include <linux/malloc.h>

#include <asm/page.h>
#include <asm/segment.h>
@@ -832,7 +835,10 @@
}

/*
- * Sort the device list according to PCI BIOS.
+ * Sort the device list according to PCI BIOS. Nasty hack, but since some
+ * fool forgot to define the `correct' device order in the PCI BIOS specs
+ * and we want to be (possibly bug-to-bug ;-]) compatible with older kernels
+ * which used BIOS ordering, we are bound to do this...
*/

__initfunc(void pcibios_sort(void))
@@ -924,11 +930,41 @@
}

/*
- * Arch-dependent fixups. We need to fix here base addresses, I/O
- * and memory enables and IRQ's as the PCI BIOS'es are buggy as hell.
+ * In case there are peer host bridges, scan bus behind each of them.
+ * Although several sources claim that the host bridges should have
+ * header type 1 and be assigned a bus number as for PCI2PCI bridges,
+ * the reality doesn't pass this test and the bus number is usually
+ * hard-wired to 1.
*/
+__initfunc(void pcibios_fixup_peer_bridges(void))
+{
+ struct pci_dev *dev;
+ int cnt = 0;

-__initfunc(void pcibios_fixup(void))
+ for(dev=pci_root.devices; dev; dev=dev->sibling)
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) {
+ DBG("PCI: Host bridge at %02x\n", dev->devfn);
+ if (cnt) {
+ struct pci_bus *b = kmalloc(sizeof(struct pci_bus), GFP_KERNEL);
+ memset(b, 0, sizeof(*b));
+ b->parent = &pci_root;
+ b->next = pci_root.next;
+ pci_root.next = b;
+ b->self = dev;
+ b->number = b->secondary = cnt;
+ b->subordinate = 0xff;
+ b->subordinate = pci_scan_bus(b);
+ }
+ cnt++;
+ }
+}
+
+/*
+ * Fix base addresses, I/O and memory enables and IRQ's (mostly work-arounds
+ * for buggy PCI BIOS'es :-[).
+ */
+
+__initfunc(void pcibios_fixup_devices(void))
{
struct pci_dev *dev;
int i, has_io, has_mem;
@@ -991,6 +1027,16 @@
if (dev->irq >= NR_IRQS)
dev->irq = 0;
}
+}
+
+/*
+ * Arch-dependent fixups.
+ */
+
+__initfunc(void pcibios_fixup(void))
+{
+ pcibios_fixup_peer_bridges();
+ pcibios_fixup_devices();

#ifdef CONFIG_PCI_BIOS
if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu

\
 
 \ /
  Last update: 2005-03-22 13:42    [W:0.044 / U:0.352 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site