diff -Naurp linux-2.6.27.1-20081010.orig/arch/mips/Kconfig linux-2.6.27.1-20081010/arch/mips/Kconfig --- linux-2.6.27.1-20081010.orig/arch/mips/Kconfig 2008-10-17 03:36:12.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/Kconfig 2008-10-17 03:36:27.000000000 -0400 @@ -397,6 +397,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 @@ -722,6 +747,9 @@ config DMA_COHERENT config DMA_IP27 bool +config DMA_IP30 + bool + config DMA_NONCOHERENT bool select DMA_NEED_PCI_MAP_STATE @@ -914,7 +942,7 @@ config BOOT_ELF32 config MIPS_L1_CACHE_SHIFT int default "4" if MACH_DECSTATION || MIKROTIK_RB532 - 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" @@ -927,12 +955,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.27.1-20081010.orig/arch/mips/Makefile linux-2.6.27.1-20081010/arch/mips/Makefile --- linux-2.6.27.1-20081010.orig/arch/mips/Makefile 2008-10-17 03:20:28.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/Makefile 2008-10-17 03:36:27.000000000 -0400 @@ -464,6 +464,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.27.1-20081010.orig/arch/mips/fw/arc/init.c linux-2.6.27.1-20081010/arch/mips/fw/arc/init.c --- linux-2.6.27.1-20081010.orig/arch/mips/fw/arc/init.c 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/fw/arc/init.c 2008-10-17 03:36:27.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.27.1-20081010.orig/arch/mips/kernel/cevt-r4k.c linux-2.6.27.1-20081010/arch/mips/kernel/cevt-r4k.c --- linux-2.6.27.1-20081010.orig/arch/mips/kernel/cevt-r4k.c 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/kernel/cevt-r4k.c 2008-10-17 03:36:27.000000000 -0400 @@ -181,7 +181,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.27.1-20081010.orig/arch/mips/kernel/setup.c linux-2.6.27.1-20081010/arch/mips/kernel/setup.c --- linux-2.6.27.1-20081010.orig/arch/mips/kernel/setup.c 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/kernel/setup.c 2008-10-17 03:36:27.000000000 -0400 @@ -475,6 +475,10 @@ static void __init arch_mem_init(char ** pr_info("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); @@ -565,7 +569,6 @@ void __init setup_arch(char **cmdline_p) #endif arch_mem_init(cmdline_p); - resource_init(); plat_smp_setup(); } diff -Naurp linux-2.6.27.1-20081010.orig/arch/mips/mm/dma-default.c linux-2.6.27.1-20081010/arch/mips/mm/dma-default.c --- linux-2.6.27.1-20081010.orig/arch/mips/mm/dma-default.c 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/mm/dma-default.c 2008-10-17 03:36:27.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.27.1-20081010.orig/arch/mips/pci/Makefile linux-2.6.27.1-20081010/arch/mips/pci/Makefile --- linux-2.6.27.1-20081010.orig/arch/mips/pci/Makefile 2008-10-17 03:36:12.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/pci/Makefile 2008-10-17 03:36:27.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.27.1-20081010.orig/arch/mips/pci/ops-bridge.c linux-2.6.27.1-20081010/arch/mips/pci/ops-bridge.c --- linux-2.6.27.1-20081010.orig/arch/mips/pci/ops-bridge.c 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/pci/ops-bridge.c 2008-10-17 03:36:27.000000000 -0400 @@ -9,9 +9,15 @@ #include #include #include + +#ifdef CONFIG_SGI_IP30 +#include +#include +#else #include #include #include +#endif /* * Most of the IOC3 PCI config register aren't present diff -Naurp linux-2.6.27.1-20081010.orig/arch/mips/pci/pci-ip30.c linux-2.6.27.1-20081010/arch/mips/pci/pci-ip30.c --- linux-2.6.27.1-20081010.orig/arch/mips/pci/pci-ip30.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/pci/pci-ip30.c 2008-10-17 03:36:27.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 +#include +#include + +#include +#include +#include +#include + +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.27.1-20081010.orig/arch/mips/pci/pci.c linux-2.6.27.1-20081010/arch/mips/pci/pci.c --- linux-2.6.27.1-20081010.orig/arch/mips/pci/pci.c 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/arch/mips/pci/pci.c 2008-10-17 03:36:27.000000000 -0400 @@ -76,6 +76,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) { @@ -144,6 +148,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) { @@ -167,6 +175,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; @@ -175,6 +184,10 @@ static int pcibios_enable_resources(stru if (!(mask & (1<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.27.1-20081010.orig/arch/mips/sgi-ip30/Makefile linux-2.6.27.1-20081010/arch/mips/sgi-ip30/Makefile --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/Makefile 2008-10-17 03:36:27.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.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-err.c linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-err.c --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-err.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-err.c 2008-10-17 03:36:27.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 + * 2007 Joshua Kinard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-irq.c linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-irq.c --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-irq.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-irq.c 2008-10-17 03:36:27.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 + * 2007 Joshua Kinard + * + * Inspired by ip27-irq.c and ip32-irq.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-power.c linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-power.c --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-power.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-power.c 2008-10-17 03:37:24.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 + * 2007 Joshua Kinard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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_cad_pid(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.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-setup.c linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-setup.c --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-setup.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-setup.c 2008-10-17 03:36:27.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 + * 2007 Joshua Kinard + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-smp-glue.S linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-smp-glue.S --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-smp-glue.S 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-smp-glue.S 2008-10-17 03:36:27.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 +#include +#include +#include + + .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.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-smp.c linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-smp.c --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-smp.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-smp.c 2008-10-17 03:36:27.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 + * 2006-2007 Joshua Kinard + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-timer.c linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-timer.c --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-timer.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-timer.c 2008-10-17 03:36:27.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 + * Inspired by ip32-timer.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-xtalk.c linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-xtalk.c --- linux-2.6.27.1-20081010.orig/arch/mips/sgi-ip30/ip30-xtalk.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/arch/mips/sgi-ip30/ip30-xtalk.c 2008-10-17 03:36:27.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 + * 2007 Joshua Kinard + * + * XIO bus probing code + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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.27.1-20081010.orig/drivers/char/Kconfig linux-2.6.27.1-20081010/drivers/char/Kconfig --- linux-2.6.27.1-20081010.orig/drivers/char/Kconfig 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/drivers/char/Kconfig 2008-10-17 03:36:27.000000000 -0400 @@ -834,6 +834,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 @@ -874,6 +885,14 @@ config DS1302 endif # RTC_LIB +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.27.1-20081010.orig/drivers/char/Makefile linux-2.6.27.1-20081010/drivers/char/Makefile --- linux-2.6.27.1-20081010.orig/drivers/char/Makefile 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/drivers/char/Makefile 2008-10-17 03:36:27.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.27.1-20081010.orig/drivers/char/ip30-leds.c linux-2.6.27.1-20081010/drivers/char/ip30-leds.c --- linux-2.6.27.1-20081010.orig/drivers/char/ip30-leds.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/drivers/char/ip30-leds.c 2008-10-17 03:36:27.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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#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 .\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 "); +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.27.1-20081010.orig/drivers/char/ip30-rtc.c linux-2.6.27.1-20081010/drivers/char/ip30-rtc.c --- linux-2.6.27.1-20081010.orig/drivers/char/ip30-rtc.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/drivers/char/ip30-rtc.c 2008-10-17 03:36:27.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 + * 2007 Joshua Kinard + * + * Somewhat based on: ip27-rtc.c (userland interface code). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +/* 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 "); +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.27.1-20081010.orig/drivers/usb/host/pci-quirks.c linux-2.6.27.1-20081010/drivers/usb/host/pci-quirks.c --- linux-2.6.27.1-20081010.orig/drivers/usb/host/pci-quirks.c 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/drivers/usb/host/pci-quirks.c 2008-10-17 03:36:27.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.27.1-20081010.orig/drivers/video/Kconfig linux-2.6.27.1-20081010/drivers/video/Kconfig --- linux-2.6.27.1-20081010.orig/drivers/video/Kconfig 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/drivers/video/Kconfig 2008-10-17 03:36:27.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.27.1-20081010.orig/drivers/video/Makefile linux-2.6.27.1-20081010/drivers/video/Makefile --- linux-2.6.27.1-20081010.orig/drivers/video/Makefile 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27.1-20081010/drivers/video/Makefile 2008-10-17 03:36:27.000000000 -0400 @@ -121,6 +121,8 @@ obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_ obj-$(CONFIG_FB_OMAP) += omap/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.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.27.1-20081010.orig/drivers/video/impactsr.c linux-2.6.27.1-20081010/drivers/video/impactsr.c --- linux-2.6.27.1-20081010.orig/drivers/video/impactsr.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.27.1-20081010/drivers/video/impactsr.c 2008-10-17 03:36:27.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: . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include