162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/arch/arm/vfp/vfpdouble.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_double vfp_double_default_qnan = {
4362306a36Sopenharmony_ci	.exponent	= 2047,
4462306a36Sopenharmony_ci	.sign		= 0,
4562306a36Sopenharmony_ci	.significand	= VFP_DOUBLE_SIGNIFICAND_QNAN,
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void vfp_double_dump(const char *str, struct vfp_double *d)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	pr_debug("VFP: %s: sign=%d exponent=%d significand=%016llx\n",
5162306a36Sopenharmony_ci		 str, d->sign != 0, d->exponent, d->significand);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void vfp_double_normalise_denormal(struct vfp_double *vd)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	int bits = 31 - fls(vd->significand >> 32);
5762306a36Sopenharmony_ci	if (bits == 31)
5862306a36Sopenharmony_ci		bits = 63 - fls(vd->significand);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	vfp_double_dump("normalise_denormal: in", vd);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (bits) {
6362306a36Sopenharmony_ci		vd->exponent -= bits - 1;
6462306a36Sopenharmony_ci		vd->significand <<= bits;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	vfp_double_dump("normalise_denormal: out", vd);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciu32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	u64 significand, incr;
7362306a36Sopenharmony_ci	int exponent, shift, underflow;
7462306a36Sopenharmony_ci	u32 rmode;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	vfp_double_dump("pack: in", vd);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/*
7962306a36Sopenharmony_ci	 * Infinities and NaNs are a special case.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	if (vd->exponent == 2047 && (vd->significand == 0 || exceptions))
8262306a36Sopenharmony_ci		goto pack;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/*
8562306a36Sopenharmony_ci	 * Special-case zero.
8662306a36Sopenharmony_ci	 */
8762306a36Sopenharmony_ci	if (vd->significand == 0) {
8862306a36Sopenharmony_ci		vd->exponent = 0;
8962306a36Sopenharmony_ci		goto pack;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	exponent = vd->exponent;
9362306a36Sopenharmony_ci	significand = vd->significand;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	shift = 32 - fls(significand >> 32);
9662306a36Sopenharmony_ci	if (shift == 32)
9762306a36Sopenharmony_ci		shift = 64 - fls(significand);
9862306a36Sopenharmony_ci	if (shift) {
9962306a36Sopenharmony_ci		exponent -= shift;
10062306a36Sopenharmony_ci		significand <<= shift;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#ifdef DEBUG
10462306a36Sopenharmony_ci	vd->exponent = exponent;
10562306a36Sopenharmony_ci	vd->significand = significand;
10662306a36Sopenharmony_ci	vfp_double_dump("pack: normalised", vd);
10762306a36Sopenharmony_ci#endif
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/*
11062306a36Sopenharmony_ci	 * Tiny number?
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	underflow = exponent < 0;
11362306a36Sopenharmony_ci	if (underflow) {
11462306a36Sopenharmony_ci		significand = vfp_shiftright64jamming(significand, -exponent);
11562306a36Sopenharmony_ci		exponent = 0;
11662306a36Sopenharmony_ci#ifdef DEBUG
11762306a36Sopenharmony_ci		vd->exponent = exponent;
11862306a36Sopenharmony_ci		vd->significand = significand;
11962306a36Sopenharmony_ci		vfp_double_dump("pack: tiny number", vd);
12062306a36Sopenharmony_ci#endif
12162306a36Sopenharmony_ci		if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1)))
12262306a36Sopenharmony_ci			underflow = 0;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/*
12662306a36Sopenharmony_ci	 * Select rounding increment.
12762306a36Sopenharmony_ci	 */
12862306a36Sopenharmony_ci	incr = 0;
12962306a36Sopenharmony_ci	rmode = fpscr & FPSCR_RMODE_MASK;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (rmode == FPSCR_ROUND_NEAREST) {
13262306a36Sopenharmony_ci		incr = 1ULL << VFP_DOUBLE_LOW_BITS;
13362306a36Sopenharmony_ci		if ((significand & (1ULL << (VFP_DOUBLE_LOW_BITS + 1))) == 0)
13462306a36Sopenharmony_ci			incr -= 1;
13562306a36Sopenharmony_ci	} else if (rmode == FPSCR_ROUND_TOZERO) {
13662306a36Sopenharmony_ci		incr = 0;
13762306a36Sopenharmony_ci	} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
13862306a36Sopenharmony_ci		incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	pr_debug("VFP: rounding increment = 0x%08llx\n", incr);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/*
14362306a36Sopenharmony_ci	 * Is our rounding going to overflow?
14462306a36Sopenharmony_ci	 */
14562306a36Sopenharmony_ci	if ((significand + incr) < significand) {
14662306a36Sopenharmony_ci		exponent += 1;
14762306a36Sopenharmony_ci		significand = (significand >> 1) | (significand & 1);
14862306a36Sopenharmony_ci		incr >>= 1;
14962306a36Sopenharmony_ci#ifdef DEBUG
15062306a36Sopenharmony_ci		vd->exponent = exponent;
15162306a36Sopenharmony_ci		vd->significand = significand;
15262306a36Sopenharmony_ci		vfp_double_dump("pack: overflow", vd);
15362306a36Sopenharmony_ci#endif
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * If any of the low bits (which will be shifted out of the
15862306a36Sopenharmony_ci	 * number) are non-zero, the result is inexact.
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	if (significand & ((1 << (VFP_DOUBLE_LOW_BITS + 1)) - 1))
16162306a36Sopenharmony_ci		exceptions |= FPSCR_IXC;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/*
16462306a36Sopenharmony_ci	 * Do our rounding.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	significand += incr;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * Infinity?
17062306a36Sopenharmony_ci	 */
17162306a36Sopenharmony_ci	if (exponent >= 2046) {
17262306a36Sopenharmony_ci		exceptions |= FPSCR_OFC | FPSCR_IXC;
17362306a36Sopenharmony_ci		if (incr == 0) {
17462306a36Sopenharmony_ci			vd->exponent = 2045;
17562306a36Sopenharmony_ci			vd->significand = 0x7fffffffffffffffULL;
17662306a36Sopenharmony_ci		} else {
17762306a36Sopenharmony_ci			vd->exponent = 2047;		/* infinity */
17862306a36Sopenharmony_ci			vd->significand = 0;
17962306a36Sopenharmony_ci		}
18062306a36Sopenharmony_ci	} else {
18162306a36Sopenharmony_ci		if (significand >> (VFP_DOUBLE_LOW_BITS + 1) == 0)
18262306a36Sopenharmony_ci			exponent = 0;
18362306a36Sopenharmony_ci		if (exponent || significand > 0x8000000000000000ULL)
18462306a36Sopenharmony_ci			underflow = 0;
18562306a36Sopenharmony_ci		if (underflow)
18662306a36Sopenharmony_ci			exceptions |= FPSCR_UFC;
18762306a36Sopenharmony_ci		vd->exponent = exponent;
18862306a36Sopenharmony_ci		vd->significand = significand >> 1;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci pack:
19262306a36Sopenharmony_ci	vfp_double_dump("pack: final", vd);
19362306a36Sopenharmony_ci	{
19462306a36Sopenharmony_ci		s64 d = vfp_double_pack(vd);
19562306a36Sopenharmony_ci		pr_debug("VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func,
19662306a36Sopenharmony_ci			 dd, d, exceptions);
19762306a36Sopenharmony_ci		vfp_put_double(d, dd);
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	return exceptions;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/*
20362306a36Sopenharmony_ci * Propagate the NaN, setting exceptions if it is signalling.
20462306a36Sopenharmony_ci * 'n' is always a NaN.  'm' may be a number, NaN or infinity.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic u32
20762306a36Sopenharmony_civfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn,
20862306a36Sopenharmony_ci		  struct vfp_double *vdm, u32 fpscr)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct vfp_double *nan;
21162306a36Sopenharmony_ci	int tn, tm = 0;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	tn = vfp_double_type(vdn);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (vdm)
21662306a36Sopenharmony_ci		tm = vfp_double_type(vdm);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (fpscr & FPSCR_DEFAULT_NAN)
21962306a36Sopenharmony_ci		/*
22062306a36Sopenharmony_ci		 * Default NaN mode - always returns a quiet NaN
22162306a36Sopenharmony_ci		 */
22262306a36Sopenharmony_ci		nan = &vfp_double_default_qnan;
22362306a36Sopenharmony_ci	else {
22462306a36Sopenharmony_ci		/*
22562306a36Sopenharmony_ci		 * Contemporary mode - select the first signalling
22662306a36Sopenharmony_ci		 * NAN, or if neither are signalling, the first
22762306a36Sopenharmony_ci		 * quiet NAN.
22862306a36Sopenharmony_ci		 */
22962306a36Sopenharmony_ci		if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
23062306a36Sopenharmony_ci			nan = vdn;
23162306a36Sopenharmony_ci		else
23262306a36Sopenharmony_ci			nan = vdm;
23362306a36Sopenharmony_ci		/*
23462306a36Sopenharmony_ci		 * Make the NaN quiet.
23562306a36Sopenharmony_ci		 */
23662306a36Sopenharmony_ci		nan->significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	*vdd = *nan;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/*
24262306a36Sopenharmony_ci	 * If one was a signalling NAN, raise invalid operation.
24362306a36Sopenharmony_ci	 */
24462306a36Sopenharmony_ci	return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/*
24862306a36Sopenharmony_ci * Extended operations
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_cistatic u32 vfp_double_fabs(int dd, int unused, int dm, u32 fpscr)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	vfp_put_double(vfp_double_packed_abs(vfp_get_double(dm)), dd);
25362306a36Sopenharmony_ci	return 0;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic u32 vfp_double_fcpy(int dd, int unused, int dm, u32 fpscr)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	vfp_put_double(vfp_get_double(dm), dd);
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic u32 vfp_double_fneg(int dd, int unused, int dm, u32 fpscr)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	vfp_put_double(vfp_double_packed_negate(vfp_get_double(dm)), dd);
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic u32 vfp_double_fsqrt(int dd, int unused, int dm, u32 fpscr)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct vfp_double vdm, vdd;
27162306a36Sopenharmony_ci	int ret, tm;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
27462306a36Sopenharmony_ci	tm = vfp_double_type(&vdm);
27562306a36Sopenharmony_ci	if (tm & (VFP_NAN|VFP_INFINITY)) {
27662306a36Sopenharmony_ci		struct vfp_double *vdp = &vdd;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		if (tm & VFP_NAN)
27962306a36Sopenharmony_ci			ret = vfp_propagate_nan(vdp, &vdm, NULL, fpscr);
28062306a36Sopenharmony_ci		else if (vdm.sign == 0) {
28162306a36Sopenharmony_ci sqrt_copy:
28262306a36Sopenharmony_ci			vdp = &vdm;
28362306a36Sopenharmony_ci			ret = 0;
28462306a36Sopenharmony_ci		} else {
28562306a36Sopenharmony_ci sqrt_invalid:
28662306a36Sopenharmony_ci			vdp = &vfp_double_default_qnan;
28762306a36Sopenharmony_ci			ret = FPSCR_IOC;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci		vfp_put_double(vfp_double_pack(vdp), dd);
29062306a36Sopenharmony_ci		return ret;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/*
29462306a36Sopenharmony_ci	 * sqrt(+/- 0) == +/- 0
29562306a36Sopenharmony_ci	 */
29662306a36Sopenharmony_ci	if (tm & VFP_ZERO)
29762306a36Sopenharmony_ci		goto sqrt_copy;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/*
30062306a36Sopenharmony_ci	 * Normalise a denormalised number
30162306a36Sopenharmony_ci	 */
30262306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
30362306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdm);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/*
30662306a36Sopenharmony_ci	 * sqrt(<0) = invalid
30762306a36Sopenharmony_ci	 */
30862306a36Sopenharmony_ci	if (vdm.sign)
30962306a36Sopenharmony_ci		goto sqrt_invalid;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	vfp_double_dump("sqrt", &vdm);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/*
31462306a36Sopenharmony_ci	 * Estimate the square root.
31562306a36Sopenharmony_ci	 */
31662306a36Sopenharmony_ci	vdd.sign = 0;
31762306a36Sopenharmony_ci	vdd.exponent = ((vdm.exponent - 1023) >> 1) + 1023;
31862306a36Sopenharmony_ci	vdd.significand = (u64)vfp_estimate_sqrt_significand(vdm.exponent, vdm.significand >> 32) << 31;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	vfp_double_dump("sqrt estimate1", &vdd);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	vdm.significand >>= 1 + (vdm.exponent & 1);
32362306a36Sopenharmony_ci	vdd.significand += 2 + vfp_estimate_div128to64(vdm.significand, 0, vdd.significand);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	vfp_double_dump("sqrt estimate2", &vdd);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/*
32862306a36Sopenharmony_ci	 * And now adjust.
32962306a36Sopenharmony_ci	 */
33062306a36Sopenharmony_ci	if ((vdd.significand & VFP_DOUBLE_LOW_BITS_MASK) <= 5) {
33162306a36Sopenharmony_ci		if (vdd.significand < 2) {
33262306a36Sopenharmony_ci			vdd.significand = ~0ULL;
33362306a36Sopenharmony_ci		} else {
33462306a36Sopenharmony_ci			u64 termh, terml, remh, reml;
33562306a36Sopenharmony_ci			vdm.significand <<= 2;
33662306a36Sopenharmony_ci			mul64to128(&termh, &terml, vdd.significand, vdd.significand);
33762306a36Sopenharmony_ci			sub128(&remh, &reml, vdm.significand, 0, termh, terml);
33862306a36Sopenharmony_ci			while ((s64)remh < 0) {
33962306a36Sopenharmony_ci				vdd.significand -= 1;
34062306a36Sopenharmony_ci				shift64left(&termh, &terml, vdd.significand);
34162306a36Sopenharmony_ci				terml |= 1;
34262306a36Sopenharmony_ci				add128(&remh, &reml, remh, reml, termh, terml);
34362306a36Sopenharmony_ci			}
34462306a36Sopenharmony_ci			vdd.significand |= (remh | reml) != 0;
34562306a36Sopenharmony_ci		}
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci	vdd.significand = vfp_shiftright64jamming(vdd.significand, 1);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fsqrt");
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/*
35362306a36Sopenharmony_ci * Equal	:= ZC
35462306a36Sopenharmony_ci * Less than	:= N
35562306a36Sopenharmony_ci * Greater than	:= C
35662306a36Sopenharmony_ci * Unordered	:= CV
35762306a36Sopenharmony_ci */
35862306a36Sopenharmony_cistatic u32 vfp_compare(int dd, int signal_on_qnan, int dm, u32 fpscr)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	s64 d, m;
36162306a36Sopenharmony_ci	u32 ret = 0;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	m = vfp_get_double(dm);
36462306a36Sopenharmony_ci	if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) {
36562306a36Sopenharmony_ci		ret |= FPSCR_C | FPSCR_V;
36662306a36Sopenharmony_ci		if (signal_on_qnan || !(vfp_double_packed_mantissa(m) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1))))
36762306a36Sopenharmony_ci			/*
36862306a36Sopenharmony_ci			 * Signalling NaN, or signalling on quiet NaN
36962306a36Sopenharmony_ci			 */
37062306a36Sopenharmony_ci			ret |= FPSCR_IOC;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	d = vfp_get_double(dd);
37462306a36Sopenharmony_ci	if (vfp_double_packed_exponent(d) == 2047 && vfp_double_packed_mantissa(d)) {
37562306a36Sopenharmony_ci		ret |= FPSCR_C | FPSCR_V;
37662306a36Sopenharmony_ci		if (signal_on_qnan || !(vfp_double_packed_mantissa(d) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1))))
37762306a36Sopenharmony_ci			/*
37862306a36Sopenharmony_ci			 * Signalling NaN, or signalling on quiet NaN
37962306a36Sopenharmony_ci			 */
38062306a36Sopenharmony_ci			ret |= FPSCR_IOC;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (ret == 0) {
38462306a36Sopenharmony_ci		if (d == m || vfp_double_packed_abs(d | m) == 0) {
38562306a36Sopenharmony_ci			/*
38662306a36Sopenharmony_ci			 * equal
38762306a36Sopenharmony_ci			 */
38862306a36Sopenharmony_ci			ret |= FPSCR_Z | FPSCR_C;
38962306a36Sopenharmony_ci		} else if (vfp_double_packed_sign(d ^ m)) {
39062306a36Sopenharmony_ci			/*
39162306a36Sopenharmony_ci			 * different signs
39262306a36Sopenharmony_ci			 */
39362306a36Sopenharmony_ci			if (vfp_double_packed_sign(d))
39462306a36Sopenharmony_ci				/*
39562306a36Sopenharmony_ci				 * d is negative, so d < m
39662306a36Sopenharmony_ci				 */
39762306a36Sopenharmony_ci				ret |= FPSCR_N;
39862306a36Sopenharmony_ci			else
39962306a36Sopenharmony_ci				/*
40062306a36Sopenharmony_ci				 * d is positive, so d > m
40162306a36Sopenharmony_ci				 */
40262306a36Sopenharmony_ci				ret |= FPSCR_C;
40362306a36Sopenharmony_ci		} else if ((vfp_double_packed_sign(d) != 0) ^ (d < m)) {
40462306a36Sopenharmony_ci			/*
40562306a36Sopenharmony_ci			 * d < m
40662306a36Sopenharmony_ci			 */
40762306a36Sopenharmony_ci			ret |= FPSCR_N;
40862306a36Sopenharmony_ci		} else if ((vfp_double_packed_sign(d) != 0) ^ (d > m)) {
40962306a36Sopenharmony_ci			/*
41062306a36Sopenharmony_ci			 * d > m
41162306a36Sopenharmony_ci			 */
41262306a36Sopenharmony_ci			ret |= FPSCR_C;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return ret;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic u32 vfp_double_fcmp(int dd, int unused, int dm, u32 fpscr)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	return vfp_compare(dd, 0, dm, fpscr);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic u32 vfp_double_fcmpe(int dd, int unused, int dm, u32 fpscr)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	return vfp_compare(dd, 1, dm, fpscr);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic u32 vfp_double_fcmpz(int dd, int unused, int dm, u32 fpscr)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	return vfp_compare(dd, 0, VFP_REG_ZERO, fpscr);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic u32 vfp_double_fcmpez(int dd, int unused, int dm, u32 fpscr)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	return vfp_compare(dd, 1, VFP_REG_ZERO, fpscr);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic u32 vfp_double_fcvts(int sd, int unused, int dm, u32 fpscr)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct vfp_double vdm;
44262306a36Sopenharmony_ci	struct vfp_single vsd;
44362306a36Sopenharmony_ci	int tm;
44462306a36Sopenharmony_ci	u32 exceptions = 0;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	tm = vfp_double_type(&vdm);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/*
45162306a36Sopenharmony_ci	 * If we have a signalling NaN, signal invalid operation.
45262306a36Sopenharmony_ci	 */
45362306a36Sopenharmony_ci	if (tm == VFP_SNAN)
45462306a36Sopenharmony_ci		exceptions = FPSCR_IOC;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
45762306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdm);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	vsd.sign = vdm.sign;
46062306a36Sopenharmony_ci	vsd.significand = vfp_hi64to32jamming(vdm.significand);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/*
46362306a36Sopenharmony_ci	 * If we have an infinity or a NaN, the exponent must be 255
46462306a36Sopenharmony_ci	 */
46562306a36Sopenharmony_ci	if (tm & (VFP_INFINITY|VFP_NAN)) {
46662306a36Sopenharmony_ci		vsd.exponent = 255;
46762306a36Sopenharmony_ci		if (tm == VFP_QNAN)
46862306a36Sopenharmony_ci			vsd.significand |= VFP_SINGLE_SIGNIFICAND_QNAN;
46962306a36Sopenharmony_ci		goto pack_nan;
47062306a36Sopenharmony_ci	} else if (tm & VFP_ZERO)
47162306a36Sopenharmony_ci		vsd.exponent = 0;
47262306a36Sopenharmony_ci	else
47362306a36Sopenharmony_ci		vsd.exponent = vdm.exponent - (1023 - 127);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fcvts");
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci pack_nan:
47862306a36Sopenharmony_ci	vfp_put_float(vfp_single_pack(&vsd), sd);
47962306a36Sopenharmony_ci	return exceptions;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic u32 vfp_double_fuito(int dd, int unused, int dm, u32 fpscr)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct vfp_double vdm;
48562306a36Sopenharmony_ci	u32 m = vfp_get_float(dm);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	vdm.sign = 0;
48862306a36Sopenharmony_ci	vdm.exponent = 1023 + 63 - 1;
48962306a36Sopenharmony_ci	vdm.significand = (u64)m;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fuito");
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic u32 vfp_double_fsito(int dd, int unused, int dm, u32 fpscr)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct vfp_double vdm;
49762306a36Sopenharmony_ci	u32 m = vfp_get_float(dm);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	vdm.sign = (m & 0x80000000) >> 16;
50062306a36Sopenharmony_ci	vdm.exponent = 1023 + 63 - 1;
50162306a36Sopenharmony_ci	vdm.significand = vdm.sign ? -m : m;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fsito");
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic u32 vfp_double_ftoui(int sd, int unused, int dm, u32 fpscr)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct vfp_double vdm;
50962306a36Sopenharmony_ci	u32 d, exceptions = 0;
51062306a36Sopenharmony_ci	int rmode = fpscr & FPSCR_RMODE_MASK;
51162306a36Sopenharmony_ci	int tm;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/*
51662306a36Sopenharmony_ci	 * Do we have a denormalised number?
51762306a36Sopenharmony_ci	 */
51862306a36Sopenharmony_ci	tm = vfp_double_type(&vdm);
51962306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
52062306a36Sopenharmony_ci		exceptions |= FPSCR_IDC;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (tm & VFP_NAN)
52362306a36Sopenharmony_ci		vdm.sign = 0;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (vdm.exponent >= 1023 + 32) {
52662306a36Sopenharmony_ci		d = vdm.sign ? 0 : 0xffffffff;
52762306a36Sopenharmony_ci		exceptions = FPSCR_IOC;
52862306a36Sopenharmony_ci	} else if (vdm.exponent >= 1023 - 1) {
52962306a36Sopenharmony_ci		int shift = 1023 + 63 - vdm.exponent;
53062306a36Sopenharmony_ci		u64 rem, incr = 0;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		/*
53362306a36Sopenharmony_ci		 * 2^0 <= m < 2^32-2^8
53462306a36Sopenharmony_ci		 */
53562306a36Sopenharmony_ci		d = (vdm.significand << 1) >> shift;
53662306a36Sopenharmony_ci		rem = vdm.significand << (65 - shift);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		if (rmode == FPSCR_ROUND_NEAREST) {
53962306a36Sopenharmony_ci			incr = 0x8000000000000000ULL;
54062306a36Sopenharmony_ci			if ((d & 1) == 0)
54162306a36Sopenharmony_ci				incr -= 1;
54262306a36Sopenharmony_ci		} else if (rmode == FPSCR_ROUND_TOZERO) {
54362306a36Sopenharmony_ci			incr = 0;
54462306a36Sopenharmony_ci		} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
54562306a36Sopenharmony_ci			incr = ~0ULL;
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		if ((rem + incr) < rem) {
54962306a36Sopenharmony_ci			if (d < 0xffffffff)
55062306a36Sopenharmony_ci				d += 1;
55162306a36Sopenharmony_ci			else
55262306a36Sopenharmony_ci				exceptions |= FPSCR_IOC;
55362306a36Sopenharmony_ci		}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		if (d && vdm.sign) {
55662306a36Sopenharmony_ci			d = 0;
55762306a36Sopenharmony_ci			exceptions |= FPSCR_IOC;
55862306a36Sopenharmony_ci		} else if (rem)
55962306a36Sopenharmony_ci			exceptions |= FPSCR_IXC;
56062306a36Sopenharmony_ci	} else {
56162306a36Sopenharmony_ci		d = 0;
56262306a36Sopenharmony_ci		if (vdm.exponent | vdm.significand) {
56362306a36Sopenharmony_ci			exceptions |= FPSCR_IXC;
56462306a36Sopenharmony_ci			if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
56562306a36Sopenharmony_ci				d = 1;
56662306a36Sopenharmony_ci			else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
56762306a36Sopenharmony_ci				d = 0;
56862306a36Sopenharmony_ci				exceptions |= FPSCR_IOC;
56962306a36Sopenharmony_ci			}
57062306a36Sopenharmony_ci		}
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	vfp_put_float(d, sd);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return exceptions;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic u32 vfp_double_ftouiz(int sd, int unused, int dm, u32 fpscr)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	return vfp_double_ftoui(sd, unused, dm, FPSCR_ROUND_TOZERO);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct vfp_double vdm;
58862306a36Sopenharmony_ci	u32 d, exceptions = 0;
58962306a36Sopenharmony_ci	int rmode = fpscr & FPSCR_RMODE_MASK;
59062306a36Sopenharmony_ci	int tm;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
59362306a36Sopenharmony_ci	vfp_double_dump("VDM", &vdm);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/*
59662306a36Sopenharmony_ci	 * Do we have denormalised number?
59762306a36Sopenharmony_ci	 */
59862306a36Sopenharmony_ci	tm = vfp_double_type(&vdm);
59962306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
60062306a36Sopenharmony_ci		exceptions |= FPSCR_IDC;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (tm & VFP_NAN) {
60362306a36Sopenharmony_ci		d = 0;
60462306a36Sopenharmony_ci		exceptions |= FPSCR_IOC;
60562306a36Sopenharmony_ci	} else if (vdm.exponent >= 1023 + 32) {
60662306a36Sopenharmony_ci		d = 0x7fffffff;
60762306a36Sopenharmony_ci		if (vdm.sign)
60862306a36Sopenharmony_ci			d = ~d;
60962306a36Sopenharmony_ci		exceptions |= FPSCR_IOC;
61062306a36Sopenharmony_ci	} else if (vdm.exponent >= 1023 - 1) {
61162306a36Sopenharmony_ci		int shift = 1023 + 63 - vdm.exponent;	/* 58 */
61262306a36Sopenharmony_ci		u64 rem, incr = 0;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci		d = (vdm.significand << 1) >> shift;
61562306a36Sopenharmony_ci		rem = vdm.significand << (65 - shift);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		if (rmode == FPSCR_ROUND_NEAREST) {
61862306a36Sopenharmony_ci			incr = 0x8000000000000000ULL;
61962306a36Sopenharmony_ci			if ((d & 1) == 0)
62062306a36Sopenharmony_ci				incr -= 1;
62162306a36Sopenharmony_ci		} else if (rmode == FPSCR_ROUND_TOZERO) {
62262306a36Sopenharmony_ci			incr = 0;
62362306a36Sopenharmony_ci		} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
62462306a36Sopenharmony_ci			incr = ~0ULL;
62562306a36Sopenharmony_ci		}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		if ((rem + incr) < rem && d < 0xffffffff)
62862306a36Sopenharmony_ci			d += 1;
62962306a36Sopenharmony_ci		if (d > 0x7fffffff + (vdm.sign != 0)) {
63062306a36Sopenharmony_ci			d = 0x7fffffff + (vdm.sign != 0);
63162306a36Sopenharmony_ci			exceptions |= FPSCR_IOC;
63262306a36Sopenharmony_ci		} else if (rem)
63362306a36Sopenharmony_ci			exceptions |= FPSCR_IXC;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		if (vdm.sign)
63662306a36Sopenharmony_ci			d = -d;
63762306a36Sopenharmony_ci	} else {
63862306a36Sopenharmony_ci		d = 0;
63962306a36Sopenharmony_ci		if (vdm.exponent | vdm.significand) {
64062306a36Sopenharmony_ci			exceptions |= FPSCR_IXC;
64162306a36Sopenharmony_ci			if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
64262306a36Sopenharmony_ci				d = 1;
64362306a36Sopenharmony_ci			else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign)
64462306a36Sopenharmony_ci				d = -1;
64562306a36Sopenharmony_ci		}
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	vfp_put_float((s32)d, sd);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	return exceptions;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic u32 vfp_double_ftosiz(int dd, int unused, int dm, u32 fpscr)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	return vfp_double_ftosi(dd, unused, dm, FPSCR_ROUND_TOZERO);
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic struct op fops_ext[32] = {
66262306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCPY)]	= { vfp_double_fcpy,   0 },
66362306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FABS)]	= { vfp_double_fabs,   0 },
66462306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FNEG)]	= { vfp_double_fneg,   0 },
66562306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FSQRT)]	= { vfp_double_fsqrt,  0 },
66662306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCMP)]	= { vfp_double_fcmp,   OP_SCALAR },
66762306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCMPE)]	= { vfp_double_fcmpe,  OP_SCALAR },
66862306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCMPZ)]	= { vfp_double_fcmpz,  OP_SCALAR },
66962306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCMPEZ)]	= { vfp_double_fcmpez, OP_SCALAR },
67062306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FCVT)]	= { vfp_double_fcvts,  OP_SCALAR|OP_SD },
67162306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FUITO)]	= { vfp_double_fuito,  OP_SCALAR|OP_SM },
67262306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FSITO)]	= { vfp_double_fsito,  OP_SCALAR|OP_SM },
67362306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FTOUI)]	= { vfp_double_ftoui,  OP_SCALAR|OP_SD },
67462306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FTOUIZ)]	= { vfp_double_ftouiz, OP_SCALAR|OP_SD },
67562306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FTOSI)]	= { vfp_double_ftosi,  OP_SCALAR|OP_SD },
67662306a36Sopenharmony_ci	[FEXT_TO_IDX(FEXT_FTOSIZ)]	= { vfp_double_ftosiz, OP_SCALAR|OP_SD },
67762306a36Sopenharmony_ci};
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic u32
68362306a36Sopenharmony_civfp_double_fadd_nonnumber(struct vfp_double *vdd, struct vfp_double *vdn,
68462306a36Sopenharmony_ci			  struct vfp_double *vdm, u32 fpscr)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct vfp_double *vdp;
68762306a36Sopenharmony_ci	u32 exceptions = 0;
68862306a36Sopenharmony_ci	int tn, tm;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	tn = vfp_double_type(vdn);
69162306a36Sopenharmony_ci	tm = vfp_double_type(vdm);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (tn & tm & VFP_INFINITY) {
69462306a36Sopenharmony_ci		/*
69562306a36Sopenharmony_ci		 * Two infinities.  Are they different signs?
69662306a36Sopenharmony_ci		 */
69762306a36Sopenharmony_ci		if (vdn->sign ^ vdm->sign) {
69862306a36Sopenharmony_ci			/*
69962306a36Sopenharmony_ci			 * different signs -> invalid
70062306a36Sopenharmony_ci			 */
70162306a36Sopenharmony_ci			exceptions = FPSCR_IOC;
70262306a36Sopenharmony_ci			vdp = &vfp_double_default_qnan;
70362306a36Sopenharmony_ci		} else {
70462306a36Sopenharmony_ci			/*
70562306a36Sopenharmony_ci			 * same signs -> valid
70662306a36Sopenharmony_ci			 */
70762306a36Sopenharmony_ci			vdp = vdn;
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci	} else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
71062306a36Sopenharmony_ci		/*
71162306a36Sopenharmony_ci		 * One infinity and one number -> infinity
71262306a36Sopenharmony_ci		 */
71362306a36Sopenharmony_ci		vdp = vdn;
71462306a36Sopenharmony_ci	} else {
71562306a36Sopenharmony_ci		/*
71662306a36Sopenharmony_ci		 * 'n' is a NaN of some type
71762306a36Sopenharmony_ci		 */
71862306a36Sopenharmony_ci		return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci	*vdd = *vdp;
72162306a36Sopenharmony_ci	return exceptions;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic u32
72562306a36Sopenharmony_civfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,
72662306a36Sopenharmony_ci	       struct vfp_double *vdm, u32 fpscr)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	u32 exp_diff;
72962306a36Sopenharmony_ci	u64 m_sig;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (vdn->significand & (1ULL << 63) ||
73262306a36Sopenharmony_ci	    vdm->significand & (1ULL << 63)) {
73362306a36Sopenharmony_ci		pr_info("VFP: bad FP values in %s\n", __func__);
73462306a36Sopenharmony_ci		vfp_double_dump("VDN", vdn);
73562306a36Sopenharmony_ci		vfp_double_dump("VDM", vdm);
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	/*
73962306a36Sopenharmony_ci	 * Ensure that 'n' is the largest magnitude number.  Note that
74062306a36Sopenharmony_ci	 * if 'n' and 'm' have equal exponents, we do not swap them.
74162306a36Sopenharmony_ci	 * This ensures that NaN propagation works correctly.
74262306a36Sopenharmony_ci	 */
74362306a36Sopenharmony_ci	if (vdn->exponent < vdm->exponent) {
74462306a36Sopenharmony_ci		struct vfp_double *t = vdn;
74562306a36Sopenharmony_ci		vdn = vdm;
74662306a36Sopenharmony_ci		vdm = t;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	/*
75062306a36Sopenharmony_ci	 * Is 'n' an infinity or a NaN?  Note that 'm' may be a number,
75162306a36Sopenharmony_ci	 * infinity or a NaN here.
75262306a36Sopenharmony_ci	 */
75362306a36Sopenharmony_ci	if (vdn->exponent == 2047)
75462306a36Sopenharmony_ci		return vfp_double_fadd_nonnumber(vdd, vdn, vdm, fpscr);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	/*
75762306a36Sopenharmony_ci	 * We have two proper numbers, where 'vdn' is the larger magnitude.
75862306a36Sopenharmony_ci	 *
75962306a36Sopenharmony_ci	 * Copy 'n' to 'd' before doing the arithmetic.
76062306a36Sopenharmony_ci	 */
76162306a36Sopenharmony_ci	*vdd = *vdn;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/*
76462306a36Sopenharmony_ci	 * Align 'm' with the result.
76562306a36Sopenharmony_ci	 */
76662306a36Sopenharmony_ci	exp_diff = vdn->exponent - vdm->exponent;
76762306a36Sopenharmony_ci	m_sig = vfp_shiftright64jamming(vdm->significand, exp_diff);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/*
77062306a36Sopenharmony_ci	 * If the signs are different, we are really subtracting.
77162306a36Sopenharmony_ci	 */
77262306a36Sopenharmony_ci	if (vdn->sign ^ vdm->sign) {
77362306a36Sopenharmony_ci		m_sig = vdn->significand - m_sig;
77462306a36Sopenharmony_ci		if ((s64)m_sig < 0) {
77562306a36Sopenharmony_ci			vdd->sign = vfp_sign_negate(vdd->sign);
77662306a36Sopenharmony_ci			m_sig = -m_sig;
77762306a36Sopenharmony_ci		} else if (m_sig == 0) {
77862306a36Sopenharmony_ci			vdd->sign = (fpscr & FPSCR_RMODE_MASK) ==
77962306a36Sopenharmony_ci				      FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
78062306a36Sopenharmony_ci		}
78162306a36Sopenharmony_ci	} else {
78262306a36Sopenharmony_ci		m_sig += vdn->significand;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci	vdd->significand = m_sig;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	return 0;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic u32
79062306a36Sopenharmony_civfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn,
79162306a36Sopenharmony_ci		    struct vfp_double *vdm, u32 fpscr)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	vfp_double_dump("VDN", vdn);
79462306a36Sopenharmony_ci	vfp_double_dump("VDM", vdm);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/*
79762306a36Sopenharmony_ci	 * Ensure that 'n' is the largest magnitude number.  Note that
79862306a36Sopenharmony_ci	 * if 'n' and 'm' have equal exponents, we do not swap them.
79962306a36Sopenharmony_ci	 * This ensures that NaN propagation works correctly.
80062306a36Sopenharmony_ci	 */
80162306a36Sopenharmony_ci	if (vdn->exponent < vdm->exponent) {
80262306a36Sopenharmony_ci		struct vfp_double *t = vdn;
80362306a36Sopenharmony_ci		vdn = vdm;
80462306a36Sopenharmony_ci		vdm = t;
80562306a36Sopenharmony_ci		pr_debug("VFP: swapping M <-> N\n");
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	vdd->sign = vdn->sign ^ vdm->sign;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/*
81162306a36Sopenharmony_ci	 * If 'n' is an infinity or NaN, handle it.  'm' may be anything.
81262306a36Sopenharmony_ci	 */
81362306a36Sopenharmony_ci	if (vdn->exponent == 2047) {
81462306a36Sopenharmony_ci		if (vdn->significand || (vdm->exponent == 2047 && vdm->significand))
81562306a36Sopenharmony_ci			return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
81662306a36Sopenharmony_ci		if ((vdm->exponent | vdm->significand) == 0) {
81762306a36Sopenharmony_ci			*vdd = vfp_double_default_qnan;
81862306a36Sopenharmony_ci			return FPSCR_IOC;
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci		vdd->exponent = vdn->exponent;
82162306a36Sopenharmony_ci		vdd->significand = 0;
82262306a36Sopenharmony_ci		return 0;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/*
82662306a36Sopenharmony_ci	 * If 'm' is zero, the result is always zero.  In this case,
82762306a36Sopenharmony_ci	 * 'n' may be zero or a number, but it doesn't matter which.
82862306a36Sopenharmony_ci	 */
82962306a36Sopenharmony_ci	if ((vdm->exponent | vdm->significand) == 0) {
83062306a36Sopenharmony_ci		vdd->exponent = 0;
83162306a36Sopenharmony_ci		vdd->significand = 0;
83262306a36Sopenharmony_ci		return 0;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/*
83662306a36Sopenharmony_ci	 * We add 2 to the destination exponent for the same reason
83762306a36Sopenharmony_ci	 * as the addition case - though this time we have +1 from
83862306a36Sopenharmony_ci	 * each input operand.
83962306a36Sopenharmony_ci	 */
84062306a36Sopenharmony_ci	vdd->exponent = vdn->exponent + vdm->exponent - 1023 + 2;
84162306a36Sopenharmony_ci	vdd->significand = vfp_hi64multiply64(vdn->significand, vdm->significand);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	vfp_double_dump("VDD", vdd);
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci#define NEG_MULTIPLY	(1 << 0)
84862306a36Sopenharmony_ci#define NEG_SUBTRACT	(1 << 1)
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic u32
85162306a36Sopenharmony_civfp_double_multiply_accumulate(int dd, int dn, int dm, u32 fpscr, u32 negate, char *func)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct vfp_double vdd, vdp, vdn, vdm;
85462306a36Sopenharmony_ci	u32 exceptions;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	vfp_double_unpack(&vdn, vfp_get_double(dn));
85762306a36Sopenharmony_ci	if (vdn.exponent == 0 && vdn.significand)
85862306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdn);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
86162306a36Sopenharmony_ci	if (vdm.exponent == 0 && vdm.significand)
86262306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdm);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
86562306a36Sopenharmony_ci	if (negate & NEG_MULTIPLY)
86662306a36Sopenharmony_ci		vdp.sign = vfp_sign_negate(vdp.sign);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	vfp_double_unpack(&vdn, vfp_get_double(dd));
86962306a36Sopenharmony_ci	if (vdn.exponent == 0 && vdn.significand)
87062306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdn);
87162306a36Sopenharmony_ci	if (negate & NEG_SUBTRACT)
87262306a36Sopenharmony_ci		vdn.sign = vfp_sign_negate(vdn.sign);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, func);
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci/*
88062306a36Sopenharmony_ci * Standard operations
88162306a36Sopenharmony_ci */
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci/*
88462306a36Sopenharmony_ci * sd = sd + (sn * sm)
88562306a36Sopenharmony_ci */
88662306a36Sopenharmony_cistatic u32 vfp_double_fmac(int dd, int dn, int dm, u32 fpscr)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, 0, "fmac");
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci/*
89262306a36Sopenharmony_ci * sd = sd - (sn * sm)
89362306a36Sopenharmony_ci */
89462306a36Sopenharmony_cistatic u32 vfp_double_fnmac(int dd, int dn, int dm, u32 fpscr)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac");
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/*
90062306a36Sopenharmony_ci * sd = -sd + (sn * sm)
90162306a36Sopenharmony_ci */
90262306a36Sopenharmony_cistatic u32 vfp_double_fmsc(int dd, int dn, int dm, u32 fpscr)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc");
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci/*
90862306a36Sopenharmony_ci * sd = -sd - (sn * sm)
90962306a36Sopenharmony_ci */
91062306a36Sopenharmony_cistatic u32 vfp_double_fnmsc(int dd, int dn, int dm, u32 fpscr)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci/*
91662306a36Sopenharmony_ci * sd = sn * sm
91762306a36Sopenharmony_ci */
91862306a36Sopenharmony_cistatic u32 vfp_double_fmul(int dd, int dn, int dm, u32 fpscr)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	struct vfp_double vdd, vdn, vdm;
92162306a36Sopenharmony_ci	u32 exceptions;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	vfp_double_unpack(&vdn, vfp_get_double(dn));
92462306a36Sopenharmony_ci	if (vdn.exponent == 0 && vdn.significand)
92562306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdn);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
92862306a36Sopenharmony_ci	if (vdm.exponent == 0 && vdm.significand)
92962306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdm);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
93262306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fmul");
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci/*
93662306a36Sopenharmony_ci * sd = -(sn * sm)
93762306a36Sopenharmony_ci */
93862306a36Sopenharmony_cistatic u32 vfp_double_fnmul(int dd, int dn, int dm, u32 fpscr)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	struct vfp_double vdd, vdn, vdm;
94162306a36Sopenharmony_ci	u32 exceptions;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	vfp_double_unpack(&vdn, vfp_get_double(dn));
94462306a36Sopenharmony_ci	if (vdn.exponent == 0 && vdn.significand)
94562306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdn);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
94862306a36Sopenharmony_ci	if (vdm.exponent == 0 && vdm.significand)
94962306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdm);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
95262306a36Sopenharmony_ci	vdd.sign = vfp_sign_negate(vdd.sign);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fnmul");
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci/*
95862306a36Sopenharmony_ci * sd = sn + sm
95962306a36Sopenharmony_ci */
96062306a36Sopenharmony_cistatic u32 vfp_double_fadd(int dd, int dn, int dm, u32 fpscr)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct vfp_double vdd, vdn, vdm;
96362306a36Sopenharmony_ci	u32 exceptions;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	vfp_double_unpack(&vdn, vfp_get_double(dn));
96662306a36Sopenharmony_ci	if (vdn.exponent == 0 && vdn.significand)
96762306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdn);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
97062306a36Sopenharmony_ci	if (vdm.exponent == 0 && vdm.significand)
97162306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdm);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fadd");
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci/*
97962306a36Sopenharmony_ci * sd = sn - sm
98062306a36Sopenharmony_ci */
98162306a36Sopenharmony_cistatic u32 vfp_double_fsub(int dd, int dn, int dm, u32 fpscr)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	struct vfp_double vdd, vdn, vdm;
98462306a36Sopenharmony_ci	u32 exceptions;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	vfp_double_unpack(&vdn, vfp_get_double(dn));
98762306a36Sopenharmony_ci	if (vdn.exponent == 0 && vdn.significand)
98862306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdn);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
99162306a36Sopenharmony_ci	if (vdm.exponent == 0 && vdm.significand)
99262306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdm);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	/*
99562306a36Sopenharmony_ci	 * Subtraction is like addition, but with a negated operand.
99662306a36Sopenharmony_ci	 */
99762306a36Sopenharmony_ci	vdm.sign = vfp_sign_negate(vdm.sign);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fsub");
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci/*
100562306a36Sopenharmony_ci * sd = sn / sm
100662306a36Sopenharmony_ci */
100762306a36Sopenharmony_cistatic u32 vfp_double_fdiv(int dd, int dn, int dm, u32 fpscr)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct vfp_double vdd, vdn, vdm;
101062306a36Sopenharmony_ci	u32 exceptions = 0;
101162306a36Sopenharmony_ci	int tm, tn;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	vfp_double_unpack(&vdn, vfp_get_double(dn));
101462306a36Sopenharmony_ci	vfp_double_unpack(&vdm, vfp_get_double(dm));
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	vdd.sign = vdn.sign ^ vdm.sign;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	tn = vfp_double_type(&vdn);
101962306a36Sopenharmony_ci	tm = vfp_double_type(&vdm);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/*
102262306a36Sopenharmony_ci	 * Is n a NAN?
102362306a36Sopenharmony_ci	 */
102462306a36Sopenharmony_ci	if (tn & VFP_NAN)
102562306a36Sopenharmony_ci		goto vdn_nan;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	/*
102862306a36Sopenharmony_ci	 * Is m a NAN?
102962306a36Sopenharmony_ci	 */
103062306a36Sopenharmony_ci	if (tm & VFP_NAN)
103162306a36Sopenharmony_ci		goto vdm_nan;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/*
103462306a36Sopenharmony_ci	 * If n and m are infinity, the result is invalid
103562306a36Sopenharmony_ci	 * If n and m are zero, the result is invalid
103662306a36Sopenharmony_ci	 */
103762306a36Sopenharmony_ci	if (tm & tn & (VFP_INFINITY|VFP_ZERO))
103862306a36Sopenharmony_ci		goto invalid;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/*
104162306a36Sopenharmony_ci	 * If n is infinity, the result is infinity
104262306a36Sopenharmony_ci	 */
104362306a36Sopenharmony_ci	if (tn & VFP_INFINITY)
104462306a36Sopenharmony_ci		goto infinity;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	/*
104762306a36Sopenharmony_ci	 * If m is zero, raise div0 exceptions
104862306a36Sopenharmony_ci	 */
104962306a36Sopenharmony_ci	if (tm & VFP_ZERO)
105062306a36Sopenharmony_ci		goto divzero;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/*
105362306a36Sopenharmony_ci	 * If m is infinity, or n is zero, the result is zero
105462306a36Sopenharmony_ci	 */
105562306a36Sopenharmony_ci	if (tm & VFP_INFINITY || tn & VFP_ZERO)
105662306a36Sopenharmony_ci		goto zero;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (tn & VFP_DENORMAL)
105962306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdn);
106062306a36Sopenharmony_ci	if (tm & VFP_DENORMAL)
106162306a36Sopenharmony_ci		vfp_double_normalise_denormal(&vdm);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/*
106462306a36Sopenharmony_ci	 * Ok, we have two numbers, we can perform division.
106562306a36Sopenharmony_ci	 */
106662306a36Sopenharmony_ci	vdd.exponent = vdn.exponent - vdm.exponent + 1023 - 1;
106762306a36Sopenharmony_ci	vdm.significand <<= 1;
106862306a36Sopenharmony_ci	if (vdm.significand <= (2 * vdn.significand)) {
106962306a36Sopenharmony_ci		vdn.significand >>= 1;
107062306a36Sopenharmony_ci		vdd.exponent++;
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci	vdd.significand = vfp_estimate_div128to64(vdn.significand, 0, vdm.significand);
107362306a36Sopenharmony_ci	if ((vdd.significand & 0x1ff) <= 2) {
107462306a36Sopenharmony_ci		u64 termh, terml, remh, reml;
107562306a36Sopenharmony_ci		mul64to128(&termh, &terml, vdm.significand, vdd.significand);
107662306a36Sopenharmony_ci		sub128(&remh, &reml, vdn.significand, 0, termh, terml);
107762306a36Sopenharmony_ci		while ((s64)remh < 0) {
107862306a36Sopenharmony_ci			vdd.significand -= 1;
107962306a36Sopenharmony_ci			add128(&remh, &reml, remh, reml, 0, vdm.significand);
108062306a36Sopenharmony_ci		}
108162306a36Sopenharmony_ci		vdd.significand |= (reml != 0);
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci	return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fdiv");
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci vdn_nan:
108662306a36Sopenharmony_ci	exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
108762306a36Sopenharmony_ci pack:
108862306a36Sopenharmony_ci	vfp_put_double(vfp_double_pack(&vdd), dd);
108962306a36Sopenharmony_ci	return exceptions;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci vdm_nan:
109262306a36Sopenharmony_ci	exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
109362306a36Sopenharmony_ci	goto pack;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci zero:
109662306a36Sopenharmony_ci	vdd.exponent = 0;
109762306a36Sopenharmony_ci	vdd.significand = 0;
109862306a36Sopenharmony_ci	goto pack;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci divzero:
110162306a36Sopenharmony_ci	exceptions = FPSCR_DZC;
110262306a36Sopenharmony_ci infinity:
110362306a36Sopenharmony_ci	vdd.exponent = 2047;
110462306a36Sopenharmony_ci	vdd.significand = 0;
110562306a36Sopenharmony_ci	goto pack;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci invalid:
110862306a36Sopenharmony_ci	vfp_put_double(vfp_double_pack(&vfp_double_default_qnan), dd);
110962306a36Sopenharmony_ci	return FPSCR_IOC;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic struct op fops[16] = {
111362306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FMAC)]	= { vfp_double_fmac,  0 },
111462306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FNMAC)]	= { vfp_double_fnmac, 0 },
111562306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FMSC)]	= { vfp_double_fmsc,  0 },
111662306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FNMSC)]	= { vfp_double_fnmsc, 0 },
111762306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FMUL)]	= { vfp_double_fmul,  0 },
111862306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FNMUL)]	= { vfp_double_fnmul, 0 },
111962306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FADD)]	= { vfp_double_fadd,  0 },
112062306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FSUB)]	= { vfp_double_fsub,  0 },
112162306a36Sopenharmony_ci	[FOP_TO_IDX(FOP_FDIV)]	= { vfp_double_fdiv,  0 },
112262306a36Sopenharmony_ci};
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci#define FREG_BANK(x)	((x) & 0x0c)
112562306a36Sopenharmony_ci#define FREG_IDX(x)	((x) & 3)
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ciu32 vfp_double_cpdo(u32 inst, u32 fpscr)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	u32 op = inst & FOP_MASK;
113062306a36Sopenharmony_ci	u32 exceptions = 0;
113162306a36Sopenharmony_ci	unsigned int dest;
113262306a36Sopenharmony_ci	unsigned int dn = vfp_get_dn(inst);
113362306a36Sopenharmony_ci	unsigned int dm;
113462306a36Sopenharmony_ci	unsigned int vecitr, veclen, vecstride;
113562306a36Sopenharmony_ci	struct op *fop;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK));
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	/*
114262306a36Sopenharmony_ci	 * fcvtds takes an sN register number as destination, not dN.
114362306a36Sopenharmony_ci	 * It also always operates on scalars.
114462306a36Sopenharmony_ci	 */
114562306a36Sopenharmony_ci	if (fop->flags & OP_SD)
114662306a36Sopenharmony_ci		dest = vfp_get_sd(inst);
114762306a36Sopenharmony_ci	else
114862306a36Sopenharmony_ci		dest = vfp_get_dd(inst);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/*
115162306a36Sopenharmony_ci	 * f[us]ito takes a sN operand, not a dN operand.
115262306a36Sopenharmony_ci	 */
115362306a36Sopenharmony_ci	if (fop->flags & OP_SM)
115462306a36Sopenharmony_ci		dm = vfp_get_sm(inst);
115562306a36Sopenharmony_ci	else
115662306a36Sopenharmony_ci		dm = vfp_get_dm(inst);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/*
115962306a36Sopenharmony_ci	 * If destination bank is zero, vector length is always '1'.
116062306a36Sopenharmony_ci	 * ARM DDI0100F C5.1.3, C5.3.2.
116162306a36Sopenharmony_ci	 */
116262306a36Sopenharmony_ci	if ((fop->flags & OP_SCALAR) || (FREG_BANK(dest) == 0))
116362306a36Sopenharmony_ci		veclen = 0;
116462306a36Sopenharmony_ci	else
116562306a36Sopenharmony_ci		veclen = fpscr & FPSCR_LENGTH_MASK;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
116862306a36Sopenharmony_ci		 (veclen >> FPSCR_LENGTH_BIT) + 1);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	if (!fop->fn)
117162306a36Sopenharmony_ci		goto invalid;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
117462306a36Sopenharmony_ci		u32 except;
117562306a36Sopenharmony_ci		char type;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		type = fop->flags & OP_SD ? 's' : 'd';
117862306a36Sopenharmony_ci		if (op == FOP_EXT)
117962306a36Sopenharmony_ci			pr_debug("VFP: itr%d (%c%u) = op[%u] (d%u)\n",
118062306a36Sopenharmony_ci				 vecitr >> FPSCR_LENGTH_BIT,
118162306a36Sopenharmony_ci				 type, dest, dn, dm);
118262306a36Sopenharmony_ci		else
118362306a36Sopenharmony_ci			pr_debug("VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n",
118462306a36Sopenharmony_ci				 vecitr >> FPSCR_LENGTH_BIT,
118562306a36Sopenharmony_ci				 type, dest, dn, FOP_TO_IDX(op), dm);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		except = fop->fn(dest, dn, dm, fpscr);
118862306a36Sopenharmony_ci		pr_debug("VFP: itr%d: exceptions=%08x\n",
118962306a36Sopenharmony_ci			 vecitr >> FPSCR_LENGTH_BIT, except);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		exceptions |= except;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		/*
119462306a36Sopenharmony_ci		 * CHECK: It appears to be undefined whether we stop when
119562306a36Sopenharmony_ci		 * we encounter an exception.  We continue.
119662306a36Sopenharmony_ci		 */
119762306a36Sopenharmony_ci		dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 3);
119862306a36Sopenharmony_ci		dn = FREG_BANK(dn) + ((FREG_IDX(dn) + vecstride) & 3);
119962306a36Sopenharmony_ci		if (FREG_BANK(dm) != 0)
120062306a36Sopenharmony_ci			dm = FREG_BANK(dm) + ((FREG_IDX(dm) + vecstride) & 3);
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci	return exceptions;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci invalid:
120562306a36Sopenharmony_ci	return ~0;
120662306a36Sopenharmony_ci}
1207