162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/arch/arm/vfp/vfpsingle.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This code is derived in part from John R. Housers softfloat library, which
562306a36Sopenharmony_ci * carries the following notice:
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * ===========================================================================
862306a36Sopenharmony_ci * This C source file is part of the SoftFloat IEC/IEEE Floating-point
962306a36Sopenharmony_ci * Arithmetic Package, Release 2.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Written by John R. Hauser.  This work was made possible in part by the
1262306a36Sopenharmony_ci * International Computer Science Institute, located at Suite 600, 1947 Center
1362306a36Sopenharmony_ci * Street, Berkeley, California 94704.  Funding was partially provided by the
1462306a36Sopenharmony_ci * National Science Foundation under grant MIP-9311980.  The original version
1562306a36Sopenharmony_ci * of this code was written as part of a project to build a fixed-point vector
1662306a36Sopenharmony_ci * processor in collaboration with the University of California at Berkeley,
1762306a36Sopenharmony_ci * overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
1862306a36Sopenharmony_ci * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
1962306a36Sopenharmony_ci * arithmetic/softfloat.html'.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
2262306a36Sopenharmony_ci * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
2362306a36Sopenharmony_ci * TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
2462306a36Sopenharmony_ci * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
2562306a36Sopenharmony_ci * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Derivative works are acceptable, even for commercial purposes, so long as
2862306a36Sopenharmony_ci * (1) they include prominent notice that the work is derivative, and (2) they
2962306a36Sopenharmony_ci * include prominent notice akin to these three paragraphs for those parts of
3062306a36Sopenharmony_ci * this code that are retained.
3162306a36Sopenharmony_ci * ===========================================================================
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci#include <linux/kernel.h>
3462306a36Sopenharmony_ci#include <linux/bitops.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include <asm/div64.h>
3762306a36Sopenharmony_ci#include <asm/vfp.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include "vfpinstr.h"
4062306a36Sopenharmony_ci#include "vfp.h"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic struct vfp_single vfp_single_default_qnan = {
4362306a36Sopenharmony_ci	.exponent	= 255,
4462306a36Sopenharmony_ci	.sign		= 0,
4562306a36Sopenharmony_ci	.significand	= VFP_SINGLE_SIGNIFICAND_QNAN,
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void vfp_single_dump(const char *str, struct vfp_single *s)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n",
5162306a36Sopenharmony_ci		 str, s->sign != 0, s->exponent, s->significand);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void vfp_single_normalise_denormal(struct vfp_single *vs)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	int bits = 31 - fls(vs->significand);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	vfp_single_dump("normalise_denormal: in", vs);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (bits) {
6162306a36Sopenharmony_ci		vs->exponent -= bits - 1;
6262306a36Sopenharmony_ci		vs->significand <<= bits;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	vfp_single_dump("normalise_denormal: out", vs);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#ifndef DEBUG
6962306a36Sopenharmony_ci#define vfp_single_normaliseround(sd,vsd,fpscr,except,func) __vfp_single_normaliseround(sd,vsd,fpscr,except)
7062306a36Sopenharmony_ciu32 __vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions)
7162306a36Sopenharmony_ci#else
7262306a36Sopenharmony_ciu32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func)
7362306a36Sopenharmony_ci#endif
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	u32 significand, incr, rmode;
7662306a36Sopenharmony_ci	int exponent, shift, underflow;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	vfp_single_dump("pack: in", vs);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci	 * Infinities and NaNs are a special case.
8262306a36Sopenharmony_ci	 */
8362306a36Sopenharmony_ci	if (vs->exponent == 255 && (vs->significand == 0 || exceptions))
8462306a36Sopenharmony_ci		goto pack;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/*
8762306a36Sopenharmony_ci	 * Special-case zero.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	if (vs->significand == 0) {
9062306a36Sopenharmony_ci		vs->exponent = 0;
9162306a36Sopenharmony_ci		goto pack;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	exponent = vs->exponent;
9562306a36Sopenharmony_ci	significand = vs->significand;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/*
9862306a36Sopenharmony_ci	 * Normalise first.  Note that we shift the significand up to
9962306a36Sopenharmony_ci	 * bit 31, so we have VFP_SINGLE_LOW_BITS + 1 below the least
10062306a36Sopenharmony_ci	 * significant bit.
10162306a36Sopenharmony_ci	 */
10262306a36Sopenharmony_ci	shift = 32 - fls(significand);
10362306a36Sopenharmony_ci	if (shift < 32 && shift) {
10462306a36Sopenharmony_ci		exponent -= shift;
10562306a36Sopenharmony_ci		significand <<= shift;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#ifdef DEBUG
10962306a36Sopenharmony_ci	vs->exponent = exponent;
11062306a36Sopenharmony_ci	vs->significand = significand;
11162306a36Sopenharmony_ci	vfp_single_dump("pack: normalised", vs);
11262306a36Sopenharmony_ci#endif
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * Tiny number?
11662306a36Sopenharmony_ci	 */
11762306a36Sopenharmony_ci	underflow = exponent < 0;
11862306a36Sopenharmony_ci	if (underflow) {
11962306a36Sopenharmony_ci		significand = vfp_shiftright32jamming(significand, -exponent);
12062306a36Sopenharmony_ci		exponent = 0;
12162306a36Sopenharmony_ci#ifdef DEBUG
12262306a36Sopenharmony_ci		vs->exponent = exponent;
12362306a36Sopenharmony_ci		vs->significand = significand;
12462306a36Sopenharmony_ci		vfp_single_dump("pack: tiny number", vs);
12562306a36Sopenharmony_ci#endif
12662306a36Sopenharmony_ci		if (!(significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1)))
12762306a36Sopenharmony_ci			underflow = 0;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * Select rounding increment.
13262306a36Sopenharmony_ci	 */
13362306a36Sopenharmony_ci	incr = 0;
13462306a36Sopenharmony_ci	rmode = fpscr & FPSCR_RMODE_MASK;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (rmode == FPSCR_ROUND_NEAREST) {
13762306a36Sopenharmony_ci		incr = 1 << VFP_SINGLE_LOW_BITS;
13862306a36Sopenharmony_ci		if ((significand & (1 << (VFP_SINGLE_LOW_BITS + 1))) == 0)
13962306a36Sopenharmony_ci			incr -= 1;
14062306a36Sopenharmony_ci	} else if (rmode == FPSCR_ROUND_TOZERO) {
14162306a36Sopenharmony_ci		incr = 0;
14262306a36Sopenharmony_ci	} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0))
14362306a36Sopenharmony_ci		incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	pr_debug("VFP: rounding increment = 0x%08x\n", incr);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/*
14862306a36Sopenharmony_ci	 * Is our rounding going to overflow?
14962306a36Sopenharmony_ci	 */
15062306a36Sopenharmony_ci	if ((significand + incr) < significand) {
15162306a36Sopenharmony_ci		exponent += 1;
15262306a36Sopenharmony_ci		significand = (significand >> 1) | (significand & 1);
15362306a36Sopenharmony_ci		incr >>= 1;
15462306a36Sopenharmony_ci#ifdef DEBUG
15562306a36Sopenharmony_ci		vs->exponent = exponent;
15662306a36Sopenharmony_ci		vs->significand = significand;
15762306a36Sopenharmony_ci		vfp_single_dump("pack: overflow", vs);
15862306a36Sopenharmony_ci#endif
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/*
16262306a36Sopenharmony_ci	 * If any of the low bits (which will be shifted out of the
16362306a36Sopenharmony_ci	 * number) are non-zero, the result is inexact.
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	if (significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1))
16662306a36Sopenharmony_ci		exceptions |= FPSCR_IXC;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * Do our rounding.
17062306a36Sopenharmony_ci	 */
17162306a36Sopenharmony_ci	significand += incr;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/*
17462306a36Sopenharmony_ci	 * Infinity?
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci	if (exponent >= 254) {
17762306a36Sopenharmony_ci		exceptions |= FPSCR_OFC | FPSCR_IXC;
17862306a36Sopenharmony_ci		if (incr == 0) {
17962306a36Sopenharmony_ci			vs->exponent = 253;
18062306a36Sopenharmony_ci			vs->significand = 0x7fffffff;
18162306a36Sopenharmony_ci		} else {
18262306a36Sopenharmony_ci			vs->exponent = 255;		/* infinity */
18362306a36Sopenharmony_ci			vs->significand = 0;
18462306a36Sopenharmony_ci		}
18562306a36Sopenharmony_ci	} else {
18662306a36Sopenharmony_ci		if (significand >> (VFP_SINGLE_LOW_BITS + 1) == 0)
18762306a36Sopenharmony_ci			exponent = 0;
18862306a36Sopenharmony_ci		if (exponent || significand > 0x80000000)
18962306a36Sopenharmony_ci			underflow = 0;
19062306a36Sopenharmony_ci		if (underflow)
19162306a36Sopenharmony_ci			exceptions |= FPSCR_UFC;
19262306a36Sopenharmony_ci		vs->exponent = exponent;
19362306a36Sopenharmony_ci		vs->significand = significand >> 1;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci pack:
19762306a36Sopenharmony_ci	vfp_single_dump("pack: final", vs);
19862306a36Sopenharmony_ci	{
19962306a36Sopenharmony_ci		s32 d = vfp_single_pack(vs);
20062306a36Sopenharmony_ci#ifdef DEBUG
20162306a36Sopenharmony_ci		pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func,
20262306a36Sopenharmony_ci			 sd, d, exceptions);
20362306a36Sopenharmony_ci#endif
20462306a36Sopenharmony_ci		vfp_put_float(d, sd);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return exceptions;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci/*
21162306a36Sopenharmony_ci * Propagate the NaN, setting exceptions if it is signalling.
21262306a36Sopenharmony_ci * 'n' is always a NaN.  'm' may be a number, NaN or infinity.
21362306a36Sopenharmony_ci */
21462306a36Sopenharmony_cistatic u32
21562306a36Sopenharmony_civfp_propagate_nan(struct vfp_single *vsd, struct vfp_single *vsn,
21662306a36Sopenharmony_ci		  struct vfp_single *vsm, u32 fpscr)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct vfp_single *nan;
21962306a36Sopenharmony_ci	int tn, tm = 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	tn = vfp_single_type(vsn);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (vsm)
22462306a36Sopenharmony_ci		tm = vfp_single_type(vsm);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (fpscr & FPSCR_DEFAULT_NAN)
22762306a36Sopenharmony_ci		/*
22862306a36Sopenharmony_ci		 * Default NaN mode - always returns a quiet NaN
22962306a36Sopenharmony_ci		 */
23062306a36Sopenharmony_ci		nan = &vfp_single_default_qnan;
23162306a36Sopenharmony_ci	else {
23262306a36Sopenharmony_ci		/*
23362306a36Sopenharmony_ci		 * Contemporary mode - select the first signalling
23462306a36Sopenharmony_ci		 * NAN, or if neither are signalling, the first
23562306a36Sopenharmony_ci		 * quiet NAN.
23662306a36Sopenharmony_ci		 */
23762306a36Sopenharmony_ci		if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
23862306a36Sopenharmony_ci			nan = vsn;
23962306a36Sopenharmony_ci		else
24062306a36Sopenharmony_ci			nan = vsm;
24162306a36Sopenharmony_ci		/*
24262306a36Sopenharmony_ci		 * Make the NaN quiet.
24362306a36Sopenharmony_ci		 */
24462306a36Sopenharmony_ci		nan->significand |= VFP_SINGLE_SIGNIFICAND_QNAN;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	*vsd = *nan;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/*
25062306a36Sopenharmony_ci	 * If one was a signalling NAN, raise invalid operation.
25162306a36Sopenharmony_ci	 */
25262306a36Sopenharmony_ci	return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/*
25762306a36Sopenharmony_ci * Extended operations
25862306a36Sopenharmony_ci */
25962306a36Sopenharmony_cistatic u32 vfp_single_fabs(int sd, int unused, s32 m, u32 fpscr)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	vfp_put_float(vfp_single_packed_abs(m), sd);
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic u32 vfp_single_fcpy(int sd, int unused, s32 m, u32 fpscr)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	vfp_put_float(m, sd);
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic u32 vfp_single_fneg(int sd, int unused, s32 m, u32 fpscr)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	vfp_put_float(vfp_single_packed_negate(m), sd);
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic const u16 sqrt_oddadjust[] = {
27862306a36Sopenharmony_ci	0x0004, 0x0022, 0x005d, 0x00b1, 0x011d, 0x019f, 0x0236, 0x02e0,
27962306a36Sopenharmony_ci	0x039c, 0x0468, 0x0545, 0x0631, 0x072b, 0x0832, 0x0946, 0x0a67
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic const u16 sqrt_evenadjust[] = {
28362306a36Sopenharmony_ci	0x0a2d, 0x08af, 0x075a, 0x0629, 0x051a, 0x0429, 0x0356, 0x029e,
28462306a36Sopenharmony_ci	0x0200, 0x0179, 0x0109, 0x00af, 0x0068, 0x0034, 0x0012, 0x0002
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ciu32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	int index;
29062306a36Sopenharmony_ci	u32 z, a;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if ((significand & 0xc0000000) != 0x40000000) {
29362306a36Sopenharmony_ci		pr_warn("VFP: estimate_sqrt: invalid significand\n");
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	a = significand << 1;
29762306a36Sopenharmony_ci	index = (a >> 27) & 15;
29862306a36Sopenharmony_ci	if (exponent & 1) {
29962306a36Sopenharmony_ci		z = 0x4000 + (a >> 17) - sqrt_oddadjust[index];
30062306a36Sopenharmony_ci		z = ((a / z) << 14) + (z << 15);
30162306a36Sopenharmony_ci		a >>= 1;
30262306a36Sopenharmony_ci	} else {
30362306a36Sopenharmony_ci		z = 0x8000 + (a >> 17) - sqrt_evenadjust[index];
30462306a36Sopenharmony_ci		z = a / z + z;
30562306a36Sopenharmony_ci		z = (z >= 0x20000) ? 0xffff8000 : (z << 15);
30662306a36Sopenharmony_ci		if (z <= a)
30762306a36Sopenharmony_ci			return (s32)a >> 1;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci	{
31062306a36Sopenharmony_ci		u64 v = (u64)a << 31;
31162306a36Sopenharmony_ci		do_div(v, z);
31262306a36Sopenharmony_ci		return v + (z >> 1);
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic u32 vfp_single_fsqrt(int sd, int unused, s32 m, u32 fpscr)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct vfp_single vsm, vsd;
31962306a36Sopenharmony_ci	int ret, tm;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
32262306a36Sopenharmony_ci	tm = vfp_single_type(&vsm);
32362306a36Sopenharmony_ci	if (tm & (VFP_NAN|VFP_INFINITY)) {
32462306a36Sopenharmony_ci		struct vfp_single *vsp = &vsd;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		if (tm & VFP_NAN)
32762306a36Sopenharmony_ci			ret = vfp_propagate_nan(vsp, &vsm, NULL, fpscr);
32862306a36Sopenharmony_ci		else if (vsm.sign == 0) {
32962306a36Sopenharmony_ci sqrt_copy:
33062306a36Sopenharmony_ci			vsp = &vsm;
33162306a36Sopenharmony_ci			ret = 0;
33262306a36Sopenharmony_ci		} else {
33362306a36Sopenharmony_ci sqrt_invalid:
33462306a36Sopenharmony_ci			vsp = &vfp_single_default_qnan;
33562306a36Sopenharmony_ci			ret = FPSCR_IOC;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci		vfp_put_float(vfp_single_pack(vsp), sd);
33862306a36Sopenharmony_ci		return ret;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/*
34262306a36Sopenharmony_ci	 * sqrt(+/- 0) == +/- 0
34362306a36Sopenharmony_ci	 */
34462306a36Sopenharmony_ci	if (tm & VFP_ZERO)
34562306a36Sopenharmony_ci		goto sqrt_copy;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/*
34862306a36Sopenharmony_ci	 * Normalise a denormalised number
34962306a36Sopenharmony_ci	 */
35062306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
35162306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsm);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/*
35462306a36Sopenharmony_ci	 * sqrt(<0) = invalid
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci	if (vsm.sign)
35762306a36Sopenharmony_ci		goto sqrt_invalid;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	vfp_single_dump("sqrt", &vsm);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/*
36262306a36Sopenharmony_ci	 * Estimate the square root.
36362306a36Sopenharmony_ci	 */
36462306a36Sopenharmony_ci	vsd.sign = 0;
36562306a36Sopenharmony_ci	vsd.exponent = ((vsm.exponent - 127) >> 1) + 127;
36662306a36Sopenharmony_ci	vsd.significand = vfp_estimate_sqrt_significand(vsm.exponent, vsm.significand) + 2;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	vfp_single_dump("sqrt estimate", &vsd);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/*
37162306a36Sopenharmony_ci	 * And now adjust.
37262306a36Sopenharmony_ci	 */
37362306a36Sopenharmony_ci	if ((vsd.significand & VFP_SINGLE_LOW_BITS_MASK) <= 5) {
37462306a36Sopenharmony_ci		if (vsd.significand < 2) {
37562306a36Sopenharmony_ci			vsd.significand = 0xffffffff;
37662306a36Sopenharmony_ci		} else {
37762306a36Sopenharmony_ci			u64 term;
37862306a36Sopenharmony_ci			s64 rem;
37962306a36Sopenharmony_ci			vsm.significand <<= !(vsm.exponent & 1);
38062306a36Sopenharmony_ci			term = (u64)vsd.significand * vsd.significand;
38162306a36Sopenharmony_ci			rem = ((u64)vsm.significand << 32) - term;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci			pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci			while (rem < 0) {
38662306a36Sopenharmony_ci				vsd.significand -= 1;
38762306a36Sopenharmony_ci				rem += ((u64)vsd.significand << 1) | 1;
38862306a36Sopenharmony_ci			}
38962306a36Sopenharmony_ci			vsd.significand |= rem != 0;
39062306a36Sopenharmony_ci		}
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	vsd.significand = vfp_shiftright32jamming(vsd.significand, 1);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fsqrt");
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/*
39862306a36Sopenharmony_ci * Equal	:= ZC
39962306a36Sopenharmony_ci * Less than	:= N
40062306a36Sopenharmony_ci * Greater than	:= C
40162306a36Sopenharmony_ci * Unordered	:= CV
40262306a36Sopenharmony_ci */
40362306a36Sopenharmony_cistatic u32 vfp_compare(int sd, int signal_on_qnan, s32 m, u32 fpscr)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	s32 d;
40662306a36Sopenharmony_ci	u32 ret = 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	d = vfp_get_float(sd);
40962306a36Sopenharmony_ci	if (vfp_single_packed_exponent(m) == 255 && vfp_single_packed_mantissa(m)) {
41062306a36Sopenharmony_ci		ret |= FPSCR_C | FPSCR_V;
41162306a36Sopenharmony_ci		if (signal_on_qnan || !(vfp_single_packed_mantissa(m) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1))))
41262306a36Sopenharmony_ci			/*
41362306a36Sopenharmony_ci			 * Signalling NaN, or signalling on quiet NaN
41462306a36Sopenharmony_ci			 */
41562306a36Sopenharmony_ci			ret |= FPSCR_IOC;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (vfp_single_packed_exponent(d) == 255 && vfp_single_packed_mantissa(d)) {
41962306a36Sopenharmony_ci		ret |= FPSCR_C | FPSCR_V;
42062306a36Sopenharmony_ci		if (signal_on_qnan || !(vfp_single_packed_mantissa(d) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1))))
42162306a36Sopenharmony_ci			/*
42262306a36Sopenharmony_ci			 * Signalling NaN, or signalling on quiet NaN
42362306a36Sopenharmony_ci			 */
42462306a36Sopenharmony_ci			ret |= FPSCR_IOC;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (ret == 0) {
42862306a36Sopenharmony_ci		if (d == m || vfp_single_packed_abs(d | m) == 0) {
42962306a36Sopenharmony_ci			/*
43062306a36Sopenharmony_ci			 * equal
43162306a36Sopenharmony_ci			 */
43262306a36Sopenharmony_ci			ret |= FPSCR_Z | FPSCR_C;
43362306a36Sopenharmony_ci		} else if (vfp_single_packed_sign(d ^ m)) {
43462306a36Sopenharmony_ci			/*
43562306a36Sopenharmony_ci			 * different signs
43662306a36Sopenharmony_ci			 */
43762306a36Sopenharmony_ci			if (vfp_single_packed_sign(d))
43862306a36Sopenharmony_ci				/*
43962306a36Sopenharmony_ci				 * d is negative, so d < m
44062306a36Sopenharmony_ci				 */
44162306a36Sopenharmony_ci				ret |= FPSCR_N;
44262306a36Sopenharmony_ci			else
44362306a36Sopenharmony_ci				/*
44462306a36Sopenharmony_ci				 * d is positive, so d > m
44562306a36Sopenharmony_ci				 */
44662306a36Sopenharmony_ci				ret |= FPSCR_C;
44762306a36Sopenharmony_ci		} else if ((vfp_single_packed_sign(d) != 0) ^ (d < m)) {
44862306a36Sopenharmony_ci			/*
44962306a36Sopenharmony_ci			 * d < m
45062306a36Sopenharmony_ci			 */
45162306a36Sopenharmony_ci			ret |= FPSCR_N;
45262306a36Sopenharmony_ci		} else if ((vfp_single_packed_sign(d) != 0) ^ (d > m)) {
45362306a36Sopenharmony_ci			/*
45462306a36Sopenharmony_ci			 * d > m
45562306a36Sopenharmony_ci			 */
45662306a36Sopenharmony_ci			ret |= FPSCR_C;
45762306a36Sopenharmony_ci		}
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci	return ret;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic u32 vfp_single_fcmp(int sd, int unused, s32 m, u32 fpscr)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	return vfp_compare(sd, 0, m, fpscr);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic u32 vfp_single_fcmpe(int sd, int unused, s32 m, u32 fpscr)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	return vfp_compare(sd, 1, m, fpscr);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic u32 vfp_single_fcmpz(int sd, int unused, s32 m, u32 fpscr)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	return vfp_compare(sd, 0, 0, fpscr);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic u32 vfp_single_fcmpez(int sd, int unused, s32 m, u32 fpscr)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	return vfp_compare(sd, 1, 0, fpscr);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic u32 vfp_single_fcvtd(int dd, int unused, s32 m, u32 fpscr)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct vfp_single vsm;
48562306a36Sopenharmony_ci	struct vfp_double vdd;
48662306a36Sopenharmony_ci	int tm;
48762306a36Sopenharmony_ci	u32 exceptions = 0;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	tm = vfp_single_type(&vsm);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/*
49462306a36Sopenharmony_ci	 * If we have a signalling NaN, signal invalid operation.
49562306a36Sopenharmony_ci	 */
49662306a36Sopenharmony_ci	if (tm == VFP_SNAN)
49762306a36Sopenharmony_ci		exceptions = FPSCR_IOC;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
50062306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsm);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	vdd.sign = vsm.sign;
50362306a36Sopenharmony_ci	vdd.significand = (u64)vsm.significand << 32;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/*
50662306a36Sopenharmony_ci	 * If we have an infinity or NaN, the exponent must be 2047.
50762306a36Sopenharmony_ci	 */
50862306a36Sopenharmony_ci	if (tm & (VFP_INFINITY|VFP_NAN)) {
50962306a36Sopenharmony_ci		vdd.exponent = 2047;
51062306a36Sopenharmony_ci		if (tm == VFP_QNAN)
51162306a36Sopenharmony_ci			vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
51262306a36Sopenharmony_ci		goto pack_nan;
51362306a36Sopenharmony_ci	} else if (tm & VFP_ZERO)
51462306a36Sopenharmony_ci		vdd.exponent = 0;
51562306a36Sopenharmony_ci	else
51662306a36Sopenharmony_ci		vdd.exponent = vsm.exponent + (1023 - 127);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fcvtd");
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci pack_nan:
52162306a36Sopenharmony_ci	vfp_put_double(vfp_double_pack(&vdd), dd);
52262306a36Sopenharmony_ci	return exceptions;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic u32 vfp_single_fuito(int sd, int unused, s32 m, u32 fpscr)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct vfp_single vs;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	vs.sign = 0;
53062306a36Sopenharmony_ci	vs.exponent = 127 + 31 - 1;
53162306a36Sopenharmony_ci	vs.significand = (u32)m;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fuito");
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic u32 vfp_single_fsito(int sd, int unused, s32 m, u32 fpscr)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct vfp_single vs;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	vs.sign = (m & 0x80000000) >> 16;
54162306a36Sopenharmony_ci	vs.exponent = 127 + 31 - 1;
54262306a36Sopenharmony_ci	vs.significand = vs.sign ? -m : m;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fsito");
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic u32 vfp_single_ftoui(int sd, int unused, s32 m, u32 fpscr)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct vfp_single vsm;
55062306a36Sopenharmony_ci	u32 d, exceptions = 0;
55162306a36Sopenharmony_ci	int rmode = fpscr & FPSCR_RMODE_MASK;
55262306a36Sopenharmony_ci	int tm;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
55562306a36Sopenharmony_ci	vfp_single_dump("VSM", &vsm);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/*
55862306a36Sopenharmony_ci	 * Do we have a denormalised number?
55962306a36Sopenharmony_ci	 */
56062306a36Sopenharmony_ci	tm = vfp_single_type(&vsm);
56162306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
56262306a36Sopenharmony_ci		exceptions |= FPSCR_IDC;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (tm & VFP_NAN)
56562306a36Sopenharmony_ci		vsm.sign = 0;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (vsm.exponent >= 127 + 32) {
56862306a36Sopenharmony_ci		d = vsm.sign ? 0 : 0xffffffff;
56962306a36Sopenharmony_ci		exceptions = FPSCR_IOC;
57062306a36Sopenharmony_ci	} else if (vsm.exponent >= 127 - 1) {
57162306a36Sopenharmony_ci		int shift = 127 + 31 - vsm.exponent;
57262306a36Sopenharmony_ci		u32 rem, incr = 0;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		/*
57562306a36Sopenharmony_ci		 * 2^0 <= m < 2^32-2^8
57662306a36Sopenharmony_ci		 */
57762306a36Sopenharmony_ci		d = (vsm.significand << 1) >> shift;
57862306a36Sopenharmony_ci		rem = vsm.significand << (33 - shift);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		if (rmode == FPSCR_ROUND_NEAREST) {
58162306a36Sopenharmony_ci			incr = 0x80000000;
58262306a36Sopenharmony_ci			if ((d & 1) == 0)
58362306a36Sopenharmony_ci				incr -= 1;
58462306a36Sopenharmony_ci		} else if (rmode == FPSCR_ROUND_TOZERO) {
58562306a36Sopenharmony_ci			incr = 0;
58662306a36Sopenharmony_ci		} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) {
58762306a36Sopenharmony_ci			incr = ~0;
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		if ((rem + incr) < rem) {
59162306a36Sopenharmony_ci			if (d < 0xffffffff)
59262306a36Sopenharmony_ci				d += 1;
59362306a36Sopenharmony_ci			else
59462306a36Sopenharmony_ci				exceptions |= FPSCR_IOC;
59562306a36Sopenharmony_ci		}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		if (d && vsm.sign) {
59862306a36Sopenharmony_ci			d = 0;
59962306a36Sopenharmony_ci			exceptions |= FPSCR_IOC;
60062306a36Sopenharmony_ci		} else if (rem)
60162306a36Sopenharmony_ci			exceptions |= FPSCR_IXC;
60262306a36Sopenharmony_ci	} else {
60362306a36Sopenharmony_ci		d = 0;
60462306a36Sopenharmony_ci		if (vsm.exponent | vsm.significand) {
60562306a36Sopenharmony_ci			exceptions |= FPSCR_IXC;
60662306a36Sopenharmony_ci			if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
60762306a36Sopenharmony_ci				d = 1;
60862306a36Sopenharmony_ci			else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
60962306a36Sopenharmony_ci				d = 0;
61062306a36Sopenharmony_ci				exceptions |= FPSCR_IOC;
61162306a36Sopenharmony_ci			}
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	vfp_put_float(d, sd);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	return exceptions;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic u32 vfp_single_ftouiz(int sd, int unused, s32 m, u32 fpscr)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	return vfp_single_ftoui(sd, unused, m, FPSCR_ROUND_TOZERO);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic u32 vfp_single_ftosi(int sd, int unused, s32 m, u32 fpscr)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct vfp_single vsm;
63062306a36Sopenharmony_ci	u32 d, exceptions = 0;
63162306a36Sopenharmony_ci	int rmode = fpscr & FPSCR_RMODE_MASK;
63262306a36Sopenharmony_ci	int tm;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
63562306a36Sopenharmony_ci	vfp_single_dump("VSM", &vsm);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	/*
63862306a36Sopenharmony_ci	 * Do we have a denormalised number?
63962306a36Sopenharmony_ci	 */
64062306a36Sopenharmony_ci	tm = vfp_single_type(&vsm);
64162306a36Sopenharmony_ci	if (vfp_single_type(&vsm) & VFP_DENORMAL)
64262306a36Sopenharmony_ci		exceptions |= FPSCR_IDC;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (tm & VFP_NAN) {
64562306a36Sopenharmony_ci		d = 0;
64662306a36Sopenharmony_ci		exceptions |= FPSCR_IOC;
64762306a36Sopenharmony_ci	} else if (vsm.exponent >= 127 + 32) {
64862306a36Sopenharmony_ci		/*
64962306a36Sopenharmony_ci		 * m >= 2^31-2^7: invalid
65062306a36Sopenharmony_ci		 */
65162306a36Sopenharmony_ci		d = 0x7fffffff;
65262306a36Sopenharmony_ci		if (vsm.sign)
65362306a36Sopenharmony_ci			d = ~d;
65462306a36Sopenharmony_ci		exceptions |= FPSCR_IOC;
65562306a36Sopenharmony_ci	} else if (vsm.exponent >= 127 - 1) {
65662306a36Sopenharmony_ci		int shift = 127 + 31 - vsm.exponent;
65762306a36Sopenharmony_ci		u32 rem, incr = 0;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		/* 2^0 <= m <= 2^31-2^7 */
66062306a36Sopenharmony_ci		d = (vsm.significand << 1) >> shift;
66162306a36Sopenharmony_ci		rem = vsm.significand << (33 - shift);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		if (rmode == FPSCR_ROUND_NEAREST) {
66462306a36Sopenharmony_ci			incr = 0x80000000;
66562306a36Sopenharmony_ci			if ((d & 1) == 0)
66662306a36Sopenharmony_ci				incr -= 1;
66762306a36Sopenharmony_ci		} else if (rmode == FPSCR_ROUND_TOZERO) {
66862306a36Sopenharmony_ci			incr = 0;
66962306a36Sopenharmony_ci		} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) {
67062306a36Sopenharmony_ci			incr = ~0;
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		if ((rem + incr) < rem && d < 0xffffffff)
67462306a36Sopenharmony_ci			d += 1;
67562306a36Sopenharmony_ci		if (d > 0x7fffffff + (vsm.sign != 0)) {
67662306a36Sopenharmony_ci			d = 0x7fffffff + (vsm.sign != 0);
67762306a36Sopenharmony_ci			exceptions |= FPSCR_IOC;
67862306a36Sopenharmony_ci		} else if (rem)
67962306a36Sopenharmony_ci			exceptions |= FPSCR_IXC;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		if (vsm.sign)
68262306a36Sopenharmony_ci			d = -d;
68362306a36Sopenharmony_ci	} else {
68462306a36Sopenharmony_ci		d = 0;
68562306a36Sopenharmony_ci		if (vsm.exponent | vsm.significand) {
68662306a36Sopenharmony_ci			exceptions |= FPSCR_IXC;
68762306a36Sopenharmony_ci			if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
68862306a36Sopenharmony_ci				d = 1;
68962306a36Sopenharmony_ci			else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign)
69062306a36Sopenharmony_ci				d = -1;
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	vfp_put_float((s32)d, sd);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	return exceptions;
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic u32 vfp_single_ftosiz(int sd, int unused, s32 m, u32 fpscr)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	return vfp_single_ftosi(sd, unused, m, FPSCR_ROUND_TOZERO);
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic struct op fops_ext[32] = {
70762306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCPY)]	= { vfp_single_fcpy,   0 },
70862306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FABS)]	= { vfp_single_fabs,   0 },
70962306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FNEG)]	= { vfp_single_fneg,   0 },
71062306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FSQRT)]	= { vfp_single_fsqrt,  0 },
71162306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCMP)]	= { vfp_single_fcmp,   OP_SCALAR },
71262306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCMPE)]	= { vfp_single_fcmpe,  OP_SCALAR },
71362306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCMPZ)]	= { vfp_single_fcmpz,  OP_SCALAR },
71462306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCMPEZ)]	= { vfp_single_fcmpez, OP_SCALAR },
71562306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCVT)]	= { vfp_single_fcvtd,  OP_SCALAR|OP_DD },
71662306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FUITO)]	= { vfp_single_fuito,  OP_SCALAR },
71762306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FSITO)]	= { vfp_single_fsito,  OP_SCALAR },
71862306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FTOUI)]	= { vfp_single_ftoui,  OP_SCALAR },
71962306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FTOUIZ)]	= { vfp_single_ftouiz, OP_SCALAR },
72062306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FTOSI)]	= { vfp_single_ftosi,  OP_SCALAR },
72162306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FTOSIZ)]	= { vfp_single_ftosiz, OP_SCALAR },
72262306a36Sopenharmony_ci};
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic u32
72962306a36Sopenharmony_civfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn,
73062306a36Sopenharmony_ci			  struct vfp_single *vsm, u32 fpscr)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct vfp_single *vsp;
73362306a36Sopenharmony_ci	u32 exceptions = 0;
73462306a36Sopenharmony_ci	int tn, tm;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	tn = vfp_single_type(vsn);
73762306a36Sopenharmony_ci	tm = vfp_single_type(vsm);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	if (tn & tm & VFP_INFINITY) {
74062306a36Sopenharmony_ci		/*
74162306a36Sopenharmony_ci		 * Two infinities.  Are they different signs?
74262306a36Sopenharmony_ci		 */
74362306a36Sopenharmony_ci		if (vsn->sign ^ vsm->sign) {
74462306a36Sopenharmony_ci			/*
74562306a36Sopenharmony_ci			 * different signs -> invalid
74662306a36Sopenharmony_ci			 */
74762306a36Sopenharmony_ci			exceptions = FPSCR_IOC;
74862306a36Sopenharmony_ci			vsp = &vfp_single_default_qnan;
74962306a36Sopenharmony_ci		} else {
75062306a36Sopenharmony_ci			/*
75162306a36Sopenharmony_ci			 * same signs -> valid
75262306a36Sopenharmony_ci			 */
75362306a36Sopenharmony_ci			vsp = vsn;
75462306a36Sopenharmony_ci		}
75562306a36Sopenharmony_ci	} else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
75662306a36Sopenharmony_ci		/*
75762306a36Sopenharmony_ci		 * One infinity and one number -> infinity
75862306a36Sopenharmony_ci		 */
75962306a36Sopenharmony_ci		vsp = vsn;
76062306a36Sopenharmony_ci	} else {
76162306a36Sopenharmony_ci		/*
76262306a36Sopenharmony_ci		 * 'n' is a NaN of some type
76362306a36Sopenharmony_ci		 */
76462306a36Sopenharmony_ci		return vfp_propagate_nan(vsd, vsn, vsm, fpscr);
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	*vsd = *vsp;
76762306a36Sopenharmony_ci	return exceptions;
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic u32
77162306a36Sopenharmony_civfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn,
77262306a36Sopenharmony_ci	       struct vfp_single *vsm, u32 fpscr)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	u32 exp_diff, m_sig;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (vsn->significand & 0x80000000 ||
77762306a36Sopenharmony_ci	    vsm->significand & 0x80000000) {
77862306a36Sopenharmony_ci		pr_info("VFP: bad FP values in %s\n", __func__);
77962306a36Sopenharmony_ci		vfp_single_dump("VSN", vsn);
78062306a36Sopenharmony_ci		vfp_single_dump("VSM", vsm);
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/*
78462306a36Sopenharmony_ci	 * Ensure that 'n' is the largest magnitude number.  Note that
78562306a36Sopenharmony_ci	 * if 'n' and 'm' have equal exponents, we do not swap them.
78662306a36Sopenharmony_ci	 * This ensures that NaN propagation works correctly.
78762306a36Sopenharmony_ci	 */
78862306a36Sopenharmony_ci	if (vsn->exponent < vsm->exponent) {
78962306a36Sopenharmony_ci		struct vfp_single *t = vsn;
79062306a36Sopenharmony_ci		vsn = vsm;
79162306a36Sopenharmony_ci		vsm = t;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/*
79562306a36Sopenharmony_ci	 * Is 'n' an infinity or a NaN?  Note that 'm' may be a number,
79662306a36Sopenharmony_ci	 * infinity or a NaN here.
79762306a36Sopenharmony_ci	 */
79862306a36Sopenharmony_ci	if (vsn->exponent == 255)
79962306a36Sopenharmony_ci		return vfp_single_fadd_nonnumber(vsd, vsn, vsm, fpscr);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	/*
80262306a36Sopenharmony_ci	 * We have two proper numbers, where 'vsn' is the larger magnitude.
80362306a36Sopenharmony_ci	 *
80462306a36Sopenharmony_ci	 * Copy 'n' to 'd' before doing the arithmetic.
80562306a36Sopenharmony_ci	 */
80662306a36Sopenharmony_ci	*vsd = *vsn;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	/*
80962306a36Sopenharmony_ci	 * Align both numbers.
81062306a36Sopenharmony_ci	 */
81162306a36Sopenharmony_ci	exp_diff = vsn->exponent - vsm->exponent;
81262306a36Sopenharmony_ci	m_sig = vfp_shiftright32jamming(vsm->significand, exp_diff);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/*
81562306a36Sopenharmony_ci	 * If the signs are different, we are really subtracting.
81662306a36Sopenharmony_ci	 */
81762306a36Sopenharmony_ci	if (vsn->sign ^ vsm->sign) {
81862306a36Sopenharmony_ci		m_sig = vsn->significand - m_sig;
81962306a36Sopenharmony_ci		if ((s32)m_sig < 0) {
82062306a36Sopenharmony_ci			vsd->sign = vfp_sign_negate(vsd->sign);
82162306a36Sopenharmony_ci			m_sig = -m_sig;
82262306a36Sopenharmony_ci		} else if (m_sig == 0) {
82362306a36Sopenharmony_ci			vsd->sign = (fpscr & FPSCR_RMODE_MASK) ==
82462306a36Sopenharmony_ci				      FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
82562306a36Sopenharmony_ci		}
82662306a36Sopenharmony_ci	} else {
82762306a36Sopenharmony_ci		m_sig = vsn->significand + m_sig;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci	vsd->significand = m_sig;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	return 0;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic u32
83562306a36Sopenharmony_civfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_single *vsm, u32 fpscr)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	vfp_single_dump("VSN", vsn);
83862306a36Sopenharmony_ci	vfp_single_dump("VSM", vsm);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	/*
84162306a36Sopenharmony_ci	 * Ensure that 'n' is the largest magnitude number.  Note that
84262306a36Sopenharmony_ci	 * if 'n' and 'm' have equal exponents, we do not swap them.
84362306a36Sopenharmony_ci	 * This ensures that NaN propagation works correctly.
84462306a36Sopenharmony_ci	 */
84562306a36Sopenharmony_ci	if (vsn->exponent < vsm->exponent) {
84662306a36Sopenharmony_ci		struct vfp_single *t = vsn;
84762306a36Sopenharmony_ci		vsn = vsm;
84862306a36Sopenharmony_ci		vsm = t;
84962306a36Sopenharmony_ci		pr_debug("VFP: swapping M <-> N\n");
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	vsd->sign = vsn->sign ^ vsm->sign;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	/*
85562306a36Sopenharmony_ci	 * If 'n' is an infinity or NaN, handle it.  'm' may be anything.
85662306a36Sopenharmony_ci	 */
85762306a36Sopenharmony_ci	if (vsn->exponent == 255) {
85862306a36Sopenharmony_ci		if (vsn->significand || (vsm->exponent == 255 && vsm->significand))
85962306a36Sopenharmony_ci			return vfp_propagate_nan(vsd, vsn, vsm, fpscr);
86062306a36Sopenharmony_ci		if ((vsm->exponent | vsm->significand) == 0) {
86162306a36Sopenharmony_ci			*vsd = vfp_single_default_qnan;
86262306a36Sopenharmony_ci			return FPSCR_IOC;
86362306a36Sopenharmony_ci		}
86462306a36Sopenharmony_ci		vsd->exponent = vsn->exponent;
86562306a36Sopenharmony_ci		vsd->significand = 0;
86662306a36Sopenharmony_ci		return 0;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	/*
87062306a36Sopenharmony_ci	 * If 'm' is zero, the result is always zero.  In this case,
87162306a36Sopenharmony_ci	 * 'n' may be zero or a number, but it doesn't matter which.
87262306a36Sopenharmony_ci	 */
87362306a36Sopenharmony_ci	if ((vsm->exponent | vsm->significand) == 0) {
87462306a36Sopenharmony_ci		vsd->exponent = 0;
87562306a36Sopenharmony_ci		vsd->significand = 0;
87662306a36Sopenharmony_ci		return 0;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	/*
88062306a36Sopenharmony_ci	 * We add 2 to the destination exponent for the same reason as
88162306a36Sopenharmony_ci	 * the addition case - though this time we have +1 from each
88262306a36Sopenharmony_ci	 * input operand.
88362306a36Sopenharmony_ci	 */
88462306a36Sopenharmony_ci	vsd->exponent = vsn->exponent + vsm->exponent - 127 + 2;
88562306a36Sopenharmony_ci	vsd->significand = vfp_hi64to32jamming((u64)vsn->significand * vsm->significand);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	vfp_single_dump("VSD", vsd);
88862306a36Sopenharmony_ci	return 0;
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci#define NEG_MULTIPLY	(1 << 0)
89262306a36Sopenharmony_ci#define NEG_SUBTRACT	(1 << 1)
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic u32
89562306a36Sopenharmony_civfp_single_multiply_accumulate(int sd, int sn, s32 m, u32 fpscr, u32 negate, char *func)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct vfp_single vsd, vsp, vsn, vsm;
89862306a36Sopenharmony_ci	u32 exceptions;
89962306a36Sopenharmony_ci	s32 v;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	v = vfp_get_float(sn);
90262306a36Sopenharmony_ci	pr_debug("VFP: s%u = %08x\n", sn, v);
90362306a36Sopenharmony_ci	vfp_single_unpack(&vsn, v);
90462306a36Sopenharmony_ci	if (vsn.exponent == 0 && vsn.significand)
90562306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsn);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
90862306a36Sopenharmony_ci	if (vsm.exponent == 0 && vsm.significand)
90962306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsm);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
91262306a36Sopenharmony_ci	if (negate & NEG_MULTIPLY)
91362306a36Sopenharmony_ci		vsp.sign = vfp_sign_negate(vsp.sign);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	v = vfp_get_float(sd);
91662306a36Sopenharmony_ci	pr_debug("VFP: s%u = %08x\n", sd, v);
91762306a36Sopenharmony_ci	vfp_single_unpack(&vsn, v);
91862306a36Sopenharmony_ci	if (vsn.exponent == 0 && vsn.significand)
91962306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsn);
92062306a36Sopenharmony_ci	if (negate & NEG_SUBTRACT)
92162306a36Sopenharmony_ci		vsn.sign = vfp_sign_negate(vsn.sign);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, func);
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci/*
92962306a36Sopenharmony_ci * Standard operations
93062306a36Sopenharmony_ci */
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci/*
93362306a36Sopenharmony_ci * sd = sd + (sn * sm)
93462306a36Sopenharmony_ci */
93562306a36Sopenharmony_cistatic u32 vfp_single_fmac(int sd, int sn, s32 m, u32 fpscr)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	return vfp_single_multiply_accumulate(sd, sn, m, fpscr, 0, "fmac");
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci/*
94162306a36Sopenharmony_ci * sd = sd - (sn * sm)
94262306a36Sopenharmony_ci */
94362306a36Sopenharmony_cistatic u32 vfp_single_fnmac(int sd, int sn, s32 m, u32 fpscr)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac");
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci/*
94962306a36Sopenharmony_ci * sd = -sd + (sn * sm)
95062306a36Sopenharmony_ci */
95162306a36Sopenharmony_cistatic u32 vfp_single_fmsc(int sd, int sn, s32 m, u32 fpscr)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc");
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci/*
95762306a36Sopenharmony_ci * sd = -sd - (sn * sm)
95862306a36Sopenharmony_ci */
95962306a36Sopenharmony_cistatic u32 vfp_single_fnmsc(int sd, int sn, s32 m, u32 fpscr)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
96262306a36Sopenharmony_ci}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci/*
96562306a36Sopenharmony_ci * sd = sn * sm
96662306a36Sopenharmony_ci */
96762306a36Sopenharmony_cistatic u32 vfp_single_fmul(int sd, int sn, s32 m, u32 fpscr)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	struct vfp_single vsd, vsn, vsm;
97062306a36Sopenharmony_ci	u32 exceptions;
97162306a36Sopenharmony_ci	s32 n = vfp_get_float(sn);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	pr_debug("VFP: s%u = %08x\n", sn, n);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	vfp_single_unpack(&vsn, n);
97662306a36Sopenharmony_ci	if (vsn.exponent == 0 && vsn.significand)
97762306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsn);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
98062306a36Sopenharmony_ci	if (vsm.exponent == 0 && vsm.significand)
98162306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsm);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
98462306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fmul");
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci/*
98862306a36Sopenharmony_ci * sd = -(sn * sm)
98962306a36Sopenharmony_ci */
99062306a36Sopenharmony_cistatic u32 vfp_single_fnmul(int sd, int sn, s32 m, u32 fpscr)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	struct vfp_single vsd, vsn, vsm;
99362306a36Sopenharmony_ci	u32 exceptions;
99462306a36Sopenharmony_ci	s32 n = vfp_get_float(sn);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	pr_debug("VFP: s%u = %08x\n", sn, n);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	vfp_single_unpack(&vsn, n);
99962306a36Sopenharmony_ci	if (vsn.exponent == 0 && vsn.significand)
100062306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsn);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
100362306a36Sopenharmony_ci	if (vsm.exponent == 0 && vsm.significand)
100462306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsm);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
100762306a36Sopenharmony_ci	vsd.sign = vfp_sign_negate(vsd.sign);
100862306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fnmul");
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci/*
101262306a36Sopenharmony_ci * sd = sn + sm
101362306a36Sopenharmony_ci */
101462306a36Sopenharmony_cistatic u32 vfp_single_fadd(int sd, int sn, s32 m, u32 fpscr)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	struct vfp_single vsd, vsn, vsm;
101762306a36Sopenharmony_ci	u32 exceptions;
101862306a36Sopenharmony_ci	s32 n = vfp_get_float(sn);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	pr_debug("VFP: s%u = %08x\n", sn, n);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/*
102362306a36Sopenharmony_ci	 * Unpack and normalise denormals.
102462306a36Sopenharmony_ci	 */
102562306a36Sopenharmony_ci	vfp_single_unpack(&vsn, n);
102662306a36Sopenharmony_ci	if (vsn.exponent == 0 && vsn.significand)
102762306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsn);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
103062306a36Sopenharmony_ci	if (vsm.exponent == 0 && vsm.significand)
103162306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsm);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fadd");
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci/*
103962306a36Sopenharmony_ci * sd = sn - sm
104062306a36Sopenharmony_ci */
104162306a36Sopenharmony_cistatic u32 vfp_single_fsub(int sd, int sn, s32 m, u32 fpscr)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	/*
104462306a36Sopenharmony_ci	 * Subtraction is addition with one sign inverted.
104562306a36Sopenharmony_ci	 */
104662306a36Sopenharmony_ci	return vfp_single_fadd(sd, sn, vfp_single_packed_negate(m), fpscr);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci/*
105062306a36Sopenharmony_ci * sd = sn / sm
105162306a36Sopenharmony_ci */
105262306a36Sopenharmony_cistatic u32 vfp_single_fdiv(int sd, int sn, s32 m, u32 fpscr)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	struct vfp_single vsd, vsn, vsm;
105562306a36Sopenharmony_ci	u32 exceptions = 0;
105662306a36Sopenharmony_ci	s32 n = vfp_get_float(sn);
105762306a36Sopenharmony_ci	int tm, tn;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	pr_debug("VFP: s%u = %08x\n", sn, n);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	vfp_single_unpack(&vsn, n);
106262306a36Sopenharmony_ci	vfp_single_unpack(&vsm, m);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	vsd.sign = vsn.sign ^ vsm.sign;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	tn = vfp_single_type(&vsn);
106762306a36Sopenharmony_ci	tm = vfp_single_type(&vsm);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/*
107062306a36Sopenharmony_ci	 * Is n a NAN?
107162306a36Sopenharmony_ci	 */
107262306a36Sopenharmony_ci	if (tn & VFP_NAN)
107362306a36Sopenharmony_ci		goto vsn_nan;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	/*
107662306a36Sopenharmony_ci	 * Is m a NAN?
107762306a36Sopenharmony_ci	 */
107862306a36Sopenharmony_ci	if (tm & VFP_NAN)
107962306a36Sopenharmony_ci		goto vsm_nan;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/*
108262306a36Sopenharmony_ci	 * If n and m are infinity, the result is invalid
108362306a36Sopenharmony_ci	 * If n and m are zero, the result is invalid
108462306a36Sopenharmony_ci	 */
108562306a36Sopenharmony_ci	if (tm & tn & (VFP_INFINITY|VFP_ZERO))
108662306a36Sopenharmony_ci		goto invalid;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	/*
108962306a36Sopenharmony_ci	 * If n is infinity, the result is infinity
109062306a36Sopenharmony_ci	 */
109162306a36Sopenharmony_ci	if (tn & VFP_INFINITY)
109262306a36Sopenharmony_ci		goto infinity;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	/*
109562306a36Sopenharmony_ci	 * If m is zero, raise div0 exception
109662306a36Sopenharmony_ci	 */
109762306a36Sopenharmony_ci	if (tm & VFP_ZERO)
109862306a36Sopenharmony_ci		goto divzero;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	/*
110162306a36Sopenharmony_ci	 * If m is infinity, or n is zero, the result is zero
110262306a36Sopenharmony_ci	 */
110362306a36Sopenharmony_ci	if (tm & VFP_INFINITY || tn & VFP_ZERO)
110462306a36Sopenharmony_ci		goto zero;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (tn & VFP_DENORMAL)
110762306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsn);
110862306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
110962306a36Sopenharmony_ci		vfp_single_normalise_denormal(&vsm);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	/*
111262306a36Sopenharmony_ci	 * Ok, we have two numbers, we can perform division.
111362306a36Sopenharmony_ci	 */
111462306a36Sopenharmony_ci	vsd.exponent = vsn.exponent - vsm.exponent + 127 - 1;
111562306a36Sopenharmony_ci	vsm.significand <<= 1;
111662306a36Sopenharmony_ci	if (vsm.significand <= (2 * vsn.significand)) {
111762306a36Sopenharmony_ci		vsn.significand >>= 1;
111862306a36Sopenharmony_ci		vsd.exponent++;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci	{
112162306a36Sopenharmony_ci		u64 significand = (u64)vsn.significand << 32;
112262306a36Sopenharmony_ci		do_div(significand, vsm.significand);
112362306a36Sopenharmony_ci		vsd.significand = significand;
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci	if ((vsd.significand & 0x3f) == 0)
112662306a36Sopenharmony_ci		vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fdiv");
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci vsn_nan:
113162306a36Sopenharmony_ci	exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
113262306a36Sopenharmony_ci pack:
113362306a36Sopenharmony_ci	vfp_put_float(vfp_single_pack(&vsd), sd);
113462306a36Sopenharmony_ci	return exceptions;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci vsm_nan:
113762306a36Sopenharmony_ci	exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
113862306a36Sopenharmony_ci	goto pack;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci zero:
114162306a36Sopenharmony_ci	vsd.exponent = 0;
114262306a36Sopenharmony_ci	vsd.significand = 0;
114362306a36Sopenharmony_ci	goto pack;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci divzero:
114662306a36Sopenharmony_ci	exceptions = FPSCR_DZC;
114762306a36Sopenharmony_ci infinity:
114862306a36Sopenharmony_ci	vsd.exponent = 255;
114962306a36Sopenharmony_ci	vsd.significand = 0;
115062306a36Sopenharmony_ci	goto pack;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci invalid:
115362306a36Sopenharmony_ci	vfp_put_float(vfp_single_pack(&vfp_single_default_qnan), sd);
115462306a36Sopenharmony_ci	return FPSCR_IOC;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic struct op fops[16] = {
115862306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FMAC)]	= { vfp_single_fmac,  0 },
115962306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FNMAC)]	= { vfp_single_fnmac, 0 },
116062306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FMSC)]	= { vfp_single_fmsc,  0 },
116162306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FNMSC)]	= { vfp_single_fnmsc, 0 },
116262306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FMUL)]	= { vfp_single_fmul,  0 },
116362306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FNMUL)]	= { vfp_single_fnmul, 0 },
116462306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FADD)]	= { vfp_single_fadd,  0 },
116562306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FSUB)]	= { vfp_single_fsub,  0 },
116662306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FDIV)]	= { vfp_single_fdiv,  0 },
116762306a36Sopenharmony_ci};
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci#define FREG_BANK(x)	((x) & 0x18)
117062306a36Sopenharmony_ci#define FREG_IDX(x)	((x) & 7)
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ciu32 vfp_single_cpdo(u32 inst, u32 fpscr)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	u32 op = inst & FOP_MASK;
117562306a36Sopenharmony_ci	u32 exceptions = 0;
117662306a36Sopenharmony_ci	unsigned int dest;
117762306a36Sopenharmony_ci	unsigned int sn = vfp_get_sn(inst);
117862306a36Sopenharmony_ci	unsigned int sm = vfp_get_sm(inst);
117962306a36Sopenharmony_ci	unsigned int vecitr, veclen, vecstride;
118062306a36Sopenharmony_ci	struct op *fop;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/*
118762306a36Sopenharmony_ci	 * fcvtsd takes a dN register number as destination, not sN.
118862306a36Sopenharmony_ci	 * Technically, if bit 0 of dd is set, this is an invalid
118962306a36Sopenharmony_ci	 * instruction.  However, we ignore this for efficiency.
119062306a36Sopenharmony_ci	 * It also only operates on scalars.
119162306a36Sopenharmony_ci	 */
119262306a36Sopenharmony_ci	if (fop->flags & OP_DD)
119362306a36Sopenharmony_ci		dest = vfp_get_dd(inst);
119462306a36Sopenharmony_ci	else
119562306a36Sopenharmony_ci		dest = vfp_get_sd(inst);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	/*
119862306a36Sopenharmony_ci	 * If destination bank is zero, vector length is always '1'.
119962306a36Sopenharmony_ci	 * ARM DDI0100F C5.1.3, C5.3.2.
120062306a36Sopenharmony_ci	 */
120162306a36Sopenharmony_ci	if ((fop->flags & OP_SCALAR) || FREG_BANK(dest) == 0)
120262306a36Sopenharmony_ci		veclen = 0;
120362306a36Sopenharmony_ci	else
120462306a36Sopenharmony_ci		veclen = fpscr & FPSCR_LENGTH_MASK;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
120762306a36Sopenharmony_ci		 (veclen >> FPSCR_LENGTH_BIT) + 1);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	if (!fop->fn)
121062306a36Sopenharmony_ci		goto invalid;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
121362306a36Sopenharmony_ci		s32 m = vfp_get_float(sm);
121462306a36Sopenharmony_ci		u32 except;
121562306a36Sopenharmony_ci		char type;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci		type = fop->flags & OP_DD ? 'd' : 's';
121862306a36Sopenharmony_ci		if (op == FOP_EXT)
121962306a36Sopenharmony_ci			pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n",
122062306a36Sopenharmony_ci				 vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
122162306a36Sopenharmony_ci				 sm, m);
122262306a36Sopenharmony_ci		else
122362306a36Sopenharmony_ci			pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n",
122462306a36Sopenharmony_ci				 vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
122562306a36Sopenharmony_ci				 FOP_TO_IDX(op), sm, m);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci		except = fop->fn(dest, sn, m, fpscr);
122862306a36Sopenharmony_ci		pr_debug("VFP: itr%d: exceptions=%08x\n",
122962306a36Sopenharmony_ci			 vecitr >> FPSCR_LENGTH_BIT, except);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci		exceptions |= except;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci		/*
123462306a36Sopenharmony_ci		 * CHECK: It appears to be undefined whether we stop when
123562306a36Sopenharmony_ci		 * we encounter an exception.  We continue.
123662306a36Sopenharmony_ci		 */
123762306a36Sopenharmony_ci		dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 7);
123862306a36Sopenharmony_ci		sn = FREG_BANK(sn) + ((FREG_IDX(sn) + vecstride) & 7);
123962306a36Sopenharmony_ci		if (FREG_BANK(sm) != 0)
124062306a36Sopenharmony_ci			sm = FREG_BANK(sm) + ((FREG_IDX(sm) + vecstride) & 7);
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci	return exceptions;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci invalid:
124562306a36Sopenharmony_ci	return (u32)-1;
124662306a36Sopenharmony_ci}
1247