lkml.org 
[lkml]   [1998]   [May]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: Dual PCI bus support
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

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