diff options
Diffstat (limited to 'arch/alpha/kernel/core_irongate.c')
-rw-r--r-- | arch/alpha/kernel/core_irongate.c | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/arch/alpha/kernel/core_irongate.c b/arch/alpha/kernel/core_irongate.c new file mode 100644 index 00000000..00096df0 --- /dev/null +++ b/arch/alpha/kernel/core_irongate.c @@ -0,0 +1,420 @@ +/* + * linux/arch/alpha/kernel/core_irongate.c + * + * Based on code written by David A. Rusling (david.rusling@reo.mts.dec.com). + * + * Copyright (C) 1999 Alpha Processor, Inc., + * (David Daniel, Stig Telfer, Soohoon Lee) + * + * Code common to all IRONGATE core logic chips. + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_irongate.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/initrd.h> +#include <linux/bootmem.h> + +#include <asm/ptrace.h> +#include <asm/pci.h> +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> + +#include "proto.h" +#include "pci_impl.h" + +/* + * BIOS32-style PCI interface: + */ + +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + +igcsr32 *IronECC; + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address accordingly. It is therefore not safe + * to have concurrent invocations to configuration space access + * routines, but there really shouldn't be any need for this. + * + * addr[31:24] reserved + * addr[23:16] bus number (8 bits = 128 possible buses) + * addr[15:11] Device number (5 bits) + * addr[10: 8] function number + * addr[ 7: 2] register number + * + * For IRONGATE: + * if (bus = addr[23:16]) == 0 + * then + * type 0 config cycle: + * addr_on_pci[31:11] = id selection for device = addr[15:11] + * addr_on_pci[10: 2] = addr[10: 2] ??? + * addr_on_pci[ 1: 0] = 00 + * else + * type 1 config cycle (pass on with no decoding): + * addr_on_pci[31:24] = 0 + * addr_on_pci[23: 2] = addr[23: 2] + * addr_on_pci[ 1: 0] = 01 + * fi + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr, unsigned char *type1) +{ + unsigned long addr; + u8 bus = pbus->number; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " + "pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + *type1 = (bus != 0); + + addr = (bus << 16) | (device_fn << 8) | where; + addr |= IRONGATE_CONF; + + *pci_addr = addr; + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +static int +irongate_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *value = __kernel_ldbu(*(vucp)addr); + break; + case 2: + *value = __kernel_ldwu(*(vusp)addr); + break; + case 4: + *value = *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int +irongate_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); + break; + case 2: + __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); + break; + case 4: + *(vuip)addr = value; + mb(); + *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops irongate_pci_ops = +{ + .read = irongate_read_config, + .write = irongate_write_config, +}; + +int +irongate_pci_clr_err(void) +{ + unsigned int nmi_ctl=0; + unsigned int IRONGATE_jd; + +again: + IRONGATE_jd = IRONGATE0->stat_cmd; + printk("Iron stat_cmd %x\n", IRONGATE_jd); + IRONGATE0->stat_cmd = IRONGATE_jd; /* write again clears error bits */ + mb(); + IRONGATE_jd = IRONGATE0->stat_cmd; /* re-read to force write */ + + IRONGATE_jd = *IronECC; + printk("Iron ECC %x\n", IRONGATE_jd); + *IronECC = IRONGATE_jd; /* write again clears error bits */ + mb(); + IRONGATE_jd = *IronECC; /* re-read to force write */ + + /* Clear ALI NMI */ + nmi_ctl = inb(0x61); + nmi_ctl |= 0x0c; + outb(nmi_ctl, 0x61); + nmi_ctl &= ~0x0c; + outb(nmi_ctl, 0x61); + + IRONGATE_jd = *IronECC; + if (IRONGATE_jd & 0x300) goto again; + + return 0; +} + +#define IRONGATE_3GB 0xc0000000UL + +/* On Albacore (aka UP1500) with 4Gb of RAM we have to reserve some + memory for PCI. At this point we just reserve memory above 3Gb. Most + of this memory will be freed after PCI setup is done. */ +static void __init +albacore_init_arch(void) +{ + unsigned long memtop = max_low_pfn << PAGE_SHIFT; + unsigned long pci_mem = (memtop + 0x1000000UL) & ~0xffffffUL; + struct percpu_struct *cpu; + int pal_rev, pal_var; + + cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); + pal_rev = cpu->pal_revision & 0xffff; + pal_var = (cpu->pal_revision >> 16) & 0xff; + + /* Consoles earlier than A5.6-18 (OSF PALcode v1.62-2) set up + the CPU incorrectly (leave speculative stores enabled), + which causes memory corruption under certain conditions. + Issue a warning for such consoles. */ + if (alpha_using_srm && + (pal_rev < 0x13e || (pal_rev == 0x13e && pal_var < 2))) + printk(KERN_WARNING "WARNING! Upgrade to SRM A5.6-19 " + "or later\n"); + + if (pci_mem > IRONGATE_3GB) + pci_mem = IRONGATE_3GB; + IRONGATE0->pci_mem = pci_mem; + alpha_mv.min_mem_address = pci_mem; + if (memtop > pci_mem) { +#ifdef CONFIG_BLK_DEV_INITRD + extern unsigned long initrd_start, initrd_end; + extern void *move_initrd(unsigned long); + + /* Move the initrd out of the way. */ + if (initrd_end && __pa(initrd_end) > pci_mem) { + unsigned long size; + + size = initrd_end - initrd_start; + free_bootmem_node(NODE_DATA(0), __pa(initrd_start), + PAGE_ALIGN(size)); + if (!move_initrd(pci_mem)) + printk("irongate_init_arch: initrd too big " + "(%ldK)\ndisabling initrd\n", + size / 1024); + } +#endif + reserve_bootmem_node(NODE_DATA(0), pci_mem, memtop - + pci_mem, BOOTMEM_DEFAULT); + printk("irongate_init_arch: temporarily reserving " + "region %08lx-%08lx for PCI\n", pci_mem, memtop - 1); + } +} + +static void __init +irongate_setup_agp(void) +{ + /* Disable the GART window. AGPGART doesn't work due to yet + unresolved memory coherency issues... */ + IRONGATE0->agpva = IRONGATE0->agpva & ~0xf; + alpha_agpgart_size = 0; +} + +void __init +irongate_init_arch(void) +{ + struct pci_controller *hose; + int amd761 = (IRONGATE0->dev_vendor >> 16) > 0x7006; /* Albacore? */ + + IronECC = amd761 ? &IRONGATE0->bacsr54_eccms761 : &IRONGATE0->dramms; + + irongate_pci_clr_err(); + + if (amd761) + albacore_init_arch(); + + irongate_setup_agp(); + + /* + * Create our single hose. + */ + + pci_isa_hose = hose = alloc_pci_controller(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->index = 0; + + /* This is for userland consumption. For some reason, the 40-bit + PIO bias that we use in the kernel through KSEG didn't work for + the page table based user mappings. So make sure we get the + 43-bit PIO bias. */ + hose->sparse_mem_base = 0; + hose->sparse_io_base = 0; + hose->dense_mem_base + = (IRONGATE_MEM & 0xffffffffffUL) | 0x80000000000UL; + hose->dense_io_base + = (IRONGATE_IO & 0xffffffffffUL) | 0x80000000000UL; + + hose->sg_isa = hose->sg_pci = NULL; + __direct_map_base = 0; + __direct_map_size = 0xffffffff; +} + +/* + * IO map and AGP support + */ +#include <linux/vmalloc.h> +#include <linux/agp_backend.h> +#include <linux/agpgart.h> +#include <linux/export.h> +#include <asm/pgalloc.h> + +#define GET_PAGE_DIR_OFF(addr) (addr >> 22) +#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr)) + +#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) +#define GET_GATT(addr) (gatt_pages[GET_PAGE_DIR_IDX(addr)]) + +void __iomem * +irongate_ioremap(unsigned long addr, unsigned long size) +{ + struct vm_struct *area; + unsigned long vaddr; + unsigned long baddr, last; + u32 *mmio_regs, *gatt_pages, *cur_gatt, pte; + unsigned long gart_bus_addr; + + if (!alpha_agpgart_size) + return (void __iomem *)(addr + IRONGATE_MEM); + + gart_bus_addr = (unsigned long)IRONGATE0->bar0 & + PCI_BASE_ADDRESS_MEM_MASK; + + /* + * Check for within the AGP aperture... + */ + do { + /* + * Check the AGP area + */ + if (addr >= gart_bus_addr && addr + size - 1 < + gart_bus_addr + alpha_agpgart_size) + break; + + /* + * Not found - assume legacy ioremap + */ + return (void __iomem *)(addr + IRONGATE_MEM); + } while(0); + + mmio_regs = (u32 *)(((unsigned long)IRONGATE0->bar1 & + PCI_BASE_ADDRESS_MEM_MASK) + IRONGATE_MEM); + + gatt_pages = (u32 *)(phys_to_virt(mmio_regs[1])); /* FIXME */ + + /* + * Adjust the limits (mappings must be page aligned) + */ + if (addr & ~PAGE_MASK) { + printk("AGP ioremap failed... addr not page aligned (0x%lx)\n", + addr); + return (void __iomem *)(addr + IRONGATE_MEM); + } + last = addr + size - 1; + size = PAGE_ALIGN(last) - addr; + +#if 0 + printk("irongate_ioremap(0x%lx, 0x%lx)\n", addr, size); + printk("irongate_ioremap: gart_bus_addr 0x%lx\n", gart_bus_addr); + printk("irongate_ioremap: gart_aper_size 0x%lx\n", gart_aper_size); + printk("irongate_ioremap: mmio_regs %p\n", mmio_regs); + printk("irongate_ioremap: gatt_pages %p\n", gatt_pages); + + for(baddr = addr; baddr <= last; baddr += PAGE_SIZE) + { + cur_gatt = phys_to_virt(GET_GATT(baddr) & ~1); + pte = cur_gatt[GET_GATT_OFF(baddr)] & ~1; + printk("irongate_ioremap: cur_gatt %p pte 0x%x\n", + cur_gatt, pte); + } +#endif + + /* + * Map it + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) return NULL; + + for(baddr = addr, vaddr = (unsigned long)area->addr; + baddr <= last; + baddr += PAGE_SIZE, vaddr += PAGE_SIZE) + { + cur_gatt = phys_to_virt(GET_GATT(baddr) & ~1); + pte = cur_gatt[GET_GATT_OFF(baddr)] & ~1; + + if (__alpha_remap_area_pages(vaddr, + pte, PAGE_SIZE, 0)) { + printk("AGP ioremap: FAILED to map...\n"); + vfree(area->addr); + return NULL; + } + } + + flush_tlb_all(); + + vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK); +#if 0 + printk("irongate_ioremap(0x%lx, 0x%lx) returning 0x%lx\n", + addr, size, vaddr); +#endif + return (void __iomem *)vaddr; +} +EXPORT_SYMBOL(irongate_ioremap); + +void +irongate_iounmap(volatile void __iomem *xaddr) +{ + unsigned long addr = (unsigned long) xaddr; + if (((long)addr >> 41) == -2) + return; /* kseg map, nothing to do */ + if (addr) + return vfree((void *)(PAGE_MASK & addr)); +} +EXPORT_SYMBOL(irongate_iounmap); |