Re: Dual PCI bus support

Martin Mares (mj@atrey.karlin.mff.cuni.cz)
Fri, 1 May 1998 15:19:27 +0200


Hi,

> Works fine for me as well. btw, I found the secondary PCI bus number on the
> Micron to be hardwired to 1.

I was thinking about the host bridges and their header types a bit more
and I guess they really don't have header type 1 and the bus number is
hardwired (both in Micron Samurai and Intel Orion chipsets). This patch
should handle it in a much cleaner way, can you test it, please?

Have a nice fortnight

-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
"Spelling checkers at maximum!  Fire!"

--- linux-2.1.98/include/linux/pci.h Sat Apr 18 06:58:48 1998 +++ linux/include/linux/pci.h Fri May 1 15:02:04 1998 @@ -1,5 +1,5 @@ /* - * $Id: pci.h,v 1.67 1998/04/16 20:48:33 mj Exp $ + * $Id: pci.h,v 1.69 1998/05/01 11:03:00 mj Exp $ * * PCI defines and function prototypes * Copyright 1994, Drew Eckhardt @@ -1084,13 +1084,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 */ diff -ruN linux-2.1.98/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c --- linux-2.1.98/arch/i386/kernel/bios32.c Sat Apr 18 06:58:48 1998 +++ linux/arch/i386/kernel/bios32.c Fri May 1 14:53:23 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.30 1998/05/01 10:57:28 mj 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> @@ -832,7 +834,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 +929,40 @@ } /* - * 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); + } + } +} + +/* + * 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; @@ -973,7 +1007,10 @@ int irq; unsigned char pin; - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (dev->bus == &pci_root) + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + else /* Behind PCI-to-PCI bridge => use interrupt pin of the bridge */ + pci_read_config_byte(dev->bus->self, PCI_INTERRUPT_PIN, &pin); if (pin) { pin--; /* interrupt pins are numbered starting from 1 */ irq = IO_APIC_get_PCI_irq_vector (dev->bus->number, PCI_SLOT(dev->devfn), pin); @@ -991,6 +1028,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)) diff -ruN linux-2.1.98/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c --- linux-2.1.98/drivers/pci/oldproc.c Thu Apr 9 22:16:18 1998 +++ linux/drivers/pci/oldproc.c Fri May 1 14:57:57 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 " diff -ruN linux-2.1.98/drivers/pci/pci.c linux/drivers/pci/pci.c --- linux-2.1.98/drivers/pci/pci.c Fri May 1 15:06:28 1998 +++ linux/drivers/pci/pci.c Fri May 1 14:57:59 1998 @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.79 1998/04/17 16:25:24 mj Exp $ + * $Id: pci.c,v 1.82 1998/05/01 11:02:56 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) { @@ -112,7 +176,7 @@ 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; + 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; diff -ruN linux-2.1.98/drivers/pci/pcisyms.c linux/drivers/pci/pcisyms.c --- linux-2.1.98/drivers/pci/pcisyms.c Sat Apr 18 06:58:48 1998 +++ linux/drivers/pci/pcisyms.c Fri May 1 14:58:00 1998 @@ -1,11 +1,12 @@ /* - * $Id: pcisyms.c,v 1.4 1998/04/17 16:34:19 mj Exp $ + * $Id: pcisyms.c,v 1.5 1998/05/01 11:05:16 mj Exp $ * * PCI Bus Services -- Exported Symbols * * Copyright 1998 Martin Mares */ +#include <linux/types.h> #include <linux/module.h> #include <linux/pci.h>

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