diff -Naurp linux-2.6.15.7.orig/arch/mips/Kconfig linux-2.6.15.7/arch/mips/Kconfig --- linux-2.6.15.7.orig/arch/mips/Kconfig 2006-04-12 23:01:42.000000000 -0400 +++ linux-2.6.15.7/arch/mips/Kconfig 2006-04-12 23:07:13.000000000 -0400 @@ -577,6 +577,21 @@ config SGI_IP27 workstations. To compile a Linux kernel that runs on these, say Y here. +config SGI_IP30 + bool "Support for SGI IP30 (Octane/Octane2)" + select ARC + select ARC64 + select BOOT_ELF64 + select DMA_IP30 + select HW_HAS_PCI + select PCI_DOMAINS + select SYS_HAS_CPU_R10000 + select SYS_SUPPORTS_64BIT_KERNEL + select SYS_SUPPORTS_BIG_ENDIAN + 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 "Support for SGI IP32 (O2) (EXPERIMENTAL)" depends on EXPERIMENTAL @@ -816,6 +831,9 @@ config DMA_COHERENT config DMA_IP27 bool +config DMA_IP30 + bool + config DMA_IP32 bool select DMA_NEED_PCI_MAP_STATE @@ -1010,7 +1028,7 @@ config BOOT_ELF32 config MIPS_L1_CACHE_SHIFT int default "4" if MACH_DECSTATION - default "7" if SGI_IP27 + default "7" if SGI_IP27 || SGI_IP30 default "5" config HAVE_STD_PC_SERIAL_PORT @@ -1025,12 +1043,12 @@ config ARC_CONSOLE config ARC_MEMORY bool - depends on MACH_JAZZ || SNI_RM200_PCI || SGI_IP32 + depends on MACH_JAZZ || SNI_RM200_PCI || SGI_IP30 || SGI_IP32 default y config ARC_PROMLIB bool - depends on MACH_JAZZ || SNI_RM200_PCI || SGI_IP22 || SGI_IP32 + depends on MACH_JAZZ || SNI_RM200_PCI || SGI_IP22 || SGI_IP30 || SGI_IP32 default y config ARC64 @@ -1577,7 +1595,7 @@ source "mm/Kconfig" config SMP bool "Multi-Processing support" - depends on CPU_RM9000 || ((SIBYTE_BCM1x80 || SIBYTE_BCM1x55 || SIBYTE_SB1250) && !SIBYTE_STANDALONE) || SGI_IP27 || MIPS_MT_SMP + depends on CPU_RM9000 || ((SIBYTE_BCM1x80 || SIBYTE_BCM1x55 || SIBYTE_SB1250) && !SIBYTE_STANDALONE) || SGI_IP27 || SGI_IP30 || MIPS_MT_SMP ---help--- This enables support for systems with more than one CPU. If you have a system with only one CPU, like most personal computers, say N. If @@ -1602,6 +1620,7 @@ config NR_CPUS range 2 64 depends on SMP default "64" if SGI_IP27 + default "4" if SGI_IP30 default "2" help This allows you to specify the maximum number of CPUs which this diff -Naurp linux-2.6.15.7.orig/arch/mips/Makefile linux-2.6.15.7/arch/mips/Makefile --- linux-2.6.15.7.orig/arch/mips/Makefile 2006-04-12 22:51:40.000000000 -0400 +++ linux-2.6.15.7/arch/mips/Makefile 2006-04-12 23:07:13.000000000 -0400 @@ -636,6 +636,15 @@ endif endif # +# 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.15.7.orig/arch/mips/kernel/setup.c linux-2.6.15.7/arch/mips/kernel/setup.c --- linux-2.6.15.7.orig/arch/mips/kernel/setup.c 2006-04-12 22:51:40.000000000 -0400 +++ linux-2.6.15.7/arch/mips/kernel/setup.c 2006-04-12 23:07:13.000000000 -0400 @@ -156,7 +156,6 @@ static inline void parse_cmdline_early(v printk("Determined physical RAM map:\n"); print_memory_map(); - for (;;) { /* * "mem=XXX[kKmM]" defines a memory region from @@ -430,11 +429,11 @@ static inline void bootmem_init(void) printk("Initial ramdisk at: 0x%p (%lu bytes)\n", (void *)initrd_start, initrd_size); - if (CPHYSADDR(initrd_end) > PFN_PHYS(max_low_pfn)) { + if (kernel_physaddr(initrd_end) > PFN_PHYS(max_low_pfn)) { printk("initrd extends beyond end of memory " "(0x%0*Lx > 0x%0*Lx)\ndisabling initrd\n", sizeof(long) * 2, - (unsigned long long)CPHYSADDR(initrd_end), + (unsigned long long)kernel_physaddr(initrd_end), sizeof(long) * 2, (unsigned long long)PFN_PHYS(max_low_pfn)); initrd_start = initrd_end = 0; @@ -456,10 +455,10 @@ static inline void resource_init(void) * The 64bit code in 32bit object format trick can't represent * 64bit wide relocations for linker script symbols. */ - code_resource.start = CPHYSADDR(&_text); - code_resource.end = CPHYSADDR(&_etext) - 1; - data_resource.start = CPHYSADDR(&_etext); - data_resource.end = CPHYSADDR(&_edata) - 1; + code_resource.start = kernel_physaddr(&_text); + code_resource.end = kernel_physaddr(&_etext) - 1; + data_resource.start = kernel_physaddr(&_etext); + data_resource.end = kernel_physaddr(&_edata) - 1; #else code_resource.start = virt_to_phys(&_text); code_resource.end = virt_to_phys(&_etext) - 1; @@ -534,6 +533,10 @@ void __init setup_arch(char **cmdline_p) /* call board setup routine */ plat_setup(); +#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(saved_command_line, command_line, COMMAND_LINE_SIZE); diff -Naurp linux-2.6.15.7.orig/arch/mips/mm/Makefile linux-2.6.15.7/arch/mips/mm/Makefile --- linux-2.6.15.7.orig/arch/mips/mm/Makefile 2006-01-02 22:21:10.000000000 -0500 +++ linux-2.6.15.7/arch/mips/mm/Makefile 2006-04-12 23:07:13.000000000 -0400 @@ -39,6 +39,7 @@ obj-$(CONFIG_DMA_COHERENT) += dma-cohere obj-$(CONFIG_DMA_NONCOHERENT) += dma-noncoherent.o endif obj-$(CONFIG_DMA_IP27) += dma-ip27.o +obj-$(CONFIG_DMA_IP30) += dma-ip30.o obj-$(CONFIG_DMA_IP32) += dma-ip32.o EXTRA_AFLAGS := $(CFLAGS) diff -Naurp linux-2.6.15.7.orig/arch/mips/mm/dma-ip30.c linux-2.6.15.7/arch/mips/mm/dma-ip30.c --- linux-2.6.15.7.orig/arch/mips/mm/dma-ip30.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/mm/dma-ip30.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,279 @@ +/* + * 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) 2000 Ani Joshi + * Copyright (C) 2000, 2001 Ralf Baechle + * swiped from i386, and cloned for MIPS by Geert, polished by Ralf. + * Fixed for IP30: Stanislaw Skowronek 2005 + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +static inline dma_addr_t pdev_to_baddr(struct pci_dev *dev, dma_addr_t addr, int virt, int size) +{ + if(dev->dma_mask == 0xFFFFFFFFFFFFFFFFUL) { /* 64-bit DMA */ + if(virt) + return (BRIDGE_CONTROLLER(dev->bus)->baddr + addr) | PCI64_ATTR_VIRTUAL; + if(size >= 0x200) + return (BRIDGE_CONTROLLER(dev->bus)->baddr + addr) | PCI64_ATTR_PREF; + if(addr >= 0x20000000 || addr < 0xA0000000) + return PCI32_DIRECT_BASE + addr - 0x20000000; + return (BRIDGE_CONTROLLER(dev->bus)->baddr + addr); + } + if(addr < 0x20000000 || addr >= 0xA0000000) { + printk(KERN_ERR "BRIDGE: Mapping can't be realized in direct DMA.\n"); + return -1; + } + return PCI32_DIRECT_BASE + addr - 0x20000000; +} + +static inline dma_addr_t dev_to_baddr(struct device *dev, dma_addr_t addr, int virt, int size) +{ + if(dev) + return pdev_to_baddr(to_pci_dev(dev), addr, virt, size); + return addr; +} + +void *dma_alloc_noncoherent(struct device *dev, size_t size, + dma_addr_t * dma_handle, unsigned int __nocast gfp) +{ + void *ret; + + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) + gfp |= GFP_DMA; + ret = (void *) __get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = dev_to_baddr(dev, virt_to_phys(ret), 1, size); + } + + return ret; +} + +EXPORT_SYMBOL(dma_alloc_noncoherent); + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t * dma_handle, unsigned int __nocast gfp) + __attribute__((alias("dma_alloc_noncoherent"))); + +EXPORT_SYMBOL(dma_alloc_coherent); + +void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + unsigned long addr = (unsigned long) vaddr; + + free_pages(addr, get_order(size)); +} + +EXPORT_SYMBOL(dma_free_noncoherent); + +void dma_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle) __attribute__((alias("dma_free_noncoherent"))); + +EXPORT_SYMBOL(dma_free_coherent); + +dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); + + return dev_to_baddr(dev, __pa(ptr), 0, size); +} + +EXPORT_SYMBOL(dma_map_single); + +void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_unmap_single); + +int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ + int i; + + BUG_ON(direction == DMA_NONE); + + for (i = 0; i < nents; i++, sg++) { + sg->dma_address = (dma_addr_t) dev_to_baddr(dev, + page_to_phys(sg->page) + sg->offset, 1, sg->length); + } + + return nents; +} + +EXPORT_SYMBOL(dma_map_sg); + +dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); + + return dev_to_baddr(dev, page_to_phys(page) + offset, 0, size); +} + +EXPORT_SYMBOL(dma_map_page); + +void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_unmap_page); + +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_unmap_sg); + +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_single_for_cpu); + +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_single_for_device); + +void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_single_range_for_cpu); + +void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_single_range_for_device); + +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_sg_for_device); + +int dma_mapping_error(dma_addr_t dma_addr) +{ + return 0; +} + +EXPORT_SYMBOL(dma_mapping_error); + +int dma_supported(struct device *dev, u64 mask) +{ + /* + * we fall back to GFP_DMA when the mask isn't all 1s, + * so we can't guarantee allocations that must be + * within a tighter range than GFP_DMA.. + */ + if (mask < 0x00ffffff) + return 0; + + return 1; +} + +EXPORT_SYMBOL(dma_supported); + +int dma_is_consistent(dma_addr_t dma_addr) +{ + return 1; +} + +EXPORT_SYMBOL(dma_is_consistent); + +void dma_cache_sync(void *vaddr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_cache_sync); + +dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, + struct page *page, unsigned long offset, int direction) +{ + dma64_addr_t addr = page_to_phys(page) + offset; + + return (dma64_addr_t) pdev_to_baddr(pdev, addr, 0, PAGE_SIZE); +} + +EXPORT_SYMBOL(pci_dac_page_to_dma); + +struct page *pci_dac_dma_to_page(struct pci_dev *pdev, + dma64_addr_t dma_addr) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus); + + return pfn_to_page((dma_addr - bc->baddr) >> PAGE_SHIFT); +} + +EXPORT_SYMBOL(pci_dac_dma_to_page); + +unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, + dma64_addr_t dma_addr) +{ + return dma_addr & ~PAGE_MASK; +} + +EXPORT_SYMBOL(pci_dac_dma_to_offset); + +void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, + dma64_addr_t dma_addr, size_t len, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); +} + +EXPORT_SYMBOL(pci_dac_dma_sync_single_for_cpu); + +void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, + dma64_addr_t dma_addr, size_t len, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); +} + +EXPORT_SYMBOL(pci_dac_dma_sync_single_for_device); diff -Naurp linux-2.6.15.7.orig/arch/mips/mm/init.c linux-2.6.15.7/arch/mips/mm/init.c --- linux-2.6.15.7.orig/arch/mips/mm/init.c 2006-01-02 22:21:10.000000000 -0500 +++ linux-2.6.15.7/arch/mips/mm/init.c 2006-04-12 23:07:13.000000000 -0400 @@ -160,8 +160,19 @@ void __init paging_init(void) zones_size[ZONE_NORMAL] = low - max_dma; } #else +#ifdef CONFIG_SGI_IP30 + max_dma = 0xA0000000UL >> PAGE_SHIFT; + if (low < max_dma) + zones_size[ZONE_DMA] = low; + else { + printk(KERN_INFO "SGI Octane system with >2GB physical memory, limiting DMA.\n"); + zones_size[ZONE_DMA] = max_dma; + zones_size[ZONE_NORMAL] = low - max_dma; + } +#else zones_size[ZONE_DMA] = low; #endif +#endif #ifdef CONFIG_HIGHMEM if (cpu_has_dc_aliases) { printk(KERN_WARNING "This processor doesn't support highmem."); @@ -261,13 +272,22 @@ void __init mem_init(void) } #endif /* !CONFIG_NEED_MULTIPLE_NODES */ +#ifdef CONFIG_64BIT +unsigned long kernel_physaddr(unsigned long kva) +{ + if(kva&0xffffffff00000000UL==0xffffffff00000000UL) + return CPHYSADDR(kva); + return XPHYSADDR(kva); +} +#endif + #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { #ifdef CONFIG_64BIT /* Switch from KSEG0 to XKPHYS addresses */ - start = (unsigned long)phys_to_virt(CPHYSADDR(start)); - end = (unsigned long)phys_to_virt(CPHYSADDR(end)); + start = (unsigned long)phys_to_virt(kernel_physaddr(start)); + end = (unsigned long)phys_to_virt(kernel_physaddr(end)); #endif if (start < end) printk(KERN_INFO "Freeing initrd memory: %ldk freed\n", @@ -293,7 +313,7 @@ void free_initmem(void) addr = (unsigned long) &__init_begin; while (addr < (unsigned long) &__init_end) { #ifdef CONFIG_64BIT - page = PAGE_OFFSET | CPHYSADDR(addr); + page = PAGE_OFFSET | kernel_physaddr(addr); #else page = addr; #endif diff -Naurp linux-2.6.15.7.orig/arch/mips/pci/Makefile linux-2.6.15.7/arch/mips/pci/Makefile --- linux-2.6.15.7.orig/arch/mips/pci/Makefile 2006-04-12 23:01:42.000000000 -0400 +++ linux-2.6.15.7/arch/mips/pci/Makefile 2006-04-12 23:07:13.000000000 -0400 @@ -44,6 +44,7 @@ obj-$(CONFIG_MOMENCO_OCELOT_G) += fixup- obj-$(CONFIG_PMC_YOSEMITE) += fixup-yosemite.o ops-titan.o ops-titan-ht.o \ pci-yosemite.o obj-$(CONFIG_SGI_IP27) += pci-ip27.o +obj-$(CONFIG_SGI_IP30) += 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_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o diff -Naurp linux-2.6.15.7.orig/arch/mips/pci/pci-ip30.c linux-2.6.15.7/arch/mips/pci/pci-ip30.c --- linux-2.6.15.7.orig/arch/mips/pci/pci-ip30.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/pci/pci-ip30.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,484 @@ +/* + * 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,5 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); + +/* + * Max #PCI busses we can handle; ie, max #PCI bridges. + */ +#define MAX_PCI_BUSSES 8 + +/* + * Max #PCI devices (like scsi controllers) we handle on a bus. + */ +#define MAX_DEVICES_PER_PCIBUS 8 + +/* + * 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[MAX_PCI_BUSSES]; + +/* + * Translate from irq to software PCI bus number and PCI slot. + */ +struct bridge_controller *irq_to_bridge[MAX_PCI_BUSSES * MAX_DEVICES_PER_PCIBUS]; +int irq_to_slot[MAX_PCI_BUSSES * MAX_DEVICES_PER_PCIBUS]; + +/* + * The Bridge ASIC supports both type 0 and type 1 access. Type 1 is + * not really documented, so right now I can't write code which uses it. + * Therefore we use type 0 accesses for now even though they won't work + * correcly for PCI-to-PCI bridges. + * + * The function is complicated by the ultimate brokeness of the IOC3 chip + * which is used in SGI systems. The IOC3 can only handle 32-bit PCI + * accesses and does only decode parts of it's address space. + */ + +static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * value) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); + bridge_t *bridge = bc->base; + int slot = PCI_SLOT(devfn); + int fn = PCI_FUNC(devfn); + volatile void *addr; + u32 cf, shift, mask; + int res; + + addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID]; + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at it for real ... + */ + if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) + goto oh_my_gawd; + + addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)]; + + if (size == 1) + res = get_dbe(*value, (u8 *) addr); + else if (size == 2) + res = get_dbe(*value, (u16 *) addr); + else + res = get_dbe(*value, (u32 *) addr); + + return res ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; + +oh_my_gawd: + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at the wrong register. + */ + if ((where >= 0x14 && where < 0x40) || (where >= 0x48)) { + *value = 0; + return PCIBIOS_SUCCESSFUL; + } + + /* + * IOC3 is fucked fucked beyond believe ... Don't try to access + * anything but 32-bit words ... + */ + addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2]; + + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + shift = ((where & 3) << 3); + mask = (0xffffffffU >> ((4 - size) << 3)); + *value = (cf >> shift) & mask; + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * value) +{ + return pci_conf0_read_config(bus, devfn, where, size, value); +} + +static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); + bridge_t *bridge = bc->base; + int slot = PCI_SLOT(devfn); + int fn = PCI_FUNC(devfn); + volatile void *addr; + u32 cf, shift, mask, smask; + int res; + + addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID]; + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at it for real ... + */ + if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) + goto oh_my_gawd; + + addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)]; + + if (size == 1) { + res = put_dbe(value, (u8 *) addr); + } else if (size == 2) { + res = put_dbe(value, (u16 *) addr); + } else { + res = put_dbe(value, (u32 *) addr); + } + + if (res) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; + +oh_my_gawd: + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to touch the wrong register. + */ + if ((where >= 0x14 && where < 0x40) || (where >= 0x48)) + return PCIBIOS_SUCCESSFUL; + + /* + * IOC3 is fucked fucked beyond believe ... Don't try to access + * anything but 32-bit words ... + */ + addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2]; + + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + shift = ((where & 3) << 3); + mask = (0xffffffffU >> ((4 - size) << 3)); + smask = mask << shift; + + cf = (cf & ~smask) | ((value & mask) << shift); + if (put_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + return pci_conf0_write_config(bus, devfn, where, size, value); +} + +static struct pci_ops bridge_pci_ops = { + .read = pci_read_config, + .write = pci_write_config, +}; + +unsigned int ip30_bridge_count=0; +bridge_t *ip30_irq_bridge[64]={NULL}; +unsigned int ip30_irq_in_bridge[64]={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 < 0xC00000) + 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 = 0x1000000; + bc->d32_p = 0x1000000; + + 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 = 0xddcc9988; + bridge->b_odd_resp = 0xddcc9988; + + /* + * 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 = 0x00000080; + /* + * 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&=0xFF000000; + bridge->b_int_enable=0x7FFFFE00; + bridge->b_int_mode=0x00000000; + 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(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.15.7.orig/arch/mips/pci/pci.c linux-2.6.15.7/arch/mips/pci/pci.c --- linux-2.6.15.7.orig/arch/mips/pci/pci.c 2006-01-02 22:21:10.000000000 -0500 +++ linux-2.6.15.7/arch/mips/pci/pci.c 2006-04-12 23:07:13.000000000 -0400 @@ -119,6 +119,10 @@ static int __init pcibios_init(void) /* Scan all of the recorded PCI controllers. */ for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { + 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) @@ -135,6 +139,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) { @@ -166,6 +174,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; @@ -174,6 +183,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->start && r->end) { printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev)); diff -Naurp linux-2.6.15.7.orig/arch/mips/sgi-ip30/Makefile linux-2.6.15.7/arch/mips/sgi-ip30/Makefile --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/Makefile 2006-04-12 23:07:13.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-irq-glue.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.15.7.orig/arch/mips/sgi-ip30/ip30-err.c linux-2.6.15.7/arch/mips/sgi-ip30/ip30-err.c --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-err.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/ip30-err.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,37 @@ +/* + * ip30-err.c: HEART error handling for IP30 architecture. + * + * Copyright (C) 2004 Stanislaw Skowronek (skylark@linux-mips.org) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +void ip30_do_err(struct pt_regs *regs) +{ + unsigned long errors = *HEART_ISR; + int i; + irq_enter(); + *HEART_CLR_ISR = 0xFFF8000000000000; + printk("IP30: HEART ATTACK! Caught errors: 0x%04x!\n",(int)((errors>>51)&0x1FFF)); + for(i=63;i>=51;i--) + if((errors>>i)&1) + printk(" interrupt #%d\n",i); + irq_exit(); +} diff -Naurp linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-irq-glue.S linux-2.6.15.7/arch/mips/sgi-ip30/ip30-irq-glue.S --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-irq-glue.S 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/ip30-irq-glue.S 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2004,5 Stanislaw Skowronek + * based on ip27-irq-glue.S: + * Copyright (C) 1999 Ralf Baechle + * Copyright (C) 1999 Silicon Graphics + */ +#include +#include +#include +#include + + .text + .set noat + .set reorder + .align 5 +NESTED(ip30_irq, PT_SIZE, sp) + SAVE_ALL + CLI + .set at + mfc0 s0, CP0_CAUSE + mfc0 t0, CP0_STATUS + and s0, t0 + + /* Check for CPU timer, IRQ60-63 */ +1: andi a0, s0, CAUSEF_IP7 + beqz a0, 1f + + move a0, sp + jal cpu_do_irq + + j ret_from_irq + + /* Check for HEART-routed IRQs, IRQ0-50. */ +1: andi a0, s0, (CAUSEF_IP2 | CAUSEF_IP3 | CAUSEF_IP4 | CAUSEF_IP5) + beqz a0, 1f + + move a0, sp + jal ip30_do_irq + + j ret_from_irq + + /* Check for HEART-routed ERRs, ERR51-63. */ +1: andi a0, s0, CAUSEF_IP6 + beqz a0, 1f + + move a0, sp + jal ip30_do_err + + j ret_from_irq + + /* Ignore IP0, IP1 (sw int) */ +1: + j ret_from_irq + END(ip30_irq) diff -Naurp linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-irq.c linux-2.6.15.7/arch/mips/sgi-ip30/ip30-irq.c --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-irq.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/ip30-irq.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,307 @@ +/* + * ip30-irq.c: Highlevel interrupt handling for IP30 architecture. + * + * Copyright (C) 2004,5 Stanislaw Skowronek + * 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 +#endif + +/* CPU IRQ */ + +void ip30_timer_bcast(void); + +void cpu_do_irq(struct pt_regs *regs) +{ +#ifdef CONFIG_SMP + ip30_timer_bcast(); +#endif + do_IRQ(TIMER_IRQ, regs); +} + +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 hw_interrupt_type cpu_irq_type = { + "CPU", + startup_cpu_irq, + shutdown_cpu_irq, + enable_cpu_irq, + disable_cpu_irq, + mask_and_ack_cpu_irq, + end_cpu_irq, + NULL +}; + +/* real HEART IRQs */ + +int heart_irq_thisowner; +static int heart_irq_owner[SOFT_IRQ_COUNT]; + +void ip30_do_irq(struct pt_regs *regs) +{ + unsigned int pcpu=cpu_logical_map(smp_processor_id()); + unsigned long irqs=((*HEART_ISR)&0x0007FFFFFFFFFFFF)&(*HEART_IMR(pcpu)); + unsigned long irqsel; + int irqnum; +#ifdef DEBUG_IRQ + bridge_t *bvma = (bridge_t *)0x900000001F000000UL; + if(irqs&~(15UL<b_int_status); +#endif + /* check for all IRQs in decreasing priority order */ + irqsel=0x0004000000000000; + irqnum=50; + while(irqsel) { /* poll all interrupts according to priority */ + if(irqs & irqsel) + do_IRQ(irqnum, regs); + 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_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<b_int_enable|=1<b_int_mode|=1<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); + return 0; /* This is probably not right; we could have pending irqs */ +} + +static void disable_heart_irq(unsigned int irq) +{ + unsigned long flags; + local_irq_save(flags); + *HEART_IMR(heart_irq_owner[irq])&=~(1UL<b_int_enable&=~(1<=IRQ_TIMER_P(0) && irq<=IRQ_IPI_P(3)) + *HEART_CLR_ISR=1UL< + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void ip30_machine_restart(char *command) +{ + printk("Rebooting..."); +#ifdef CONFIG_SMP + smp_send_stop(); + udelay(1000); +#endif + /* execute HEART cold reset */ + *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 + if(!ip30_can_powerdown()) + return; + printk("Powering down, please wait..."); +#ifdef CONFIG_SMP + smp_send_stop(); + udelay(1000); +#endif + /* kill interrupts */ + *HEART_CLR_ISR=0xFFFFFFFFFFFFFFFF; + *HEART_IMR(0)=0x0000000000000000; + *HEART_IMR(1)=0x0000000000000000; + *HEART_IMR(2)=0x0000000000000000; + *HEART_IMR(3)=0x0000000000000000; + /* 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 */ + +#define IP30_POWER_IRQ 14 +#define IP30_ACFAIL_IRQ 15 + +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, struct pt_regs *regs) +{ + /* 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, struct pt_regs *regs) +{ + /* 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.15.7.orig/arch/mips/sgi-ip30/ip30-setup.c linux-2.6.15.7/arch/mips/sgi-ip30/ip30-setup.c --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-setup.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/ip30-setup.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,74 @@ +/* + * 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,5 Stanislaw Skowronek + */ +#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 __init ip30_time_init(void); +extern void ip30_timer_setup(struct irqaction *irq); + +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 > 0x40000000) { + printk(KERN_INFO "Updating PROM memory size.\n"); + add_memory_region(0x60000000, size - 0x40000000, BOOT_MEM_RAM); + } +} + +int __init plat_setup(void) +{ + printk("Silicon Graphics Octane (IP30) support: (c) 2004, 2005 Stanislaw Skowronek.\n"); + set_io_port_base(0x9000000000000000); + board_timer_setup = ip30_timer_setup; + board_time_init = ip30_time_init; + _machine_restart = ip30_machine_restart; + _machine_halt = ip30_machine_halt; + _machine_power_off = ip30_machine_power_off; + ip30_fix_memory(); + ip30_xtalk_setup(); +#ifdef CONFIG_SMP + ip30_locate_bootcpu(); +#endif + return 0; +} diff -Naurp linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-smp-glue.S linux-2.6.15.7/arch/mips/sgi-ip30/ip30-smp-glue.S --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-smp-glue.S 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/ip30-smp-glue.S 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2005 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.15.7.orig/arch/mips/sgi-ip30/ip30-smp.c linux-2.6.15.7/arch/mips/sgi-ip30/ip30-smp.c --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-smp.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/ip30-smp.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,142 @@ +/* + * ip30-smp.c: SMP on IP30 architecture. + * + * Copyright (C) 2005 Stanislaw Skowronek + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#undef DEBUG_IPI + +extern void asmlinkage ip30_smp_bootstrap(void); +extern void ip30_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); + +void core_send_ipi(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)); +} + +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, struct pt_regs *regs) +{ + 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 local_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs); +void ip30_timer_bcast(void) +{ + int i; + for(i=1;ithread_info; + MP_LAUNCH(pcpu)=ip30_smp_bootstrap; +} + +void prom_init_secondary(void) +{ + ip30_secondary_init_irq(); + mips_hpt_init(mips_hpt_read()); +} + +void prom_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(); +} + +void prom_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); +} diff -Naurp linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-timer.c linux-2.6.15.7/arch/mips/sgi-ip30/ip30-timer.c --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-timer.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/ip30-timer.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,47 @@ +/* + * ip30-timer.c: Timer handling for IP30 architecture. + * + * Copyright (C) 2004,5 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 ip30_time_init(void) +{ + unsigned long heart_compare; + printk("IP30: initializing timer.\n", heart_compare); + 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); +} + +void __init ip30_timer_setup(struct irqaction *irq) +{ + setup_irq(TIMER_IRQ, irq); +} diff -Naurp linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-xtalk.c linux-2.6.15.7/arch/mips/sgi-ip30/ip30-xtalk.c --- linux-2.6.15.7.orig/arch/mips/sgi-ip30/ip30-xtalk.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/arch/mips/sgi-ip30/ip30-xtalk.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,174 @@ +/* + * ip30-xtalk.c + * (c) 2004, 2005 Stanislaw Skowronek + * + * XIO bus probing code + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct widget_ident { + unsigned mfgr; + unsigned part; + char *name; + char *revs[16]; +}; + +static struct widget_ident widget_idents[]={ + { + 0x0, + 0x0, + "XBow", + {NULL, "1.0", "1.1", "1.2", "1.3", "2.0", NULL} + }, + { + 0x0, + 0xd000, + "XXBow", + {NULL, "1.0", "2.0", NULL} + }, + { + 0x023, + 0xc013, + "Buzz / Odyssey", + {NULL, "A", "B", NULL} + }, + { + 0x024, + 0xc202, + "TPU", + {"0", NULL} + }, + { + 0x024, + 0xd002, + "XBridge", + {NULL, "A", "B", NULL} + }, + { + 0x036, + 0xc001, + "Heart", + {NULL, "A", "B", "C", "D", "E", "F", NULL} + }, + { + 0x036, + 0xc002, + "Bridge", + {NULL, "A", "B", "C", "D", NULL} + }, + { + 0x036, + 0xc101, + "Hub", + {NULL, "1.0", "2.0", "2.1", "2.2", "2.3", "2.4", NULL} + }, + { + 0x036, + 0xc110, + "Bedrock", + {NULL, "1.0", "1.1", NULL} + }, + { + 0x2aa, + 0xc003, + "HQ4 / ImpactSR", + {NULL, "A", "B", NULL} + }, + { + 0x2aa, + 0xc102, + "XG / KONA", + {NULL} + }, + { + -1, + -1, + NULL, + {NULL} + } +}; + +extern int bridge_probe(nasid_t nasid, int widget, int masterwid); + +unsigned long ip30_xtalk_swin(int wid) +{ + return 0x10000000|(wid<<24); +} + +unsigned ip30_xtalk_get_id(int wid) +{ + unsigned int link_stat; + if(wid!=0 && (wid<8 || wid>15)) + return 0xffffffff; + if(wid) { + link_stat = *(volatile unsigned int *)(RAW_NODE_SWIN_BASE(0, 0) + 0x114 + 0x40 * (wid - 8)); + if (!(link_stat & 0x80000000)) /* link alive */ + return 0xffffffff; + } + return *(volatile unsigned int *)(RAW_NODE_SWIN_BASE(0, wid) + 0x4); +} + +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(((wid_id>>1)&0x7ff)==mfgr && ((wid_id>>12)&0xffff)==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> 1) & 0x7ff; + wid_part = (wid_id >> 12) & 0xffff; + wid_rev = (wid_id >> 28) & 0xf; + 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.15.7.orig/drivers/char/Kconfig linux-2.6.15.7/drivers/char/Kconfig --- linux-2.6.15.7.orig/drivers/char/Kconfig 2006-04-12 22:51:39.000000000 -0400 +++ linux-2.6.15.7/drivers/char/Kconfig 2006-04-12 23:07:13.000000000 -0400 @@ -390,6 +390,14 @@ config ZS Documentation on the Zilog 85C350 serial communications controller is downloadable at . +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 QTRONIX_KEYBOARD bool "Enable Qtronix 990P Keyboard Support" depends on MIPS && (MIPS_ITE8172 || MIPS_IVR) @@ -782,6 +790,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 && !SPARC diff -Naurp linux-2.6.15.7.orig/drivers/char/Makefile linux-2.6.15.7/drivers/char/Makefile --- linux-2.6.15.7.orig/drivers/char/Makefile 2006-04-12 22:51:39.000000000 -0400 +++ linux-2.6.15.7/drivers/char/Makefile 2006-04-12 23:07:13.000000000 -0400 @@ -64,6 +64,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_S3C2410_RTC) += s3c2410-rtc.o obj-$(CONFIG_RTC_VR41XX) += vr41xx_rtc.o diff -Naurp linux-2.6.15.7.orig/drivers/char/ip30-leds.c linux-2.6.15.7/drivers/char/ip30-leds.c --- linux-2.6.15.7.orig/drivers/char/ip30-leds.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/drivers/char/ip30-leds.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,270 @@ +/* + * Driver for the LEDs in SGI Octane. + * + * Copyright (C) 2004 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); + + 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_init(leds_init); +module_exit(leds_exit); diff -Naurp linux-2.6.15.7.orig/drivers/char/ip30-rtc.c linux-2.6.15.7/drivers/char/ip30-rtc.c --- linux-2.6.15.7.orig/drivers/char/ip30-rtc.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/drivers/char/ip30-rtc.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,395 @@ +/* + * Driver for the Maxim/Dallas DS1687 real time clock in SGI Octane. + * + * Copyright (C) 2004 Stanislaw Skowronek + * 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; + +#define RTC_ADDR (*(unsigned char *) ((unsigned long)(ioc3->vma) + IOC3_BYTEBUS_DEV1)) +#define RTC_DATA (*(unsigned char *) ((unsigned long)(ioc3->vma) + IOC3_BYTEBUS_DEV2)) + +static unsigned char ip30_rtc_read(int addr) +{ + RTC_ADDR = addr & 0x7f; + return RTC_DATA; +} +static void ip30_rtc_write(int addr, unsigned char data) +{ + RTC_ADDR = addr & 0x7f; + RTC_DATA = data; +} + +/* RTC hardware driver */ + +static void rtc_begin_access(int bank) +{ + unsigned char val = ip30_rtc_read(0x0b); + unsigned long start = jiffies; + spin_lock_irq(&rtc_lock); + ip30_rtc_write(0x0b, val | 0x80); /* SET bit */ + val = ip30_rtc_read(0x0a); + while (val & 0x80) { /* UIP bit */ + udelay(10); + if (jiffies > start + 137) { + printk(KERN_ERR "ip30-rtc: RTC access lock timeout.\n"); + return; + } + val = ip30_rtc_read(0x0a); + } + ip30_rtc_write(0x0a, (val & 0xef) | (bank << 4)); +} + +static void rtc_end_access(void) +{ + unsigned char val = ip30_rtc_read(0x0b); + ip30_rtc_write(0x0b, 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(0x00); + rtc_tm->tm_min = ip30_rtc_read(0x02); + rtc_tm->tm_hour = ip30_rtc_read(0x04); + rtc_tm->tm_mday = ip30_rtc_read(0x07); + rtc_tm->tm_mon = ip30_rtc_read(0x08); + rtc_tm->tm_year = ip30_rtc_read(0x09); + rtc_tm->tm_year += 100 * ip30_rtc_read(0x48); + 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(0x00, sec); + ip30_rtc_write(0x02, min); + ip30_rtc_write(0x04, hrs); + ip30_rtc_write(0x07, day); + ip30_rtc_write(0x08, mon); + ip30_rtc_write(0x09, yrs % 100); + ip30_rtc_write(0x48, 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(0x0a); + ip30_rtc_write(0x0a, val | 0x10); + ip30_rtc_write(0x4a, 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(0x4b); + val &= 0x2a; + val |= 0x81; + ip30_rtc_write(0x4b, val); + rtc_end_access(); + + while (1) { + ip30_rtc_write(0x4a, 0x08); /* power down */ + udelay(100000); + } +/* there is no way out */ +} + +/* 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; + if (len<0) len = 0; + return len; +} + +/* general MIPS compatibility */ + +static unsigned long ip30_mips_rtc_get_time(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); + } + return mktime(2004, 8, 23, 12, 15, 0); +} + +static int ip30_mips_rtc_set_time(unsigned long tim) +{ + struct rtc_time tm; + 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); + + rtc_get_time = ip30_mips_rtc_get_time; + rtc_set_time = ip30_mips_rtc_set_time; + + 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_init(rtc_init); +module_exit(rtc_exit); diff -Naurp linux-2.6.15.7.orig/drivers/usb/host/pci-quirks.c linux-2.6.15.7/drivers/usb/host/pci-quirks.c --- linux-2.6.15.7.orig/drivers/usb/host/pci-quirks.c 2006-01-02 22:21:10.000000000 -0500 +++ linux-2.6.15.7/drivers/usb/host/pci-quirks.c 2006-04-12 23:07:13.000000000 -0400 @@ -152,6 +152,9 @@ static void __devinit quirk_usb_handoff_ unsigned long base = 0; int i; + if (!pci_enable_device(pdev)) + return; + if (!pio_enabled(pdev)) return; @@ -176,6 +179,9 @@ static void __devinit quirk_usb_handoff_ int wait_time; u32 control; + if (!pci_enable_device(pdev)) + return; + if (!mmio_resource_enabled(pdev, 0)) return; diff -Naurp linux-2.6.15.7.orig/drivers/video/Kconfig linux-2.6.15.7/drivers/video/Kconfig --- linux-2.6.15.7.orig/drivers/video/Kconfig 2006-04-12 22:51:39.000000000 -0400 +++ linux-2.6.15.7/drivers/video/Kconfig 2006-04-12 23:07:13.000000000 -0400 @@ -525,6 +525,20 @@ config FB_GBE_MEM This is the amount of memory reserved for the framebuffer, which can be any value between 1MB and 8MB. +config FB_IMPACTSR + tristate "SGI Octane ImpactSR graphics support" + depends on FB && SGI_IP30 + select FB_SOFT_CURSOR + 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 + select FB_SOFT_CURSOR + help + SGI Octane Odyssey (VPro V6/V8/V10/V12) graphics card support. + config BUS_I2C bool depends on (FB = y) && VISWS diff -Naurp linux-2.6.15.7.orig/drivers/video/Makefile linux-2.6.15.7/drivers/video/Makefile --- linux-2.6.15.7.orig/drivers/video/Makefile 2006-04-12 22:51:39.000000000 -0400 +++ linux-2.6.15.7/drivers/video/Makefile 2006-04-12 23:07:13.000000000 -0400 @@ -96,6 +96,8 @@ obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxf obj-$(CONFIG_FB_IMX) += imxfb.o obj-$(CONFIG_FB_SMIVGX) += smivgxfb.o obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o +obj-$(CONFIG_FB_IMPACTSR) += impactsr.o +obj-$(CONFIG_FB_ODYSSEY) += odyssey.o # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o diff -Naurp linux-2.6.15.7.orig/drivers/video/impactsr.c linux-2.6.15.7/drivers/video/impactsr.c --- linux-2.6.15.7.orig/drivers/video/impactsr.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.15.7/drivers/video/impactsr.c 2006-04-12 23:07:13.000000000 -0400 @@ -0,0 +1,910 @@ +/* + * 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