/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright 2010, Google Inc. * * Brought in from coreboot uldivmod.S */ #include <linux/linkage.h> #include <asm/assembler.h> /* * A, Q = r0 + (r1 << 32) * B, R = r2 + (r3 << 32) * A / B = Q ... R */ A_0 .req r0 A_1 .req r1 B_0 .req r2 B_1 .req r3 C_0 .req r4 C_1 .req r5 D_0 .req r6 D_1 .req r7 Q_0 .req r0 Q_1 .req r1 R_0 .req r2 R_1 .req r3 THUMB( TMP .req r8 ) .pushsection .text.__aeabi_uldivmod, "ax" ENTRY(__aeabi_uldivmod) stmfd sp!, {r4, r5, r6, r7, THUMB(TMP,) lr} @ Test if B == 0 orrs ip, B_0, B_1 @ Z set -> B == 0 beq L_div_by_0 @ Test if B is power of 2: (B & (B - 1)) == 0 subs C_0, B_0, #1 sbc C_1, B_1, #0 tst C_0, B_0 tsteq B_1, C_1 beq L_pow2 @ Test if A_1 == B_1 == 0 orrs ip, A_1, B_1 beq L_div_32_32 L_div_64_64: /* CLZ only exists in ARM architecture version 5 and above. */ #ifdef HAVE_CLZ mov C_0, #1 mov C_1, #0 @ D_0 = clz A teq A_1, #0 clz D_0, A_1 clzeq ip, A_0 addeq D_0, D_0, ip @ D_1 = clz B teq B_1, #0 clz D_1, B_1 clzeq ip, B_0 addeq D_1, D_1, ip @ if clz B - clz A > 0 subs D_0, D_1, D_0 bls L_done_shift @ B <<= (clz B - clz A) subs D_1, D_0, #32 rsb ip, D_0, #32 movmi B_1, B_1, lsl D_0 ARM( orrmi B_1, B_1, B_0, lsr ip ) THUMB( lsrmi TMP, B_0, ip ) THUMB( orrmi B_1, B_1, TMP ) movpl B_1, B_0, lsl D_1 mov B_0, B_0, lsl D_0 @ C = 1 << (clz B - clz A) movmi C_1, C_1, lsl D_0 ARM( orrmi C_1, C_1, C_0, lsr ip ) THUMB( lsrmi TMP, C_0, ip ) THUMB( orrmi C_1, C_1, TMP ) movpl C_1, C_0, lsl D_1 mov C_0, C_0, lsl D_0 L_done_shift: mov D_0, #0 mov D_1, #0 @ C: current bit; D: result #else @ C: current bit; D: result mov C_0, #1 mov C_1, #0 mov D_0, #0 mov D_1, #0 L_lsl_4: cmp B_1, #0x10000000 cmpcc B_1, A_1 cmpeq B_0, A_0 bcs L_lsl_1 @ B <<= 4 mov B_1, B_1, lsl #4 orr B_1, B_1, B_0, lsr #28 mov B_0, B_0, lsl #4 @ C <<= 4 mov C_1, C_1, lsl #4 orr C_1, C_1, C_0, lsr #28 mov C_0, C_0, lsl #4 b L_lsl_4 L_lsl_1: cmp B_1, #0x80000000 cmpcc B_1, A_1 cmpeq B_0, A_0 bcs L_subtract @ B <<= 1 mov B_1, B_1, lsl #1 orr B_1, B_1, B_0, lsr #31 mov B_0, B_0, lsl #1 @ C <<= 1 mov C_1, C_1, lsl #1 orr C_1, C_1, C_0, lsr #31 mov C_0, C_0, lsl #1 b L_lsl_1 #endif L_subtract: @ if A >= B cmp A_1, B_1 cmpeq A_0, B_0 bcc L_update @ A -= B subs A_0, A_0, B_0 sbc A_1, A_1, B_1 @ D |= C orr D_0, D_0, C_0 orr D_1, D_1, C_1 L_update: @ if A == 0: break orrs ip, A_1, A_0 beq L_exit @ C >>= 1 movs C_1, C_1, lsr #1 movs C_0, C_0, rrx @ if C == 0: break orrs ip, C_1, C_0 beq L_exit @ B >>= 1 movs B_1, B_1, lsr #1 mov B_0, B_0, rrx b L_subtract L_exit: @ Note: A, B & Q, R are aliases mov R_0, A_0 mov R_1, A_1 mov Q_0, D_0 mov Q_1, D_1 ldmfd sp!, {r4, r5, r6, r7, THUMB(TMP,) pc} L_div_32_32: @ Note: A_0 & r0 are aliases @ Q_1 r1 mov r1, B_0 bl __aeabi_uidivmod mov R_0, r1 mov R_1, #0 mov Q_1, #0 ldmfd sp!, {r4, r5, r6, r7, THUMB(TMP,) pc} L_pow2: #ifdef HAVE_CLZ @ Note: A, B and Q, R are aliases @ R = A & (B - 1) and C_0, A_0, C_0 and C_1, A_1, C_1 @ Q = A >> log2(B) @ Note: B must not be 0 here! clz D_0, B_0 add D_1, D_0, #1 rsbs D_0, D_0, #31 bpl L_1 clz D_0, B_1 rsb D_0, D_0, #31 mov A_0, A_1, lsr D_0 add D_0, D_0, #32 L_1: movpl A_0, A_0, lsr D_0 ARM( orrpl A_0, A_0, A_1, lsl D_1 ) THUMB( lslpl TMP, A_1, D_1 ) THUMB( orrpl A_0, A_0, TMP ) mov A_1, A_1, lsr D_0 @ Mov back C to R mov R_0, C_0 mov R_1, C_1 ldmfd sp!, {r4, r5, r6, r7, THUMB(TMP,) pc} #else @ Note: A, B and Q, R are aliases @ R = A & (B - 1) and C_0, A_0, C_0 and C_1, A_1, C_1 @ Q = A >> log2(B) @ Note: B must not be 0 here! @ Count the leading zeroes in B. mov D_0, #0 orrs B_0, B_0, B_0 @ If B is greater than 1 << 31, divide A and B by 1 << 32. moveq A_0, A_1 moveq A_1, #0 moveq B_0, B_1 @ Count the remaining leading zeroes in B. movs B_1, B_0, lsl #16 addeq D_0, #16 moveq B_0, B_0, lsr #16 tst B_0, #0xff addeq D_0, #8 moveq B_0, B_0, lsr #8 tst B_0, #0xf addeq D_0, #4 moveq B_0, B_0, lsr #4 tst B_0, #0x3 addeq D_0, #2 moveq B_0, B_0, lsr #2 tst B_0, #0x1 addeq D_0, #1 @ Shift A to the right by the appropriate amount. rsb D_1, D_0, #32 mov Q_0, A_0, lsr D_0 ARM( orr Q_0, Q_0, A_1, lsl D_1 ) THUMB( lsl A_1, D_1 ) THUMB( orr Q_0, A_1 ) mov Q_1, A_1, lsr D_0 @ Move C to R mov R_0, C_0 mov R_1, C_1 ldmfd sp!, {r4, r5, r6, r7, THUMB(TMP,) pc} #endif L_div_by_0: bl __div0 @ As wrong as it could be mov Q_0, #0 mov Q_1, #0 mov R_0, #0 mov R_1, #0 ldmfd sp!, {r4, r5, r6, r7, THUMB(TMP,) pc} ENDPROC(__aeabi_uldivmod) .popsection