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