Messages in this thread Patch in this message | | | Date | Sat, 2 May 1998 22:14:17 +0200 | From | Martin Mares <> | Subject | More 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
| |