linux-mips
[Top] [All Lists]

[PATCH/RFC]: SGI Octane (IP30) Patches, Part two, Octane core

To: Linux MIPS List <linux-mips@linux-mips.org>
Subject: [PATCH/RFC]: SGI Octane (IP30) Patches, Part two, Octane core
From: Kumba <kumba@gentoo.org>
Date: Thu, 31 Jul 2008 01:24:04 -0400
Original-recipient: rfc822;linux-mips@linux-mips.org
Sender: linux-mips-bounce@linux-mips.org
User-agent: Thunderbird 2.0.0.16 (Windows/20080708)

The second part is the actual IP30 Patch that makes these beasts boot. Assuming you've already lit incense candles and sacrificed a PC to the MIPS Gods above.

There's one change that probably needs good scrutiny, as it changes a value in dma-default.c, and this'll affect other systems:

diff -Naurp linux-2.6.26.orig/arch/mips/mm/dma-default.c linux-2.6.26/arch/mips/mm/dma-default.c --- linux-2.6.26.orig/arch/mips/mm/dma-default.c 2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/mm/dma-default.c     2008-07-25 03:14:40.000000000 
-0400
@@ -209,7 +209,7 @@ dma_addr_t dma_map_page(struct device *d
                dma_cache_wback_inv(addr, size);
        }

-       return plat_map_dma_mem_page(dev, page) + offset;
+       return plat_map_dma_mem_page(dev, page, size) + offset;
 }


It's used because IP30's implementation of plat_map_dma_mem_page passes the 'size' var to a few functions, ultimately leading to IP30's version of pdev_to_baddr, where it checks to make sure we're not above a certain memory limit (2GB) that could cause problems with DMA & PCI (I think - it's been awhile).

That's just one example, though. There's probably more, but I've mostly done forward ports, and haven't really messed with re-writing much. Hence why I'd like to ask others to look, poke, prod, compile, and boot, and see if they have other suggestions for improving and fixing this up.

Thanks!,


--Kumba

--
Unofficial Gentoo/MIPS Hermit & Kernel Monkey

"The past tempts us, the present confuses us, the future frightens us. And our lives slip away, moment by moment, lost in that vast, terrible in-between."

--Emperor Turhan, Centauri Republic
diff -Naurp linux-2.6.26.orig/arch/mips/Kconfig linux-2.6.26/arch/mips/Kconfig
--- linux-2.6.26.orig/arch/mips/Kconfig 2008-07-25 01:03:02.000000000 -0400
+++ linux-2.6.26/arch/mips/Kconfig      2008-07-25 03:15:09.000000000 -0400
@@ -449,6 +449,31 @@ config SGI_IP28
         This is the SGI Indigo2 with R10000 processor.  To compile a Linux
         kernel that runs on these, say Y here.
 
+config SGI_IP30
+       bool "SGI IP30 (Octane/Octane2) (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       select ARC
+       select ARC64
+       select BOOT_ELF64
+        select CEVT_R4K
+        select CSRC_R4K
+       select DMA_IP30
+       select DMA_COHERENT
+       select GENERIC_ISA_DMA
+       select HW_HAS_PCI
+        select IRQ_CPU
+       select NR_CPUS_DEFAULT_2
+       select PCI_DOMAINS
+       select SYS_HAS_CPU_R10000
+       select SYS_SUPPORTS_64BIT_KERNEL
+       select SYS_SUPPORTS_BIG_ENDIAN
+       select SYS_SUPPORTS_SMP
+        select ARC_MEMORY
+        select ARC_PROMLIB
+       help
+         This are the SGI Octane and Octane2 graphics workstations.  To
+         compile a Linux kernel that runs on these, say Y here.
+
 config SGI_IP32
        bool "SGI IP32 (O2)"
        select ARC
@@ -812,6 +837,9 @@ config DMA_COHERENT
 config DMA_IP27
        bool
 
+config DMA_IP30
+       bool
+
 config DMA_NONCOHERENT
        bool
        select DMA_NEED_PCI_MAP_STATE
@@ -1006,7 +1034,7 @@ config BOOT_ELF32
 config MIPS_L1_CACHE_SHIFT
        int
        default "4" if MACH_DECSTATION
-       default "7" if SGI_IP22 || SGI_IP27 || SGI_IP28 || SNI_RM
+       default "7" if SGI_IP22 || SGI_IP27 || SGI_IP28 || SGI_IP30 || SNI_RM
        default "4" if PMC_MSP4200_EVAL
        default "5"
 
@@ -1019,12 +1047,12 @@ config ARC_CONSOLE
 
 config ARC_MEMORY
        bool
-       depends on MACH_JAZZ || SNI_RM || SGI_IP32
+       depends on MACH_JAZZ || SNI_RM || SGI_IP30 || SGI_IP32
        default y
 
 config ARC_PROMLIB
        bool
-       depends on MACH_JAZZ || SNI_RM || SGI_IP22 || SGI_IP28 || SGI_IP32
+       depends on MACH_JAZZ || SNI_RM || SGI_IP22 || SGI_IP28 || SGI_IP30 || 
SGI_IP32
        default y
 
 config ARC64
diff -Naurp linux-2.6.26.orig/arch/mips/Makefile linux-2.6.26/arch/mips/Makefile
--- linux-2.6.26.orig/arch/mips/Makefile        2008-07-23 22:26:29.000000000 
-0400
+++ linux-2.6.26/arch/mips/Makefile     2008-07-25 03:14:40.000000000 -0400
@@ -492,6 +492,15 @@ cflags-$(CONFIG_SGI_IP28)  += -mr10k-cach
 load-$(CONFIG_SGI_IP28)                += 0xa800000020004000
 
 #
+# SGI-IP30 (Octane/Octane2)
+#
+ifdef CONFIG_SGI_IP30
+core-$(CONFIG_SGI_IP30)                += arch/mips/sgi-ip30/
+cflags-$(CONFIG_SGI_IP30)      += -Iinclude/asm-mips/mach-ip30
+load-$(CONFIG_SGI_IP30)                += 0xa800000020004000
+endif
+
+#
 # SGI-IP32 (O2)
 #
 # Set the load address to >= 80069000 if you want to leave space for symmon,
diff -Naurp linux-2.6.26.orig/arch/mips/fw/arc/init.c 
linux-2.6.26/arch/mips/fw/arc/init.c
--- linux-2.6.26.orig/arch/mips/fw/arc/init.c   2008-07-13 17:51:29.000000000 
-0400
+++ linux-2.6.26/arch/mips/fw/arc/init.c        2008-07-25 03:14:40.000000000 
-0400
@@ -56,4 +56,11 @@ void __init prom_init(void)
                register_smp_ops(&ip27_smp_ops);
        }
 #endif
+#ifdef CONFIG_SGI_IP30
+       {
+               extern struct plat_smp_ops ip30_smp_ops;
+
+               register_smp_ops(&ip30_smp_ops);
+       }
+#endif
 }
diff -Naurp linux-2.6.26.orig/arch/mips/kernel/cevt-r4k.c 
linux-2.6.26/arch/mips/kernel/cevt-r4k.c
--- linux-2.6.26.orig/arch/mips/kernel/cevt-r4k.c       2008-07-13 
17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/kernel/cevt-r4k.c    2008-07-25 03:14:40.000000000 
-0400
@@ -251,7 +251,6 @@ int __cpuinit mips_clockevent_init(void)
        irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
        if (get_c0_compare_int)
                irq = get_c0_compare_int();
-
        cd = &per_cpu(mips_clockevent_device, cpu);
 
        cd->name                = "MIPS";
diff -Naurp linux-2.6.26.orig/arch/mips/kernel/setup.c 
linux-2.6.26/arch/mips/kernel/setup.c
--- linux-2.6.26.orig/arch/mips/kernel/setup.c  2008-07-13 17:51:29.000000000 
-0400
+++ linux-2.6.26/arch/mips/kernel/setup.c       2008-07-25 03:14:40.000000000 
-0400
@@ -481,6 +481,10 @@ static void __init arch_mem_init(char **
        printk("Determined physical RAM map:\n");
        print_memory_map();
 
+#ifdef CONFIG_CMDLINE
+       if (strlen(CONFIG_CMDLINE))
+               strlcpy(arcs_cmdline, CONFIG_CMDLINE, sizeof(command_line));
+#endif
        strlcpy(command_line, arcs_cmdline, sizeof(command_line));
        strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
 
@@ -575,7 +579,6 @@ void __init setup_arch(char **cmdline_p)
 #endif
 
        arch_mem_init(cmdline_p);
-
        resource_init();
        plat_smp_setup();
 }
diff -Naurp linux-2.6.26.orig/arch/mips/mm/dma-default.c 
linux-2.6.26/arch/mips/mm/dma-default.c
--- linux-2.6.26.orig/arch/mips/mm/dma-default.c        2008-07-13 
17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/mm/dma-default.c     2008-07-25 03:14:40.000000000 
-0400
@@ -209,7 +209,7 @@ dma_addr_t dma_map_page(struct device *d
                dma_cache_wback_inv(addr, size);
        }
 
-       return plat_map_dma_mem_page(dev, page) + offset;
+       return plat_map_dma_mem_page(dev, page, size) + offset;
 }
 
 EXPORT_SYMBOL(dma_map_page);
diff -Naurp linux-2.6.26.orig/arch/mips/pci/Makefile 
linux-2.6.26/arch/mips/pci/Makefile
--- linux-2.6.26.orig/arch/mips/pci/Makefile    2008-07-25 01:03:02.000000000 
-0400
+++ linux-2.6.26/arch/mips/pci/Makefile 2008-07-25 03:14:40.000000000 -0400
@@ -34,6 +34,7 @@ obj-$(CONFIG_PMC_MSP7120_FPGA)        += fixup-
 obj-$(CONFIG_PMC_YOSEMITE)     += fixup-yosemite.o ops-titan.o ops-titan-ht.o \
                                   pci-yosemite.o
 obj-$(CONFIG_SGI_IP27)         += ops-bridge.o pci-ip27.o
+obj-$(CONFIG_SGI_IP30)         += ops-bridge.o pci-ip30.o
 obj-$(CONFIG_SGI_IP32)         += fixup-ip32.o ops-mace.o pci-ip32.o
 obj-$(CONFIG_SIBYTE_SB1250)    += fixup-sb1250.o pci-sb1250.o
 obj-$(CONFIG_SIBYTE_BCM112X)   += fixup-sb1250.o pci-sb1250.o
diff -Naurp linux-2.6.26.orig/arch/mips/pci/ops-bridge.c 
linux-2.6.26/arch/mips/pci/ops-bridge.c
--- linux-2.6.26.orig/arch/mips/pci/ops-bridge.c        2008-07-13 
17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/pci/ops-bridge.c     2008-07-25 03:14:40.000000000 
-0400
@@ -9,9 +9,15 @@
 #include <linux/pci.h>
 #include <asm/paccess.h>
 #include <asm/pci/bridge.h>
+
+#ifdef CONFIG_SGI_IP30
+#include <asm/mach-ip30/addrs.h>
+#include <asm/mach-ip30/pcibr.h>
+#else
 #include <asm/sn/arch.h>
 #include <asm/sn/intr.h>
 #include <asm/sn/sn0/hub.h>
