patch-2.1.121 linux/arch/alpha/kernel/bios32.c
Next file: linux/arch/alpha/kernel/bios32.h
Previous file: linux/arch/alpha/kernel/Makefile
Back to the patch index
Back to the overall index
- Lines: 507
- Date:
Sun Sep 6 10:34:33 1998
- Orig file:
v2.1.120/linux/arch/alpha/kernel/bios32.c
- Orig date:
Tue Aug 18 22:02:02 1998
diff -u --recursive --new-file v2.1.120/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
@@ -23,6 +23,7 @@
* Manuals are $25 each or $50 for all three, plus $7 shipping
* within the United States, $35 abroad.
*/
+
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/tasks.h>
@@ -38,6 +39,7 @@
#include "bios32.h"
#define DEBUG_DEVS 0
+#define DEBUG_HOSE 0
#if DEBUG_DEVS
# define DBG_DEVS(args) printk args
@@ -45,6 +47,12 @@
# define DBG_DEVS(args)
#endif
+#if DEBUG_HOSE
+# define DBG_HOSE(args) printk args
+#else
+# define DBG_HOSE(args)
+#endif
+
#ifndef CONFIG_PCI
asmlinkage int sys_pciconfig_read() { return -ENOSYS; }
@@ -68,21 +76,28 @@
#define MAJOR_REV 0
#define MINOR_REV 4 /* minor revision 4, add multi-PCI handling */
+struct linux_hose_info *bus2hose[256];
+struct linux_hose_info *hose_head, **hose_tail = &hose_head;
+int hose_count;
+int pci_probe_enabled;
+
+static void layout_hoses(void);
int
pcibios_present(void)
{
- return alpha_mv.pci_read_config_byte != NULL;
+ return alpha_mv.hose_read_config_byte != NULL;
}
void __init
pcibios_init(void)
{
- printk("Alpha PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
+ if (!pcibios_present())
+ return;
+
+ printk("Alpha PCI BIOS32 revision %d.%02d\n", MAJOR_REV, MINOR_REV);
if (alpha_use_srm_setup)
printk(" NOT modifying existing (SRM) PCI configuration\n");
-
- /* FIXME: Scan for multiple PCI busses here. */
}
char * __init
@@ -106,8 +121,11 @@
pcibios_read_config_byte (u8 bus, u8 dev, u8 where, u8 *value)
{
int r = PCIBIOS_FUNC_NOT_SUPPORTED;
- if (alpha_mv.pci_read_config_byte)
- r = alpha_mv.pci_read_config_byte(bus, dev, where, value);
+ *value = 0xff;
+ if (alpha_mv.hose_read_config_byte) {
+ r = (alpha_mv.hose_read_config_byte
+ (bus, dev, where, value, bus2hose[bus]));
+ }
return r;
}
@@ -115,8 +133,13 @@
pcibios_read_config_word (u8 bus, u8 dev, u8 where, u16 *value)
{
int r = PCIBIOS_FUNC_NOT_SUPPORTED;
- if (alpha_mv.pci_read_config_word)
- r = alpha_mv.pci_read_config_word(bus, dev, where, value);
+ *value = 0xffff;
+ if (alpha_mv.hose_read_config_word) {
+ r = PCIBIOS_BAD_REGISTER_NUMBER;
+ if (!(where & 1))
+ r = (alpha_mv.hose_read_config_word
+ (bus, dev, where, value, bus2hose[bus]));
+ }
return r;
}
@@ -124,8 +147,13 @@
pcibios_read_config_dword (u8 bus, u8 dev, u8 where, u32 *value)
{
int r = PCIBIOS_FUNC_NOT_SUPPORTED;
- if (alpha_mv.pci_read_config_dword)
- r = alpha_mv.pci_read_config_dword(bus, dev, where, value);
+ *value = 0xffffffff;
+ if (alpha_mv.hose_read_config_dword) {
+ r = PCIBIOS_BAD_REGISTER_NUMBER;
+ if (!(where & 3))
+ r = (alpha_mv.hose_read_config_dword
+ (bus, dev, where, value, bus2hose[bus]));
+ }
return r;
}
@@ -133,8 +161,10 @@
pcibios_write_config_byte (u8 bus, u8 dev, u8 where, u8 value)
{
int r = PCIBIOS_FUNC_NOT_SUPPORTED;
- if (alpha_mv.pci_write_config_byte)
- r = alpha_mv.pci_write_config_byte(bus, dev, where, value);
+ if (alpha_mv.hose_write_config_byte) {
+ r = (alpha_mv.hose_write_config_byte
+ (bus, dev, where, value, bus2hose[bus]));
+ }
return r;
}
@@ -142,8 +172,12 @@
pcibios_write_config_word (u8 bus, u8 dev, u8 where, u16 value)
{
int r = PCIBIOS_FUNC_NOT_SUPPORTED;
- if (alpha_mv.pci_write_config_word)
- r = alpha_mv.pci_write_config_word(bus, dev, where, value);
+ if (alpha_mv.hose_write_config_word) {
+ r = PCIBIOS_BAD_REGISTER_NUMBER;
+ if (!(where & 1))
+ r = (alpha_mv.hose_write_config_word
+ (bus, dev, where, value, bus2hose[bus]));
+ }
return r;
}
@@ -151,8 +185,12 @@
pcibios_write_config_dword (u8 bus, u8 dev, u8 where, u32 value)
{
int r = PCIBIOS_FUNC_NOT_SUPPORTED;
- if (alpha_mv.pci_write_config_dword)
- r = alpha_mv.pci_write_config_dword(bus, dev, where, value);
+ if (alpha_mv.hose_write_config_dword) {
+ r = PCIBIOS_BAD_REGISTER_NUMBER;
+ if (!(where & 3))
+ r = (alpha_mv.hose_write_config_dword
+ (bus, dev, where, value, bus2hose[bus]));
+ }
return r;
}
@@ -171,31 +209,23 @@
if (!pcibios_present())
return -ENOSYS;
- lock_kernel();
switch (len) {
case 1:
err = pcibios_read_config_byte(bus, dfn, off, &ubyte);
- if (err != PCIBIOS_SUCCESSFUL)
- ubyte = 0xff;
put_user(ubyte, buf);
break;
case 2:
err = pcibios_read_config_word(bus, dfn, off, &ushort);
- if (err != PCIBIOS_SUCCESSFUL)
- ushort = 0xffff;
put_user(ushort, (unsigned short *)buf);
break;
case 4:
err = pcibios_read_config_dword(bus, dfn, off, &uint);
- if (err != PCIBIOS_SUCCESSFUL)
- uint = 0xffffffff;
put_user(uint, (unsigned int *)buf);
break;
default:
err = -EINVAL;
break;
}
- unlock_kernel();
return err;
}
@@ -214,7 +244,6 @@
if (!pcibios_present())
return -ENOSYS;
- lock_kernel();
switch (len) {
case 1:
err = get_user(ubyte, buf);
@@ -247,7 +276,6 @@
err = -EINVAL;
break;
}
- unlock_kernel();
return err;
}
@@ -256,8 +284,6 @@
* Gory details start here...
*/
-struct linux_hose_info *bus2hose[256];
-
/*
* Align VAL to ALIGN, which must be a power of two.
*/
@@ -715,18 +741,7 @@
{
struct pci_bus *cur;
-#if defined(CONFIG_ALPHA_GENERIC)
- static struct linux_hose_info dummy_hose;
- int i;
-
- /*
- * HACK: Emulate a multi-bus machine to a limited extent
- * by initializing bus2hose to point to something that
- * has pci_hose_index & pci_first_busno zero.
- */
- for (i = 0; i <= 0xff; i++)
- bus2hose[i] = &dummy_hose;
-#endif
+ layout_hoses();
/*
* Scan the tree, allocating PCI memory and I/O space.
@@ -869,6 +884,18 @@
continue;
/*
+ * We don't have code that will init the CYPRESS bridge
+ * correctly so we do the next best thing, and depend on
+ * the previous console code to do the right thing, and
+ * ignore it here... :-\
+ */
+ if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
+ dev->device == PCI_DEVICE_ID_CONTAQ_82C693) {
+ DBG_DEVS(("common_pci_fixup: ignoring CYPRESS bridge...\n"));
+ continue;
+ }
+
+ /*
* This device is not on the primary bus, we need
* to figure out which interrupt pin it will come
* in on. We know which slot it will come in on
@@ -997,4 +1024,259 @@
/* The slot is the slot of the last bridge. */
return PCI_SLOT(dev->devfn);
}
+
+/*
+ * On multiple bus machines, in order to cope with a somewhat deficient
+ * API, we must map the 8-bit bus identifier so that it is unique across
+ * multiple interfaces (hoses). At the same time we do this, chain the
+ * other hoses off of pci_root so that they will be found during normal
+ * PCI probing and layout.
+ */
+
+#define PRIMARY(b) ((b)&0xff)
+#define SECONDARY(b) (((b)>>8)&0xff)
+#define SUBORDINATE(b) (((b)>>16)&0xff)
+
+static int __init
+hose_scan_bridges(struct linux_hose_info *hose, unsigned char bus)
+{
+ unsigned int devfn, l, class;
+ unsigned char hdr_type = 0;
+ unsigned int found = 0;
+
+ for (devfn = 0; devfn < 0xff; ++devfn) {
+ if (PCI_FUNC(devfn) == 0) {
+ alpha_mv.hose_read_config_byte(bus, devfn,
+ PCI_HEADER_TYPE,
+ &hdr_type, hose);
+ } else if (!(hdr_type & 0x80)) {
+ /* not a multi-function device */
+ continue;
+ }
+
+ /* Check if there is anything here. */
+ alpha_mv.hose_read_config_dword(bus, devfn, PCI_VENDOR_ID,
+ &l, hose);
+ if (l == 0xffffffff || l == 0x00000000) {
+ hdr_type = 0;
+ continue;
+ }
+
+ /* See if this is a bridge device. */
+ alpha_mv.hose_read_config_dword(bus, devfn, PCI_CLASS_REVISION,
+ &class, hose);
+
+ if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+ unsigned int busses;
+
+ found++;
+
+ alpha_mv.hose_read_config_dword(bus, devfn,
+ PCI_PRIMARY_BUS,
+ &busses, hose);
+
+ DBG_HOSE(("hose_scan_bridges: hose %d bus %d "
+ "slot %d busses 0x%x\n",
+ hose->pci_hose_index, bus, PCI_SLOT(devfn),
+ busses));
+
+ /*
+ * Do something with first_busno and last_busno
+ */
+ if (hose->pci_first_busno > PRIMARY(busses)) {
+ hose->pci_first_busno = PRIMARY(busses);
+ DBG_HOSE(("hose_scan_bridges: hose %d bus %d "
+ "slot %d change first to %d\n",
+ hose->pci_hose_index, bus,
+ PCI_SLOT(devfn), PRIMARY(busses)));
+ }
+ if (hose->pci_last_busno < SUBORDINATE(busses)) {
+ hose->pci_last_busno = SUBORDINATE(busses);
+ DBG_HOSE(("hose_scan_bridges: hose %d bus %d "
+ "slot %d change last to %d\n",
+ hose->pci_hose_index, bus,
+ PCI_SLOT(devfn),
+ SUBORDINATE(busses)));
+ }
+ /*
+ * Now scan everything underneath the bridge.
+ */
+ hose_scan_bridges(hose, SECONDARY(busses));
+ }
+ }
+ return found;
+}
+
+static void __init
+hose_reconfigure_bridges(struct linux_hose_info *hose, unsigned char bus)
+{
+ unsigned int devfn, l, class;
+ unsigned char hdr_type = 0;
+
+ for (devfn = 0; devfn < 0xff; ++devfn) {
+ if (PCI_FUNC(devfn) == 0) {
+ alpha_mv.hose_read_config_byte(bus, devfn,
+ PCI_HEADER_TYPE,
+ &hdr_type, hose);
+ } else if (!(hdr_type & 0x80)) {
+ /* not a multi-function device */
+ continue;
+ }
+
+ /* Check if there is anything here. */
+ alpha_mv.hose_read_config_dword(bus, devfn, PCI_VENDOR_ID,
+ &l, hose);
+ if (l == 0xffffffff || l == 0x00000000) {
+ hdr_type = 0;
+ continue;
+ }
+
+ /* See if this is a bridge device. */
+ alpha_mv.hose_read_config_dword(bus, devfn, PCI_CLASS_REVISION,
+ &class, hose);
+
+ if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+ unsigned int busses;
+
+ alpha_mv.hose_read_config_dword(bus, devfn,
+ PCI_PRIMARY_BUS,
+ &busses, hose);
+
+ /*
+ * First reconfigure everything underneath the bridge.
+ */
+ hose_reconfigure_bridges(hose, (busses >> 8) & 0xff);
+
+ /*
+ * Unconfigure this bridges bus numbers,
+ * pci_scan_bus() will fix this up properly.
+ */
+ busses &= 0xff000000;
+ alpha_mv.hose_write_config_dword(bus, devfn,
+ PCI_PRIMARY_BUS,
+ busses, hose);
+ }
+ }
+}
+
+static void __init
+hose_fixup_busno(struct linux_hose_info *hose, unsigned char bus)
+{
+ int nbus;
+
+ /*
+ * First, scan for all bridge devices underneath this hose,
+ * to determine the first and last busnos.
+ */
+ DBG_HOSE(("hose_fixup_busno: before hose_scan_bridges()\n"));
+
+ if (!hose_scan_bridges(hose, 0)) {
+ /* none found, exit */
+ hose->pci_first_busno = bus;
+ hose->pci_last_busno = bus;
+ } else {
+ /*
+ * Reconfigure all bridge devices underneath this hose.
+ */
+ DBG_HOSE(("hose_fixup_busno: before hose_reconfigure_bridges\n"));
+ hose_reconfigure_bridges(hose, hose->pci_first_busno);
+ }
+
+ /*
+ * Now reconfigure the hose to it's new bus number and set up
+ * our bus2hose mapping for this hose.
+ */
+ nbus = hose->pci_last_busno - hose->pci_first_busno;
+
+ hose->pci_first_busno = bus;
+
+ DBG_HOSE(("hose_fixup_busno: hose %d startbus %d nbus %d\n",
+ hose->pci_hose_index, bus, nbus));
+
+ do {
+ bus2hose[bus++] = hose;
+ } while (nbus-- > 0);
+ DBG_HOSE(("hose_fixup_busno: returning...\n"));
+}
+
+static void __init
+layout_one_hose(struct linux_hose_info *hose)
+{
+ static struct pci_bus *pchain = NULL;
+ struct pci_bus *pbus = &hose->pci_bus;
+ static unsigned char busno = 0;
+
+ DBG_HOSE(("layout_one_hose: entry\n"));
+
+ /*
+ * Hoses include child PCI bridges in bus-range property,
+ * but we don't scan each of those ourselves, Linux generic PCI
+ * probing code will find child bridges and link them into this
+ * hose's root PCI device hierarchy.
+ */
+
+ pbus->number = pbus->secondary = busno;
+ pbus->sysdata = hose;
+
+ DBG_HOSE(("layout_one_hose: before hose_fixup_busno()\n"));
+
+ hose_fixup_busno(hose, busno);
+
+ DBG_HOSE(("layout_one_hose: before pci_scan_bus()\n"));
+
+ pbus->subordinate = pci_scan_bus(pbus); /* the original! */
+
+ /*
+ * Set the maximum subordinate bus of this hose.
+ */
+ hose->pci_last_busno = pbus->subordinate;
+#if 0
+ alpha_mv.hose_write_config_byte(busno, 0, 0x41, hose->pci_last_busno,
+ hose);
+#endif
+ busno = pbus->subordinate + 1;
+
+ /*
+ * Fixup the chain of primary PCI busses.
+ */
+ if (pchain) {
+ pchain->next = &hose->pci_bus;
+ pchain = pchain->next;
+ } else {
+ pchain = &pci_root;
+ memcpy(pchain, &hose->pci_bus, sizeof(pci_root));
+ }
+ DBG_HOSE(("layout_one_hose: returning...\n"));
+}
+
+static void __init
+layout_hoses(void)
+{
+ struct linux_hose_info * hose;
+ int i;
+
+ /* On multiple bus machines, we play games with pci_root in order
+ that all of the busses are probed as part of the normal PCI
+ setup. The existance of the busses was determined in init_arch. */
+
+ if (hose_head) {
+ /* Multi-bus machines did not yet wish to allow bus
+ accesses. We now do our own thing after the normal
+ pci_scan_bus is over. This mechanism is relatively
+ broken but will be fixed later. */
+ pci_probe_enabled = 1;
+
+ for (hose = hose_head; hose; hose = hose->next)
+ layout_one_hose(hose);
+ } else {
+ /* For the benefit of single-bus machines, emulate a
+ multi-bus machine to the (limited) extent necessary.
+ Init all bus2hose entries to point to a dummy. */
+ hose = kmalloc(sizeof(*hose), GFP_KERNEL);
+ memset(hose, 0, sizeof(*hose));
+ for (i = 0; i < 256; ++i)
+ bus2hose[i] = hose;
+ }
+}
+
#endif /* CONFIG_PCI */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov