summaryrefslogtreecommitdiffstats
path: root/arch/alpha/kernel/bios32.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/alpha/kernel/bios32.c')
-rw-r--r--arch/alpha/kernel/bios32.c478
1 files changed, 478 insertions, 0 deletions
diff --git a/arch/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c
new file mode 100644
index 000000000..37c566fb1
--- /dev/null
+++ b/arch/alpha/kernel/bios32.c
@@ -0,0 +1,478 @@
+#define DEBUG
+/*
+ * bios32.c - PCI BIOS functions for Alpha systems not using BIOS
+ * emulation code.
+ *
+ * Written by Dave Rusling (david.rusling@reo.mts.dec.com)
+ *
+ * Adapted to 64-bit kernel and then rewritten by David Mosberger
+ * (davidm@cs.arizona.edu)
+ *
+ * For more information, please consult
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000
+ * +1 (800) 433-5177
+ *
+ * Manuals are $25 each or $50 for all three, plus $7 shipping
+ * within the United States, $35 abroad.
+ */
+#include <linux/config.h>
+
+#ifndef CONFIG_PCI
+
+int pcibios_present(void)
+{
+ return 0;
+}
+
+#else /* CONFIG_PCI */
+
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <asm/hwrpb.h>
+#include <asm/io.h>
+
+
+#define KB 1024
+#define MB (1024*KB)
+#define GB (1024*MB)
+
+#define MAJOR_REV 0
+#define MINOR_REV 2
+
+/*
+ * Align VAL to ALIGN, which must be a power of two.
+ */
+#define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
+
+
+/*
+ * Temporary internal macro. If this 0, then do not write to any of
+ * the PCI registers, merely read them (i.e., use configuration as
+ * determined by SRM). The SRM seem do be doing a less than perfect
+ * job in configuring PCI devices, so for now we do it ourselves.
+ * Reconfiguring PCI devices breaks console (RPB) callbacks, but
+ * those don't work properly with 64 bit addresses anyways.
+ *
+ * The accepted convention seems to be that the console (POST
+ * software) should fully configure boot devices and configure the
+ * interrupt routing of *all* devices. In particular, the base
+ * addresses of non-boot devices need not be initialized. For
+ * example, on the AXPpci33 board, the base address a #9 GXE PCI
+ * graphics card reads as zero (this may, however, be due to a bug in
+ * the graphics card---there have been some rumor that the #9 BIOS
+ * incorrectly resets that address to 0...).
+ */
+#define PCI_MODIFY 1
+
+
+extern struct hwrpb_struct *hwrpb;
+
+
+#if PCI_MODIFY
+
+static unsigned int io_base = 64*KB; /* <64KB are (E)ISA ports */
+static unsigned int mem_base = 16*MB; /* <16MB is ISA memory */
+
+
+/*
+ * Layout memory and I/O for a device:
+ */
+static void layout_dev(struct pci_dev *dev)
+{
+ struct pci_bus *bus;
+ unsigned short cmd;
+ unsigned int base, mask, size, reg;
+
+ bus = dev->bus;
+ pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
+
+ for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ /*
+ * Figure out how much space and of what type this
+ * device wants.
+ */
+ pcibios_write_config_dword(bus->number, dev->devfn, reg,
+ 0xffffffff);
+ pcibios_read_config_dword(bus->number, dev->devfn, reg, &base);
+ if (!base) {
+ break; /* done with this device */
+ }
+ /*
+ * We've read the base address register back after
+ * writing all ones and so now we must decode it.
+ */
+ if (base & PCI_BASE_ADDRESS_SPACE_IO) {
+ /*
+ * I/O space base address register.
+ */
+ cmd |= PCI_COMMAND_IO;
+
+ base &= PCI_BASE_ADDRESS_IO_MASK;
+ mask = (~base << 1) | 0x1;
+ size = (mask & base) & 0xffffffff;
+ base = ALIGN(io_base, size);
+ io_base = base + size;
+ pcibios_write_config_dword(bus->number, dev->devfn,
+ reg, base | 0x1);
+ } else {
+ unsigned int type;
+ /*
+ * Memory space base address register.
+ */
+ cmd |= PCI_COMMAND_MEMORY;
+
+ type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ mask = (~base << 1) | 0x1;
+ size = (mask & base) & 0xffffffff;
+ switch (type) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ break;
+
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ printk("bios32 WARNING: "
+ "ignoring 64-bit device in "
+ "slot %d, function %d: \n",
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ reg += 4; /* skip extra 4 bytes */
+ continue;
+
+ case PCI_BASE_ADDRESS_MEM_TYPE_1M:
+ /*
+ * Allocating memory below 1MB is *very*
+ * tricky, as there may be all kinds of
+ * ISA devices lurking that we don't know
+ * about. For now, we just cross fingers
+ * and hope nobody tries to do this on an
+ * Alpha (or that the console has set it
+ * up properly).
+ */
+ printk("bios32 WARNING: slot %d, function %d "
+ "requests memory below 1MB---don't "
+ "know how to do that.\n",
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ continue;
+ }
+ /*
+ * The following holds at least for the Low Cost
+ * Alpha implementation of the PCI interface:
+ *
+ * In sparse memory address space, the first
+ * octant (16MB) of every 128MB segment is
+ * aliased to the the very first 16MB of the
+ * address space (i.e., it aliases the ISA
+ * memory address space). Thus, we try to
+ * avoid allocating PCI devices in that range.
+ * Can be allocated in 2nd-7th octant only.
+ * Devices that need more than 112MB of
+ * address space must be accessed through
+ * dense memory space only!
+ */
+ base = ALIGN(mem_base, size);
+ if (size > 7 * 16*MB) {
+ printk("bios32 WARNING: slot %d, function %d "
+ "requests %dB of contiguous address "
+ " space---don't use sparse memory "
+ " accesses on this device!!\n",
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), size);
+ } else {
+ if (((base / 16*MB) & 0x7) == 0) {
+ base &= ~(128*MB - 1);
+ base += 16*MB;
+ base = ALIGN(base, size);
+ }
+ if (base / 128*MB != (base + size) / 128*MB) {
+ base &= ~(128*MB - 1);
+ base += (128 + 16)*MB;
+ base = ALIGN(base, size);
+ }
+ }
+ mem_base = base + size;
+ pcibios_write_config_dword(bus->number, dev->devfn,
+ reg, base);
+ }
+ }
+ /* enable device: */
+ pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND,
+ cmd | PCI_COMMAND_MASTER);
+}
+
+
+static void layout_bus(struct pci_bus *bus)
+{
+ unsigned int l, tio, bio, tmem, bmem;
+ struct pci_bus *child;
+ struct pci_dev *dev;
+
+ if (!bus->devices && !bus->children)
+ return;
+
+ /*
+ * Align the current bases on appropriate boundaries (4K for
+ * IO and 1MB for memory).
+ */
+ bio = io_base = ALIGN(io_base, 4*KB);
+ bmem = mem_base = ALIGN(mem_base, 1*MB);
+
+ /*
+ * Allocate space to each device:
+ */
+ for (dev = bus->devices; dev; dev = dev->sibling) {
+ if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) {
+ layout_dev(dev);
+ }
+ }
+ /*
+ * Recursively allocate space for all of the sub-buses:
+ */
+ for (child = bus->children; child; child = child->next) {
+ layout_bus(child);
+ }
+ /*
+ * Align the current bases on 4K and 1MB boundaries:
+ */
+ tio = io_base = ALIGN(io_base, 4*KB);
+ tmem = mem_base = ALIGN(mem_base, 1*MB);
+
+ if (bus->self) {
+ struct pci_dev *bridge = bus->self;
+ /*
+ * Set up the top and bottom of the I/O memory segment
+ * for this bus.
+ */
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ 0x1c, &l);
+ l = l | (bio >> 8) | ((tio - 1) & 0xf000);
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x1c, l);
+
+ l = ((bmem & 0xfff00000) >> 16) | ((tmem - 1) & 0xfff00000);
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x20, l);
+ /*
+ * Turn off downstream PF memory address range:
+ */
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x24, 0x0000ffff);
+ /*
+ * Tell bridge that there is an ISA bus in the system:
+ */
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x3c, 0x00040000);
+ /*
+ * Clear status bits, enable I/O (for downstream I/O),
+ * turn on master enable (for upstream I/O), turn on
+ * memory enable (for downstream memory), turn on
+ * master enable (for upstream memory and I/O).
+ */
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x4, 0xffff0007);
+ }
+}
+
+#endif /* !PCI_MODIFY */
+
+
+/*
+ * Given the vendor and device ids, find the n'th instance of that device
+ * in the system.
+ */
+int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+ unsigned short index, unsigned char *bus,
+ unsigned char *devfn)
+{
+ unsigned int current = 0;
+ struct pci_dev *dev;
+
+ for (dev = pci_devices; dev; dev = dev->next) {
+ if (dev->vendor == vendor && dev->device == device_id) {
+ if (current == index) {
+ *devfn = dev->devfn;
+ *bus = dev->bus->number;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++current;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+/*
+ * Given the class, find the n'th instance of that device
+ * in the system.
+ */
+int pcibios_find_class (unsigned int class_code, unsigned short index,
+ unsigned char *bus, unsigned char *devfn)
+{
+ unsigned int current = 0;
+ struct pci_dev *dev;
+
+ for (dev = pci_devices; dev; dev = dev->next) {
+ if (dev->class == class_code) {
+ if (current == index) {
+ *devfn = dev->devfn;
+ *bus = dev->bus->number;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++current;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+int pcibios_present(void)
+{
+ return 1;
+}
+
+
+unsigned long pcibios_init(unsigned long mem_start,
+ unsigned long mem_end)
+{
+ printk("Alpha PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
+
+#if !PCI_MODIFY
+ printk("...NOT modifying existing (SRM) PCI configuration\n");
+#endif
+ return mem_start;
+}
+
+
+/*
+ * Fixup configuration for Noname boards (AXPpci33).
+ */
+static void noname_fixup(void)
+{
+ struct pci_dev *dev;
+
+ /*
+ * The Noname board has 5 PCI slots with each of the 4
+ * interrupt pins routed to different pins on the PCI/ISA
+ * bridge (PIRQ0-PIRQ3). I don't have any information yet as
+ * to how INTB, INTC, and INTD get routed (4/12/95,
+ * davidm@cs.arizona.edu).
+ */
+ static const char pirq_tab[5][4] = {
+ { 3, -1, -1, -1}, /* slot 6 (53c810) */
+ {-1, -1, -1, -1}, /* slot 7 (PCI/ISA bridge) */
+ { 2, -1, -1, -1}, /* slot 8 (slot closest to ISA) */
+ { 1, -1, -1, -1}, /* slot 9 (middle slot) */
+ { 0, -1, -1, -1}, /* slot 10 (slot furthest from ISA) */
+ };
+ /*
+ * route_tab selects irq routing in PCI/ISA bridge so that:
+ * PIRQ0 -> irq 15
+ * PIRQ1 -> irq 9
+ * PIRQ2 -> irq 10
+ * PIRQ3 -> irq 11
+ */
+ const unsigned int route_tab = 0x0b0a090f;
+ unsigned char pin;
+ int pirq;
+
+ pcibios_write_config_dword(0, PCI_DEVFN(7, 0), 0x60, route_tab);
+
+ /* ensure irq 9, 10, 11, and 15 are level sensitive: */
+ outb((1<<(9-8)) | (1<<(10-8)) | (1<<(11-8)) | (1<<(15-8)), 0x4d1);
+
+ /*
+ * Go through all devices, fixing up irqs as we see fit:
+ */
+ for (dev = pci_devices; dev; dev = dev->next) {
+ dev->irq = 0;
+ if (dev->bus->number != 0 ||
+ PCI_SLOT(dev->devfn) < 6 || PCI_SLOT(dev->devfn) > 10)
+ {
+ printk("noname_set_irq: no dev on bus %d, slot %d!!\n",
+ dev->bus->number, PCI_SLOT(dev->devfn));
+ continue;
+ }
+
+ pcibios_read_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_PIN, &pin);
+ if (!pin) {
+ if (dev->vendor == PCI_VENDOR_ID_S3 &&
+ (dev->device == PCI_DEVICE_ID_S3_864_1 ||
+ dev->device == PCI_DEVICE_ID_S3_864_2))
+ {
+ pin = 1;
+ } else {
+ continue; /* no interrupt line */
+ }
+ }
+ pirq = pirq_tab[PCI_SLOT(dev->devfn) - 6][pin - 1];
+ if (pirq < 0) {
+ continue;
+ }
+ dev->irq = (route_tab >> (8 * pirq)) & 0xff;
+#if PCI_MODIFY
+ /* tell the device: */
+ pcibios_write_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_LINE, dev->irq);
+#endif
+ }
+
+#if PCI_MODIFY
+ {
+ unsigned char hostid;
+ /*
+ * SRM console version X3.9 seems to reset the SCSI
+ * host-id to 0 no matter what console environment
+ * variable pka0_host_id is set to. Thus, if the
+ * host-id reads out as a zero, we set it to 7. The
+ * SCSI controller is on the motherboard on bus 0,
+ * slot 6
+ */
+ if (pcibios_read_config_byte(0, PCI_DEVFN(6, 0), 0x84, &hostid)
+ == PCIBIOS_SUCCESSFUL && (hostid == 0))
+ {
+ pcibios_write_config_byte(0, PCI_DEVFN(6, 0),
+ 0x84, 7);
+ }
+ }
+#endif /* !PCI_MODIFY */
+}
+
+
+unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
+{
+#if PCI_MODIFY
+ /*
+ * Scan the tree, allocating PCI memory and I/O space.
+ */
+ layout_bus(&pci_root);
+#endif
+
+ /*
+ * Now is the time to do all those dirty little deeds...
+ */
+ switch (hwrpb->sys_type) {
+ case ST_DEC_AXPPCI_33: noname_fixup(); break;
+
+ default:
+ printk("pcibios_fixup: don't know how to fixup sys type %ld\n",
+ hwrpb->sys_type);
+ break;
+ }
+ return mem_start;
+}
+
+#endif /* CONFIG_PCI */