+#endif
 
 /*
  * Most of the IOC3 PCI config register aren't present
diff -Naurp linux-2.6.26.orig/arch/mips/pci/pci-ip30.c 
linux-2.6.26/arch/mips/pci/pci-ip30.c
--- linux-2.6.26.orig/arch/mips/pci/pci-ip30.c  1969-12-31 19:00:00.000000000 
-0500
+++ linux-2.6.26/arch/mips/pci/pci-ip30.c       2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,321 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek (skylark@linux-mips.org)
+ * Based on pci-ip27.c by
+ *  Copyright (C) 2003 Christoph Hellwig (hch@lst.de)
+ *  Copyright (C) 1999, 2000, 04 Ralf Baechle (ralf@linux-mips.org)
+ *  Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include <asm/mach-ip30/addrs.h>
+#include <asm/mach-ip30/pcibr.h>
+#include <asm/pci/bridge.h>
+#include <asm/paccess.h>
+
+extern unsigned int allocate_irqno(void);
+
+/*
+ * XXX: No kmalloc available when we do our crosstalk scan,
+ *     we should try to move it later in the boot process.
+ */
+static struct bridge_controller bridges[PCIBR_MAX_PCI_BUSSES];
+
+/* Translate from irq to software PCI bus number and PCI slot. */
+struct bridge_controller *irq_to_bridge[PCIBR_MAX_PCI_BUSSES * 
PCIBR_MAX_DEV_PCIBUS];
+int irq_to_slot[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS];
+bridge_t *ip30_irq_bridge[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS] = 
{NULL};
+unsigned int ip30_irq_in_bridge[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS] = 
{0};
+
+extern struct pci_ops bridge_pci_ops;
+
+unsigned int ip30_bridge_count = 0;
+unsigned int ip30_irq_assigned = PCIBR_IRQ_BASE;
+
+
+/* OK, spikey dildo time */
+#define AT_FAIL        0
+#define AT_D32 1
+#define AT_D64 2
+#define AT_DIO 3
+#define AT_WIN 4
+
+static char *at_names[] = {"failed", "direct 32-bit", "direct 64-bit",
+                          "direct I/O", "window"};
+
+static unsigned int align(unsigned int ptr, unsigned int size)
+{
+       return (ptr + size - 1) & ~(size - 1);
+}
+
+static inline unsigned int win_size(int n)
+{
+       return (n < 2)? 0x200000 : 0x100000;
+}
+
+static inline unsigned int win_base(int n)
+{
+       return (n < 3) ? (0x200000 * (n + 1)) : (0x100000 * (n + 4));
+}
+
+static int startup_resource(struct pci_controller *hose, struct pci_dev *dev, 
int res)
+{
+       struct bridge_controller *bc = (struct bridge_controller *)hose;
+       struct resource *rs = &dev->resource[res];
+       bridge_t *bvma = (bridge_t *)bc->base;
+       int slot = PCI_SLOT(dev->devfn);
+       int is_be = bc->slot_be[slot];
+       int is_io = !!(rs->flags & IORESOURCE_IO);
+       unsigned int size = rs->end - rs->start + 1;
+       int at = AT_FAIL;
+       unsigned int base = 0;
+       unsigned long vma = 0;
+       unsigned int devio;
+       int i, j;
+
+       /* check for nonexistant resources */
+       if (size < 2)
+               return 0;
+
+       /* try direct mappings first */
+       if (!is_io && !is_be) {
+               base = align(bc->d32_p, size);
+               vma = base + BRIDGE_PCI_MEM32_BASE;
+               bc->d32_p = base + size;
+               at = AT_D32;
+       }
+       if (is_io && !is_be && bc->bridge_rev >= BRIDGE_REV_D) {
+               base = align(bc->dio_p, size);
+               vma = base + BRIDGE_PCI_IO_BASE;
+               bc->dio_p = base + size;
+               at = AT_DIO;
+       }
+
+       /* OK, that failed, try finding a compatible DevIO */
+       if (at == AT_FAIL)
+               for (j = 0; j < 8; j++) {
+                       i = (j + slot) & 7;
+                       if (bc->win_p[i] && bc->win_io[i] == is_io && 
bc->win_be[i] == is_be)
+                               if (align(bc->win_p[i], size) + size <= 
win_size(i)) {
+                                       base = align(bc->win_p[i], size);
+                                       bc->win_p[i] = base + size;
+                                       base += win_base(i);
+                                       vma = base;
+                                       at = AT_WIN;
+                                       break;
+                               }
+               }
+
+       /* if everything else fails, allocate a new DevIO */
+       if (at == AT_FAIL)
+               for (j = 0; j < 8; j++) {
+                       i = (j + slot) & 7;
+                       if (!bc->win_p[i] && size <= win_size(i)) {
+                               bc->win_p[i] = size;
+                               bc->win_io[i] = is_io;
+                               bc->win_be[i] = is_be;
+                               base = win_base(i);
+                               vma = base;
+                               at = AT_WIN;
+                               /* set the DevIO params */
+                               devio = bvma->b_device[i].reg;
+                               if (is_be)
+                                       devio |= BRIDGE_DEV_DEV_SWAP;
+                               else
+                                       devio &= ~BRIDGE_DEV_DEV_SWAP;
+                               if (is_io)
+                                       devio &= ~BRIDGE_DEV_DEV_IO_MEM;
+                               else
+                                       devio |= BRIDGE_DEV_DEV_IO_MEM;
+                               devio &= ~BRIDGE_DEV_OFF_MASK;
+                               devio |= win_base(i) >> 
BRIDGE_DEV_OFF_ADDR_SHFT;
+                               bvma->b_device[i].reg = devio;
+                               break;
+                       }
+               }
+
+       /* get real VMA */
+       if (vma < PCIBR_OFFSET_END)
+               vma += NODE_SWIN_BASE(bc->nasid, bc->widget_id);
+       else
+               vma += NODE_BWIN_BASE(bc->nasid, bc->widget_id);
+
+       /* dump useless info to console */
+       if (at != AT_FAIL)
+               printk(KERN_INFO "BRIDGE: %s #%d, size 0x%x for %s-endian %s 
--> %s at bus 0x%08x vma 0x%016lx\n",
+                                       is_io ? "IO" : "Memory", res, size,
+                                       is_be ? "big" : "little", pci_name(dev),
+                                       at_names[at], base, vma);
+       else
+               printk(KERN_INFO "BRIDGE: %s #%d, size 0x%x for %s-endian %s 
--> %s\n",
+                                       is_io ? "IO" : "Memory", res, size,
+                                       is_be ? "big" : "little", pci_name(dev),
+                                       at_names[at]);
+
+       if (at == AT_FAIL)
+               return -ENOMEM;
+
+       /* set the device resource to the new address */
+       rs->start = vma;
+       rs->end = vma + size - 1;
+       pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (4 * res), &devio);
+       devio &= 15;
+       devio |= base & ~15;
+       pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (4 * res), devio);
+       
+       return 0;
+}
+
+int __init bridge_probe(nasid_t nasid, int widget_id, int masterwid)
+{
+       struct bridge_controller *bc;
+       static int num_bridges = 0;
+       bridge_t *bridge;
+       int i;
+
+       printk(KERN_INFO "BRIDGE chip at xtalk:%d, initializing...\n", 
widget_id);
+
+       /* XXX: kludge alert.. */
+       if (!num_bridges)
+               ioport_resource.end = ~0UL;
+
+       bc = &bridges[num_bridges];
+
+       bc->pc.pre_enable       = startup_resource;
+
+       bc->pc.pci_ops          = &bridge_pci_ops;
+       bc->pc.mem_resource     = &bc->mem;
+       bc->pc.io_resource      = &bc->io;
+
+       bc->pc.index            = num_bridges;
+
+       bc->mem.name            = "Bridge PCI MEM";
+       bc->mem.start           = NODE_SWIN_BASE(0, widget_id) + 
PCIBR_OFFSET_MEM;
+       bc->mem.end             = NODE_SWIN_BASE(0, widget_id) + 
PCIBR_OFFSET_IO - 1;
+       bc->pc.mem_offset       = NODE_SWIN_BASE(0, widget_id);
+       bc->mem.flags           = IORESOURCE_MEM;
+
+       bc->io.name             = "Bridge IO MEM";
+       bc->io.start            = NODE_SWIN_BASE(0, widget_id) + 
PCIBR_OFFSET_IO;
+       bc->io.end              = NODE_SWIN_BASE(0, widget_id) + 
PCIBR_OFFSET_END - 1;
+       bc->pc.io_offset        = NODE_SWIN_BASE(0, widget_id);
+       bc->io.flags            = IORESOURCE_IO;
+
+       bc->irq_cpu = smp_processor_id();
+       bc->widget_id = widget_id;
+       bc->nasid = nasid;
+
+       /* set direct allocation base */
+       bc->dio_p = PCIBR_DIR_ALLOC_BASE;
+       bc->d32_p = PCIBR_DIR_ALLOC_BASE;
+
+       bc->baddr = (u64)masterwid << 60;
+       bc->baddr |= (1UL << 56);       /* Barrier set */
+
+       /* point to this bridge */
+       bridge = (bridge_t *) RAW_NODE_SWIN_BASE(nasid, widget_id);
+
+       bc->bridge_rev          = bridge->b_wid_id >> 28;
+
+       /* Clear all pending interrupts. */
+       bridge->b_int_rst_stat = BRIDGE_IRR_ALL_CLR;
+
+       /* Until otherwise set up, assume all interrupts are from slot 0 */
+       bridge->b_int_device = 0x0;
+
+       /* Fix the initial b_device configuration. */
+       bridge->b_wid_control &= ~(BRIDGE_CTRL_IO_SWAP | BRIDGE_CTRL_MEM_SWAP);
+
+       for (i = 0; i < 8; i++)
+               bridge->b_device[i].reg = BRIDGE_DEV_ERR_LOCK_EN | 
BRIDGE_DEV_VIRTUAL_EN |
+                                         BRIDGE_DEV_PMU_WRGA_EN | 
BRIDGE_DEV_DIR_WRGA_EN |
+                                         BRIDGE_DEV_SWAP_PMU | 
BRIDGE_DEV_SWAP_DIR | 
+                                         BRIDGE_DEV_COH;
+
+       /* Configure direct-mapped DMA */
+       bridge->b_dir_map = (masterwid << BRIDGE_DIRMAP_W_ID_SHFT) | 
BRIDGE_DIRMAP_ADD512;
+
+       /*
+        * Allocate the RRBs randomly.
+        *
+        * No, I'm joking :)
+        * These are occult numbers of the Black Priesthood of Ancient Mu.
+        */
+       bridge->b_even_resp = PCIBR_ANCIENT_MU_EVEN_RESP;
+       bridge->b_odd_resp = PCIBR_ANCIENT_MU_ODD_RESP;
+
+       /*
+        * Route all PCI bridge interrupts to the HEART ASIC. The idea is
+        * that we cause the bridge to send an Xtalk write to a specified
+        * interrupt register (0x80 for HEART, 0x90 for HUB) in a defined
+        * widget. The actual IRQ support and masking is done elsewhere.
+        */
+       bridge->b_wid_int_upper = masterwid << 16;
+       bridge->b_wid_int_lower = PCIBR_XIO_SEES_HEART;
+
+       /* We map the IRQs to slots in a straightforward way. */
+       bc->irq_base = ip30_irq_assigned;
+       for (i = 0; i < 8; i++) {
+               bridge->b_int_addr[i].addr = ip30_irq_assigned;
+               ip30_irq_bridge[ip30_irq_assigned] = bridge;
+               ip30_irq_in_bridge[ip30_irq_assigned] = i;
+               ip30_irq_assigned++;
+       }
+       bridge->b_int_device &= PCIBR_ANCIENT_MU_INT_DEVICE;
+       bridge->b_int_enable = PCIBR_ANCIENT_MU_INT_ENABLE;
+       bridge->b_int_mode = PCIBR_ANCIENT_MU_INT_MODE;
+       ip30_bridge_count++;
+
+       bridge->b_wid_tflush;     /* wait until Bridge PIO complete */
+
+       bc->base = bridge;
+
+       register_pci_controller(&bc->pc);
+
+       num_bridges++;
+
+       return 0;
+}
+
+int __devinit pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+       struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
+
+       return (bc->irq_base + slot);
+}
+
+/* Do platform specific device initialization at pci_enable_device() time */
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+       return 0;
+}
+
+static inline void pci_disable_swapping_pio(struct pci_dev *dev)
+{
+       struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
+
+       bc->slot_be[PCI_SLOT(dev->devfn)] = 1;
+}
+
+static inline void pci_disable_swapping_dma(struct pci_dev *dev)
+{
+       struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
+       bridge_t *bvma = (bridge_t *)bc->base;
+       unsigned int devio;
+       int slot = PCI_SLOT(dev->devfn);
+
+       bc->slot_bs[slot] = 1;
+       devio = bvma->b_device[slot].reg;
+       devio &= ~(BRIDGE_DEV_SWAP_PMU | BRIDGE_DEV_SWAP_DIR);
+       bvma->b_device[slot].reg = devio;
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
+       pci_disable_swapping_dma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_RAD1,
+       pci_disable_swapping_dma);
diff -Naurp linux-2.6.26.orig/arch/mips/pci/pci.c 
linux-2.6.26/arch/mips/pci/pci.c
--- linux-2.6.26.orig/arch/mips/pci/pci.c       2008-07-13 17:51:29.000000000 
-0400
+++ linux-2.6.26/arch/mips/pci/pci.c    2008-07-25 03:14:40.000000000 -0400
@@ -77,6 +77,10 @@ pcibios_align_resource(void *data, struc
 
 void __devinit register_pci_controller(struct pci_controller *hose)
 {
+       if (hose->pre_scan)
+               if(hose->pre_scan(hose) < 0)
+                       goto out;
+
        if (request_resource(&iomem_resource, hose->mem_resource) < 0)
                goto out;
        if (request_resource(&ioport_resource, hose->io_resource) < 0) {
@@ -145,6 +149,10 @@ static int __init pcibios_init(void)
                hose->need_domain_info = need_domain_info;
                if (bus) {
                        next_busno = bus->subordinate + 1;
+ 
+                       if (hose->post_scan)
+                               hose->post_scan(hose, bus);
+
                        /* Don't allow 8-bit bus number overflow inside the 
hose -
                           reserve some space for bridges. */
                        if (next_busno > 224) {
@@ -168,6 +176,7 @@ static int pcibios_enable_resources(stru
        u16 cmd, old_cmd;
        int idx;
        struct resource *r;
+       struct pci_controller *hose = (struct pci_controller *)dev->sysdata;
 
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        old_cmd = cmd;
@@ -176,6 +185,10 @@ static int pcibios_enable_resources(stru
                if (!(mask & (1<<idx)))
                        continue;
 
+               if(hose->pre_enable)
+                       if(hose->pre_enable(hose, dev, idx) < 0)
+                               return -EINVAL;
+
                r = &dev->resource[idx];
                if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
                        continue;
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/Makefile 
linux-2.6.26/arch/mips/sgi-ip30/Makefile
--- linux-2.6.26.orig/arch/mips/sgi-ip30/Makefile       1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/Makefile    2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,8 @@
+#
+# Makefile for the IP30 specific kernel interface routines under Linux.
+#
+
+obj-y  := ip30-setup.o ip30-irq.o ip30-timer.o ip30-err.o ip30-xtalk.o 
ip30-power.o
+obj-$(CONFIG_SMP)      += ip30-smp.o ip30-smp-glue.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-err.c 
linux-2.6.26/arch/mips/sgi-ip30/ip30-err.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-err.c     1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-err.c  2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,42 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-err.c: HEART error handling for IP30 architecture.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ *              2007 Joshua Kinard <kumba@gentoo.org>
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+
+#include <asm/mach-ip30/heart.h>
+
+void ip30_do_err(void)
+{
+       unsigned long errors = *HEART_ISR;
+       int i;
+       irq_enter();
+       *HEART_CLR_ISR = HEART_INT_LEVEL4;
+       printk("IP30: HEART ATTACK! Caught errors: 0x%04x!\n",
+               (int)((errors >> HEART_ERR_MASK_START) & HEART_ERR_MASK));
+       for(i = HEART_ERR_MASK_END; i >= HEART_ERR_MASK_START; i--)
+               if ((errors >> i) & 1)
+                       printk("    interrupt #%d\n", i);
+       irq_exit();
+}
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-irq.c 
linux-2.6.26/arch/mips/sgi-ip30/ip30-irq.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-irq.c     1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-irq.c  2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,337 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-irq.c: Highlevel interrupt handling for IP30 architecture.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ *              2007 Joshua Kinard <kumba@gentoo.org>
+ *
+ * Inspired by ip27-irq.c and ip32-irq.c
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/pcibr.h>
+#include <asm/mach-ip30/racermp.h>
+#include <asm/pci/bridge.h>
+
+#undef DEBUG_IRQ
+#undef DEBUG_IRQ_SET
+
+#define DYNAMIC_IRQ_START 64
+
+#ifndef CONFIG_SMP
+#define cpu_logical_map(x) 0
+#define cpu_next_pcpu(x) 0
+#else
+extern int cpu_next_pcpu(int pcpu);
+#endif
+
+/* CPU IRQ */
+
+void ip30_timer_bcast(void);
+
+void cpu_do_irq(void)
+{
+#ifdef CONFIG_SMP
+       ip30_timer_bcast();
+#endif
+       do_IRQ(TIMER_IRQ);
+}
+
+static void enable_cpu_irq(unsigned int irq)
+{
+       set_c0_status(STATUSF_IP7);
+}
+
+static unsigned int startup_cpu_irq(unsigned int irq)
+{
+       enable_cpu_irq(irq);
+       return 0;
+}
+
+static void disable_cpu_irq(unsigned int irq)
+{
+       clear_c0_status(STATUSF_IP7);
+}
+
+static void end_cpu_irq(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+               enable_cpu_irq(irq);
+}
+
+#define shutdown_cpu_irq disable_cpu_irq
+#define mask_and_ack_cpu_irq disable_cpu_irq
+
+static struct irq_chip ip30_cpu_irq = {
+       .typename = "CPU",
+       .startup = startup_cpu_irq,
+       .shutdown = shutdown_cpu_irq,
+       .enable = enable_cpu_irq,
+       .disable = disable_cpu_irq,
+       .ack = mask_and_ack_cpu_irq,
+       .end = end_cpu_irq,
+};
+
+/* real HEART IRQs */
+
+int heart_irq_thisowner;
+static int heart_irq_owner[SOFT_IRQ_COUNT];
+
+void ip30_do_irq(void)
+{
+       unsigned int pcpu=cpu_logical_map(smp_processor_id());
+       unsigned long irqs=((*HEART_ISR) & HEART_ATK_MASK) & (*HEART_IMR(pcpu));
+       unsigned long irqsel;
+       int irqnum;
+#ifdef DEBUG_IRQ
+       bridge_t *bvma = (bridge_t *)RAW_NODE_SWIN_BASE(0, 15);
+       if(irqs & ~(15UL << IRQ_TIMER_P(0)))
+               printk("IP30: received HEART IRQs: 0x%016lx (mask 0x%016lx) 
PCPU%d BRIDGE %08x\n",
+                       *HEART_ISR, *HEART_IMR(pcpu), pcpu, bvma->b_int_status);
+#endif
+       /* check for all IRQs in decreasing priority order */
+       irqsel = NON_HEART_IRQ_ST;
+       irqnum = 50;
+
+       /* poll all interrupts according to priority */
+       while (irqsel) {
+               if(irqs & irqsel)
+                       do_IRQ(irqnum);
+               irqsel >>= 1;
+               irqnum--;
+       }
+}
+
+static void enable_heart_irq(unsigned int irq)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+       *HEART_IMR(heart_irq_owner[irq]) |= (1UL << irq);
+       local_irq_restore(flags);
+}
+
+static unsigned int startup_heart_irq(unsigned int irq)
+{
+       unsigned long flags;
+       unsigned int device;
+       unsigned int pcpu;
+       if (irq == 14 || irq == 15)
+               pcpu = 0;
+       else if (irq < IRQ_TIMER_P(0) || irq > IRQ_IPI_P(3))
+               pcpu = heart_irq_thisowner = cpu_next_pcpu(heart_irq_thisowner);
+       else
+               pcpu = cpu_logical_map(smp_processor_id());
+#ifdef DEBUG_IRQ_SET
+       printk("IP30: start up IRQ%d for PCPU%d\n", irq, pcpu);
+#endif
+       local_irq_save(flags);
+
+       if (heart_irq_owner[irq] != -1) {
+               printk(KERN_ERR "IP30: ambiprocessorous IRQ startup request (is 
%d, was %d).\n",
+                       pcpu, heart_irq_owner[irq]);
+               local_irq_restore(flags);
+               return 0;
+       }
+
+       heart_irq_owner[irq] = pcpu;
+       *HEART_CLR_ISR = (1UL << irq);                          /* clear IRQ 
flag */
+       *HEART_IMR(heart_irq_owner[irq]) |= (1UL << irq);       /* unmask IRQ */
+
+       if (ip30_irq_bridge[irq]) {
+               ip30_irq_bridge[irq]->b_int_enable |= (1 << 
ip30_irq_in_bridge[irq]);
+               ip30_irq_bridge[irq]->b_int_mode |= (1 << 
ip30_irq_in_bridge[irq]);
+               device = ip30_irq_bridge[irq]->b_int_device;
+               device &= ~(7 << (ip30_irq_in_bridge[irq] * 3));
+               device |= (ip30_irq_in_bridge[irq] << (ip30_irq_in_bridge[irq] 
* 3));
+               ip30_irq_bridge[irq]->b_int_device = device;
+               ip30_irq_bridge[irq]->b_widget.w_tflush;
+       }
+
+       local_irq_restore(flags);
+
+       /* This is probably not right; we could have pending irqs */
+       return 0;
+}
+
+static void disable_heart_irq(unsigned int irq)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+       *HEART_IMR(heart_irq_owner[irq]) &= ~(1UL << irq);
+       local_irq_restore(flags);
+}
+
+static void shutdown_heart_irq(unsigned int irq)
+{
+       unsigned long flags;
+#ifdef DEBUG_IRQ_SET
+       printk("IP30: shutdown IRQ%d\n", irq);
+#endif
+       local_irq_save(flags);
+       *HEART_IMR(heart_irq_owner[irq]) &= ~(1UL << irq);              /* mask 
IRQ */
+       if (ip30_irq_bridge[irq])
+               ip30_irq_bridge[irq]->b_int_enable &= ~(1 << 
ip30_irq_in_bridge[irq]);
+       heart_irq_owner[irq] = -1;
+       local_irq_restore(flags);
+}
+
+static void mask_and_ack_heart_irq (unsigned int irq)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+       if (irq >= IRQ_TIMER_P(0) && irq <= IRQ_IPI_P(3))
+               *HEART_CLR_ISR = (1UL << irq);
+       if (!ip30_irq_bridge[irq])
+               *HEART_ISR = (1UL << irq);
+       *HEART_IMR(heart_irq_owner[irq]) &= ~(1UL << irq);
+       local_irq_restore(flags);
+}
+
+static void end_heart_irq(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+               enable_heart_irq(irq);
+}
+
+static struct irq_chip ip30_heart_irq = {
+       .typename = "HEART",
+       .startup = startup_heart_irq,
+       .shutdown = shutdown_heart_irq,
+       .enable = enable_heart_irq,
+       .disable = disable_heart_irq,
+       .ack = mask_and_ack_heart_irq,
+       .end = end_heart_irq,
+};
+
+/* dynamic pseudo-IRQs */
+
+static void do_nothing_irq(unsigned int irq)
+{
+       /* Empty */
+}
+
+static unsigned int do_nothing_irq_i(unsigned int irq)
+{
+       return 0;
+}
+
+static struct irq_chip dynamic_allocated_irq = {
+       .typename = "Allocated",
+       .startup = do_nothing_irq_i,
+       .shutdown = do_nothing_irq,
+       .enable = do_nothing_irq,
+       .disable = do_nothing_irq,
+       .ack = do_nothing_irq,
+       .end = do_nothing_irq,
+};
+
+static struct irq_chip dynamic_free_irq = {
+       .typename = "Free",
+       .startup = do_nothing_irq_i,
+       .shutdown = do_nothing_irq,
+       .enable = do_nothing_irq,
+       .disable = do_nothing_irq,
+       .ack = do_nothing_irq,
+       .end = do_nothing_irq,
+};
+
+int new_dynamic_irq(void)
+{
+       int i;
+       for (i = 0; i < NR_IRQS; i++)
+               if(irq_desc[i].chip == &dynamic_free_irq)
+                       break;
+       if ( i== NR_IRQS)
+               return -1;
+       irq_desc[i].chip = &dynamic_allocated_irq;
+       return i;
+}
+
+void delete_dynamic_irq(int irq)
+{
+       irq_desc[irq].chip = &dynamic_free_irq;
+}
+
+void call_dynamic_irq(int irq)
+{
+       do_IRQ(irq);
+}
+
+/* setup procedure */
+
+extern void ip30_do_err(void);
+
+asmlinkage void plat_irq_dispatch(void)
+{
+       unsigned int pending = read_c0_cause();
+
+       if (likely(pending & (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3)))
+               ip30_do_irq();
+       else if (unlikely(pending & IE_IRQ4))
+               ip30_do_err();
+       else if (unlikely(pending & IE_IRQ5))
+               cpu_do_irq();
+}
+
+void __init arch_init_irq(void)
+{
+       int i;
+       *HEART_CLR_ISR = HEART_ACK_ALL_MASK;    /* acknowledge everything */
+       for (i = 0; i < MP_NCPU; i++)           /* mask all IRQs, leave errors 
on */
+               *HEART_IMR(i) = HEART_CLR_ALL_MASK;
+
+       *HEART_IMR(cpu_logical_map(0)) = HEART_BR_ERR_MASK;
+
+       for (i = 0; i < SOFT_IRQ_COUNT; i++) {
+               irq_desc[i].status = IRQ_DISABLED;
+               irq_desc[i].action = 0;
+               irq_desc[i].depth  = 1;
+               irq_desc[i].chip   = &ip30_heart_irq;
+               heart_irq_owner[i] = -1;
+       }
+
+       irq_desc[TIMER_IRQ].status = IRQ_DISABLED;
+       irq_desc[TIMER_IRQ].action = 0;
+       irq_desc[TIMER_IRQ].depth  = 1;
+       irq_desc[TIMER_IRQ].chip   = &ip30_cpu_irq;
+
+       for ( i = DYNAMIC_IRQ_START; i < NR_IRQS; i++) {
+               irq_desc[i].status = IRQ_DISABLED;
+               irq_desc[i].action = 0;
+               irq_desc[i].depth  = 1;
+               irq_desc[i].chip   = &dynamic_free_irq;
+       }
+
+       /* mask IP0, IP1 (sw int) */
+       change_c0_status(ST0_IM, STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 |
+                                STATUSF_IP5 | STATUSF_IP6);
+       set_c0_status(ST0_IE);
+       printk("IP30: interrupt controller initialized.\n");
+}
+
+void ip30_secondary_init_irq(void)
+{
+       int pcpu = cpu_logical_map(smp_processor_id());
+       *HEART_IMR(pcpu) = HEART_CLR_ALL_MASK;
+       change_c0_status(ST0_IM, STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 |
+                                STATUSF_IP5 | STATUSF_IP6);
+}
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-power.c 
linux-2.6.26/arch/mips/sgi-ip30/ip30-power.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-power.c   1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-power.c        2008-07-25 
03:14:40.000000000 -0400
@@ -0,0 +1,132 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-power.c: Software powerdown and reset handling for IP30 architecture.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ *              2007 Joshua Kinard <kumba@gentoo.org>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/time.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+
+#include <asm/mach-ip30/addrs.h>
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/racermp.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/pci/bridge.h>
+
+#define IP30_POWER_IRQ 14
+#define IP30_ACFAIL_IRQ        15
+
+void ip30_machine_restart(char *command)
+{
+       printk("Rebooting...");
+#ifdef CONFIG_SMP
+       smp_send_stop();
+       udelay(1000);
+#endif
+       /* execute HEART cold reset
+        *      Yes, it's cold-HEARTed! */
+       *HEART_MODE |= (1UL << 23);
+}
+
+void ip30_soft_powerdown(void);
+int ip30_clear_power_irq(void);
+int ip30_can_powerdown(void);
+
+void ip30_machine_power_off(void)
+{
+#ifdef CONFIG_SGI_IP30_RTC     
+       int i;
+
+       if (!ip30_can_powerdown())
+               return;
+       printk("Powering down, please wait...");
+
+#ifdef CONFIG_SMP
+       smp_send_stop();
+       udelay(1000);
+#endif
+
+       /* kill interrupts */
+        *HEART_CLR_ISR = HEART_ACK_ALL_MASK;
+        for (i = 0; i < MP_NCPU; i++)
+                *HEART_IMR(i) = HEART_CLR_ALL_MASK;
+
+       /* execute RTC powerdown */
+       ip30_soft_powerdown();
+#else
+       printk("RTC support is required to power down.\n");
+       printk("System halted.\n");
+       while (1);
+#endif
+}
+
+void ip30_machine_halt(void)
+{
+       ip30_machine_power_off();
+}
+
+/* power button */
+static struct timer_list power_timer;
+
+static int is_shutdown;
+
+static void power_timeout(unsigned long data)
+{
+       ip30_machine_power_off();
+}
+
+static irqreturn_t power_irq(int irq, void *dev_id)
+{
+       /* prepare for next IRQs */
+#ifdef CONFIG_SGI_IP30_RTC
+       if (!ip30_clear_power_irq())
+#endif
+               disable_irq_nosync(irq);
+
+       /* button pressed twice or no init */
+       if (is_shutdown || kill_proc(1, SIGINT, 1)) {
+               printk(KERN_INFO "Immediate powerdown...\n");
+               ip30_machine_power_off();
+               return IRQ_HANDLED;
+       }
+
+       /* power button, set LEDs if we can */
+       is_shutdown = 1;
+       printk(KERN_INFO "Power button pressed, shutting down...\n");
+
+       init_timer(&power_timer);
+       power_timer.function = power_timeout;
+       power_timer.expires = jiffies + (30 * HZ);
+       add_timer(&power_timer);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t acfail_irq(int irq, void *dev_id)
+{
+       /* we have a bit of time here */
+       return IRQ_HANDLED;
+}
+
+static int __init reboot_setup(void)
+{
+       request_irq(IP30_POWER_IRQ, power_irq, 0, "powerbtn", NULL);
+       request_irq(IP30_ACFAIL_IRQ, acfail_irq, 0, "acfail", NULL);
+       return 0;
+}
+
+subsys_initcall(reboot_setup);
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-setup.c 
linux-2.6.26/arch/mips/sgi-ip30/ip30-setup.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-setup.c   1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-setup.c        2008-07-25 
03:14:40.000000000 -0400
@@ -0,0 +1,73 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * SGI IP30 specific setup.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ *               2007 Joshua Kinard <kumba@gentoo.org>
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/time.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+
+#include <asm/bootinfo.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/addrs.h>
+
+extern void ip30_machine_restart(char *command);
+extern void ip30_machine_halt(void);
+extern void ip30_machine_power_off(void);
+
+extern void ip30_xtalk_setup(void);
+
+extern void ip30_time_init(void);
+
+extern int ip30_locate_bootcpu(void);
+
+static unsigned long ip30_size_memory(void)
+{
+       unsigned long result = 0;
+       unsigned int *memcfg = (unsigned int *)HEART_MEMCFG0;
+       int i;
+
+       for (i = 0; i < 8; i++)
+               if(memcfg[i] & HEART_MEMCFG_VLD)
+                       result += ((memcfg[i] & HEART_MEMCFG_RAM_MSK) 
+                                       >> HEART_MEMCFG_RAM_SHFT) + 1;
+       return result << HEART_MEMCFG_UNIT_SHFT;
+}
+
+
+static void ip30_fix_memory(void)
+{
+       unsigned long size = ip30_size_memory();
+       printk(KERN_INFO "Detected %ld MB of physical memory.\n", size >> 20);
+
+       if(size > IP30_MAX_PROM_MEM) {
+               printk(KERN_INFO "Updating PROM memory size.\n");
+               add_memory_region((IP30_MAX_PROM_MEM + IP30_MEM_BASE),
+                                       size - IP30_MAX_PROM_MEM, BOOT_MEM_RAM);
+       }
+}
+
+
+void __init plat_mem_setup(void)
+{
+       printk("Silicon Graphics Octane (IP30) support: (c) 2004-2007 Stanislaw 
Skowronek.\n");
+       set_io_port_base(IP30_IO_PORT_BASE);
+        _machine_restart  = ip30_machine_restart;
+        _machine_halt = ip30_machine_halt;
+        pm_power_off = ip30_machine_power_off;
+       ip30_fix_memory();
+       ip30_xtalk_setup();
+}
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-smp-glue.S 
linux-2.6.26/arch/mips/sgi-ip30/ip30-smp-glue.S
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-smp-glue.S        1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-smp-glue.S     2008-07-25 
03:14:40.000000000 -0400
@@ -0,0 +1,21 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005-2007 Stanislaw Skowronek
+ */
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+       .text
+       .set    noat
+       .set    reorder
+       .align  5
+LEAF(ip30_smp_bootstrap)
+       move    gp, a0
+       j       smp_bootstrap
+       END(ip30_smp_bootstrap)
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-smp.c 
linux-2.6.26/arch/mips/sgi-ip30/ip30-smp.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-smp.c     1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-smp.c  2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,165 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-smp.c: SMP on IP30 architecture.
+ *
+ * Copyright (C) 2005-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ *               2006-2007 Joshua Kinard <kumba@gentoo.org>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/spinlock.h>
+
+#include <asm/mmu_context.h>
+#include <asm/bootinfo.h>
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/racermp.h>
+#include <asm/mach-ip30/addrs.h>
+
+#undef DEBUG_IPI
+
+extern void asmlinkage ip30_smp_bootstrap(void);
+extern void plat_time_init(void);
+extern void ip30_secondary_init_irq(void);
+
+static spinlock_t ipi_mbx_lock = SPIN_LOCK_UNLOCKED;
+static volatile unsigned int ipi_mailbox[NR_CPUS];
+
+extern unsigned int (*mips_hpt_read)(void);
+extern void (*mips_hpt_init)(unsigned int);
+extern void ip30_secondary_timer_setup(void);
+
+static void ip30_send_ipi_single(int cpu, unsigned int action)
+{
+       unsigned long flags;
+#ifdef DEBUG_IPI
+       if(action == SMP_CALL_FUNCTION)
+               printk("KERN_INFO IPI call_function TX -> %d\n", cpu);
+#endif
+       spin_lock_irqsave(&ipi_mbx_lock, flags);
+       ipi_mailbox[cpu] |= action;
+       spin_unlock_irqrestore(&ipi_mbx_lock, flags);
+       *HEART_SET_ISR = 1UL << (IRQ_IPI_P(cpu));
+}
+
+static void ip30_send_ipi_mask(cpumask_t mask, unsigned int action)
+{
+        unsigned int i;
+
+        for_each_cpu_mask(i, mask)
+                ip30_send_ipi_single(i, action);
+}
+
+int cpu_next_pcpu(int pcpu)
+{
+       int i;
+       for (i = (pcpu + 1) % MP_NCPU; !cpu_isset(i, phys_cpu_present_map); i = 
(i + 1) % MP_NCPU)
+               if(i == pcpu)
+                       return pcpu;
+       return i;
+}
+
+irqreturn_t ip30_mailbox_irq(int irq, void *dev)
+{
+       int cpu = smp_processor_id();
+       int mbx;
+       spin_lock(&ipi_mbx_lock);
+       mbx = ipi_mailbox[cpu];
+       ipi_mailbox[cpu] = 0;
+       spin_unlock(&ipi_mbx_lock);
+       if (mbx & SMP_RESCHEDULE_YOURSELF)
+               /* ignore - reschedule after IRQ */ ;
+       if (mbx & SMP_CALL_FUNCTION) {
+               smp_call_function_interrupt();
+#ifdef DEBUG_IPI
+               printk("KERN_INFO IPI call_function RX -> %d\n",
+                       smp_processor_id());
+#endif
+       }
+       return IRQ_HANDLED;
+}
+
+void ip30_timer_bcast(void)
+{
+       int i;
+       for (i = 1; i < NR_CPUS; i++)
+               if (cpu_isset(i, cpu_present_map))
+                       *HEART_SET_ISR = 1UL << (IRQ_TIMER_P(i));
+}
+
+irqreturn_t ip30_secondary_timer_irq(int irq, void *dev)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+       local_irq_restore(flags);
+       return IRQ_HANDLED;
+}
+
+static void __init ip30_prepare_cpus(unsigned int max_cpus)
+{
+       /* everything should be ready by now */
+}
+
+static void __init ip30_smp_setup(void)
+{
+       int i, j;
+       
+       cpus_clear(phys_cpu_present_map);
+       for (i = 0, j = 0; i < MP_NCPU; i++)
+               if (MP_MAGIC(i) == MPCONF_MAGIC && MP_VIRTID(i) < NR_CPUS) {
+                       cpu_set(i, phys_cpu_present_map);
+                       __cpu_number_map[i] = MP_VIRTID(i);
+                       __cpu_logical_map[MP_VIRTID(i)] = i;
+                       j++;
+               }
+       printk("Detected %d enabled CPU(s).\n", j);
+}
+
+static void __cpuinit ip30_boot_secondary(int cpu, struct task_struct *idle)
+{
+       int pcpu = cpu_logical_map(cpu);
+       MP_STACKADDR(pcpu) = __KSTK_TOS(idle);
+       MP_LPARM(pcpu) = (unsigned long)idle->stack;
+       MP_LAUNCH(pcpu) = ip30_smp_bootstrap;
+}
+
+static void __cpuinit ip30_init_secondary(void)
+{
+       ip30_secondary_init_irq();
+}
+
+static void __cpuinit ip30_smp_finish(void)
+{
+       int cpu = smp_processor_id();
+       if (request_irq(IRQ_IPI_P(cpu), ip30_mailbox_irq, 0, "SMP IPI", NULL))
+               printk("IP30: IPI allocation for CPU%d failed.\n", cpu);
+       if (request_irq(IRQ_TIMER_P(cpu), ip30_secondary_timer_irq, 0, "SMP 
TIMER", NULL))
+               printk("IP30: TIMER allocation for CPU%d failed.\n", cpu);
+       local_irq_enable();
+}
+
+static void __init ip30_cpus_done(void)
+{
+       int cpu = smp_processor_id();
+       if (request_irq(IRQ_IPI_P(cpu), ip30_mailbox_irq, 0, "SMP IPI", NULL))
+               printk("IP30: IPI allocation for CPU%d failed.\n", cpu);
+}
+
+struct plat_smp_ops ip30_smp_ops = {
+       .send_ipi_single        = ip30_send_ipi_single,
+       .send_ipi_mask          = ip30_send_ipi_mask,
+       .init_secondary         = ip30_init_secondary,
+       .smp_finish             = ip30_smp_finish,
+       .cpus_done              = ip30_cpus_done,
+       .boot_secondary         = ip30_boot_secondary,
+       .smp_setup              = ip30_smp_setup,
+       .prepare_cpus           = ip30_prepare_cpus,
+};
+
+
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-timer.c 
linux-2.6.26/arch/mips/sgi-ip30/ip30-timer.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-timer.c   1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-timer.c        2008-07-25 
03:14:40.000000000 -0400
@@ -0,0 +1,51 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-timer.c: Timer handling for IP30 architecture.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ * Inspired by ip32-timer.c
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <asm/i8253.h>
+
+#include <asm/time.h>
+#include <asm/mipsregs.h>
+#include <asm/mmu_context.h>
+#include <asm/mach-ip30/heart.h>
+
+#define NSEC_PER_CYCLE         80
+#define CYCLES_PER_100MSEC     (100000000 / NSEC_PER_CYCLE)
+
+void __init plat_time_init(void)
+{
+       unsigned long heart_compare;
+       printk("IP30: initializing timer.\n");
+       heart_compare = (*HEART_COUNT) + CYCLES_PER_100MSEC;
+       write_c0_count(0);
+       while ((*HEART_COUNT - heart_compare) & 0x800000) ;
+       mips_hpt_frequency = read_c0_count() * 10;
+       printk("%d MHz CPU detected\n", (mips_hpt_frequency * 2) / 1000000);
+}
+
+unsigned int get_c0_compare_int(void) {
+       /* Return Octane's Timer IRQ */
+       return TIMER_IRQ;
+}
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-xtalk.c 
linux-2.6.26/arch/mips/sgi-ip30/ip30-xtalk.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-xtalk.c   1969-12-31 
19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-xtalk.c        2008-07-25 
03:14:40.000000000 -0400
@@ -0,0 +1,182 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-xtalk.c
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ *              2007 Joshua Kinard <kumba@gentoo.org>
+ *
+ * XIO bus probing code
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/addrs.h>
+#include <asm/mach-ip30/pcibr.h>
+#include <asm/mach-ip30/xtalk.h>
+#include <asm/xtalk/xwidget.h>
+
+static struct widget_ident widget_idents[] = {
+       {
+               XTALK_XBOW_MFGR_ID,
+               XTALK_XBOW_PART_ID,
+               "XBow",
+               {NULL, "1.0", "1.1", "1.2", "1.3", "2.0", NULL}
+       },
+       {
+               XTALK_XXBOW_MFGR_ID,
+               XTALK_XXBOW_PART_ID,
+               "XXBow",
+               {NULL, "1.0", "2.0", NULL}
+       },
+       {
+               XTALK_ODYS_MFGR_ID,
+               XTALK_ODYS_PART_ID,
+               "Buzz / Odyssey",
+               {NULL, "A", "B", NULL}
+       },
+       {
+               XTALK_TPU_MFGR_ID,
+               XTALK_TPU_PART_ID,
+               "TPU",
+               {"0", NULL}
+       },
+       {
+               XTALK_XBRDG_MFGR_ID,
+               XTALK_XBRDG_PART_ID,
+               "XBridge",
+               {NULL, "A", "B", NULL}
+       },
+       {
+               XTALK_HEART_MFGR_ID,
+               XTALK_HEART_PART_ID,
+               "Heart",
+               {NULL, "A", "B", "C", "D", "E", "F", NULL}
+       },
+       {
+               XTALK_BRIDG_MFGR_ID,
+               XTALK_BRIDG_PART_ID,
+               "Bridge",
+               {NULL, "A", "B", "C", "D", NULL}
+       },
+       {
+               XTALK_HUB_MFGR_ID,
+               XTALK_HUB_PART_ID,
+               "Hub",
+               {NULL, "1.0", "2.0", "2.1", "2.2", "2.3", "2.4", NULL}
+       },
+       {
+               XTALK_BDRCK_MFGR_ID,
+               XTALK_BDRCK_PART_ID,
+               "Bedrock",
+               {NULL, "1.0", "1.1", NULL}
+       },
+       {
+               XTALK_IMPCT_MFGR_ID,
+               XTALK_IMPCT_PART_ID,
+               "HQ4 / ImpactSR",
+               {NULL, "A", "B", NULL}
+       },
+       {
+               XTALK_KONA_MFGR_ID,
+               XTALK_KONA_PART_ID,
+               "XG / KONA",
+               {NULL}
+       },
+       {
+               XTALK_NULL_MFGR_ID,
+               XTALK_NULL_PART_ID,
+               NULL,
+               {NULL}
+       }
+};
+
+extern int bridge_probe(nasid_t nasid, int widget, int masterwid);
+
+unsigned long ip30_xtalk_swin(int wid)
+{
+       return NODE_SWIN_BASE(0, wid);
+}
+
+unsigned ip30_xtalk_get_id(int wid)
+{
+       unsigned int link_stat;
+       if (wid != XTALK_XBOW &&
+               (wid < XTALK_LOW_DEV || wid > XTALK_HIGH_DEV))
+                       return XTALK_NODEV;
+
+       if (wid) {
+               link_stat = *(volatile unsigned int *)(RAW_NODE_SWIN_BASE(0, 0) 
+ 
+                               XBOW_REG_LINK_STAT_0 + 
+                               XBOW_REG_LINK_BLOCK_SIZE * (wid - 
XTALK_LOW_DEV));
+               if (!(link_stat & XBOW_REG_LINK_ALIVE)) /* is the link alive? */
+                       return XTALK_NODEV;
+       }
+
+       return *(volatile unsigned int *)(RAW_NODE_SWIN_BASE(0, wid) + 
WIDGET_ID);
+}
+
+int ip30_xtalk_find(unsigned mfgr, unsigned part, int last)
+{
+       unsigned wid_id;
+       while (last > 0) {
+               last--;
+               wid_id = ip30_xtalk_get_id(last);
+               if (XWIDGET_MFG_NUM(wid_id) == mfgr &&
+                       XWIDGET_PART_NUM(wid_id) == part)
+                               return last;
+       }
+       return -1;
+}
+
+void __init ip30_xtalk_setup(void)
+{
+       int i;
+       unsigned int wid_id;
+       unsigned int wid_part, wid_mfgr, wid_rev;
+       struct widget_ident *res;
+
+       for (i = 0; i < IP30_XTALK_NUM_WID; i++) {
+               wid_id = ip30_xtalk_get_id(i);
+               if (wid_id != XTALK_NODEV) {
+                       printk(KERN_INFO "xtalk: Detected ");
+                       wid_mfgr = XWIDGET_MFG_NUM(wid_id);
+                       wid_part = XWIDGET_PART_NUM(wid_id);
+                       wid_rev = XWIDGET_REV_NUM(wid_id);
+
+                       for (res = widget_idents; res->name; res++)
+                               if(res->mfgr == wid_mfgr && res->part == 
wid_part)
+                                       break;
+
+                       if (res->name) {
+                               printk(res->name);
+                               if (res->revs[wid_rev])
+                                       printk(" (revision %s)", 
res->revs[wid_rev]);
+                               else
+                                       printk(" (unknown revision %d)", 
wid_rev);
+                       } else
+                               printk("unknown widget 0x%08x", wid_id);
+                       printk(" at %d.\n", i);
+               }
+       }
+
+       i = IP30_XTALK_NUM_WID;
+       while ((i = ip30_xtalk_find(PCIBR_XTALK_MFGR, PCIBR_XTALK_PART, i)) != 
-1)
+               bridge_probe(0, i, IP30_WIDGET_HEART);
+}
diff -Naurp linux-2.6.26.orig/drivers/char/Kconfig 
linux-2.6.26/drivers/char/Kconfig
--- linux-2.6.26.orig/drivers/char/Kconfig      2008-07-13 17:51:29.000000000 
-0400
+++ linux-2.6.26/drivers/char/Kconfig   2008-07-25 03:14:40.000000000 -0400
@@ -819,6 +819,17 @@ config SGI_IP27_RTC
          via the file /proc/rtc and its behaviour is set by various ioctls on
          /dev/rtc.
 
+config SGI_IP30_RTC
+       bool "SGI Octane RTC support"
+       depends on SGI_IP30 && SGI_IOC3
+       help
+         If you say Y here and create a character special file /dev/rtc with
+         major number 10 and minor number 135 using mknod ("man mknod"), you
+         will get access to the real time clock built into your computer.
+         Every SGI has such a clock built in. It reports status information
+         via the file /proc/rtc and its behaviour is set by various ioctls on
+         /dev/rtc.
+
 config GEN_RTC
        tristate "Generic /dev/rtc emulation"
        depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV 
&& !S390 && !SUPERH && !AVR32
@@ -866,6 +877,14 @@ config COBALT_LCD
          This option enables support for the LCD display and buttons found
          on Cobalt systems through a misc device.
 
+config SGI_IP30_LEDS
+       bool "SGI Octane LED support"
+       depends on SGI_IP30 && SGI_IOC3
+       help
+         If you say Y here and create a character special file /dev/leds with
+         major number 10 and minor number 42 using mknod ("man mknod"), you
+         will be able to control the lightbar on your Octane.
+
 config DTLK
        tristate "Double Talk PC internal speech card support"
        depends on ISA
diff -Naurp linux-2.6.26.orig/drivers/char/Makefile 
linux-2.6.26/drivers/char/Makefile
--- linux-2.6.26.orig/drivers/char/Makefile     2008-07-13 17:51:29.000000000 
-0400
+++ linux-2.6.26/drivers/char/Makefile  2008-07-25 03:14:40.000000000 -0400
@@ -76,6 +76,8 @@ obj-$(CONFIG_GEN_RTC)         += genrtc.o
 obj-$(CONFIG_EFI_RTC)          += efirtc.o
 obj-$(CONFIG_SGI_DS1286)       += ds1286.o
 obj-$(CONFIG_SGI_IP27_RTC)     += ip27-rtc.o
+obj-$(CONFIG_SGI_IP30_LEDS) += ip30-leds.o
+obj-$(CONFIG_SGI_IP30_RTC) += ip30-rtc.o
 obj-$(CONFIG_DS1302)           += ds1302.o
 obj-$(CONFIG_XILINX_HWICAP)    += xilinx_hwicap/
 ifeq ($(CONFIG_GENERIC_NVRAM),y)
diff -Naurp linux-2.6.26.orig/drivers/char/ip30-leds.c 
linux-2.6.26/drivers/char/ip30-leds.c
--- linux-2.6.26.orig/drivers/char/ip30-leds.c  1969-12-31 19:00:00.000000000 
-0500
+++ linux-2.6.26/drivers/char/ip30-leds.c       2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,280 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ *     Driver for the LEDs in SGI Octane.
+ *
+ *     Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ */
+
+#include <linux/bcd.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+
+#include <linux/miscdevice.h>
+#include <asm/mach-ip30/leds.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/ioc3.h>
+
+#define LEDS_STREAM_SIZE       4096
+
+
+/* hardware dependent LEDs driver */
+static struct ioc3_driver_data *ioc3 = NULL;
+static unsigned int leds_buff;
+
+static void ip30_leds_begin(void)
+{
+       leds_buff = ioc3->gpdr_shadow;
+}
+
+static void ip30_leds_set(int led, unsigned char state)
+{
+       state >>= 7;
+       leds_buff &= ~(1 << led);
+       leds_buff |= state << led;
+}
+
+static void ip30_leds_end(void)
+{
+       ioc3_gpio(ioc3, 3, leds_buff);
+}
+
+
+/* generic LEDs stream interpreter part */
+static spinlock_t leds_lock = SPIN_LOCK_UNLOCKED;
+static int leds_are_open = 0;
+static struct timer_list leds_timer;
+static unsigned char leds_stream[LEDS_STREAM_SIZE];
+static int leds_pc = 0;
+
+static void leds_timer_proc(unsigned long param)
+{
+       unsigned long timer_ms = 0;
+       int end_flag = 0;
+       unsigned char byte1, byte2;
+
+       ip30_leds_begin();
+
+       while (!end_flag) {
+               byte1 = leds_stream[leds_pc++];
+               byte2 = leds_stream[leds_pc++];
+
+               switch (byte1 >> 6) {
+               case LEDS_OP_SET:
+                       ip30_leds_set(byte1 & 0x3f, byte2);
+                       break;
+               case LEDS_OP_LOOP:
+                       leds_pc = 0;
+               case LEDS_OP_WAIT:
+                       timer_ms = ((unsigned long)byte2) << (byte1 & 0x3f);
+                       end_flag = 1;
+                       break;
+               case LEDS_OP_RSVD:
+                       printk(KERN_INFO "ip30-leds: Stream to the future!\n");
+                       leds_pc = 0;
+                       timer_ms = 0;
+                       end_flag = 1;
+                       break;
+               }
+
+               if(leds_pc >= LEDS_STREAM_SIZE) {
+                       printk(KERN_INFO "ip30-leds: The Neverending 
Stream?\n");
+                       leds_pc = 0;
+                       timer_ms = 0;
+                       end_flag = 1;
+               }
+       }
+
+       ip30_leds_end();
+
+       if (timer_ms) {
+               timer_ms = (timer_ms * HZ) / 1000;
+               leds_timer.expires = jiffies + timer_ms;
+               add_timer(&leds_timer);
+       }
+}
+
+static int leds_open(struct inode *inode, struct file *file)
+{
+       spin_lock_irq(&leds_lock);
+       if (leds_are_open) {
+               spin_unlock_irq(&leds_lock);
+               return -EBUSY;
+       }
+       leds_are_open = 1;
+       del_timer(&leds_timer);
+       memset(leds_stream, 0xFF, LEDS_STREAM_SIZE);
+       spin_unlock_irq(&leds_lock);
+
+       return 0;
+}
+
+static int leds_release(struct inode *inode, struct file *file)
+{
+       spin_lock_irq(&leds_lock);
+       leds_are_open = 0;
+       leds_pc = 0;
+       leds_timer.expires = (jiffies + 1);
+       leds_timer.function = leds_timer_proc;
+       add_timer(&leds_timer);
+       spin_unlock_irq(&leds_lock);
+
+       return 0;
+}
+
+static ssize_t leds_write(struct file *file, const char *buf, size_t count, 
loff_t * ppos)
+{
+       if (count > LEDS_STREAM_SIZE)
+               return -ENOSPC;
+       copy_from_user(leds_stream, buf, count);
+       return count;
+}
+
+static struct file_operations leds_fops = {
+       .owner          = THIS_MODULE,
+       .open           = leds_open,
+       .write          = leds_write,
+       .release        = leds_release,
+};
+
+static struct miscdevice leds_dev= {
+       LEDS_MINOR,
+       "leds",
+       &leds_fops
+};
+
+
+/* special hacks */
+static int panic_event(struct notifier_block *this, unsigned long event,
+                      void *ptr)
+{
+       del_timer(&leds_timer);
+       memset(leds_stream, 0xFF, LEDS_STREAM_SIZE);
+
+       leds_stream[0] = 0x00;
+       leds_stream[1] = 0x00;
+       leds_stream[2] = 0x01;
+       leds_stream[3] = 0xFF;
+
+       leds_stream[4] = 0x49;
+       leds_stream[5] = 0x01;
+
+       leds_stream[6] = 0x01;
+       leds_stream[7] = 0x00;
+       leds_stream[8] = 0x00;
+       leds_stream[9] = 0xFF;
+
+       leds_stream[10] = 0x89;
+       leds_stream[11] = 0x01;
+
+       leds_pc = 0;
+       leds_timer.expires = (jiffies + 1);
+       leds_timer.function = leds_timer_proc;
+       add_timer(&leds_timer);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+       .notifier_call  = panic_event,
+};
+
+
+/* IOC3 SuperIO probe */
+static int ioc3led_probe(struct ioc3_submodule *is, struct ioc3_driver_data 
*idd)
+{
+       int i, p = 0;
+       if (ioc3 || idd->class != IOC3_CLASS_BASE_IP30)
+               return 1; /* no sense in setting LEDs on the MENETs */
+
+       ioc3 = idd;
+
+       if (misc_register(&leds_dev)) {
+               printk(KERN_ERR "ip30-leds: There is no place for me here <sob, 
sniff>.\n");
+               return 1;
+       }
+
+       for (i = 0; i < 3; i++) {
+               leds_stream[p++] = 0x00;
+               leds_stream[p++] = 0x00;
+               leds_stream[p++] = 0x01;
+               leds_stream[p++] = 0xff;
+
+               leds_stream[p++] = 0x48;
+               leds_stream[p++] = 0x01;
+
+               leds_stream[p++] = 0x01;
+               leds_stream[p++] = 0x00;
+               leds_stream[p++] = 0x00;
+               leds_stream[p++] = 0xff;
+
+               leds_stream[p++] = 0x48;
+               leds_stream[p++] = 0x01;
+       }
+       leds_stream[p++] = 0x80;
+       leds_stream[p++] = 0x00;
+
+       init_timer(&leds_timer);
+       leds_timer.expires = (jiffies + 1);
+       leds_timer.function = leds_timer_proc;
+       add_timer(&leds_timer);
+
+       atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
+
+       return 0;
+}
+
+static int ioc3led_remove(struct ioc3_submodule *is, struct ioc3_driver_data 
*idd)
+{
+       if (ioc3 != idd)
+               return 1;
+
+       misc_deregister(&leds_dev);
+       ioc3 = NULL;
+       return 0;
+}
+
+
+/* entry/exit functions */
+static struct ioc3_submodule ioc3led_submodule = {
+       .name = "leds",
+       .probe = ioc3led_probe,
+       .remove = ioc3led_remove,
+       .owner = THIS_MODULE,
+};
+
+static int __init leds_init(void)
+{
+       ioc3_register_submodule(&ioc3led_submodule);
+       return 0;
+}
+
+static void __exit leds_exit (void)
+{
+       ioc3_unregister_submodule(&ioc3led_submodule);
+}
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>");
+MODULE_DESCRIPTION("SGI Octane (IP30) LEDS Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("R28");
+
+module_init(leds_init);
+module_exit(leds_exit);
diff -Naurp linux-2.6.26.orig/drivers/char/ip30-rtc.c 
linux-2.6.26/drivers/char/ip30-rtc.c
--- linux-2.6.26.orig/drivers/char/ip30-rtc.c   1969-12-31 19:00:00.000000000 
-0500
+++ linux-2.6.26/drivers/char/ip30-rtc.c        2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,417 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ *     Driver for the Maxim/Dallas DS1687 real time clock in SGI Octane.
+ *
+ *     Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
+ *                        2007 Joshua Kinard <kumba@gentoo.org>
+ *
+ *     Somewhat based on: ip27-rtc.c (userland interface code).
+ */
+
+#include <linux/bcd.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/time.h>
+
+#include <asm/mach-ip30/ds1687.h>
+
+
+/* physical access functions */
+extern spinlock_t rtc_lock;
+static struct ioc3_driver_data *ioc3 = NULL;
+
+static unsigned char ip30_rtc_read(int addr)
+{
+       RTC_ADDR = addr & 0x7f;         /* field is 7-bits wide */
+       return RTC_DATA;
+}
+
+static void ip30_rtc_write(int addr, unsigned char data)
+{
+       RTC_ADDR = addr & 0x7f;         /* field is 7-bits wide */
+       RTC_DATA = data;
+}
+
+
+/* RTC hardware driver */
+static void rtc_begin_access(int bank)
+{
+       unsigned char val = ip30_rtc_read(DS1687_CTRL_B);
+       unsigned long start = jiffies;
+       spin_lock_irq(&rtc_lock);
+       ip30_rtc_write(DS1687_CTRL_B, val | DS1687_BIT7);       /* SET bit */
+       val = ip30_rtc_read(DS1687_CTRL_A);
+       while (val & DS1687_BIT7) {                             /* UIP bit */
+               udelay(10);
+
+               /* 137 is a magic number.  Don't touch! */
+               if (jiffies > start + 137) {
+                       printk(KERN_ERR "ip30-rtc: RTC access lock timeout.\n");
+                       return;
+               }
+               val = ip30_rtc_read(DS1687_CTRL_A);
+       }
+       ip30_rtc_write(DS1687_CTRL_A, (val & 0xef) | (bank << 4));
+}
+
+static void rtc_end_access(void)
+{
+       unsigned char val = ip30_rtc_read(DS1687_CTRL_B);
+       ip30_rtc_write(DS1687_CTRL_B, val & 0x7f);
+       spin_unlock_irq(&rtc_lock);
+}
+
+static void get_rtc_time(struct rtc_time *rtc_tm)
+{
+       rtc_begin_access(1);
+       rtc_tm->tm_sec = ip30_rtc_read(DS1687_SECNDS);
+       rtc_tm->tm_min = ip30_rtc_read(DS1687_MINS);
+       rtc_tm->tm_hour = ip30_rtc_read(DS1687_HOUR);
+       rtc_tm->tm_mday = ip30_rtc_read(DS1687_DATE);
+       rtc_tm->tm_mon = ip30_rtc_read(DS1687_MONTH);
+       rtc_tm->tm_year = ip30_rtc_read(DS1687_YEAR);
+       rtc_tm->tm_year += ip30_rtc_read(DS1687_CENTURY) * 100;
+       rtc_end_access();
+
+       rtc_tm->tm_year -= 1900;
+       rtc_tm->tm_mon--;
+}
+
+static const unsigned char days_in_mo[] =
+{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static int set_rtc_time(struct rtc_time *rtc_tm)
+{
+       unsigned char mon, day, hrs, min, sec, leap_yr;
+       unsigned int yrs;
+
+       yrs = rtc_tm->tm_year + 1900;
+       mon = rtc_tm->tm_mon + 1;   /* tm_mon starts at zero */
+       day = rtc_tm->tm_mday;
+       hrs = rtc_tm->tm_hour;
+       min = rtc_tm->tm_min;
+       sec = rtc_tm->tm_sec;
+
+       leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+       if ((mon > 12) || (day == 0))
+               return -EINVAL;
+
+       if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+               return -EINVAL;
+
+       if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+               return -EINVAL;
+
+       rtc_begin_access(1);
+       ip30_rtc_write(DS1687_SECNDS, sec);
+       ip30_rtc_write(DS1687_MINS, min);
+       ip30_rtc_write(DS1687_HOUR, hrs);
+       ip30_rtc_write(DS1687_DATE, day);
+       ip30_rtc_write(DS1687_MONTH, mon);
+       ip30_rtc_write(DS1687_YEAR, yrs % 100);
+       ip30_rtc_write(DS1687_CENTURY, yrs / 100);
+       rtc_end_access();
+
+       return 0;
+}
+
+
+/* power-down logic */
+static int ip30_soft_powerdown_called;
+static int ip30_clear_irq_called;
+
+int ip30_clear_power_irq(void)
+{
+       unsigned char val;
+
+       if (!ioc3) {
+               ip30_clear_irq_called = 1;
+               return 0;
+       }
+
+       spin_lock_irq(&rtc_lock);
+       val = ip30_rtc_read(DS1687_CTRL_A);
+       ip30_rtc_write(DS1687_CTRL_A, val | DS1687_BIT4);       /* select 
extended regs */
+       ip30_rtc_write(DS1687_EXT_CTRL_4A, 0x00);
+       spin_unlock_irq(&rtc_lock);
+       return 1;
+}
+
+int ip30_can_powerdown(void)
+{
+       if (!ioc3) {
+               ip30_soft_powerdown_called = 1;
+               return 0;
+       }
+       return 1;
+}
+
+void ip30_soft_powerdown(void)
+{
+       unsigned char val;
+       rtc_begin_access(1);
+
+       if (!ioc3) {
+               ip30_soft_powerdown_called = 1;
+               return;
+       }
+
+       /* prepare the RTC for waking us up so we don't wind up dead */
+       val = ip30_rtc_read(DS1687_EXT_CTRL_4B);
+       val &= 0x2a;
+       val |= 0x81;
+       ip30_rtc_write(DS1687_EXT_CTRL_4B, val);
+       rtc_end_access();
+
+       while (1) {
+               ip30_rtc_write(DS1687_EXT_CTRL_4A, DS1687_BIT3);        /* 
power down */
+               udelay(100000);
+       }
+
+       /* there is no way out */
+       /* (of mordor!) */
+}
+
+
+/* userland interface stuff */
+static int rtc_is_open = 0;
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+                    unsigned long arg)
+{
+
+       struct rtc_time wtime;
+
+       switch (cmd) {
+       case RTC_RD_TIME:       /* Read the time/date from RTC  */
+               get_rtc_time(&wtime);
+               return copy_to_user((void *)arg, &wtime, sizeof wtime) ? 
-EFAULT : 0;
+
+       case RTC_SET_TIME:      /* Set the RTC */
+               if (!capable(CAP_SYS_TIME))
+                       return -EACCES;
+
+               if (copy_from_user(&wtime, (struct rtc_time*)arg,
+                                  sizeof(struct rtc_time)))
+                       return -EFAULT;
+
+               return set_rtc_time(&wtime);
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+       spin_lock_irq(&rtc_lock);
+
+       if (rtc_is_open) {
+               spin_unlock_irq(&rtc_lock);
+               return -EBUSY;
+       }
+
+       rtc_is_open = 1;
+       spin_unlock_irq(&rtc_lock);
+
+       return 0;
+}
+
+static int rtc_release(struct inode *inode, struct file *file)
+{
+       spin_lock_irq(&rtc_lock);
+       rtc_is_open = 0;
+       spin_unlock_irq(&rtc_lock);
+
+       return 0;
+}
+
+static struct file_operations rtc_fops = {
+       .owner          = THIS_MODULE,
+       .ioctl          = rtc_ioctl,
+       .open           = rtc_open,
+       .release        = rtc_release,
+};
+
+static struct miscdevice rtc_dev=
+{
+       RTC_MINOR,
+       "rtc",
+       &rtc_fops
+};
+
+
+/* Info exported via "/proc/rtc". */
+static int rtc_get_status(char *buf)
+{
+       char *p;
+       struct rtc_time tm;
+
+       /* Just emulate the standard /proc/rtc */
+       p = buf;
+       get_rtc_time(&tm);
+
+       /*
+        * There is no way to tell if the luser has the RTC set for local
+        * time or for Universal Standard Time (GMT). Probably local though.
+        */
+       p += sprintf(p,
+                    "rtc_time\t: %02d:%02d:%02d\n"
+                    "rtc_date\t: %04d-%02d-%02d\n"
+                    "rtc_epoch\t: %04u\n"
+                    "BCD\t\t: no\n"
+                    "24hr\t\t: yes\n",
+                    tm.tm_hour, tm.tm_min, tm.tm_sec,
+                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900);
+
+       return  p - buf;
+}
+
+static int rtc_read_proc(char *page, char **start, off_t off,
+                                 int count, int *eof, void *data)
+{
+        int len = rtc_get_status(page);
+
+        if (len <= (off + count))
+               *eof = 1;
+
+        *start = (page + off);
+        len -= off;
+
+        if (len > count)
+               len = count;
+
+       /* Block negative time */
+        if (len < 0)
+               len = 0;
+
+        return len;
+}
+
+
+/* general MIPS compatibility */
+unsigned long read_persistent_clock(void)
+{
+       struct rtc_time tm;
+       if (ioc3) {
+               get_rtc_time(&tm);
+               return mktime((tm.tm_year + 1900), (tm.tm_mon + 1),
+                             tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+       }
+
+       /* default value ??? */
+       return mktime(2004, 8, 23, 12, 15, 0);
+}
+
+int rtc_mips_set_time(unsigned long tim)
+{
+       struct rtc_time tm;
+       rtc_time_to_tm(tim, &tm);
+
+       if (ioc3)
+               set_rtc_time(&tm);
+
+       return 0;
+}
+
+
+/* IOC3 SuperIO probe */
+static int ioc3rtc_probe(struct ioc3_submodule *is, struct ioc3_driver_data 
*idd)
+{
+       struct rtc_time wtime;
+
+       if (ioc3 || idd->class != IOC3_CLASS_BASE_IP30)
+               return 1; /* this is good and proper */
+
+       ioc3 = idd;
+
+       if (misc_register(&rtc_dev)) {
+               printk(KERN_ERR "ip30-rtc: Cannot register device.\n");
+               return 1;
+       }
+
+       if (!create_proc_read_entry("driver/rtc", 0, NULL, rtc_read_proc, 
NULL)) {
+               printk(KERN_ERR "ip30-rtc: Cannot create procfs entry.\n");
+               misc_deregister(&rtc_dev);
+               return 1;
+       }
+
+       /* can we set xtime here? */
+       get_rtc_time(&wtime);
+       write_seqlock_irq(&xtime_lock);
+       xtime.tv_sec = mktime((wtime.tm_year + 1900), (wtime.tm_mon + 1),
+                             wtime.tm_mday, wtime.tm_hour, wtime.tm_min,
+                             wtime.tm_sec);
+       xtime.tv_nsec = 0;
+       set_normalized_timespec(&wall_to_monotonic,
+                               -xtime.tv_sec, -xtime.tv_nsec);
+
+       write_sequnlock_irq(&xtime_lock);
+
+       ip30_clear_power_irq();
+
+       if (ip30_clear_irq_called)
+               enable_irq(14);
+
+       if (ip30_soft_powerdown_called)
+               ip30_soft_powerdown();
+
+       return 0;
+}
+
+static int ioc3rtc_remove(struct ioc3_submodule *is, struct ioc3_driver_data 
*idd)
+{
+       if (ioc3 != idd)
+               return 1;
+
+       misc_deregister(&rtc_dev);
+
+       /* TODO: kill proc, although this driver should not be removable anyway 
*/
+       ioc3 = NULL;
+       return 0;
+}
+
+
+/* entry/exit functions */
+static struct ioc3_submodule ioc3rtc_submodule = {
+       .name = "rtc",
+       .probe = ioc3rtc_probe,
+       .remove = ioc3rtc_remove,
+       .owner = THIS_MODULE,
+};
+
+static int __init rtc_init(void)
+{
+       ioc3_register_submodule(&ioc3rtc_submodule);
+       return 0;
+}
+
+static void __exit rtc_exit (void)
+{
+       ioc3_unregister_submodule(&ioc3rtc_submodule);
+}
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>");
+MODULE_DESCRIPTION("SGI Octane (IP30) RTC Interface Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("R28");
+
+module_init(rtc_init);
+module_exit(rtc_exit);
diff -Naurp linux-2.6.26.orig/drivers/usb/host/pci-quirks.c 
linux-2.6.26/drivers/usb/host/pci-quirks.c
--- linux-2.6.26.orig/drivers/usb/host/pci-quirks.c     2008-07-13 
17:51:29.000000000 -0400
+++ linux-2.6.26/drivers/usb/host/pci-quirks.c  2008-07-25 03:14:40.000000000 
-0400
@@ -147,6 +147,9 @@ static void __devinit quirk_usb_handoff_
        unsigned long base = 0;
        int i;
 
+       if (!pci_enable_device(pdev))
+               return;
+
        if (!pio_enabled(pdev))
                return;
 
diff -Naurp linux-2.6.26.orig/drivers/video/Kconfig 
linux-2.6.26/drivers/video/Kconfig
--- linux-2.6.26.orig/drivers/video/Kconfig     2008-07-13 17:51:29.000000000 
-0400
+++ linux-2.6.26/drivers/video/Kconfig  2008-07-25 03:14:40.000000000 -0400
@@ -955,6 +955,18 @@ config FB_ATMEL_STN
 
          If unsure, say N.
 
+config FB_IMPACTSR
+       tristate "SGI Octane ImpactSR graphics support"
+       depends on FB && SGI_IP30
+       help
+         SGI Octane ImpactSR (SI/SSI/MXI/SE/SSE/MXE) graphics card support.
+
+config FB_ODYSSEY
+       tristate "SGI Octane Odyssey graphics support"
+       depends on FB && SGI_IP30
+       help
+         SGI Octane Odyssey (VPro V6/V8/V10/V12) graphics card support.
+
 config FB_NVIDIA
        tristate "nVidia Framebuffer Support"
        depends on FB && PCI
diff -Naurp linux-2.6.26.orig/drivers/video/Makefile 
linux-2.6.26/drivers/video/Makefile
--- linux-2.6.26.orig/drivers/video/Makefile    2008-07-13 17:51:29.000000000 
-0400
+++ linux-2.6.26/drivers/video/Makefile 2008-07-25 03:15:47.000000000 -0400
@@ -117,6 +117,8 @@ obj-$(CONFIG_FB_SM501)            += sm5
 obj-$(CONFIG_FB_XILINX)           += xilinxfb.o
 obj-$(CONFIG_FB_OMAP)             += omap/
 obj-$(CONFIG_XEN_FBDEV_FRONTEND)  += xen-fbfront.o
+obj-$(CONFIG_FB_IMPACTSR)         += impactsr.o
+obj-$(CONFIG_FB_ODYSSEY)          += odyssey.o
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_UVESA)            += uvesafb.o
diff -Naurp linux-2.6.26.orig/drivers/video/impactsr.c 
linux-2.6.26/drivers/video/impactsr.c
--- linux-2.6.26.orig/drivers/video/impactsr.c  1969-12-31 19:00:00.000000000 
-0500
+++ linux-2.6.26/drivers/video/impactsr.c       2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,994 @@
+/*
+ * linux/drivers/video/impactsr.c -- SGI Octane MardiGras (IMPACTSR) graphics
+ *
+ *  Copyright (c) 2004 by Stanislaw Skowronek
+ *
+ *  Based on linux/drivers/video/skeletonfb.c
+ *
+ *  This driver, as most of the IP30 (SGI Octane) port, is a result of massive
+ *  amounts of reverse engineering and trial-and-error. If anyone is interested
+ *  in helping with it, please contact me: <skylark@linux-mips.org>.
+ *
+ *  The basic functions of this driver are filling and blitting rectangles.
+ *  To achieve the latter, two DMA operations are used on Impact. It is unclear
+ *  to me, why is it so, but even Xsgi (the IRIX X11 server) does it this way.
+ *  It seems that fb->fb operations are not operational on these cards.
+ *
+ *  For this purpose, a kernel DMA pool is allocated (pool number 0). This pool
+ *  is (by default) 64kB in size. An ioctl could be used to set the value at
+ *  run-time. Applications can use this pool, however proper locking has to be
+ *  guaranteed. Kernel should be locked out from this pool by an ioctl.
+ *
+ *  The IMPACTSR is quite well worked-out currently, except for the Geometry
+ *  Engines (GE11). Any information about use of those devices would be very
+ *  useful. It would enable a Linux OpenGL driver, as most of OpenGL calls are
+ *  supported directly by the hardware. So far, I can't initialize the GE11.
+ *  Verification of microcode crashes the graphics.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/font.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-ip30/xtalk.h>
+#include <video/impactsr.h>
+
+#define IMPACTSR_KPOOL_SIZE    65536
+
+struct impactsr_par {
+       /* physical mmio base in HEART XTalk space */
+       unsigned long mmio_base;
+
+       /* virtual mmio base in kernel space */
+       unsigned long mmio_virt;
+
+       /* DMA pool management */
+       unsigned int *pool_txtbl[5];
+       unsigned int pool_txnum[5];
+       unsigned int pool_txmax[5];
+       unsigned long pool_txphys[5];
+
+       /* kernel DMA pools */
+       unsigned long **kpool_virt[5];
+       unsigned long *kpool_phys[5];
+       unsigned int kpool_size[5];
+
+       /* board config */
+       unsigned int num_ge, num_rss;
+
+       /* locks to prevent simultaneous user and kernel access */
+       int open_flag;
+       spinlock_t lock;
+};
+
+static struct fb_fix_screeninfo impactsr_fix = {
+       .id =           "ImpactSR 0RSS", 
+       .smem_start =   0,
+       .smem_len =     0,
+       .type =         FB_TYPE_PACKED_PIXELS,
+       .visual =       FB_VISUAL_TRUECOLOR,
+       .xpanstep =     0,
+       .ypanstep =     0,
+       .ywrapstep =    0, 
+       .line_length =  0,
+       .accel =        FB_ACCEL_SGI_IMPACTSR,
+};
+
+static struct fb_var_screeninfo impactsr_var = {
+       .xres =         1280,
+       .yres =         1024,
+       .xres_virtual = 1280,
+       .yres_virtual = 1024,
+       .bits_per_pixel = 24,
+       .red =          { .offset = 0, .length = 8 },
+       .green =        { .offset = 8, .length = 8 },
+       .blue =         { .offset = 16, .length = 8 },
+       .transp =       { .offset = 24, .length = 8 },
+};
+
+static struct fb_info info;
+static unsigned int pseudo_palette[256];
+static struct impactsr_par current_par;
+int impactsr_init(void);
+
+
+/* --------------------- Gory Details --------------------- */
+#define MMIO (((struct impactsr_par *)p->par)->mmio_virt)
+#define PAR (*((struct impactsr_par *)p->par))
+
+static void impactsr_wait_cfifo(struct fb_info *p, int nslots)
+{
+       while ((IMPACTSR_FIFOSTATUS(MMIO) & 0xff) > (IMPACTSR_CFIFO_MAX - 
nslots));
+}
+
+static void impactsr_wait_cfifo_empty(struct fb_info *p)
+{
+       while (IMPACTSR_FIFOSTATUS(MMIO) & 0xff);
+}
+
+static void impactsr_wait_bfifo(struct fb_info *p, int nslots)
+{
+       while ((IMPACTSR_GIOSTATUS(MMIO) & 0x1f) > (IMPACTSR_BFIFO_MAX - 
nslots));
+}
+
+static void impactsr_wait_bfifo_empty(struct fb_info *p)
+{
+       while (IMPACTSR_GIOSTATUS(MMIO) & 0x1f);
+}
+
+static void impactsr_wait_dma(struct fb_info *p)
+{
+       while (IMPACTSR_DMABUSY(MMIO) & 0x1f);
+       while (!(IMPACTSR_STATUS(MMIO) & 1));
+       while (!(IMPACTSR_STATUS(MMIO) & 2));
+       while (!(IMPACTSR_RESTATUS(MMIO) & 0x100));
+}
+static void impactsr_wait_dmaready(struct fb_info *p)
+{
+       IMPACTSR_CFIFOW(MMIO) = 0x000e0100;
+       while (IMPACTSR_DMABUSY(MMIO) & 0x1eff);
+       while (!(IMPACTSR_STATUS(MMIO) & 2));
+}
+
+static void impactsr_inithq4(struct fb_info *p)
+{
+       /* CFIFO parameters */
+       IMPACTSR_CFIFO_HW(MMIO) = 0x47;
+       IMPACTSR_CFIFO_LW(MMIO) = 0x14;
+       IMPACTSR_CFIFO_DELAY(MMIO) = 0x64;
+
+       /* DFIFO parameters */
+       IMPACTSR_DFIFO_HW(MMIO) = 0x40;
+       IMPACTSR_DFIFO_LW(MMIO) = 0x10;
+       IMPACTSR_DFIFO_DELAY(MMIO) = 0;
+}
+
+static void impactsr_initrss(struct fb_info *p)
+{
+       /* transfer mask registers */
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKLSBSA(0xffffff);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKLSBSB(0xffffff);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKMSBS(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMASKLO(0xffffff);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMASKHI(0xffffff);
+
+       /* use the main plane */
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_DRBPOINTERS(0xc8240);
+
+       /* set the RE into vertical flip mode */
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xcac);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XYWIN(0, 0x3ff);
+}
+
+static void impactsr_initxmap(struct fb_info *p)
+{
+       /* set XMAP into 24-bpp mode */
+       IMPACTSR_XMAP_PP1SELECT(MMIO) = 0x01;
+       IMPACTSR_XMAP_INDEX(MMIO) = 0x00;
+       IMPACTSR_XMAP_MAIN_MODE(MMIO) = 0x07a4;
+}
+
+static void impactsr_initvc3(struct fb_info *p)
+{
+       /* cursor-b-gone (disable DISPLAY bit) */
+       IMPACTSR_VC3_INDEXDATA(MMIO) = 0x1d000100;
+}
+
+static void impactsr_initdma(struct fb_info *p)
+{
+       unsigned long pool;
+       /* clear DMA pools */
+       for (pool = 0; pool < 5; pool++) {
+               impactsr_wait_cfifo_empty(p);
+               IMPACTSR_CFIFOPW(MMIO) = IMPACTSR_CMD_HQ_TXBASE(pool);
+               IMPACTSR_CFIFOP(MMIO) = 0x0000000000000009;
+               IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_TXMAX(pool, 0);
+               IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_PGBITS(pool, 0);
+               IMPACTSR_CFIFOP(MMIO) = 0x00484b0400080000|(pool << 40);
+               PAR.pool_txmax[pool] = 0;
+               PAR.pool_txnum[pool] = 0;
+       }
+
+       /* set DMA parameters */
+       IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_PGSIZE(0);
+       IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_STACKPTR(0);
+       IMPACTSR_CFIFOP(MMIO) = 0x00484a0400180000;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_REG32(MMIO,0x40918) = 0x00680000;
+       IMPACTSR_REG32(MMIO,0x40920) = 0x80280000;
+       IMPACTSR_REG32(MMIO,0x40928) = 0x00000000;
+}
+
+static void impactsr_alloctxtbl(struct fb_info *p, int pool, int txmax)
+{
+       dma_addr_t dma_handle;
+       int alloc_size;
+       if (txmax > PAR.pool_txmax[pool]) {     /* grow the pool - unlikely but 
supported */
+               alloc_size = txmax;
+               if (alloc_size < 1024)
+                       alloc_size = 1024;
+               if (PAR.pool_txmax[pool])
+                       dma_free_coherent(NULL, (PAR.pool_txmax[pool] * 4),
+                                         PAR.pool_txtbl[pool],
+                                         PAR.pool_txphys[pool]);
+               PAR.pool_txtbl[pool] = dma_alloc_coherent(NULL, (alloc_size * 
4),
+                                                         &dma_handle, 
GFP_KERNEL);
+               PAR.pool_txphys[pool] = dma_handle;
+               PAR.pool_txmax[pool] = alloc_size;
+       }
+       PAR.pool_txnum[pool] = txmax;
+}
+
+static void impactsr_writetxtbl(struct fb_info *p, int pool)
+{
+       impactsr_wait_cfifo_empty(p);
+
+       /* inform the card about a new DMA pool */
+       IMPACTSR_CFIFOPW(MMIO) = IMPACTSR_CMD_HQ_TXBASE(pool);
+       IMPACTSR_CFIFOP(MMIO) = PAR.pool_txphys[pool];
+       IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_TXMAX(pool, 
PAR.pool_txnum[pool]);
+       IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_PGBITS(pool, 0x0a);
+       IMPACTSR_CFIFOP(MMIO) = (0x00484b0400180000 | ((long)pool << 40));
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+       IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+}
+
+static void impactsr_settxtbl(struct fb_info *p, int pool, unsigned *txtbl,
+                             int txmax)
+{
+       impactsr_alloctxtbl(p, pool, txmax);
+       memcpy(PAR.pool_txtbl[pool], txtbl, (txmax * 4));
+       impactsr_writetxtbl(p, pool);
+}
+
+static void impactsr_resizekpool(struct fb_info *p, int pool, int size,
+                                int growonly)
+{
+       int pages;
+       int i;
+       dma_addr_t dma_handle;
+
+       if (growonly && PAR.kpool_size[pool] >= size)
+               return;
+
+       if (size < 8192)        /* single line smallcopy (1280 * 4) *must* work 
*/
+               size = 8192;
+
+       pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       if (PAR.kpool_size[pool] > 0) {
+               for (i = 0; i < PAR.pool_txnum[pool]; i++) {
+                       
ClearPageReserved(virt_to_page(PAR.kpool_virt[pool][i]));
+                       dma_free_coherent(NULL, PAGE_SIZE, 
PAR.kpool_virt[pool][i],
+                                         PAR.kpool_phys[pool][i]);
+               }
+               vfree(PAR.kpool_phys[pool]);
+               vfree(PAR.kpool_virt[pool]);
+       }
+
+       impactsr_alloctxtbl(p, pool, pages);
+       PAR.kpool_virt[pool] = vmalloc(pages * sizeof(unsigned long));
+       PAR.kpool_phys[pool] = vmalloc(pages * sizeof(unsigned long));
+       for (i = 0; i < PAR.pool_txnum[pool]; i++) {
+               PAR.kpool_virt[pool][i] = dma_alloc_coherent(NULL, PAGE_SIZE,
+                                                            &dma_handle, 
GFP_KERNEL);
+               SetPageReserved(virt_to_page(PAR.kpool_virt[pool][i]));
+               PAR.kpool_phys[pool][i] = dma_handle;
+               PAR.pool_txtbl[pool][i] = PAR.kpool_phys[pool][i] >> PAGE_SHIFT;
+               if (!PAR.kpool_virt[pool][i])
+                       printk(KERN_ERR "impactsr: Page allocation failed!\n");
+       }
+
+       impactsr_writetxtbl(p, pool);
+       PAR.kpool_size[pool] = (pages * PAGE_SIZE);
+}
+
+static void impactsr_rect(struct fb_info *p, int x, int y, int w, int h, 
unsigned c, int lo)
+{
+       impactsr_wait_cfifo_empty(p);
+
+       if (lo == IMPACTSR_LO_COPY)
+               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6300, lo);
+       else
+               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6304, lo);
+
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PACKEDCOLOR(c);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(x, y);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI(x + w - 1, y + h - 1);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+}
+
+static void impactsr_framerect(struct fb_info *p, int x, int y, int w, int h,
+                              unsigned c)
+{
+       impactsr_rect(p, x, y, w, 1, c, IMPACTSR_LO_COPY);
+       impactsr_rect(p, x, (y + h - 1), w, 1, c, IMPACTSR_LO_COPY);
+       impactsr_rect(p, x, y, 1, h, c, IMPACTSR_LO_COPY);
+       impactsr_rect(p, (x + w - 1), y, 1, h, c, IMPACTSR_LO_COPY);
+}
+
+static unsigned long dcntr;
+static void impactsr_debug(struct fb_info *p,int v)
+{
+       int i;
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PIXCMD(3);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PIXELFORMAT(0xe00);
+
+       switch (v) {
+       case 0:
+               for (i = 0; i < 64; i++)
+                       impactsr_rect(p, 4 * (i & 7), 28 - 4 * (i >> 3),
+                                     4, 4, (dcntr & (1L << i)) ? 0xa080ff : 
0x100030,
+                                     IMPACTSR_LO_COPY);
+               break;
+
+       case 1:
+               dcntr++;
+               for (i = 0; i < 64; i++)
+                       impactsr_rect(p, 4 * (i & 7), 28 - 4 * (i >> 3),
+                                     4, 4, (dcntr & (1L << i)) ? 0xff80a0 : 
0x300010,
+                                     IMPACTSR_LO_COPY);
+               break;
+
+       case 2:
+               for (i = 0; i < 64; i++)
+                       impactsr_rect(p, 4 * (i & 7), 28 - 4 * (i >> 3),
+                                     4, 4, (dcntr & (1L << i)) ? 0xa0ff80 : 
0x103000,
+                                     IMPACTSR_LO_COPY);
+       }
+}
+
+static void impactsr_smallcopy(struct fb_info *p, unsigned sx, unsigned sy,
+                              unsigned dx, unsigned dy, unsigned w, unsigned h)
+{
+       if (w < 1 || h < 1)
+               return;
+
+       w=(w + 1) & ~1;
+
+       /* setup and perform DMA from RE to HOST */
+       impactsr_wait_dma(p);
+
+       /* select RSS to read from */
+       if (PAR.num_rss == 2) {
+               if (sy & 1)
+                       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xca5);
+               else
+                       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xca4);
+       } else  /* 1 */
+               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xca4);
+
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PIXCMD(2);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x2200, 
IMPACTSR_LO_COPY);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKLSBSA(0xffffff);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKLSBSB(0xffffff);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKMSBS(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_DRBPOINTERS(0xc8240);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(sx, sy + h - 1);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI(sx + w - 1, sy);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMASKLO(0xffffff);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMASKHI(0xffffff);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRSIZE(w, h);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(w, h);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMODE(0x00080);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0x01000000);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PIXELFORMAT(0x200);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_SCANWIDTH(w << 2);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_DMATYPE(0x0a);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_LIST_0(0x80000000);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_WIDTH(w << 2);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_OFFSET(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_STARTADDR(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_LINECNT(h);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_WIDTHA(w << 2);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCONTROL(8);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(1);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+       IMPACTSR_CFIFOW(MMIO) = 0x00080b04;
+       IMPACTSR_CFIFO(MMIO) = 0x000000b900190204L;
+       IMPACTSR_CFIFOW(MMIO) = 0x00000009;
+       impactsr_wait_dmaready(p);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_RE_TOGGLECNTX(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(0, 0);
+
+       /* setup and perform DMA from HOST to RE */
+       impactsr_wait_dma(p);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xca4);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6200, 
IMPACTSR_LO_COPY);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(dx, dy + h - 1);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI(dx + w - 1, dy);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0x01400000);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMODE(0x00080);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PIXELFORMAT(0x600);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_SCANWIDTH(w << 2);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_DMATYPE(0x0c);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PIXCMD(3);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRSIZE(w, h);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(w, h);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(1);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCONTROL(1);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_LIST_0(0x80000000);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_OFFSET(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_STARTADDR(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_LINECNT(h);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_WIDTHA(w << 2);
+       IMPACTSR_CFIFOW(MMIO) = 0x0080b04;
+       IMPACTSR_CFIFO(MMIO) = 0x000000b1000e0400L;
+
+       impactsr_wait_dma(p);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_RE_TOGGLECNTX(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(0, 0);
+}
+
+static unsigned impactsr_getpalreg(struct fb_info *p, unsigned i)
+{
+       return ((unsigned *)p->pseudo_palette)[i];
+}
+
+
+/* ------------ Accelerated Functions --------------------- */
+static void impactsr_fillrect(struct fb_info *p, const struct fb_fillrect 
*region)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&PAR.lock, flags);
+
+       if (PAR.open_flag) {
+               spin_unlock_irqrestore(&PAR.lock, flags);
+               return;
+       }
+
+       switch (region->rop) {
+       case ROP_XOR:
+               impactsr_rect(p, region->dx, region->dy, region->width, 
region->height,
+                             impactsr_getpalreg(p, region->color), 
IMPACTSR_LO_XOR);
+               break;
+
+       case ROP_COPY:
+       default:
+               impactsr_rect(p, region->dx, region->dy, region->width, 
region->height,
+                             impactsr_getpalreg(p, region->color), 
IMPACTSR_LO_COPY);
+               break;
+       }
+
+       spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+static void impactsr_copyarea(struct fb_info *p, const struct fb_copyarea 
*area) 
+{
+       unsigned sx, sy, dx, dy, w, h;
+       unsigned th, ah;
+       unsigned long flags;
+
+       w = area->width;
+       h = area->height;
+
+       if (w < 1 || h < 1)
+               return;
+
+       spin_lock_irqsave(&PAR.lock, flags);
+       if (PAR.open_flag) {
+               spin_unlock_irqrestore(&PAR.lock, flags);
+               return;
+       }
+
+       sx = area->sx;
+       sy = 0x3ff - (area->sy + h - 1);
+       dx = area->dx;
+       dy = 0x3ff - (area->dy + h - 1);
+       th = PAR.kpool_size[0] / (w * 4);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XYWIN(0, 0);
+
+       if (dy > sy) {
+               dy += h;
+               sy += h;
+               while (h > 0) {
+                       ah = (th > h) ? h : th;
+                       impactsr_smallcopy(p, sx, sy - ah, dx, dy - ah, w, ah);
+                       dy -= ah;
+                       sy -= ah;
+                       h -= ah;
+               }
+       } else {
+               while (h > 0) {
+                       ah = (th > h) ? h : th;
+                       impactsr_smallcopy(p, sx, sy, dx, dy, w, ah);
+                       dy += ah;
+                       sy += ah;
+                       h -= ah;
+               }
+       }
+
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PIXCMD(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PIXELFORMAT(0xe00);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xcac);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XYWIN(0, 0x3ff);
+       spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+/* 8-bpp blits are done as PIO draw operation; the pixels are unpacked into 
32-bpp
+   values from the current palette in software */
+static void impactsr_imageblit_8bpp(struct fb_info *p, const struct fb_image 
*image)
+{
+       int i,u,v;
+       const unsigned char *dp;
+       unsigned pix;
+       unsigned pal[256];
+
+       /* setup PIO to RE */
+       impactsr_wait_cfifo_empty(p);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6300, 
IMPACTSR_LO_COPY);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(image->dx, image->dy);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI((image->dx + 
image->width - 1),
+                                                       (image->dy + 
image->height - 1));
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0x00c00000);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMODE(0x00080);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRSIZE(image->width, 
image->height);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(image->width, 
image->height);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(1);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+
+       /* another workaround.. 33 writes to alpha... hmm... */
+       for (i = 0; i < 33; i++)
+               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_ALPHA(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCONTROL(2);
+
+       /* pairs of pixels are sent in two writes to the RE */
+       i = 0;
+       dp = image->data;
+       for (v = 0; v < 256; v++)
+               pal[v] = impactsr_getpalreg(p, v);
+       for (v = 0; v < image->height; v++) {
+               for (u = 0; u < image->width; u++) {
+                       pix = pal[*(dp++)];
+                       if (i)
+                               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CHAR_L(pix);
+                       else
+                               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CHAR_H(pix);
+                       i ^= 1;
+               }
+       }
+       if (i)
+               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CHAR_L(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_RE_TOGGLECNTX(0);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(0, 0);
+}
+
+/* 1-bpp blits are done as character drawing; the bitmaps are drawn as 8-bit 
wide
+   strips; technically, Impact supports 16-pixel wide characters, but Linux
+   bitmap alignment is 8 bits and most draws are 8 pixels wide (font width), 
anyway */
+static void impactsr_imageblit_1bpp(struct fb_info *p, const struct fb_image 
*image) 
+{
+       int x, y, w, h, b;
+       int u, v, a;
+       const unsigned char *d;
+       impactsr_wait_cfifo_empty(p);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6300, 
IMPACTSR_LO_COPY);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0x400018);
+       a = impactsr_getpalreg(p, image->fg_color);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PACKEDCOLOR(a);
+       a = impactsr_getpalreg(p, image->bg_color);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BKGRD_RG(a & 0xffff);
+       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BKGRD_BA((a & 0xff0000) >> 16);
+
+       x = image->dx;
+       y = image->dy;
+       w = image->width;
+       h = image->height;
+       b = ((w + 7) / 8);
+
+       for (u = 0; u < b; u++) {
+               impactsr_wait_cfifo_empty(p);
+               a = (w < 8) ? w : 8;
+               d = (image->data + u);
+               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(x, y);
+               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI(x + a - 1, y + 
h - 1);
+               IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+               for (v = 0; v < h; v++) {
+                       IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CHAR((*d) << 24);
+                       d += b;
+               }
+               w -= a;
+               x += a;
+       }
+}
+
+static void impactsr_imageblit(struct fb_info *p, const struct fb_image 
*image) 
+{
+       unsigned long flags;
+       spin_lock_irqsave(&PAR.lock, flags);
+       if (PAR.open_flag) {
+               spin_unlock_irqrestore(&PAR.lock, flags);
+               return;
+       }
+
+       switch (image->depth) {
+       case 1:
+               impactsr_imageblit_1bpp(p, image);
+               break;
+       case 8:
+               impactsr_imageblit_8bpp(p, image);
+               break;
+       }
+       spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+static int impactsr_sync(struct fb_info *info)
+{
+       return 0;
+}
+
+static int impactsr_blank(int blank_mode, struct fb_info *info)
+{
+       /* TODO */
+       return 0;
+}
+
+static int impactsr_setcolreg(unsigned regno, unsigned red, unsigned green,
+                             unsigned blue, unsigned transp, struct fb_info 
*info)
+{
+       if (regno > 255)
+               return 1;
+
+       ((unsigned *)info->pseudo_palette)[regno] = (red >> 8) |
+                                                   (green & 0xff00) |
+                                                   ((blue << 8) & 0xff0000);
+       return 0;
+}
+
+
+/* ------------------- Framebuffer Access -------------------- */
+ssize_t impactsr_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+       return -EINVAL;
+}
+
+ssize_t impactsr_write(struct file *file, const char *buf, size_t count,
+                      loff_t *ppos)
+{
+       return -EINVAL;
+}
+
+
+/* --------------------- Userland Access --------------------- */
+int impactsr_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+       return -EINVAL;
+}
+
+int impactsr_mmap(struct fb_info *p, struct vm_area_struct *vma)
+{
+       unsigned pool, i, n;
+       unsigned long size = vma->vm_end - vma->vm_start;
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+       unsigned long start = vma->vm_start;
+
+       switch (offset) {
+       case 0x0000000:
+               if (size != 0x200000)
+                       return -EINVAL;
+
+               if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+                       return -EINVAL;
+
+               offset += MMIO;
+               vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+               vma->vm_flags |= VM_IO;
+               if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, 
size, vma->vm_page_prot))
+                       return -EAGAIN;
+
+               return 0;
+
+       case 0x1000000:
+       case 0x2000000:
+       case 0x3000000:
+       case 0x8000000:
+       case 0x9000000:
+       case 0xa000000:
+       case 0xb000000:
+               if (size > 0x1000000)
+                       return EINVAL;
+
+               pool = (offset >> 24) & 3;
+               impactsr_resizekpool(&info, pool, size, (offset & 0x8000000));
+               n = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+               for (i = 0; i < n; i++) {
+                       if (remap_pfn_range(vma, start,
+                                           PAR.kpool_phys[pool][i] >> 
PAGE_SHIFT,
+                                           PAGE_SIZE, vma->vm_page_prot))
+                               return -EAGAIN;
+                       start += PAGE_SIZE;
+               }
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int impactsr_open(struct fb_info *p, int user)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&PAR.lock, flags);
+        if (user)
+               PAR.open_flag++;
+       spin_unlock_irqrestore(&PAR.lock, flags);
+       return 0;
+}
+
+static int impactsr_release(struct fb_info *p, int user)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&PAR.lock, flags);
+        if (user && PAR.open_flag)
+                PAR.open_flag--;
+       spin_unlock_irqrestore(&PAR.lock, flags);
+       return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Frame buffer operations
+     */
+
+static struct fb_ops impactsr_ops = {
+       .owner          = THIS_MODULE,
+       .fb_read        = impactsr_read,
+       .fb_write       = impactsr_write,
+       .fb_blank       = impactsr_blank,
+       .fb_fillrect    = impactsr_fillrect,
+       .fb_copyarea    = impactsr_copyarea,
+       .fb_imageblit   = impactsr_imageblit,
+       .fb_sync        = impactsr_sync,
+       .fb_ioctl       = impactsr_ioctl,
+       .fb_setcolreg   = impactsr_setcolreg,
+       .fb_mmap        = impactsr_mmap,
+       .fb_open        = impactsr_open,
+       .fb_release     = impactsr_release,
+};
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Private early console
+     */
+
+#define MMIO_FIXED     0x900000001c000000
+
+static void impactsr_earlyrect(int x, int y, int w, int h, unsigned c)
+{
+       while (IMPACTSR_FIFOSTATUS(MMIO_FIXED) & 0xff);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_PP1FILLMODE(0x6300, 
IMPACTSR_LO_COPY);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_FILLMODE(0);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_PACKEDCOLOR(c);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BLOCKXYSTARTI(x, y);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BLOCKXYENDI(x + w - 1, y + h 
- 1);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_IR_ALIAS(0x18);
+}
+static void impactsr_paintchar(int x, int y, unsigned char *b, unsigned c, 
unsigned a)
+{
+       int v;
+       while (IMPACTSR_FIFOSTATUS(MMIO_FIXED) & 0xff);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_PP1FILLMODE(0x6300, 
IMPACTSR_LO_COPY);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_FILLMODE(0x400018);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_PACKEDCOLOR(c);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BKGRD_RG(a & 0xffff);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BKGRD_BA((a&0xff0000) >> 16);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BLOCKXYSTARTI(x, y);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BLOCKXYENDI(x + 7, y + 15);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_IR_ALIAS(0x18);
+       for (v = 0; v < 16; v++)
+               IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_CHAR((*(b++)) << 24);
+}
+static void impactsr_earlyhwinit(void)
+{
+       IMPACTSR_CFIFO_HW(MMIO_FIXED) = 0x47;
+       IMPACTSR_CFIFO_LW(MMIO_FIXED) = 0x14;
+       IMPACTSR_CFIFO_DELAY(MMIO_FIXED) = 0x64;
+       IMPACTSR_DFIFO_HW(MMIO_FIXED) = 0x40;
+       IMPACTSR_DFIFO_LW(MMIO_FIXED) = 0x10;
+       IMPACTSR_DFIFO_DELAY(MMIO_FIXED) = 0;
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_COLORMASKLSBSA(0xffffff);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_COLORMASKLSBSB(0xffffff);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_COLORMASKMSBS(0);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_XFRMASKLO(0xffffff);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_XFRMASKHI(0xffffff);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_DRBPOINTERS(0xc8240);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_CONFIG(0xcac);
+       IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_XYWIN(0, 0x3ff);
+       IMPACTSR_XMAP_PP1SELECT(MMIO_FIXED) = 0x01;
+       IMPACTSR_XMAP_INDEX(MMIO_FIXED) = 0x00;
+       IMPACTSR_XMAP_MAIN_MODE(MMIO_FIXED) = 0x07a4;
+       IMPACTSR_VC3_INDEXDATA(MMIO_FIXED) = 0x1d000100;
+}
+
+static int posx = -1, posy;
+static spinlock_t earlylock = SPIN_LOCK_UNLOCKED;
+
+void impactsr_earlychar(unsigned char c, unsigned f)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&earlylock, flags);
+       if (posx == -1)
+               goto out;
+
+       if (c == '\n') {
+               posy += 16;
+               if (posy >= 1024)
+                       posy = 0;
+               posx = 0;
+               goto out;
+       }
+
+       if (posx == 0) {
+               impactsr_earlyrect(0, posy, 1280, 16, 0x000000);
+               if (posy + 16 < 1024)
+                       impactsr_earlyrect(0, posy + 16, 1280, 2, 0x0000ff);
+       }
+
+       impactsr_paintchar(posx, posy, (unsigned char *)font_vga_8x16.data + (c 
<< 4),
+                          f, 0);
+       posx += 8;
+
+       if (posx >= 1280) {
+               posx = 0;
+               posy++;
+               if (posy >= 1024)
+                       posy = 0;
+       }
+out:
+       spin_unlock_irqrestore(&earlylock, flags);
+}
+void impactsr_earlystring(char *s, unsigned f)
+{
+       while (*s)
+               impactsr_earlychar(*(s++), f);
+}
+void impactsr_earlyinit(void)
+{
+       impactsr_earlyhwinit();
+       impactsr_earlyrect(0, 0, 1280, 1024, 0);
+       posx = 0;
+       posy = 0;
+       impactsr_earlystring("ImpactSR early console ready.\n",0xffffff);
+}
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Initialization
+     */
+
+static void __init impactsr_hwinit(void)
+{
+       /* initialize hardware */
+       impactsr_inithq4(&info);
+       impactsr_initvc3(&info);
+       impactsr_initrss(&info);
+       impactsr_initxmap(&info);
+       impactsr_initdma(&info);
+}
+
+static int __init impactsr_devinit(void)
+{
+       int xwid;
+
+       xwid = ip30_xtalk_find(IMPACTSR_XTALK_MFGR, IMPACTSR_XTALK_PART,
+                              IP30_XTALK_NUM_WID);
+       if (xwid == -1)
+               return -ENODEV;
+
+       current_par.open_flag = 0;
+       current_par.lock = SPIN_LOCK_UNLOCKED;
+
+       current_par.mmio_base = ip30_xtalk_swin(xwid);
+       current_par.mmio_virt = (unsigned long)ioremap(current_par.mmio_base, 
0x200000);
+
+       impactsr_fix.mmio_start = current_par.mmio_base;
+       impactsr_fix.mmio_len = 0x200000;
+
+       info.flags = FBINFO_FLAG_DEFAULT;
+       info.screen_base = NULL;
+       info.fbops = &impactsr_ops;
+       info.fix = impactsr_fix;
+       info.var = impactsr_var;
+       info.par = &current_par;
+       info.pseudo_palette = pseudo_palette;
+
+       /* get board config */
+       current_par.num_ge = IMPACTSR_BDVERS1(current_par.mmio_virt) & 3;
+       current_par.num_rss = current_par.num_ge;
+       info.fix.id[9] = '0' + current_par.num_rss;
+
+       impactsr_hwinit();
+
+       /* initialize buffers */
+       impactsr_resizekpool(&info, 0, 65536, 0);
+       impactsr_resizekpool(&info, 1, 8192, 0);
+       impactsr_resizekpool(&info, 2, 8192, 0);
+       impactsr_resizekpool(&info, 3, 8192, 0);
+       impactsr_resizekpool(&info, 4, 8192, 0);
+
+       /* This has to been done !!! */ 
+       fb_alloc_cmap(&info.cmap, 256, 0);
+
+       if (register_framebuffer(&info) < 0)
+               return -EINVAL;
+
+       printk(KERN_INFO "fb%d: %s frame buffer device\n", info.node,
+                         info.fix.id);
+       return 0;
+}
+
+static int __init impactsr_probe(struct device *dev)
+{
+       return impactsr_devinit();
+}
+
+static struct device_driver impactsr_driver = {
+       .name = "impactsr",
+       .bus = &platform_bus_type,
+       .probe = impactsr_probe,
+       /* add remove someday */
+};
+
+static struct platform_device impactsr_device = {
+       .name = "impactsr",
+};
+
+int __init impactsr_init(void)
+{
+       int ret = driver_register(&impactsr_driver);
+       if (!ret) {
+               ret = platform_device_register(&impactsr_device);
+               if (ret)
+                       driver_unregister(&impactsr_driver);
+       }
+       return ret;
+}
+
+void __exit impactsr_exit(void)
+{
+        driver_unregister(&impactsr_driver);
+}
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>");
+MODULE_DESCRIPTION("SGI Octane (IP30) ImpactSR / HQ4 Video Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("R28");
+
+module_init(impactsr_init);
+module_exit(impactsr_exit);
+
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.26.orig/drivers/video/logo/Kconfig 
linux-2.6.26/drivers/video/logo/Kconfig
--- linux-2.6.26.orig/drivers/video/logo/Kconfig        2008-07-13 
17:51:29.000000000 -0400
+++ linux-2.6.26/drivers/video/logo/Kconfig     2008-07-25 03:14:40.000000000 
-0400
@@ -54,7 +54,7 @@ config LOGO_PARISC_CLUT224
 
 config LOGO_SGI_CLUT224
        bool "224-color SGI Linux logo"
-       depends on SGI_IP22 || SGI_IP27 || SGI_IP32 || X86_VISWS
+       depends on SGI_IP22 || SGI_IP27 || SGI_IP30 || SGI_IP32 || X86_VISWS
        default y
 
 config LOGO_SUN_CLUT224
diff -Naurp linux-2.6.26.orig/drivers/video/odyssey.c 
linux-2.6.26/drivers/video/odyssey.c
--- linux-2.6.26.orig/drivers/video/odyssey.c   1969-12-31 19:00:00.000000000 
-0500
+++ linux-2.6.26/drivers/video/odyssey.c        2008-07-25 03:14:40.000000000 
-0400
@@ -0,0 +1,1055 @@
+/*
+ * linux/drivers/video/odyssey.c -- SGI Octane Odyssey graphics
+ *
+ *  Copyright (c) 2005 by Stanislaw Skowronek
+ *
+ *  This driver, as most of the IP30 (SGI Octane) port, is a result of massive
+ *  amounts of reverse engineering and trial-and-error. If anyone is interested
+ *  in helping with it, please contact me: <skylark@linux-mips.org>.
+ *
+ *  Note: the driver is specialcased for 8x16 font (will be a bit faster).
+ *
+ *  Odyssey is a really cool graphics device. It is a dual-chip OpenGL
+ *  implementation with ARB_imaging support, and overall a very elegant design.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/font.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-ip30/xtalk.h>
+#include <video/odyssey.h>
+
+struct odyssey_par {
+       /* physical mmio base in HEART XTalk space */
+       unsigned long mmio_base;
+
+       /* virtual mmio base in kernel space */
+       unsigned long mmio_virt;
+
+       /* locks to prevent simultaneous user and kernel access */
+       int open_flag;
+       int mmap_flag;
+       spinlock_t lock;
+};
+
+static struct fb_fix_screeninfo odyssey_fix = {
+       .id =           "Odyssey", 
+       .smem_start =   0,
+       .smem_len =     0,
+       .type =         FB_TYPE_PACKED_PIXELS,
+       .visual =       FB_VISUAL_TRUECOLOR,
+       .xpanstep =     0,
+       .ypanstep =     0,
+       .ywrapstep =    0, 
+       .line_length =  0,
+       .accel =        FB_ACCEL_SGI_ODYSSEY,
+};
+
+static struct fb_var_screeninfo odyssey_var = {
+       .xres =         1280,
+       .yres =         1024,
+       .xres_virtual = 1280,
+       .yres_virtual = 1024,
+       .bits_per_pixel = 24,
+       .red =          { .offset = 0, .length = 8 },
+       .green =        { .offset = 8, .length = 8 },
+       .blue =         { .offset = 16, .length = 8 },
+       .transp =       { .offset = 24, .length = 8 },
+};
+
+static struct fb_info info;
+static unsigned int pseudo_palette[256];
+static struct odyssey_par current_par;
+int odyssey_init(void);
+
+
+/* Most of the hex numbers seen in the various functions, especially those in
+ * the hardware init functions, were discovered via reverse engineering of IRIX
+ * drivers.  Little is known as to what they do or what they mean.
+ *
+ * Possibly, these hex numbers are addresses to locations outside of what we
+ * perceive as normal reality, and instead reference a location within the void
+ * itself, from which various dark and black magiks flow forth and breathe life
+ * into this hardware.
+ *
+ * If you think you can come up with a better explanation, then feel free to
+ * send a patch!
+ */
+
+
+/* --------------------- Gory Details --------------------- */
+#define MMIO (((struct odyssey_par *)p->par)->mmio_virt)
+#define PAR (*((struct odyssey_par *)p->par))
+
+static unsigned int pack_ieee754(int val)
+{
+       unsigned sign,exp;
+
+       if (!val)
+               return 0;
+       sign = (val & 0x80000000);
+
+       if (sign)
+               val = -val;
+
+       if (val & 0xff000000)
+               return 0;
+
+       exp = 150;
+       while (!(val & 0x00800000)) {
+               val << =1;
+               exp--;
+       }
+
+       return sign | (exp << 23) | (val & 0x007fffff);
+}
+
+static void odyssey_wait_cfifo(unsigned long mmio)
+{
+       while (!(ODY_STATUS0(mmio) & ODY_STATUS0_CFIFO_LW));
+}
+
+static void odyssey_wait_dfifo(unsigned long mmio, int lw)
+{
+       while ((ODY_DBESTAT(mmio) & 0x7f) > lw);
+}
+
+static void odyssey_dfifo_write(unsigned long mmio, unsigned reg, unsigned val)
+{
+       ODY_DFIFO_D(mmio) = ((unsigned long)(0x30000001 | (reg << 14)) << 32) | 
val;
+}
+
+static void odyssey_flush(unsigned long mmio)
+{
+       odyssey_wait_cfifo(mmio);
+       ODY_CFIFO_W(mmio) = 0x00010443;
+       ODY_CFIFO_W(mmio) = 0x000000fa;
+       ODY_CFIFO_W(mmio) = 0x00010046;
+       ODY_CFIFO_W(mmio) = 0x00010046;
+       ODY_CFIFO_W(mmio) = 0x00010019;
+       ODY_CFIFO_W(mmio) = 0x00010443;
+       ODY_CFIFO_W(mmio) = 0x000000