diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-04-05 04:55:58 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-04-05 04:55:58 +0000 |
commit | 74a9f2e1b4d3ab45a9f72cb5b556c9f521524ab3 (patch) | |
tree | 7c4cdb103ab1b388c9852a88bd6fb1e73eba0b5c /arch/sparc64/kernel | |
parent | ee6374c8b0d333c08061c6a97bc77090d7461225 (diff) |
Merge with Linux 2.4.3.
Note that mingetty does no longer work with serial console, you have to
switch to another getty like getty_ps. This commit also includes a
fix for a setitimer bug which did prevent getty_ps from working on
older kernels.
Diffstat (limited to 'arch/sparc64/kernel')
34 files changed, 3313 insertions, 650 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 8648bb190..218242631 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.63 2000/12/14 22:57:25 davem Exp $ +# $Id: Makefile,v 1.64 2001/02/28 05:59:45 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -27,7 +27,7 @@ obj-y := process.o setup.o cpu.o idprom.o \ power.o sbus.o iommu_common.o sparc64_ksyms.o obj-$(CONFIG_PCI) += ebus.o pci_common.o pci_iommu.o \ - pci_psycho.o pci_sabre.o + pci_psycho.o pci_sabre.o pci_schizo.o obj-$(CONFIG_SMP) += smp.o trampoline.o obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o ioctl32.o obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c index cc26817b4..10787f22f 100644 --- a/arch/sparc64/kernel/auxio.c +++ b/arch/sparc64/kernel/auxio.c @@ -18,6 +18,7 @@ #include <asm/sbus.h> #include <asm/ebus.h> #include <asm/fhc.h> +#include <asm/spitfire.h> #include <asm/starfire.h> /* Probe and map in the Auxiliary I/O register */ @@ -56,7 +57,7 @@ found_sdev: return; } #endif - if(central_bus || this_is_starfire) { + if (central_bus || this_is_starfire || (tlb_type == cheetah)) { auxio_register = 0UL; return; } diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index bda2fb6a5..314230ef2 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -277,24 +277,24 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) goto beyond_if; } - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, fd_offset); - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); if (error != N_TXTADDR(ex)) { send_sig(SIGKILL, current, 0); return error; } - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, fd_offset + ex.a_text); - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); if (error != N_DATADDR(ex)) { send_sig(SIGKILL, current, 0); return error; @@ -318,7 +318,8 @@ beyond_if: unsigned long pgd_cache; pgd_cache = ((unsigned long)current->mm->pgd[0])<<11UL; - __asm__ __volatile__("stxa\t%0, [%1] %2" + __asm__ __volatile__("stxa\t%0, [%1] %2\n\t" + "membar #Sync" : /* no outputs */ : "r" (pgd_cache), "r" (TSB_REG), "i" (ASI_DMMU)); @@ -368,12 +369,12 @@ static int load_aout32_library(struct file *file) start_addr = ex.a_entry & 0xfffff000; /* Now use mmap to map the library into memory. */ - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); error = do_mmap(file, start_addr, ex.a_text + ex.a_data, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, N_TXTOFF(ex)); - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); retval = error; if (error != start_addr) goto out; diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index 8e1a0367c..a3bf89bb2 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -34,7 +34,8 @@ struct cpu_fp_info linux_sparc_fpu[] = { { 0x22, 0x10, 0, "UltraSparc II integrated FPU"}, { 0x17, 0x11, 0, "UltraSparc II integrated FPU"}, { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"}, - { 0x17, 0x14, 0, "UltraSparc III integrated FPU"}, + { 0x17, 0x13, 0, "UltraSparc IIe integrated FPU"}, + { 0x3e, 0x14, 0, "UltraSparc III integrated FPU"}, }; #define NSPARCFPU (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info)) @@ -44,7 +45,8 @@ struct cpu_iu_info linux_sparc_chips[] = { { 0x22, 0x10, "TI UltraSparc II (BlackBird)"}, { 0x17, 0x11, "TI UltraSparc II (BlackBird)"}, { 0x17, 0x12, "TI UltraSparc IIi"}, - { 0x17, 0x14, "TI UltraSparc III (Cheetah)"}, /* A guess... */ + { 0x17, 0x13, "TI UltraSparc IIe"}, + { 0x3e, 0x14, "TI UltraSparc III (Cheetah)"}, }; #define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info)) diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index be8771985..849a44c87 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -13,6 +13,7 @@ #include <asm/oplib.h> #include <asm/system.h> #include <asm/smp.h> +#include <asm/spitfire.h> struct prom_cpuinfo linux_cpus[64] __initdata = { { 0 } }; unsigned prom_cpu_nodes[64]; @@ -50,8 +51,14 @@ void __init device_scan(void) if(strcmp(node_str, "cpu") == 0) { cpu_nds[cpu_ctr] = scan; linux_cpus[cpu_ctr].prom_node = scan; - prom_getproperty(scan, "upa-portid", - (char *) &thismid, sizeof(thismid)); + thismid = 0; + if (tlb_type == spitfire) { + prom_getproperty(scan, "upa-portid", + (char *) &thismid, sizeof(thismid)); + } else if (tlb_type == cheetah) { + prom_getproperty(scan, "portid", + (char *) &thismid, sizeof(thismid)); + } linux_cpus[cpu_ctr].mid = thismid; printk("Found CPU %d (node=%08x,mid=%d)\n", cpu_ctr, (unsigned) scan, thismid); diff --git a/arch/sparc64/kernel/dtlb_base.S b/arch/sparc64/kernel/dtlb_base.S index 80c74aa18..f3a32d714 100644 --- a/arch/sparc64/kernel/dtlb_base.S +++ b/arch/sparc64/kernel/dtlb_base.S @@ -1,4 +1,4 @@ -/* $Id: dtlb_base.S,v 1.8 2000/11/10 08:28:45 davem Exp $ +/* $Id: dtlb_base.S,v 1.9 2001/03/22 00:12:32 davem Exp $ * dtlb_base.S: Front end to DTLB miss replacement strategy. * This is included directly into the trap table. * @@ -10,8 +10,6 @@ #define VPTE_SHIFT (PAGE_SHIFT - 3) #define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) #define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) -#define KERN_LOWBITS_IO (_PAGE_E | _PAGE_P | _PAGE_W) -#define KERN_IOBITS (KERN_LOWBITS ^ KERN_LOWBITS_IO) /* %g1 TLB_SFSR (%g1 + %g1 == TLB_TAG_ACCESS) * %g2 (KERN_HIGHBITS | KERN_LOWBITS) @@ -94,5 +92,3 @@ #undef VPTE_SHIFT #undef KERN_HIGHBITS #undef KERN_LOWBITS -#undef KERN_LOWBITS_IO -#undef KERN_IOBITS diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index 6dbe45a6c..a1e633353 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.54 2001/02/13 01:16:44 davem Exp $ +/* $Id: ebus.c,v 1.60 2001/03/15 02:11:09 davem Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -25,6 +25,7 @@ struct linux_ebus *ebus_chain = 0; #ifdef CONFIG_SUN_AUXIO extern void auxio_probe(void); #endif +extern void rs_init(void); static inline void *ebus_alloc(size_t size) { @@ -159,7 +160,7 @@ void __init fill_ebus_child(int node, struct linux_prom_registers *preg, struct pci_controller_info *p = pbm->parent; if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) { - dev->irqs[i] = p->irq_build(p, + dev->irqs[i] = p->irq_build(pbm, dev->bus->self, irqs[i]); } else { @@ -200,7 +201,10 @@ void __init fill_ebus_device(int node, struct linux_ebus_device *dev) dev->num_addrs = len / sizeof(struct linux_prom_registers); for (i = 0; i < dev->num_addrs; i++) { - n = (regs[i].which_io - 0x10) >> 2; + if (dev->bus->is_rio == 0) + n = (regs[i].which_io - 0x10) >> 2; + else + n = regs[i].which_io; dev->resource[i].start = dev->bus->self->resource[n].start; dev->resource[i].start += (unsigned long)regs[i].phys_addr; @@ -222,7 +226,7 @@ void __init fill_ebus_device(int node, struct linux_ebus_device *dev) struct pci_controller_info *p = pbm->parent; if (ebus_intmap_match(dev->bus, ®s[0], &irqs[i]) != -1) { - dev->irqs[i] = p->irq_build(p, + dev->irqs[i] = p->irq_build(pbm, dev->bus->self, irqs[i]); } else { @@ -269,14 +273,19 @@ void __init ebus_init(void) struct linux_ebus *ebus; struct pci_dev *pdev; struct pcidev_cookie *cookie; - int nd, ebusnd; + int nd, ebusnd, is_rio; int num_ebus = 0; if (!pci_present()) return; + is_rio = 0; pdev = pci_find_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, 0); if (!pdev) { + pdev = pci_find_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_RIO_EBUS, 0); + is_rio = 1; + } + if (!pdev) { printk("ebus: No EBus's found.\n"); return; } @@ -286,6 +295,7 @@ void __init ebus_init(void) ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus)); ebus->next = 0; + ebus->is_rio = is_rio; while (ebusnd) { /* SUNW,pci-qfe uses four empty ebuses on it. @@ -295,8 +305,16 @@ void __init ebus_init(void) we'd have to tweak with the ebus_chain in the runtime after initialization. -jj */ if (!prom_getchild (ebusnd)) { + struct pci_dev *orig_pdev = pdev; + + is_rio = 0; pdev = pci_find_device(PCI_VENDOR_ID_SUN, - PCI_DEVICE_ID_SUN_EBUS, pdev); + PCI_DEVICE_ID_SUN_EBUS, orig_pdev); + if (!pdev) { + pdev = pci_find_device(PCI_VENDOR_ID_SUN, + PCI_DEVICE_ID_SUN_RIO_EBUS, orig_pdev); + is_rio = 1; + } if (!pdev) { if (ebus == ebus_chain) { ebus_chain = NULL; @@ -305,7 +323,7 @@ void __init ebus_init(void) } break; } - + ebus->is_rio = is_rio; cookie = pdev->sysdata; ebusnd = cookie->prom_node; continue; @@ -346,10 +364,20 @@ void __init ebus_init(void) next_ebus: printk("\n"); - pdev = pci_find_device(PCI_VENDOR_ID_SUN, - PCI_DEVICE_ID_SUN_EBUS, pdev); - if (!pdev) - break; + { + struct pci_dev *orig_pdev = pdev; + + is_rio = 0; + pdev = pci_find_device(PCI_VENDOR_ID_SUN, + PCI_DEVICE_ID_SUN_EBUS, orig_pdev); + if (!pdev) { + pdev = pci_find_device(PCI_VENDOR_ID_SUN, + PCI_DEVICE_ID_SUN_RIO_EBUS, orig_pdev); + is_rio = 1; + } + if (!pdev) + break; + } cookie = pdev->sysdata; ebusnd = cookie->prom_node; @@ -357,9 +385,11 @@ void __init ebus_init(void) ebus->next = ebus_alloc(sizeof(struct linux_ebus)); ebus = ebus->next; ebus->next = 0; + ebus->is_rio = is_rio; ++num_ebus; } + rs_init(); #ifdef CONFIG_SUN_AUXIO auxio_probe(); #endif diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index fd6096ec5..9aed69e26 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.120 2000/09/08 13:58:12 jj Exp $ +/* $Id: entry.S,v 1.127 2001/03/23 07:56:30 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -42,13 +42,29 @@ sparc64_vpte_patchme2: /* This is trivial with the new code... */ .globl do_fpdis do_fpdis: - ldub [%g6 + AOFF_task_thread + AOFF_thread_fpsaved], %g5 ! Load Group sethi %hi(TSTATE_PEF), %g4 ! IEU0 + rdpr %tstate, %g5 + andcc %g5, %g4, %g0 + be,pt %xcc, 1f + nop + rd %fprs, %g5 + andcc %g5, FPRS_FEF, %g0 + be,pt %xcc, 1f + nop + + /* Legal state when DCR_IFPOE is set in Cheetah %dcr. */ + sethi %hi(109f), %g7 + ba,pt %xcc, etrap +109: or %g7, %lo(109b), %g7 + add %g0, %g0, %g0 + ba,a,pt %xcc, rtrap_clr_l6 + +1: ldub [%g6 + AOFF_task_thread + AOFF_thread_fpsaved], %g5 ! Load Group wr %g0, FPRS_FEF, %fprs ! LSU Group+4bubbles andcc %g5, FPRS_FEF, %g0 ! IEU1 Group be,a,pt %icc, 1f ! CTI clr %g7 ! IEU0 - ldub [%g6 + AOFF_task_thread + AOFF_thread_gsr], %g7 ! Load Group + ldx [%g6 + AOFF_task_thread + AOFF_thread_gsr], %g7 ! Load Group 1: andcc %g5, FPRS_DL, %g0 ! IEU1 bne,pn %icc, 2f ! CTI fzero %f0 ! FPA @@ -92,10 +108,9 @@ do_fpdis: ldxa [%g3] ASI_DMMU, %g5 add %g6, AOFF_task_fpregs + 0xc0, %g2 stxa %g0, [%g3] ASI_DMMU + membar #Sync faddd %f0, %f2, %f8 fmuld %f0, %f2, %f10 - flush %g6 - membar #StoreLoad | #LoadLoad ldda [%g1] ASI_BLK_S, %f32 ! grrr, where is ASI_BLK_NUCLEUS 8-( ldda [%g2] ASI_BLK_S, %f48 faddd %f0, %f2, %f12 @@ -118,11 +133,10 @@ do_fpdis: ldxa [%g3] ASI_DMMU, %g5 add %g6, AOFF_task_fpregs, %g1 stxa %g0, [%g3] ASI_DMMU + membar #Sync add %g6, AOFF_task_fpregs + 0x40, %g2 faddd %f32, %f34, %f36 fmuld %f32, %f34, %f38 - flush %g6 - membar #StoreLoad | #LoadLoad ldda [%g1] ASI_BLK_S, %f0 ! grrr, where is ASI_BLK_NUCLEUS 8-( ldda [%g2] ASI_BLK_S, %f16 faddd %f32, %f34, %f40 @@ -137,15 +151,14 @@ do_fpdis: fmuld %f32, %f34, %f58 faddd %f32, %f34, %f60 fmuld %f32, %f34, %f62 - b,pt %xcc, fpdis_exit + ba,pt %xcc, fpdis_exit membar #Sync 3: mov SECONDARY_CONTEXT, %g3 add %g6, AOFF_task_fpregs, %g1 ldxa [%g3] ASI_DMMU, %g5 mov 0x40, %g2 stxa %g0, [%g3] ASI_DMMU - flush %g6 - membar #StoreLoad | #LoadLoad + membar #Sync ldda [%g1] ASI_BLK_S, %f0 ! grrr, where is ASI_BLK_NUCLEUS 8-( ldda [%g1 + %g2] ASI_BLK_S, %f16 add %g1, 0x80, %g1 @@ -154,7 +167,7 @@ do_fpdis: membar #Sync fpdis_exit: stxa %g5, [%g3] ASI_DMMU - flush %g6 + membar #Sync fpdis_exit2: wr %g7, 0, %gsr ldx [%g6 + AOFF_task_thread + AOFF_thread_xfsr], %fsr @@ -164,22 +177,152 @@ fpdis_exit2: wr %g0, FPRS_FEF, %fprs ! clean DU/DL bits retry + .align 32 +fp_other_bounce: + call do_fpother + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap + clr %l6 + + .globl do_fpother_check_fitos + .align 32 +do_fpother_check_fitos: + sethi %hi(fp_other_bounce - 4), %g7 + or %g7, %lo(fp_other_bounce - 4), %g7 + + /* NOTE: Need to preserve %g7 until we fully commit + * to the fitos fixup. + */ + stx %fsr, [%g6 + AOFF_task_thread + AOFF_thread_xfsr] + rdpr %tstate, %g3 + andcc %g3, TSTATE_PRIV, %g0 + bne,pn %xcc, do_fptrap_after_fsr + nop + ldx [%g6 + AOFF_task_thread + AOFF_thread_xfsr], %g3 + srlx %g3, 14, %g1 + and %g1, 7, %g1 + cmp %g1, 2 ! Unfinished FP-OP + bne,pn %xcc, do_fptrap_after_fsr + sethi %hi(1 << 23), %g1 ! Inexact + andcc %g3, %g1, %g0 + bne,pn %xcc, do_fptrap_after_fsr + rdpr %tpc, %g1 + lduwa [%g1] ASI_AIUP, %g3 ! This cannot ever fail +#define FITOS_MASK 0xc1f83fe0 +#define FITOS_COMPARE 0x81a01880 + sethi %hi(FITOS_MASK), %g1 + or %g1, %lo(FITOS_MASK), %g1 + and %g3, %g1, %g1 + sethi %hi(FITOS_COMPARE), %g2 + or %g2, %lo(FITOS_COMPARE), %g2 + cmp %g1, %g2 + bne,pn %xcc, do_fptrap_after_fsr + nop + std %f62, [%g6 + AOFF_task_fpregs + (62 * 4)] + sethi %hi(fitos_table_1), %g1 + and %g3, 0x1f, %g2 + or %g1, %lo(fitos_table_1), %g1 + sllx %g2, 2, %g2 + jmpl %g1 + %g2, %g0 + ba,pt %xcc, fitos_emul_continue + +fitos_table_1: + fitod %f0, %f62 + fitod %f1, %f62 + fitod %f2, %f62 + fitod %f3, %f62 + fitod %f4, %f62 + fitod %f5, %f62 + fitod %f6, %f62 + fitod %f7, %f62 + fitod %f8, %f62 + fitod %f9, %f62 + fitod %f10, %f62 + fitod %f11, %f62 + fitod %f12, %f62 + fitod %f13, %f62 + fitod %f14, %f62 + fitod %f15, %f62 + fitod %f16, %f62 + fitod %f17, %f62 + fitod %f18, %f62 + fitod %f19, %f62 + fitod %f20, %f62 + fitod %f21, %f62 + fitod %f22, %f62 + fitod %f23, %f62 + fitod %f24, %f62 + fitod %f25, %f62 + fitod %f26, %f62 + fitod %f27, %f62 + fitod %f28, %f62 + fitod %f29, %f62 + fitod %f30, %f62 + fitod %f31, %f62 + +fitos_emul_continue: + sethi %hi(fitos_table_2), %g1 + srl %g3, 25, %g2 + or %g1, %lo(fitos_table_2), %g1 + and %g2, 0x1f, %g2 + sllx %g2, 2, %g2 + jmpl %g1 + %g2, %g0 + ba,pt %xcc, fitos_emul_fini + +fitos_table_2: + fdtos %f62, %f0 + fdtos %f62, %f1 + fdtos %f62, %f2 + fdtos %f62, %f3 + fdtos %f62, %f4 + fdtos %f62, %f5 + fdtos %f62, %f6 + fdtos %f62, %f7 + fdtos %f62, %f8 + fdtos %f62, %f9 + fdtos %f62, %f10 + fdtos %f62, %f11 + fdtos %f62, %f12 + fdtos %f62, %f13 + fdtos %f62, %f14 + fdtos %f62, %f15 + fdtos %f62, %f16 + fdtos %f62, %f17 + fdtos %f62, %f18 + fdtos %f62, %f19 + fdtos %f62, %f20 + fdtos %f62, %f21 + fdtos %f62, %f22 + fdtos %f62, %f23 + fdtos %f62, %f24 + fdtos %f62, %f25 + fdtos %f62, %f26 + fdtos %f62, %f27 + fdtos %f62, %f28 + fdtos %f62, %f29 + fdtos %f62, %f30 + fdtos %f62, %f31 + +fitos_emul_fini: + ldd [%g6 + AOFF_task_fpregs + (62 * 4)], %f62 + done + .globl do_fptrap .align 32 do_fptrap: - ldub [%g6 + AOFF_task_thread + AOFF_thread_fpsaved], %g3 stx %fsr, [%g6 + AOFF_task_thread + AOFF_thread_xfsr] +do_fptrap_after_fsr: + ldub [%g6 + AOFF_task_thread + AOFF_thread_fpsaved], %g3 rd %fprs, %g1 or %g3, %g1, %g3 stb %g3, [%g6 + AOFF_task_thread + AOFF_thread_fpsaved] rd %gsr, %g3 - stb %g3, [%g6 + AOFF_task_thread + AOFF_thread_gsr] + stx %g3, [%g6 + AOFF_task_thread + AOFF_thread_gsr] mov SECONDARY_CONTEXT, %g3 add %g6, AOFF_task_fpregs, %g2 ldxa [%g3] ASI_DMMU, %g5 stxa %g0, [%g3] ASI_DMMU - flush %g6 - membar #StoreStore | #LoadStore + membar #Sync andcc %g1, FPRS_DL, %g0 be,pn %icc, 4f mov 0x40, %g3 @@ -193,7 +336,7 @@ do_fptrap: 5: mov SECONDARY_CONTEXT, %g1 membar #Sync stxa %g5, [%g1] ASI_DMMU - flush %g6 + membar #Sync ba,pt %xcc, etrap wr %g0, 0, %fprs @@ -215,7 +358,7 @@ do_fptrap: .globl do_ivec do_ivec: mov 0x40, %g3 - ldxa [%g3 + %g0] ASI_UDB_INTR_R, %g3 + ldxa [%g3 + %g0] ASI_INTR_R, %g3 sethi %hi(KERNBASE), %g4 cmp %g3, %g4 bgeu,pn %xcc, do_ivec_xcall @@ -242,10 +385,10 @@ do_ivec: do_ivec_xcall: mov 0x50, %g1 - ldxa [%g1 + %g0] ASI_UDB_INTR_R, %g1 + ldxa [%g1 + %g0] ASI_INTR_R, %g1 srl %g3, 0, %g3 mov 0x60, %g7 - ldxa [%g7 + %g0] ASI_UDB_INTR_R, %g7 + ldxa [%g7 + %g0] ASI_INTR_R, %g7 stxa %g0, [%g0] ASI_INTR_RECEIVE membar #Sync jmpl %g3, %g0 @@ -1114,9 +1257,18 @@ do_gettimeofday: /* %o0 = timevalp */ ldx [%g3 + %lo(timer_tick_offset)], %g3 or %g2, %lo(xtime), %g2 or %g1, %lo(timer_tick_compare), %g1 -1: ldda [%g2] ASI_NUCLEUS_QUAD_LDD, %o4 - rd %tick, %o1 - ldx [%g1], %g7 +1: rdpr %ver, %o2 + sethi %hi(0x003e0014), %o1 + srlx %o2, 32, %o2 + or %o1, %lo(0x003e0014), %o1 + ldda [%g2] ASI_NUCLEUS_QUAD_LDD, %o4 + cmp %o2, %o1 + bne,pt %xcc, 2f + nop + ba,pt %xcc, 3f + rd %asr24, %o1 +2: rd %tick, %o1 +3: ldx [%g1], %g7 ldda [%g2] ASI_NUCLEUS_QUAD_LDD, %o2 xor %o4, %o2, %o2 xor %o5, %o3, %o3 diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S index b0a8f766d..c6df20212 100644 --- a/arch/sparc64/kernel/etrap.S +++ b/arch/sparc64/kernel/etrap.S @@ -1,4 +1,4 @@ -/* $Id: etrap.S,v 1.43 2000/03/29 09:55:30 davem Exp $ +/* $Id: etrap.S,v 1.44 2001/03/22 00:51:25 davem Exp $ * etrap.S: Preparing for entry into the kernel on Sparc V9. * * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -186,3 +186,5 @@ scetrap: rdpr %pil, %g2 ! Single Group nop #undef TASK_REGOFF +#undef ETRAP_PSTATE1 +#undef ETRAP_PSTATE2 diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 5e855ad2c..6d52fb821 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.65 2000/05/09 17:40:13 davem Exp $ +/* $Id: head.S,v 1.75 2001/03/22 09:54:26 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -21,6 +21,8 @@ #include <asm/signal.h> #include <asm/processor.h> #include <asm/lsu.h> +#include <asm/dcr.h> +#include <asm/dcu.h> #include <asm/head.h> #include <asm/ttable.h> @@ -76,11 +78,145 @@ sparc_ramdisk_size: * PROM entry point is on %o4 */ sparc64_boot: + rdpr %ver, %g1 + sethi %hi(0x003e0014), %g5 + srlx %g1, 32, %g1 + or %g5, %lo(0x003e0014), %g5 + cmp %g1, %g5 + bne,pt %icc, spitfire_boot + nop + +cheetah_boot: + mov DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1 + wr %g1, %asr18 + + sethi %uhi(DCU_ME | DCU_RE | DCU_PE | DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5 + or %g5, %ulo(DCU_ME | DCU_RE | DCU_PE | DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5 + sllx %g5, 32, %g5 + or %g5, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g5 + ldxa [%g0] ASI_DCU_CONTROL_REG, %g3 + or %g5, %g3, %g5 + stxa %g5, [%g0] ASI_DCU_CONTROL_REG + membar #Sync + + wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate + wr %g0, 0, %fprs + + /* Just like for Spitfire, we probe itlb-2 for a mapping which + * matches our current %pc. We take the physical address in + * that mapping and use it to make our own. + */ + + /* %g5 holds the tlb data */ + sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 + sllx %g5, 32, %g5 + or %g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W | _PAGE_G), %g5 + + /* Put PADDR tlb data mask into %g3. */ + sethi %uhi(_PAGE_PADDR), %g3 + or %g3, %ulo(_PAGE_PADDR), %g3 + sllx %g3, 32, %g3 + sethi %hi(_PAGE_PADDR), %g7 + or %g7, %lo(_PAGE_PADDR), %g7 + or %g3, %g7, %g3 + + set 2 << 16, %l0 /* TLB entry walker. */ + set 0x1fff, %l2 /* Page mask. */ + rd %pc, %l3 + andn %l3, %l2, %g2 /* vaddr comparator */ + +1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1 + membar #Sync + andn %g1, %l2, %g1 + cmp %g1, %g2 + be,pn %xcc, cheetah_got_tlbentry + nop + and %l0, (127 << 3), %g1 + cmp %g1, (127 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + +cheetah_got_tlbentry: + ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1 + membar #Sync + and %g1, %g3, %g1 + sub %g1, %g2, %g1 + or %g5, %g1, %g5 + + /* Clear out any KERNBASE area entries. */ + set 2 << 16, %l0 + sethi %hi(KERNBASE), %g3 + sethi %hi(KERNBASE<<1), %g7 + mov TLB_TAG_ACCESS, %l7 + + /* First, check ITLB */ +1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1 + membar #Sync + andn %g1, %l2, %g1 + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_IMMU + membar #Sync + stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS + membar #Sync + +2: and %l0, (127 << 3), %g1 + cmp %g1, (127 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + /* Next, check DTLB */ + set 2 << 16, %l0 +1: ldxa [%l0] ASI_DTLB_TAG_READ, %g1 + membar #Sync + andn %g1, %l2, %g1 + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_DMMU + membar #Sync + stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS + membar #Sync + +2: and %l0, (511 << 3), %g1 + cmp %g1, (511 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + /* Now lock the TTE we created into ITLB-0 and DTLB-0, + * entry 15. + */ + sethi %hi(KERNBASE), %g3 + set (0 << 16) | (15 << 3), %g7 + stxa %g3, [%l7] ASI_DMMU + membar #Sync + stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS + membar #Sync + stxa %g3, [%l7] ASI_IMMU + membar #Sync + stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS + membar #Sync + flush %g3 + membar #Sync + ba,pt %xcc, 1f + nop + +1: set sun4u_init, %g2 + jmpl %g2 + %g0, %g0 + nop + +spitfire_boot: /* Typically PROM has already enabled both MMU's and both on-chip * caches, but we do it here anyway just to be paranoid. */ mov (LSU_CONTROL_IC|LSU_CONTROL_DC|LSU_CONTROL_IM|LSU_CONTROL_DM), %g1 stxa %g1, [%g0] ASI_LSU_CONTROL + membar #Sync /* * Make sure we are in privileged mode, have address masking, @@ -93,7 +229,7 @@ sparc64_boot: wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate wr %g0, 0, %fprs -create_mappings: +spitfire_create_mappings: /* %g5 holds the tlb data */ sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 sllx %g5, 32, %g5 @@ -104,11 +240,11 @@ create_mappings: */ /* Put PADDR tlb data mask into %g3. */ - sethi %uhi(_PAGE_PADDR), %g3 - or %g3, %ulo(_PAGE_PADDR), %g3 + sethi %uhi(_PAGE_PADDR_SF), %g3 + or %g3, %ulo(_PAGE_PADDR_SF), %g3 sllx %g3, 32, %g3 - sethi %hi(_PAGE_PADDR), %g7 - or %g7, %lo(_PAGE_PADDR), %g7 + sethi %hi(_PAGE_PADDR_SF), %g7 + or %g7, %lo(_PAGE_PADDR_SF), %g7 or %g3, %g7, %g3 /* Walk through entire ITLB, looking for entry which maps @@ -126,13 +262,13 @@ create_mappings: nop andn %g1, %l2, %g1 /* Get vaddr */ cmp %g1, %g2 - be,a,pn %xcc, got_tlbentry + be,a,pn %xcc, spitfire_got_tlbentry ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1 cmp %l0, (63 << 3) blu,pt %xcc, 1b add %l0, (1 << 3), %l0 -got_tlbentry: +spitfire_got_tlbentry: /* Nops here again, perhaps Cheetah/Blackbird are better behaved... */ nop nop @@ -164,6 +300,7 @@ got_tlbentry: nop stxa %g0, [%l7] ASI_IMMU stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS + membar #Sync 2: cmp %l0, (63 << 3) blu,pt %xcc, 1b @@ -186,6 +323,7 @@ got_tlbentry: nop stxa %g0, [%l7] ASI_DMMU stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS + membar #Sync 2: cmp %l0, (63 << 3) blu,pt %xcc, 1b @@ -235,7 +373,47 @@ sun4u_init: mov TLB_TAG_ACCESS, %g2 stxa %g3, [%g2] ASI_IMMU stxa %g3, [%g2] ASI_DMMU + membar #Sync + + rdpr %ver, %g1 + sethi %hi(0x003e0014), %g5 + srlx %g1, 32, %g1 + or %g5, %lo(0x003e0014), %g5 + cmp %g1, %g5 + bne,pt %icc, spitfire_tlb_fixup + nop + +cheetah_tlb_fixup: + set (0 << 16) | (15 << 3), %g7 + ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1 + andn %g1, (_PAGE_G), %g1 + stxa %g1, [%g7] ASI_ITLB_DATA_ACCESS + membar #Sync + ldxa [%g7] ASI_DTLB_DATA_ACCESS, %g1 + andn %g1, (_PAGE_G), %g1 + stxa %g1, [%g7] ASI_DTLB_DATA_ACCESS + membar #Sync + + /* Kill instruction prefetch queues. */ + flush %g3 + membar #Sync + + /* Set TLB type to cheetah. */ + mov 1, %g2 + sethi %hi(tlb_type), %g5 + stw %g2, [%g5 + %lo(tlb_type)] + + /* Patch copy/page operations to cheetah optimized versions. */ + call cheetah_patch_copyops + nop + call cheetah_patch_pgcopyops + nop + + ba,pt %xcc, tlb_fixup_done + nop + +spitfire_tlb_fixup: mov (63 << 3), %g7 ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1 andn %g1, (_PAGE_G), %g1 @@ -251,6 +429,12 @@ sun4u_init: flush %g3 membar #Sync + /* Set TLB type to spitfire. */ + mov 0, %g2 + sethi %hi(tlb_type), %g5 + stw %g2, [%g5 + %lo(tlb_type)] + +tlb_fixup_done: sethi %hi(init_task_union), %g6 or %g6, %lo(init_task_union), %g6 mov %sp, %l6 @@ -285,28 +469,19 @@ sun4u_init: wrpr %g0, 0x0, %tl /* Clear the bss */ - sethi %hi(8191), %l2 - or %l2, %lo(8191), %l2 - sethi %hi(__bss_start), %l0 - or %l0, %lo(__bss_start), %l0 - sethi %hi(_end), %l1 - or %l1, %lo(_end), %l1 - add %l1, %l2, %l1 - andn %l1, %l2, %l1 - add %l2, 1, %l2 - add %l0, %g0, %o0 -1: - mov %l2, %o1 + sethi %hi(__bss_start), %o0 + or %o0, %lo(__bss_start), %o0 + sethi %hi(_end), %o1 + or %o1, %lo(_end), %o1 call __bzero - add %l0, %l2, %l0 - cmp %l0, %l1 - blu,pt %xcc, 1b - add %l0, %g0, %o0 + sub %o1, %o0, %o1 /* Now clear empty_zero_page */ - mov %l2, %o1 + sethi %hi(8192), %o1 + or %o1, %lo(8192), %o1 + sethi %hi(KERNBASE), %g3 call __bzero - mov %g3, %o0 + or %g3, %lo(KERNBASE), %o0 mov %l6, %o1 ! OpenPROM stack call prom_init @@ -340,14 +515,16 @@ setup_tba: /* i0 = is_starfire */ wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate /* Set fixed globals used by dTLB miss handler. */ -#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) +#define KERN_HIGHBITS ((_PAGE_VALID|_PAGE_SZ4MB)^0xfffff80000000000) #define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) -#ifdef THIS_IS_CHEETAH -#error Dave, make sure you took care of other issues in rest of sparc64 code... -#define VPTE_BASE 0xffe0000000000000 -#else /* Spitfire/Blackbird */ -#define VPTE_BASE 0xfffffffe00000000 + +#define VPTE_BASE_SPITFIRE 0xfffffffe00000000 +#if 1 +#define VPTE_BASE_CHEETAH VPTE_BASE_SPITFIRE +#else +#define VPTE_BASE_CHEETAH 0xffe0000000000000 #endif + mov TSB_REG, %g1 stxa %g0, [%g1] ASI_DMMU membar #Sync @@ -356,13 +533,30 @@ setup_tba: /* i0 = is_starfire */ or %g2, %ulo(KERN_HIGHBITS), %g2 sllx %g2, 32, %g2 or %g2, KERN_LOWBITS, %g2 - sethi %uhi(VPTE_BASE), %g3 - or %g3, %ulo(VPTE_BASE), %g3 - sllx %g3, 32, %g3 + + rdpr %ver, %g3 + sethi %hi(0x003e0014), %g7 + srlx %g3, 32, %g3 + or %g7, %lo(0x003e0014), %g7 + cmp %g3, %g7 + bne,pt %icc, 1f + nop + + sethi %uhi(VPTE_BASE_CHEETAH), %g3 + or %g3, %ulo(VPTE_BASE_CHEETAH), %g3 + ba,pt %xcc, 2f + sllx %g3, 32, %g3 +1: + sethi %uhi(VPTE_BASE_SPITFIRE), %g3 + or %g3, %ulo(VPTE_BASE_SPITFIRE), %g3 + sllx %g3, 32, %g3 + +2: clr %g7 #undef KERN_HIGHBITS #undef KERN_LOWBITS -#undef VPTE_BASE +#undef VPTE_BASE_SPITFIRE +#undef VPTE_BASE_CHEETAH /* Setup Interrupt globals */ wrpr %o1, (PSTATE_IG|PSTATE_IE), %pstate @@ -371,9 +565,6 @@ setup_tba: /* i0 = is_starfire */ or %g5, %lo(__up_workvec), %g6 #else /* By definition of where we are, this is boot_cpu. */ - sethi %hi(cpu_data), %g5 - or %g5, %lo(cpu_data), %g5 - brz,pt %i0, not_starfire sethi %hi(0x1fff4000), %g1 or %g1, %lo(0x1fff4000), %g1 @@ -384,12 +575,27 @@ setup_tba: /* i0 = is_starfire */ nop not_starfire: + rdpr %ver, %g1 + sethi %hi(0x003e0014), %g5 + srlx %g1, 32, %g1 + or %g7, %lo(0x003e0014), %g5 + cmp %g1, %g5 + bne,pt %icc, not_cheetah + nop + + ldxa [%g0] ASI_SAFARI_CONFIG, %g1 + srlx %g1, 17, %g1 + and %g1, 0x3ff, %g1 ! 10bit Safari Agent ID + +not_cheetah: ldxa [%g0] ASI_UPA_CONFIG, %g1 srlx %g1, 17, %g1 and %g1, 0x1f, %g1 /* In theory this is: &(cpu_data[boot_cpu_id].irq_worklists[0]) */ set_worklist: + sethi %hi(cpu_data), %g5 + or %g5, %lo(cpu_data), %g5 sllx %g1, 7, %g1 add %g5, %g1, %g5 add %g5, 64, %g6 @@ -398,9 +604,23 @@ set_worklist: /* Kill PROM timer */ wr %g0, 0, %tick_cmpr + rdpr %ver, %g1 + sethi %hi(0x003e0014), %g5 + srlx %g1, 32, %g1 + or %g7, %lo(0x003e0014), %g5 + cmp %g1, %g5 + bne,pt %icc, 1f + nop + + /* Disable STICK_INT interrupts. */ + sethi %hi(0x80000000), %g1 + sllx %g1, 32, %g1 + wr %g1, %asr25 + /* Ok, we're done setting up all the state our trap mechanims needs, * now get back into normal globals and let the PROM know what is up. */ +1: wrpr %g0, %g0, %wstate wrpr %o1, PSTATE_IE, %pstate diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index e2cdc3613..621e5762f 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.107 2001/02/13 01:16:44 davem Exp $ +/* $Id: ioctl32.c,v 1.110 2001/03/22 12:51:25 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) @@ -48,6 +48,7 @@ #include <linux/blkpg.h> #include <linux/blk.h> #include <linux/elevator.h> +#include <linux/rtc.h> #if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) /* Ugh. This header really is not clean */ #define min min @@ -67,7 +68,6 @@ #include <asm/fbio.h> #include <asm/kbio.h> #include <asm/vuid_event.h> -#include <asm/rtc.h> #include <asm/openpromio.h> #include <asm/envctrl.h> #include <asm/audioio.h> @@ -2068,6 +2068,7 @@ typedef struct { u32 proc; u32 pv[ABS_MAX_PV + 1]; u32 lv[ABS_MAX_LV + 1]; + uint8_t vg_uuid[UUID_LEN+1]; /* volume group UUID */ } vg32_t; typedef struct { @@ -2093,6 +2094,7 @@ typedef struct { uint32_t pe_stale; u32 pe; u32 inode; + uint8_t pv_uuid[UUID_LEN+1]; } pv32_t; typedef struct { @@ -2103,9 +2105,16 @@ typedef struct { typedef struct { u32 lv_index; u32 lv; + /* Transfer size because user space and kernel space differ */ + uint16_t size; } lv_status_byindex_req32_t; typedef struct { + dev_t dev; + u32 lv; +} lv_status_bydev_req32_t; + +typedef struct { uint8_t lv_name[NAME_LEN]; kdev_t old_dev; kdev_t new_dev; @@ -2204,11 +2213,12 @@ static lv_t *get_lv_t(u32 p, int *errp) if (l->lv_block_exception) { lbe32 = (lv_block_exception32_t *)A(ptr2); memset(lbe, 0, size); - for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) { - err |= get_user(lbe->rsector_org, &lbe32->rsector_org); - err |= __get_user(lbe->rdev_org, &lbe32->rdev_org); - err |= __get_user(lbe->rsector_new, &lbe32->rsector_new); - err |= __get_user(lbe->rdev_new, &lbe32->rdev_new); + for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) { + err |= get_user(lbe->rsector_org, &lbe32->rsector_org); + err |= __get_user(lbe->rdev_org, &lbe32->rdev_org); + err |= __get_user(lbe->rsector_new, &lbe32->rsector_new); + err |= __get_user(lbe->rdev_new, &lbe32->rdev_new); + } } } @@ -2239,8 +2249,9 @@ static int copy_lv_t(u32 ptr, lv_t *l) err |= __copy_to_user(&ul->lv_remap_ptr, &l->lv_remap_ptr, ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); size = l->lv_allocated_le * sizeof(pe_t); - err |= __copy_to_user((void *)A(ptr1), l->lv_current_pe, size); - return -EFAULT; + if (ptr1) + err |= __copy_to_user((void *)A(ptr1), l->lv_current_pe, size); + return err ? -EFAULT : 0; } static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) @@ -2250,7 +2261,8 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) lv_req_t lv_req; le_remap_req_t le_remap; lv_status_byindex_req_t lv_byindex; - pv_status_req32_t pv_status; + lv_status_bydev_req_t lv_bydev; + pv_status_req_t pv_status; } u; pv_t p; int err; @@ -2273,6 +2285,11 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) kfree(v); return -EFAULT; } + if (copy_from_user(v->vg_uuid, ((vg32_t *)arg)->vg_uuid, UUID_LEN+1)) { + kfree(v); + return -EFAULT; + } + karg = v; memset(v->pv, 0, sizeof(v->pv) + sizeof(v->lv)); if (v->pv_max > ABS_MAX_PV || v->lv_max > ABS_MAX_LV) @@ -2286,11 +2303,18 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) err = -ENOMEM; break; } - err = copy_from_user(v->pv[i], (void *)A(ptr), sizeof(pv32_t) - 8); + err = copy_from_user(v->pv[i], (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); if (err) { err = -EFAULT; break; } + err = copy_from_user(v->pv[i]->pv_uuid, ((pv32_t *)A(ptr))->pv_uuid, UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + + v->pv[i]->pe = NULL; v->pv[i]->inode = NULL; } } @@ -2309,8 +2333,9 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) case LV_EXTEND: case LV_REDUCE: case LV_REMOVE: + case LV_RENAME: case LV_STATUS_BYNAME: - err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name)); + err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name)); if (err) return -EFAULT; if (cmd != LV_REMOVE) { err = __get_user(ptr, &((lv_req32_t *)arg)->lv); @@ -2319,24 +2344,29 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) } else u.lv_req.lv = NULL; break; + + case LV_STATUS_BYINDEX: err = get_user(u.lv_byindex.lv_index, &((lv_status_byindex_req32_t *)arg)->lv_index); err |= __get_user(ptr, &((lv_status_byindex_req32_t *)arg)->lv); if (err) return err; u.lv_byindex.lv = get_lv_t(ptr, &err); break; + case LV_STATUS_BYDEV: + err = get_user(u.lv_bydev.dev, &((lv_status_bydev_req32_t *)arg)->dev); + u.lv_bydev.lv = get_lv_t(ptr, &err); + if (err) return err; + u.lv_bydev.lv = &p; + p.pe = NULL; p.inode = NULL; + break; case VG_EXTEND: - err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8); + err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_from_user(p.pv_uuid, ((pv32_t *)arg)->pv_uuid, UUID_LEN+1); if (err) return -EFAULT; p.pe = NULL; p.inode = NULL; karg = &p; break; - case LE_REMAP: - err = copy_from_user(&u.le_remap, (void *)arg, sizeof(le_remap_req32_t)); - if (err) return -EFAULT; - u.le_remap.new_pe = ((le_remap_req32_t *)&u.le_remap)->new_pe; - u.le_remap.old_pe = ((le_remap_req32_t *)&u.le_remap)->old_pe; - break; case PV_CHANGE: case PV_STATUS: err = copy_from_user(&u.pv_status, arg, sizeof(u.lv_req.lv_name)); @@ -2345,7 +2375,7 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) if (err) return err; u.pv_status.pv = &p; if (cmd == PV_CHANGE) { - err = copy_from_user(&p, (void *)A(ptr), sizeof(pv32_t) - 8); + err = copy_from_user(&p, (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); if (err) return -EFAULT; p.pe = NULL; p.inode = NULL; } @@ -2361,6 +2391,9 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) clear_user(&((vg32_t *)arg)->proc, sizeof(vg32_t) - (long)&((vg32_t *)0)->proc)) err = -EFAULT; } + if (copy_to_user(((vg32_t *)arg)->vg_uuid, v->vg_uuid, UUID_LEN+1)) { + err = -EFAULT; + } kfree(v); break; case VG_CREATE: @@ -2383,12 +2416,21 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) if (!err) err = copy_lv_t(ptr, u.lv_byindex.lv); put_lv_t(u.lv_byindex.lv); } + break; case PV_STATUS: if (!err) { - err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8); - if (err) return -EFAULT; + err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_to_user(((pv_t *)A(ptr))->pv_uuid, p.pv_uuid, UUID_LEN + 1); + if (err) return -EFAULT; } break; + case LV_STATUS_BYDEV: + if (!err) { + if (!err) err = copy_lv_t(ptr, u.lv_bydev.lv); + put_lv_t(u.lv_byindex.lv); + } + break; } return err; } @@ -3222,8 +3264,22 @@ COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+5, int)) COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+6, int)) COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+7, int)) /* Little p (/dev/rtc, /dev/envctrl, etc.) */ -COMPATIBLE_IOCTL(RTCGET) -COMPATIBLE_IOCTL(RTCSET) +COMPATIBLE_IOCTL(_IOR('p', 20, int[7])) /* RTCGET */ +COMPATIBLE_IOCTL(_IOW('p', 21, int[7])) /* RTCSET */ +COMPATIBLE_IOCTL(RTC_AIE_ON) +COMPATIBLE_IOCTL(RTC_AIE_OFF) +COMPATIBLE_IOCTL(RTC_UIE_ON) +COMPATIBLE_IOCTL(RTC_UIE_OFF) +COMPATIBLE_IOCTL(RTC_PIE_ON) +COMPATIBLE_IOCTL(RTC_PIE_OFF) +COMPATIBLE_IOCTL(RTC_WIE_ON) +COMPATIBLE_IOCTL(RTC_WIE_OFF) +COMPATIBLE_IOCTL(RTC_ALM_SET) +COMPATIBLE_IOCTL(RTC_ALM_READ) +COMPATIBLE_IOCTL(RTC_RD_TIME) +COMPATIBLE_IOCTL(RTC_SET_TIME) +COMPATIBLE_IOCTL(RTC_WKALM_SET) +COMPATIBLE_IOCTL(RTC_WKALM_RD) COMPATIBLE_IOCTL(ENVCTRL_RD_WARNING_TEMPERATURE) COMPATIBLE_IOCTL(ENVCTRL_RD_SHUTDOWN_TEMPERATURE) COMPATIBLE_IOCTL(ENVCTRL_RD_CPU_TEMPERATURE) @@ -3331,6 +3387,7 @@ COMPATIBLE_IOCTL(PPPIOCSMRRU) COMPATIBLE_IOCTL(PPPIOCCONNECT) COMPATIBLE_IOCTL(PPPIOCDISCONN) COMPATIBLE_IOCTL(PPPIOCATTCHAN) +COMPATIBLE_IOCTL(PPPIOCGCHAN) /* PPPOX */ COMPATIBLE_IOCTL(PPPOEIOCSFWD); COMPATIBLE_IOCTL(PPPOEIOCDFWD); @@ -3565,6 +3622,7 @@ COMPATIBLE_IOCTL(VG_SET_EXTENDABLE) COMPATIBLE_IOCTL(VG_STATUS_GET_COUNT) COMPATIBLE_IOCTL(VG_STATUS_GET_NAMELIST) COMPATIBLE_IOCTL(VG_REMOVE) +COMPATIBLE_IOCTL(VG_RENAME) COMPATIBLE_IOCTL(VG_REDUCE) COMPATIBLE_IOCTL(PE_LOCK_UNLOCK) COMPATIBLE_IOCTL(PV_FLUSH) @@ -3576,6 +3634,9 @@ COMPATIBLE_IOCTL(LVM_RESET) COMPATIBLE_IOCTL(LV_SET_ACCESS) COMPATIBLE_IOCTL(LV_SET_STATUS) COMPATIBLE_IOCTL(LV_SET_ALLOCATION) +COMPATIBLE_IOCTL(LE_REMAP) +COMPATIBLE_IOCTL(LV_BMAP) +COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE) #endif /* LVM */ #if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC) @@ -3749,9 +3810,9 @@ HANDLE_IOCTL(LV_CREATE, do_lvm_ioctl) HANDLE_IOCTL(LV_REMOVE, do_lvm_ioctl) HANDLE_IOCTL(LV_EXTEND, do_lvm_ioctl) HANDLE_IOCTL(LV_REDUCE, do_lvm_ioctl) +HANDLE_IOCTL(LV_RENAME, do_lvm_ioctl) HANDLE_IOCTL(LV_STATUS_BYNAME, do_lvm_ioctl) HANDLE_IOCTL(LV_STATUS_BYINDEX, do_lvm_ioctl) -HANDLE_IOCTL(LE_REMAP, do_lvm_ioctl) HANDLE_IOCTL(PV_CHANGE, do_lvm_ioctl) HANDLE_IOCTL(PV_STATUS, do_lvm_ioctl) #endif /* LVM */ @@ -3766,6 +3827,12 @@ HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs); HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma); HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx); #endif /* DRM */ +#if 0 +HANDLE_IOCTL(RTC32_IRQP_READ, do_rtc_ioctl) +HANDLE_IOCTL(RTC32_IRQP_SET, do_rtc_ioctl) +HANDLE_IOCTL(RTC32_EPOCH_READ, do_rtc_ioctl) +HANDLE_IOCTL(RTC32_EPOCH_SET, do_rtc_ioctl) +#endif IOCTL_TABLE_END unsigned int ioctl32_hash_table[1024]; diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 1bd29505b..dbb55aaed 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.95 2001/02/13 01:16:44 davem Exp $ +/* $Id: irq.c,v 1.99 2001/03/22 02:19:23 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -132,14 +132,23 @@ void enable_irq(unsigned int irq) if (imap == 0UL) return; - if(this_is_starfire == 0) { + if (tlb_type == cheetah) { + /* We set it to our Safari AID. */ + __asm__ __volatile__("ldxa [%%g0] %1, %0" + : "=r" (tid) + : "i" (ASI_SAFARI_CONFIG)); + tid = ((tid & (0x3ffUL<<17)) << 9); + tid &= IMAP_AID_SAFARI; + } else if (this_is_starfire == 0) { /* We set it to our UPA MID. */ __asm__ __volatile__("ldxa [%%g0] %1, %0" : "=r" (tid) : "i" (ASI_UPA_CONFIG)); tid = ((tid & UPA_CONFIG_MID) << 9); + tid &= IMAP_TID_UPA; } else { tid = (starfire_translate(imap, current->processor) << 26); + tid &= IMAP_TID_UPA; } /* NOTE NOTE NOTE, IGN and INO are read-only, IGN is a product @@ -150,7 +159,7 @@ void enable_irq(unsigned int irq) * * Things like FFB can now be handled via the new IRQ mechanism. */ - upa_writel(IMAP_VALID | (tid & IMAP_TID), imap); + upa_writel(tid | IMAP_VALID, imap); } /* This now gets passed true ino's as well. */ @@ -729,6 +738,10 @@ void handler_irq(int irq, struct pt_regs *regs) /* Voo-doo programming. */ if (cpu_data[buddy].idle_volume < FORWARD_VOLUME) should_forward = 0; + + /* This just so happens to be correct on Cheetah + * at the moment. + */ buddy <<= 26; } #endif @@ -737,10 +750,23 @@ void handler_irq(int irq, struct pt_regs *regs) /* * Check for TICK_INT on level 14 softint. */ - if ((irq == 14) && (get_softint() & (1UL << 0))) - irq = 0; -#endif + { + unsigned long clr_mask = 1 << irq; + unsigned long tick_mask; + + if (SPARC64_USE_STICK) + tick_mask = (1UL << 16); + else + tick_mask = (1UL << 0); + if ((irq == 14) && (get_softint() & tick_mask)) { + irq = 0; + clr_mask = tick_mask; + } + clear_softint(clr_mask); + } +#else clear_softint(1 << irq); +#endif irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; @@ -952,8 +978,13 @@ void init_timers(void (*cfunc)(int, void *, struct pt_regs *), extern void smp_tick_init(void); #endif - node = linux_cpus[0].prom_node; - *clock = prom_getint(node, "clock-frequency"); + if (!SPARC64_USE_STICK) { + node = linux_cpus[0].prom_node; + *clock = prom_getint(node, "clock-frequency"); + } else { + node = prom_root_node; + *clock = prom_getint(node, "stick-frequency"); + } timer_tick_offset = *clock / HZ; #ifdef CONFIG_SMP smp_tick_init(); @@ -1003,6 +1034,7 @@ void init_timers(void (*cfunc)(int, void *, struct pt_regs *), * at the start of an I-cache line, and perform a dummy * read back from %tick_cmpr right after writing to it. -DaveM */ + if (!SPARC64_USE_STICK) { __asm__ __volatile__(" rd %%tick, %%g1 ba,pt %%xcc, 1f @@ -1013,6 +1045,26 @@ void init_timers(void (*cfunc)(int, void *, struct pt_regs *), : /* no outputs */ : "r" (timer_tick_offset) : "g1"); + } else { + /* Let the user get at STICK too. */ + __asm__ __volatile__(" + sethi %%hi(0x80000000), %%g1 + sllx %%g1, 32, %%g1 + rd %%asr24, %%g2 + andn %%g2, %%g1, %%g2 + wr %%g2, 0, %%asr24" + : /* no outputs */ + : /* no inputs */ + : "g1", "g2"); + + __asm__ __volatile__(" + rd %%asr24, %%g1 + add %%g1, %0, %%g1 + wr %%g1, 0x0, %%asr25" + : /* no outputs */ + : "r" (timer_tick_offset) + : "g1"); + } /* Restore PSTATE_IE. */ __asm__ __volatile__("wrpr %0, 0x0, %%pstate" @@ -1033,12 +1085,17 @@ static int retarget_one_irq(struct irqaction *p, int goal_cpu) if (bucket->pil == 12) return goal_cpu; - if(this_is_starfire == 0) { + if (tlb_type == cheetah) { + tid = __cpu_logical_map[goal_cpu] << 26; + tid &= IMAP_AID_SAFARI; + } else if (this_is_starfire == 0) { tid = __cpu_logical_map[goal_cpu] << 26; + tid &= IMAP_TID_UPA; } else { tid = (starfire_translate(imap, __cpu_logical_map[goal_cpu]) << 26); + tid &= IMAP_TID_UPA; } - upa_writel(IMAP_VALID | (tid & IMAP_TID), imap); + upa_writel(tid | IMAP_VALID, imap); goal_cpu++; if(goal_cpu >= NR_CPUS || @@ -1120,7 +1177,7 @@ static void kill_prom_timer(void) stxa %%g0, [%%g0] %0 membar #Sync " : /* no outputs */ - : "i" (ASI_INTR_RECEIVE), "i" (ASI_UDB_INTR_R) + : "i" (ASI_INTR_RECEIVE), "i" (ASI_INTR_R) : "g1", "g2"); } diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 009900165..fbdb39748 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -1,4 +1,4 @@ -/* $Id: pci.c,v 1.21 2001/01/10 18:22:59 davem Exp $ +/* $Id: pci.c,v 1.23 2001/03/14 04:17:14 davem Exp $ * pci.c: UltraSparc PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com) @@ -78,9 +78,7 @@ volatile int pci_poke_faulted; /* Probe for all PCI controllers in the system. */ extern void sabre_init(int); extern void psycho_init(int); -#if 0 extern void schizo_init(int); -#endif static struct { char *model_name; @@ -88,12 +86,11 @@ static struct { } pci_controller_table[] = { { "SUNW,sabre", sabre_init }, { "pci108e,a000", sabre_init }, + { "pci108e,a001", sabre_init }, { "SUNW,psycho", psycho_init }, - { "pci108e,8000", psycho_init } -#if 0 + { "pci108e,8000", psycho_init }, { "SUNW,schizo", schizo_init }, { "pci108e,8001", schizo_init } -#endif }; #define PCI_NUM_CONTROLLER_TYPES (sizeof(pci_controller_table) / \ sizeof(pci_controller_table[0])) diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index ff7060c0a..bcf950ee0 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -1,4 +1,4 @@ -/* $Id: pci_common.c,v 1.13 2001/02/13 01:16:44 davem Exp $ +/* $Id: pci_common.c,v 1.14 2001/02/28 03:28:55 davem Exp $ * pci_common.c: PCI controller common support. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) @@ -560,19 +560,19 @@ static void __init pdev_fixup_irq(struct pci_dev *pdev) /* Fully specified already? */ if (((prom_irq & PCI_IRQ_IGN) >> 6) == portid) { - pdev->irq = p->irq_build(p, pdev, prom_irq); + pdev->irq = p->irq_build(pbm, pdev, prom_irq); goto have_irq; } /* An onboard device? (bit 5 set) */ if ((prom_irq & PCI_IRQ_INO) & 0x20) { - pdev->irq = p->irq_build(p, pdev, (portid << 6 | prom_irq)); + pdev->irq = p->irq_build(pbm, pdev, (portid << 6 | prom_irq)); goto have_irq; } /* Can we find a matching entry in the interrupt-map? */ if (pci_intmap_match(pdev, &prom_irq)) { - pdev->irq = p->irq_build(p, pdev, (portid << 6) | prom_irq); + pdev->irq = p->irq_build(pbm, pdev, (portid << 6) | prom_irq); goto have_irq; } @@ -609,7 +609,7 @@ static void __init pdev_fixup_irq(struct pci_dev *pdev) } slot = slot << 2; - pdev->irq = p->irq_build(p, pdev, + pdev->irq = p->irq_build(pbm, pdev, ((portid << 6) & PCI_IRQ_IGN) | (bus | slot | line)); } @@ -632,17 +632,11 @@ void __init pci_fixup_irq(struct pci_pbm_info *pbm, pci_fixup_irq(pbm, pci_bus_b(walk)); } -#undef DEBUG_BUSMASTERING - static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz) { u16 cmd; u8 hdr_type, min_gnt, ltimer; -#ifdef DEBUG_BUSMASTERING - printk("PCI: Checking DEV(%s), ", pdev->name); -#endif - pci_read_config_word(pdev, PCI_COMMAND, &cmd); cmd |= PCI_COMMAND_MASTER; pci_write_config_word(pdev, PCI_COMMAND, cmd); @@ -652,43 +646,28 @@ static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz) * mastering so we have nothing to do here. */ pci_read_config_word(pdev, PCI_COMMAND, &cmd); - if ((cmd & PCI_COMMAND_MASTER) == 0) { -#ifdef DEBUG_BUSMASTERING - printk("no bus mastering...\n"); -#endif + if ((cmd & PCI_COMMAND_MASTER) == 0) return; - } /* Set correct cache line size, 64-byte on all * Sparc64 PCI systems. Note that the value is * measured in 32-bit words. */ -#ifdef DEBUG_BUSMASTERING - printk("set cachelinesize, "); -#endif pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64 / sizeof(u32)); pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr_type); hdr_type &= ~0x80; - if (hdr_type != PCI_HEADER_TYPE_NORMAL) { -#ifdef DEBUG_BUSMASTERING - printk("hdr_type=%x, exit\n", hdr_type); -#endif + if (hdr_type != PCI_HEADER_TYPE_NORMAL) return; - } /* If the latency timer is already programmed with a non-zero * value, assume whoever set it (OBP or whoever) knows what * they are doing. */ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, <imer); - if (ltimer != 0) { -#ifdef DEBUG_BUSMASTERING - printk("ltimer was %x, exit\n", ltimer); -#endif + if (ltimer != 0) return; - } /* XXX Since I'm tipping off the min grant value to * XXX choose a suitable latency timer value, I also @@ -738,9 +717,6 @@ static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz) } pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ltimer); -#ifdef DEBUG_BUSMASTERING - printk("set ltimer to %x\n", ltimer); -#endif } void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm, diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c index 08d5b8ee3..5e21a9649 100644 --- a/arch/sparc64/kernel/pci_iommu.c +++ b/arch/sparc64/kernel/pci_iommu.c @@ -1,4 +1,4 @@ -/* $Id: pci_iommu.c,v 1.12 2001/01/11 16:26:45 davem Exp $ +/* $Id: pci_iommu.c,v 1.13 2001/03/14 08:42:38 davem Exp $ * pci_iommu.c: UltraSparc PCI controller IOM/STC support. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) @@ -213,9 +213,7 @@ void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_ad first_page += PAGE_SIZE; } - if (iommu->iommu_ctxflush) { - pci_iommu_write(iommu->iommu_ctxflush, ctx); - } else { + { int i; u32 daddr = *dma_addrp; diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index 7a671158e..ac7f5ab81 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -1,4 +1,4 @@ -/* $Id: pci_psycho.c,v 1.19 2001/02/13 01:16:44 davem Exp $ +/* $Id: pci_psycho.c,v 1.21 2001/02/28 03:28:55 davem Exp $ * pci_psycho.c: PSYCHO/U2P specific PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) @@ -72,7 +72,7 @@ * --------------------------------------------------------- */ #define PSYCHO_CONFIG_BASE(PBM) \ - ((PBM)->parent->config_space | (1UL << 24)) + ((PBM)->config_space | (1UL << 24)) #define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG) \ (((unsigned long)(BUS) << 16) | \ ((unsigned long)(DEVFN) << 8) | \ @@ -376,10 +376,11 @@ static int __init psycho_ino_to_pil(struct pci_dev *pdev, unsigned int ino) return ret; } -static unsigned int __init psycho_irq_build(struct pci_controller_info *p, +static unsigned int __init psycho_irq_build(struct pci_pbm_info *pbm, struct pci_dev *pdev, unsigned int ino) { + struct pci_controller_info *p = pbm->parent; struct ino_bucket *bucket; unsigned long imap, iclr; unsigned long imap_off, iclr_off; @@ -1002,12 +1003,13 @@ static void psycho_pcierr_intr(int irq, void *dev_id, struct pt_regs *regs) #define PSYCHO_PCIERR_B_INO 0x31 static void __init psycho_register_error_handlers(struct pci_controller_info *p) { + struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */ unsigned long base = p->controller_regs; unsigned int irq, portid = p->portid; u64 tmp; /* Build IRQs and register handlers. */ - irq = psycho_irq_build(p, NULL, (portid << 6) | PSYCHO_UE_INO); + irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_UE_INO); if (request_irq(irq, psycho_ue_intr, SA_SHIRQ, "PSYCHO UE", p) < 0) { prom_printf("PSYCHO%d: Cannot register UE interrupt.\n", @@ -1015,7 +1017,7 @@ static void __init psycho_register_error_handlers(struct pci_controller_info *p) prom_halt(); } - irq = psycho_irq_build(p, NULL, (portid << 6) | PSYCHO_CE_INO); + irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_CE_INO); if (request_irq(irq, psycho_ce_intr, SA_SHIRQ, "PSYCHO CE", p) < 0) { prom_printf("PSYCHO%d: Cannot register CE interrupt.\n", @@ -1023,7 +1025,7 @@ static void __init psycho_register_error_handlers(struct pci_controller_info *p) prom_halt(); } - irq = psycho_irq_build(p, NULL, (portid << 6) | PSYCHO_PCIERR_A_INO); + irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_A_INO); if (request_irq(irq, psycho_pcierr_intr, SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_A) < 0) { prom_printf("PSYCHO%d(PBMA): Cannot register PciERR interrupt.\n", @@ -1031,7 +1033,7 @@ static void __init psycho_register_error_handlers(struct pci_controller_info *p) prom_halt(); } - irq = psycho_irq_build(p, NULL, (portid << 6) | PSYCHO_PCIERR_B_INO); + irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_B_INO); if (request_irq(irq, psycho_pcierr_intr, SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_B) < 0) { prom_printf("PSYCHO%d(PBMB): Cannot register PciERR interrupt.\n", @@ -1574,8 +1576,10 @@ void __init psycho_init(int node) printk("PCI: Found PSYCHO, control regs at %016lx\n", p->controller_regs); - p->config_space = pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE; - printk("PSYCHO: PCI config space at %016lx\n", p->config_space); + p->pbm_A.config_space = p->pbm_B.config_space = + (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE); + printk("PSYCHO: Shared PCI config space at %016lx\n", + p->pbm_A.config_space); /* * Psycho's PCI MEM space is mapped to a 2GB aligned area, so diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index cce2e5467..07632eaec 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -1,4 +1,4 @@ -/* $Id: pci_sabre.c,v 1.23 2001/02/13 01:16:44 davem Exp $ +/* $Id: pci_sabre.c,v 1.25 2001/02/28 03:28:55 davem Exp $ * pci_sabre.c: Sabre specific PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) @@ -209,7 +209,7 @@ * --------------------------------------------------------- */ #define SABRE_CONFIG_BASE(PBM) \ - ((PBM)->parent->config_space | (1UL << 24)) + ((PBM)->config_space | (1UL << 24)) #define SABRE_CONFIG_ENCODE(BUS, DEVFN, REG) \ (((unsigned long)(BUS) << 16) | \ ((unsigned long)(DEVFN) << 8) | \ @@ -604,10 +604,11 @@ static int __init sabre_ino_to_pil(struct pci_dev *pdev, unsigned int ino) return ret; } -static unsigned int __init sabre_irq_build(struct pci_controller_info *p, +static unsigned int __init sabre_irq_build(struct pci_pbm_info *pbm, struct pci_dev *pdev, unsigned int ino) { + struct pci_controller_info *p = pbm->parent; struct ino_bucket *bucket; unsigned long imap, iclr; unsigned long imap_off, iclr_off; @@ -961,6 +962,7 @@ static void sabre_pcierr_intr(int irq, void *dev_id, struct pt_regs *regs) #define SABRE_PCIERR_INO 0x30 static void __init sabre_register_error_handlers(struct pci_controller_info *p) { + struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */ unsigned long base = p->controller_regs; unsigned long irq, portid = p->portid; u64 tmp; @@ -973,7 +975,7 @@ static void __init sabre_register_error_handlers(struct pci_controller_info *p) (SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR | SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR | SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE)); - irq = sabre_irq_build(p, NULL, (portid << 6) | SABRE_UE_INO); + irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_UE_INO); if (request_irq(irq, sabre_ue_intr, SA_SHIRQ, "SABRE UE", p) < 0) { prom_printf("SABRE%d: Cannot register UE interrupt.\n", @@ -984,7 +986,7 @@ static void __init sabre_register_error_handlers(struct pci_controller_info *p) sabre_write(base + SABRE_CE_AFSR, (SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR | SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR)); - irq = sabre_irq_build(p, NULL, (portid << 6) | SABRE_CE_INO); + irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_CE_INO); if (request_irq(irq, sabre_ce_intr, SA_SHIRQ, "SABRE CE", p) < 0) { prom_printf("SABRE%d: Cannot register CE interrupt.\n", @@ -992,7 +994,7 @@ static void __init sabre_register_error_handlers(struct pci_controller_info *p) prom_halt(); } - irq = sabre_irq_build(p, NULL, (portid << 6) | SABRE_PCIERR_INO); + irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_PCIERR_INO); if (request_irq(irq, sabre_pcierr_intr, SA_SHIRQ, "SABRE PCIERR", p) < 0) { prom_printf("SABRE%d: Cannot register PciERR interrupt.\n", @@ -1434,8 +1436,10 @@ void __init sabre_init(int pnode) SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN)); /* Now map in PCI config space for entire SABRE. */ - p->config_space = p->controller_regs + SABRE_CONFIGSPACE; - printk("SABRE: PCI config space at %016lx\n", p->config_space); + p->pbm_A.config_space = p->pbm_B.config_space = + (p->controller_regs + SABRE_CONFIGSPACE); + printk("SABRE: Shared PCI config space at %016lx\n", + p->pbm_A.config_space); err = prom_getproperty(pnode, "virtual-dma", (char *)&vdma[0], sizeof(vdma)); diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c index daac0d783..ebeeb0bfa 100644 --- a/arch/sparc64/kernel/pci_schizo.c +++ b/arch/sparc64/kernel/pci_schizo.c @@ -1,4 +1,4 @@ -/* $Id: pci_schizo.c,v 1.3 2001/02/13 01:16:44 davem Exp $ +/* $Id: pci_schizo.c,v 1.13 2001/03/21 00:29:58 davem Exp $ * pci_schizo.c: SCHIZO specific PCI controller support. * * Copyright (C) 2001 David S. Miller (davem@redhat.com) @@ -13,37 +13,244 @@ #include <asm/pbm.h> #include <asm/iommu.h> #include <asm/irq.h> +#include <asm/upa.h> #include "pci_impl.h" +/* All SCHIZO registers are 64-bits. The following accessor + * routines are how they are accessed. The REG parameter + * is a physical address. + */ +#define schizo_read(__reg) \ +({ u64 __ret; \ + __asm__ __volatile__("ldxa [%1] %2, %0" \ + : "=r" (__ret) \ + : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ + : "memory"); \ + __ret; \ +}) +#define schizo_write(__reg, __val) \ + __asm__ __volatile__("stxa %0, [%1] %2" \ + : /* no outputs */ \ + : "r" (__val), "r" (__reg), \ + "i" (ASI_PHYS_BYPASS_EC_E)) + +/* This is a convention that at least Excalibur and Merlin + * follow. I suppose the SCHIZO used in Starcat and friends + * will do similar. + * + * The only way I could see this changing is if the newlink + * block requires more space in Schizo's address space than + * they predicted, thus requiring an address space reorg when + * the newer Schizo is taped out. + * + * These offsets look weird because I keep in p->controller_regs + * the second PROM register property minus 0x10000 which is the + * base of the Safari and UPA64S registers of SCHIZO. + */ +#define SCHIZO_PBM_A_REGS_OFF (0x600000UL - 0x400000UL) +#define SCHIZO_PBM_B_REGS_OFF (0x700000UL - 0x400000UL) + +/* Streaming buffer control register. */ +#define SCHIZO_STRBUF_CTRL_LPTR 0x00000000000000f0UL /* LRU Lock Pointer */ +#define SCHIZO_STRBUF_CTRL_LENAB 0x0000000000000008UL /* LRU Lock Enable */ +#define SCHIZO_STRBUF_CTRL_RRDIS 0x0000000000000004UL /* Rerun Disable */ +#define SCHIZO_STRBUF_CTRL_DENAB 0x0000000000000002UL /* Diagnostic Mode Enable */ +#define SCHIZO_STRBUF_CTRL_ENAB 0x0000000000000001UL /* Streaming Buffer Enable */ + +/* IOMMU control register. */ +#define SCHIZO_IOMMU_CTRL_RESV 0xfffffffff9000000 /* Reserved */ +#define SCHIZO_IOMMU_CTRL_XLTESTAT 0x0000000006000000 /* Translation Error Status */ +#define SCHIZO_IOMMU_CTRL_XLTEERR 0x0000000001000000 /* Translation Error encountered */ +#define SCHIZO_IOMMU_CTRL_LCKEN 0x0000000000800000 /* Enable translation locking */ +#define SCHIZO_IOMMU_CTRL_LCKPTR 0x0000000000780000 /* Translation lock pointer */ +#define SCHIZO_IOMMU_CTRL_TSBSZ 0x0000000000070000 /* TSB Size */ +#define SCHIZO_IOMMU_TSBSZ_1K 0x0000000000000000 /* TSB Table 1024 8-byte entries */ +#define SCHIZO_IOMMU_TSBSZ_2K 0x0000000000010000 /* TSB Table 2048 8-byte entries */ +#define SCHIZO_IOMMU_TSBSZ_4K 0x0000000000020000 /* TSB Table 4096 8-byte entries */ +#define SCHIZO_IOMMU_TSBSZ_8K 0x0000000000030000 /* TSB Table 8192 8-byte entries */ +#define SCHIZO_IOMMU_TSBSZ_16K 0x0000000000040000 /* TSB Table 16k 8-byte entries */ +#define SCHIZO_IOMMU_TSBSZ_32K 0x0000000000050000 /* TSB Table 32k 8-byte entries */ +#define SCHIZO_IOMMU_TSBSZ_64K 0x0000000000060000 /* TSB Table 64k 8-byte entries */ +#define SCHIZO_IOMMU_TSBSZ_128K 0x0000000000070000 /* TSB Table 128k 8-byte entries */ +#define SCHIZO_IOMMU_CTRL_RESV2 0x000000000000fff8 /* Reserved */ +#define SCHIZO_IOMMU_CTRL_TBWSZ 0x0000000000000004 /* Assumed page size, 0=8k 1=64k */ +#define SCHIZO_IOMMU_CTRL_DENAB 0x0000000000000002 /* Diagnostic mode enable */ +#define SCHIZO_IOMMU_CTRL_ENAB 0x0000000000000001 /* IOMMU Enable */ + +/* Schizo config space address format is nearly identical to + * that of PSYCHO: + * + * 32 24 23 16 15 11 10 8 7 2 1 0 + * --------------------------------------------------------- + * |0 0 0 0 0 0 0 0 0| bus | device | function | reg | 0 0 | + * --------------------------------------------------------- + */ +#define SCHIZO_CONFIG_BASE(PBM) ((PBM)->config_space) +#define SCHIZO_CONFIG_ENCODE(BUS, DEVFN, REG) \ + (((unsigned long)(BUS) << 16) | \ + ((unsigned long)(DEVFN) << 8) | \ + ((unsigned long)(REG))) + +static void *schizo_pci_config_mkaddr(struct pci_pbm_info *pbm, + unsigned char bus, + unsigned int devfn, + int where) +{ + if (!pbm) + return NULL; + return (void *) + (SCHIZO_CONFIG_BASE(pbm) | + SCHIZO_CONFIG_ENCODE(bus, devfn, where)); +} + +/* 4 slots on pbm A, and 6 slots on pbm B. In both cases + * slot 0 is the SCHIZO host bridge itself. + */ +static int schizo_out_of_range(struct pci_pbm_info *pbm, + unsigned char bus, + unsigned char devfn) +{ + return ((pbm->parent == 0) || + ((pbm == &pbm->parent->pbm_B) && + (bus == pbm->pci_first_busno) && + PCI_SLOT(devfn) > 6) || + ((pbm == &pbm->parent->pbm_A) && + (bus == pbm->pci_first_busno) && + PCI_SLOT(devfn) > 4)); +} + +/* SCHIZO PCI configuration space accessors. */ + static int schizo_read_byte(struct pci_dev *dev, int where, u8 *value) { - /* IMPLEMENT ME */ + struct pci_pbm_info *pbm = pci_bus2pbm[dev->bus->number]; + unsigned char bus = dev->bus->number; + unsigned int devfn = dev->devfn; + u8 *addr; + + *value = 0xff; + addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where); + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (schizo_out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + pci_config_read8(addr, value); + return PCIBIOS_SUCCESSFUL; } static int schizo_read_word(struct pci_dev *dev, int where, u16 *value) { - /* IMPLEMENT ME */ + struct pci_pbm_info *pbm = pci_bus2pbm[dev->bus->number]; + unsigned char bus = dev->bus->number; + unsigned int devfn = dev->devfn; + u16 *addr; + + *value = 0xffff; + addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where); + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (schizo_out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x01) { + printk("pcibios_read_config_word: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_config_read16(addr, value); + return PCIBIOS_SUCCESSFUL; } static int schizo_read_dword(struct pci_dev *dev, int where, u32 *value) { - /* IMPLEMENT ME */ + struct pci_pbm_info *pbm = pci_bus2pbm[dev->bus->number]; + unsigned char bus = dev->bus->number; + unsigned int devfn = dev->devfn; + u32 *addr; + + *value = 0xffffffff; + addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where); + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (schizo_out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x03) { + printk("pcibios_read_config_dword: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + + pci_config_read32(addr, value); + return PCIBIOS_SUCCESSFUL; } static int schizo_write_byte(struct pci_dev *dev, int where, u8 value) { - /* IMPLEMENT ME */ + struct pci_pbm_info *pbm = pci_bus2pbm[dev->bus->number]; + unsigned char bus = dev->bus->number; + unsigned int devfn = dev->devfn; + u8 *addr; + + addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where); + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (schizo_out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + pci_config_write8(addr, value); + return PCIBIOS_SUCCESSFUL; } static int schizo_write_word(struct pci_dev *dev, int where, u16 value) { - /* IMPLEMENT ME */ + struct pci_pbm_info *pbm = pci_bus2pbm[dev->bus->number]; + unsigned char bus = dev->bus->number; + unsigned int devfn = dev->devfn; + u16 *addr; + + addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where); + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (schizo_out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x01) { + printk("pcibios_write_config_word: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_config_write16(addr, value); + return PCIBIOS_SUCCESSFUL; } static int schizo_write_dword(struct pci_dev *dev, int where, u32 value) { - /* IMPLEMENT ME */ + struct pci_pbm_info *pbm = pci_bus2pbm[dev->bus->number]; + unsigned char bus = dev->bus->number; + unsigned int devfn = dev->devfn; + u32 *addr; + + addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where); + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (schizo_out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x03) { + printk("pcibios_write_config_dword: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_config_write32(addr, value); + return PCIBIOS_SUCCESSFUL; } static struct pci_ops schizo_ops = { @@ -55,34 +262,1445 @@ static struct pci_ops schizo_ops = { schizo_write_dword }; -static void __init schizo_scan_bus(struct pci_controller_info *p) +/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the + * imap/iclr registers are per-PBM. + */ +#define SCHIZO_IMAP_BASE 0x1000UL +#define SCHIZO_ICLR_BASE 0x1400UL + +static unsigned long schizo_imap_offset(unsigned long ino) { - /* IMPLEMENT ME */ + return SCHIZO_IMAP_BASE + (ino * 8UL); } -static unsigned int __init schizo_irq_build(struct pci_controller_info *p, +static unsigned long schizo_iclr_offset(unsigned long ino) +{ + return SCHIZO_ICLR_BASE + (ino * 8UL); +} + +/* PCI SCHIZO INO number to Sparc PIL level. This table only matters for + * INOs which will not have an associated PCI device struct, ie. onboard + * EBUS devices and PCI controller internal error interrupts. + */ +static unsigned char schizo_pil_table[] = { +/*0x00*/0, 0, 0, 0, /* PCI slot 0 Int A, B, C, D */ +/*0x04*/0, 0, 0, 0, /* PCI slot 1 Int A, B, C, D */ +/*0x08*/0, 0, 0, 0, /* PCI slot 2 Int A, B, C, D */ +/*0x0c*/0, 0, 0, 0, /* PCI slot 3 Int A, B, C, D */ +/*0x10*/0, 0, 0, 0, /* PCI slot 4 Int A, B, C, D */ +/*0x14*/0, 0, 0, 0, /* PCI slot 5 Int A, B, C, D */ +/*0x18*/3, /* SCSI */ +/*0x19*/3, /* second SCSI */ +/*0x1a*/0, /* UNKNOWN */ +/*0x1b*/0, /* UNKNOWN */ +/*0x1c*/8, /* Parallel */ +/*0x1d*/5, /* Ethernet */ +/*0x1e*/8, /* Firewire-1394 */ +/*0x1f*/9, /* USB */ +/*0x20*/13, /* Audio Record */ +/*0x21*/14, /* Audio Playback */ +/*0x22*/12, /* Serial */ +/*0x23*/2, /* EBUS I2C */ +/*0x24*/10, /* RTC Clock */ +/*0x25*/11, /* Floppy */ +/*0x26*/0, /* UNKNOWN */ +/*0x27*/0, /* UNKNOWN */ +/*0x28*/0, /* UNKNOWN */ +/*0x29*/0, /* UNKNOWN */ +/*0x2a*/10, /* UPA 1 */ +/*0x2b*/10, /* UPA 2 */ +/*0x2c*/0, /* UNKNOWN */ +/*0x2d*/0, /* UNKNOWN */ +/*0x2e*/0, /* UNKNOWN */ +/*0x2f*/0, /* UNKNOWN */ +/*0x30*/15, /* Uncorrectable ECC */ +/*0x31*/15, /* Correctable ECC */ +/*0x32*/15, /* PCI Bus A Error */ +/*0x33*/15, /* PCI Bus B Error */ +/*0x34*/15, /* Safari Bus Error */ +/*0x35*/0, /* Reserved */ +/*0x36*/0, /* Reserved */ +/*0x37*/0, /* Reserved */ +/*0x38*/0, /* Reserved for NewLink */ +/*0x39*/0, /* Reserved for NewLink */ +/*0x3a*/0, /* Reserved for NewLink */ +/*0x3b*/0, /* Reserved for NewLink */ +/*0x3c*/0, /* Reserved for NewLink */ +/*0x3d*/0, /* Reserved for NewLink */ +/*0x3e*/0, /* Reserved for NewLink */ +/*0x3f*/0, /* Reserved for NewLink */ +}; + +static int __init schizo_ino_to_pil(struct pci_dev *pdev, unsigned int ino) +{ + int ret; + + ret = schizo_pil_table[ino]; + if (ret == 0 && pdev == NULL) { + ret = 1; + } else if (ret == 0) { + switch ((pdev->class >> 16) & 0x0f) { + case PCI_BASE_CLASS_STORAGE: + ret = 4; + + case PCI_BASE_CLASS_NETWORK: + ret = 6; + + case PCI_BASE_CLASS_DISPLAY: + ret = 9; + + case PCI_BASE_CLASS_MULTIMEDIA: + case PCI_BASE_CLASS_MEMORY: + case PCI_BASE_CLASS_BRIDGE: + ret = 10; + + default: + ret = 1; + }; + } + + return ret; +} + +static unsigned int __init schizo_irq_build(struct pci_pbm_info *pbm, struct pci_dev *pdev, unsigned int ino) { - /* IMPLEMENT ME */ + struct pci_controller_info *p = pbm->parent; + struct ino_bucket *bucket; + unsigned long imap, iclr, pbm_off; + unsigned long imap_off, iclr_off; + int pil, inofixup = 0; + + if (pbm == &p->pbm_A) + pbm_off = SCHIZO_PBM_A_REGS_OFF; + else + pbm_off = SCHIZO_PBM_B_REGS_OFF; + + ino &= PCI_IRQ_INO; + imap_off = schizo_imap_offset(ino); + + /* Now build the IRQ bucket. */ + pil = schizo_ino_to_pil(pdev, ino); + imap = p->controller_regs + pbm_off + imap_off; + imap += 4; + + iclr_off = schizo_iclr_offset(ino); + iclr = p->controller_regs + pbm_off + iclr_off; + iclr += 4; + + if (ino < 0x18) + inofixup = ino & 0x03; + + bucket = __bucket(build_irq(pil, inofixup, iclr, imap)); + bucket->flags |= IBF_PCI; + + return __irq(bucket); +} + +/* SCHIZO error handling support. */ +enum schizo_error_type { + UE_ERR, CE_ERR, PCI_ERR, SAFARI_ERR +}; + +static spinlock_t stc_buf_lock = SPIN_LOCK_UNLOCKED; +static unsigned long stc_error_buf[128]; +static unsigned long stc_tag_buf[16]; +static unsigned long stc_line_buf[16]; + +static void schizo_clear_other_err_intr(int irq) +{ + struct ino_bucket *bucket = __bucket(irq); + unsigned long iclr = bucket->iclr; + + iclr += (SCHIZO_PBM_B_REGS_OFF - SCHIZO_PBM_A_REGS_OFF); + upa_writel(ICLR_IDLE, iclr); +} + +#define SCHIZO_STC_ERR 0xb800UL /* --> 0xba00 */ +#define SCHIZO_STC_TAG 0xba00UL /* --> 0xba80 */ +#define SCHIZO_STC_LINE 0xbb00UL /* --> 0xbb80 */ + +#define SCHIZO_STCERR_WRITE 0x2UL +#define SCHIZO_STCERR_READ 0x1UL + +#define SCHIZO_STCTAG_PPN 0x3fffffff00000000UL +#define SCHIZO_STCTAG_VPN 0x00000000ffffe000UL +#define SCHIZO_STCTAG_VALID 0x8000000000000000UL +#define SCHIZO_STCTAG_READ 0x4000000000000000UL + +#define SCHIZO_STCLINE_LINDX 0x0000000007800000UL +#define SCHIZO_STCLINE_SPTR 0x000000000007e000UL +#define SCHIZO_STCLINE_LADDR 0x0000000000001fc0UL +#define SCHIZO_STCLINE_EPTR 0x000000000000003fUL +#define SCHIZO_STCLINE_VALID 0x0000000000600000UL +#define SCHIZO_STCLINE_FOFN 0x0000000000180000UL + +static void __schizo_check_stc_error_pbm(struct pci_pbm_info *pbm, + enum schizo_error_type type) +{ + struct pci_controller_info *p = pbm->parent; + struct pci_strbuf *strbuf = &pbm->stc; + unsigned long regbase = p->controller_regs; + unsigned long err_base, tag_base, line_base; + u64 control; + char pbm_name = (pbm == &p->pbm_A ? 'A' : 'B'); + int i; + + if (pbm == &p->pbm_A) + regbase += SCHIZO_PBM_A_REGS_OFF; + else + regbase += SCHIZO_PBM_B_REGS_OFF; + + err_base = regbase + SCHIZO_STC_ERR; + tag_base = regbase + SCHIZO_STC_TAG; + line_base = regbase + SCHIZO_STC_LINE; + + spin_lock(&stc_buf_lock); + + /* This is __REALLY__ dangerous. When we put the + * streaming buffer into diagnostic mode to probe + * it's tags and error status, we _must_ clear all + * of the line tag valid bits before re-enabling + * the streaming buffer. If any dirty data lives + * in the STC when we do this, we will end up + * invalidating it before it has a chance to reach + * main memory. + */ + control = schizo_read(strbuf->strbuf_control); + schizo_write(strbuf->strbuf_control, + (control | SCHIZO_STRBUF_CTRL_DENAB)); + for (i = 0; i < 128; i++) { + unsigned long val; + + val = schizo_read(err_base + (i * 8UL)); + schizo_write(err_base + (i * 8UL), 0UL); + stc_error_buf[i] = val; + } + for (i = 0; i < 16; i++) { + stc_tag_buf[i] = schizo_read(tag_base + (i * 8UL)); + stc_line_buf[i] = schizo_read(line_base + (i * 8UL)); + schizo_write(tag_base + (i * 8UL), 0UL); + schizo_write(line_base + (i * 8UL), 0UL); + } + + /* OK, state is logged, exit diagnostic mode. */ + schizo_write(strbuf->strbuf_control, control); + + for (i = 0; i < 16; i++) { + int j, saw_error, first, last; + + saw_error = 0; + first = i * 8; + last = first + 8; + for (j = first; j < last; j++) { + unsigned long errval = stc_error_buf[j]; + if (errval != 0) { + saw_error++; + printk("SCHIZO%d: PBM-%c STC_ERR(%d)[wr(%d)rd(%d)]\n", + p->index, pbm_name, + j, + (errval & SCHIZO_STCERR_WRITE) ? 1 : 0, + (errval & SCHIZO_STCERR_READ) ? 1 : 0); + } + } + if (saw_error != 0) { + unsigned long tagval = stc_tag_buf[i]; + unsigned long lineval = stc_line_buf[i]; + printk("SCHIZO%d: PBM-%c STC_TAG(%d)[PA(%016lx)VA(%08lx)V(%d)R(%d)]\n", + p->index, pbm_name, + i, + ((tagval & SCHIZO_STCTAG_PPN) >> 19UL), + (tagval & SCHIZO_STCTAG_VPN), + ((tagval & SCHIZO_STCTAG_VALID) ? 1 : 0), + ((tagval & SCHIZO_STCTAG_READ) ? 1 : 0)); + + /* XXX Should spit out per-bank error information... -DaveM */ + printk("SCHIZO%d: PBM-%c STC_LINE(%d)[LIDX(%lx)SP(%lx)LADDR(%lx)EP(%lx)" + "V(%d)FOFN(%d)]\n", + p->index, pbm_name, + i, + ((lineval & SCHIZO_STCLINE_LINDX) >> 23UL), + ((lineval & SCHIZO_STCLINE_SPTR) >> 13UL), + ((lineval & SCHIZO_STCLINE_LADDR) >> 6UL), + ((lineval & SCHIZO_STCLINE_EPTR) >> 0UL), + ((lineval & SCHIZO_STCLINE_VALID) ? 1 : 0), + ((lineval & SCHIZO_STCLINE_FOFN) ? 1 : 0)); + } + } + + spin_unlock(&stc_buf_lock); +} + +/* IOMMU is per-PBM in Schizo, so interrogate both for anonymous + * controller level errors. + */ + +#define SCHIZO_IOMMU_TAG 0xa580UL +#define SCHIZO_IOMMU_DATA 0xa600UL + +#define SCHIZO_IOMMU_TAG_CTXT 0x0000001ffe000000UL +#define SCHIZO_IOMMU_TAG_ERRSTS 0x0000000001800000UL +#define SCHIZO_IOMMU_TAG_ERR 0x0000000000400000UL +#define SCHIZO_IOMMU_TAG_WRITE 0x0000000000200000UL +#define SCHIZO_IOMMU_TAG_STREAM 0x0000000000100000UL +#define SCHIZO_IOMMU_TAG_SIZE 0x0000000000080000UL +#define SCHIZO_IOMMU_TAG_VPAGE 0x000000000007ffffUL + +#define SCHIZO_IOMMU_DATA_VALID 0x0000000100000000UL +#define SCHIZO_IOMMU_DATA_CACHE 0x0000000040000000UL +#define SCHIZO_IOMMU_DATA_PPAGE 0x000000003fffffffUL + +static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, + enum schizo_error_type type) +{ + struct pci_controller_info *p = pbm->parent; + struct pci_iommu *iommu = pbm->iommu; + unsigned long iommu_tag[16]; + unsigned long iommu_data[16]; + unsigned long flags; + u64 control; + char pbm_name = (pbm == &p->pbm_A ? 'A' : 'B'); + int i; + + spin_lock_irqsave(&iommu->lock, flags); + control = schizo_read(iommu->iommu_control); + if (control & SCHIZO_IOMMU_CTRL_XLTEERR) { + unsigned long base; + char *type_string; + + /* Clear the error encountered bit. */ + control &= ~SCHIZO_IOMMU_CTRL_XLTEERR; + schizo_write(iommu->iommu_control, control); + + switch((control & SCHIZO_IOMMU_CTRL_XLTESTAT) >> 25UL) { + case 0: + type_string = "Protection Error"; + break; + case 1: + type_string = "Invalid Error"; + break; + case 2: + type_string = "TimeOut Error"; + break; + case 3: + default: + type_string = "ECC Error"; + break; + }; + printk("SCHIZO%d: PBM-%c IOMMU Error, type[%s]\n", + p->index, pbm_name, type_string); + + /* Put the IOMMU into diagnostic mode and probe + * it's TLB for entries with error status. + * + * It is very possible for another DVMA to occur + * while we do this probe, and corrupt the system + * further. But we are so screwed at this point + * that we are likely to crash hard anyways, so + * get as much diagnostic information to the + * console as we can. + */ + schizo_write(iommu->iommu_control, + control | SCHIZO_IOMMU_CTRL_DENAB); + + base = p->controller_regs; + if (pbm == &p->pbm_A) + base += SCHIZO_PBM_A_REGS_OFF; + else + base += SCHIZO_PBM_B_REGS_OFF; + + for (i = 0; i < 16; i++) { + iommu_tag[i] = + schizo_read(base + SCHIZO_IOMMU_TAG + (i * 8UL)); + iommu_data[i] = + schizo_read(base + SCHIZO_IOMMU_DATA + (i * 8UL)); + + /* Now clear out the entry. */ + schizo_write(base + SCHIZO_IOMMU_TAG + (i * 8UL), 0); + schizo_write(base + SCHIZO_IOMMU_DATA + (i * 8UL), 0); + } + + /* Leave diagnostic mode. */ + schizo_write(iommu->iommu_control, control); + + for (i = 0; i < 16; i++) { + unsigned long tag, data; + + tag = iommu_tag[i]; + if (!(tag & SCHIZO_IOMMU_TAG_ERR)) + continue; + + data = iommu_data[i]; + switch((tag & SCHIZO_IOMMU_TAG_ERRSTS) >> 23UL) { + case 0: + type_string = "Protection Error"; + break; + case 1: + type_string = "Invalid Error"; + break; + case 2: + type_string = "TimeOut Error"; + break; + case 3: + default: + type_string = "ECC Error"; + break; + }; + printk("SCHIZO%d: PBM-%c IOMMU TAG(%d)[error(%s) ctx(%x) wr(%d) str(%d) " + "sz(%dK) vpg(%08lx)]\n", + p->index, pbm_name, i, type_string, + (int)((tag & SCHIZO_IOMMU_TAG_CTXT) >> 25UL), + ((tag & SCHIZO_IOMMU_TAG_WRITE) ? 1 : 0), + ((tag & SCHIZO_IOMMU_TAG_STREAM) ? 1 : 0), + ((tag & SCHIZO_IOMMU_TAG_SIZE) ? 64 : 8), + (tag & SCHIZO_IOMMU_TAG_VPAGE) << PAGE_SHIFT); + printk("SCHIZO%d: PBM-%c IOMMU DATA(%d)[valid(%d) cache(%d) ppg(%016lx)]\n", + p->index, pbm_name, i, + ((data & SCHIZO_IOMMU_DATA_VALID) ? 1 : 0), + ((data & SCHIZO_IOMMU_DATA_CACHE) ? 1 : 0), + (data & SCHIZO_IOMMU_DATA_PPAGE) << PAGE_SHIFT); + } + } + __schizo_check_stc_error_pbm(pbm, type); + spin_unlock_irqrestore(&iommu->lock, flags); +} + +static void schizo_check_iommu_error(struct pci_controller_info *p, + enum schizo_error_type type) +{ + schizo_check_iommu_error_pbm(&p->pbm_A, type); + schizo_check_iommu_error_pbm(&p->pbm_B, type); +} + +/* Uncorrectable ECC error status gathering. */ +#define SCHIZO_UE_AFSR 0x10030UL +#define SCHIZO_UE_AFAR 0x10038UL + +#define SCHIZO_UEAFSR_PPIO 0x8000000000000000UL +#define SCHIZO_UEAFSR_PDRD 0x4000000000000000UL +#define SCHIZO_UEAFSR_PDWR 0x2000000000000000UL +#define SCHIZO_UEAFSR_SPIO 0x1000000000000000UL +#define SCHIZO_UEAFSR_SDMA 0x0800000000000000UL +#define SCHIZO_UEAFSR_ERRPNDG 0x0300000000000000UL +#define SCHIZO_UEAFSR_BMSK 0x000003ff00000000UL +#define SCHIZO_UEAFSR_QOFF 0x00000000c0000000UL +#define SCHIZO_UEAFSR_AID 0x000000001f000000UL +#define SCHIZO_UEAFSR_PARTIAL 0x0000000000800000UL +#define SCHIZO_UEAFSR_OWNEDIN 0x0000000000400000UL +#define SCHIZO_UEAFSR_MTAGSYND 0x00000000000f0000UL +#define SCHIZO_UEAFSR_MTAG 0x000000000000e000UL +#define SCHIZO_UEAFSR_ECCSYND 0x00000000000001ffUL + +static void schizo_ue_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pci_controller_info *p = dev_id; + unsigned long afsr_reg = p->controller_regs + SCHIZO_UE_AFSR; + unsigned long afar_reg = p->controller_regs + SCHIZO_UE_AFAR; + unsigned long afsr, afar, error_bits; + int reported, limit; + + /* Latch uncorrectable error status. */ + afar = schizo_read(afar_reg); + + /* If either of the error pending bits are set in the + * AFSR, the error status is being actively updated by + * the hardware and we must re-read to get a clean value. + */ + limit = 1000; + do { + afsr = schizo_read(afsr_reg); + } while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit); + + /* Clear the primary/secondary error status bits. */ + error_bits = afsr & + (SCHIZO_UEAFSR_PPIO | SCHIZO_UEAFSR_PDRD | SCHIZO_UEAFSR_PDWR | + SCHIZO_UEAFSR_SPIO | SCHIZO_UEAFSR_SDMA); + schizo_write(afsr_reg, error_bits); + + /* Log the error. */ + printk("SCHIZO%d: Uncorrectable Error, primary error type[%s]\n", + p->index, + (((error_bits & SCHIZO_UEAFSR_PPIO) ? + "PIO" : + ((error_bits & SCHIZO_UEAFSR_PDRD) ? + "DMA Read" : + ((error_bits & SCHIZO_UEAFSR_PDWR) ? + "DMA Write" : "???"))))); + printk("SCHIZO%d: bytemask[%04lx] qword_offset[%lx] SAFARI_AID[%02lx]\n", + p->index, + (afsr & SCHIZO_UEAFSR_BMSK) >> 32UL, + (afsr & SCHIZO_UEAFSR_QOFF) >> 30UL, + (afsr & SCHIZO_UEAFSR_AID) >> 24UL); + printk("SCHIZO%d: partial[%d] owned_in[%d] mtag[%lx] mtag_synd[%lx] ecc_sync[%lx]\n", + p->index, + (afsr & SCHIZO_UEAFSR_PARTIAL) ? 1 : 0, + (afsr & SCHIZO_UEAFSR_OWNEDIN) ? 1 : 0, + (afsr & SCHIZO_UEAFSR_MTAG) >> 13UL, + (afsr & SCHIZO_UEAFSR_MTAGSYND) >> 16UL, + (afsr & SCHIZO_UEAFSR_ECCSYND) >> 0UL); + printk("SCHIZO%d: UE AFAR [%016lx]\n", p->index, afar); + printk("SCHIZO%d: UE Secondary errors [", p->index); + reported = 0; + if (afsr & SCHIZO_UEAFSR_SPIO) { + reported++; + printk("(PIO)"); + } + if (afsr & SCHIZO_UEAFSR_SDMA) { + reported++; + printk("(DMA)"); + } + if (!reported) + printk("(none)"); + printk("]\n"); + + /* Interrogate IOMMU for error status. */ + schizo_check_iommu_error(p, UE_ERR); + + schizo_clear_other_err_intr(irq); +} + +#define SCHIZO_CE_AFSR 0x10040UL +#define SCHIZO_CE_AFAR 0x10048UL + +#define SCHIZO_CEAFSR_PPIO 0x8000000000000000UL +#define SCHIZO_CEAFSR_PDRD 0x4000000000000000UL +#define SCHIZO_CEAFSR_PDWR 0x2000000000000000UL +#define SCHIZO_CEAFSR_SPIO 0x1000000000000000UL +#define SCHIZO_CEAFSR_SDMA 0x0800000000000000UL +#define SCHIZO_CEAFSR_ERRPNDG 0x0300000000000000UL +#define SCHIZO_CEAFSR_BMSK 0x000003ff00000000UL +#define SCHIZO_CEAFSR_QOFF 0x00000000c0000000UL +#define SCHIZO_CEAFSR_AID 0x000000001f000000UL +#define SCHIZO_CEAFSR_PARTIAL 0x0000000000800000UL +#define SCHIZO_CEAFSR_OWNEDIN 0x0000000000400000UL +#define SCHIZO_CEAFSR_MTAGSYND 0x00000000000f0000UL +#define SCHIZO_CEAFSR_MTAG 0x000000000000e000UL +#define SCHIZO_CEAFSR_ECCSYND 0x00000000000001ffUL + +static void schizo_ce_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pci_controller_info *p = dev_id; + unsigned long afsr_reg = p->controller_regs + SCHIZO_CE_AFSR; + unsigned long afar_reg = p->controller_regs + SCHIZO_CE_AFAR; + unsigned long afsr, afar, error_bits; + int reported, limit; + + /* Latch error status. */ + afar = schizo_read(afar_reg); + + /* If either of the error pending bits are set in the + * AFSR, the error status is being actively updated by + * the hardware and we must re-read to get a clean value. + */ + limit = 1000; + do { + afsr = schizo_read(afsr_reg); + } while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit); + + /* Clear primary/secondary error status bits. */ + error_bits = afsr & + (SCHIZO_CEAFSR_PPIO | SCHIZO_CEAFSR_PDRD | SCHIZO_CEAFSR_PDWR | + SCHIZO_CEAFSR_SPIO | SCHIZO_CEAFSR_SDMA); + schizo_write(afsr_reg, error_bits); + + /* Log the error. */ + printk("SCHIZO%d: Correctable Error, primary error type[%s]\n", + p->index, + (((error_bits & SCHIZO_CEAFSR_PPIO) ? + "PIO" : + ((error_bits & SCHIZO_CEAFSR_PDRD) ? + "DMA Read" : + ((error_bits & SCHIZO_CEAFSR_PDWR) ? + "DMA Write" : "???"))))); + + /* XXX Use syndrome and afar to print out module string just like + * XXX UDB CE trap handler does... -DaveM + */ + printk("SCHIZO%d: bytemask[%04lx] qword_offset[%lx] SAFARI_AID[%02lx]\n", + p->index, + (afsr & SCHIZO_UEAFSR_BMSK) >> 32UL, + (afsr & SCHIZO_UEAFSR_QOFF) >> 30UL, + (afsr & SCHIZO_UEAFSR_AID) >> 24UL); + printk("SCHIZO%d: partial[%d] owned_in[%d] mtag[%lx] mtag_synd[%lx] ecc_sync[%lx]\n", + p->index, + (afsr & SCHIZO_UEAFSR_PARTIAL) ? 1 : 0, + (afsr & SCHIZO_UEAFSR_OWNEDIN) ? 1 : 0, + (afsr & SCHIZO_UEAFSR_MTAG) >> 13UL, + (afsr & SCHIZO_UEAFSR_MTAGSYND) >> 16UL, + (afsr & SCHIZO_UEAFSR_ECCSYND) >> 0UL); + printk("SCHIZO%d: CE AFAR [%016lx]\n", p->index, afar); + printk("SCHIZO%d: CE Secondary errors [", p->index); + reported = 0; + if (afsr & SCHIZO_CEAFSR_SPIO) { + reported++; + printk("(PIO)"); + } + if (afsr & SCHIZO_CEAFSR_SDMA) { + reported++; + printk("(DMA)"); + } + if (!reported) + printk("(none)"); + printk("]\n"); + + schizo_clear_other_err_intr(irq); +} + +#define SCHIZO_PCI_AFSR 0x2010UL +#define SCHIZO_PCI_AFAR 0x2018UL + +#define SCHIZO_PCIAFSR_PMA 0x8000000000000000UL +#define SCHIZO_PCIAFSR_PTA 0x4000000000000000UL +#define SCHIZO_PCIAFSR_PRTRY 0x2000000000000000UL +#define SCHIZO_PCIAFSR_PPERR 0x1000000000000000UL +#define SCHIZO_PCIAFSR_PTTO 0x0800000000000000UL +#define SCHIZO_PCIAFSR_PUNUS 0x0400000000000000UL +#define SCHIZO_PCIAFSR_SMA 0x0200000000000000UL +#define SCHIZO_PCIAFSR_STA 0x0100000000000000UL +#define SCHIZO_PCIAFSR_SRTRY 0x0080000000000000UL +#define SCHIZO_PCIAFSR_SPERR 0x0040000000000000UL +#define SCHIZO_PCIAFSR_STTO 0x0020000000000000UL +#define SCHIZO_PCIAFSR_SUNUS 0x0010000000000000UL +#define SCHIZO_PCIAFSR_BMSK 0x000003ff00000000UL +#define SCHIZO_PCIAFSR_BLK 0x0000000080000000UL +#define SCHIZO_PCIAFSR_CFG 0x0000000040000000UL +#define SCHIZO_PCIAFSR_MEM 0x0000000020000000UL +#define SCHIZO_PCIAFSR_IO 0x0000000010000000UL + +static void schizo_pcierr_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pci_pbm_info *pbm = dev_id; + struct pci_controller_info *p = pbm->parent; + unsigned long afsr_reg, afar_reg, base; + unsigned long afsr, afar, error_bits; + int reported; + char pbm_name; + + base = p->controller_regs; + if (pbm == &pbm->parent->pbm_A) { + base += SCHIZO_PBM_A_REGS_OFF; + pbm_name = 'A'; + } else { + base += SCHIZO_PBM_B_REGS_OFF; + pbm_name = 'B'; + } + + afsr_reg = base + SCHIZO_PCI_AFSR; + afar_reg = base + SCHIZO_PCI_AFAR; + + /* Latch error status. */ + afar = schizo_read(afar_reg); + afsr = schizo_read(afsr_reg); + + /* Clear primary/secondary error status bits. */ + error_bits = afsr & + (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | + SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | + SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS | + SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA | + SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | + SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS); + schizo_write(afsr_reg, error_bits); + + /* Log the error. */ + printk("SCHIZO%d: PBM-%c PCI Error, primary error type[%s]\n", + p->index, pbm_name, + (((error_bits & SCHIZO_PCIAFSR_PMA) ? + "Master Abort" : + ((error_bits & SCHIZO_PCIAFSR_PTA) ? + "Target Abort" : + ((error_bits & SCHIZO_PCIAFSR_PRTRY) ? + "Excessive Retries" : + ((error_bits & SCHIZO_PCIAFSR_PPERR) ? + "Parity Error" : + ((error_bits & SCHIZO_PCIAFSR_PTTO) ? + "Timeout" : + ((error_bits & SCHIZO_PCIAFSR_PUNUS) ? + "Bus Unusable" : "???")))))))); + printk("SCHIZO%d: PBM-%c bytemask[%04lx] was_block(%d) space(%s)\n", + p->index, pbm_name, + (afsr & SCHIZO_PCIAFSR_BMSK) >> 32UL, + (afsr & SCHIZO_PCIAFSR_BLK) ? 1 : 0, + ((afsr & SCHIZO_PCIAFSR_CFG) ? + "Config" : + ((afsr & SCHIZO_PCIAFSR_MEM) ? + "Memory" : + ((afsr & SCHIZO_PCIAFSR_IO) ? + "I/O" : "???")))); + printk("SCHIZO%d: PBM-%c PCI AFAR [%016lx]\n", + p->index, pbm_name, afar); + printk("SCHIZO%d: PBM-%c PCI Secondary errors [", + p->index, pbm_name); + reported = 0; + if (afsr & SCHIZO_PCIAFSR_SMA) { + reported++; + printk("(Master Abort)"); + } + if (afsr & SCHIZO_PCIAFSR_STA) { + reported++; + printk("(Target Abort)"); + } + if (afsr & SCHIZO_PCIAFSR_SRTRY) { + reported++; + printk("(Excessive Retries)"); + } + if (afsr & SCHIZO_PCIAFSR_SPERR) { + reported++; + printk("(Parity Error)"); + } + if (afsr & SCHIZO_PCIAFSR_STTO) { + reported++; + printk("(Timeout)"); + } + if (afsr & SCHIZO_PCIAFSR_SUNUS) { + reported++; + printk("(Bus Unusable)"); + } + if (!reported) + printk("(none)"); + printk("]\n"); + + /* For the error types shown, scan PBM's PCI bus for devices + * which have logged that error type. + */ + + /* If we see a Target Abort, this could be the result of an + * IOMMU translation error of some sort. It is extremely + * useful to log this information as usually it indicates + * a bug in the IOMMU support code or a PCI device driver. + */ + if (error_bits & (SCHIZO_PCIAFSR_PTA | SCHIZO_PCIAFSR_STA)) { + schizo_check_iommu_error(p, PCI_ERR); + pci_scan_for_target_abort(p, pbm, pbm->pci_bus); + } + if (error_bits & (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_SMA)) + pci_scan_for_master_abort(p, pbm, pbm->pci_bus); + + /* For excessive retries, PSYCHO/PBM will abort the device + * and there is no way to specifically check for excessive + * retries in the config space status registers. So what + * we hope is that we'll catch it via the master/target + * abort events. + */ + + if (error_bits & (SCHIZO_PCIAFSR_PPERR | SCHIZO_PCIAFSR_SPERR)) + pci_scan_for_parity_error(p, pbm, pbm->pci_bus); + + schizo_clear_other_err_intr(irq); +} + +#define SCHIZO_SAFARI_ERRLOG 0x10018UL + +#define SAFARI_ERRLOG_ERROUT 0x8000000000000000UL + +#define SAFARI_ERROR_BADCMD 0x4000000000000000UL +#define SAFARI_ERROR_SSMDIS 0x2000000000000000UL +#define SAFARI_ERROR_BADMA 0x1000000000000000UL +#define SAFARI_ERROR_BADMB 0x0800000000000000UL +#define SAFARI_ERROR_BADMC 0x0400000000000000UL +#define SAFARI_ERROR_CPU1PS 0x0000000000002000UL +#define SAFARI_ERROR_CPU1PB 0x0000000000001000UL +#define SAFARI_ERROR_CPU0PS 0x0000000000000800UL +#define SAFARI_ERROR_CPU0PB 0x0000000000000400UL +#define SAFARI_ERROR_CIQTO 0x0000000000000200UL +#define SAFARI_ERROR_LPQTO 0x0000000000000100UL +#define SAFARI_ERROR_SFPQTO 0x0000000000000080UL +#define SAFARI_ERROR_UFPQTO 0x0000000000000040UL +#define SAFARI_ERROR_APERR 0x0000000000000020UL +#define SAFARI_ERROR_UNMAP 0x0000000000000010UL +#define SAFARI_ERROR_BUSERR 0x0000000000000004UL +#define SAFARI_ERROR_TIMEOUT 0x0000000000000002UL +#define SAFARI_ERROR_ILL 0x0000000000000001UL + +/* We only expect UNMAP errors here. The rest of the Safari errors + * are marked fatal and thus cause a system reset. + */ +static void schizo_safarierr_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pci_controller_info *p = dev_id; + u64 errlog; + + errlog = schizo_read(p->controller_regs + SCHIZO_SAFARI_ERRLOG); + schizo_write(p->controller_regs + SCHIZO_SAFARI_ERRLOG, + errlog & ~(SAFARI_ERRLOG_ERROUT)); + + if (!(errlog & SAFARI_ERROR_UNMAP)) { + printk("SCHIZO%d: Unexpected Safari error interrupt, errlog[%016lx]\n", + p->index, errlog); + + schizo_clear_other_err_intr(irq); + return; + } + + printk("SCHIZO%d: Safari interrupt, UNMAPPED error, interrogating IOMMUs.\n", + p->index); + schizo_check_iommu_error(p, SAFARI_ERR); + + schizo_clear_other_err_intr(irq); +} + +/* Nearly identical to PSYCHO equivalents... */ +#define SCHIZO_ECC_CTRL 0x10020UL +#define SCHIZO_ECCCTRL_EE 0x8000000000000000 /* Enable ECC Checking */ +#define SCHIZO_ECCCTRL_UE 0x4000000000000000 /* Enable UE Interrupts */ +#define SCHIZO_ECCCTRL_CE 0x2000000000000000 /* Enable CE INterrupts */ + +#define SCHIZO_SAFARI_ERRCTRL 0x10008UL +#define SCHIZO_SAFERRCTRL_EN 0x8000000000000000UL +#define SCHIZO_SAFARI_IRQCTRL 0x10010UL +#define SCHIZO_SAFIRQCTRL_EN 0x8000000000000000UL + +#define SCHIZO_UE_INO 0x30 /* Uncorrectable ECC error */ +#define SCHIZO_CE_INO 0x31 /* Correctable ECC error */ +#define SCHIZO_PCIERR_A_INO 0x32 /* PBM A PCI bus error */ +#define SCHIZO_PCIERR_B_INO 0x33 /* PBM B PCI bus error */ +#define SCHIZO_SERR_INO 0x34 /* Safari interface error */ + +#define SCHIZO_PCIA_CTRL (SCHIZO_PBM_A_REGS_OFF + 0x2000UL) +#define SCHIZO_PCIB_CTRL (SCHIZO_PBM_B_REGS_OFF + 0x2000UL) +#define SCHIZO_PCICTRL_BUNUS (1UL << 63UL) +#define SCHIZO_PCICTRL_ESLCK (1UL << 51UL) +#define SCHIZO_PCICTRL_TTO_ERR (1UL << 38UL) +#define SCHIZO_PCICTRL_RTRY_ERR (1UL << 37UL) +#define SCHIZO_PCICTRL_DTO_ERR (1UL << 36UL) +#define SCHIZO_PCICTRL_SBH_ERR (1UL << 35UL) +#define SCHIZO_PCICTRL_SERR (1UL << 34UL) +#define SCHIZO_PCICTRL_SBH_INT (1UL << 18UL) +#define SCHIZO_PCICTRL_EEN (1UL << 17UL) + +static void __init schizo_register_error_handlers(struct pci_controller_info *p) +{ + struct pci_pbm_info *pbm_a = &p->pbm_A; + struct pci_pbm_info *pbm_b = &p->pbm_B; + unsigned long base = p->controller_regs; + unsigned int irq, portid = p->portid; + struct ino_bucket *bucket; + u64 tmp; + + /* Build IRQs and register handlers. */ + irq = schizo_irq_build(pbm_a, NULL, (portid << 6) | SCHIZO_UE_INO); + if (request_irq(irq, schizo_ue_intr, + SA_SHIRQ, "SCHIZO UE", p) < 0) { + prom_printf("SCHIZO%d: Cannot register UE interrupt.\n", + p->index); + prom_halt(); + } + bucket = __bucket(irq); + tmp = readl(bucket->imap); + upa_writel(tmp, (base + SCHIZO_PBM_B_REGS_OFF + schizo_imap_offset(SCHIZO_UE_INO) + 4)); + + irq = schizo_irq_build(pbm_a, NULL, (portid << 6) | SCHIZO_CE_INO); + if (request_irq(irq, schizo_ce_intr, + SA_SHIRQ, "SCHIZO CE", p) < 0) { + prom_printf("SCHIZO%d: Cannot register CE interrupt.\n", + p->index); + prom_halt(); + } + bucket = __bucket(irq); + tmp = upa_readl(bucket->imap); + upa_writel(tmp, (base + SCHIZO_PBM_B_REGS_OFF + schizo_imap_offset(SCHIZO_CE_INO) + 4)); + + irq = schizo_irq_build(pbm_a, NULL, (portid << 6) | SCHIZO_PCIERR_A_INO); + if (request_irq(irq, schizo_pcierr_intr, + SA_SHIRQ, "SCHIZO PCIERR", pbm_a) < 0) { + prom_printf("SCHIZO%d(PBMA): Cannot register PciERR interrupt.\n", + p->index); + prom_halt(); + } + bucket = __bucket(irq); + tmp = upa_readl(bucket->imap); + upa_writel(tmp, (base + SCHIZO_PBM_B_REGS_OFF + schizo_imap_offset(SCHIZO_PCIERR_A_INO) + 4)); + + irq = schizo_irq_build(pbm_a, NULL, (portid << 6) | SCHIZO_PCIERR_B_INO); + if (request_irq(irq, schizo_pcierr_intr, + SA_SHIRQ, "SCHIZO PCIERR", pbm_b) < 0) { + prom_printf("SCHIZO%d(PBMB): Cannot register PciERR interrupt.\n", + p->index); + prom_halt(); + } + bucket = __bucket(irq); + tmp = upa_readl(bucket->imap); + upa_writel(tmp, (base + SCHIZO_PBM_B_REGS_OFF + schizo_imap_offset(SCHIZO_PCIERR_B_INO) + 4)); + + irq = schizo_irq_build(pbm_a, NULL, (portid << 6) | SCHIZO_SERR_INO); + if (request_irq(irq, schizo_safarierr_intr, + SA_SHIRQ, "SCHIZO SERR", p) < 0) { + prom_printf("SCHIZO%d(PBMB): Cannot register SafariERR interrupt.\n", + p->index); + prom_halt(); + } + bucket = __bucket(irq); + tmp = upa_readl(bucket->imap); + upa_writel(tmp, (base + SCHIZO_PBM_B_REGS_OFF + schizo_imap_offset(SCHIZO_SERR_INO) + 4)); + + /* Enable UE and CE interrupts for controller. */ + schizo_write(base + SCHIZO_ECC_CTRL, + (SCHIZO_ECCCTRL_EE | + SCHIZO_ECCCTRL_UE | + SCHIZO_ECCCTRL_CE)); + + /* Enable PCI Error interrupts and clear error + * bits for each PBM. + */ + tmp = schizo_read(base + SCHIZO_PCIA_CTRL); + tmp |= (SCHIZO_PCICTRL_BUNUS | + SCHIZO_PCICTRL_ESLCK | + SCHIZO_PCICTRL_TTO_ERR | + SCHIZO_PCICTRL_RTRY_ERR | + SCHIZO_PCICTRL_DTO_ERR | + SCHIZO_PCICTRL_SBH_ERR | + SCHIZO_PCICTRL_SERR | + SCHIZO_PCICTRL_SBH_INT | + SCHIZO_PCICTRL_EEN); + schizo_write(base + SCHIZO_PCIA_CTRL, tmp); + + tmp = schizo_read(base + SCHIZO_PCIB_CTRL); + tmp |= (SCHIZO_PCICTRL_BUNUS | + SCHIZO_PCICTRL_ESLCK | + SCHIZO_PCICTRL_TTO_ERR | + SCHIZO_PCICTRL_RTRY_ERR | + SCHIZO_PCICTRL_DTO_ERR | + SCHIZO_PCICTRL_SBH_ERR | + SCHIZO_PCICTRL_SERR | + SCHIZO_PCICTRL_SBH_INT | + SCHIZO_PCICTRL_EEN); + schizo_write(base + SCHIZO_PCIB_CTRL, tmp); + + schizo_write(base + SCHIZO_PBM_A_REGS_OFF + SCHIZO_PCI_AFSR, + (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | + SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | + SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS | + SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA | + SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | + SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS)); + schizo_write(base + SCHIZO_PBM_B_REGS_OFF + SCHIZO_PCI_AFSR, + (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | + SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | + SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS | + SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA | + SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | + SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS)); + + /* Make all Safari error conditions fatal except unmapped errors + * which we make generate interrupts. + */ + schizo_write(base + SCHIZO_SAFARI_ERRCTRL, + (SCHIZO_SAFERRCTRL_EN | + (SAFARI_ERROR_BADCMD | SAFARI_ERROR_SSMDIS | + SAFARI_ERROR_BADMA | SAFARI_ERROR_BADMB | + SAFARI_ERROR_BADMC | SAFARI_ERROR_CPU1PS | + SAFARI_ERROR_CPU1PB | SAFARI_ERROR_CPU0PS | + SAFARI_ERROR_CPU0PB | SAFARI_ERROR_CIQTO | + SAFARI_ERROR_LPQTO | SAFARI_ERROR_SFPQTO | + SAFARI_ERROR_UFPQTO | SAFARI_ERROR_APERR | + SAFARI_ERROR_BUSERR | SAFARI_ERROR_TIMEOUT | + SAFARI_ERROR_ILL))); + + schizo_write(base + SCHIZO_SAFARI_IRQCTRL, + (SCHIZO_SAFIRQCTRL_EN | (SAFARI_ERROR_UNMAP))); +} + +/* We have to do the config space accesses by hand, thus... */ +#define PBM_BRIDGE_BUS 0x40 +#define PBM_BRIDGE_SUBORDINATE 0x41 +static void __init pbm_renumber(struct pci_pbm_info *pbm, u8 orig_busno) +{ + u8 *addr, busno; + int nbus; + + busno = pci_highest_busnum; + nbus = pbm->pci_last_busno - pbm->pci_first_busno; + + addr = schizo_pci_config_mkaddr(pbm, orig_busno, + 0, PBM_BRIDGE_BUS); + pci_config_write8(addr, busno); + addr = schizo_pci_config_mkaddr(pbm, busno, + 0, PBM_BRIDGE_SUBORDINATE); + pci_config_write8(addr, busno + nbus); + + pbm->pci_first_busno = busno; + pbm->pci_last_busno = busno + nbus; + pci_highest_busnum = busno + nbus + 1; + + do { + pci_bus2pbm[busno++] = pbm; + } while (nbus--); +} + +/* We have to do the config space accesses by hand here since + * the pci_bus2pbm array is not ready yet. + */ +static void __init pbm_pci_bridge_renumber(struct pci_pbm_info *pbm, + u8 busno) +{ + u32 devfn, l, class; + u8 hdr_type; + int is_multi = 0; + + for(devfn = 0; devfn < 0xff; ++devfn) { + u32 *dwaddr; + u8 *baddr; + + if (PCI_FUNC(devfn) != 0 && is_multi == 0) + continue; + + /* Anything there? */ + dwaddr = schizo_pci_config_mkaddr(pbm, busno, devfn, PCI_VENDOR_ID); + l = 0xffffffff; + pci_config_read32(dwaddr, &l); + if (l == 0xffffffff || l == 0x00000000 || + l == 0x0000ffff || l == 0xffff0000) { + is_multi = 0; + continue; + } + + baddr = schizo_pci_config_mkaddr(pbm, busno, devfn, PCI_HEADER_TYPE); + pci_config_read8(baddr, &hdr_type); + if (PCI_FUNC(devfn) == 0) + is_multi = hdr_type & 0x80; + + dwaddr = schizo_pci_config_mkaddr(pbm, busno, devfn, PCI_CLASS_REVISION); + class = 0xffffffff; + pci_config_read32(dwaddr, &class); + if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) { + u32 buses = 0xffffffff; + + dwaddr = schizo_pci_config_mkaddr(pbm, busno, devfn, + PCI_PRIMARY_BUS); + pci_config_read32(dwaddr, &buses); + pbm_pci_bridge_renumber(pbm, (buses >> 8) & 0xff); + buses &= 0xff000000; + pci_config_write32(dwaddr, buses); + } + } +} + +static void __init pbm_bridge_reconfigure(struct pci_controller_info *p) +{ + struct pci_pbm_info *pbm; + u8 *addr; + + /* Clear out primary/secondary/subordinate bus numbers on + * all PCI-to-PCI bridges under each PBM. The generic bus + * probing will fix them up. + */ + pbm_pci_bridge_renumber(&p->pbm_B, p->pbm_B.pci_first_busno); + pbm_pci_bridge_renumber(&p->pbm_A, p->pbm_A.pci_first_busno); + + /* Move PBM A out of the way. */ + pbm = &p->pbm_A; + addr = schizo_pci_config_mkaddr(pbm, pbm->pci_first_busno, + 0, PBM_BRIDGE_BUS); + pci_config_write8(addr, 0xff); + addr = schizo_pci_config_mkaddr(pbm, 0xff, + 0, PBM_BRIDGE_SUBORDINATE); + pci_config_write8(addr, 0xff); + + /* Now we can safely renumber both PBMs. */ + pbm_renumber(&p->pbm_B, p->pbm_B.pci_first_busno); + pbm_renumber(&p->pbm_A, 0xff); +} + +static void __init pbm_config_busmastering(struct pci_pbm_info *pbm) +{ + u8 *addr; + + /* Set cache-line size to 64 bytes, this is actually + * a nop but I do it for completeness. + */ + addr = schizo_pci_config_mkaddr(pbm, pbm->pci_first_busno, + 0, PCI_CACHE_LINE_SIZE); + pci_config_write8(addr, 64 / sizeof(u32)); + + /* Set PBM latency timer to 64 PCI clocks. */ + addr = schizo_pci_config_mkaddr(pbm, pbm->pci_first_busno, + 0, PCI_LATENCY_TIMER); + pci_config_write8(addr, 64); +} + +static void __init pbm_scan_bus(struct pci_controller_info *p, + struct pci_pbm_info *pbm) +{ + pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, + p->pci_ops, + pbm); + pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node); + pci_record_assignments(pbm, pbm->pci_bus); + pci_assign_unassigned(pbm, pbm->pci_bus); + pci_fixup_irq(pbm, pbm->pci_bus); + pci_determine_66mhz_disposition(pbm, pbm->pci_bus); + pci_setup_busmastering(pbm, pbm->pci_bus); +} + +static void __init schizo_scan_bus(struct pci_controller_info *p) +{ + pbm_bridge_reconfigure(p); + pbm_config_busmastering(&p->pbm_B); + p->pbm_B.is_66mhz_capable = 0; + pbm_config_busmastering(&p->pbm_A); + p->pbm_A.is_66mhz_capable = 1; + pbm_scan_bus(p, &p->pbm_B); + pbm_scan_bus(p, &p->pbm_A); + + /* After the PCI bus scan is complete, we can register + * the error interrupt handlers. + */ + schizo_register_error_handlers(p); } static void __init schizo_base_address_update(struct pci_dev *pdev, int resource) { - /* IMPLEMENT ME */ + struct pcidev_cookie *pcp = pdev->sysdata; + struct pci_pbm_info *pbm = pcp->pbm; + struct resource *res, *root; + u32 reg; + int where, size, is_64bit; + + res = &pdev->resource[resource]; + where = PCI_BASE_ADDRESS_0 + (resource * 4); + + is_64bit = 0; + if (res->flags & IORESOURCE_IO) + root = &pbm->io_space; + else { + root = &pbm->mem_space; + if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) + == PCI_BASE_ADDRESS_MEM_TYPE_64) + is_64bit = 1; + } + + size = res->end - res->start; + pci_read_config_dword(pdev, where, ®); + reg = ((reg & size) | + (((u32)(res->start - root->start)) & ~size)); + pci_write_config_dword(pdev, where, reg); + + /* This knows that the upper 32-bits of the address + * must be zero. Our PCI common layer enforces this. + */ + if (is_64bit) + pci_write_config_dword(pdev, where + 4, 0); } static void __init schizo_resource_adjust(struct pci_dev *pdev, struct resource *res, struct resource *root) { - /* IMPLEMENT ME */ + res->start += root->start; + res->end += root->start; +} + +/* Interrogate Safari match/mask registers to figure out where + * PCI MEM, I/O, and Config space are for this PCI bus module. + */ + +#define SCHIZO_PCI_A_MEM_MATCH 0x00040UL +#define SCHIZO_PCI_A_MEM_MASK 0x00048UL +#define SCHIZO_PCI_A_IO_MATCH 0x00050UL +#define SCHIZO_PCI_A_IO_MASK 0x00058UL +#define SCHIZO_PCI_B_MEM_MATCH 0x00060UL +#define SCHIZO_PCI_B_MEM_MASK 0x00068UL +#define SCHIZO_PCI_B_IO_MATCH 0x00070UL +#define SCHIZO_PCI_B_IO_MASK 0x00078UL + +/* VAL must be non-zero. */ +static unsigned long strip_to_lowest_bit_set(unsigned long val) +{ + unsigned long tmp; + + tmp = 1UL; + while (!(tmp & val)) + tmp <<= 1UL; + + return tmp; +} + +static void schizo_determine_mem_io_space(struct pci_pbm_info *pbm, + int is_pbm_a, unsigned long reg_base) +{ + u64 mem_match, mem_mask; + u64 io_match; + u64 long a, b; + + if (is_pbm_a) { + mem_match = reg_base + SCHIZO_PCI_A_MEM_MATCH; + io_match = reg_base + SCHIZO_PCI_A_IO_MATCH; + } else { + mem_match = reg_base + SCHIZO_PCI_B_MEM_MATCH; + io_match = reg_base + SCHIZO_PCI_B_IO_MATCH; + } + mem_mask = mem_match + 0x8UL; + + a = schizo_read(mem_match) & ~0x8000000000000000UL; + b = strip_to_lowest_bit_set(schizo_read(mem_mask)); + + /* It should be 2GB in size. */ + pbm->mem_space.start = a; + pbm->mem_space.end = a + (b - 1UL); + pbm->mem_space.flags = IORESOURCE_MEM; + + /* This 32MB area is divided into two pieces. The first + * 16MB is Config space, the next 16MB is I/O space. + */ + + a = schizo_read(io_match) & ~0x8000000000000000UL; + pbm->config_space = a; + printk("SCHIZO PBM%c: Local PCI config space at %016lx\n", + (is_pbm_a ? 'A' : 'B'), pbm->config_space); + + a += (16UL * 1024UL * 1024UL); + pbm->io_space.start = a; + pbm->io_space.end = a + ((16UL * 1024UL * 1024UL) - 1UL); + pbm->io_space.flags = IORESOURCE_IO; +} + +static void __init pbm_register_toplevel_resources(struct pci_controller_info *p, + struct pci_pbm_info *pbm) +{ + char *name = pbm->name; + + sprintf(name, "SCHIZO%d PBM%c", + p->index, + (pbm == &p->pbm_A ? 'A' : 'B')); + pbm->io_space.name = pbm->mem_space.name = name; + + request_resource(&ioport_resource, &pbm->io_space); + request_resource(&iomem_resource, &pbm->mem_space); +} + +#define SCHIZO_STRBUF_CONTROL_A (SCHIZO_PBM_A_REGS_OFF + 0x02800UL) +#define SCHIZO_STRBUF_FLUSH_A (SCHIZO_PBM_A_REGS_OFF + 0x02808UL) +#define SCHIZO_STRBUF_FSYNC_A (SCHIZO_PBM_A_REGS_OFF + 0x02810UL) +#define SCHIZO_STRBUF_CTXFLUSH_A (SCHIZO_PBM_A_REGS_OFF + 0x02818UL) +#define SCHIZO_STRBUF_CTXMATCH_A (SCHIZO_PBM_A_REGS_OFF + 0x10000UL) + +#define SCHIZO_STRBUF_CONTROL_B (SCHIZO_PBM_B_REGS_OFF + 0x02800UL) +#define SCHIZO_STRBUF_FLUSH_B (SCHIZO_PBM_B_REGS_OFF + 0x02808UL) +#define SCHIZO_STRBUF_FSYNC_B (SCHIZO_PBM_B_REGS_OFF + 0x02810UL) +#define SCHIZO_STRBUF_CTXFLUSH_B (SCHIZO_PBM_B_REGS_OFF + 0x02818UL) +#define SCHIZO_STRBUF_CTXMATCH_B (SCHIZO_PBM_B_REGS_OFF + 0x10000UL) + +static void schizo_pbm_strbuf_init(struct pci_controller_info *p, + struct pci_pbm_info *pbm, + int is_pbm_a) +{ + unsigned long base = p->controller_regs; + u64 control; + + /* SCHIZO has context flushing. */ + if (is_pbm_a) { + pbm->stc.strbuf_control = base + SCHIZO_STRBUF_CONTROL_A; + pbm->stc.strbuf_pflush = base + SCHIZO_STRBUF_FLUSH_A; + pbm->stc.strbuf_fsync = base + SCHIZO_STRBUF_FSYNC_A; + pbm->stc.strbuf_ctxflush = base + SCHIZO_STRBUF_CTXFLUSH_A; + pbm->stc.strbuf_ctxmatch_base = base + SCHIZO_STRBUF_CTXMATCH_A; + } else { + pbm->stc.strbuf_control = base + SCHIZO_STRBUF_CONTROL_B; + pbm->stc.strbuf_pflush = base + SCHIZO_STRBUF_FLUSH_B; + pbm->stc.strbuf_fsync = base + SCHIZO_STRBUF_FSYNC_B; + pbm->stc.strbuf_ctxflush = base + SCHIZO_STRBUF_CTXFLUSH_B; + pbm->stc.strbuf_ctxmatch_base = base + SCHIZO_STRBUF_CTXMATCH_B; + } + + pbm->stc.strbuf_flushflag = (volatile unsigned long *) + ((((unsigned long)&pbm->stc.__flushflag_buf[0]) + + 63UL) + & ~63UL); + pbm->stc.strbuf_flushflag_pa = (unsigned long) + __pa(pbm->stc.strbuf_flushflag); + + /* Turn off LRU locking and diag mode, enable the + * streaming buffer and leave the rerun-disable + * setting however OBP set it. + */ + control = schizo_read(pbm->stc.strbuf_control); + control &= ~(SCHIZO_STRBUF_CTRL_LPTR | + SCHIZO_STRBUF_CTRL_LENAB | + SCHIZO_STRBUF_CTRL_DENAB); + control |= SCHIZO_STRBUF_CTRL_ENAB; + schizo_write(pbm->stc.strbuf_control, control); + + pbm->stc.strbuf_enabled = 1; +} + +#define SCHIZO_IOMMU_CONTROL_A (SCHIZO_PBM_A_REGS_OFF + 0x00200UL) +#define SCHIZO_IOMMU_TSBBASE_A (SCHIZO_PBM_A_REGS_OFF + 0x00208UL) +#define SCHIZO_IOMMU_FLUSH_A (SCHIZO_PBM_A_REGS_OFF + 0x00210UL) +#define SCHIZO_IOMMU_CTXFLUSH_A (SCHIZO_PBM_A_REGS_OFF + 0x00218UL) +#define SCHIZO_IOMMU_TAG_A (SCHIZO_PBM_A_REGS_OFF + 0x0a580UL) +#define SCHIZO_IOMMU_DATA_A (SCHIZO_PBM_A_REGS_OFF + 0x0a600UL) +#define SCHIZO_IOMMU_CONTROL_B (SCHIZO_PBM_B_REGS_OFF + 0x00200UL) +#define SCHIZO_IOMMU_TSBBASE_B (SCHIZO_PBM_B_REGS_OFF + 0x00208UL) +#define SCHIZO_IOMMU_FLUSH_B (SCHIZO_PBM_B_REGS_OFF + 0x00210UL) +#define SCHIZO_IOMMU_CTXFLUSH_B (SCHIZO_PBM_B_REGS_OFF + 0x00218UL) +#define SCHIZO_IOMMU_TAG_B (SCHIZO_PBM_B_REGS_OFF + 0x0a580UL) +#define SCHIZO_IOMMU_DATA_B (SCHIZO_PBM_B_REGS_OFF + 0x0a600UL) + +static void schizo_pbm_iommu_init(struct pci_controller_info *p, + struct pci_pbm_info *pbm, + int is_pbm_a) +{ + struct pci_iommu *iommu = pbm->iommu; + unsigned long tsbbase, i, tagbase, database; + u64 control; + + /* Setup initial software IOMMU state. */ + spin_lock_init(&iommu->lock); + iommu->iommu_cur_ctx = 0; + + /* Register addresses, SCHIZO has iommu ctx flushing. */ + if (is_pbm_a) { + iommu->iommu_control = p->controller_regs + SCHIZO_IOMMU_CONTROL_A; + iommu->iommu_tsbbase = p->controller_regs + SCHIZO_IOMMU_TSBBASE_A; + iommu->iommu_flush = p->controller_regs + SCHIZO_IOMMU_FLUSH_A; + iommu->iommu_ctxflush = p->controller_regs + SCHIZO_IOMMU_CTXFLUSH_A; + } else { + iommu->iommu_control = p->controller_regs + SCHIZO_IOMMU_CONTROL_B; + iommu->iommu_tsbbase = p->controller_regs + SCHIZO_IOMMU_TSBBASE_B; + iommu->iommu_flush = p->controller_regs + SCHIZO_IOMMU_FLUSH_B; + iommu->iommu_ctxflush = p->controller_regs + SCHIZO_IOMMU_CTXFLUSH_B; + } + + /* We use the main control/status register of SCHIZO as the write + * completion register. + */ + iommu->write_complete_reg = p->controller_regs + 0x10000UL; + + /* + * Invalidate TLB Entries. + */ + control = schizo_read(iommu->iommu_control); + control |= SCHIZO_IOMMU_CTRL_DENAB; + schizo_write(iommu->iommu_control, control); + + if (is_pbm_a) + tagbase = SCHIZO_IOMMU_TAG_A, database = SCHIZO_IOMMU_DATA_A; + else + tagbase = SCHIZO_IOMMU_TAG_B, database = SCHIZO_IOMMU_DATA_B; + for(i = 0; i < 16; i++) { + schizo_write(p->controller_regs + tagbase + (i * 8UL), 0); + schizo_write(p->controller_regs + database + (i * 8UL), 0); + } + + /* Leave diag mode enabled for full-flushing done + * in pci_iommu.c + */ + + /* Using assumed page size 8K with 128K entries we need 1MB iommu page + * table (128K ioptes * 8 bytes per iopte). This is + * page order 7 on UltraSparc. + */ + tsbbase = __get_free_pages(GFP_KERNEL, 7); + if (!tsbbase) { + prom_printf("SCHIZO_IOMMU: Error, gfp(tsb) failed.\n"); + prom_halt(); + } + iommu->page_table = (iopte_t *)tsbbase; + iommu->page_table_sz_bits = 17; + iommu->page_table_map_base = 0xc0000000; + iommu->dma_addr_mask = 0xffffffff; + memset((char *)tsbbase, 0, PAGE_SIZE << 7); + + /* We start with no consistent mappings. */ + iommu->lowest_consistent_map = + 1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS); + + for (i = 0; i < PBM_NCLUSTERS; i++) { + iommu->alloc_info[i].flush = 0; + iommu->alloc_info[i].next = 0; + } + + schizo_write(iommu->iommu_tsbbase, __pa(tsbbase)); + + control = schizo_read(iommu->iommu_control); + control &= ~(SCHIZO_IOMMU_CTRL_TSBSZ | SCHIZO_IOMMU_CTRL_TBWSZ); + control |= (SCHIZO_IOMMU_TSBSZ_128K | SCHIZO_IOMMU_CTRL_ENAB); + schizo_write(iommu->iommu_control, control); } static void schizo_pbm_init(struct pci_controller_info *p, int prom_node, int is_pbm_a) { - /* IMPLEMENT ME */ + unsigned int busrange[2]; + struct pci_pbm_info *pbm; + int err; + + if (is_pbm_a) + pbm = &p->pbm_A; + else + pbm = &p->pbm_B; + + schizo_determine_mem_io_space(pbm, is_pbm_a, p->controller_regs); + pbm_register_toplevel_resources(p, pbm); + + pbm->parent = p; + pbm->prom_node = prom_node; + prom_getstring(prom_node, "name", + pbm->prom_name, + sizeof(pbm->prom_name)); + + err = prom_getproperty(prom_node, "ranges", + (char *) pbm->pbm_ranges, + sizeof(pbm->pbm_ranges)); + if (err != -1) + pbm->num_pbm_ranges = + (err / sizeof(struct linux_prom_pci_ranges)); + else + pbm->num_pbm_ranges = 0; + + err = prom_getproperty(prom_node, "interrupt-map", + (char *)pbm->pbm_intmap, + sizeof(pbm->pbm_intmap)); + if (err != -1) { + pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); + err = prom_getproperty(prom_node, "interrupt-map-mask", + (char *)&pbm->pbm_intmask, + sizeof(pbm->pbm_intmask)); + if (err == -1) { + prom_printf("SCHIZO-PBM: Fatal error, no " + "interrupt-map-mask.\n"); + prom_halt(); + } + } else { + pbm->num_pbm_intmap = 0; + memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); + } + + err = prom_getproperty(prom_node, "bus-range", + (char *)&busrange[0], + sizeof(busrange)); + if (err == 0 || err == -1) { + prom_printf("SCHIZO-PBM: Fatal error, no bus-range.\n"); + prom_halt(); + } + pbm->pci_first_busno = busrange[0]; + pbm->pci_last_busno = busrange[1]; + + schizo_pbm_iommu_init(p, pbm, is_pbm_a); + schizo_pbm_strbuf_init(p, pbm, is_pbm_a); +} + +static void schizo_controller_hwinit(struct pci_controller_info *p) +{ + unsigned long pbm_a_base, pbm_b_base; + u64 tmp; + + pbm_a_base = p->controller_regs + SCHIZO_PBM_A_REGS_OFF; + pbm_b_base = p->controller_regs + SCHIZO_PBM_B_REGS_OFF; + + /* Set IRQ retry to infinity. */ + schizo_write(pbm_a_base + 0x1a00UL, 0xff); + schizo_write(pbm_b_base + 0x1a00UL, 0xff); + + /* Enable arbiter for all PCI slots. */ + tmp = schizo_read(pbm_a_base + 0x2000UL); + tmp |= 0x3fUL; + schizo_write(pbm_a_base + 0x2000UL, tmp); + + tmp = schizo_read(pbm_b_base + 0x2000UL); + tmp |= 0x3fUL; + schizo_write(pbm_b_base + 0x2000UL, tmp); } void __init schizo_init(int node) @@ -90,6 +1708,7 @@ void __init schizo_init(int node) struct linux_prom64_registers pr_regs[3]; struct pci_controller_info *p; struct pci_iommu *iommu; + unsigned long flags; u32 portid; int is_pbm_a, err; @@ -142,11 +1761,10 @@ void __init schizo_init(int node) p->resource_adjust = schizo_resource_adjust; p->pci_ops = &schizo_ops; -pbm_init: /* Three OBP regs: * 1) PBM controller regs * 2) Schizo front-end controller regs (same for both PBMs) - * 3) Unknown... (0x7ffec000000 and 0x7ffee000000 on Excalibur) + * 3) PBM PCI config space */ err = prom_getproperty(node, "reg", (char *)&pr_regs[0], @@ -156,14 +1774,16 @@ pbm_init: prom_halt(); } - /* XXX Read REG base, record in controller/pbm structures. */ - - /* XXX Report controller to console. */ + p->controller_regs = pr_regs[1].phys_addr - 0x10000UL; + printk("PCI: Found SCHIZO, control regs at %016lx\n", + p->controller_regs); - /* XXX Setup pci_memspace_mask */ + /* Like PSYCHO we have a 2GB aligned area for memory space. */ + pci_memspace_mask = 0x7fffffffUL; - /* XXX Init core controller and IOMMU */ + /* Init core controller. */ + schizo_controller_hwinit(p); - is_pbm_a = XXX; /* Figure out this test */ + is_pbm_a = ((pr_regs[0].phys_addr & 0x00700000) == 0x00600000); schizo_pbm_init(p, node, is_pbm_a); } diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 3fc337d71..e0930d55e 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.114 2001/02/13 01:16:44 davem Exp $ +/* $Id: process.c,v 1.116 2001/03/24 09:36:01 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -416,14 +416,14 @@ void flush_thread(void) unsigned long pgd_cache; if (pgd_none(*pgd0)) { - pmd_t *page = get_pmd_fast(); + pmd_t *page = pmd_alloc_one_fast(NULL, 0); if (!page) - (void) get_pmd_slow(pgd0, 0); - else - pgd_set(pgd0, page); + page = pmd_alloc_one(NULL, 0); + pgd_set(pgd0, page); } pgd_cache = pgd_val(*pgd0) << 11UL; - __asm__ __volatile__("stxa %0, [%1] %2" + __asm__ __volatile__("stxa %0, [%1] %2\n\t" + "membar #Sync" : /* no outputs */ : "r" (pgd_cache), "r" (TSB_REG), diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 7f791c4d0..031ec5582 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -53,10 +53,10 @@ static inline void pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr) { if (current->thread.flags & SPARC_FLAG_32BIT) { - if(put_user(value, (unsigned int *)addr)) + if (put_user(value, (unsigned int *)addr)) return pt_error_return(regs, EFAULT); } else { - if(put_user(value, addr)) + if (put_user(value, addr)) return pt_error_return(regs, EFAULT); } regs->u_regs[UREG_I0] = 0; @@ -137,7 +137,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) s, request, pid, addr, data, addr2); } #endif - if(request == PTRACE_TRACEME) { + if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) { pt_error_return(regs, EPERM); @@ -149,7 +149,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out; } #ifndef ALLOW_INIT_TRACING - if(pid == 1) { + if (pid == 1) { /* Can't dork with init. */ pt_error_return(regs, EPERM); goto out; @@ -157,9 +157,11 @@ asmlinkage void do_ptrace(struct pt_regs *regs) #endif read_lock(&tasklist_lock); child = find_task_by_pid(pid); + if (child) + get_task_struct(child); read_unlock(&tasklist_lock); - if(!child) { + if (!child) { pt_error_return(regs, ESRCH); goto out; } @@ -168,32 +170,32 @@ asmlinkage void do_ptrace(struct pt_regs *regs) || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { unsigned long flags; - if(child == current) { + if (child == current) { /* Try this under SunOS/Solaris, bwa haha * You'll never be able to kill the process. ;-) */ pt_error_return(regs, EPERM); - goto out; + goto out_tsk; } - if((!child->dumpable || - (current->uid != child->euid) || - (current->uid != child->uid) || - (current->uid != child->suid) || - (current->gid != child->egid) || - (current->gid != child->sgid) || - (!cap_issubset(child->cap_permitted, current->cap_permitted)) || - (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) { + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->uid) || + (current->uid != child->suid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) { pt_error_return(regs, EPERM); - goto out; + goto out_tsk; } /* the same process cannot be attached many times */ if (child->ptrace & PT_PTRACED) { pt_error_return(regs, EPERM); - goto out; + goto out_tsk; } child->ptrace |= PT_PTRACED; write_lock_irqsave(&tasklist_lock, flags); - if(child->p_pptr != current) { + if (child->p_pptr != current) { REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); @@ -201,32 +203,32 @@ asmlinkage void do_ptrace(struct pt_regs *regs) write_unlock_irqrestore(&tasklist_lock, flags); send_sig(SIGSTOP, child, 1); pt_succ_return(regs, 0); - goto out; + goto out_tsk; } if (!(child->ptrace & PT_PTRACED)) { pt_error_return(regs, ESRCH); - goto out; + goto out_tsk; } - if(child->state != TASK_STOPPED) { - if(request != PTRACE_KILL) { + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) { pt_error_return(regs, ESRCH); - goto out; + goto out_tsk; } } - if(child->p_pptr != current) { + if (child->p_pptr != current) { pt_error_return(regs, ESRCH); - goto out; + goto out_tsk; } - if(!(child->thread.flags & SPARC_FLAG_32BIT) && - ((request == PTRACE_READDATA64) || - (request == PTRACE_WRITEDATA64) || - (request == PTRACE_READTEXT64) || - (request == PTRACE_WRITETEXT64) || - (request == PTRACE_PEEKTEXT64) || - (request == PTRACE_POKETEXT64) || - (request == PTRACE_PEEKDATA64) || - (request == PTRACE_POKEDATA64))) { + if (!(child->thread.flags & SPARC_FLAG_32BIT) && + ((request == PTRACE_READDATA64) || + (request == PTRACE_WRITEDATA64) || + (request == PTRACE_READTEXT64) || + (request == PTRACE_WRITETEXT64) || + (request == PTRACE_PEEKTEXT64) || + (request == PTRACE_POKETEXT64) || + (request == PTRACE_PEEKDATA64) || + (request == PTRACE_POKEDATA64))) { addr = regs->u_regs[UREG_G2]; addr2 = regs->u_regs[UREG_G3]; request -= 30; /* wheee... */ @@ -278,7 +280,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) if (copied == sizeof(tmp64)) res = 0; } - if(res < 0) + if (res < 0) pt_error_return(regs, -res); else pt_succ_return(regs, res); @@ -295,42 +297,45 @@ asmlinkage void do_ptrace(struct pt_regs *regs) __put_user(cregs->tnpc, (&pregs->npc)) || __put_user(cregs->y, (&pregs->y))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } - for(rval = 1; rval < 16; rval++) + for (rval = 1; rval < 16; rval++) if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } pt_succ_return(regs, 0); #ifdef DEBUG_PTRACE printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); #endif - goto out; + goto out_tsk; } case PTRACE_GETREGS64: { struct pt_regs *pregs = (struct pt_regs *) addr; struct pt_regs *cregs = child->thread.kregs; + unsigned long tpc = cregs->tpc; int rval; + if ((child->thread.flags & SPARC_FLAG_32BIT) != 0) + tpc &= 0xffffffff; if (__put_user(cregs->tstate, (&pregs->tstate)) || - __put_user(cregs->tpc, (&pregs->tpc)) || + __put_user(tpc, (&pregs->tpc)) || __put_user(cregs->tnpc, (&pregs->tnpc)) || __put_user(cregs->y, (&pregs->y))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } - for(rval = 1; rval < 16; rval++) + for (rval = 1; rval < 16; rval++) if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } pt_succ_return(regs, 0); #ifdef DEBUG_PTRACE printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); #endif - goto out; + goto out_tsk; } case PTRACE_SETREGS: { @@ -347,23 +352,23 @@ asmlinkage void do_ptrace(struct pt_regs *regs) __get_user(npc, (&pregs->npc)) || __get_user(y, (&pregs->y))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } cregs->tstate &= ~(TSTATE_ICC); cregs->tstate |= psr_to_tstate_icc(psr); - if(!((pc | npc) & 3)) { + if (!((pc | npc) & 3)) { cregs->tpc = pc; cregs->tnpc = npc; } cregs->y = y; - for(i = 1; i < 16; i++) { + for (i = 1; i < 16; i++) { if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } } pt_succ_return(regs, 0); - goto out; + goto out_tsk; } case PTRACE_SETREGS64: { @@ -380,24 +385,28 @@ asmlinkage void do_ptrace(struct pt_regs *regs) __get_user(tnpc, (&pregs->tnpc)) || __get_user(y, (&pregs->y))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; + } + if ((child->thread.flags & SPARC_FLAG_32BIT) != 0) { + tpc &= 0xffffffff; + tnpc &= 0xffffffff; } tstate &= (TSTATE_ICC | TSTATE_XCC); cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); cregs->tstate |= tstate; - if(!((tpc | tnpc) & 3)) { + if (!((tpc | tnpc) & 3)) { cregs->tpc = tpc; cregs->tnpc = tnpc; } cregs->y = y; - for(i = 1; i < 16; i++) { + for (i = 1; i < 16; i++) { if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } } pt_succ_return(regs, 0); - goto out; + goto out_tsk; } case PTRACE_GETFPREGS: { @@ -422,10 +431,10 @@ asmlinkage void do_ptrace(struct pt_regs *regs) __put_user(0, (&fps->extra)) || clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } pt_succ_return(regs, 0); - goto out; + goto out_tsk; } case PTRACE_GETFPREGS64: { @@ -439,10 +448,10 @@ asmlinkage void do_ptrace(struct pt_regs *regs) (64 * sizeof(unsigned int))) || __put_user(child->thread.xfsr[0], (&fps->fsr))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } pt_succ_return(regs, 0); - goto out; + goto out_tsk; } case PTRACE_SETFPREGS: { @@ -464,7 +473,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) (32 * sizeof(unsigned int))) || __get_user(fsr, (&fps->fsr))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } child->thread.xfsr[0] &= 0xffffffff00000000UL; child->thread.xfsr[0] |= fsr; @@ -472,7 +481,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) child->thread.gsr[0] = 0; child->thread.fpsaved[0] |= (FPRS_FEF | FPRS_DL); pt_succ_return(regs, 0); - goto out; + goto out_tsk; } case PTRACE_SETFPREGS64: { @@ -486,13 +495,13 @@ asmlinkage void do_ptrace(struct pt_regs *regs) (64 * sizeof(unsigned int))) || __get_user(child->thread.xfsr[0], (&fps->fsr))) { pt_error_return(regs, EFAULT); - goto out; + goto out_tsk; } if (!(child->thread.fpsaved[0] & FPRS_FEF)) child->thread.gsr[0] = 0; child->thread.fpsaved[0] |= (FPRS_FEF | FPRS_DL | FPRS_DU); pt_succ_return(regs, 0); - goto out; + goto out_tsk; } case PTRACE_READTEXT: @@ -528,19 +537,24 @@ asmlinkage void do_ptrace(struct pt_regs *regs) case PTRACE_CONT: { /* restart after signal. */ if (data > _NSIG) { pt_error_return(regs, EIO); - goto out; + goto out_tsk; } if (addr != 1) { + unsigned long pc_mask = ~0UL; + + if ((child->thread.flags & SPARC_FLAG_32BIT) != 0) + pc_mask = 0xffffffff; + if (addr & 3) { pt_error_return(regs, EINVAL); - goto out; + goto out_tsk; } #ifdef DEBUG_PTRACE printk ("Original: %016lx %016lx\n", child->thread.kregs->tpc, child->thread.kregs->tnpc); printk ("Continuing with %016lx %016lx\n", addr, addr+4); #endif - child->thread.kregs->tpc = addr; - child->thread.kregs->tnpc = addr + 4; + child->thread.kregs->tpc = (addr & pc_mask); + child->thread.kregs->tnpc = ((addr + 4) & pc_mask); } if (request == PTRACE_SYSCALL) @@ -558,7 +572,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) #endif wake_up_process(child); pt_succ_return(regs, 0); - goto out; + goto out_tsk; } /* @@ -569,12 +583,12 @@ asmlinkage void do_ptrace(struct pt_regs *regs) case PTRACE_KILL: { if (child->state == TASK_ZOMBIE) { /* already dead */ pt_succ_return(regs, 0); - goto out; + goto out_tsk; } child->exit_code = SIGKILL; wake_up_process(child); pt_succ_return(regs, 0); - goto out; + goto out_tsk; } case PTRACE_SUNDETACH: { /* detach a process that was attached. */ @@ -582,7 +596,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) if ((unsigned long) data > _NSIG) { pt_error_return(regs, EIO); - goto out; + goto out_tsk; } child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); child->exit_code = data; @@ -595,29 +609,39 @@ asmlinkage void do_ptrace(struct pt_regs *regs) wake_up_process(child); pt_succ_return(regs, 0); - goto out; + goto out_tsk; } /* PTRACE_DUMPCORE unsupported... */ default: pt_error_return(regs, EIO); - goto out; + goto out_tsk; } flush_and_out: { unsigned long va; - for(va = 0; va < (PAGE_SIZE << 1); va += 32) - spitfire_put_dcache_tag(va, 0x0); - if (request == PTRACE_PEEKTEXT || - request == PTRACE_POKETEXT || - request == PTRACE_READTEXT || - request == PTRACE_WRITETEXT) { - for(va = 0; va < (PAGE_SIZE << 1); va += 32) - spitfire_put_icache_tag(va, 0x0); - __asm__ __volatile__("flush %g6"); + + if (tlb_type == cheetah) { + for (va = 0; va < (1 << 16); va += (1 << 5)) + spitfire_put_dcache_tag(va, 0x0); + /* No need to mess with I-cache on Cheetah. */ + } else { + for (va = 0; va < (PAGE_SIZE << 1); va += 32) + spitfire_put_dcache_tag(va, 0x0); + if (request == PTRACE_PEEKTEXT || + request == PTRACE_POKETEXT || + request == PTRACE_READTEXT || + request == PTRACE_WRITETEXT) { + for (va = 0; va < (PAGE_SIZE << 1); va += 32) + spitfire_put_icache_tag(va, 0x0); + __asm__ __volatile__("flush %g6"); + } } } +out_tsk: + if (child) + free_task_struct(child); out: unlock_kernel(); } diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index 729ecd911..952f0caa5 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.53 2000/08/06 05:20:35 davem Exp $ +/* $Id: rtrap.S,v 1.54 2001/03/08 22:08:51 davem Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -192,7 +192,7 @@ to_kernel: ldub [%g6 + AOFF_task_thread + AOFF_thread_fpdepth], %l5 rd %fprs, %g5 wr %g5, FPRS_FEF, %fprs - ldub [%o1 + %o0], %g5 + ldx [%o1 + %o5], %g5 add %g6, AOFF_task_thread + AOFF_thread_xfsr, %o1 membar #StoreLoad | #LoadLoad sll %o0, 8, %o2 diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 7817f4566..cf6bbb896 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.59 2001/02/13 01:16:44 davem Exp $ +/* $Id: setup.c,v 1.63 2001/03/09 22:04:25 davem Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -43,8 +43,6 @@ #include <net/ipconfig.h> #endif -#undef PROM_DEBUG_CONSOLE - struct screen_info screen_info = { 0, 0, /* orig-x, orig-y */ 0, /* unused */ @@ -166,10 +164,14 @@ int prom_callback(long *args) "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU)); /* - * Locked down tlb entry 63. + * Locked down tlb entry. */ - tte = spitfire_get_dtlb_data(63); + if (tlb_type == spitfire) + tte = spitfire_get_dtlb_data(SPITFIRE_HIGHEST_LOCKED_TLBENT); + else if (tlb_type == cheetah) + tte = cheetah_get_ldtlb_data(CHEETAH_HIGHEST_LOCKED_TLBENT); + res = PROM_TRUE; goto done; } @@ -253,7 +255,7 @@ int prom_callback(long *args) unsigned long tte; tte = args[3]; - prom_printf("%lx ", (tte & _PAGE_SOFT2) >> 50); + prom_printf("%lx ", (tte & 0x07FC000000000000) >> 50); args[2] = 2; args[args[1] + 3] = 0; @@ -285,14 +287,12 @@ static int console_fb __initdata = 0; /* Exported for mm/init.c:paging_init. */ unsigned long cmdline_memory_size = 0; -#ifdef PROM_DEBUG_CONSOLE static struct console prom_debug_console = { name: "debug", write: prom_console_write, flags: CON_PRINTBUFFER, index: -1, }; -#endif /* XXX Implement this at some point... */ void kernel_enter_debugger(void) @@ -326,6 +326,10 @@ static void __init process_switch(char c) prom_printf("boot_flags_init: Halt!\n"); prom_halt(); break; + case 'p': + /* Use PROM debug console. */ + register_console(&prom_debug_console); + break; default: printk("Unknown boot switch (-%c)\n", c); break; @@ -454,10 +458,6 @@ void __init setup_arch(char **cmdline_p) *cmdline_p = prom_getbootargs(); strcpy(saved_command_line, *cmdline_p); -#ifdef PROM_DEBUG_CONSOLE - register_console(&prom_debug_console); -#endif - printk("ARCH: SUN4U\n"); #ifdef CONFIG_DUMMY_CONSOLE diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index 23d0774b4..354021278 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.55 2001/01/24 21:05:13 davem Exp $ +/* $Id: signal.c,v 1.56 2001/03/21 11:46:20 davem Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -108,6 +108,10 @@ asmlinkage void sparc64_set_context(struct pt_regs *regs) recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } + if ((tp->flags & SPARC_FLAG_32BIT) != 0) { + pc &= 0xffffffff; + npc &= 0xffffffff; + } regs->tpc = pc; regs->tnpc = npc; err |= __get_user(regs->y, &((*grp)[MC_Y])); @@ -190,9 +194,13 @@ asmlinkage void sparc64_get_context(struct pt_regs *regs) grp = &mcp->mc_gregs; /* Skip over the trap instruction, first. */ - regs->tpc = regs->tnpc; - regs->tnpc += 4; - + if ((tp->flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc = (regs->tnpc & 0xffffffff); + regs->tnpc = (regs->tnpc + 4) & 0xffffffff; + } else { + regs->tpc = regs->tnpc; + regs->tnpc += 4; + } err = 0; if (_NSIG_WORDS == 1) err |= __put_user(current->blocked.sig[0], @@ -289,8 +297,13 @@ asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs) recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - regs->tpc = regs->tnpc; - regs->tnpc += 4; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc = (regs->tnpc & 0xffffffff); + regs->tnpc = (regs->tnpc + 4) & 0xffffffff; + } else { + regs->tpc = regs->tnpc; + regs->tnpc += 4; + } /* Condition codes and return value where set here for sigpause, * and so got used by setup_frame, which again causes sigreturn() @@ -344,8 +357,13 @@ asmlinkage void do_rt_sigsuspend(sigset_t *uset, size_t sigsetsize, struct pt_re recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - regs->tpc = regs->tnpc; - regs->tnpc += 4; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc = (regs->tnpc & 0xffffffff); + regs->tnpc = (regs->tnpc + 4) & 0xffffffff; + } else { + regs->tpc = regs->tnpc; + regs->tnpc += 4; + } /* Condition codes and return value where set here for sigpause, * and so got used by setup_frame, which again causes sigreturn() @@ -407,6 +425,10 @@ void do_rt_sigreturn(struct pt_regs *regs) err = get_user(tpc, &sf->regs.tpc); err |= __get_user(tnpc, &sf->regs.tnpc); + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + tpc &= 0xffffffff; + tnpc &= 0xffffffff; + } err |= ((tpc | tnpc) & 3); /* 2. Restore the state */ @@ -555,7 +577,10 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, /* 5. signal handler */ regs->tpc = (unsigned long) ka->sa.sa_handler; regs->tnpc = (regs->tpc + 4); - + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } /* 4. return to kernel instructions */ regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; return; diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 0886d9d39..9613c7869 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.68 2001/01/24 21:05:13 davem Exp $ +/* $Id: signal32.c,v 1.69 2001/03/21 11:46:20 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -155,6 +155,10 @@ asmlinkage void _sigpause32_common(old_sigset_t32 set, struct pt_regs *regs) regs->tpc = regs->tnpc; regs->tnpc += 4; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } /* Condition codes and return value where set here for sigpause, * and so got used by setup_frame, which again causes sigreturn() @@ -206,6 +210,10 @@ asmlinkage void do_rt_sigsuspend32(u32 uset, size_t sigsetsize, struct pt_regs * regs->tpc = regs->tnpc; regs->tnpc += 4; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } /* Condition codes and return value where set here for sigpause, * and so got used by setup_frame, which again causes sigreturn() @@ -268,6 +276,10 @@ void do_new_sigreturn32(struct pt_regs *regs) if ((pc | npc) & 3) goto segv; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + pc &= 0xffffffff; + npc &= 0xffffffff; + } regs->tpc = pc; regs->tnpc = npc; @@ -355,6 +367,10 @@ asmlinkage void do_sigreturn32(struct pt_regs *regs) recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + pc &= 0xffffffff; + npc &= 0xffffffff; + } regs->tpc = pc; regs->tnpc = npc; err = __get_user(regs->u_regs[UREG_FP], &scptr->sigc_sp); @@ -398,6 +414,10 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) if ((pc | npc) & 3) goto segv; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + pc &= 0xffffffff; + npc &= 0xffffffff; + } regs->tpc = pc; regs->tnpc = npc; @@ -489,6 +509,11 @@ setup_frame32(struct sigaction *sa, struct pt_regs *regs, int signr, sigset_t *o #endif unsigned psr; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + pc &= 0xffffffff; + npc &= 0xffffffff; + } + synchronize_user_stack(); save_and_clear_fpu(); @@ -615,6 +640,10 @@ setup_frame32(struct sigaction *sa, struct pt_regs *regs, int signr, sigset_t *o regs->u_regs[UREG_FP] = (unsigned long) sframep; regs->tpc = (unsigned long) sa->sa_handler; regs->tnpc = (regs->tpc + 4); + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } return; sigsegv: @@ -678,6 +707,10 @@ static inline void new_setup_frame32(struct k_sigaction *ka, struct pt_regs *reg } /* 2. Save the current process state */ + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } err = put_user(regs->tpc, &sf->info.si_regs.pc); err |= __put_user(regs->tnpc, &sf->info.si_regs.npc); err |= __put_user(regs->y, &sf->info.si_regs.y); @@ -728,11 +761,15 @@ static inline void new_setup_frame32(struct k_sigaction *ka, struct pt_regs *reg /* 4. signal handler */ regs->tpc = (unsigned long) ka->sa.sa_handler; regs->tnpc = (regs->tpc + 4); + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } /* 5. return to kernel instructions */ - if (ka->ka_restorer) + if (ka->ka_restorer) { regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; - else { + } else { /* Flush instruction space. */ unsigned long address = ((unsigned long)&(sf->insns[0])); pgd_t *pgdp = pgd_offset(current->mm, address); @@ -819,6 +856,10 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, err |= __copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned)); /* Store registers */ + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } err |= __put_user(regs->tpc, &((*gr) [SVR4_PC])); err |= __put_user(regs->tnpc, &((*gr) [SVR4_NPC])); psr = tstate_to_psr (regs->tstate); @@ -883,6 +924,10 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, regs->u_regs[UREG_FP] = (unsigned long) sfp; regs->tpc = (unsigned long) sa->sa_handler; regs->tnpc = (regs->tpc + 4); + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } #ifdef DEBUG_SIGNALS printk ("Solaris-frame: %x %x\n", (int) regs->tpc, (int) regs->tnpc); @@ -940,6 +985,10 @@ svr4_getcontext(svr4_ucontext_t *uc, struct pt_regs *regs) err |= __copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned)); /* Store registers */ + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } err |= __put_user(regs->tpc, &uc->mcontext.greg [SVR4_PC]); err |= __put_user(regs->tnpc, &uc->mcontext.greg [SVR4_NPC]); #if 1 @@ -1037,6 +1086,10 @@ asmlinkage int svr4_setcontext(svr4_ucontext_t *c, struct pt_regs *regs) spin_unlock_irq(¤t->sigmask_lock); regs->tpc = pc; regs->tnpc = npc | 1; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } err |= __get_user(regs->y, &((*gr) [SVR4_Y])); err |= __get_user(psr, &((*gr) [SVR4_PSR])); regs->tstate &= ~(TSTATE_ICC|TSTATE_XCC); @@ -1095,6 +1148,10 @@ static inline void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs } /* 2. Save the current process state */ + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } err = put_user(regs->tpc, &sf->regs.pc); err |= __put_user(regs->tnpc, &sf->regs.npc); err |= __put_user(regs->y, &sf->regs.y); @@ -1150,6 +1207,10 @@ static inline void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs /* 4. signal handler */ regs->tpc = (unsigned long) ka->sa.sa_handler; regs->tnpc = (regs->tpc + 4); + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } /* 5. return to kernel instructions */ if (ka->ka_restorer) diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 76045d0d2..bceac6597 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -65,7 +65,7 @@ int smp_info(char *buf) strcpy(buf, "State:\n"); for (i = 0; i < NR_CPUS; i++) - if(cpu_present_map & (1UL << i)) + if (cpu_present_map & (1UL << i)) len += sprintf(buf + len, "CPU%d:\t\tonline\n", i); return len; @@ -76,7 +76,7 @@ int smp_bogo(char *buf) int len = 0, i; for (i = 0; i < NR_CPUS; i++) - if(cpu_present_map & (1UL << i)) + if (cpu_present_map & (1UL << i)) len += sprintf(buf + len, "Cpu%dBogo\t: %lu.%02lu\n", i, cpu_data[i].udelay_val / (500000/HZ), @@ -99,7 +99,7 @@ void __init smp_store_cpu_info(int id) cpu_data[id].pgd_cache = NULL; cpu_data[id].idle_volume = 1; - for(i = 0; i < 16; i++) + for (i = 0; i < 16; i++) cpu_data[id].irq_worklists[i] = 0; } @@ -153,6 +153,19 @@ void __init smp_callin(void) : /* no inputs */ : "g1", "g2"); + if (SPARC64_USE_STICK) { + /* Let the user get at STICK too. */ + __asm__ __volatile__(" + sethi %%hi(0x80000000), %%g1 + sllx %%g1, 32, %%g1 + rd %%asr24, %%g2 + andn %%g2, %%g1, %%g2 + wr %%g2, 0, %%asr24" + : /* no outputs */ + : /* no inputs */ + : "g1", "g2"); + } + /* Restore PSTATE_IE. */ __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : /* no outputs */ @@ -177,7 +190,7 @@ void __init smp_callin(void) atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; - while(!smp_processors_ready) + while (!smp_processors_ready) membar("#LoadLoad"); } @@ -222,14 +235,14 @@ void __init smp_boot_cpus(void) smp_tune_scheduling(); init_idle(); - if(linux_num_cpus == 1) + if (linux_num_cpus == 1) return; - for(i = 0; i < NR_CPUS; i++) { - if(i == boot_cpu_id) + for (i = 0; i < NR_CPUS; i++) { + if (i == boot_cpu_id) continue; - if(cpu_present_map & (1UL << i)) { + if (cpu_present_map & (1UL << i)) { unsigned long entry = (unsigned long)(&sparc64_cpu_startup); unsigned long cookie = (unsigned long)(&cpu_new_task); struct task_struct *p; @@ -256,12 +269,12 @@ void __init smp_boot_cpus(void) cpu_new_task = p; prom_startcpu(linux_cpus[no].prom_node, entry, cookie); - for(timeout = 0; timeout < 5000000; timeout++) { - if(callin_flag) + for (timeout = 0; timeout < 5000000; timeout++) { + if (callin_flag) break; udelay(100); } - if(callin_flag) { + if (callin_flag) { __cpu_number_map[i] = cpucount; __cpu_logical_map[cpucount] = i; prom_cpu_nodes[i] = linux_cpus[no].prom_node; @@ -272,20 +285,20 @@ void __init smp_boot_cpus(void) prom_printf("FAILED\n"); } } - if(!callin_flag) { + if (!callin_flag) { cpu_present_map &= ~(1UL << i); __cpu_number_map[i] = -1; } } cpu_new_task = NULL; - if(cpucount == 0) { + if (cpucount == 0) { printk("Error: only one processor found.\n"); cpu_present_map = (1UL << smp_processor_id()); } else { unsigned long bogosum = 0; - for(i = 0; i < NR_CPUS; i++) { - if(cpu_present_map & (1UL << i)) + for (i = 0; i < NR_CPUS; i++) { + if (cpu_present_map & (1UL << i)) bogosum += cpu_data[i].udelay_val; } printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n", @@ -299,9 +312,7 @@ void __init smp_boot_cpus(void) membar("#StoreStore | #StoreLoad"); } -/* #define XCALL_DEBUG */ - -static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu) +static void spitfire_xcall_helper(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu) { u64 result, target; int stuck, tmp; @@ -314,10 +325,6 @@ static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, un } target = (cpu << 14) | 0x70; -#ifdef XCALL_DEBUG - printk("CPU[%d]: xcall(data[%016lx:%016lx:%016lx],tgt[%016lx])\n", - smp_processor_id(), data0, data1, data2, target); -#endif again: /* Ok, this is the real Spitfire Errata #54. * One must read back from a UDB internal register @@ -340,7 +347,7 @@ again: ldxa [%%g1] 0x7f, %%g0 membar #Sync" : "=r" (tmp) - : "r" (pstate), "i" (PSTATE_IE), "i" (ASI_UDB_INTR_W), + : "r" (pstate), "i" (PSTATE_IE), "i" (ASI_INTR_W), "r" (data0), "r" (data1), "r" (data2), "r" (target), "r" (0x10), "0" (tmp) : "g1"); @@ -350,46 +357,155 @@ again: __asm__ __volatile__("ldxa [%%g0] %1, %0" : "=r" (result) : "i" (ASI_INTR_DISPATCH_STAT)); - if(result == 0) { + if (result == 0) { __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); return; } stuck -= 1; - if(stuck == 0) + if (stuck == 0) break; - } while(result & 0x1); + } while (result & 0x1); __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); - if(stuck == 0) { -#ifdef XCALL_DEBUG + if (stuck == 0) { printk("CPU[%d]: mondo stuckage result[%016lx]\n", smp_processor_id(), result); -#endif } else { -#ifdef XCALL_DEBUG - printk("CPU[%d]: Penguin %d NACK's master.\n", smp_processor_id(), cpu); -#endif udelay(2); goto again; } } -void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2) +static __inline__ void spitfire_xcall_deliver(u64 data0, u64 data1, u64 data2, unsigned long mask) { - if(smp_processors_ready) { - unsigned long mask = (cpu_present_map & ~(1UL<<smp_processor_id())); - u64 pstate, data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff)); + int ncpus = smp_num_cpus - 1; + int i; + u64 pstate; + + __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); + for (i = 0; (i < NR_CPUS) && ncpus; i++) { + if (mask & (1UL << i)) { + spitfire_xcall_helper(data0, data1, data2, pstate, i); + ncpus--; + } + } +} + +/* Cheetah now allows to send the whole 64-bytes of data in the interrupt + * packet, but we have no use for that. However we do take advantage of + * the new pipelining feature (ie. dispatch to multiple cpus simultaneously). + */ +#if NR_CPUS > 32 +#error Fixup cheetah_xcall_deliver Dave... +#endif +static void cheetah_xcall_deliver(u64 data0, u64 data1, u64 data2, unsigned long mask) +{ + u64 pstate; + int nack_busy_id; + + if (!mask) + return; + + __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); + +retry: + __asm__ __volatile__("wrpr %0, %1, %%pstate\n\t" + : : "r" (pstate), "i" (PSTATE_IE)); + + /* Setup the dispatch data registers. */ + __asm__ __volatile__("stxa %0, [%3] %6\n\t" + "membar #Sync\n\t" + "stxa %1, [%4] %6\n\t" + "membar #Sync\n\t" + "stxa %2, [%5] %6\n\t" + "membar #Sync\n\t" + : /* no outputs */ + : "r" (data0), "r" (data1), "r" (data2), + "r" (0x40), "r" (0x50), "r" (0x60), + "i" (ASI_INTR_W)); + + nack_busy_id = 0; + { int i, ncpus = smp_num_cpus - 1; - __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); - for(i = 0; i < NR_CPUS; i++) { - if(mask & (1UL << i)) { - xcall_deliver(data0, data1, data2, pstate, i); + for (i = 0; (i < NR_CPUS) && ncpus; i++) { + if (mask & (1UL << i)) { + u64 target = (i << 14) | 0x70; + + target |= (nack_busy_id++ << 24); + __asm__ __volatile__("stxa %%g0, [%0] %1\n\t" + "membar #Sync\n\t" + : /* no outputs */ + : "r" (target), "i" (ASI_INTR_W)); ncpus--; } - if (!ncpus) break; } + } + + /* Now, poll for completion. */ + { + u64 dispatch_stat; + long stuck; + + stuck = 100000 * nack_busy_id; + do { + __asm__ __volatile__("ldxa [%%g0] %1, %0" + : "=r" (dispatch_stat) + : "i" (ASI_INTR_DISPATCH_STAT)); + if (dispatch_stat == 0UL) { + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" + : : "r" (pstate)); + return; + } + if (!--stuck) + break; + } while (dispatch_stat & 0x5555555555555555UL); + + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" + : : "r" (pstate)); + + if ((stuck & ~(0x5555555555555555UL)) == 0) { + /* Busy bits will not clear, continue instead + * of freezing up on this cpu. + */ + printk("CPU[%d]: mondo stuckage result[%016lx]\n", + smp_processor_id(), dispatch_stat); + } else { + int i, this_busy_nack = 0; + + /* Delay some random time with interrupts enabled + * to prevent deadlock. + */ + udelay(2 * nack_busy_id); + + /* Clear out the mask bits for cpus which did not + * NACK us. + */ + for (i = 0; i < NR_CPUS; i++) { + if (mask & (1UL << i)) { + if ((dispatch_stat & (0x2 << this_busy_nack)) == 0) + mask &= ~(1UL << i); + this_busy_nack += 2; + } + } + + goto retry; + } + } +} + +void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2) +{ + if (smp_processors_ready) { + unsigned long mask = (cpu_present_map & ~(1UL<<smp_processor_id())); + u64 data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff)); + + if (tlb_type == spitfire) + spitfire_xcall_deliver(data0, data1, data2, mask); + else + cheetah_xcall_deliver(data0, data1, data2, mask); + /* NOTE: Caller runs local copy on master. */ } } @@ -445,11 +561,17 @@ extern unsigned long xcall_receive_signal; void smp_receive_signal(int cpu) { - if(smp_processors_ready && - (cpu_present_map & (1UL<<cpu)) != 0) { - u64 pstate, data0 = (((u64)&xcall_receive_signal) & 0xffffffff); - __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); - xcall_deliver(data0, 0, 0, pstate, cpu); + if (smp_processors_ready) { + unsigned long mask = 1UL << cpu; + + if ((cpu_present_map & mask) != 0) { + u64 data0 = (((u64)&xcall_receive_signal) & 0xffffffff); + + if (tlb_type == spitfire) + spitfire_xcall_deliver(data0, 0, 0, mask); + else + cheetah_xcall_deliver(data0, 0, 0, mask); + } } } @@ -609,7 +731,7 @@ void smp_capture(void) int result = __atomic_add(1, &smp_capture_depth); membar("#StoreStore | #LoadStore"); - if(result == 1) { + if (result == 1) { int ncpus = smp_num_cpus; #ifdef CAPTURE_DEBUG @@ -620,7 +742,7 @@ void smp_capture(void) membar("#StoreStore | #LoadStore"); atomic_inc(&smp_capture_registry); smp_cross_call(&xcall_capture, 0, 0, 0); - while(atomic_read(&smp_capture_registry) != ncpus) + while (atomic_read(&smp_capture_registry) != ncpus) membar("#LoadLoad"); #ifdef CAPTURE_DEBUG printk("done\n"); @@ -631,8 +753,8 @@ void smp_capture(void) void smp_release(void) { - if(smp_processors_ready) { - if(atomic_dec_and_test(&smp_capture_depth)) { + if (smp_processors_ready) { + if (atomic_dec_and_test(&smp_capture_depth)) { #ifdef CAPTURE_DEBUG printk("CPU[%d]: Giving pardon to imprisoned penguins\n", smp_processor_id()); @@ -659,7 +781,7 @@ void smp_penguin_jailcell(void) prom_world(1); atomic_inc(&smp_capture_registry); membar("#StoreLoad | #StoreStore"); - while(penguins_are_doing_time) + while (penguins_are_doing_time) membar("#LoadLoad"); restore_alternate_globals(global_save); atomic_dec(&smp_capture_registry); @@ -690,14 +812,23 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) /* * Check for level 14 softint. */ - if (!(get_softint() & (1UL << 0))) { - extern void handler_irq(int, struct pt_regs *); + { + unsigned long tick_mask; - handler_irq(14, regs); - return; + if (SPARC64_USE_STICK) + tick_mask = (1UL << 16); + else + tick_mask = (1UL << 0); + + if (!(get_softint() & tick_mask)) { + extern void handler_irq(int, struct pt_regs *); + + handler_irq(14, regs); + return; + } + clear_softint(tick_mask); } - clear_softint((1UL << 0)); do { if (!user) sparc64_do_profile(regs->tpc, regs->u_regs[UREG_RETPC]); @@ -740,6 +871,7 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) * that %tick is not prone to this bug, but I am not * taking any chances. */ + if (!SPARC64_USE_STICK) { __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" "ba,pt %%xcc, 1f\n\t" " add %0, %2, %0\n\t" @@ -750,6 +882,14 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) "mov %1, %1" : "=&r" (compare), "=r" (tick) : "r" (current_tick_offset)); + } else { + __asm__ __volatile__("rd %%asr25, %0\n\t" + "add %0, %2, %0\n\t" + "wr %0, 0x0, %%asr25\n\t" + "rd %%asr24, %1\n\t" + : "=&r" (compare), "=r" (tick) + : "r" (current_tick_offset)); + } /* Restore PSTATE_IE. */ __asm__ __volatile__("wrpr %0, 0x0, %%pstate" @@ -782,6 +922,7 @@ static void __init smp_setup_percpu_timer(void) * at the start of an I-cache line, and perform a dummy * read back from %tick_cmpr right after writing to it. -DaveM */ + if (!SPARC64_USE_STICK) { __asm__ __volatile__(" rd %%tick, %%g1 ba,pt %%xcc, 1f @@ -792,6 +933,15 @@ static void __init smp_setup_percpu_timer(void) : /* no outputs */ : "r" (current_tick_offset) : "g1"); + } else { + __asm__ __volatile__(" + rd %%asr24, %%g1 + add %%g1, %0, %%g1 + wr %%g1, 0x0, %%asr25" + : /* no outputs */ + : "r" (current_tick_offset) + : "g1"); + } /* Restore PSTATE_IE. */ __asm__ __volatile__("wrpr %0, 0x0, %%pstate" @@ -806,9 +956,9 @@ void __init smp_tick_init(void) boot_cpu_id = hard_smp_processor_id(); current_tick_offset = timer_tick_offset; cpu_present_map = 0; - for(i = 0; i < linux_num_cpus; i++) + for (i = 0; i < linux_num_cpus; i++) cpu_present_map |= (1UL << linux_cpus[i].mid); - for(i = 0; i < NR_CPUS; i++) { + for (i = 0; i < NR_CPUS; i++) { __cpu_number_map[i] = -1; __cpu_logical_map[i] = -1; } @@ -827,11 +977,11 @@ static inline unsigned long find_flush_base(unsigned long size) size = PAGE_ALIGN(size); found = size; base = (unsigned long) page_address(p); - while(found != 0) { + while (found != 0) { /* Failure. */ - if(p >= (mem_map + max_mapnr)) + if (p >= (mem_map + max_mapnr)) return 0UL; - if(PageReserved(p)) { + if (PageReserved(p)) { found = size; base = (unsigned long) page_address(p); } else { @@ -924,12 +1074,12 @@ int setup_profiling_timer(unsigned int multiplier) unsigned long flags; int i; - if((!multiplier) || (timer_tick_offset / multiplier) < 1000) + if ((!multiplier) || (timer_tick_offset / multiplier) < 1000) return -EINVAL; save_and_cli(flags); - for(i = 0; i < NR_CPUS; i++) { - if(cpu_present_map & (1UL << i)) + for (i = 0; i < NR_CPUS; i++) { + if (cpu_present_map & (1UL << i)) prof_multiplier(i) = multiplier; } current_tick_offset = (timer_tick_offset / multiplier); diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index f35e38a5d..2dc8ddb4f 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc64_ksyms.c,v 1.100 2001/01/11 15:07:09 davem Exp $ +/* $Id: sparc64_ksyms.c,v 1.102 2001/03/24 09:36:01 davem Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -179,6 +179,8 @@ EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(__flushw_user); +EXPORT_SYMBOL(tlb_type); + EXPORT_SYMBOL(flush_icache_range); EXPORT_SYMBOL(__flush_dcache_page); @@ -232,8 +234,7 @@ EXPORT_SYMBOL(_sigpause_common); /* Should really be in linux/kernel/ksyms.c */ EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_fpu); -EXPORT_SYMBOL(get_pmd_slow); -EXPORT_SYMBOL(get_pte_slow); +EXPORT_SYMBOL(pte_alloc_one); #ifndef CONFIG_SMP EXPORT_SYMBOL(pgt_quicklists); #endif diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index e3cd81c97..16c9e7e19 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.48 2001/02/13 01:16:44 davem Exp $ +/* $Id: sys_sparc.c,v 1.50 2001/03/24 09:36:10 davem Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -243,9 +243,9 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, if (flags & MAP_SHARED) current->thread.flags |= SPARC_FLAG_MMAPSHARED; - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); retval = do_mmap(file, addr, len, prot, flags, off); - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED); @@ -263,9 +263,9 @@ asmlinkage long sys64_munmap(unsigned long addr, size_t len) if (len > -PAGE_OFFSET || (addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET)) return -EINVAL; - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); ret = do_munmap(current->mm, addr, len); - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); return ret; } @@ -285,7 +285,7 @@ asmlinkage unsigned long sys64_mremap(unsigned long addr, goto out; if (addr < PAGE_OFFSET && addr + old_len > -PAGE_OFFSET) goto out; - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); vma = find_vma(current->mm, addr); if (vma && (vma->vm_flags & VM_SHARED)) current->thread.flags |= SPARC_FLAG_MMAPSHARED; @@ -305,7 +305,7 @@ asmlinkage unsigned long sys64_mremap(unsigned long addr, ret = do_mremap(addr, old_len, new_len, flags, new_addr); out_sem: current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED); - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); out: return ret; } @@ -335,6 +335,10 @@ sparc_breakpoint (struct pt_regs *regs) { siginfo_t info; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } #ifdef DEBUG_SPARC_BREAKPOINT printk ("TRAP: Entering kernel PC=%lx, nPC=%lx\n", regs->tpc, regs->tnpc); #endif @@ -384,6 +388,10 @@ asmlinkage int solaris_syscall(struct pt_regs *regs) regs->tpc = regs->tnpc; regs->tnpc += 4; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } if(++count <= 5) { printk ("For Solaris binary emulation you need solaris module loaded\n"); show_regs (regs); @@ -400,6 +408,10 @@ asmlinkage int sunos_syscall(struct pt_regs *regs) regs->tpc = regs->tnpc; regs->tnpc += 4; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } if(++count <= 20) printk ("SunOS binary emulation not compiled in\n"); force_sig(SIGSEGV, current); diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index d68b75cab..571826a12 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.173 2001/02/13 01:16:44 davem Exp $ +/* $Id: sys_sparc32.c,v 1.174 2001/03/24 09:36:10 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -4133,7 +4133,7 @@ asmlinkage unsigned long sys32_mremap(unsigned long addr, goto out; if (addr > 0xf0000000UL - old_len) goto out; - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); vma = find_vma(current->mm, addr); if (vma && (vma->vm_flags & VM_SHARED)) current->thread.flags |= SPARC_FLAG_MMAPSHARED; @@ -4152,7 +4152,7 @@ asmlinkage unsigned long sys32_mremap(unsigned long addr, ret = do_mremap(addr, old_len, new_len, flags, new_addr); out_sem: current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED); - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); out: return ret; } diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index bfbd8841c..a51b69783 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos32.c,v 1.57 2001/02/13 01:16:44 davem Exp $ +/* $Id: sys_sunos32.c,v 1.59 2001/03/24 09:36:11 davem Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -100,12 +100,12 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of flags &= ~_MAP_NEW; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); retval = do_mmap(file, (unsigned long) addr, (unsigned long) len, (unsigned long) prot, (unsigned long) flags, (unsigned long) off); - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); if(!ret_type) retval = ((retval < 0xf0000000) ? 0 : retval); out_putf: @@ -126,7 +126,7 @@ asmlinkage int sunos_brk(u32 baddr) unsigned long rlim; unsigned long newbrk, oldbrk, brk = (unsigned long) baddr; - down(¤t->mm->mmap_sem); + down_write(¤t->mm->mmap_sem); if (brk < current->mm->end_code) goto out; newbrk = PAGE_ALIGN(brk); @@ -170,7 +170,7 @@ asmlinkage int sunos_brk(u32 baddr) do_brk(oldbrk, newbrk-oldbrk); retval = 0; out: - up(¤t->mm->mmap_sem); + up_write(¤t->mm->mmap_sem); return retval; } @@ -456,6 +456,10 @@ asmlinkage int sunos_nosys(void) static int cnt; regs = current->thread.kregs; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } info.si_signo = SIGSYS; info.si_errno = 0; info.si_code = __SI_FAULT|0x100; diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index a3340f54a..f369368d2 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.33 2001/01/11 15:07:09 davem Exp $ +/* $Id: time.c,v 1.36 2001/03/15 08:51:24 anton Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -20,6 +20,8 @@ #include <linux/timex.h> #include <linux/init.h> #include <linux/ioport.h> +#include <linux/mc146818rtc.h> +#include <linux/delay.h> #include <asm/oplib.h> #include <asm/mostek.h> @@ -35,7 +37,11 @@ extern rwlock_t xtime_lock; spinlock_t mostek_lock = SPIN_LOCK_UNLOCKED; +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; unsigned long mstk48t02_regs = 0UL; +#ifdef CONFIG_PCI +unsigned long ds1287_regs = 0UL; +#endif static unsigned long mstk48t08_regs = 0UL; static unsigned long mstk48t59_regs = 0UL; @@ -77,6 +83,7 @@ void sparc64_do_profile(unsigned long pc, unsigned long o7) extern int rwlock_impl_begin, rwlock_impl_end; extern int atomic_impl_begin, atomic_impl_end; extern int __memcpy_begin, __memcpy_end; + extern int __bzero_begin, __bzero_end; extern int __bitops_begin, __bitops_end; if ((pc >= (unsigned long) &atomic_impl_begin && @@ -85,6 +92,8 @@ void sparc64_do_profile(unsigned long pc, unsigned long o7) pc < (unsigned long) &rwlock_impl_end) || (pc >= (unsigned long) &__memcpy_begin && pc < (unsigned long) &__memcpy_end) || + (pc >= (unsigned long) &__bzero_begin && + pc < (unsigned long) &__bzero_end) || (pc >= (unsigned long) &__bitops_begin && pc < (unsigned long) &__bitops_end)) pc = o7; @@ -135,6 +144,7 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) * that %tick is not prone to this bug, but I am not * taking any chances. */ + if (!SPARC64_USE_STICK) { __asm__ __volatile__(" rd %%tick_cmpr, %0 ba,pt %%xcc, 1f @@ -146,6 +156,15 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) mov %1, %1" : "=&r" (timer_tick_compare), "=r" (ticks) : "r" (timer_tick_offset)); + } else { + __asm__ __volatile__(" + rd %%asr25, %0 + add %0, %2, %0 + wr %0, 0, %%asr25 + rd %%asr24, %1" + : "=&r" (timer_tick_compare), "=r" (ticks) + : "r" (timer_tick_offset)); + } /* Restore PSTATE_IE. */ __asm__ __volatile__("wrpr %0, 0x0, %%pstate" @@ -168,11 +187,19 @@ void timer_tick_interrupt(struct pt_regs *regs) /* * Only keep timer_tick_offset uptodate, but don't set TICK_CMPR. */ + if (!SPARC64_USE_STICK) { __asm__ __volatile__(" rd %%tick_cmpr, %0 add %0, %1, %0" : "=&r" (timer_tick_compare) : "r" (timer_tick_offset)); + } else { + __asm__ __volatile__(" + rd %%asr25, %0 + add %0, %1, %0" + : "=&r" (timer_tick_compare) + : "r" (timer_tick_offset)); + } timer_check_rtc(); @@ -282,41 +309,95 @@ static int __init has_low_battery(void) return (data1 == data2); /* Was the write blocked? */ } +#ifndef BCD_TO_BIN +#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) +#endif + +#ifndef BIN_TO_BCD +#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) +#endif /* Probe for the real time clock chip. */ static void __init set_system_time(void) { unsigned int year, mon, day, hour, min, sec; unsigned long mregs = mstk48t02_regs; +#ifdef CONFIG_PCI + unsigned long dregs = ds1287_regs; +#else + unsigned long dregs = 0UL; +#endif u8 tmp; do_get_fast_time = do_gettimeofday; - if(!mregs) { + if (!mregs && !dregs) { prom_printf("Something wrong, clock regs not mapped yet.\n"); prom_halt(); } - spin_lock_irq(&mostek_lock); + if (mregs) { + spin_lock_irq(&mostek_lock); - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); + /* Traditional Mostek chip. */ + tmp = mostek_read(mregs + MOSTEK_CREG); + tmp |= MSTK_CREG_READ; + mostek_write(mregs + MOSTEK_CREG, tmp); + + sec = MSTK_REG_SEC(mregs); + min = MSTK_REG_MIN(mregs); + hour = MSTK_REG_HOUR(mregs); + day = MSTK_REG_DOM(mregs); + mon = MSTK_REG_MONTH(mregs); + year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); + } else { + int i; + + /* Dallas 12887 RTC chip. */ + + /* Stolen from arch/i386/kernel/time.c, see there for + * credits and descriptive comments. + */ + for (i = 0; i < 1000000; i++) { + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) + break; + udelay(10); + } + for (i = 0; i < 1000000; i++) { + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) + break; + udelay(10); + } + do { + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + } - sec = MSTK_REG_SEC(mregs); - min = MSTK_REG_MIN(mregs); - hour = MSTK_REG_HOUR(mregs); - day = MSTK_REG_DOM(mregs); - mon = MSTK_REG_MONTH(mregs); - year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_usec = 0; - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); + if (mregs) { + tmp = mostek_read(mregs + MOSTEK_CREG); + tmp &= ~MSTK_CREG_READ; + mostek_write(mregs + MOSTEK_CREG, tmp); - spin_unlock_irq(&mostek_lock); + spin_unlock_irq(&mostek_lock); + } } void __init clock_probe(void) @@ -358,21 +439,22 @@ void __init clock_probe(void) busnd = sbus_root->prom_node; } - if(busnd == -1) { + if (busnd == -1) { prom_printf("clock_probe: problem, cannot find bus to search.\n"); prom_halt(); } node = prom_getchild(busnd); - while(1) { + while (1) { if (!node) model[0] = 0; else prom_getstring(node, "model", model, sizeof(model)); - if(strcmp(model, "mk48t02") && - strcmp(model, "mk48t08") && - strcmp(model, "mk48t59")) { + if (strcmp(model, "mk48t02") && + strcmp(model, "mk48t08") && + strcmp(model, "mk48t59") && + strcmp(model, "ds1287")) { if (node) node = prom_getsibling(node); #ifdef CONFIG_PCI @@ -384,7 +466,7 @@ void __init clock_probe(void) } } #endif - if(node == 0) { + if (node == 0) { prom_printf("clock_probe: Cannot find timer chip\n"); prom_halt(); } @@ -415,8 +497,12 @@ void __init clock_probe(void) prom_halt(); } - mstk48t59_regs = edev->resource[0].start; - mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; + if (!strcmp(model, "ds1287")) { + ds1287_regs = edev->resource[0].start; + } else { + mstk48t59_regs = edev->resource[0].start; + mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; + } break; } #endif @@ -456,27 +542,21 @@ void __init clock_probe(void) break; } - /* Report a low battery voltage condition. */ - if (has_low_battery()) - prom_printf("NVRAM: Low battery voltage!\n"); + if (mstk48t02_regs != 0UL) { + /* Report a low battery voltage condition. */ + if (has_low_battery()) + prom_printf("NVRAM: Low battery voltage!\n"); - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); + /* Kick start the clock if it is completely stopped. */ + if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) + kick_start_clock(); + } set_system_time(); __restore_flags(flags); } -#ifndef BCD_TO_BIN -#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) -#endif - -#ifndef BIN_TO_BCD -#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) -#endif - extern void init_timers(void (*func)(int, void *, struct pt_regs *), unsigned long *); @@ -497,6 +577,7 @@ static __inline__ unsigned long do_gettimeoffset(void) { unsigned long ticks; + if (!SPARC64_USE_STICK) { __asm__ __volatile__(" rd %%tick, %%g1 add %1, %%g1, %0 @@ -505,6 +586,14 @@ static __inline__ unsigned long do_gettimeoffset(void) : "=r" (ticks) : "r" (timer_tick_offset), "r" (timer_tick_compare) : "g1", "g2"); + } else { + __asm__ __volatile__("rd %%asr24, %%g1\n\t" + "add %1, %%g1, %0\n\t" + "sub %0, %2, %0\n\t" + : "=&r" (ticks) + : "r" (timer_tick_offset), "r" (timer_tick_compare) + : "g1"); + } return (ticks * timer_ticks_per_usec_quotient) >> 32UL; } @@ -533,8 +622,13 @@ void do_settimeofday(struct timeval *tv) static int set_rtc_mmss(unsigned long nowtime) { - int real_seconds, real_minutes, mostek_minutes; - unsigned long regs = mstk48t02_regs; + int real_seconds, real_minutes, chip_minutes; + unsigned long mregs = mstk48t02_regs; +#ifdef CONFIG_PCI + unsigned long dregs = ds1287_regs; +#else + unsigned long dregs = 0UL; +#endif unsigned long flags; u8 tmp; @@ -542,52 +636,96 @@ static int set_rtc_mmss(unsigned long nowtime) * Not having a register set can lead to trouble. * Also starfire doesnt have a tod clock. */ - if (!regs) + if (!mregs && !dregs) return -1; - spin_lock_irqsave(&mostek_lock, flags); + if (mregs) { + spin_lock_irqsave(&mostek_lock, flags); - /* Read the current RTC minutes. */ - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(regs + MOSTEK_CREG, tmp); + /* Read the current RTC minutes. */ + tmp = mostek_read(mregs + MOSTEK_CREG); + tmp |= MSTK_CREG_READ; + mostek_write(mregs + MOSTEK_CREG, tmp); - mostek_minutes = MSTK_REG_MIN(regs); + chip_minutes = MSTK_REG_MIN(mregs); - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(regs + MOSTEK_CREG, tmp); + tmp = mostek_read(mregs + MOSTEK_CREG); + tmp &= ~MSTK_CREG_READ; + mostek_write(mregs + MOSTEK_CREG, tmp); - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; - if (abs(real_minutes - mostek_minutes) < 30) { - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); + if (abs(real_minutes - chip_minutes) < 30) { + tmp = mostek_read(mregs + MOSTEK_CREG); + tmp |= MSTK_CREG_WRITE; + mostek_write(mregs + MOSTEK_CREG, tmp); - MSTK_SET_REG_SEC(regs,real_seconds); - MSTK_SET_REG_MIN(regs,real_minutes); + MSTK_SET_REG_SEC(mregs,real_seconds); + MSTK_SET_REG_MIN(mregs,real_minutes); - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); + tmp = mostek_read(mregs + MOSTEK_CREG); + tmp &= ~MSTK_CREG_WRITE; + mostek_write(mregs + MOSTEK_CREG, tmp); + + spin_unlock_irqrestore(&mostek_lock, flags); - spin_unlock_irqrestore(&mostek_lock, flags); + return 0; + } else { + spin_unlock_irqrestore(&mostek_lock, flags); - return 0; + return -1; + } } else { - spin_unlock_irqrestore(&mostek_lock, flags); + int retval = 0; + unsigned char save_control, save_freq_select; - return -1; + /* Stolen from arch/i386/kernel/time.c, see there for + * credits and descriptive comments. + */ + spin_lock_irqsave(&rtc_lock, flags); + save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + chip_minutes = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + BCD_TO_BIN(chip_minutes); + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) + real_minutes += 30; + real_minutes %= 60; + + if (abs(real_minutes - chip_minutes) < 30) { + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + } + CMOS_WRITE(real_seconds,RTC_SECONDS); + CMOS_WRITE(real_minutes,RTC_MINUTES); + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + chip_minutes, real_minutes); + retval = -1; + } + + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock_irqrestore(&rtc_lock, flags); + + return retval; } } diff --git a/arch/sparc64/kernel/trampoline.S b/arch/sparc64/kernel/trampoline.S index 4f2606c97..578a5ae80 100644 --- a/arch/sparc64/kernel/trampoline.S +++ b/arch/sparc64/kernel/trampoline.S @@ -1,4 +1,4 @@ -/* $Id: trampoline.S,v 1.12 1999/12/15 15:45:12 davem Exp $ +/* $Id: trampoline.S,v 1.19 2001/03/22 09:54:26 davem Exp $ * trampoline.S: Jump start slave processors on sparc64. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -7,6 +7,8 @@ #include <asm/head.h> #include <asm/asi.h> #include <asm/lsu.h> +#include <asm/dcr.h> +#include <asm/dcu.h> #include <asm/pstate.h> #include <asm/page.h> #include <asm/pgtable.h> @@ -30,177 +32,246 @@ dtlb_load: sparc64_cpu_startup: flushw - mov (LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), %g1 - stxa %g1, [%g0] ASI_LSU_CONTROL - membar #Sync + rdpr %ver, %g1 + sethi %hi(0x003e0014), %g5 + srlx %g1, 32, %g1 + or %g5, %lo(0x003e0014), %g5 + cmp %g1, %g5 + bne,pt %icc, spitfire_startup + nop + +cheetah_startup: + mov DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1 + wr %g1, %asr18 + + sethi %uhi(DCU_ME | DCU_RE | DCU_PE | DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5 + or %g5, %ulo(DCU_ME | DCU_RE | DCU_PE | DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5 + sllx %g5, 32, %g5 + or %g5, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g5 + ldxa [%g0] ASI_DCU_CONTROL_REG, %g3 + or %g5, %g3, %g5 + stxa %g5, [%g0] ASI_DCU_CONTROL_REG + membar #Sync + + /* Disable STICK_INT interrupts. */ + sethi %hi(0x80000000), %g5 + sllx %g5, 32, %g5 + wr %g5, %asr25 + + ba,pt %xcc, startup_continue + nop - wrpr %g0, 15, %pil - wr %g0, 0, %tick_cmpr +spitfire_startup: + mov (LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), %g1 + stxa %g1, [%g0] ASI_LSU_CONTROL + membar #Sync + +startup_continue: + wrpr %g0, 15, %pil + wr %g0, 0, %tick_cmpr /* Call OBP by hand to lock KERNBASE into i/d tlbs. */ - mov %o0, %l0 - - sethi %hi(prom_entry_lock), %g2 -1: ldstub [%g2 + %lo(prom_entry_lock)], %g1 - brnz,pn %g1, 1b - membar #StoreLoad | #StoreStore - - sethi %hi(p1275buf), %g2 - or %g2, %lo(p1275buf), %g2 - ldx [%g2 + 0x10], %l2 - mov %sp, %l1 - add %l2, -(192 + 128), %sp + mov %o0, %l0 + + sethi %hi(prom_entry_lock), %g2 +1: ldstub [%g2 + %lo(prom_entry_lock)], %g1 + brnz,pn %g1, 1b + membar #StoreLoad | #StoreStore + + sethi %hi(p1275buf), %g2 + or %g2, %lo(p1275buf), %g2 + ldx [%g2 + 0x10], %l2 + mov %sp, %l1 + add %l2, -(192 + 128), %sp flushw - sethi %hi(call_method), %g2 - or %g2, %lo(call_method), %g2 - stx %g2, [%sp + 2047 + 128 + 0x00] - mov 5, %g2 - stx %g2, [%sp + 2047 + 128 + 0x08] - mov 1, %g2 - stx %g2, [%sp + 2047 + 128 + 0x10] - sethi %hi(itlb_load), %g2 - or %g2, %lo(itlb_load), %g2 - stx %g2, [%sp + 2047 + 128 + 0x18] - sethi %hi(mmu_ihandle_cache), %g2 - lduw [%g2 + %lo(mmu_ihandle_cache)], %g2 - stx %g2, [%sp + 2047 + 128 + 0x20] - sethi %hi(KERNBASE), %g2 - stx %g2, [%sp + 2047 + 128 + 0x28] - sethi %hi(kern_locked_tte_data), %g2 - ldx [%g2 + %lo(kern_locked_tte_data)], %g2 - stx %g2, [%sp + 2047 + 128 + 0x30] - mov 63, %g2 - stx %g2, [%sp + 2047 + 128 + 0x38] - sethi %hi(p1275buf), %g2 - or %g2, %lo(p1275buf), %g2 - ldx [%g2 + 0x08], %o1 - call %o1 - add %sp, (2047 + 128), %o0 - - sethi %hi(call_method), %g2 - or %g2, %lo(call_method), %g2 - stx %g2, [%sp + 2047 + 128 + 0x00] - mov 5, %g2 - stx %g2, [%sp + 2047 + 128 + 0x08] - mov 1, %g2 - stx %g2, [%sp + 2047 + 128 + 0x10] - sethi %hi(dtlb_load), %g2 - or %g2, %lo(dtlb_load), %g2 - stx %g2, [%sp + 2047 + 128 + 0x18] - sethi %hi(mmu_ihandle_cache), %g2 - lduw [%g2 + %lo(mmu_ihandle_cache)], %g2 - stx %g2, [%sp + 2047 + 128 + 0x20] - sethi %hi(KERNBASE), %g2 - stx %g2, [%sp + 2047 + 128 + 0x28] - sethi %hi(kern_locked_tte_data), %g2 - ldx [%g2 + %lo(kern_locked_tte_data)], %g2 - stx %g2, [%sp + 2047 + 128 + 0x30] - mov 63, %g2 - stx %g2, [%sp + 2047 + 128 + 0x38] - sethi %hi(p1275buf), %g2 - or %g2, %lo(p1275buf), %g2 - ldx [%g2 + 0x08], %o1 - call %o1 - add %sp, (2047 + 128), %o0 - - sethi %hi(prom_entry_lock), %g2 - stb %g0, [%g2 + %lo(prom_entry_lock)] - membar #StoreStore | #StoreLoad - - mov %l1, %sp + sethi %hi(call_method), %g2 + or %g2, %lo(call_method), %g2 + stx %g2, [%sp + 2047 + 128 + 0x00] + mov 5, %g2 + stx %g2, [%sp + 2047 + 128 + 0x08] + mov 1, %g2 + stx %g2, [%sp + 2047 + 128 + 0x10] + sethi %hi(itlb_load), %g2 + or %g2, %lo(itlb_load), %g2 + stx %g2, [%sp + 2047 + 128 + 0x18] + sethi %hi(mmu_ihandle_cache), %g2 + lduw [%g2 + %lo(mmu_ihandle_cache)], %g2 + stx %g2, [%sp + 2047 + 128 + 0x20] + sethi %hi(KERNBASE), %g2 + stx %g2, [%sp + 2047 + 128 + 0x28] + sethi %hi(kern_locked_tte_data), %g2 + ldx [%g2 + %lo(kern_locked_tte_data)], %g2 + stx %g2, [%sp + 2047 + 128 + 0x30] + + rdpr %ver, %g1 + sethi %hi(0x003e0014), %g5 + srlx %g1, 32, %g1 + or %g5, %lo(0x003e0014), %g5 + cmp %g1, %g5 + bne,a,pt %icc, 1f + mov 63, %g2 + mov 15, %g2 +1: + stx %g2, [%sp + 2047 + 128 + 0x38] + sethi %hi(p1275buf), %g2 + or %g2, %lo(p1275buf), %g2 + ldx [%g2 + 0x08], %o1 + call %o1 + add %sp, (2047 + 128), %o0 + + sethi %hi(call_method), %g2 + or %g2, %lo(call_method), %g2 + stx %g2, [%sp + 2047 + 128 + 0x00] + mov 5, %g2 + stx %g2, [%sp + 2047 + 128 + 0x08] + mov 1, %g2 + stx %g2, [%sp + 2047 + 128 + 0x10] + sethi %hi(dtlb_load), %g2 + or %g2, %lo(dtlb_load), %g2 + stx %g2, [%sp + 2047 + 128 + 0x18] + sethi %hi(mmu_ihandle_cache), %g2 + lduw [%g2 + %lo(mmu_ihandle_cache)], %g2 + stx %g2, [%sp + 2047 + 128 + 0x20] + sethi %hi(KERNBASE), %g2 + stx %g2, [%sp + 2047 + 128 + 0x28] + sethi %hi(kern_locked_tte_data), %g2 + ldx [%g2 + %lo(kern_locked_tte_data)], %g2 + stx %g2, [%sp + 2047 + 128 + 0x30] + + rdpr %ver, %g1 + sethi %hi(0x003e0014), %g5 + srlx %g1, 32, %g1 + or %g5, %lo(0x003e0014), %g5 + cmp %g1, %g5 + bne,a,pt %icc, 1f + mov 63, %g2 + mov 15, %g2 +1: + + stx %g2, [%sp + 2047 + 128 + 0x38] + sethi %hi(p1275buf), %g2 + or %g2, %lo(p1275buf), %g2 + ldx [%g2 + 0x08], %o1 + call %o1 + add %sp, (2047 + 128), %o0 + + sethi %hi(prom_entry_lock), %g2 + stb %g0, [%g2 + %lo(prom_entry_lock)] + membar #StoreStore | #StoreLoad + + mov %l1, %sp flushw - mov %l0, %o0 + mov %l0, %o0 - wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate - wr %g0, 0, %fprs + wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate + wr %g0, 0, %fprs - sethi %uhi(PAGE_OFFSET), %g4 - sllx %g4, 32, %g4 + sethi %uhi(PAGE_OFFSET), %g4 + sllx %g4, 32, %g4 /* XXX Buggy PROM... */ - srl %o0, 0, %o0 - ldx [%o0], %g6 + srl %o0, 0, %o0 + ldx [%o0], %g6 - wr %g0, ASI_P, %asi + wr %g0, ASI_P, %asi - mov PRIMARY_CONTEXT, %g7 - stxa %g0, [%g7] ASI_DMMU - membar #Sync - mov SECONDARY_CONTEXT, %g7 - stxa %g0, [%g7] ASI_DMMU - membar #Sync + mov PRIMARY_CONTEXT, %g7 + stxa %g0, [%g7] ASI_DMMU + membar #Sync + mov SECONDARY_CONTEXT, %g7 + stxa %g0, [%g7] ASI_DMMU + membar #Sync - mov 1, %g5 - sllx %g5, (PAGE_SHIFT + 1), %g5 - sub %g5, (REGWIN_SZ + STACK_BIAS), %g5 - add %g6, %g5, %sp - mov 0, %fp + mov 1, %g5 + sllx %g5, (PAGE_SHIFT + 1), %g5 + sub %g5, (REGWIN_SZ + STACK_BIAS), %g5 + add %g6, %g5, %sp + mov 0, %fp - wrpr %g0, 0, %wstate - wrpr %g0, 0, %tl + wrpr %g0, 0, %wstate + wrpr %g0, 0, %tl /* Setup the trap globals, then we can resurface. */ - rdpr %pstate, %o1 - mov %g6, %o2 - wrpr %o1, PSTATE_AG, %pstate - sethi %hi(sparc64_ttable_tl0), %g5 - wrpr %g5, %tba - mov %o2, %g6 - - wrpr %o1, PSTATE_MG, %pstate -#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) + rdpr %pstate, %o1 + mov %g6, %o2 + wrpr %o1, PSTATE_AG, %pstate + sethi %hi(sparc64_ttable_tl0), %g5 + wrpr %g5, %tba + mov %o2, %g6 + + wrpr %o1, PSTATE_MG, %pstate +#define KERN_HIGHBITS ((_PAGE_VALID|_PAGE_SZ4MB)^0xfffff80000000000) #define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) -#ifdef THIS_IS_CHEETAH -#error Dave, make sure you took care of other issues in rest of sparc64 code... -#define VPTE_BASE 0xffe0000000000000 -#else /* Spitfire/Blackbird */ -#define VPTE_BASE 0xfffffffe00000000 + +#define VPTE_BASE_SPITFIRE 0xfffffffe00000000 +#if 1 +#define VPTE_BASE_CHEETAH VPTE_BASE_SPITFIRE +#else +#define VPTE_BASE_CHEETAH 0xffe0000000000000 #endif - mov TSB_REG, %g1 - stxa %g0, [%g1] ASI_DMMU - membar #Sync - mov TLB_SFSR, %g1 - sethi %uhi(KERN_HIGHBITS), %g2 - or %g2, %ulo(KERN_HIGHBITS), %g2 - sllx %g2, 32, %g2 - or %g2, KERN_LOWBITS, %g2 - sethi %uhi(VPTE_BASE), %g3 - or %g3, %ulo(VPTE_BASE), %g3 - sllx %g3, 32, %g3 + + mov TSB_REG, %g1 + stxa %g0, [%g1] ASI_DMMU + membar #Sync + mov TLB_SFSR, %g1 + sethi %uhi(KERN_HIGHBITS), %g2 + or %g2, %ulo(KERN_HIGHBITS), %g2 + sllx %g2, 32, %g2 + or %g2, KERN_LOWBITS, %g2 + + rdpr %ver, %g3 + sethi %hi(0x003e0014), %g7 + srlx %g3, 32, %g3 + or %g7, %lo(0x003e0014), %g7 + cmp %g3, %g7 + bne,pt %icc, 1f + nop + + sethi %uhi(VPTE_BASE_CHEETAH), %g3 + or %g3, %ulo(VPTE_BASE_CHEETAH), %g3 + ba,pt %xcc, 2f + sllx %g3, 32, %g3 +1: + sethi %uhi(VPTE_BASE_SPITFIRE), %g3 + or %g3, %ulo(VPTE_BASE_SPITFIRE), %g3 + sllx %g3, 32, %g3 + +2: clr %g7 #undef KERN_HIGHBITS #undef KERN_LOWBITS -#undef VPTE_BASE +#undef VPTE_BASE_SPITFIRE +#undef VPTE_BASE_CHEETAH /* Setup interrupt globals, we are always SMP. */ - wrpr %o1, PSTATE_IG, %pstate + wrpr %o1, PSTATE_IG, %pstate /* Get our UPA MID. */ - lduw [%o2 + AOFF_task_processor], %g1 - sethi %hi(cpu_data), %g5 - or %g5, %lo(cpu_data), %g5 + lduw [%o2 + AOFF_task_processor], %g1 + sethi %hi(cpu_data), %g5 + or %g5, %lo(cpu_data), %g5 /* In theory this is: &(cpu_data[this_upamid].irq_worklists[0]) */ - sllx %g1, 7, %g1 - add %g5, %g1, %g1 - add %g1, 64, %g6 + sllx %g1, 7, %g1 + add %g5, %g1, %g1 + add %g1, 64, %g6 - wrpr %g0, 0, %wstate - or %o1, PSTATE_IE, %o1 - wrpr %o1, 0, %pstate + wrpr %g0, 0, %wstate + or %o1, PSTATE_IE, %o1 + wrpr %o1, 0, %pstate - call prom_set_trap_table - sethi %hi(sparc64_ttable_tl0), %o0 + call prom_set_trap_table + sethi %hi(sparc64_ttable_tl0), %o0 - call smp_callin + call smp_callin nop - call cpu_idle - mov 0, %o0 - call cpu_panic + call cpu_idle + mov 0, %o0 + call cpu_panic nop -1: b,a,pt %xcc, 1b +1: b,a,pt %xcc, 1b .align 8 sparc64_cpu_startup_end: diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index cc703d3bc..0a368bcee 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.70 2001/02/09 05:46:44 davem Exp $ +/* $Id: traps.c,v 1.73 2001/03/22 07:26:03 davem Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -26,6 +26,7 @@ #include <asm/uaccess.h> #include <asm/fpumacro.h> #include <asm/lsu.h> +#include <asm/dcu.h> #include <asm/psrcompat.h> #ifdef CONFIG_KMOD #include <linux/kmod.h> @@ -263,6 +264,10 @@ void bad_trap (struct pt_regs *regs, long lvl) } if (regs->tstate & TSTATE_PRIV) die_if_kernel ("Kernel bad trap", regs); + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } info.si_signo = SIGILL; info.si_errno = 0; info.si_code = ILL_ILLTRP; @@ -291,6 +296,10 @@ void instruction_access_exception (struct pt_regs *regs, #endif die_if_kernel("Iax", regs); } + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } info.si_signo = SIGSEGV; info.si_errno = 0; info.si_code = SEGV_MAPERR; @@ -342,32 +351,41 @@ void data_access_exception (struct pt_regs *regs, #ifdef CONFIG_PCI /* This is really pathetic... */ -/* #define DEBUG_PCI_POKES */ extern volatile int pci_poke_in_progress; extern volatile int pci_poke_faulted; #endif /* When access exceptions happen, we must do this. */ -static __inline__ void clean_and_reenable_l1_caches(void) +static void clean_and_reenable_l1_caches(void) { unsigned long va; - /* Clean 'em. */ - for(va = 0; va < (PAGE_SIZE << 1); va += 32) { - spitfire_put_icache_tag(va, 0x0); - spitfire_put_dcache_tag(va, 0x0); - } + if (tlb_type == spitfire) { + /* Clean 'em. */ + for (va = 0; va < (PAGE_SIZE << 1); va += 32) { + spitfire_put_icache_tag(va, 0x0); + spitfire_put_dcache_tag(va, 0x0); + } - /* Re-enable. */ - __asm__ __volatile__("flush %%g6\n\t" - "membar #Sync\n\t" - "stxa %0, [%%g0] %1\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC | - LSU_CONTROL_IM | LSU_CONTROL_DM), - "i" (ASI_LSU_CONTROL) - : "memory"); + /* Re-enable in LSU. */ + __asm__ __volatile__("flush %%g6\n\t" + "membar #Sync\n\t" + "stxa %0, [%%g0] %1\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC | + LSU_CONTROL_IM | LSU_CONTROL_DM), + "i" (ASI_LSU_CONTROL) + : "memory"); + } else if (tlb_type == cheetah) { + /* Flush D-cache */ + for (va = 0; va < (1 << 16); va += (1 << 5)) { + __asm__ __volatile__("stxa %%g0, [%0] %1\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (va), "i" (ASI_DCACHE_TAG)); + } + } } void do_iae(struct pt_regs *regs) @@ -387,20 +405,16 @@ void do_iae(struct pt_regs *regs) void do_dae(struct pt_regs *regs) { #ifdef CONFIG_PCI - if(pci_poke_in_progress) { -#ifdef DEBUG_PCI_POKES - prom_printf(" (POKE tpc[%016lx] tnpc[%016lx] ", - regs->tpc, regs->tnpc); -#endif + if (pci_poke_in_progress) { + clean_and_reenable_l1_caches(); + pci_poke_faulted = 1; - regs->tnpc = regs->tpc + 4; + /* Why the fuck did they have to change this? */ + if (tlb_type == cheetah) + regs->tpc += 4; -#ifdef DEBUG_PCI_POKES - prom_printf("PCI) "); - /* prom_halt(); */ -#endif - clean_and_reenable_l1_caches(); + regs->tnpc = regs->tpc + 4; return; } #endif @@ -534,6 +548,10 @@ void do_fpe_common(struct pt_regs *regs) unsigned long fsr = current->thread.xfsr[0]; siginfo_t info; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } info.si_signo = SIGFPE; info.si_errno = 0; info.si_addr = (void *)regs->tpc; @@ -589,6 +607,10 @@ void do_tof(struct pt_regs *regs) if(regs->tstate & TSTATE_PRIV) die_if_kernel("Penguin overflow trap from kernel mode", regs); + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } info.si_signo = SIGEMT; info.si_errno = 0; info.si_code = EMT_TAGOVF; @@ -601,6 +623,10 @@ void do_div0(struct pt_regs *regs) { siginfo_t info; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } info.si_signo = SIGFPE; info.si_errno = 0; info.si_code = FPE_INTDIV; @@ -700,8 +726,13 @@ void die_if_kernel(char *str, struct pt_regs *regs) (rw->ins[6] + STACK_BIAS); } instruction_dump ((unsigned int *) regs->tpc); - } else + } else { + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } user_instruction_dump ((unsigned int *) regs->tpc); + } #ifdef CONFIG_SMP smp_report_regs(); #endif @@ -765,6 +796,10 @@ void do_privop(struct pt_regs *regs) { siginfo_t info; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } info.si_signo = SIGILL; info.si_errno = 0; info.si_code = ILL_PRVOPC; @@ -907,6 +942,10 @@ void do_getpsr(struct pt_regs *regs) regs->u_regs[UREG_I0] = tstate_to_psr(regs->tstate); regs->tpc = regs->tnpc; regs->tnpc += 4; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } } void trap_init(void) diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index b1081b9fe..2f7e0e16c 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.31 2000/05/09 17:40:14 davem Exp $ +/* $Id: ttable.S,v 1.32 2001/03/23 07:56:30 davem Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -22,7 +22,7 @@ tl0_resv018: BTRAP(0x18) BTRAP(0x19) BTRAP(0x1a) BTRAP(0x1b) BTRAP(0x1c) BTRAP(0 tl0_resv01e: BTRAP(0x1e) BTRAP(0x1f) tl0_fpdis: TRAP_NOSAVE(do_fpdis) tl0_fpieee: TRAP_SAVEFPU(do_fpieee) -tl0_fpother: TRAP_SAVEFPU(do_fpother) +tl0_fpother: TRAP_NOSAVE(do_fpother_check_fitos) tl0_tof: TRAP(do_tof) tl0_cwin: CLEAN_WINDOW tl0_div0: TRAP(do_div0) diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c index f43204c3b..7a21f0080 100644 --- a/arch/sparc64/kernel/unaligned.c +++ b/arch/sparc64/kernel/unaligned.c @@ -1,4 +1,4 @@ -/* $Id: unaligned.c,v 1.20 2000/04/29 08:05:21 anton Exp $ +/* $Id: unaligned.c,v 1.21 2001/03/21 11:46:20 davem Exp $ * unaligned.c: Unaligned load/store trap handling with special * cases for the kernel to do them more quickly. * @@ -334,6 +334,10 @@ static inline void advance(struct pt_regs *regs) { regs->tpc = regs->tnpc; regs->tnpc += 4; + if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { + regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } } static inline int floating_point_load_or_store_p(unsigned int insn) |