diff options
Diffstat (limited to 'arch/powerpc/math-emu/math_efp.c')
-rw-r--r-- | arch/powerpc/math-emu/math_efp.c | 65 |
1 files changed, 60 insertions, 5 deletions
diff --git a/arch/powerpc/math-emu/math_efp.c b/arch/powerpc/math-emu/math_efp.c index 41f4ef30e480..62279200d965 100644 --- a/arch/powerpc/math-emu/math_efp.c +++ b/arch/powerpc/math-emu/math_efp.c @@ -1,7 +1,7 @@ /* * arch/powerpc/math-emu/math_efp.c * - * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved. + * Copyright (C) 2006-2008, 2010 Freescale Semiconductor, Inc. * * Author: Ebony Zhu, <ebony.zhu@freescale.com> * Yu Liu, <yu.liu@freescale.com> @@ -104,6 +104,8 @@ #define FP_EX_MASK (FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \ FP_EX_UNDERFLOW | FP_EX_OVERFLOW) +static int have_e500_cpu_a005_erratum; + union dw_union { u64 dp[1]; u32 wp[2]; @@ -320,7 +322,8 @@ int do_spe_mathemu(struct pt_regs *regs) } else { _FP_ROUND_ZERO(1, SB); } - FP_TO_INT_S(vc.wp[1], SB, 32, ((func & 0x3) != 0)); + FP_TO_INT_S(vc.wp[1], SB, 32, + (((func & 0x3) != 0) || SB_s)); goto update_regs; default: @@ -458,7 +461,8 @@ cmp_s: } else { _FP_ROUND_ZERO(2, DB); } - FP_TO_INT_D(vc.wp[1], DB, 32, ((func & 0x3) != 0)); + FP_TO_INT_D(vc.wp[1], DB, 32, + (((func & 0x3) != 0) || DB_s)); goto update_regs; default: @@ -589,8 +593,10 @@ cmp_d: _FP_ROUND_ZERO(1, SB0); _FP_ROUND_ZERO(1, SB1); } - FP_TO_INT_S(vc.wp[0], SB0, 32, ((func & 0x3) != 0)); - FP_TO_INT_S(vc.wp[1], SB1, 32, ((func & 0x3) != 0)); + FP_TO_INT_S(vc.wp[0], SB0, 32, + (((func & 0x3) != 0) || SB0_s)); + FP_TO_INT_S(vc.wp[1], SB1, 32, + (((func & 0x3) != 0) || SB1_s)); goto update_regs; default: @@ -652,6 +658,15 @@ update_regs: return 0; illegal: + if (have_e500_cpu_a005_erratum) { + /* according to e500 cpu a005 erratum, reissue efp inst */ + regs->nip -= 4; +#ifdef DEBUG + printk(KERN_DEBUG "re-issue efp inst: %08lx\n", speinsn); +#endif + return 0; + } + printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn); return -ENOSYS; } @@ -718,3 +733,43 @@ int speround_handler(struct pt_regs *regs) return 0; } + +int __init spe_mathemu_init(void) +{ + u32 pvr, maj, min; + + pvr = mfspr(SPRN_PVR); + + if ((PVR_VER(pvr) == PVR_VER_E500V1) || + (PVR_VER(pvr) == PVR_VER_E500V2)) { + maj = PVR_MAJ(pvr); + min = PVR_MIN(pvr); + + /* + * E500 revision below 1.1, 2.3, 3.1, 4.1, 5.1 + * need cpu a005 errata workaround + */ + switch (maj) { + case 1: + if (min < 1) + have_e500_cpu_a005_erratum = 1; + break; + case 2: + if (min < 3) + have_e500_cpu_a005_erratum = 1; + break; + case 3: + case 4: + case 5: + if (min < 1) + have_e500_cpu_a005_erratum = 1; + break; + default: + break; + } + } + + return 0; +} + +module_init(spe_mathemu_init); |