xref: /linux/arch/arm/vfp/vfpsingle.c (revision e5451c8f8330e03ad3cfa16048b4daf961af434f)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/arch/arm/vfp/vfpsingle.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * This code is derived in part from John R. Housers softfloat library, which
51da177e4SLinus Torvalds  * carries the following notice:
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * ===========================================================================
81da177e4SLinus Torvalds  * This C source file is part of the SoftFloat IEC/IEEE Floating-point
91da177e4SLinus Torvalds  * Arithmetic Package, Release 2.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Written by John R. Hauser.  This work was made possible in part by the
121da177e4SLinus Torvalds  * International Computer Science Institute, located at Suite 600, 1947 Center
131da177e4SLinus Torvalds  * Street, Berkeley, California 94704.  Funding was partially provided by the
141da177e4SLinus Torvalds  * National Science Foundation under grant MIP-9311980.  The original version
151da177e4SLinus Torvalds  * of this code was written as part of a project to build a fixed-point vector
161da177e4SLinus Torvalds  * processor in collaboration with the University of California at Berkeley,
171da177e4SLinus Torvalds  * overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
181da177e4SLinus Torvalds  * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
191da177e4SLinus Torvalds  * arithmetic/softfloat.html'.
201da177e4SLinus Torvalds  *
211da177e4SLinus Torvalds  * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
221da177e4SLinus Torvalds  * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
231da177e4SLinus Torvalds  * TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
241da177e4SLinus Torvalds  * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
251da177e4SLinus Torvalds  * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
261da177e4SLinus Torvalds  *
271da177e4SLinus Torvalds  * Derivative works are acceptable, even for commercial purposes, so long as
281da177e4SLinus Torvalds  * (1) they include prominent notice that the work is derivative, and (2) they
291da177e4SLinus Torvalds  * include prominent notice akin to these three paragraphs for those parts of
301da177e4SLinus Torvalds  * this code that are retained.
311da177e4SLinus Torvalds  * ===========================================================================
321da177e4SLinus Torvalds  */
331da177e4SLinus Torvalds #include <linux/kernel.h>
341da177e4SLinus Torvalds #include <linux/bitops.h>
35438a7616SRussell King 
36438a7616SRussell King #include <asm/div64.h>
371da177e4SLinus Torvalds #include <asm/vfp.h>
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds #include "vfpinstr.h"
401da177e4SLinus Torvalds #include "vfp.h"
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds static struct vfp_single vfp_single_default_qnan = {
431da177e4SLinus Torvalds 	.exponent	= 255,
441da177e4SLinus Torvalds 	.sign		= 0,
451da177e4SLinus Torvalds 	.significand	= VFP_SINGLE_SIGNIFICAND_QNAN,
461da177e4SLinus Torvalds };
471da177e4SLinus Torvalds 
vfp_single_dump(const char * str,struct vfp_single * s)481da177e4SLinus Torvalds static void vfp_single_dump(const char *str, struct vfp_single *s)
491da177e4SLinus Torvalds {
501da177e4SLinus Torvalds 	pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n",
511da177e4SLinus Torvalds 		 str, s->sign != 0, s->exponent, s->significand);
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
vfp_single_normalise_denormal(struct vfp_single * vs)541da177e4SLinus Torvalds static void vfp_single_normalise_denormal(struct vfp_single *vs)
551da177e4SLinus Torvalds {
561da177e4SLinus Torvalds 	int bits = 31 - fls(vs->significand);
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	vfp_single_dump("normalise_denormal: in", vs);
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	if (bits) {
611da177e4SLinus Torvalds 		vs->exponent -= bits - 1;
621da177e4SLinus Torvalds 		vs->significand <<= bits;
631da177e4SLinus Torvalds 	}
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	vfp_single_dump("normalise_denormal: out", vs);
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds #ifndef DEBUG
691da177e4SLinus Torvalds #define vfp_single_normaliseround(sd,vsd,fpscr,except,func) __vfp_single_normaliseround(sd,vsd,fpscr,except)
__vfp_single_normaliseround(int sd,struct vfp_single * vs,u32 fpscr,u32 exceptions)701da177e4SLinus Torvalds u32 __vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions)
711da177e4SLinus Torvalds #else
721da177e4SLinus Torvalds u32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func)
731da177e4SLinus Torvalds #endif
741da177e4SLinus Torvalds {
751da177e4SLinus Torvalds 	u32 significand, incr, rmode;
761da177e4SLinus Torvalds 	int exponent, shift, underflow;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	vfp_single_dump("pack: in", vs);
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	/*
811da177e4SLinus Torvalds 	 * Infinities and NaNs are a special case.
821da177e4SLinus Torvalds 	 */
831da177e4SLinus Torvalds 	if (vs->exponent == 255 && (vs->significand == 0 || exceptions))
841da177e4SLinus Torvalds 		goto pack;
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	/*
871da177e4SLinus Torvalds 	 * Special-case zero.
881da177e4SLinus Torvalds 	 */
891da177e4SLinus Torvalds 	if (vs->significand == 0) {
901da177e4SLinus Torvalds 		vs->exponent = 0;
911da177e4SLinus Torvalds 		goto pack;
921da177e4SLinus Torvalds 	}
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	exponent = vs->exponent;
951da177e4SLinus Torvalds 	significand = vs->significand;
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 	/*
981da177e4SLinus Torvalds 	 * Normalise first.  Note that we shift the significand up to
991da177e4SLinus Torvalds 	 * bit 31, so we have VFP_SINGLE_LOW_BITS + 1 below the least
1001da177e4SLinus Torvalds 	 * significant bit.
1011da177e4SLinus Torvalds 	 */
1021da177e4SLinus Torvalds 	shift = 32 - fls(significand);
1031da177e4SLinus Torvalds 	if (shift < 32 && shift) {
1041da177e4SLinus Torvalds 		exponent -= shift;
1051da177e4SLinus Torvalds 		significand <<= shift;
1061da177e4SLinus Torvalds 	}
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds #ifdef DEBUG
1091da177e4SLinus Torvalds 	vs->exponent = exponent;
1101da177e4SLinus Torvalds 	vs->significand = significand;
1111da177e4SLinus Torvalds 	vfp_single_dump("pack: normalised", vs);
1121da177e4SLinus Torvalds #endif
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	/*
1151da177e4SLinus Torvalds 	 * Tiny number?
1161da177e4SLinus Torvalds 	 */
1171da177e4SLinus Torvalds 	underflow = exponent < 0;
1181da177e4SLinus Torvalds 	if (underflow) {
1191da177e4SLinus Torvalds 		significand = vfp_shiftright32jamming(significand, -exponent);
1201da177e4SLinus Torvalds 		exponent = 0;
1211da177e4SLinus Torvalds #ifdef DEBUG
1221da177e4SLinus Torvalds 		vs->exponent = exponent;
1231da177e4SLinus Torvalds 		vs->significand = significand;
1241da177e4SLinus Torvalds 		vfp_single_dump("pack: tiny number", vs);
1251da177e4SLinus Torvalds #endif
1261da177e4SLinus Torvalds 		if (!(significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1)))
1271da177e4SLinus Torvalds 			underflow = 0;
1281da177e4SLinus Torvalds 	}
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	/*
1311da177e4SLinus Torvalds 	 * Select rounding increment.
1321da177e4SLinus Torvalds 	 */
1331da177e4SLinus Torvalds 	incr = 0;
1341da177e4SLinus Torvalds 	rmode = fpscr & FPSCR_RMODE_MASK;
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 	if (rmode == FPSCR_ROUND_NEAREST) {
1371da177e4SLinus Torvalds 		incr = 1 << VFP_SINGLE_LOW_BITS;
1381da177e4SLinus Torvalds 		if ((significand & (1 << (VFP_SINGLE_LOW_BITS + 1))) == 0)
1391da177e4SLinus Torvalds 			incr -= 1;
1401da177e4SLinus Torvalds 	} else if (rmode == FPSCR_ROUND_TOZERO) {
1411da177e4SLinus Torvalds 		incr = 0;
1421da177e4SLinus Torvalds 	} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0))
1431da177e4SLinus Torvalds 		incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1;
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	pr_debug("VFP: rounding increment = 0x%08x\n", incr);
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 	/*
1481da177e4SLinus Torvalds 	 * Is our rounding going to overflow?
1491da177e4SLinus Torvalds 	 */
1501da177e4SLinus Torvalds 	if ((significand + incr) < significand) {
1511da177e4SLinus Torvalds 		exponent += 1;
1521da177e4SLinus Torvalds 		significand = (significand >> 1) | (significand & 1);
1531da177e4SLinus Torvalds 		incr >>= 1;
1541da177e4SLinus Torvalds #ifdef DEBUG
1551da177e4SLinus Torvalds 		vs->exponent = exponent;
1561da177e4SLinus Torvalds 		vs->significand = significand;
1571da177e4SLinus Torvalds 		vfp_single_dump("pack: overflow", vs);
1581da177e4SLinus Torvalds #endif
1591da177e4SLinus Torvalds 	}
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	/*
1621da177e4SLinus Torvalds 	 * If any of the low bits (which will be shifted out of the
1631da177e4SLinus Torvalds 	 * number) are non-zero, the result is inexact.
1641da177e4SLinus Torvalds 	 */
1651da177e4SLinus Torvalds 	if (significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1))
1661da177e4SLinus Torvalds 		exceptions |= FPSCR_IXC;
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	/*
1691da177e4SLinus Torvalds 	 * Do our rounding.
1701da177e4SLinus Torvalds 	 */
1711da177e4SLinus Torvalds 	significand += incr;
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 	/*
1741da177e4SLinus Torvalds 	 * Infinity?
1751da177e4SLinus Torvalds 	 */
1761da177e4SLinus Torvalds 	if (exponent >= 254) {
1771da177e4SLinus Torvalds 		exceptions |= FPSCR_OFC | FPSCR_IXC;
1781da177e4SLinus Torvalds 		if (incr == 0) {
1791da177e4SLinus Torvalds 			vs->exponent = 253;
1801da177e4SLinus Torvalds 			vs->significand = 0x7fffffff;
1811da177e4SLinus Torvalds 		} else {
1821da177e4SLinus Torvalds 			vs->exponent = 255;		/* infinity */
1831da177e4SLinus Torvalds 			vs->significand = 0;
1841da177e4SLinus Torvalds 		}
1851da177e4SLinus Torvalds 	} else {
1861da177e4SLinus Torvalds 		if (significand >> (VFP_SINGLE_LOW_BITS + 1) == 0)
1871da177e4SLinus Torvalds 			exponent = 0;
1881da177e4SLinus Torvalds 		if (exponent || significand > 0x80000000)
1891da177e4SLinus Torvalds 			underflow = 0;
1901da177e4SLinus Torvalds 		if (underflow)
1911da177e4SLinus Torvalds 			exceptions |= FPSCR_UFC;
1921da177e4SLinus Torvalds 		vs->exponent = exponent;
1931da177e4SLinus Torvalds 		vs->significand = significand >> 1;
1941da177e4SLinus Torvalds 	}
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds  pack:
1971da177e4SLinus Torvalds 	vfp_single_dump("pack: final", vs);
1981da177e4SLinus Torvalds 	{
1991da177e4SLinus Torvalds 		s32 d = vfp_single_pack(vs);
20042d3fb5aSFrederik Deweerdt #ifdef DEBUG
2011da177e4SLinus Torvalds 		pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func,
2021da177e4SLinus Torvalds 			 sd, d, exceptions);
20342d3fb5aSFrederik Deweerdt #endif
2040355b3e0SDaniel Jacobowitz 		vfp_put_float(d, sd);
2051da177e4SLinus Torvalds 	}
2061da177e4SLinus Torvalds 
207928bd1b4SRussell King 	return exceptions;
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds /*
2111da177e4SLinus Torvalds  * Propagate the NaN, setting exceptions if it is signalling.
2121da177e4SLinus Torvalds  * 'n' is always a NaN.  'm' may be a number, NaN or infinity.
2131da177e4SLinus Torvalds  */
2141da177e4SLinus Torvalds static u32
vfp_propagate_nan(struct vfp_single * vsd,struct vfp_single * vsn,struct vfp_single * vsm,u32 fpscr)2151da177e4SLinus Torvalds vfp_propagate_nan(struct vfp_single *vsd, struct vfp_single *vsn,
2161da177e4SLinus Torvalds 		  struct vfp_single *vsm, u32 fpscr)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds 	struct vfp_single *nan;
2191da177e4SLinus Torvalds 	int tn, tm = 0;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	tn = vfp_single_type(vsn);
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	if (vsm)
2241da177e4SLinus Torvalds 		tm = vfp_single_type(vsm);
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 	if (fpscr & FPSCR_DEFAULT_NAN)
2271da177e4SLinus Torvalds 		/*
2281da177e4SLinus Torvalds 		 * Default NaN mode - always returns a quiet NaN
2291da177e4SLinus Torvalds 		 */
2301da177e4SLinus Torvalds 		nan = &vfp_single_default_qnan;
2311da177e4SLinus Torvalds 	else {
2321da177e4SLinus Torvalds 		/*
2331da177e4SLinus Torvalds 		 * Contemporary mode - select the first signalling
2341da177e4SLinus Torvalds 		 * NAN, or if neither are signalling, the first
2351da177e4SLinus Torvalds 		 * quiet NAN.
2361da177e4SLinus Torvalds 		 */
2371da177e4SLinus Torvalds 		if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
2381da177e4SLinus Torvalds 			nan = vsn;
2391da177e4SLinus Torvalds 		else
2401da177e4SLinus Torvalds 			nan = vsm;
2411da177e4SLinus Torvalds 		/*
2421da177e4SLinus Torvalds 		 * Make the NaN quiet.
2431da177e4SLinus Torvalds 		 */
2441da177e4SLinus Torvalds 		nan->significand |= VFP_SINGLE_SIGNIFICAND_QNAN;
2451da177e4SLinus Torvalds 	}
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds 	*vsd = *nan;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	/*
2501da177e4SLinus Torvalds 	 * If one was a signalling NAN, raise invalid operation.
2511da177e4SLinus Torvalds 	 */
2521da177e4SLinus Torvalds 	return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG;
2531da177e4SLinus Torvalds }
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds /*
2571da177e4SLinus Torvalds  * Extended operations
2581da177e4SLinus Torvalds  */
vfp_single_fabs(int sd,int unused,s32 m,u32 fpscr)2591da177e4SLinus Torvalds static u32 vfp_single_fabs(int sd, int unused, s32 m, u32 fpscr)
2601da177e4SLinus Torvalds {
2610355b3e0SDaniel Jacobowitz 	vfp_put_float(vfp_single_packed_abs(m), sd);
2621da177e4SLinus Torvalds 	return 0;
2631da177e4SLinus Torvalds }
2641da177e4SLinus Torvalds 
vfp_single_fcpy(int sd,int unused,s32 m,u32 fpscr)2651da177e4SLinus Torvalds static u32 vfp_single_fcpy(int sd, int unused, s32 m, u32 fpscr)
2661da177e4SLinus Torvalds {
2670355b3e0SDaniel Jacobowitz 	vfp_put_float(m, sd);
2681da177e4SLinus Torvalds 	return 0;
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
vfp_single_fneg(int sd,int unused,s32 m,u32 fpscr)2711da177e4SLinus Torvalds static u32 vfp_single_fneg(int sd, int unused, s32 m, u32 fpscr)
2721da177e4SLinus Torvalds {
2730355b3e0SDaniel Jacobowitz 	vfp_put_float(vfp_single_packed_negate(m), sd);
2741da177e4SLinus Torvalds 	return 0;
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds static const u16 sqrt_oddadjust[] = {
2781da177e4SLinus Torvalds 	0x0004, 0x0022, 0x005d, 0x00b1, 0x011d, 0x019f, 0x0236, 0x02e0,
2791da177e4SLinus Torvalds 	0x039c, 0x0468, 0x0545, 0x0631, 0x072b, 0x0832, 0x0946, 0x0a67
2801da177e4SLinus Torvalds };
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds static const u16 sqrt_evenadjust[] = {
2831da177e4SLinus Torvalds 	0x0a2d, 0x08af, 0x075a, 0x0629, 0x051a, 0x0429, 0x0356, 0x029e,
2841da177e4SLinus Torvalds 	0x0200, 0x0179, 0x0109, 0x00af, 0x0068, 0x0034, 0x0012, 0x0002
2851da177e4SLinus Torvalds };
2861da177e4SLinus Torvalds 
vfp_estimate_sqrt_significand(u32 exponent,u32 significand)2871da177e4SLinus Torvalds u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds 	int index;
2901da177e4SLinus Torvalds 	u32 z, a;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	if ((significand & 0xc0000000) != 0x40000000) {
293*4ed89f22SRussell King 		pr_warn("VFP: estimate_sqrt: invalid significand\n");
2941da177e4SLinus Torvalds 	}
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	a = significand << 1;
2971da177e4SLinus Torvalds 	index = (a >> 27) & 15;
2981da177e4SLinus Torvalds 	if (exponent & 1) {
2991da177e4SLinus Torvalds 		z = 0x4000 + (a >> 17) - sqrt_oddadjust[index];
3001da177e4SLinus Torvalds 		z = ((a / z) << 14) + (z << 15);
3011da177e4SLinus Torvalds 		a >>= 1;
3021da177e4SLinus Torvalds 	} else {
3031da177e4SLinus Torvalds 		z = 0x8000 + (a >> 17) - sqrt_evenadjust[index];
3041da177e4SLinus Torvalds 		z = a / z + z;
3051da177e4SLinus Torvalds 		z = (z >= 0x20000) ? 0xffff8000 : (z << 15);
3061da177e4SLinus Torvalds 		if (z <= a)
3071da177e4SLinus Torvalds 			return (s32)a >> 1;
3081da177e4SLinus Torvalds 	}
309438a7616SRussell King 	{
310438a7616SRussell King 		u64 v = (u64)a << 31;
311438a7616SRussell King 		do_div(v, z);
312438a7616SRussell King 		return v + (z >> 1);
313438a7616SRussell King 	}
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
vfp_single_fsqrt(int sd,int unused,s32 m,u32 fpscr)3161da177e4SLinus Torvalds static u32 vfp_single_fsqrt(int sd, int unused, s32 m, u32 fpscr)
3171da177e4SLinus Torvalds {
3181da177e4SLinus Torvalds 	struct vfp_single vsm, vsd;
3191da177e4SLinus Torvalds 	int ret, tm;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
3221da177e4SLinus Torvalds 	tm = vfp_single_type(&vsm);
3231da177e4SLinus Torvalds 	if (tm & (VFP_NAN|VFP_INFINITY)) {
3241da177e4SLinus Torvalds 		struct vfp_single *vsp = &vsd;
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 		if (tm & VFP_NAN)
3271da177e4SLinus Torvalds 			ret = vfp_propagate_nan(vsp, &vsm, NULL, fpscr);
3281da177e4SLinus Torvalds 		else if (vsm.sign == 0) {
3291da177e4SLinus Torvalds  sqrt_copy:
3301da177e4SLinus Torvalds 			vsp = &vsm;
3311da177e4SLinus Torvalds 			ret = 0;
3321da177e4SLinus Torvalds 		} else {
3331da177e4SLinus Torvalds  sqrt_invalid:
3341da177e4SLinus Torvalds 			vsp = &vfp_single_default_qnan;
3351da177e4SLinus Torvalds 			ret = FPSCR_IOC;
3361da177e4SLinus Torvalds 		}
3370355b3e0SDaniel Jacobowitz 		vfp_put_float(vfp_single_pack(vsp), sd);
3381da177e4SLinus Torvalds 		return ret;
3391da177e4SLinus Torvalds 	}
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 	/*
3421da177e4SLinus Torvalds 	 * sqrt(+/- 0) == +/- 0
3431da177e4SLinus Torvalds 	 */
3441da177e4SLinus Torvalds 	if (tm & VFP_ZERO)
3451da177e4SLinus Torvalds 		goto sqrt_copy;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	/*
3481da177e4SLinus Torvalds 	 * Normalise a denormalised number
3491da177e4SLinus Torvalds 	 */
3501da177e4SLinus Torvalds 	if (tm & VFP_DENORMAL)
3511da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsm);
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	/*
3541da177e4SLinus Torvalds 	 * sqrt(<0) = invalid
3551da177e4SLinus Torvalds 	 */
3561da177e4SLinus Torvalds 	if (vsm.sign)
3571da177e4SLinus Torvalds 		goto sqrt_invalid;
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 	vfp_single_dump("sqrt", &vsm);
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	/*
3621da177e4SLinus Torvalds 	 * Estimate the square root.
3631da177e4SLinus Torvalds 	 */
3641da177e4SLinus Torvalds 	vsd.sign = 0;
3651da177e4SLinus Torvalds 	vsd.exponent = ((vsm.exponent - 127) >> 1) + 127;
3661da177e4SLinus Torvalds 	vsd.significand = vfp_estimate_sqrt_significand(vsm.exponent, vsm.significand) + 2;
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 	vfp_single_dump("sqrt estimate", &vsd);
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	/*
3711da177e4SLinus Torvalds 	 * And now adjust.
3721da177e4SLinus Torvalds 	 */
3731da177e4SLinus Torvalds 	if ((vsd.significand & VFP_SINGLE_LOW_BITS_MASK) <= 5) {
3741da177e4SLinus Torvalds 		if (vsd.significand < 2) {
3751da177e4SLinus Torvalds 			vsd.significand = 0xffffffff;
3761da177e4SLinus Torvalds 		} else {
3771da177e4SLinus Torvalds 			u64 term;
3781da177e4SLinus Torvalds 			s64 rem;
3791da177e4SLinus Torvalds 			vsm.significand <<= !(vsm.exponent & 1);
3801da177e4SLinus Torvalds 			term = (u64)vsd.significand * vsd.significand;
3811da177e4SLinus Torvalds 			rem = ((u64)vsm.significand << 32) - term;
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds 			pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem);
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds 			while (rem < 0) {
3861da177e4SLinus Torvalds 				vsd.significand -= 1;
3871da177e4SLinus Torvalds 				rem += ((u64)vsd.significand << 1) | 1;
3881da177e4SLinus Torvalds 			}
3891da177e4SLinus Torvalds 			vsd.significand |= rem != 0;
3901da177e4SLinus Torvalds 		}
3911da177e4SLinus Torvalds 	}
3921da177e4SLinus Torvalds 	vsd.significand = vfp_shiftright32jamming(vsd.significand, 1);
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 	return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fsqrt");
3951da177e4SLinus Torvalds }
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds /*
3981da177e4SLinus Torvalds  * Equal	:= ZC
3991da177e4SLinus Torvalds  * Less than	:= N
4001da177e4SLinus Torvalds  * Greater than	:= C
4011da177e4SLinus Torvalds  * Unordered	:= CV
4021da177e4SLinus Torvalds  */
vfp_compare(int sd,int signal_on_qnan,s32 m,u32 fpscr)4031da177e4SLinus Torvalds static u32 vfp_compare(int sd, int signal_on_qnan, s32 m, u32 fpscr)
4041da177e4SLinus Torvalds {
4051da177e4SLinus Torvalds 	s32 d;
4061da177e4SLinus Torvalds 	u32 ret = 0;
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	d = vfp_get_float(sd);
4091da177e4SLinus Torvalds 	if (vfp_single_packed_exponent(m) == 255 && vfp_single_packed_mantissa(m)) {
4101da177e4SLinus Torvalds 		ret |= FPSCR_C | FPSCR_V;
4111da177e4SLinus Torvalds 		if (signal_on_qnan || !(vfp_single_packed_mantissa(m) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1))))
4121da177e4SLinus Torvalds 			/*
4131da177e4SLinus Torvalds 			 * Signalling NaN, or signalling on quiet NaN
4141da177e4SLinus Torvalds 			 */
4151da177e4SLinus Torvalds 			ret |= FPSCR_IOC;
4161da177e4SLinus Torvalds 	}
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 	if (vfp_single_packed_exponent(d) == 255 && vfp_single_packed_mantissa(d)) {
4191da177e4SLinus Torvalds 		ret |= FPSCR_C | FPSCR_V;
4201da177e4SLinus Torvalds 		if (signal_on_qnan || !(vfp_single_packed_mantissa(d) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1))))
4211da177e4SLinus Torvalds 			/*
4221da177e4SLinus Torvalds 			 * Signalling NaN, or signalling on quiet NaN
4231da177e4SLinus Torvalds 			 */
4241da177e4SLinus Torvalds 			ret |= FPSCR_IOC;
4251da177e4SLinus Torvalds 	}
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	if (ret == 0) {
4281da177e4SLinus Torvalds 		if (d == m || vfp_single_packed_abs(d | m) == 0) {
4291da177e4SLinus Torvalds 			/*
4301da177e4SLinus Torvalds 			 * equal
4311da177e4SLinus Torvalds 			 */
4321da177e4SLinus Torvalds 			ret |= FPSCR_Z | FPSCR_C;
4331da177e4SLinus Torvalds 		} else if (vfp_single_packed_sign(d ^ m)) {
4341da177e4SLinus Torvalds 			/*
4351da177e4SLinus Torvalds 			 * different signs
4361da177e4SLinus Torvalds 			 */
4371da177e4SLinus Torvalds 			if (vfp_single_packed_sign(d))
4381da177e4SLinus Torvalds 				/*
4391da177e4SLinus Torvalds 				 * d is negative, so d < m
4401da177e4SLinus Torvalds 				 */
4411da177e4SLinus Torvalds 				ret |= FPSCR_N;
4421da177e4SLinus Torvalds 			else
4431da177e4SLinus Torvalds 				/*
4441da177e4SLinus Torvalds 				 * d is positive, so d > m
4451da177e4SLinus Torvalds 				 */
4461da177e4SLinus Torvalds 				ret |= FPSCR_C;
4471da177e4SLinus Torvalds 		} else if ((vfp_single_packed_sign(d) != 0) ^ (d < m)) {
4481da177e4SLinus Torvalds 			/*
4491da177e4SLinus Torvalds 			 * d < m
4501da177e4SLinus Torvalds 			 */
4511da177e4SLinus Torvalds 			ret |= FPSCR_N;
4521da177e4SLinus Torvalds 		} else if ((vfp_single_packed_sign(d) != 0) ^ (d > m)) {
4531da177e4SLinus Torvalds 			/*
4541da177e4SLinus Torvalds 			 * d > m
4551da177e4SLinus Torvalds 			 */
4561da177e4SLinus Torvalds 			ret |= FPSCR_C;
4571da177e4SLinus Torvalds 		}
4581da177e4SLinus Torvalds 	}
4591da177e4SLinus Torvalds 	return ret;
4601da177e4SLinus Torvalds }
4611da177e4SLinus Torvalds 
vfp_single_fcmp(int sd,int unused,s32 m,u32 fpscr)4621da177e4SLinus Torvalds static u32 vfp_single_fcmp(int sd, int unused, s32 m, u32 fpscr)
4631da177e4SLinus Torvalds {
4641da177e4SLinus Torvalds 	return vfp_compare(sd, 0, m, fpscr);
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds 
vfp_single_fcmpe(int sd,int unused,s32 m,u32 fpscr)4671da177e4SLinus Torvalds static u32 vfp_single_fcmpe(int sd, int unused, s32 m, u32 fpscr)
4681da177e4SLinus Torvalds {
4691da177e4SLinus Torvalds 	return vfp_compare(sd, 1, m, fpscr);
4701da177e4SLinus Torvalds }
4711da177e4SLinus Torvalds 
vfp_single_fcmpz(int sd,int unused,s32 m,u32 fpscr)4721da177e4SLinus Torvalds static u32 vfp_single_fcmpz(int sd, int unused, s32 m, u32 fpscr)
4731da177e4SLinus Torvalds {
4741da177e4SLinus Torvalds 	return vfp_compare(sd, 0, 0, fpscr);
4751da177e4SLinus Torvalds }
4761da177e4SLinus Torvalds 
vfp_single_fcmpez(int sd,int unused,s32 m,u32 fpscr)4771da177e4SLinus Torvalds static u32 vfp_single_fcmpez(int sd, int unused, s32 m, u32 fpscr)
4781da177e4SLinus Torvalds {
4791da177e4SLinus Torvalds 	return vfp_compare(sd, 1, 0, fpscr);
4801da177e4SLinus Torvalds }
4811da177e4SLinus Torvalds 
vfp_single_fcvtd(int dd,int unused,s32 m,u32 fpscr)4821da177e4SLinus Torvalds static u32 vfp_single_fcvtd(int dd, int unused, s32 m, u32 fpscr)
4831da177e4SLinus Torvalds {
4841da177e4SLinus Torvalds 	struct vfp_single vsm;
4851da177e4SLinus Torvalds 	struct vfp_double vdd;
4861da177e4SLinus Torvalds 	int tm;
4871da177e4SLinus Torvalds 	u32 exceptions = 0;
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds 	tm = vfp_single_type(&vsm);
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 	/*
4941da177e4SLinus Torvalds 	 * If we have a signalling NaN, signal invalid operation.
4951da177e4SLinus Torvalds 	 */
4961da177e4SLinus Torvalds 	if (tm == VFP_SNAN)
4971da177e4SLinus Torvalds 		exceptions = FPSCR_IOC;
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds 	if (tm & VFP_DENORMAL)
5001da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsm);
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 	vdd.sign = vsm.sign;
5031da177e4SLinus Torvalds 	vdd.significand = (u64)vsm.significand << 32;
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 	/*
5061da177e4SLinus Torvalds 	 * If we have an infinity or NaN, the exponent must be 2047.
5071da177e4SLinus Torvalds 	 */
5081da177e4SLinus Torvalds 	if (tm & (VFP_INFINITY|VFP_NAN)) {
5091da177e4SLinus Torvalds 		vdd.exponent = 2047;
510b53a2b41SDaniel Jacobowitz 		if (tm == VFP_QNAN)
5111da177e4SLinus Torvalds 			vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
5121da177e4SLinus Torvalds 		goto pack_nan;
5131da177e4SLinus Torvalds 	} else if (tm & VFP_ZERO)
5141da177e4SLinus Torvalds 		vdd.exponent = 0;
5151da177e4SLinus Torvalds 	else
5161da177e4SLinus Torvalds 		vdd.exponent = vsm.exponent + (1023 - 127);
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds 	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fcvtd");
5191da177e4SLinus Torvalds 
5201da177e4SLinus Torvalds  pack_nan:
5210355b3e0SDaniel Jacobowitz 	vfp_put_double(vfp_double_pack(&vdd), dd);
5221da177e4SLinus Torvalds 	return exceptions;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds 
vfp_single_fuito(int sd,int unused,s32 m,u32 fpscr)5251da177e4SLinus Torvalds static u32 vfp_single_fuito(int sd, int unused, s32 m, u32 fpscr)
5261da177e4SLinus Torvalds {
5271da177e4SLinus Torvalds 	struct vfp_single vs;
5281da177e4SLinus Torvalds 
5291da177e4SLinus Torvalds 	vs.sign = 0;
5301da177e4SLinus Torvalds 	vs.exponent = 127 + 31 - 1;
5311da177e4SLinus Torvalds 	vs.significand = (u32)m;
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fuito");
5341da177e4SLinus Torvalds }
5351da177e4SLinus Torvalds 
vfp_single_fsito(int sd,int unused,s32 m,u32 fpscr)5361da177e4SLinus Torvalds static u32 vfp_single_fsito(int sd, int unused, s32 m, u32 fpscr)
5371da177e4SLinus Torvalds {
5381da177e4SLinus Torvalds 	struct vfp_single vs;
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds 	vs.sign = (m & 0x80000000) >> 16;
5411da177e4SLinus Torvalds 	vs.exponent = 127 + 31 - 1;
5421da177e4SLinus Torvalds 	vs.significand = vs.sign ? -m : m;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fsito");
5451da177e4SLinus Torvalds }
5461da177e4SLinus Torvalds 
vfp_single_ftoui(int sd,int unused,s32 m,u32 fpscr)5471da177e4SLinus Torvalds static u32 vfp_single_ftoui(int sd, int unused, s32 m, u32 fpscr)
5481da177e4SLinus Torvalds {
5491da177e4SLinus Torvalds 	struct vfp_single vsm;
5501da177e4SLinus Torvalds 	u32 d, exceptions = 0;
5511da177e4SLinus Torvalds 	int rmode = fpscr & FPSCR_RMODE_MASK;
5521da177e4SLinus Torvalds 	int tm;
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
5551da177e4SLinus Torvalds 	vfp_single_dump("VSM", &vsm);
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds 	/*
5581da177e4SLinus Torvalds 	 * Do we have a denormalised number?
5591da177e4SLinus Torvalds 	 */
5601da177e4SLinus Torvalds 	tm = vfp_single_type(&vsm);
5611da177e4SLinus Torvalds 	if (tm & VFP_DENORMAL)
5621da177e4SLinus Torvalds 		exceptions |= FPSCR_IDC;
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 	if (tm & VFP_NAN)
5651da177e4SLinus Torvalds 		vsm.sign = 0;
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 	if (vsm.exponent >= 127 + 32) {
5681da177e4SLinus Torvalds 		d = vsm.sign ? 0 : 0xffffffff;
5691da177e4SLinus Torvalds 		exceptions = FPSCR_IOC;
5701da177e4SLinus Torvalds 	} else if (vsm.exponent >= 127 - 1) {
5711da177e4SLinus Torvalds 		int shift = 127 + 31 - vsm.exponent;
5721da177e4SLinus Torvalds 		u32 rem, incr = 0;
5731da177e4SLinus Torvalds 
5741da177e4SLinus Torvalds 		/*
5751da177e4SLinus Torvalds 		 * 2^0 <= m < 2^32-2^8
5761da177e4SLinus Torvalds 		 */
5771da177e4SLinus Torvalds 		d = (vsm.significand << 1) >> shift;
5781da177e4SLinus Torvalds 		rem = vsm.significand << (33 - shift);
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds 		if (rmode == FPSCR_ROUND_NEAREST) {
5811da177e4SLinus Torvalds 			incr = 0x80000000;
5821da177e4SLinus Torvalds 			if ((d & 1) == 0)
5831da177e4SLinus Torvalds 				incr -= 1;
5841da177e4SLinus Torvalds 		} else if (rmode == FPSCR_ROUND_TOZERO) {
5851da177e4SLinus Torvalds 			incr = 0;
5861da177e4SLinus Torvalds 		} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) {
5871da177e4SLinus Torvalds 			incr = ~0;
5881da177e4SLinus Torvalds 		}
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 		if ((rem + incr) < rem) {
5911da177e4SLinus Torvalds 			if (d < 0xffffffff)
5921da177e4SLinus Torvalds 				d += 1;
5931da177e4SLinus Torvalds 			else
5941da177e4SLinus Torvalds 				exceptions |= FPSCR_IOC;
5951da177e4SLinus Torvalds 		}
5961da177e4SLinus Torvalds 
5971da177e4SLinus Torvalds 		if (d && vsm.sign) {
5981da177e4SLinus Torvalds 			d = 0;
5991da177e4SLinus Torvalds 			exceptions |= FPSCR_IOC;
6001da177e4SLinus Torvalds 		} else if (rem)
6011da177e4SLinus Torvalds 			exceptions |= FPSCR_IXC;
6021da177e4SLinus Torvalds 	} else {
6031da177e4SLinus Torvalds 		d = 0;
6041da177e4SLinus Torvalds 		if (vsm.exponent | vsm.significand) {
6051da177e4SLinus Torvalds 			exceptions |= FPSCR_IXC;
6061da177e4SLinus Torvalds 			if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
6071da177e4SLinus Torvalds 				d = 1;
6081da177e4SLinus Torvalds 			else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
6091da177e4SLinus Torvalds 				d = 0;
6101da177e4SLinus Torvalds 				exceptions |= FPSCR_IOC;
6111da177e4SLinus Torvalds 			}
6121da177e4SLinus Torvalds 		}
6131da177e4SLinus Torvalds 	}
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds 	pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
6161da177e4SLinus Torvalds 
6170355b3e0SDaniel Jacobowitz 	vfp_put_float(d, sd);
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	return exceptions;
6201da177e4SLinus Torvalds }
6211da177e4SLinus Torvalds 
vfp_single_ftouiz(int sd,int unused,s32 m,u32 fpscr)6221da177e4SLinus Torvalds static u32 vfp_single_ftouiz(int sd, int unused, s32 m, u32 fpscr)
6231da177e4SLinus Torvalds {
6241da177e4SLinus Torvalds 	return vfp_single_ftoui(sd, unused, m, FPSCR_ROUND_TOZERO);
6251da177e4SLinus Torvalds }
6261da177e4SLinus Torvalds 
vfp_single_ftosi(int sd,int unused,s32 m,u32 fpscr)6271da177e4SLinus Torvalds static u32 vfp_single_ftosi(int sd, int unused, s32 m, u32 fpscr)
6281da177e4SLinus Torvalds {
6291da177e4SLinus Torvalds 	struct vfp_single vsm;
6301da177e4SLinus Torvalds 	u32 d, exceptions = 0;
6311da177e4SLinus Torvalds 	int rmode = fpscr & FPSCR_RMODE_MASK;
6321320a80dSCatalin Marinas 	int tm;
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
6351da177e4SLinus Torvalds 	vfp_single_dump("VSM", &vsm);
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 	/*
6381da177e4SLinus Torvalds 	 * Do we have a denormalised number?
6391da177e4SLinus Torvalds 	 */
6401320a80dSCatalin Marinas 	tm = vfp_single_type(&vsm);
6411da177e4SLinus Torvalds 	if (vfp_single_type(&vsm) & VFP_DENORMAL)
6421da177e4SLinus Torvalds 		exceptions |= FPSCR_IDC;
6431da177e4SLinus Torvalds 
6441320a80dSCatalin Marinas 	if (tm & VFP_NAN) {
6451320a80dSCatalin Marinas 		d = 0;
6461320a80dSCatalin Marinas 		exceptions |= FPSCR_IOC;
6471320a80dSCatalin Marinas 	} else if (vsm.exponent >= 127 + 32) {
6481da177e4SLinus Torvalds 		/*
6491da177e4SLinus Torvalds 		 * m >= 2^31-2^7: invalid
6501da177e4SLinus Torvalds 		 */
6511da177e4SLinus Torvalds 		d = 0x7fffffff;
6521da177e4SLinus Torvalds 		if (vsm.sign)
6531da177e4SLinus Torvalds 			d = ~d;
6541da177e4SLinus Torvalds 		exceptions |= FPSCR_IOC;
6551da177e4SLinus Torvalds 	} else if (vsm.exponent >= 127 - 1) {
6561da177e4SLinus Torvalds 		int shift = 127 + 31 - vsm.exponent;
6571da177e4SLinus Torvalds 		u32 rem, incr = 0;
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 		/* 2^0 <= m <= 2^31-2^7 */
6601da177e4SLinus Torvalds 		d = (vsm.significand << 1) >> shift;
6611da177e4SLinus Torvalds 		rem = vsm.significand << (33 - shift);
6621da177e4SLinus Torvalds 
6631da177e4SLinus Torvalds 		if (rmode == FPSCR_ROUND_NEAREST) {
6641da177e4SLinus Torvalds 			incr = 0x80000000;
6651da177e4SLinus Torvalds 			if ((d & 1) == 0)
6661da177e4SLinus Torvalds 				incr -= 1;
6671da177e4SLinus Torvalds 		} else if (rmode == FPSCR_ROUND_TOZERO) {
6681da177e4SLinus Torvalds 			incr = 0;
6691da177e4SLinus Torvalds 		} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) {
6701da177e4SLinus Torvalds 			incr = ~0;
6711da177e4SLinus Torvalds 		}
6721da177e4SLinus Torvalds 
6731da177e4SLinus Torvalds 		if ((rem + incr) < rem && d < 0xffffffff)
6741da177e4SLinus Torvalds 			d += 1;
6751da177e4SLinus Torvalds 		if (d > 0x7fffffff + (vsm.sign != 0)) {
6761da177e4SLinus Torvalds 			d = 0x7fffffff + (vsm.sign != 0);
6771da177e4SLinus Torvalds 			exceptions |= FPSCR_IOC;
6781da177e4SLinus Torvalds 		} else if (rem)
6791da177e4SLinus Torvalds 			exceptions |= FPSCR_IXC;
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 		if (vsm.sign)
6821da177e4SLinus Torvalds 			d = -d;
6831da177e4SLinus Torvalds 	} else {
6841da177e4SLinus Torvalds 		d = 0;
6851da177e4SLinus Torvalds 		if (vsm.exponent | vsm.significand) {
6861da177e4SLinus Torvalds 			exceptions |= FPSCR_IXC;
6871da177e4SLinus Torvalds 			if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
6881da177e4SLinus Torvalds 				d = 1;
6891da177e4SLinus Torvalds 			else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign)
6901da177e4SLinus Torvalds 				d = -1;
6911da177e4SLinus Torvalds 		}
6921da177e4SLinus Torvalds 	}
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds 	pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
6951da177e4SLinus Torvalds 
6960355b3e0SDaniel Jacobowitz 	vfp_put_float((s32)d, sd);
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	return exceptions;
6991da177e4SLinus Torvalds }
7001da177e4SLinus Torvalds 
vfp_single_ftosiz(int sd,int unused,s32 m,u32 fpscr)7011da177e4SLinus Torvalds static u32 vfp_single_ftosiz(int sd, int unused, s32 m, u32 fpscr)
7021da177e4SLinus Torvalds {
7031da177e4SLinus Torvalds 	return vfp_single_ftosi(sd, unused, m, FPSCR_ROUND_TOZERO);
7041da177e4SLinus Torvalds }
7051da177e4SLinus Torvalds 
7064cc9bd2eSGen FUKATSU static struct op fops_ext[32] = {
7074cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FCPY)]	= { vfp_single_fcpy,   0 },
7084cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FABS)]	= { vfp_single_fabs,   0 },
7094cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FNEG)]	= { vfp_single_fneg,   0 },
7104cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FSQRT)]	= { vfp_single_fsqrt,  0 },
7114cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FCMP)]	= { vfp_single_fcmp,   OP_SCALAR },
7124cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FCMPE)]	= { vfp_single_fcmpe,  OP_SCALAR },
7134cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FCMPZ)]	= { vfp_single_fcmpz,  OP_SCALAR },
7144cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FCMPEZ)]	= { vfp_single_fcmpez, OP_SCALAR },
715baf97ce6SRussell King 	[FEXT_TO_IDX(FEXT_FCVT)]	= { vfp_single_fcvtd,  OP_SCALAR|OP_DD },
7164cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FUITO)]	= { vfp_single_fuito,  OP_SCALAR },
7174cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FSITO)]	= { vfp_single_fsito,  OP_SCALAR },
7184cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FTOUI)]	= { vfp_single_ftoui,  OP_SCALAR },
7194cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FTOUIZ)]	= { vfp_single_ftouiz, OP_SCALAR },
7204cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FTOSI)]	= { vfp_single_ftosi,  OP_SCALAR },
7214cc9bd2eSGen FUKATSU 	[FEXT_TO_IDX(FEXT_FTOSIZ)]	= { vfp_single_ftosiz, OP_SCALAR },
7221da177e4SLinus Torvalds };
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds static u32
vfp_single_fadd_nonnumber(struct vfp_single * vsd,struct vfp_single * vsn,struct vfp_single * vsm,u32 fpscr)7291da177e4SLinus Torvalds vfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn,
7301da177e4SLinus Torvalds 			  struct vfp_single *vsm, u32 fpscr)
7311da177e4SLinus Torvalds {
7321da177e4SLinus Torvalds 	struct vfp_single *vsp;
7331da177e4SLinus Torvalds 	u32 exceptions = 0;
7341da177e4SLinus Torvalds 	int tn, tm;
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 	tn = vfp_single_type(vsn);
7371da177e4SLinus Torvalds 	tm = vfp_single_type(vsm);
7381da177e4SLinus Torvalds 
7391da177e4SLinus Torvalds 	if (tn & tm & VFP_INFINITY) {
7401da177e4SLinus Torvalds 		/*
7411da177e4SLinus Torvalds 		 * Two infinities.  Are they different signs?
7421da177e4SLinus Torvalds 		 */
7431da177e4SLinus Torvalds 		if (vsn->sign ^ vsm->sign) {
7441da177e4SLinus Torvalds 			/*
7451da177e4SLinus Torvalds 			 * different signs -> invalid
7461da177e4SLinus Torvalds 			 */
7471da177e4SLinus Torvalds 			exceptions = FPSCR_IOC;
7481da177e4SLinus Torvalds 			vsp = &vfp_single_default_qnan;
7491da177e4SLinus Torvalds 		} else {
7501da177e4SLinus Torvalds 			/*
7511da177e4SLinus Torvalds 			 * same signs -> valid
7521da177e4SLinus Torvalds 			 */
7531da177e4SLinus Torvalds 			vsp = vsn;
7541da177e4SLinus Torvalds 		}
7551da177e4SLinus Torvalds 	} else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
7561da177e4SLinus Torvalds 		/*
7571da177e4SLinus Torvalds 		 * One infinity and one number -> infinity
7581da177e4SLinus Torvalds 		 */
7591da177e4SLinus Torvalds 		vsp = vsn;
7601da177e4SLinus Torvalds 	} else {
7611da177e4SLinus Torvalds 		/*
7621da177e4SLinus Torvalds 		 * 'n' is a NaN of some type
7631da177e4SLinus Torvalds 		 */
7641da177e4SLinus Torvalds 		return vfp_propagate_nan(vsd, vsn, vsm, fpscr);
7651da177e4SLinus Torvalds 	}
7661da177e4SLinus Torvalds 	*vsd = *vsp;
7671da177e4SLinus Torvalds 	return exceptions;
7681da177e4SLinus Torvalds }
7691da177e4SLinus Torvalds 
7701da177e4SLinus Torvalds static u32
vfp_single_add(struct vfp_single * vsd,struct vfp_single * vsn,struct vfp_single * vsm,u32 fpscr)7711da177e4SLinus Torvalds vfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn,
7721da177e4SLinus Torvalds 	       struct vfp_single *vsm, u32 fpscr)
7731da177e4SLinus Torvalds {
7741da177e4SLinus Torvalds 	u32 exp_diff, m_sig;
7751da177e4SLinus Torvalds 
7761da177e4SLinus Torvalds 	if (vsn->significand & 0x80000000 ||
7771da177e4SLinus Torvalds 	    vsm->significand & 0x80000000) {
7781da177e4SLinus Torvalds 		pr_info("VFP: bad FP values in %s\n", __func__);
7791da177e4SLinus Torvalds 		vfp_single_dump("VSN", vsn);
7801da177e4SLinus Torvalds 		vfp_single_dump("VSM", vsm);
7811da177e4SLinus Torvalds 	}
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	/*
7841da177e4SLinus Torvalds 	 * Ensure that 'n' is the largest magnitude number.  Note that
7851da177e4SLinus Torvalds 	 * if 'n' and 'm' have equal exponents, we do not swap them.
7861da177e4SLinus Torvalds 	 * This ensures that NaN propagation works correctly.
7871da177e4SLinus Torvalds 	 */
7881da177e4SLinus Torvalds 	if (vsn->exponent < vsm->exponent) {
7891da177e4SLinus Torvalds 		struct vfp_single *t = vsn;
7901da177e4SLinus Torvalds 		vsn = vsm;
7911da177e4SLinus Torvalds 		vsm = t;
7921da177e4SLinus Torvalds 	}
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 	/*
7951da177e4SLinus Torvalds 	 * Is 'n' an infinity or a NaN?  Note that 'm' may be a number,
7961da177e4SLinus Torvalds 	 * infinity or a NaN here.
7971da177e4SLinus Torvalds 	 */
7981da177e4SLinus Torvalds 	if (vsn->exponent == 255)
7991da177e4SLinus Torvalds 		return vfp_single_fadd_nonnumber(vsd, vsn, vsm, fpscr);
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds 	/*
8021da177e4SLinus Torvalds 	 * We have two proper numbers, where 'vsn' is the larger magnitude.
8031da177e4SLinus Torvalds 	 *
8041da177e4SLinus Torvalds 	 * Copy 'n' to 'd' before doing the arithmetic.
8051da177e4SLinus Torvalds 	 */
8061da177e4SLinus Torvalds 	*vsd = *vsn;
8071da177e4SLinus Torvalds 
8081da177e4SLinus Torvalds 	/*
8091da177e4SLinus Torvalds 	 * Align both numbers.
8101da177e4SLinus Torvalds 	 */
8111da177e4SLinus Torvalds 	exp_diff = vsn->exponent - vsm->exponent;
8121da177e4SLinus Torvalds 	m_sig = vfp_shiftright32jamming(vsm->significand, exp_diff);
8131da177e4SLinus Torvalds 
8141da177e4SLinus Torvalds 	/*
8151da177e4SLinus Torvalds 	 * If the signs are different, we are really subtracting.
8161da177e4SLinus Torvalds 	 */
8171da177e4SLinus Torvalds 	if (vsn->sign ^ vsm->sign) {
8181da177e4SLinus Torvalds 		m_sig = vsn->significand - m_sig;
8191da177e4SLinus Torvalds 		if ((s32)m_sig < 0) {
8201da177e4SLinus Torvalds 			vsd->sign = vfp_sign_negate(vsd->sign);
8211da177e4SLinus Torvalds 			m_sig = -m_sig;
8221da177e4SLinus Torvalds 		} else if (m_sig == 0) {
8231da177e4SLinus Torvalds 			vsd->sign = (fpscr & FPSCR_RMODE_MASK) ==
8241da177e4SLinus Torvalds 				      FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
8251da177e4SLinus Torvalds 		}
8261da177e4SLinus Torvalds 	} else {
8271da177e4SLinus Torvalds 		m_sig = vsn->significand + m_sig;
8281da177e4SLinus Torvalds 	}
8291da177e4SLinus Torvalds 	vsd->significand = m_sig;
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds 	return 0;
8321da177e4SLinus Torvalds }
8331da177e4SLinus Torvalds 
8341da177e4SLinus Torvalds static u32
vfp_single_multiply(struct vfp_single * vsd,struct vfp_single * vsn,struct vfp_single * vsm,u32 fpscr)8351da177e4SLinus Torvalds vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_single *vsm, u32 fpscr)
8361da177e4SLinus Torvalds {
8371da177e4SLinus Torvalds 	vfp_single_dump("VSN", vsn);
8381da177e4SLinus Torvalds 	vfp_single_dump("VSM", vsm);
8391da177e4SLinus Torvalds 
8401da177e4SLinus Torvalds 	/*
8411da177e4SLinus Torvalds 	 * Ensure that 'n' is the largest magnitude number.  Note that
8421da177e4SLinus Torvalds 	 * if 'n' and 'm' have equal exponents, we do not swap them.
8431da177e4SLinus Torvalds 	 * This ensures that NaN propagation works correctly.
8441da177e4SLinus Torvalds 	 */
8451da177e4SLinus Torvalds 	if (vsn->exponent < vsm->exponent) {
8461da177e4SLinus Torvalds 		struct vfp_single *t = vsn;
8471da177e4SLinus Torvalds 		vsn = vsm;
8481da177e4SLinus Torvalds 		vsm = t;
8491da177e4SLinus Torvalds 		pr_debug("VFP: swapping M <-> N\n");
8501da177e4SLinus Torvalds 	}
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds 	vsd->sign = vsn->sign ^ vsm->sign;
8531da177e4SLinus Torvalds 
8541da177e4SLinus Torvalds 	/*
8551da177e4SLinus Torvalds 	 * If 'n' is an infinity or NaN, handle it.  'm' may be anything.
8561da177e4SLinus Torvalds 	 */
8571da177e4SLinus Torvalds 	if (vsn->exponent == 255) {
8581da177e4SLinus Torvalds 		if (vsn->significand || (vsm->exponent == 255 && vsm->significand))
8591da177e4SLinus Torvalds 			return vfp_propagate_nan(vsd, vsn, vsm, fpscr);
8601da177e4SLinus Torvalds 		if ((vsm->exponent | vsm->significand) == 0) {
8611da177e4SLinus Torvalds 			*vsd = vfp_single_default_qnan;
8621da177e4SLinus Torvalds 			return FPSCR_IOC;
8631da177e4SLinus Torvalds 		}
8641da177e4SLinus Torvalds 		vsd->exponent = vsn->exponent;
8651da177e4SLinus Torvalds 		vsd->significand = 0;
8661da177e4SLinus Torvalds 		return 0;
8671da177e4SLinus Torvalds 	}
8681da177e4SLinus Torvalds 
8691da177e4SLinus Torvalds 	/*
8701da177e4SLinus Torvalds 	 * If 'm' is zero, the result is always zero.  In this case,
8711da177e4SLinus Torvalds 	 * 'n' may be zero or a number, but it doesn't matter which.
8721da177e4SLinus Torvalds 	 */
8731da177e4SLinus Torvalds 	if ((vsm->exponent | vsm->significand) == 0) {
8741da177e4SLinus Torvalds 		vsd->exponent = 0;
8751da177e4SLinus Torvalds 		vsd->significand = 0;
8761da177e4SLinus Torvalds 		return 0;
8771da177e4SLinus Torvalds 	}
8781da177e4SLinus Torvalds 
8791da177e4SLinus Torvalds 	/*
8801da177e4SLinus Torvalds 	 * We add 2 to the destination exponent for the same reason as
8811da177e4SLinus Torvalds 	 * the addition case - though this time we have +1 from each
8821da177e4SLinus Torvalds 	 * input operand.
8831da177e4SLinus Torvalds 	 */
8841da177e4SLinus Torvalds 	vsd->exponent = vsn->exponent + vsm->exponent - 127 + 2;
8851da177e4SLinus Torvalds 	vsd->significand = vfp_hi64to32jamming((u64)vsn->significand * vsm->significand);
8861da177e4SLinus Torvalds 
8871da177e4SLinus Torvalds 	vfp_single_dump("VSD", vsd);
8881da177e4SLinus Torvalds 	return 0;
8891da177e4SLinus Torvalds }
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds #define NEG_MULTIPLY	(1 << 0)
8921da177e4SLinus Torvalds #define NEG_SUBTRACT	(1 << 1)
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds static u32
vfp_single_multiply_accumulate(int sd,int sn,s32 m,u32 fpscr,u32 negate,char * func)8951da177e4SLinus Torvalds vfp_single_multiply_accumulate(int sd, int sn, s32 m, u32 fpscr, u32 negate, char *func)
8961da177e4SLinus Torvalds {
8971da177e4SLinus Torvalds 	struct vfp_single vsd, vsp, vsn, vsm;
8981da177e4SLinus Torvalds 	u32 exceptions;
8991da177e4SLinus Torvalds 	s32 v;
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	v = vfp_get_float(sn);
9021da177e4SLinus Torvalds 	pr_debug("VFP: s%u = %08x\n", sn, v);
9031da177e4SLinus Torvalds 	vfp_single_unpack(&vsn, v);
9041da177e4SLinus Torvalds 	if (vsn.exponent == 0 && vsn.significand)
9051da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsn);
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
9081da177e4SLinus Torvalds 	if (vsm.exponent == 0 && vsm.significand)
9091da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsm);
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds 	exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
9121da177e4SLinus Torvalds 	if (negate & NEG_MULTIPLY)
9131da177e4SLinus Torvalds 		vsp.sign = vfp_sign_negate(vsp.sign);
9141da177e4SLinus Torvalds 
9151da177e4SLinus Torvalds 	v = vfp_get_float(sd);
9161da177e4SLinus Torvalds 	pr_debug("VFP: s%u = %08x\n", sd, v);
9171da177e4SLinus Torvalds 	vfp_single_unpack(&vsn, v);
918244b4783SJay Foad 	if (vsn.exponent == 0 && vsn.significand)
919244b4783SJay Foad 		vfp_single_normalise_denormal(&vsn);
9201da177e4SLinus Torvalds 	if (negate & NEG_SUBTRACT)
9211da177e4SLinus Torvalds 		vsn.sign = vfp_sign_negate(vsn.sign);
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds 	exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
9241da177e4SLinus Torvalds 
9251da177e4SLinus Torvalds 	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, func);
9261da177e4SLinus Torvalds }
9271da177e4SLinus Torvalds 
9281da177e4SLinus Torvalds /*
9291da177e4SLinus Torvalds  * Standard operations
9301da177e4SLinus Torvalds  */
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds /*
9331da177e4SLinus Torvalds  * sd = sd + (sn * sm)
9341da177e4SLinus Torvalds  */
vfp_single_fmac(int sd,int sn,s32 m,u32 fpscr)9351da177e4SLinus Torvalds static u32 vfp_single_fmac(int sd, int sn, s32 m, u32 fpscr)
9361da177e4SLinus Torvalds {
9371da177e4SLinus Torvalds 	return vfp_single_multiply_accumulate(sd, sn, m, fpscr, 0, "fmac");
9381da177e4SLinus Torvalds }
9391da177e4SLinus Torvalds 
9401da177e4SLinus Torvalds /*
9411da177e4SLinus Torvalds  * sd = sd - (sn * sm)
9421da177e4SLinus Torvalds  */
vfp_single_fnmac(int sd,int sn,s32 m,u32 fpscr)9431da177e4SLinus Torvalds static u32 vfp_single_fnmac(int sd, int sn, s32 m, u32 fpscr)
9441da177e4SLinus Torvalds {
9451da177e4SLinus Torvalds 	return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac");
9461da177e4SLinus Torvalds }
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds /*
9491da177e4SLinus Torvalds  * sd = -sd + (sn * sm)
9501da177e4SLinus Torvalds  */
vfp_single_fmsc(int sd,int sn,s32 m,u32 fpscr)9511da177e4SLinus Torvalds static u32 vfp_single_fmsc(int sd, int sn, s32 m, u32 fpscr)
9521da177e4SLinus Torvalds {
9531da177e4SLinus Torvalds 	return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc");
9541da177e4SLinus Torvalds }
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds /*
9571da177e4SLinus Torvalds  * sd = -sd - (sn * sm)
9581da177e4SLinus Torvalds  */
vfp_single_fnmsc(int sd,int sn,s32 m,u32 fpscr)9591da177e4SLinus Torvalds static u32 vfp_single_fnmsc(int sd, int sn, s32 m, u32 fpscr)
9601da177e4SLinus Torvalds {
9611da177e4SLinus Torvalds 	return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
9621da177e4SLinus Torvalds }
9631da177e4SLinus Torvalds 
9641da177e4SLinus Torvalds /*
9651da177e4SLinus Torvalds  * sd = sn * sm
9661da177e4SLinus Torvalds  */
vfp_single_fmul(int sd,int sn,s32 m,u32 fpscr)9671da177e4SLinus Torvalds static u32 vfp_single_fmul(int sd, int sn, s32 m, u32 fpscr)
9681da177e4SLinus Torvalds {
9691da177e4SLinus Torvalds 	struct vfp_single vsd, vsn, vsm;
9701da177e4SLinus Torvalds 	u32 exceptions;
9711da177e4SLinus Torvalds 	s32 n = vfp_get_float(sn);
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds 	pr_debug("VFP: s%u = %08x\n", sn, n);
9741da177e4SLinus Torvalds 
9751da177e4SLinus Torvalds 	vfp_single_unpack(&vsn, n);
9761da177e4SLinus Torvalds 	if (vsn.exponent == 0 && vsn.significand)
9771da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsn);
9781da177e4SLinus Torvalds 
9791da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
9801da177e4SLinus Torvalds 	if (vsm.exponent == 0 && vsm.significand)
9811da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsm);
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 	exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
9841da177e4SLinus Torvalds 	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fmul");
9851da177e4SLinus Torvalds }
9861da177e4SLinus Torvalds 
9871da177e4SLinus Torvalds /*
9881da177e4SLinus Torvalds  * sd = -(sn * sm)
9891da177e4SLinus Torvalds  */
vfp_single_fnmul(int sd,int sn,s32 m,u32 fpscr)9901da177e4SLinus Torvalds static u32 vfp_single_fnmul(int sd, int sn, s32 m, u32 fpscr)
9911da177e4SLinus Torvalds {
9921da177e4SLinus Torvalds 	struct vfp_single vsd, vsn, vsm;
9931da177e4SLinus Torvalds 	u32 exceptions;
9941da177e4SLinus Torvalds 	s32 n = vfp_get_float(sn);
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds 	pr_debug("VFP: s%u = %08x\n", sn, n);
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 	vfp_single_unpack(&vsn, n);
9991da177e4SLinus Torvalds 	if (vsn.exponent == 0 && vsn.significand)
10001da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsn);
10011da177e4SLinus Torvalds 
10021da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
10031da177e4SLinus Torvalds 	if (vsm.exponent == 0 && vsm.significand)
10041da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsm);
10051da177e4SLinus Torvalds 
10061da177e4SLinus Torvalds 	exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
10071da177e4SLinus Torvalds 	vsd.sign = vfp_sign_negate(vsd.sign);
10081da177e4SLinus Torvalds 	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fnmul");
10091da177e4SLinus Torvalds }
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds /*
10121da177e4SLinus Torvalds  * sd = sn + sm
10131da177e4SLinus Torvalds  */
vfp_single_fadd(int sd,int sn,s32 m,u32 fpscr)10141da177e4SLinus Torvalds static u32 vfp_single_fadd(int sd, int sn, s32 m, u32 fpscr)
10151da177e4SLinus Torvalds {
10161da177e4SLinus Torvalds 	struct vfp_single vsd, vsn, vsm;
10171da177e4SLinus Torvalds 	u32 exceptions;
10181da177e4SLinus Torvalds 	s32 n = vfp_get_float(sn);
10191da177e4SLinus Torvalds 
10201da177e4SLinus Torvalds 	pr_debug("VFP: s%u = %08x\n", sn, n);
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	/*
10231da177e4SLinus Torvalds 	 * Unpack and normalise denormals.
10241da177e4SLinus Torvalds 	 */
10251da177e4SLinus Torvalds 	vfp_single_unpack(&vsn, n);
10261da177e4SLinus Torvalds 	if (vsn.exponent == 0 && vsn.significand)
10271da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsn);
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
10301da177e4SLinus Torvalds 	if (vsm.exponent == 0 && vsm.significand)
10311da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsm);
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds 	exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr);
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fadd");
10361da177e4SLinus Torvalds }
10371da177e4SLinus Torvalds 
10381da177e4SLinus Torvalds /*
10391da177e4SLinus Torvalds  * sd = sn - sm
10401da177e4SLinus Torvalds  */
vfp_single_fsub(int sd,int sn,s32 m,u32 fpscr)10411da177e4SLinus Torvalds static u32 vfp_single_fsub(int sd, int sn, s32 m, u32 fpscr)
10421da177e4SLinus Torvalds {
10431da177e4SLinus Torvalds 	/*
10441da177e4SLinus Torvalds 	 * Subtraction is addition with one sign inverted.
10451da177e4SLinus Torvalds 	 */
10461da177e4SLinus Torvalds 	return vfp_single_fadd(sd, sn, vfp_single_packed_negate(m), fpscr);
10471da177e4SLinus Torvalds }
10481da177e4SLinus Torvalds 
10491da177e4SLinus Torvalds /*
10501da177e4SLinus Torvalds  * sd = sn / sm
10511da177e4SLinus Torvalds  */
vfp_single_fdiv(int sd,int sn,s32 m,u32 fpscr)10521da177e4SLinus Torvalds static u32 vfp_single_fdiv(int sd, int sn, s32 m, u32 fpscr)
10531da177e4SLinus Torvalds {
10541da177e4SLinus Torvalds 	struct vfp_single vsd, vsn, vsm;
10551da177e4SLinus Torvalds 	u32 exceptions = 0;
10561da177e4SLinus Torvalds 	s32 n = vfp_get_float(sn);
10571da177e4SLinus Torvalds 	int tm, tn;
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 	pr_debug("VFP: s%u = %08x\n", sn, n);
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds 	vfp_single_unpack(&vsn, n);
10621da177e4SLinus Torvalds 	vfp_single_unpack(&vsm, m);
10631da177e4SLinus Torvalds 
10641da177e4SLinus Torvalds 	vsd.sign = vsn.sign ^ vsm.sign;
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	tn = vfp_single_type(&vsn);
10671da177e4SLinus Torvalds 	tm = vfp_single_type(&vsm);
10681da177e4SLinus Torvalds 
10691da177e4SLinus Torvalds 	/*
10701da177e4SLinus Torvalds 	 * Is n a NAN?
10711da177e4SLinus Torvalds 	 */
10721da177e4SLinus Torvalds 	if (tn & VFP_NAN)
10731da177e4SLinus Torvalds 		goto vsn_nan;
10741da177e4SLinus Torvalds 
10751da177e4SLinus Torvalds 	/*
10761da177e4SLinus Torvalds 	 * Is m a NAN?
10771da177e4SLinus Torvalds 	 */
10781da177e4SLinus Torvalds 	if (tm & VFP_NAN)
10791da177e4SLinus Torvalds 		goto vsm_nan;
10801da177e4SLinus Torvalds 
10811da177e4SLinus Torvalds 	/*
10821da177e4SLinus Torvalds 	 * If n and m are infinity, the result is invalid
10831da177e4SLinus Torvalds 	 * If n and m are zero, the result is invalid
10841da177e4SLinus Torvalds 	 */
10851da177e4SLinus Torvalds 	if (tm & tn & (VFP_INFINITY|VFP_ZERO))
10861da177e4SLinus Torvalds 		goto invalid;
10871da177e4SLinus Torvalds 
10881da177e4SLinus Torvalds 	/*
10891da177e4SLinus Torvalds 	 * If n is infinity, the result is infinity
10901da177e4SLinus Torvalds 	 */
10911da177e4SLinus Torvalds 	if (tn & VFP_INFINITY)
10921da177e4SLinus Torvalds 		goto infinity;
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds 	/*
10951da177e4SLinus Torvalds 	 * If m is zero, raise div0 exception
10961da177e4SLinus Torvalds 	 */
10971da177e4SLinus Torvalds 	if (tm & VFP_ZERO)
10981da177e4SLinus Torvalds 		goto divzero;
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds 	/*
11011da177e4SLinus Torvalds 	 * If m is infinity, or n is zero, the result is zero
11021da177e4SLinus Torvalds 	 */
11031da177e4SLinus Torvalds 	if (tm & VFP_INFINITY || tn & VFP_ZERO)
11041da177e4SLinus Torvalds 		goto zero;
11051da177e4SLinus Torvalds 
11061da177e4SLinus Torvalds 	if (tn & VFP_DENORMAL)
11071da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsn);
11081da177e4SLinus Torvalds 	if (tm & VFP_DENORMAL)
11091da177e4SLinus Torvalds 		vfp_single_normalise_denormal(&vsm);
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds 	/*
11121da177e4SLinus Torvalds 	 * Ok, we have two numbers, we can perform division.
11131da177e4SLinus Torvalds 	 */
11141da177e4SLinus Torvalds 	vsd.exponent = vsn.exponent - vsm.exponent + 127 - 1;
11151da177e4SLinus Torvalds 	vsm.significand <<= 1;
11161da177e4SLinus Torvalds 	if (vsm.significand <= (2 * vsn.significand)) {
11171da177e4SLinus Torvalds 		vsn.significand >>= 1;
11181da177e4SLinus Torvalds 		vsd.exponent++;
11191da177e4SLinus Torvalds 	}
1120438a7616SRussell King 	{
1121438a7616SRussell King 		u64 significand = (u64)vsn.significand << 32;
1122438a7616SRussell King 		do_div(significand, vsm.significand);
1123438a7616SRussell King 		vsd.significand = significand;
1124438a7616SRussell King 	}
11251da177e4SLinus Torvalds 	if ((vsd.significand & 0x3f) == 0)
11261da177e4SLinus Torvalds 		vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32);
11271da177e4SLinus Torvalds 
11281da177e4SLinus Torvalds 	return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fdiv");
11291da177e4SLinus Torvalds 
11301da177e4SLinus Torvalds  vsn_nan:
11311da177e4SLinus Torvalds 	exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
11321da177e4SLinus Torvalds  pack:
11330355b3e0SDaniel Jacobowitz 	vfp_put_float(vfp_single_pack(&vsd), sd);
11341da177e4SLinus Torvalds 	return exceptions;
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds  vsm_nan:
11371da177e4SLinus Torvalds 	exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
11381da177e4SLinus Torvalds 	goto pack;
11391da177e4SLinus Torvalds 
11401da177e4SLinus Torvalds  zero:
11411da177e4SLinus Torvalds 	vsd.exponent = 0;
11421da177e4SLinus Torvalds 	vsd.significand = 0;
11431da177e4SLinus Torvalds 	goto pack;
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds  divzero:
11461da177e4SLinus Torvalds 	exceptions = FPSCR_DZC;
11471da177e4SLinus Torvalds  infinity:
11481da177e4SLinus Torvalds 	vsd.exponent = 255;
11491da177e4SLinus Torvalds 	vsd.significand = 0;
11501da177e4SLinus Torvalds 	goto pack;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds  invalid:
11530355b3e0SDaniel Jacobowitz 	vfp_put_float(vfp_single_pack(&vfp_single_default_qnan), sd);
11541da177e4SLinus Torvalds 	return FPSCR_IOC;
11551da177e4SLinus Torvalds }
11561da177e4SLinus Torvalds 
11574cc9bd2eSGen FUKATSU static struct op fops[16] = {
11584cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FMAC)]	= { vfp_single_fmac,  0 },
11594cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FNMAC)]	= { vfp_single_fnmac, 0 },
11604cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FMSC)]	= { vfp_single_fmsc,  0 },
11614cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FNMSC)]	= { vfp_single_fnmsc, 0 },
11624cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FMUL)]	= { vfp_single_fmul,  0 },
11634cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FNMUL)]	= { vfp_single_fnmul, 0 },
11644cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FADD)]	= { vfp_single_fadd,  0 },
11654cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FSUB)]	= { vfp_single_fsub,  0 },
11664cc9bd2eSGen FUKATSU 	[FOP_TO_IDX(FOP_FDIV)]	= { vfp_single_fdiv,  0 },
11671da177e4SLinus Torvalds };
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds #define FREG_BANK(x)	((x) & 0x18)
11701da177e4SLinus Torvalds #define FREG_IDX(x)	((x) & 7)
11711da177e4SLinus Torvalds 
vfp_single_cpdo(u32 inst,u32 fpscr)11721da177e4SLinus Torvalds u32 vfp_single_cpdo(u32 inst, u32 fpscr)
11731da177e4SLinus Torvalds {
11741da177e4SLinus Torvalds 	u32 op = inst & FOP_MASK;
11751da177e4SLinus Torvalds 	u32 exceptions = 0;
1176c29ecac1SDaniel Jacobowitz 	unsigned int dest;
11771da177e4SLinus Torvalds 	unsigned int sn = vfp_get_sn(inst);
11781da177e4SLinus Torvalds 	unsigned int sm = vfp_get_sm(inst);
11791da177e4SLinus Torvalds 	unsigned int vecitr, veclen, vecstride;
11804cc9bd2eSGen FUKATSU 	struct op *fop;
11811da177e4SLinus Torvalds 
11821da177e4SLinus Torvalds 	vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK);
11831da177e4SLinus Torvalds 
11844cc9bd2eSGen FUKATSU 	fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];
1185baf97ce6SRussell King 
11861da177e4SLinus Torvalds 	/*
1187c29ecac1SDaniel Jacobowitz 	 * fcvtsd takes a dN register number as destination, not sN.
1188c29ecac1SDaniel Jacobowitz 	 * Technically, if bit 0 of dd is set, this is an invalid
1189c29ecac1SDaniel Jacobowitz 	 * instruction.  However, we ignore this for efficiency.
1190c29ecac1SDaniel Jacobowitz 	 * It also only operates on scalars.
1191c29ecac1SDaniel Jacobowitz 	 */
1192baf97ce6SRussell King 	if (fop->flags & OP_DD)
1193c29ecac1SDaniel Jacobowitz 		dest = vfp_get_dd(inst);
1194baf97ce6SRussell King 	else
1195c29ecac1SDaniel Jacobowitz 		dest = vfp_get_sd(inst);
1196c29ecac1SDaniel Jacobowitz 
1197c29ecac1SDaniel Jacobowitz 	/*
11981da177e4SLinus Torvalds 	 * If destination bank is zero, vector length is always '1'.
11991da177e4SLinus Torvalds 	 * ARM DDI0100F C5.1.3, C5.3.2.
12001da177e4SLinus Torvalds 	 */
1201baf97ce6SRussell King 	if ((fop->flags & OP_SCALAR) || FREG_BANK(dest) == 0)
12021da177e4SLinus Torvalds 		veclen = 0;
12034cc9bd2eSGen FUKATSU 	else
12044cc9bd2eSGen FUKATSU 		veclen = fpscr & FPSCR_LENGTH_MASK;
12051da177e4SLinus Torvalds 
12061da177e4SLinus Torvalds 	pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
12071da177e4SLinus Torvalds 		 (veclen >> FPSCR_LENGTH_BIT) + 1);
12081da177e4SLinus Torvalds 
12094cc9bd2eSGen FUKATSU 	if (!fop->fn)
12101da177e4SLinus Torvalds 		goto invalid;
12111da177e4SLinus Torvalds 
12121da177e4SLinus Torvalds 	for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
12131da177e4SLinus Torvalds 		s32 m = vfp_get_float(sm);
12141da177e4SLinus Torvalds 		u32 except;
1215baf97ce6SRussell King 		char type;
12161da177e4SLinus Torvalds 
1217baf97ce6SRussell King 		type = fop->flags & OP_DD ? 'd' : 's';
1218baf97ce6SRussell King 		if (op == FOP_EXT)
1219baf97ce6SRussell King 			pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n",
1220baf97ce6SRussell King 				 vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
1221baf97ce6SRussell King 				 sm, m);
12221da177e4SLinus Torvalds 		else
1223baf97ce6SRussell King 			pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n",
1224baf97ce6SRussell King 				 vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
12251da177e4SLinus Torvalds 				 FOP_TO_IDX(op), sm, m);
12261da177e4SLinus Torvalds 
12274cc9bd2eSGen FUKATSU 		except = fop->fn(dest, sn, m, fpscr);
12281da177e4SLinus Torvalds 		pr_debug("VFP: itr%d: exceptions=%08x\n",
12291da177e4SLinus Torvalds 			 vecitr >> FPSCR_LENGTH_BIT, except);
12301da177e4SLinus Torvalds 
12311da177e4SLinus Torvalds 		exceptions |= except;
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds 		/*
12341da177e4SLinus Torvalds 		 * CHECK: It appears to be undefined whether we stop when
12351da177e4SLinus Torvalds 		 * we encounter an exception.  We continue.
12361da177e4SLinus Torvalds 		 */
1237c29ecac1SDaniel Jacobowitz 		dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 7);
12381da177e4SLinus Torvalds 		sn = FREG_BANK(sn) + ((FREG_IDX(sn) + vecstride) & 7);
12391da177e4SLinus Torvalds 		if (FREG_BANK(sm) != 0)
12401da177e4SLinus Torvalds 			sm = FREG_BANK(sm) + ((FREG_IDX(sm) + vecstride) & 7);
12411da177e4SLinus Torvalds 	}
12421da177e4SLinus Torvalds 	return exceptions;
12431da177e4SLinus Torvalds 
12441da177e4SLinus Torvalds  invalid:
12451da177e4SLinus Torvalds 	return (u32)-1;
12461da177e4SLinus Torvalds }
1247