diff options
author | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
commit | 871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch) | |
tree | 8718f573808810c2a1e8cb8fb6ac469093ca2784 /ANDROID_3.4.5/arch/arm/vfp | |
parent | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff) | |
download | FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2 FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip |
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized.
Changes are basically to make it look like kernel structure.
Diffstat (limited to 'ANDROID_3.4.5/arch/arm/vfp')
-rw-r--r-- | ANDROID_3.4.5/arch/arm/vfp/Makefile | 15 | ||||
-rw-r--r-- | ANDROID_3.4.5/arch/arm/vfp/entry.S | 70 | ||||
-rw-r--r-- | ANDROID_3.4.5/arch/arm/vfp/vfp.h | 380 | ||||
-rw-r--r-- | ANDROID_3.4.5/arch/arm/vfp/vfpdouble.c | 1204 | ||||
-rw-r--r-- | ANDROID_3.4.5/arch/arm/vfp/vfphw.S | 309 | ||||
-rw-r--r-- | ANDROID_3.4.5/arch/arm/vfp/vfpinstr.h | 88 | ||||
-rw-r--r-- | ANDROID_3.4.5/arch/arm/vfp/vfpmodule.c | 729 | ||||
-rw-r--r-- | ANDROID_3.4.5/arch/arm/vfp/vfpsingle.c | 1244 |
8 files changed, 0 insertions, 4039 deletions
diff --git a/ANDROID_3.4.5/arch/arm/vfp/Makefile b/ANDROID_3.4.5/arch/arm/vfp/Makefile deleted file mode 100644 index a81404c0..00000000 --- a/ANDROID_3.4.5/arch/arm/vfp/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# -# linux/arch/arm/vfp/Makefile -# -# Copyright (C) 2001 ARM Limited -# - -# ccflags-y := -DDEBUG -# asflags-y := -DDEBUG - -KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=softvfp+vfp -mfloat-abi=soft) -LDFLAGS +=--no-warn-mismatch - -obj-y += vfp.o - -vfp-$(CONFIG_VFP) += vfpmodule.o entry.o vfphw.o vfpsingle.o vfpdouble.o diff --git a/ANDROID_3.4.5/arch/arm/vfp/entry.S b/ANDROID_3.4.5/arch/arm/vfp/entry.S deleted file mode 100644 index c1a97840..00000000 --- a/ANDROID_3.4.5/arch/arm/vfp/entry.S +++ /dev/null @@ -1,70 +0,0 @@ -/* - * linux/arch/arm/vfp/entry.S - * - * Copyright (C) 2004 ARM Limited. - * Written by Deep Blue Solutions Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Basic entry code, called from the kernel's undefined instruction trap. - * r0 = faulted instruction - * r2 = faulted PC+4 - * r9 = successful return - * r10 = thread_info structure - * lr = failure return - */ -#include <asm/thread_info.h> -#include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" - -ENTRY(do_vfp) -#ifdef CONFIG_PREEMPT - ldr r4, [r10, #TI_PREEMPT] @ get preempt count - add r11, r4, #1 @ increment it - str r11, [r10, #TI_PREEMPT] -#endif - enable_irq - str r2, [sp, #S_PC] @ update regs->ARM_pc for Thumb 2 case - ldr r4, .LCvfp - ldr r11, [r10, #TI_CPU] @ CPU number - add r10, r10, #TI_VFPSTATE @ r10 = workspace - ldr pc, [r4] @ call VFP entry point -ENDPROC(do_vfp) - -ENTRY(vfp_null_entry) -#ifdef CONFIG_PREEMPT - get_thread_info r10 - ldr r4, [r10, #TI_PREEMPT] @ get preempt count - sub r11, r4, #1 @ decrement it - str r11, [r10, #TI_PREEMPT] -#endif - mov pc, lr -ENDPROC(vfp_null_entry) - - .align 2 -.LCvfp: - .word vfp_vector - -@ This code is called if the VFP does not exist. It needs to flag the -@ failure to the VFP initialisation code. - - __INIT -ENTRY(vfp_testing_entry) -#ifdef CONFIG_PREEMPT - get_thread_info r10 - ldr r4, [r10, #TI_PREEMPT] @ get preempt count - sub r11, r4, #1 @ decrement it - str r11, [r10, #TI_PREEMPT] -#endif - ldr r0, VFP_arch_address - str r5, [r0] @ known non-zero value - mov pc, r9 @ we have handled the fault -ENDPROC(vfp_testing_entry) - - .align 2 -VFP_arch_address: - .word VFP_arch - - __FINIT diff --git a/ANDROID_3.4.5/arch/arm/vfp/vfp.h b/ANDROID_3.4.5/arch/arm/vfp/vfp.h deleted file mode 100644 index c8c98dd4..00000000 --- a/ANDROID_3.4.5/arch/arm/vfp/vfp.h +++ /dev/null @@ -1,380 +0,0 @@ -/* - * linux/arch/arm/vfp/vfp.h - * - * Copyright (C) 2004 ARM Limited. - * Written by Deep Blue Solutions Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift) -{ - if (shift) { - if (shift < 32) - val = val >> shift | ((val << (32 - shift)) != 0); - else - val = val != 0; - } - return val; -} - -static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift) -{ - if (shift) { - if (shift < 64) - val = val >> shift | ((val << (64 - shift)) != 0); - else - val = val != 0; - } - return val; -} - -static inline u32 vfp_hi64to32jamming(u64 val) -{ - u32 v; - - asm( - "cmp %Q1, #1 @ vfp_hi64to32jamming\n\t" - "movcc %0, %R1\n\t" - "orrcs %0, %R1, #1" - : "=r" (v) : "r" (val) : "cc"); - - return v; -} - -static inline void add128(u64 *resh, u64 *resl, u64 nh, u64 nl, u64 mh, u64 ml) -{ - asm( "adds %Q0, %Q2, %Q4\n\t" - "adcs %R0, %R2, %R4\n\t" - "adcs %Q1, %Q3, %Q5\n\t" - "adc %R1, %R3, %R5" - : "=r" (nl), "=r" (nh) - : "0" (nl), "1" (nh), "r" (ml), "r" (mh) - : "cc"); - *resh = nh; - *resl = nl; -} - -static inline void sub128(u64 *resh, u64 *resl, u64 nh, u64 nl, u64 mh, u64 ml) -{ - asm( "subs %Q0, %Q2, %Q4\n\t" - "sbcs %R0, %R2, %R4\n\t" - "sbcs %Q1, %Q3, %Q5\n\t" - "sbc %R1, %R3, %R5\n\t" - : "=r" (nl), "=r" (nh) - : "0" (nl), "1" (nh), "r" (ml), "r" (mh) - : "cc"); - *resh = nh; - *resl = nl; -} - -static inline void mul64to128(u64 *resh, u64 *resl, u64 n, u64 m) -{ - u32 nh, nl, mh, ml; - u64 rh, rma, rmb, rl; - - nl = n; - ml = m; - rl = (u64)nl * ml; - - nh = n >> 32; - rma = (u64)nh * ml; - - mh = m >> 32; - rmb = (u64)nl * mh; - rma += rmb; - - rh = (u64)nh * mh; - rh += ((u64)(rma < rmb) << 32) + (rma >> 32); - - rma <<= 32; - rl += rma; - rh += (rl < rma); - - *resl = rl; - *resh = rh; -} - -static inline void shift64left(u64 *resh, u64 *resl, u64 n) -{ - *resh = n >> 63; - *resl = n << 1; -} - -static inline u64 vfp_hi64multiply64(u64 n, u64 m) -{ - u64 rh, rl; - mul64to128(&rh, &rl, n, m); - return rh | (rl != 0); -} - -static inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m) -{ - u64 mh, ml, remh, reml, termh, terml, z; - - if (nh >= m) - return ~0ULL; - mh = m >> 32; - if (mh << 32 <= nh) { - z = 0xffffffff00000000ULL; - } else { - z = nh; - do_div(z, mh); - z <<= 32; - } - mul64to128(&termh, &terml, m, z); - sub128(&remh, &reml, nh, nl, termh, terml); - ml = m << 32; - while ((s64)remh < 0) { - z -= 0x100000000ULL; - add128(&remh, &reml, remh, reml, mh, ml); - } - remh = (remh << 32) | (reml >> 32); - if (mh << 32 <= remh) { - z |= 0xffffffff; - } else { - do_div(remh, mh); - z |= remh; - } - return z; -} - -/* - * Operations on unpacked elements - */ -#define vfp_sign_negate(sign) (sign ^ 0x8000) - -/* - * Single-precision - */ -struct vfp_single { - s16 exponent; - u16 sign; - u32 significand; -}; - -extern s32 vfp_get_float(unsigned int reg); -extern void vfp_put_float(s32 val, unsigned int reg); - -/* - * VFP_SINGLE_MANTISSA_BITS - number of bits in the mantissa - * VFP_SINGLE_EXPONENT_BITS - number of bits in the exponent - * VFP_SINGLE_LOW_BITS - number of low bits in the unpacked significand - * which are not propagated to the float upon packing. - */ -#define VFP_SINGLE_MANTISSA_BITS (23) -#define VFP_SINGLE_EXPONENT_BITS (8) -#define VFP_SINGLE_LOW_BITS (32 - VFP_SINGLE_MANTISSA_BITS - 2) -#define VFP_SINGLE_LOW_BITS_MASK ((1 << VFP_SINGLE_LOW_BITS) - 1) - -/* - * The bit in an unpacked float which indicates that it is a quiet NaN - */ -#define VFP_SINGLE_SIGNIFICAND_QNAN (1 << (VFP_SINGLE_MANTISSA_BITS - 1 + VFP_SINGLE_LOW_BITS)) - -/* - * Operations on packed single-precision numbers - */ -#define vfp_single_packed_sign(v) ((v) & 0x80000000) -#define vfp_single_packed_negate(v) ((v) ^ 0x80000000) -#define vfp_single_packed_abs(v) ((v) & ~0x80000000) -#define vfp_single_packed_exponent(v) (((v) >> VFP_SINGLE_MANTISSA_BITS) & ((1 << VFP_SINGLE_EXPONENT_BITS) - 1)) -#define vfp_single_packed_mantissa(v) ((v) & ((1 << VFP_SINGLE_MANTISSA_BITS) - 1)) - -/* - * Unpack a single-precision float. Note that this returns the magnitude - * of the single-precision float mantissa with the 1. if necessary, - * aligned to bit 30. - */ -static inline void vfp_single_unpack(struct vfp_single *s, s32 val) -{ - u32 significand; - - s->sign = vfp_single_packed_sign(val) >> 16, - s->exponent = vfp_single_packed_exponent(val); - - significand = (u32) val; - significand = (significand << (32 - VFP_SINGLE_MANTISSA_BITS)) >> 2; - if (s->exponent && s->exponent != 255) - significand |= 0x40000000; - s->significand = significand; -} - -/* - * Re-pack a single-precision float. This assumes that the float is - * already normalised such that the MSB is bit 30, _not_ bit 31. - */ -static inline s32 vfp_single_pack(struct vfp_single *s) -{ - u32 val; - val = (s->sign << 16) + - (s->exponent << VFP_SINGLE_MANTISSA_BITS) + - (s->significand >> VFP_SINGLE_LOW_BITS); - return (s32)val; -} - -#define VFP_NUMBER (1<<0) -#define VFP_ZERO (1<<1) -#define VFP_DENORMAL (1<<2) -#define VFP_INFINITY (1<<3) -#define VFP_NAN (1<<4) -#define VFP_NAN_SIGNAL (1<<5) - -#define VFP_QNAN (VFP_NAN) -#define VFP_SNAN (VFP_NAN|VFP_NAN_SIGNAL) - -static inline int vfp_single_type(struct vfp_single *s) -{ - int type = VFP_NUMBER; - if (s->exponent == 255) { - if (s->significand == 0) - type = VFP_INFINITY; - else if (s->significand & VFP_SINGLE_SIGNIFICAND_QNAN) - type = VFP_QNAN; - else - type = VFP_SNAN; - } else if (s->exponent == 0) { - if (s->significand == 0) - type |= VFP_ZERO; - else - type |= VFP_DENORMAL; - } - return type; -} - -#ifndef DEBUG -#define vfp_single_normaliseround(sd,vsd,fpscr,except,func) __vfp_single_normaliseround(sd,vsd,fpscr,except) -u32 __vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions); -#else -u32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func); -#endif - -/* - * Double-precision - */ -struct vfp_double { - s16 exponent; - u16 sign; - u64 significand; -}; - -/* - * VFP_REG_ZERO is a special register number for vfp_get_double - * which returns (double)0.0. This is useful for the compare with - * zero instructions. - */ -#ifdef CONFIG_VFPv3 -#define VFP_REG_ZERO 32 -#else -#define VFP_REG_ZERO 16 -#endif -extern u64 vfp_get_double(unsigned int reg); -extern void vfp_put_double(u64 val, unsigned int reg); - -#define VFP_DOUBLE_MANTISSA_BITS (52) -#define VFP_DOUBLE_EXPONENT_BITS (11) -#define VFP_DOUBLE_LOW_BITS (64 - VFP_DOUBLE_MANTISSA_BITS - 2) -#define VFP_DOUBLE_LOW_BITS_MASK ((1 << VFP_DOUBLE_LOW_BITS) - 1) - -/* - * The bit in an unpacked double which indicates that it is a quiet NaN - */ -#define VFP_DOUBLE_SIGNIFICAND_QNAN (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1 + VFP_DOUBLE_LOW_BITS)) - -/* - * Operations on packed single-precision numbers - */ -#define vfp_double_packed_sign(v) ((v) & (1ULL << 63)) -#define vfp_double_packed_negate(v) ((v) ^ (1ULL << 63)) -#define vfp_double_packed_abs(v) ((v) & ~(1ULL << 63)) -#define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1)) -#define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1)) - -/* - * Unpack a double-precision float. Note that this returns the magnitude - * of the double-precision float mantissa with the 1. if necessary, - * aligned to bit 62. - */ -static inline void vfp_double_unpack(struct vfp_double *s, s64 val) -{ - u64 significand; - - s->sign = vfp_double_packed_sign(val) >> 48; - s->exponent = vfp_double_packed_exponent(val); - - significand = (u64) val; - significand = (significand << (64 - VFP_DOUBLE_MANTISSA_BITS)) >> 2; - if (s->exponent && s->exponent != 2047) - significand |= (1ULL << 62); - s->significand = significand; -} - -/* - * Re-pack a double-precision float. This assumes that the float is - * already normalised such that the MSB is bit 30, _not_ bit 31. - */ -static inline s64 vfp_double_pack(struct vfp_double *s) -{ - u64 val; - val = ((u64)s->sign << 48) + - ((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) + - (s->significand >> VFP_DOUBLE_LOW_BITS); - return (s64)val; -} - -static inline int vfp_double_type(struct vfp_double *s) -{ - int type = VFP_NUMBER; - if (s->exponent == 2047) { - if (s->significand == 0) - type = VFP_INFINITY; - else if (s->significand & VFP_DOUBLE_SIGNIFICAND_QNAN) - type = VFP_QNAN; - else - type = VFP_SNAN; - } else if (s->exponent == 0) { - if (s->significand == 0) - type |= VFP_ZERO; - else - type |= VFP_DENORMAL; - } - return type; -} - -u32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func); - -u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand); - -/* - * A special flag to tell the normalisation code not to normalise. - */ -#define VFP_NAN_FLAG 0x100 - -/* - * A bit pattern used to indicate the initial (unset) value of the - * exception mask, in case nothing handles an instruction. This - * doesn't include the NAN flag, which get masked out before - * we check for an error. - */ -#define VFP_EXCEPTION_ERROR ((u32)-1 & ~VFP_NAN_FLAG) - -/* - * A flag to tell vfp instruction type. - * OP_SCALAR - this operation always operates in scalar mode - * OP_SD - the instruction exceptionally writes to a single precision result. - * OP_DD - the instruction exceptionally writes to a double precision result. - * OP_SM - the instruction exceptionally reads from a single precision operand. - */ -#define OP_SCALAR (1 << 0) -#define OP_SD (1 << 1) -#define OP_DD (1 << 1) -#define OP_SM (1 << 2) - -struct op { - u32 (* const fn)(int dd, int dn, int dm, u32 fpscr); - u32 flags; -}; - -extern void vfp_save_state(void *location, u32 fpexc); diff --git a/ANDROID_3.4.5/arch/arm/vfp/vfpdouble.c b/ANDROID_3.4.5/arch/arm/vfp/vfpdouble.c deleted file mode 100644 index 6cac43bd..00000000 --- a/ANDROID_3.4.5/arch/arm/vfp/vfpdouble.c +++ /dev/null @@ -1,1204 +0,0 @@ -/* - * linux/arch/arm/vfp/vfpdouble.c - * - * This code is derived in part from John R. Housers softfloat library, which - * carries the following notice: - * - * =========================================================================== - * This C source file is part of the SoftFloat IEC/IEEE Floating-point - * Arithmetic Package, Release 2. - * - * Written by John R. Hauser. This work was made possible in part by the - * International Computer Science Institute, located at Suite 600, 1947 Center - * Street, Berkeley, California 94704. Funding was partially provided by the - * National Science Foundation under grant MIP-9311980. The original version - * of this code was written as part of a project to build a fixed-point vector - * processor in collaboration with the University of California at Berkeley, - * overseen by Profs. Nelson Morgan and John Wawrzynek. More information - * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ - * arithmetic/softfloat.html'. - * - * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort - * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT - * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO - * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY - * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. - * - * Derivative works are acceptable, even for commercial purposes, so long as - * (1) they include prominent notice that the work is derivative, and (2) they - * include prominent notice akin to these three paragraphs for those parts of - * this code that are retained. - * =========================================================================== - */ -#include <linux/kernel.h> -#include <linux/bitops.h> - -#include <asm/div64.h> -#include <asm/vfp.h> - -#include "vfpinstr.h" -#include "vfp.h" - -static struct vfp_double vfp_double_default_qnan = { - .exponent = 2047, - .sign = 0, - .significand = VFP_DOUBLE_SIGNIFICAND_QNAN, -}; - -static void vfp_double_dump(const char *str, struct vfp_double *d) -{ - pr_debug("VFP: %s: sign=%d exponent=%d significand=%016llx\n", - str, d->sign != 0, d->exponent, d->significand); -} - -static void vfp_double_normalise_denormal(struct vfp_double *vd) -{ - int bits = 31 - fls(vd->significand >> 32); - if (bits == 31) - bits = 63 - fls(vd->significand); - - vfp_double_dump("normalise_denormal: in", vd); - - if (bits) { - vd->exponent -= bits - 1; - vd->significand <<= bits; - } - - vfp_double_dump("normalise_denormal: out", vd); -} - -u32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func) -{ - u64 significand, incr; - int exponent, shift, underflow; - u32 rmode; - - vfp_double_dump("pack: in", vd); - - /* - * Infinities and NaNs are a special case. - */ - if (vd->exponent == 2047 && (vd->significand == 0 || exceptions)) - goto pack; - - /* - * Special-case zero. - */ - if (vd->significand == 0) { - vd->exponent = 0; - goto pack; - } - - exponent = vd->exponent; - significand = vd->significand; - - shift = 32 - fls(significand >> 32); - if (shift == 32) - shift = 64 - fls(significand); - if (shift) { - exponent -= shift; - significand <<= shift; - } - -#ifdef DEBUG - vd->exponent = exponent; - vd->significand = significand; - vfp_double_dump("pack: normalised", vd); -#endif - - /* - * Tiny number? - */ - underflow = exponent < 0; - if (underflow) { - significand = vfp_shiftright64jamming(significand, -exponent); - exponent = 0; -#ifdef DEBUG - vd->exponent = exponent; - vd->significand = significand; - vfp_double_dump("pack: tiny number", vd); -#endif - if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1))) - underflow = 0; - } - - /* - * Select rounding increment. - */ - incr = 0; - rmode = fpscr & FPSCR_RMODE_MASK; - - if (rmode == FPSCR_ROUND_NEAREST) { - incr = 1ULL << VFP_DOUBLE_LOW_BITS; - if ((significand & (1ULL << (VFP_DOUBLE_LOW_BITS + 1))) == 0) - incr -= 1; - } else if (rmode == FPSCR_ROUND_TOZERO) { - incr = 0; - } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0)) - incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1; - - pr_debug("VFP: rounding increment = 0x%08llx\n", incr); - - /* - * Is our rounding going to overflow? - */ - if ((significand + incr) < significand) { - exponent += 1; - significand = (significand >> 1) | (significand & 1); - incr >>= 1; -#ifdef DEBUG - vd->exponent = exponent; - vd->significand = significand; - vfp_double_dump("pack: overflow", vd); -#endif - } - - /* - * If any of the low bits (which will be shifted out of the - * number) are non-zero, the result is inexact. - */ - if (significand & ((1 << (VFP_DOUBLE_LOW_BITS + 1)) - 1)) - exceptions |= FPSCR_IXC; - - /* - * Do our rounding. - */ - significand += incr; - - /* - * Infinity? - */ - if (exponent >= 2046) { - exceptions |= FPSCR_OFC | FPSCR_IXC; - if (incr == 0) { - vd->exponent = 2045; - vd->significand = 0x7fffffffffffffffULL; - } else { - vd->exponent = 2047; /* infinity */ - vd->significand = 0; - } - } else { - if (significand >> (VFP_DOUBLE_LOW_BITS + 1) == 0) - exponent = 0; - if (exponent || significand > 0x8000000000000000ULL) - underflow = 0; - if (underflow) - exceptions |= FPSCR_UFC; - vd->exponent = exponent; - vd->significand = significand >> 1; - } - - pack: - vfp_double_dump("pack: final", vd); - { - s64 d = vfp_double_pack(vd); - pr_debug("VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func, - dd, d, exceptions); - vfp_put_double(d, dd); - } - return exceptions; -} - -/* - * Propagate the NaN, setting exceptions if it is signalling. - * 'n' is always a NaN. 'm' may be a number, NaN or infinity. - */ -static u32 -vfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn, - struct vfp_double *vdm, u32 fpscr) -{ - struct vfp_double *nan; - int tn, tm = 0; - - tn = vfp_double_type(vdn); - - if (vdm) - tm = vfp_double_type(vdm); - - if (fpscr & FPSCR_DEFAULT_NAN) - /* - * Default NaN mode - always returns a quiet NaN - */ - nan = &vfp_double_default_qnan; - else { - /* - * Contemporary mode - select the first signalling - * NAN, or if neither are signalling, the first - * quiet NAN. - */ - if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN)) - nan = vdn; - else - nan = vdm; - /* - * Make the NaN quiet. - */ - nan->significand |= VFP_DOUBLE_SIGNIFICAND_QNAN; - } - - *vdd = *nan; - - /* - * If one was a signalling NAN, raise invalid operation. - */ - return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG; -} - -/* - * Extended operations - */ -static u32 vfp_double_fabs(int dd, int unused, int dm, u32 fpscr) -{ - vfp_put_double(vfp_double_packed_abs(vfp_get_double(dm)), dd); - return 0; -} - -static u32 vfp_double_fcpy(int dd, int unused, int dm, u32 fpscr) -{ - vfp_put_double(vfp_get_double(dm), dd); - return 0; -} - -static u32 vfp_double_fneg(int dd, int unused, int dm, u32 fpscr) -{ - vfp_put_double(vfp_double_packed_negate(vfp_get_double(dm)), dd); - return 0; -} - -static u32 vfp_double_fsqrt(int dd, int unused, int dm, u32 fpscr) -{ - struct vfp_double vdm, vdd; - int ret, tm; - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - tm = vfp_double_type(&vdm); - if (tm & (VFP_NAN|VFP_INFINITY)) { - struct vfp_double *vdp = &vdd; - - if (tm & VFP_NAN) - ret = vfp_propagate_nan(vdp, &vdm, NULL, fpscr); - else if (vdm.sign == 0) { - sqrt_copy: - vdp = &vdm; - ret = 0; - } else { - sqrt_invalid: - vdp = &vfp_double_default_qnan; - ret = FPSCR_IOC; - } - vfp_put_double(vfp_double_pack(vdp), dd); - return ret; - } - - /* - * sqrt(+/- 0) == +/- 0 - */ - if (tm & VFP_ZERO) - goto sqrt_copy; - - /* - * Normalise a denormalised number - */ - if (tm & VFP_DENORMAL) - vfp_double_normalise_denormal(&vdm); - - /* - * sqrt(<0) = invalid - */ - if (vdm.sign) - goto sqrt_invalid; - - vfp_double_dump("sqrt", &vdm); - - /* - * Estimate the square root. - */ - vdd.sign = 0; - vdd.exponent = ((vdm.exponent - 1023) >> 1) + 1023; - vdd.significand = (u64)vfp_estimate_sqrt_significand(vdm.exponent, vdm.significand >> 32) << 31; - - vfp_double_dump("sqrt estimate1", &vdd); - - vdm.significand >>= 1 + (vdm.exponent & 1); - vdd.significand += 2 + vfp_estimate_div128to64(vdm.significand, 0, vdd.significand); - - vfp_double_dump("sqrt estimate2", &vdd); - - /* - * And now adjust. - */ - if ((vdd.significand & VFP_DOUBLE_LOW_BITS_MASK) <= 5) { - if (vdd.significand < 2) { - vdd.significand = ~0ULL; - } else { - u64 termh, terml, remh, reml; - vdm.significand <<= 2; - mul64to128(&termh, &terml, vdd.significand, vdd.significand); - sub128(&remh, &reml, vdm.significand, 0, termh, terml); - while ((s64)remh < 0) { - vdd.significand -= 1; - shift64left(&termh, &terml, vdd.significand); - terml |= 1; - add128(&remh, &reml, remh, reml, termh, terml); - } - vdd.significand |= (remh | reml) != 0; - } - } - vdd.significand = vfp_shiftright64jamming(vdd.significand, 1); - - return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fsqrt"); -} - -/* - * Equal := ZC - * Less than := N - * Greater than := C - * Unordered := CV - */ -static u32 vfp_compare(int dd, int signal_on_qnan, int dm, u32 fpscr) -{ - s64 d, m; - u32 ret = 0; - - m = vfp_get_double(dm); - if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) { - ret |= FPSCR_C | FPSCR_V; - if (signal_on_qnan || !(vfp_double_packed_mantissa(m) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1)))) - /* - * Signalling NaN, or signalling on quiet NaN - */ - ret |= FPSCR_IOC; - } - - d = vfp_get_double(dd); - if (vfp_double_packed_exponent(d) == 2047 && vfp_double_packed_mantissa(d)) { - ret |= FPSCR_C | FPSCR_V; - if (signal_on_qnan || !(vfp_double_packed_mantissa(d) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1)))) - /* - * Signalling NaN, or signalling on quiet NaN - */ - ret |= FPSCR_IOC; - } - - if (ret == 0) { - if (d == m || vfp_double_packed_abs(d | m) == 0) { - /* - * equal - */ - ret |= FPSCR_Z | FPSCR_C; - } else if (vfp_double_packed_sign(d ^ m)) { - /* - * different signs - */ - if (vfp_double_packed_sign(d)) - /* - * d is negative, so d < m - */ - ret |= FPSCR_N; - else - /* - * d is positive, so d > m - */ - ret |= FPSCR_C; - } else if ((vfp_double_packed_sign(d) != 0) ^ (d < m)) { - /* - * d < m - */ - ret |= FPSCR_N; - } else if ((vfp_double_packed_sign(d) != 0) ^ (d > m)) { - /* - * d > m - */ - ret |= FPSCR_C; - } - } - - return ret; -} - -static u32 vfp_double_fcmp(int dd, int unused, int dm, u32 fpscr) -{ - return vfp_compare(dd, 0, dm, fpscr); -} - -static u32 vfp_double_fcmpe(int dd, int unused, int dm, u32 fpscr) -{ - return vfp_compare(dd, 1, dm, fpscr); -} - -static u32 vfp_double_fcmpz(int dd, int unused, int dm, u32 fpscr) -{ - return vfp_compare(dd, 0, VFP_REG_ZERO, fpscr); -} - -static u32 vfp_double_fcmpez(int dd, int unused, int dm, u32 fpscr) -{ - return vfp_compare(dd, 1, VFP_REG_ZERO, fpscr); -} - -static u32 vfp_double_fcvts(int sd, int unused, int dm, u32 fpscr) -{ - struct vfp_double vdm; - struct vfp_single vsd; - int tm; - u32 exceptions = 0; - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - - tm = vfp_double_type(&vdm); - - /* - * If we have a signalling NaN, signal invalid operation. - */ - if (tm == VFP_SNAN) - exceptions = FPSCR_IOC; - - if (tm & VFP_DENORMAL) - vfp_double_normalise_denormal(&vdm); - - vsd.sign = vdm.sign; - vsd.significand = vfp_hi64to32jamming(vdm.significand); - - /* - * If we have an infinity or a NaN, the exponent must be 255 - */ - if (tm & (VFP_INFINITY|VFP_NAN)) { - vsd.exponent = 255; - if (tm == VFP_QNAN) - vsd.significand |= VFP_SINGLE_SIGNIFICAND_QNAN; - goto pack_nan; - } else if (tm & VFP_ZERO) - vsd.exponent = 0; - else - vsd.exponent = vdm.exponent - (1023 - 127); - - return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fcvts"); - - pack_nan: - vfp_put_float(vfp_single_pack(&vsd), sd); - return exceptions; -} - -static u32 vfp_double_fuito(int dd, int unused, int dm, u32 fpscr) -{ - struct vfp_double vdm; - u32 m = vfp_get_float(dm); - - vdm.sign = 0; - vdm.exponent = 1023 + 63 - 1; - vdm.significand = (u64)m; - - return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fuito"); -} - -static u32 vfp_double_fsito(int dd, int unused, int dm, u32 fpscr) -{ - struct vfp_double vdm; - u32 m = vfp_get_float(dm); - - vdm.sign = (m & 0x80000000) >> 16; - vdm.exponent = 1023 + 63 - 1; - vdm.significand = vdm.sign ? -m : m; - - return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fsito"); -} - -static u32 vfp_double_ftoui(int sd, int unused, int dm, u32 fpscr) -{ - struct vfp_double vdm; - u32 d, exceptions = 0; - int rmode = fpscr & FPSCR_RMODE_MASK; - int tm; - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - - /* - * Do we have a denormalised number? - */ - tm = vfp_double_type(&vdm); - if (tm & VFP_DENORMAL) - exceptions |= FPSCR_IDC; - - if (tm & VFP_NAN) - vdm.sign = 0; - - if (vdm.exponent >= 1023 + 32) { - d = vdm.sign ? 0 : 0xffffffff; - exceptions = FPSCR_IOC; - } else if (vdm.exponent >= 1023 - 1) { - int shift = 1023 + 63 - vdm.exponent; - u64 rem, incr = 0; - - /* - * 2^0 <= m < 2^32-2^8 - */ - d = (vdm.significand << 1) >> shift; - rem = vdm.significand << (65 - shift); - - if (rmode == FPSCR_ROUND_NEAREST) { - incr = 0x8000000000000000ULL; - if ((d & 1) == 0) - incr -= 1; - } else if (rmode == FPSCR_ROUND_TOZERO) { - incr = 0; - } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) { - incr = ~0ULL; - } - - if ((rem + incr) < rem) { - if (d < 0xffffffff) - d += 1; - else - exceptions |= FPSCR_IOC; - } - - if (d && vdm.sign) { - d = 0; - exceptions |= FPSCR_IOC; - } else if (rem) - exceptions |= FPSCR_IXC; - } else { - d = 0; - if (vdm.exponent | vdm.significand) { - exceptions |= FPSCR_IXC; - if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) - d = 1; - else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) { - d = 0; - exceptions |= FPSCR_IOC; - } - } - } - - pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); - - vfp_put_float(d, sd); - - return exceptions; -} - -static u32 vfp_double_ftouiz(int sd, int unused, int dm, u32 fpscr) -{ - return vfp_double_ftoui(sd, unused, dm, FPSCR_ROUND_TOZERO); -} - -static u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr) -{ - struct vfp_double vdm; - u32 d, exceptions = 0; - int rmode = fpscr & FPSCR_RMODE_MASK; - int tm; - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - vfp_double_dump("VDM", &vdm); - - /* - * Do we have denormalised number? - */ - tm = vfp_double_type(&vdm); - if (tm & VFP_DENORMAL) - exceptions |= FPSCR_IDC; - - if (tm & VFP_NAN) { - d = 0; - exceptions |= FPSCR_IOC; - } else if (vdm.exponent >= 1023 + 32) { - d = 0x7fffffff; - if (vdm.sign) - d = ~d; - exceptions |= FPSCR_IOC; - } else if (vdm.exponent >= 1023 - 1) { - int shift = 1023 + 63 - vdm.exponent; /* 58 */ - u64 rem, incr = 0; - - d = (vdm.significand << 1) >> shift; - rem = vdm.significand << (65 - shift); - - if (rmode == FPSCR_ROUND_NEAREST) { - incr = 0x8000000000000000ULL; - if ((d & 1) == 0) - incr -= 1; - } else if (rmode == FPSCR_ROUND_TOZERO) { - incr = 0; - } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) { - incr = ~0ULL; - } - - if ((rem + incr) < rem && d < 0xffffffff) - d += 1; - if (d > 0x7fffffff + (vdm.sign != 0)) { - d = 0x7fffffff + (vdm.sign != 0); - exceptions |= FPSCR_IOC; - } else if (rem) - exceptions |= FPSCR_IXC; - - if (vdm.sign) - d = -d; - } else { - d = 0; - if (vdm.exponent | vdm.significand) { - exceptions |= FPSCR_IXC; - if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) - d = 1; - else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) - d = -1; - } - } - - pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); - - vfp_put_float((s32)d, sd); - - return exceptions; -} - -static u32 vfp_double_ftosiz(int dd, int unused, int dm, u32 fpscr) -{ - return vfp_double_ftosi(dd, unused, dm, FPSCR_ROUND_TOZERO); -} - - -static struct op fops_ext[32] = { - [FEXT_TO_IDX(FEXT_FCPY)] = { vfp_double_fcpy, 0 }, - [FEXT_TO_IDX(FEXT_FABS)] = { vfp_double_fabs, 0 }, - [FEXT_TO_IDX(FEXT_FNEG)] = { vfp_double_fneg, 0 }, - [FEXT_TO_IDX(FEXT_FSQRT)] = { vfp_double_fsqrt, 0 }, - [FEXT_TO_IDX(FEXT_FCMP)] = { vfp_double_fcmp, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FCMPE)] = { vfp_double_fcmpe, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FCMPZ)] = { vfp_double_fcmpz, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FCMPEZ)] = { vfp_double_fcmpez, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FCVT)] = { vfp_double_fcvts, OP_SCALAR|OP_SD }, - [FEXT_TO_IDX(FEXT_FUITO)] = { vfp_double_fuito, OP_SCALAR|OP_SM }, - [FEXT_TO_IDX(FEXT_FSITO)] = { vfp_double_fsito, OP_SCALAR|OP_SM }, - [FEXT_TO_IDX(FEXT_FTOUI)] = { vfp_double_ftoui, OP_SCALAR|OP_SD }, - [FEXT_TO_IDX(FEXT_FTOUIZ)] = { vfp_double_ftouiz, OP_SCALAR|OP_SD }, - [FEXT_TO_IDX(FEXT_FTOSI)] = { vfp_double_ftosi, OP_SCALAR|OP_SD }, - [FEXT_TO_IDX(FEXT_FTOSIZ)] = { vfp_double_ftosiz, OP_SCALAR|OP_SD }, -}; - - - - -static u32 -vfp_double_fadd_nonnumber(struct vfp_double *vdd, struct vfp_double *vdn, - struct vfp_double *vdm, u32 fpscr) -{ - struct vfp_double *vdp; - u32 exceptions = 0; - int tn, tm; - - tn = vfp_double_type(vdn); - tm = vfp_double_type(vdm); - - if (tn & tm & VFP_INFINITY) { - /* - * Two infinities. Are they different signs? - */ - if (vdn->sign ^ vdm->sign) { - /* - * different signs -> invalid - */ - exceptions = FPSCR_IOC; - vdp = &vfp_double_default_qnan; - } else { - /* - * same signs -> valid - */ - vdp = vdn; - } - } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) { - /* - * One infinity and one number -> infinity - */ - vdp = vdn; - } else { - /* - * 'n' is a NaN of some type - */ - return vfp_propagate_nan(vdd, vdn, vdm, fpscr); - } - *vdd = *vdp; - return exceptions; -} - -static u32 -vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn, - struct vfp_double *vdm, u32 fpscr) -{ - u32 exp_diff; - u64 m_sig; - - if (vdn->significand & (1ULL << 63) || - vdm->significand & (1ULL << 63)) { - pr_info("VFP: bad FP values in %s\n", __func__); - vfp_double_dump("VDN", vdn); - vfp_double_dump("VDM", vdm); - } - - /* - * Ensure that 'n' is the largest magnitude number. Note that - * if 'n' and 'm' have equal exponents, we do not swap them. - * This ensures that NaN propagation works correctly. - */ - if (vdn->exponent < vdm->exponent) { - struct vfp_double *t = vdn; - vdn = vdm; - vdm = t; - } - - /* - * Is 'n' an infinity or a NaN? Note that 'm' may be a number, - * infinity or a NaN here. - */ - if (vdn->exponent == 2047) - return vfp_double_fadd_nonnumber(vdd, vdn, vdm, fpscr); - - /* - * We have two proper numbers, where 'vdn' is the larger magnitude. - * - * Copy 'n' to 'd' before doing the arithmetic. - */ - *vdd = *vdn; - - /* - * Align 'm' with the result. - */ - exp_diff = vdn->exponent - vdm->exponent; - m_sig = vfp_shiftright64jamming(vdm->significand, exp_diff); - - /* - * If the signs are different, we are really subtracting. - */ - if (vdn->sign ^ vdm->sign) { - m_sig = vdn->significand - m_sig; - if ((s64)m_sig < 0) { - vdd->sign = vfp_sign_negate(vdd->sign); - m_sig = -m_sig; - } else if (m_sig == 0) { - vdd->sign = (fpscr & FPSCR_RMODE_MASK) == - FPSCR_ROUND_MINUSINF ? 0x8000 : 0; - } - } else { - m_sig += vdn->significand; - } - vdd->significand = m_sig; - - return 0; -} - -static u32 -vfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn, - struct vfp_double *vdm, u32 fpscr) -{ - vfp_double_dump("VDN", vdn); - vfp_double_dump("VDM", vdm); - - /* - * Ensure that 'n' is the largest magnitude number. Note that - * if 'n' and 'm' have equal exponents, we do not swap them. - * This ensures that NaN propagation works correctly. - */ - if (vdn->exponent < vdm->exponent) { - struct vfp_double *t = vdn; - vdn = vdm; - vdm = t; - pr_debug("VFP: swapping M <-> N\n"); - } - - vdd->sign = vdn->sign ^ vdm->sign; - - /* - * If 'n' is an infinity or NaN, handle it. 'm' may be anything. - */ - if (vdn->exponent == 2047) { - if (vdn->significand || (vdm->exponent == 2047 && vdm->significand)) - return vfp_propagate_nan(vdd, vdn, vdm, fpscr); - if ((vdm->exponent | vdm->significand) == 0) { - *vdd = vfp_double_default_qnan; - return FPSCR_IOC; - } - vdd->exponent = vdn->exponent; - vdd->significand = 0; - return 0; - } - - /* - * If 'm' is zero, the result is always zero. In this case, - * 'n' may be zero or a number, but it doesn't matter which. - */ - if ((vdm->exponent | vdm->significand) == 0) { - vdd->exponent = 0; - vdd->significand = 0; - return 0; - } - - /* - * We add 2 to the destination exponent for the same reason - * as the addition case - though this time we have +1 from - * each input operand. - */ - vdd->exponent = vdn->exponent + vdm->exponent - 1023 + 2; - vdd->significand = vfp_hi64multiply64(vdn->significand, vdm->significand); - - vfp_double_dump("VDD", vdd); - return 0; -} - -#define NEG_MULTIPLY (1 << 0) -#define NEG_SUBTRACT (1 << 1) - -static u32 -vfp_double_multiply_accumulate(int dd, int dn, int dm, u32 fpscr, u32 negate, char *func) -{ - struct vfp_double vdd, vdp, vdn, vdm; - u32 exceptions; - - vfp_double_unpack(&vdn, vfp_get_double(dn)); - if (vdn.exponent == 0 && vdn.significand) - vfp_double_normalise_denormal(&vdn); - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - if (vdm.exponent == 0 && vdm.significand) - vfp_double_normalise_denormal(&vdm); - - exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr); - if (negate & NEG_MULTIPLY) - vdp.sign = vfp_sign_negate(vdp.sign); - - vfp_double_unpack(&vdn, vfp_get_double(dd)); - if (negate & NEG_SUBTRACT) - vdn.sign = vfp_sign_negate(vdn.sign); - - exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr); - - return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, func); -} - -/* - * Standard operations - */ - -/* - * sd = sd + (sn * sm) - */ -static u32 vfp_double_fmac(int dd, int dn, int dm, u32 fpscr) -{ - return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, 0, "fmac"); -} - -/* - * sd = sd - (sn * sm) - */ -static u32 vfp_double_fnmac(int dd, int dn, int dm, u32 fpscr) -{ - return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac"); -} - -/* - * sd = -sd + (sn * sm) - */ -static u32 vfp_double_fmsc(int dd, int dn, int dm, u32 fpscr) -{ - return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc"); -} - -/* - * sd = -sd - (sn * sm) - */ -static u32 vfp_double_fnmsc(int dd, int dn, int dm, u32 fpscr) -{ - return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc"); -} - -/* - * sd = sn * sm - */ -static u32 vfp_double_fmul(int dd, int dn, int dm, u32 fpscr) -{ - struct vfp_double vdd, vdn, vdm; - u32 exceptions; - - vfp_double_unpack(&vdn, vfp_get_double(dn)); - if (vdn.exponent == 0 && vdn.significand) - vfp_double_normalise_denormal(&vdn); - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - if (vdm.exponent == 0 && vdm.significand) - vfp_double_normalise_denormal(&vdm); - - exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); - return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fmul"); -} - -/* - * sd = -(sn * sm) - */ -static u32 vfp_double_fnmul(int dd, int dn, int dm, u32 fpscr) -{ - struct vfp_double vdd, vdn, vdm; - u32 exceptions; - - vfp_double_unpack(&vdn, vfp_get_double(dn)); - if (vdn.exponent == 0 && vdn.significand) - vfp_double_normalise_denormal(&vdn); - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - if (vdm.exponent == 0 && vdm.significand) - vfp_double_normalise_denormal(&vdm); - - exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); - vdd.sign = vfp_sign_negate(vdd.sign); - - return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fnmul"); -} - -/* - * sd = sn + sm - */ -static u32 vfp_double_fadd(int dd, int dn, int dm, u32 fpscr) -{ - struct vfp_double vdd, vdn, vdm; - u32 exceptions; - - vfp_double_unpack(&vdn, vfp_get_double(dn)); - if (vdn.exponent == 0 && vdn.significand) - vfp_double_normalise_denormal(&vdn); - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - if (vdm.exponent == 0 && vdm.significand) - vfp_double_normalise_denormal(&vdm); - - exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr); - - return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fadd"); -} - -/* - * sd = sn - sm - */ -static u32 vfp_double_fsub(int dd, int dn, int dm, u32 fpscr) -{ - struct vfp_double vdd, vdn, vdm; - u32 exceptions; - - vfp_double_unpack(&vdn, vfp_get_double(dn)); - if (vdn.exponent == 0 && vdn.significand) - vfp_double_normalise_denormal(&vdn); - - vfp_double_unpack(&vdm, vfp_get_double(dm)); - if (vdm.exponent == 0 && vdm.significand) - vfp_double_normalise_denormal(&vdm); - - /* - * Subtraction is like addition, but with a negated operand. - */ - vdm.sign = vfp_sign_negate(vdm.sign); - - exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr); - - return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fsub"); -} - -/* - * sd = sn / sm - */ -static u32 vfp_double_fdiv(int dd, int dn, int dm, u32 fpscr) -{ - struct vfp_double vdd, vdn, vdm; - u32 exceptions = 0; - int tm, tn; - - vfp_double_unpack(&vdn, vfp_get_double(dn)); - vfp_double_unpack(&vdm, vfp_get_double(dm)); - - vdd.sign = vdn.sign ^ vdm.sign; - - tn = vfp_double_type(&vdn); - tm = vfp_double_type(&vdm); - - /* - * Is n a NAN? - */ - if (tn & VFP_NAN) - goto vdn_nan; - - /* - * Is m a NAN? - */ - if (tm & VFP_NAN) - goto vdm_nan; - - /* - * If n and m are infinity, the result is invalid - * If n and m are zero, the result is invalid - */ - if (tm & tn & (VFP_INFINITY|VFP_ZERO)) - goto invalid; - - /* - * If n is infinity, the result is infinity - */ - if (tn & VFP_INFINITY) - goto infinity; - - /* - * If m is zero, raise div0 exceptions - */ - if (tm & VFP_ZERO) - goto divzero; - - /* - * If m is infinity, or n is zero, the result is zero - */ - if (tm & VFP_INFINITY || tn & VFP_ZERO) - goto zero; - - if (tn & VFP_DENORMAL) - vfp_double_normalise_denormal(&vdn); - if (tm & VFP_DENORMAL) - vfp_double_normalise_denormal(&vdm); - - /* - * Ok, we have two numbers, we can perform division. - */ - vdd.exponent = vdn.exponent - vdm.exponent + 1023 - 1; - vdm.significand <<= 1; - if (vdm.significand <= (2 * vdn.significand)) { - vdn.significand >>= 1; - vdd.exponent++; - } - vdd.significand = vfp_estimate_div128to64(vdn.significand, 0, vdm.significand); - if ((vdd.significand & 0x1ff) <= 2) { - u64 termh, terml, remh, reml; - mul64to128(&termh, &terml, vdm.significand, vdd.significand); - sub128(&remh, &reml, vdn.significand, 0, termh, terml); - while ((s64)remh < 0) { - vdd.significand -= 1; - add128(&remh, &reml, remh, reml, 0, vdm.significand); - } - vdd.significand |= (reml != 0); - } - return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fdiv"); - - vdn_nan: - exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr); - pack: - vfp_put_double(vfp_double_pack(&vdd), dd); - return exceptions; - - vdm_nan: - exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr); - goto pack; - - zero: - vdd.exponent = 0; - vdd.significand = 0; - goto pack; - - divzero: - exceptions = FPSCR_DZC; - infinity: - vdd.exponent = 2047; - vdd.significand = 0; - goto pack; - - invalid: - vfp_put_double(vfp_double_pack(&vfp_double_default_qnan), dd); - return FPSCR_IOC; -} - -static struct op fops[16] = { - [FOP_TO_IDX(FOP_FMAC)] = { vfp_double_fmac, 0 }, - [FOP_TO_IDX(FOP_FNMAC)] = { vfp_double_fnmac, 0 }, - [FOP_TO_IDX(FOP_FMSC)] = { vfp_double_fmsc, 0 }, - [FOP_TO_IDX(FOP_FNMSC)] = { vfp_double_fnmsc, 0 }, - [FOP_TO_IDX(FOP_FMUL)] = { vfp_double_fmul, 0 }, - [FOP_TO_IDX(FOP_FNMUL)] = { vfp_double_fnmul, 0 }, - [FOP_TO_IDX(FOP_FADD)] = { vfp_double_fadd, 0 }, - [FOP_TO_IDX(FOP_FSUB)] = { vfp_double_fsub, 0 }, - [FOP_TO_IDX(FOP_FDIV)] = { vfp_double_fdiv, 0 }, -}; - -#define FREG_BANK(x) ((x) & 0x0c) -#define FREG_IDX(x) ((x) & 3) - -u32 vfp_double_cpdo(u32 inst, u32 fpscr) -{ - u32 op = inst & FOP_MASK; - u32 exceptions = 0; - unsigned int dest; - unsigned int dn = vfp_get_dn(inst); - unsigned int dm; - unsigned int vecitr, veclen, vecstride; - struct op *fop; - - vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK)); - - fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)]; - - /* - * fcvtds takes an sN register number as destination, not dN. - * It also always operates on scalars. - */ - if (fop->flags & OP_SD) - dest = vfp_get_sd(inst); - else - dest = vfp_get_dd(inst); - - /* - * f[us]ito takes a sN operand, not a dN operand. - */ - if (fop->flags & OP_SM) - dm = vfp_get_sm(inst); - else - dm = vfp_get_dm(inst); - - /* - * If destination bank is zero, vector length is always '1'. - * ARM DDI0100F C5.1.3, C5.3.2. - */ - if ((fop->flags & OP_SCALAR) || (FREG_BANK(dest) == 0)) - veclen = 0; - else - veclen = fpscr & FPSCR_LENGTH_MASK; - - pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, - (veclen >> FPSCR_LENGTH_BIT) + 1); - - if (!fop->fn) - goto invalid; - - for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) { - u32 except; - char type; - - type = fop->flags & OP_SD ? 's' : 'd'; - if (op == FOP_EXT) - pr_debug("VFP: itr%d (%c%u) = op[%u] (d%u)\n", - vecitr >> FPSCR_LENGTH_BIT, - type, dest, dn, dm); - else - pr_debug("VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n", - vecitr >> FPSCR_LENGTH_BIT, - type, dest, dn, FOP_TO_IDX(op), dm); - - except = fop->fn(dest, dn, dm, fpscr); - pr_debug("VFP: itr%d: exceptions=%08x\n", - vecitr >> FPSCR_LENGTH_BIT, except); - - exceptions |= except; - - /* - * CHECK: It appears to be undefined whether we stop when - * we encounter an exception. We continue. - */ - dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 3); - dn = FREG_BANK(dn) + ((FREG_IDX(dn) + vecstride) & 3); - if (FREG_BANK(dm) != 0) - dm = FREG_BANK(dm) + ((FREG_IDX(dm) + vecstride) & 3); - } - return exceptions; - - invalid: - return ~0; -} diff --git a/ANDROID_3.4.5/arch/arm/vfp/vfphw.S b/ANDROID_3.4.5/arch/arm/vfp/vfphw.S deleted file mode 100644 index 2d30c7f6..00000000 --- a/ANDROID_3.4.5/arch/arm/vfp/vfphw.S +++ /dev/null @@ -1,309 +0,0 @@ -/* - * linux/arch/arm/vfp/vfphw.S - * - * Copyright (C) 2004 ARM Limited. - * Written by Deep Blue Solutions Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This code is called from the kernel's undefined instruction trap. - * r9 holds the return address for successful handling. - * lr holds the return address for unrecognised instructions. - * r10 points at the start of the private FP workspace in the thread structure - * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h) - */ -#include <asm/thread_info.h> -#include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" - - .macro DBGSTR, str -#ifdef DEBUG - stmfd sp!, {r0-r3, ip, lr} - add r0, pc, #4 - bl printk - b 1f - .asciz "<7>VFP: \str\n" - .balign 4 -1: ldmfd sp!, {r0-r3, ip, lr} -#endif - .endm - - .macro DBGSTR1, str, arg -#ifdef DEBUG - stmfd sp!, {r0-r3, ip, lr} - mov r1, \arg - add r0, pc, #4 - bl printk - b 1f - .asciz "<7>VFP: \str\n" - .balign 4 -1: ldmfd sp!, {r0-r3, ip, lr} -#endif - .endm - - .macro DBGSTR3, str, arg1, arg2, arg3 -#ifdef DEBUG - stmfd sp!, {r0-r3, ip, lr} - mov r3, \arg3 - mov r2, \arg2 - mov r1, \arg1 - add r0, pc, #4 - bl printk - b 1f - .asciz "<7>VFP: \str\n" - .balign 4 -1: ldmfd sp!, {r0-r3, ip, lr} -#endif - .endm - - -@ VFP hardware support entry point. -@ -@ r0 = faulted instruction -@ r2 = faulted PC+4 -@ r9 = successful return -@ r10 = vfp_state union -@ r11 = CPU number -@ lr = failure return - -ENTRY(vfp_support_entry) - DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10 - - VFPFMRX r1, FPEXC @ Is the VFP enabled? - DBGSTR1 "fpexc %08x", r1 - tst r1, #FPEXC_EN - bne look_for_VFP_exceptions @ VFP is already enabled - - DBGSTR1 "enable %x", r10 - ldr r3, vfp_current_hw_state_address - orr r1, r1, #FPEXC_EN @ user FPEXC has the enable bit set - ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer - bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled - cmp r4, r10 @ this thread owns the hw context? -#ifndef CONFIG_SMP - @ For UP, checking that this thread owns the hw context is - @ sufficient to determine that the hardware state is valid. - beq vfp_hw_state_valid - - @ On UP, we lazily save the VFP context. As a different - @ thread wants ownership of the VFP hardware, save the old - @ state if there was a previous (valid) owner. - - VFPFMXR FPEXC, r5 @ enable VFP, disable any pending - @ exceptions, so we can get at the - @ rest of it - - DBGSTR1 "save old state %p", r4 - cmp r4, #0 @ if the vfp_current_hw_state is NULL - beq vfp_reload_hw @ then the hw state needs reloading - VFPFSTMIA r4, r5 @ save the working registers - VFPFMRX r5, FPSCR @ current status -#ifndef CONFIG_CPU_FEROCEON - tst r1, #FPEXC_EX @ is there additional state to save? - beq 1f - VFPFMRX r6, FPINST @ FPINST (only if FPEXC.EX is set) - tst r1, #FPEXC_FP2V @ is there an FPINST2 to read? - beq 1f - VFPFMRX r8, FPINST2 @ FPINST2 if needed (and present) -1: -#endif - stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 -vfp_reload_hw: - -#else - @ For SMP, if this thread does not own the hw context, then we - @ need to reload it. No need to save the old state as on SMP, - @ we always save the state when we switch away from a thread. - bne vfp_reload_hw - - @ This thread has ownership of the current hardware context. - @ However, it may have been migrated to another CPU, in which - @ case the saved state is newer than the hardware context. - @ Check this by looking at the CPU number which the state was - @ last loaded onto. - ldr ip, [r10, #VFP_CPU] - teq ip, r11 - beq vfp_hw_state_valid - -vfp_reload_hw: - @ We're loading this threads state into the VFP hardware. Update - @ the CPU number which contains the most up to date VFP context. - str r11, [r10, #VFP_CPU] - - VFPFMXR FPEXC, r5 @ enable VFP, disable any pending - @ exceptions, so we can get at the - @ rest of it -#endif - - DBGSTR1 "load state %p", r10 - str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer - @ Load the saved state back into the VFP - VFPFLDMIA r10, r5 @ reload the working registers while - @ FPEXC is in a safe state - ldmia r10, {r1, r5, r6, r8} @ load FPEXC, FPSCR, FPINST, FPINST2 -#ifndef CONFIG_CPU_FEROCEON - tst r1, #FPEXC_EX @ is there additional state to restore? - beq 1f - VFPFMXR FPINST, r6 @ restore FPINST (only if FPEXC.EX is set) - tst r1, #FPEXC_FP2V @ is there an FPINST2 to write? - beq 1f - VFPFMXR FPINST2, r8 @ FPINST2 if needed (and present) -1: -#endif - VFPFMXR FPSCR, r5 @ restore status - -@ The context stored in the VFP hardware is up to date with this thread -vfp_hw_state_valid: - tst r1, #FPEXC_EX - bne process_exception @ might as well handle the pending - @ exception before retrying branch - @ out before setting an FPEXC that - @ stops us reading stuff - VFPFMXR FPEXC, r1 @ restore FPEXC last - sub r2, r2, #4 - str r2, [sp, #S_PC] @ retry the instruction -#ifdef CONFIG_PREEMPT - get_thread_info r10 - ldr r4, [r10, #TI_PREEMPT] @ get preempt count - sub r11, r4, #1 @ decrement it - str r11, [r10, #TI_PREEMPT] -#endif - mov pc, r9 @ we think we have handled things - - -look_for_VFP_exceptions: - @ Check for synchronous or asynchronous exception - tst r1, #FPEXC_EX | FPEXC_DEX - bne process_exception - @ On some implementations of the VFP subarch 1, setting FPSCR.IXE - @ causes all the CDP instructions to be bounced synchronously without - @ setting the FPEXC.EX bit - VFPFMRX r5, FPSCR - tst r5, #FPSCR_IXE - bne process_exception - - @ Fall into hand on to next handler - appropriate coproc instr - @ not recognised by VFP - - DBGSTR "not VFP" -#ifdef CONFIG_PREEMPT - get_thread_info r10 - ldr r4, [r10, #TI_PREEMPT] @ get preempt count - sub r11, r4, #1 @ decrement it - str r11, [r10, #TI_PREEMPT] -#endif - mov pc, lr - -process_exception: - DBGSTR "bounce" - mov r2, sp @ nothing stacked - regdump is at TOS - mov lr, r9 @ setup for a return to the user code. - - @ Now call the C code to package up the bounce to the support code - @ r0 holds the trigger instruction - @ r1 holds the FPEXC value - @ r2 pointer to register dump - b VFP_bounce @ we have handled this - the support - @ code will raise an exception if - @ required. If not, the user code will - @ retry the faulted instruction -ENDPROC(vfp_support_entry) - -ENTRY(vfp_save_state) - @ Save the current VFP state - @ r0 - save location - @ r1 - FPEXC - DBGSTR1 "save VFP state %p", r0 - VFPFSTMIA r0, r2 @ save the working registers - VFPFMRX r2, FPSCR @ current status - tst r1, #FPEXC_EX @ is there additional state to save? - beq 1f - VFPFMRX r3, FPINST @ FPINST (only if FPEXC.EX is set) - tst r1, #FPEXC_FP2V @ is there an FPINST2 to read? - beq 1f - VFPFMRX r12, FPINST2 @ FPINST2 if needed (and present) -1: - stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2 - mov pc, lr -ENDPROC(vfp_save_state) - - .align -vfp_current_hw_state_address: - .word vfp_current_hw_state - - .macro tbl_branch, base, tmp, shift -#ifdef CONFIG_THUMB2_KERNEL - adr \tmp, 1f - add \tmp, \tmp, \base, lsl \shift - mov pc, \tmp -#else - add pc, pc, \base, lsl \shift - mov r0, r0 -#endif -1: - .endm - -ENTRY(vfp_get_float) - tbl_branch r0, r3, #3 - .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 -1: mrc p10, 0, r0, c\dr, c0, 0 @ fmrs r0, s0 - mov pc, lr - .org 1b + 8 -1: mrc p10, 0, r0, c\dr, c0, 4 @ fmrs r0, s1 - mov pc, lr - .org 1b + 8 - .endr -ENDPROC(vfp_get_float) - -ENTRY(vfp_put_float) - tbl_branch r1, r3, #3 - .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 -1: mcr p10, 0, r0, c\dr, c0, 0 @ fmsr r0, s0 - mov pc, lr - .org 1b + 8 -1: mcr p10, 0, r0, c\dr, c0, 4 @ fmsr r0, s1 - mov pc, lr - .org 1b + 8 - .endr -ENDPROC(vfp_put_float) - -ENTRY(vfp_get_double) - tbl_branch r0, r3, #3 - .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 -1: fmrrd r0, r1, d\dr - mov pc, lr - .org 1b + 8 - .endr -#ifdef CONFIG_VFPv3 - @ d16 - d31 registers - .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 -1: mrrc p11, 3, r0, r1, c\dr @ fmrrd r0, r1, d\dr - mov pc, lr - .org 1b + 8 - .endr -#endif - - @ virtual register 16 (or 32 if VFPv3) for compare with zero - mov r0, #0 - mov r1, #0 - mov pc, lr -ENDPROC(vfp_get_double) - -ENTRY(vfp_put_double) - tbl_branch r2, r3, #3 - .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 -1: fmdrr d\dr, r0, r1 - mov pc, lr - .org 1b + 8 - .endr -#ifdef CONFIG_VFPv3 - @ d16 - d31 registers - .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 -1: mcrr p11, 3, r0, r1, c\dr @ fmdrr r0, r1, d\dr - mov pc, lr - .org 1b + 8 - .endr -#endif -ENDPROC(vfp_put_double) diff --git a/ANDROID_3.4.5/arch/arm/vfp/vfpinstr.h b/ANDROID_3.4.5/arch/arm/vfp/vfpinstr.h deleted file mode 100644 index 15b95b5a..00000000 --- a/ANDROID_3.4.5/arch/arm/vfp/vfpinstr.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * linux/arch/arm/vfp/vfpinstr.h - * - * Copyright (C) 2004 ARM Limited. - * Written by Deep Blue Solutions Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * VFP instruction masks. - */ -#define INST_CPRTDO(inst) (((inst) & 0x0f000000) == 0x0e000000) -#define INST_CPRT(inst) ((inst) & (1 << 4)) -#define INST_CPRT_L(inst) ((inst) & (1 << 20)) -#define INST_CPRT_Rd(inst) (((inst) & (15 << 12)) >> 12) -#define INST_CPRT_OP(inst) (((inst) >> 21) & 7) -#define INST_CPNUM(inst) ((inst) & 0xf00) -#define CPNUM(cp) ((cp) << 8) - -#define FOP_MASK (0x00b00040) -#define FOP_FMAC (0x00000000) -#define FOP_FNMAC (0x00000040) -#define FOP_FMSC (0x00100000) -#define FOP_FNMSC (0x00100040) -#define FOP_FMUL (0x00200000) -#define FOP_FNMUL (0x00200040) -#define FOP_FADD (0x00300000) -#define FOP_FSUB (0x00300040) -#define FOP_FDIV (0x00800000) -#define FOP_EXT (0x00b00040) - -#define FOP_TO_IDX(inst) ((inst & 0x00b00000) >> 20 | (inst & (1 << 6)) >> 4) - -#define FEXT_MASK (0x000f0080) -#define FEXT_FCPY (0x00000000) -#define FEXT_FABS (0x00000080) -#define FEXT_FNEG (0x00010000) -#define FEXT_FSQRT (0x00010080) -#define FEXT_FCMP (0x00040000) -#define FEXT_FCMPE (0x00040080) -#define FEXT_FCMPZ (0x00050000) -#define FEXT_FCMPEZ (0x00050080) -#define FEXT_FCVT (0x00070080) -#define FEXT_FUITO (0x00080000) -#define FEXT_FSITO (0x00080080) -#define FEXT_FTOUI (0x000c0000) -#define FEXT_FTOUIZ (0x000c0080) -#define FEXT_FTOSI (0x000d0000) -#define FEXT_FTOSIZ (0x000d0080) - -#define FEXT_TO_IDX(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7) - -#define vfp_get_sd(inst) ((inst & 0x0000f000) >> 11 | (inst & (1 << 22)) >> 22) -#define vfp_get_dd(inst) ((inst & 0x0000f000) >> 12 | (inst & (1 << 22)) >> 18) -#define vfp_get_sm(inst) ((inst & 0x0000000f) << 1 | (inst & (1 << 5)) >> 5) -#define vfp_get_dm(inst) ((inst & 0x0000000f) | (inst & (1 << 5)) >> 1) -#define vfp_get_sn(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7) -#define vfp_get_dn(inst) ((inst & 0x000f0000) >> 16 | (inst & (1 << 7)) >> 3) - -#define vfp_single(inst) (((inst) & 0x0000f00) == 0xa00) - -#define FPSCR_N (1 << 31) -#define FPSCR_Z (1 << 30) -#define FPSCR_C (1 << 29) -#define FPSCR_V (1 << 28) - -/* - * Since we aren't building with -mfpu=vfp, we need to code - * these instructions using their MRC/MCR equivalents. - */ -#define vfpreg(_vfp_) #_vfp_ - -#define fmrx(_vfp_) ({ \ - u32 __v; \ - asm("mrc p10, 7, %0, " vfpreg(_vfp_) ", cr0, 0 @ fmrx %0, " #_vfp_ \ - : "=r" (__v) : : "cc"); \ - __v; \ - }) - -#define fmxr(_vfp_,_var_) \ - asm("mcr p10, 7, %0, " vfpreg(_vfp_) ", cr0, 0 @ fmxr " #_vfp_ ", %0" \ - : : "r" (_var_) : "cc") - -u32 vfp_single_cpdo(u32 inst, u32 fpscr); -u32 vfp_single_cprt(u32 inst, u32 fpscr, struct pt_regs *regs); - -u32 vfp_double_cpdo(u32 inst, u32 fpscr); diff --git a/ANDROID_3.4.5/arch/arm/vfp/vfpmodule.c b/ANDROID_3.4.5/arch/arm/vfp/vfpmodule.c deleted file mode 100644 index 1ef803aa..00000000 --- a/ANDROID_3.4.5/arch/arm/vfp/vfpmodule.c +++ /dev/null @@ -1,729 +0,0 @@ -/* - * linux/arch/arm/vfp/vfpmodule.c - * - * Copyright (C) 2004 ARM Limited. - * Written by Deep Blue Solutions Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include <linux/types.h> -#include <linux/cpu.h> -#include <linux/cpu_pm.h> -#include <linux/hardirq.h> -#include <linux/kernel.h> -#include <linux/notifier.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/smp.h> -#include <linux/init.h> -#include <linux/uaccess.h> -#include <linux/user.h> - -#include <asm/cp15.h> -#include <asm/cputype.h> -#include <asm/system_info.h> -#include <asm/thread_notify.h> -#include <asm/vfp.h> - -#include "vfpinstr.h" -#include "vfp.h" - -/* - * Our undef handlers (in entry.S) - */ -void vfp_testing_entry(void); -void vfp_support_entry(void); -void vfp_null_entry(void); - -void (*vfp_vector)(void) = vfp_null_entry; - -/* - * Dual-use variable. - * Used in startup: set to non-zero if VFP checks fail - * After startup, holds VFP architecture - */ -unsigned int VFP_arch; - -/* - * The pointer to the vfpstate structure of the thread which currently - * owns the context held in the VFP hardware, or NULL if the hardware - * context is invalid. - * - * For UP, this is sufficient to tell which thread owns the VFP context. - * However, for SMP, we also need to check the CPU number stored in the - * saved state too to catch migrations. - */ -union vfp_state *vfp_current_hw_state[NR_CPUS]; - -/* - * Is 'thread's most up to date state stored in this CPUs hardware? - * Must be called from non-preemptible context. - */ -static bool vfp_state_in_hw(unsigned int cpu, struct thread_info *thread) -{ -#ifdef CONFIG_SMP - if (thread->vfpstate.hard.cpu != cpu) - return false; -#endif - return vfp_current_hw_state[cpu] == &thread->vfpstate; -} - -/* - * Force a reload of the VFP context from the thread structure. We do - * this by ensuring that access to the VFP hardware is disabled, and - * clear vfp_current_hw_state. Must be called from non-preemptible context. - */ -static void vfp_force_reload(unsigned int cpu, struct thread_info *thread) -{ - if (vfp_state_in_hw(cpu, thread)) { - fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); - vfp_current_hw_state[cpu] = NULL; - } -#ifdef CONFIG_SMP - thread->vfpstate.hard.cpu = NR_CPUS; -#endif -} - -/* - * Per-thread VFP initialization. - */ -static void vfp_thread_flush(struct thread_info *thread) -{ - union vfp_state *vfp = &thread->vfpstate; - unsigned int cpu; - - /* - * Disable VFP to ensure we initialize it first. We must ensure - * that the modification of vfp_current_hw_state[] and hardware - * disable are done for the same CPU and without preemption. - * - * Do this first to ensure that preemption won't overwrite our - * state saving should access to the VFP be enabled at this point. - */ - cpu = get_cpu(); - if (vfp_current_hw_state[cpu] == vfp) - vfp_current_hw_state[cpu] = NULL; - fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); - put_cpu(); - - memset(vfp, 0, sizeof(union vfp_state)); - - vfp->hard.fpexc = FPEXC_EN; - vfp->hard.fpscr = FPSCR_ROUND_NEAREST; -#ifdef CONFIG_SMP - vfp->hard.cpu = NR_CPUS; -#endif -} - -static void vfp_thread_exit(struct thread_info *thread) -{ - /* release case: Per-thread VFP cleanup. */ - union vfp_state *vfp = &thread->vfpstate; - unsigned int cpu = get_cpu(); - - if (vfp_current_hw_state[cpu] == vfp) - vfp_current_hw_state[cpu] = NULL; - put_cpu(); -} - -static void vfp_thread_copy(struct thread_info *thread) -{ - struct thread_info *parent = current_thread_info(); - - vfp_sync_hwstate(parent); - thread->vfpstate = parent->vfpstate; -#ifdef CONFIG_SMP - thread->vfpstate.hard.cpu = NR_CPUS; -#endif -} - -/* - * When this function is called with the following 'cmd's, the following - * is true while this function is being run: - * THREAD_NOFTIFY_SWTICH: - * - the previously running thread will not be scheduled onto another CPU. - * - the next thread to be run (v) will not be running on another CPU. - * - thread->cpu is the local CPU number - * - not preemptible as we're called in the middle of a thread switch - * THREAD_NOTIFY_FLUSH: - * - the thread (v) will be running on the local CPU, so - * v === current_thread_info() - * - thread->cpu is the local CPU number at the time it is accessed, - * but may change at any time. - * - we could be preempted if tree preempt rcu is enabled, so - * it is unsafe to use thread->cpu. - * THREAD_NOTIFY_EXIT - * - the thread (v) will be running on the local CPU, so - * v === current_thread_info() - * - thread->cpu is the local CPU number at the time it is accessed, - * but may change at any time. - * - we could be preempted if tree preempt rcu is enabled, so - * it is unsafe to use thread->cpu. - */ -static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) -{ - struct thread_info *thread = v; - u32 fpexc; -#ifdef CONFIG_SMP - unsigned int cpu; -#endif - - switch (cmd) { - case THREAD_NOTIFY_SWITCH: - fpexc = fmrx(FPEXC); - -#ifdef CONFIG_SMP - cpu = thread->cpu; - - /* - * On SMP, if VFP is enabled, save the old state in - * case the thread migrates to a different CPU. The - * restoring is done lazily. - */ - if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) - vfp_save_state(vfp_current_hw_state[cpu], fpexc); -#endif - - /* - * Always disable VFP so we can lazily save/restore the - * old state. - */ - fmxr(FPEXC, fpexc & ~FPEXC_EN); - break; - - case THREAD_NOTIFY_FLUSH: - vfp_thread_flush(thread); - break; - - case THREAD_NOTIFY_EXIT: - vfp_thread_exit(thread); - break; - - case THREAD_NOTIFY_COPY: - vfp_thread_copy(thread); - break; - } - - return NOTIFY_DONE; -} - -static struct notifier_block vfp_notifier_block = { - .notifier_call = vfp_notifier, -}; - -/* - * Raise a SIGFPE for the current process. - * sicode describes the signal being raised. - */ -static void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs) -{ - siginfo_t info; - - memset(&info, 0, sizeof(info)); - - info.si_signo = SIGFPE; - info.si_code = sicode; - info.si_addr = (void __user *)(instruction_pointer(regs) - 4); - - /* - * This is the same as NWFPE, because it's not clear what - * this is used for - */ - current->thread.error_code = 0; - current->thread.trap_no = 6; - - send_sig_info(SIGFPE, &info, current); -} - -static void vfp_panic(char *reason, u32 inst) -{ - int i; - - printk(KERN_ERR "VFP: Error: %s\n", reason); - printk(KERN_ERR "VFP: EXC 0x%08x SCR 0x%08x INST 0x%08x\n", - fmrx(FPEXC), fmrx(FPSCR), inst); - for (i = 0; i < 32; i += 2) - printk(KERN_ERR "VFP: s%2u: 0x%08x s%2u: 0x%08x\n", - i, vfp_get_float(i), i+1, vfp_get_float(i+1)); -} - -/* - * Process bitmask of exception conditions. - */ -static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs) -{ - int si_code = 0; - - pr_debug("VFP: raising exceptions %08x\n", exceptions); - - if (exceptions == VFP_EXCEPTION_ERROR) { - vfp_panic("unhandled bounce", inst); - vfp_raise_sigfpe(0, regs); - return; - } - - /* - * If any of the status flags are set, update the FPSCR. - * Comparison instructions always return at least one of - * these flags set. - */ - if (exceptions & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V)) - fpscr &= ~(FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V); - - fpscr |= exceptions; - - fmxr(FPSCR, fpscr); - -#define RAISE(stat,en,sig) \ - if (exceptions & stat && fpscr & en) \ - si_code = sig; - - /* - * These are arranged in priority order, least to highest. - */ - RAISE(FPSCR_DZC, FPSCR_DZE, FPE_FLTDIV); - RAISE(FPSCR_IXC, FPSCR_IXE, FPE_FLTRES); - RAISE(FPSCR_UFC, FPSCR_UFE, FPE_FLTUND); - RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF); - RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV); - - if (si_code) - vfp_raise_sigfpe(si_code, regs); -} - -/* - * Emulate a VFP instruction. - */ -static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs) -{ - u32 exceptions = VFP_EXCEPTION_ERROR; - - pr_debug("VFP: emulate: INST=0x%08x SCR=0x%08x\n", inst, fpscr); - - if (INST_CPRTDO(inst)) { - if (!INST_CPRT(inst)) { - /* - * CPDO - */ - if (vfp_single(inst)) { - exceptions = vfp_single_cpdo(inst, fpscr); - } else { - exceptions = vfp_double_cpdo(inst, fpscr); - } - } else { - /* - * A CPRT instruction can not appear in FPINST2, nor - * can it cause an exception. Therefore, we do not - * have to emulate it. - */ - } - } else { - /* - * A CPDT instruction can not appear in FPINST2, nor can - * it cause an exception. Therefore, we do not have to - * emulate it. - */ - } - return exceptions & ~VFP_NAN_FLAG; -} - -/* - * Package up a bounce condition. - */ -void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) -{ - u32 fpscr, orig_fpscr, fpsid, exceptions; - - pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc); - - /* - * At this point, FPEXC can have the following configuration: - * - * EX DEX IXE - * 0 1 x - synchronous exception - * 1 x 0 - asynchronous exception - * 1 x 1 - sychronous on VFP subarch 1 and asynchronous on later - * 0 0 1 - synchronous on VFP9 (non-standard subarch 1 - * implementation), undefined otherwise - * - * Clear various bits and enable access to the VFP so we can - * handle the bounce. - */ - fmxr(FPEXC, fpexc & ~(FPEXC_EX|FPEXC_DEX|FPEXC_FP2V|FPEXC_VV|FPEXC_TRAP_MASK)); - - fpsid = fmrx(FPSID); - orig_fpscr = fpscr = fmrx(FPSCR); - - /* - * Check for the special VFP subarch 1 and FPSCR.IXE bit case - */ - if ((fpsid & FPSID_ARCH_MASK) == (1 << FPSID_ARCH_BIT) - && (fpscr & FPSCR_IXE)) { - /* - * Synchronous exception, emulate the trigger instruction - */ - goto emulate; - } - - if (fpexc & FPEXC_EX) { -#ifndef CONFIG_CPU_FEROCEON - /* - * Asynchronous exception. The instruction is read from FPINST - * and the interrupted instruction has to be restarted. - */ - trigger = fmrx(FPINST); - regs->ARM_pc -= 4; -#endif - } else if (!(fpexc & FPEXC_DEX)) { - /* - * Illegal combination of bits. It can be caused by an - * unallocated VFP instruction but with FPSCR.IXE set and not - * on VFP subarch 1. - */ - vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs); - goto exit; - } - - /* - * Modify fpscr to indicate the number of iterations remaining. - * If FPEXC.EX is 0, FPEXC.DEX is 1 and the FPEXC.VV bit indicates - * whether FPEXC.VECITR or FPSCR.LEN is used. - */ - if (fpexc & (FPEXC_EX | FPEXC_VV)) { - u32 len; - - len = fpexc + (1 << FPEXC_LENGTH_BIT); - - fpscr &= ~FPSCR_LENGTH_MASK; - fpscr |= (len & FPEXC_LENGTH_MASK) << (FPSCR_LENGTH_BIT - FPEXC_LENGTH_BIT); - } - - /* - * Handle the first FP instruction. We used to take note of the - * FPEXC bounce reason, but this appears to be unreliable. - * Emulate the bounced instruction instead. - */ - exceptions = vfp_emulate_instruction(trigger, fpscr, regs); - if (exceptions) - vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); - - /* - * If there isn't a second FP instruction, exit now. Note that - * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1. - */ - if (fpexc ^ (FPEXC_EX | FPEXC_FP2V)) - goto exit; - - /* - * The barrier() here prevents fpinst2 being read - * before the condition above. - */ - barrier(); - trigger = fmrx(FPINST2); - - emulate: - exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs); - if (exceptions) - vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); - exit: - preempt_enable(); -} - -static void vfp_enable(void *unused) -{ - u32 access; - - BUG_ON(preemptible()); - access = get_copro_access(); - - /* - * Enable full access to VFP (cp10 and cp11) - */ - set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); -} - -#ifdef CONFIG_CPU_PM -static int vfp_pm_suspend(void) -{ - struct thread_info *ti = current_thread_info(); - u32 fpexc = fmrx(FPEXC); - - /* if vfp is on, then save state for resumption */ - if (fpexc & FPEXC_EN) { - printk(KERN_DEBUG "%s: saving vfp state\n", __func__); - vfp_save_state(&ti->vfpstate, fpexc); - - /* disable, just in case */ - fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); - } else if (vfp_current_hw_state[ti->cpu]) { -#ifndef CONFIG_SMP - fmxr(FPEXC, fpexc | FPEXC_EN); - vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc); - fmxr(FPEXC, fpexc); -#endif - } - - /* clear any information we had about last context state */ - vfp_current_hw_state[ti->cpu] = NULL; - - return 0; -} - -static void vfp_pm_resume(void) -{ - /* ensure we have access to the vfp */ - vfp_enable(NULL); - - /* and disable it to ensure the next usage restores the state */ - fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); -} - -static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd, - void *v) -{ - switch (cmd) { - case CPU_PM_ENTER: - vfp_pm_suspend(); - break; - case CPU_PM_ENTER_FAILED: - case CPU_PM_EXIT: - vfp_pm_resume(); - break; - } - return NOTIFY_OK; -} - -static struct notifier_block vfp_cpu_pm_notifier_block = { - .notifier_call = vfp_cpu_pm_notifier, -}; - -static void vfp_pm_init(void) -{ - cpu_pm_register_notifier(&vfp_cpu_pm_notifier_block); -} - -#else -static inline void vfp_pm_init(void) { } -#endif /* CONFIG_CPU_PM */ - -/* - * Ensure that the VFP state stored in 'thread->vfpstate' is up to date - * with the hardware state. - */ -void vfp_sync_hwstate(struct thread_info *thread) -{ - unsigned int cpu = get_cpu(); - - if (vfp_state_in_hw(cpu, thread)) { - u32 fpexc = fmrx(FPEXC); - - /* - * Save the last VFP state on this CPU. - */ - fmxr(FPEXC, fpexc | FPEXC_EN); - vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN); - fmxr(FPEXC, fpexc); - } - - put_cpu(); -} - -/* Ensure that the thread reloads the hardware VFP state on the next use. */ -void vfp_flush_hwstate(struct thread_info *thread) -{ - unsigned int cpu = get_cpu(); - - vfp_force_reload(cpu, thread); - - put_cpu(); -} - -/* - * Save the current VFP state into the provided structures and prepare - * for entry into a new function (signal handler). - */ -int vfp_preserve_user_clear_hwstate(struct user_vfp __user *ufp, - struct user_vfp_exc __user *ufp_exc) -{ - struct thread_info *thread = current_thread_info(); - struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; - int err = 0; - - /* Ensure that the saved hwstate is up-to-date. */ - vfp_sync_hwstate(thread); - - /* - * Copy the floating point registers. There can be unused - * registers see asm/hwcap.h for details. - */ - err |= __copy_to_user(&ufp->fpregs, &hwstate->fpregs, - sizeof(hwstate->fpregs)); - /* - * Copy the status and control register. - */ - __put_user_error(hwstate->fpscr, &ufp->fpscr, err); - - /* - * Copy the exception registers. - */ - __put_user_error(hwstate->fpexc, &ufp_exc->fpexc, err); - __put_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); - __put_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); - - if (err) - return -EFAULT; - - /* Ensure that VFP is disabled. */ - vfp_flush_hwstate(thread); - - /* - * As per the PCS, clear the length and stride bits for function - * entry. - */ - hwstate->fpscr &= ~(FPSCR_LENGTH_MASK | FPSCR_STRIDE_MASK); - return 0; -} - -/* Sanitise and restore the current VFP state from the provided structures. */ -int vfp_restore_user_hwstate(struct user_vfp __user *ufp, - struct user_vfp_exc __user *ufp_exc) -{ - struct thread_info *thread = current_thread_info(); - struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; - unsigned long fpexc; - int err = 0; - - /* Disable VFP to avoid corrupting the new thread state. */ - vfp_flush_hwstate(thread); - - /* - * Copy the floating point registers. There can be unused - * registers see asm/hwcap.h for details. - */ - err |= __copy_from_user(&hwstate->fpregs, &ufp->fpregs, - sizeof(hwstate->fpregs)); - /* - * Copy the status and control register. - */ - __get_user_error(hwstate->fpscr, &ufp->fpscr, err); - - /* - * Sanitise and restore the exception registers. - */ - __get_user_error(fpexc, &ufp_exc->fpexc, err); - - /* Ensure the VFP is enabled. */ - fpexc |= FPEXC_EN; - - /* Ensure FPINST2 is invalid and the exception flag is cleared. */ - fpexc &= ~(FPEXC_EX | FPEXC_FP2V); - hwstate->fpexc = fpexc; - - __get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); - __get_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); - - return err ? -EFAULT : 0; -} - -/* - * VFP hardware can lose all context when a CPU goes offline. - * As we will be running in SMP mode with CPU hotplug, we will save the - * hardware state at every thread switch. We clear our held state when - * a CPU has been killed, indicating that the VFP hardware doesn't contain - * a threads VFP state. When a CPU starts up, we re-enable access to the - * VFP hardware. - * - * Both CPU_DYING and CPU_STARTING are called on the CPU which - * is being offlined/onlined. - */ -static int vfp_hotplug(struct notifier_block *b, unsigned long action, - void *hcpu) -{ - if (action == CPU_DYING || action == CPU_DYING_FROZEN) { - vfp_force_reload((long)hcpu, current_thread_info()); - } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) - vfp_enable(NULL); - return NOTIFY_OK; -} - -/* - * VFP support code initialisation. - */ -static int __init vfp_init(void) -{ - unsigned int vfpsid; - unsigned int cpu_arch = cpu_architecture(); - - if (cpu_arch >= CPU_ARCH_ARMv6) - on_each_cpu(vfp_enable, NULL, 1); - - /* - * First check that there is a VFP that we can use. - * The handler is already setup to just log calls, so - * we just need to read the VFPSID register. - */ - vfp_vector = vfp_testing_entry; - barrier(); - vfpsid = fmrx(FPSID); - barrier(); - vfp_vector = vfp_null_entry; - - printk(KERN_INFO "VFP support v0.3: "); - if (VFP_arch) - printk("not present\n"); - else if (vfpsid & FPSID_NODOUBLE) { - printk("no double precision support\n"); - } else { - hotcpu_notifier(vfp_hotplug, 0); - - VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ - printk("implementor %02x architecture %d part %02x variant %x rev %x\n", - (vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT, - (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT, - (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT, - (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT, - (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT); - - vfp_vector = vfp_support_entry; - - thread_register_notifier(&vfp_notifier_block); - vfp_pm_init(); - - /* - * We detected VFP, and the support code is - * in place; report VFP support to userspace. - */ - elf_hwcap |= HWCAP_VFP; -#ifdef CONFIG_VFPv3 - if (VFP_arch >= 2) { - elf_hwcap |= HWCAP_VFPv3; - - /* - * Check for VFPv3 D16. CPUs in this configuration - * only have 16 x 64bit registers. - */ - if (((fmrx(MVFR0) & MVFR0_A_SIMD_MASK)) == 1) - elf_hwcap |= HWCAP_VFPv3D16; - } -#endif - /* - * Check for the presence of the Advanced SIMD - * load/store instructions, integer and single - * precision floating point operations. Only check - * for NEON if the hardware has the MVFR registers. - */ - if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) { -#ifdef CONFIG_NEON - if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100) - elf_hwcap |= HWCAP_NEON; -#endif - if ((fmrx(MVFR1) & 0xf0000000) == 0x10000000) - elf_hwcap |= HWCAP_VFPv4; - } - } - return 0; -} - -late_initcall(vfp_init); diff --git a/ANDROID_3.4.5/arch/arm/vfp/vfpsingle.c b/ANDROID_3.4.5/arch/arm/vfp/vfpsingle.c deleted file mode 100644 index b252631b..00000000 --- a/ANDROID_3.4.5/arch/arm/vfp/vfpsingle.c +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * linux/arch/arm/vfp/vfpsingle.c - * - * This code is derived in part from John R. Housers softfloat library, which - * carries the following notice: - * - * =========================================================================== - * This C source file is part of the SoftFloat IEC/IEEE Floating-point - * Arithmetic Package, Release 2. - * - * Written by John R. Hauser. This work was made possible in part by the - * International Computer Science Institute, located at Suite 600, 1947 Center - * Street, Berkeley, California 94704. Funding was partially provided by the - * National Science Foundation under grant MIP-9311980. The original version - * of this code was written as part of a project to build a fixed-point vector - * processor in collaboration with the University of California at Berkeley, - * overseen by Profs. Nelson Morgan and John Wawrzynek. More information - * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ - * arithmetic/softfloat.html'. - * - * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort - * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT - * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO - * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY - * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. - * - * Derivative works are acceptable, even for commercial purposes, so long as - * (1) they include prominent notice that the work is derivative, and (2) they - * include prominent notice akin to these three paragraphs for those parts of - * this code that are retained. - * =========================================================================== - */ -#include <linux/kernel.h> -#include <linux/bitops.h> - -#include <asm/div64.h> -#include <asm/vfp.h> - -#include "vfpinstr.h" -#include "vfp.h" - -static struct vfp_single vfp_single_default_qnan = { - .exponent = 255, - .sign = 0, - .significand = VFP_SINGLE_SIGNIFICAND_QNAN, -}; - -static void vfp_single_dump(const char *str, struct vfp_single *s) -{ - pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n", - str, s->sign != 0, s->exponent, s->significand); -} - -static void vfp_single_normalise_denormal(struct vfp_single *vs) -{ - int bits = 31 - fls(vs->significand); - - vfp_single_dump("normalise_denormal: in", vs); - - if (bits) { - vs->exponent -= bits - 1; - vs->significand <<= bits; - } - - vfp_single_dump("normalise_denormal: out", vs); -} - -#ifndef DEBUG -#define vfp_single_normaliseround(sd,vsd,fpscr,except,func) __vfp_single_normaliseround(sd,vsd,fpscr,except) -u32 __vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions) -#else -u32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func) -#endif -{ - u32 significand, incr, rmode; - int exponent, shift, underflow; - - vfp_single_dump("pack: in", vs); - - /* - * Infinities and NaNs are a special case. - */ - if (vs->exponent == 255 && (vs->significand == 0 || exceptions)) - goto pack; - - /* - * Special-case zero. - */ - if (vs->significand == 0) { - vs->exponent = 0; - goto pack; - } - - exponent = vs->exponent; - significand = vs->significand; - - /* - * Normalise first. Note that we shift the significand up to - * bit 31, so we have VFP_SINGLE_LOW_BITS + 1 below the least - * significant bit. - */ - shift = 32 - fls(significand); - if (shift < 32 && shift) { - exponent -= shift; - significand <<= shift; - } - -#ifdef DEBUG - vs->exponent = exponent; - vs->significand = significand; - vfp_single_dump("pack: normalised", vs); -#endif - - /* - * Tiny number? - */ - underflow = exponent < 0; - if (underflow) { - significand = vfp_shiftright32jamming(significand, -exponent); - exponent = 0; -#ifdef DEBUG - vs->exponent = exponent; - vs->significand = significand; - vfp_single_dump("pack: tiny number", vs); -#endif - if (!(significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1))) - underflow = 0; - } - - /* - * Select rounding increment. - */ - incr = 0; - rmode = fpscr & FPSCR_RMODE_MASK; - - if (rmode == FPSCR_ROUND_NEAREST) { - incr = 1 << VFP_SINGLE_LOW_BITS; - if ((significand & (1 << (VFP_SINGLE_LOW_BITS + 1))) == 0) - incr -= 1; - } else if (rmode == FPSCR_ROUND_TOZERO) { - incr = 0; - } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0)) - incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1; - - pr_debug("VFP: rounding increment = 0x%08x\n", incr); - - /* - * Is our rounding going to overflow? - */ - if ((significand + incr) < significand) { - exponent += 1; - significand = (significand >> 1) | (significand & 1); - incr >>= 1; -#ifdef DEBUG - vs->exponent = exponent; - vs->significand = significand; - vfp_single_dump("pack: overflow", vs); -#endif - } - - /* - * If any of the low bits (which will be shifted out of the - * number) are non-zero, the result is inexact. - */ - if (significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1)) - exceptions |= FPSCR_IXC; - - /* - * Do our rounding. - */ - significand += incr; - - /* - * Infinity? - */ - if (exponent >= 254) { - exceptions |= FPSCR_OFC | FPSCR_IXC; - if (incr == 0) { - vs->exponent = 253; - vs->significand = 0x7fffffff; - } else { - vs->exponent = 255; /* infinity */ - vs->significand = 0; - } - } else { - if (significand >> (VFP_SINGLE_LOW_BITS + 1) == 0) - exponent = 0; - if (exponent || significand > 0x80000000) - underflow = 0; - if (underflow) - exceptions |= FPSCR_UFC; - vs->exponent = exponent; - vs->significand = significand >> 1; - } - - pack: - vfp_single_dump("pack: final", vs); - { - s32 d = vfp_single_pack(vs); -#ifdef DEBUG - pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func, - sd, d, exceptions); -#endif - vfp_put_float(d, sd); - } - - return exceptions; -} - -/* - * Propagate the NaN, setting exceptions if it is signalling. - * 'n' is always a NaN. 'm' may be a number, NaN or infinity. - */ -static u32 -vfp_propagate_nan(struct vfp_single *vsd, struct vfp_single *vsn, - struct vfp_single *vsm, u32 fpscr) -{ - struct vfp_single *nan; - int tn, tm = 0; - - tn = vfp_single_type(vsn); - - if (vsm) - tm = vfp_single_type(vsm); - - if (fpscr & FPSCR_DEFAULT_NAN) - /* - * Default NaN mode - always returns a quiet NaN - */ - nan = &vfp_single_default_qnan; - else { - /* - * Contemporary mode - select the first signalling - * NAN, or if neither are signalling, the first - * quiet NAN. - */ - if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN)) - nan = vsn; - else - nan = vsm; - /* - * Make the NaN quiet. - */ - nan->significand |= VFP_SINGLE_SIGNIFICAND_QNAN; - } - - *vsd = *nan; - - /* - * If one was a signalling NAN, raise invalid operation. - */ - return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG; -} - - -/* - * Extended operations - */ -static u32 vfp_single_fabs(int sd, int unused, s32 m, u32 fpscr) -{ - vfp_put_float(vfp_single_packed_abs(m), sd); - return 0; -} - -static u32 vfp_single_fcpy(int sd, int unused, s32 m, u32 fpscr) -{ - vfp_put_float(m, sd); - return 0; -} - -static u32 vfp_single_fneg(int sd, int unused, s32 m, u32 fpscr) -{ - vfp_put_float(vfp_single_packed_negate(m), sd); - return 0; -} - -static const u16 sqrt_oddadjust[] = { - 0x0004, 0x0022, 0x005d, 0x00b1, 0x011d, 0x019f, 0x0236, 0x02e0, - 0x039c, 0x0468, 0x0545, 0x0631, 0x072b, 0x0832, 0x0946, 0x0a67 -}; - -static const u16 sqrt_evenadjust[] = { - 0x0a2d, 0x08af, 0x075a, 0x0629, 0x051a, 0x0429, 0x0356, 0x029e, - 0x0200, 0x0179, 0x0109, 0x00af, 0x0068, 0x0034, 0x0012, 0x0002 -}; - -u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand) -{ - int index; - u32 z, a; - - if ((significand & 0xc0000000) != 0x40000000) { - printk(KERN_WARNING "VFP: estimate_sqrt: invalid significand\n"); - } - - a = significand << 1; - index = (a >> 27) & 15; - if (exponent & 1) { - z = 0x4000 + (a >> 17) - sqrt_oddadjust[index]; - z = ((a / z) << 14) + (z << 15); - a >>= 1; - } else { - z = 0x8000 + (a >> 17) - sqrt_evenadjust[index]; - z = a / z + z; - z = (z >= 0x20000) ? 0xffff8000 : (z << 15); - if (z <= a) - return (s32)a >> 1; - } - { - u64 v = (u64)a << 31; - do_div(v, z); - return v + (z >> 1); - } -} - -static u32 vfp_single_fsqrt(int sd, int unused, s32 m, u32 fpscr) -{ - struct vfp_single vsm, vsd; - int ret, tm; - - vfp_single_unpack(&vsm, m); - tm = vfp_single_type(&vsm); - if (tm & (VFP_NAN|VFP_INFINITY)) { - struct vfp_single *vsp = &vsd; - - if (tm & VFP_NAN) - ret = vfp_propagate_nan(vsp, &vsm, NULL, fpscr); - else if (vsm.sign == 0) { - sqrt_copy: - vsp = &vsm; - ret = 0; - } else { - sqrt_invalid: - vsp = &vfp_single_default_qnan; - ret = FPSCR_IOC; - } - vfp_put_float(vfp_single_pack(vsp), sd); - return ret; - } - - /* - * sqrt(+/- 0) == +/- 0 - */ - if (tm & VFP_ZERO) - goto sqrt_copy; - - /* - * Normalise a denormalised number - */ - if (tm & VFP_DENORMAL) - vfp_single_normalise_denormal(&vsm); - - /* - * sqrt(<0) = invalid - */ - if (vsm.sign) - goto sqrt_invalid; - - vfp_single_dump("sqrt", &vsm); - - /* - * Estimate the square root. - */ - vsd.sign = 0; - vsd.exponent = ((vsm.exponent - 127) >> 1) + 127; - vsd.significand = vfp_estimate_sqrt_significand(vsm.exponent, vsm.significand) + 2; - - vfp_single_dump("sqrt estimate", &vsd); - - /* - * And now adjust. - */ - if ((vsd.significand & VFP_SINGLE_LOW_BITS_MASK) <= 5) { - if (vsd.significand < 2) { - vsd.significand = 0xffffffff; - } else { - u64 term; - s64 rem; - vsm.significand <<= !(vsm.exponent & 1); - term = (u64)vsd.significand * vsd.significand; - rem = ((u64)vsm.significand << 32) - term; - - pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem); - - while (rem < 0) { - vsd.significand -= 1; - rem += ((u64)vsd.significand << 1) | 1; - } - vsd.significand |= rem != 0; - } - } - vsd.significand = vfp_shiftright32jamming(vsd.significand, 1); - - return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fsqrt"); -} - -/* - * Equal := ZC - * Less than := N - * Greater than := C - * Unordered := CV - */ -static u32 vfp_compare(int sd, int signal_on_qnan, s32 m, u32 fpscr) -{ - s32 d; - u32 ret = 0; - - d = vfp_get_float(sd); - if (vfp_single_packed_exponent(m) == 255 && vfp_single_packed_mantissa(m)) { - ret |= FPSCR_C | FPSCR_V; - if (signal_on_qnan || !(vfp_single_packed_mantissa(m) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1)))) - /* - * Signalling NaN, or signalling on quiet NaN - */ - ret |= FPSCR_IOC; - } - - if (vfp_single_packed_exponent(d) == 255 && vfp_single_packed_mantissa(d)) { - ret |= FPSCR_C | FPSCR_V; - if (signal_on_qnan || !(vfp_single_packed_mantissa(d) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1)))) - /* - * Signalling NaN, or signalling on quiet NaN - */ - ret |= FPSCR_IOC; - } - - if (ret == 0) { - if (d == m || vfp_single_packed_abs(d | m) == 0) { - /* - * equal - */ - ret |= FPSCR_Z | FPSCR_C; - } else if (vfp_single_packed_sign(d ^ m)) { - /* - * different signs - */ - if (vfp_single_packed_sign(d)) - /* - * d is negative, so d < m - */ - ret |= FPSCR_N; - else - /* - * d is positive, so d > m - */ - ret |= FPSCR_C; - } else if ((vfp_single_packed_sign(d) != 0) ^ (d < m)) { - /* - * d < m - */ - ret |= FPSCR_N; - } else if ((vfp_single_packed_sign(d) != 0) ^ (d > m)) { - /* - * d > m - */ - ret |= FPSCR_C; - } - } - return ret; -} - -static u32 vfp_single_fcmp(int sd, int unused, s32 m, u32 fpscr) -{ - return vfp_compare(sd, 0, m, fpscr); -} - -static u32 vfp_single_fcmpe(int sd, int unused, s32 m, u32 fpscr) -{ - return vfp_compare(sd, 1, m, fpscr); -} - -static u32 vfp_single_fcmpz(int sd, int unused, s32 m, u32 fpscr) -{ - return vfp_compare(sd, 0, 0, fpscr); -} - -static u32 vfp_single_fcmpez(int sd, int unused, s32 m, u32 fpscr) -{ - return vfp_compare(sd, 1, 0, fpscr); -} - -static u32 vfp_single_fcvtd(int dd, int unused, s32 m, u32 fpscr) -{ - struct vfp_single vsm; - struct vfp_double vdd; - int tm; - u32 exceptions = 0; - - vfp_single_unpack(&vsm, m); - - tm = vfp_single_type(&vsm); - - /* - * If we have a signalling NaN, signal invalid operation. - */ - if (tm == VFP_SNAN) - exceptions = FPSCR_IOC; - - if (tm & VFP_DENORMAL) - vfp_single_normalise_denormal(&vsm); - - vdd.sign = vsm.sign; - vdd.significand = (u64)vsm.significand << 32; - - /* - * If we have an infinity or NaN, the exponent must be 2047. - */ - if (tm & (VFP_INFINITY|VFP_NAN)) { - vdd.exponent = 2047; - if (tm == VFP_QNAN) - vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN; - goto pack_nan; - } else if (tm & VFP_ZERO) - vdd.exponent = 0; - else - vdd.exponent = vsm.exponent + (1023 - 127); - - return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fcvtd"); - - pack_nan: - vfp_put_double(vfp_double_pack(&vdd), dd); - return exceptions; -} - -static u32 vfp_single_fuito(int sd, int unused, s32 m, u32 fpscr) -{ - struct vfp_single vs; - - vs.sign = 0; - vs.exponent = 127 + 31 - 1; - vs.significand = (u32)m; - - return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fuito"); -} - -static u32 vfp_single_fsito(int sd, int unused, s32 m, u32 fpscr) -{ - struct vfp_single vs; - - vs.sign = (m & 0x80000000) >> 16; - vs.exponent = 127 + 31 - 1; - vs.significand = vs.sign ? -m : m; - - return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fsito"); -} - -static u32 vfp_single_ftoui(int sd, int unused, s32 m, u32 fpscr) -{ - struct vfp_single vsm; - u32 d, exceptions = 0; - int rmode = fpscr & FPSCR_RMODE_MASK; - int tm; - - vfp_single_unpack(&vsm, m); - vfp_single_dump("VSM", &vsm); - - /* - * Do we have a denormalised number? - */ - tm = vfp_single_type(&vsm); - if (tm & VFP_DENORMAL) - exceptions |= FPSCR_IDC; - - if (tm & VFP_NAN) - vsm.sign = 0; - - if (vsm.exponent >= 127 + 32) { - d = vsm.sign ? 0 : 0xffffffff; - exceptions = FPSCR_IOC; - } else if (vsm.exponent >= 127 - 1) { - int shift = 127 + 31 - vsm.exponent; - u32 rem, incr = 0; - - /* - * 2^0 <= m < 2^32-2^8 - */ - d = (vsm.significand << 1) >> shift; - rem = vsm.significand << (33 - shift); - - if (rmode == FPSCR_ROUND_NEAREST) { - incr = 0x80000000; - if ((d & 1) == 0) - incr -= 1; - } else if (rmode == FPSCR_ROUND_TOZERO) { - incr = 0; - } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) { - incr = ~0; - } - - if ((rem + incr) < rem) { - if (d < 0xffffffff) - d += 1; - else - exceptions |= FPSCR_IOC; - } - - if (d && vsm.sign) { - d = 0; - exceptions |= FPSCR_IOC; - } else if (rem) - exceptions |= FPSCR_IXC; - } else { - d = 0; - if (vsm.exponent | vsm.significand) { - exceptions |= FPSCR_IXC; - if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) - d = 1; - else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) { - d = 0; - exceptions |= FPSCR_IOC; - } - } - } - - pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); - - vfp_put_float(d, sd); - - return exceptions; -} - -static u32 vfp_single_ftouiz(int sd, int unused, s32 m, u32 fpscr) -{ - return vfp_single_ftoui(sd, unused, m, FPSCR_ROUND_TOZERO); -} - -static u32 vfp_single_ftosi(int sd, int unused, s32 m, u32 fpscr) -{ - struct vfp_single vsm; - u32 d, exceptions = 0; - int rmode = fpscr & FPSCR_RMODE_MASK; - int tm; - - vfp_single_unpack(&vsm, m); - vfp_single_dump("VSM", &vsm); - - /* - * Do we have a denormalised number? - */ - tm = vfp_single_type(&vsm); - if (vfp_single_type(&vsm) & VFP_DENORMAL) - exceptions |= FPSCR_IDC; - - if (tm & VFP_NAN) { - d = 0; - exceptions |= FPSCR_IOC; - } else if (vsm.exponent >= 127 + 32) { - /* - * m >= 2^31-2^7: invalid - */ - d = 0x7fffffff; - if (vsm.sign) - d = ~d; - exceptions |= FPSCR_IOC; - } else if (vsm.exponent >= 127 - 1) { - int shift = 127 + 31 - vsm.exponent; - u32 rem, incr = 0; - - /* 2^0 <= m <= 2^31-2^7 */ - d = (vsm.significand << 1) >> shift; - rem = vsm.significand << (33 - shift); - - if (rmode == FPSCR_ROUND_NEAREST) { - incr = 0x80000000; - if ((d & 1) == 0) - incr -= 1; - } else if (rmode == FPSCR_ROUND_TOZERO) { - incr = 0; - } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) { - incr = ~0; - } - - if ((rem + incr) < rem && d < 0xffffffff) - d += 1; - if (d > 0x7fffffff + (vsm.sign != 0)) { - d = 0x7fffffff + (vsm.sign != 0); - exceptions |= FPSCR_IOC; - } else if (rem) - exceptions |= FPSCR_IXC; - - if (vsm.sign) - d = -d; - } else { - d = 0; - if (vsm.exponent | vsm.significand) { - exceptions |= FPSCR_IXC; - if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) - d = 1; - else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) - d = -1; - } - } - - pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); - - vfp_put_float((s32)d, sd); - - return exceptions; -} - -static u32 vfp_single_ftosiz(int sd, int unused, s32 m, u32 fpscr) -{ - return vfp_single_ftosi(sd, unused, m, FPSCR_ROUND_TOZERO); -} - -static struct op fops_ext[32] = { - [FEXT_TO_IDX(FEXT_FCPY)] = { vfp_single_fcpy, 0 }, - [FEXT_TO_IDX(FEXT_FABS)] = { vfp_single_fabs, 0 }, - [FEXT_TO_IDX(FEXT_FNEG)] = { vfp_single_fneg, 0 }, - [FEXT_TO_IDX(FEXT_FSQRT)] = { vfp_single_fsqrt, 0 }, - [FEXT_TO_IDX(FEXT_FCMP)] = { vfp_single_fcmp, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FCMPE)] = { vfp_single_fcmpe, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FCMPZ)] = { vfp_single_fcmpz, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FCMPEZ)] = { vfp_single_fcmpez, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FCVT)] = { vfp_single_fcvtd, OP_SCALAR|OP_DD }, - [FEXT_TO_IDX(FEXT_FUITO)] = { vfp_single_fuito, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FSITO)] = { vfp_single_fsito, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FTOUI)] = { vfp_single_ftoui, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FTOUIZ)] = { vfp_single_ftouiz, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FTOSI)] = { vfp_single_ftosi, OP_SCALAR }, - [FEXT_TO_IDX(FEXT_FTOSIZ)] = { vfp_single_ftosiz, OP_SCALAR }, -}; - - - - - -static u32 -vfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn, - struct vfp_single *vsm, u32 fpscr) -{ - struct vfp_single *vsp; - u32 exceptions = 0; - int tn, tm; - - tn = vfp_single_type(vsn); - tm = vfp_single_type(vsm); - - if (tn & tm & VFP_INFINITY) { - /* - * Two infinities. Are they different signs? - */ - if (vsn->sign ^ vsm->sign) { - /* - * different signs -> invalid - */ - exceptions = FPSCR_IOC; - vsp = &vfp_single_default_qnan; - } else { - /* - * same signs -> valid - */ - vsp = vsn; - } - } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) { - /* - * One infinity and one number -> infinity - */ - vsp = vsn; - } else { - /* - * 'n' is a NaN of some type - */ - return vfp_propagate_nan(vsd, vsn, vsm, fpscr); - } - *vsd = *vsp; - return exceptions; -} - -static u32 -vfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn, - struct vfp_single *vsm, u32 fpscr) -{ - u32 exp_diff, m_sig; - - if (vsn->significand & 0x80000000 || - vsm->significand & 0x80000000) { - pr_info("VFP: bad FP values in %s\n", __func__); - vfp_single_dump("VSN", vsn); - vfp_single_dump("VSM", vsm); - } - - /* - * Ensure that 'n' is the largest magnitude number. Note that - * if 'n' and 'm' have equal exponents, we do not swap them. - * This ensures that NaN propagation works correctly. - */ - if (vsn->exponent < vsm->exponent) { - struct vfp_single *t = vsn; - vsn = vsm; - vsm = t; - } - - /* - * Is 'n' an infinity or a NaN? Note that 'm' may be a number, - * infinity or a NaN here. - */ - if (vsn->exponent == 255) - return vfp_single_fadd_nonnumber(vsd, vsn, vsm, fpscr); - - /* - * We have two proper numbers, where 'vsn' is the larger magnitude. - * - * Copy 'n' to 'd' before doing the arithmetic. - */ - *vsd = *vsn; - - /* - * Align both numbers. - */ - exp_diff = vsn->exponent - vsm->exponent; - m_sig = vfp_shiftright32jamming(vsm->significand, exp_diff); - - /* - * If the signs are different, we are really subtracting. - */ - if (vsn->sign ^ vsm->sign) { - m_sig = vsn->significand - m_sig; - if ((s32)m_sig < 0) { - vsd->sign = vfp_sign_negate(vsd->sign); - m_sig = -m_sig; - } else if (m_sig == 0) { - vsd->sign = (fpscr & FPSCR_RMODE_MASK) == - FPSCR_ROUND_MINUSINF ? 0x8000 : 0; - } - } else { - m_sig = vsn->significand + m_sig; - } - vsd->significand = m_sig; - - return 0; -} - -static u32 -vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_single *vsm, u32 fpscr) -{ - vfp_single_dump("VSN", vsn); - vfp_single_dump("VSM", vsm); - - /* - * Ensure that 'n' is the largest magnitude number. Note that - * if 'n' and 'm' have equal exponents, we do not swap them. - * This ensures that NaN propagation works correctly. - */ - if (vsn->exponent < vsm->exponent) { - struct vfp_single *t = vsn; - vsn = vsm; - vsm = t; - pr_debug("VFP: swapping M <-> N\n"); - } - - vsd->sign = vsn->sign ^ vsm->sign; - - /* - * If 'n' is an infinity or NaN, handle it. 'm' may be anything. - */ - if (vsn->exponent == 255) { - if (vsn->significand || (vsm->exponent == 255 && vsm->significand)) - return vfp_propagate_nan(vsd, vsn, vsm, fpscr); - if ((vsm->exponent | vsm->significand) == 0) { - *vsd = vfp_single_default_qnan; - return FPSCR_IOC; - } - vsd->exponent = vsn->exponent; - vsd->significand = 0; - return 0; - } - - /* - * If 'm' is zero, the result is always zero. In this case, - * 'n' may be zero or a number, but it doesn't matter which. - */ - if ((vsm->exponent | vsm->significand) == 0) { - vsd->exponent = 0; - vsd->significand = 0; - return 0; - } - - /* - * We add 2 to the destination exponent for the same reason as - * the addition case - though this time we have +1 from each - * input operand. - */ - vsd->exponent = vsn->exponent + vsm->exponent - 127 + 2; - vsd->significand = vfp_hi64to32jamming((u64)vsn->significand * vsm->significand); - - vfp_single_dump("VSD", vsd); - return 0; -} - -#define NEG_MULTIPLY (1 << 0) -#define NEG_SUBTRACT (1 << 1) - -static u32 -vfp_single_multiply_accumulate(int sd, int sn, s32 m, u32 fpscr, u32 negate, char *func) -{ - struct vfp_single vsd, vsp, vsn, vsm; - u32 exceptions; - s32 v; - - v = vfp_get_float(sn); - pr_debug("VFP: s%u = %08x\n", sn, v); - vfp_single_unpack(&vsn, v); - if (vsn.exponent == 0 && vsn.significand) - vfp_single_normalise_denormal(&vsn); - - vfp_single_unpack(&vsm, m); - if (vsm.exponent == 0 && vsm.significand) - vfp_single_normalise_denormal(&vsm); - - exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr); - if (negate & NEG_MULTIPLY) - vsp.sign = vfp_sign_negate(vsp.sign); - - v = vfp_get_float(sd); - pr_debug("VFP: s%u = %08x\n", sd, v); - vfp_single_unpack(&vsn, v); - if (negate & NEG_SUBTRACT) - vsn.sign = vfp_sign_negate(vsn.sign); - - exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr); - - return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, func); -} - -/* - * Standard operations - */ - -/* - * sd = sd + (sn * sm) - */ -static u32 vfp_single_fmac(int sd, int sn, s32 m, u32 fpscr) -{ - return vfp_single_multiply_accumulate(sd, sn, m, fpscr, 0, "fmac"); -} - -/* - * sd = sd - (sn * sm) - */ -static u32 vfp_single_fnmac(int sd, int sn, s32 m, u32 fpscr) -{ - return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac"); -} - -/* - * sd = -sd + (sn * sm) - */ -static u32 vfp_single_fmsc(int sd, int sn, s32 m, u32 fpscr) -{ - return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc"); -} - -/* - * sd = -sd - (sn * sm) - */ -static u32 vfp_single_fnmsc(int sd, int sn, s32 m, u32 fpscr) -{ - return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc"); -} - -/* - * sd = sn * sm - */ -static u32 vfp_single_fmul(int sd, int sn, s32 m, u32 fpscr) -{ - struct vfp_single vsd, vsn, vsm; - u32 exceptions; - s32 n = vfp_get_float(sn); - - pr_debug("VFP: s%u = %08x\n", sn, n); - - vfp_single_unpack(&vsn, n); - if (vsn.exponent == 0 && vsn.significand) - vfp_single_normalise_denormal(&vsn); - - vfp_single_unpack(&vsm, m); - if (vsm.exponent == 0 && vsm.significand) - vfp_single_normalise_denormal(&vsm); - - exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); - return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fmul"); -} - -/* - * sd = -(sn * sm) - */ -static u32 vfp_single_fnmul(int sd, int sn, s32 m, u32 fpscr) -{ - struct vfp_single vsd, vsn, vsm; - u32 exceptions; - s32 n = vfp_get_float(sn); - - pr_debug("VFP: s%u = %08x\n", sn, n); - - vfp_single_unpack(&vsn, n); - if (vsn.exponent == 0 && vsn.significand) - vfp_single_normalise_denormal(&vsn); - - vfp_single_unpack(&vsm, m); - if (vsm.exponent == 0 && vsm.significand) - vfp_single_normalise_denormal(&vsm); - - exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); - vsd.sign = vfp_sign_negate(vsd.sign); - return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fnmul"); -} - -/* - * sd = sn + sm - */ -static u32 vfp_single_fadd(int sd, int sn, s32 m, u32 fpscr) -{ - struct vfp_single vsd, vsn, vsm; - u32 exceptions; - s32 n = vfp_get_float(sn); - - pr_debug("VFP: s%u = %08x\n", sn, n); - - /* - * Unpack and normalise denormals. - */ - vfp_single_unpack(&vsn, n); - if (vsn.exponent == 0 && vsn.significand) - vfp_single_normalise_denormal(&vsn); - - vfp_single_unpack(&vsm, m); - if (vsm.exponent == 0 && vsm.significand) - vfp_single_normalise_denormal(&vsm); - - exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr); - - return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fadd"); -} - -/* - * sd = sn - sm - */ -static u32 vfp_single_fsub(int sd, int sn, s32 m, u32 fpscr) -{ - /* - * Subtraction is addition with one sign inverted. - */ - return vfp_single_fadd(sd, sn, vfp_single_packed_negate(m), fpscr); -} - -/* - * sd = sn / sm - */ -static u32 vfp_single_fdiv(int sd, int sn, s32 m, u32 fpscr) -{ - struct vfp_single vsd, vsn, vsm; - u32 exceptions = 0; - s32 n = vfp_get_float(sn); - int tm, tn; - - pr_debug("VFP: s%u = %08x\n", sn, n); - - vfp_single_unpack(&vsn, n); - vfp_single_unpack(&vsm, m); - - vsd.sign = vsn.sign ^ vsm.sign; - - tn = vfp_single_type(&vsn); - tm = vfp_single_type(&vsm); - - /* - * Is n a NAN? - */ - if (tn & VFP_NAN) - goto vsn_nan; - - /* - * Is m a NAN? - */ - if (tm & VFP_NAN) - goto vsm_nan; - - /* - * If n and m are infinity, the result is invalid - * If n and m are zero, the result is invalid - */ - if (tm & tn & (VFP_INFINITY|VFP_ZERO)) - goto invalid; - - /* - * If n is infinity, the result is infinity - */ - if (tn & VFP_INFINITY) - goto infinity; - - /* - * If m is zero, raise div0 exception - */ - if (tm & VFP_ZERO) - goto divzero; - - /* - * If m is infinity, or n is zero, the result is zero - */ - if (tm & VFP_INFINITY || tn & VFP_ZERO) - goto zero; - - if (tn & VFP_DENORMAL) - vfp_single_normalise_denormal(&vsn); - if (tm & VFP_DENORMAL) - vfp_single_normalise_denormal(&vsm); - - /* - * Ok, we have two numbers, we can perform division. - */ - vsd.exponent = vsn.exponent - vsm.exponent + 127 - 1; - vsm.significand <<= 1; - if (vsm.significand <= (2 * vsn.significand)) { - vsn.significand >>= 1; - vsd.exponent++; - } - { - u64 significand = (u64)vsn.significand << 32; - do_div(significand, vsm.significand); - vsd.significand = significand; - } - if ((vsd.significand & 0x3f) == 0) - vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32); - - return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fdiv"); - - vsn_nan: - exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr); - pack: - vfp_put_float(vfp_single_pack(&vsd), sd); - return exceptions; - - vsm_nan: - exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr); - goto pack; - - zero: - vsd.exponent = 0; - vsd.significand = 0; - goto pack; - - divzero: - exceptions = FPSCR_DZC; - infinity: - vsd.exponent = 255; - vsd.significand = 0; - goto pack; - - invalid: - vfp_put_float(vfp_single_pack(&vfp_single_default_qnan), sd); - return FPSCR_IOC; -} - -static struct op fops[16] = { - [FOP_TO_IDX(FOP_FMAC)] = { vfp_single_fmac, 0 }, - [FOP_TO_IDX(FOP_FNMAC)] = { vfp_single_fnmac, 0 }, - [FOP_TO_IDX(FOP_FMSC)] = { vfp_single_fmsc, 0 }, - [FOP_TO_IDX(FOP_FNMSC)] = { vfp_single_fnmsc, 0 }, - [FOP_TO_IDX(FOP_FMUL)] = { vfp_single_fmul, 0 }, - [FOP_TO_IDX(FOP_FNMUL)] = { vfp_single_fnmul, 0 }, - [FOP_TO_IDX(FOP_FADD)] = { vfp_single_fadd, 0 }, - [FOP_TO_IDX(FOP_FSUB)] = { vfp_single_fsub, 0 }, - [FOP_TO_IDX(FOP_FDIV)] = { vfp_single_fdiv, 0 }, -}; - -#define FREG_BANK(x) ((x) & 0x18) -#define FREG_IDX(x) ((x) & 7) - -u32 vfp_single_cpdo(u32 inst, u32 fpscr) -{ - u32 op = inst & FOP_MASK; - u32 exceptions = 0; - unsigned int dest; - unsigned int sn = vfp_get_sn(inst); - unsigned int sm = vfp_get_sm(inst); - unsigned int vecitr, veclen, vecstride; - struct op *fop; - - vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK); - - fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)]; - - /* - * fcvtsd takes a dN register number as destination, not sN. - * Technically, if bit 0 of dd is set, this is an invalid - * instruction. However, we ignore this for efficiency. - * It also only operates on scalars. - */ - if (fop->flags & OP_DD) - dest = vfp_get_dd(inst); - else - dest = vfp_get_sd(inst); - - /* - * If destination bank is zero, vector length is always '1'. - * ARM DDI0100F C5.1.3, C5.3.2. - */ - if ((fop->flags & OP_SCALAR) || FREG_BANK(dest) == 0) - veclen = 0; - else - veclen = fpscr & FPSCR_LENGTH_MASK; - - pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, - (veclen >> FPSCR_LENGTH_BIT) + 1); - - if (!fop->fn) - goto invalid; - - for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) { - s32 m = vfp_get_float(sm); - u32 except; - char type; - - type = fop->flags & OP_DD ? 'd' : 's'; - if (op == FOP_EXT) - pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n", - vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, - sm, m); - else - pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n", - vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, - FOP_TO_IDX(op), sm, m); - - except = fop->fn(dest, sn, m, fpscr); - pr_debug("VFP: itr%d: exceptions=%08x\n", - vecitr >> FPSCR_LENGTH_BIT, except); - - exceptions |= except; - - /* - * CHECK: It appears to be undefined whether we stop when - * we encounter an exception. We continue. - */ - dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 7); - sn = FREG_BANK(sn) + ((FREG_IDX(sn) + vecstride) & 7); - if (FREG_BANK(sm) != 0) - sm = FREG_BANK(sm) + ((FREG_IDX(sm) + vecstride) & 7); - } - return exceptions; - - invalid: - return (u32)-1; -} |