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 = ¤t_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 |