root/arch/arm/lib/findbit.S
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/findbit.S
 *
 *  Copyright (C) 1995-2000 Russell King
 *
 * 16th March 2001 - John Ripley <jripley@sonicblue.com>
 *   Fixed so that "size" is an exclusive not an inclusive quantity.
 *   All users of these functions expect exclusive sizes, and may
 *   also call with zero size.
 * Reworked by rmk.
 */
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/unwind.h>
                .text

#ifdef __ARMEB__
#define SWAB_ENDIAN le
#else
#define SWAB_ENDIAN be
#endif

                .macro  find_first, endian, set, name
ENTRY(_find_first_\name\()bit_\endian)
        UNWIND( .fnstart)
                teq     r1, #0
                beq     3f
                mov     r2, #0
1:              ldr     r3, [r0], #4
                .ifeq \set
                mvns    r3, r3                  @ invert/test bits
                .else
                movs    r3, r3                  @ test bits
                .endif
                .ifc \endian, SWAB_ENDIAN
                bne     .L_found_swab
                .else
                bne     .L_found                @ found the bit?
                .endif
                add     r2, r2, #32             @ next index
2:              cmp     r2, r1                  @ any more?
                blo     1b
3:              mov     r0, r1                  @ no more bits
                ret     lr
        UNWIND( .fnend)
ENDPROC(_find_first_\name\()bit_\endian)
                .endm

                .macro  find_next, endian, set, name
ENTRY(_find_next_\name\()bit_\endian)
        UNWIND( .fnstart)
                cmp     r2, r1
                bhs     3b
                mov     ip, r2, lsr #5          @ word index
                add     r0, r0, ip, lsl #2
                ands    ip, r2, #31             @ bit position
                beq     1b
                ldr     r3, [r0], #4
                .ifeq \set
                mvn     r3, r3                  @ invert bits
                .endif
                .ifc \endian, SWAB_ENDIAN
                rev_l   r3, ip
                .if     .Lrev_l_uses_tmp
                @ we need to recompute ip because rev_l will have overwritten
                @ it.
                and     ip, r2, #31             @ bit position
                .endif
                .endif
                movs    r3, r3, lsr ip          @ shift off unused bits
                bne     .L_found
                orr     r2, r2, #31             @ no zero bits
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
        UNWIND( .fnend)
ENDPROC(_find_next_\name\()bit_\endian)
                .endm

                .macro  find_bit, endian, set, name
                find_first \endian, \set, \name
                find_next  \endian, \set, \name
                .endm

/* _find_first_zero_bit_le and _find_next_zero_bit_le */
                find_bit le, 0, zero_

/* _find_first_bit_le and _find_next_bit_le */
                find_bit le, 1

#ifdef __ARMEB__

/* _find_first_zero_bit_be and _find_next_zero_bit_be */
                find_bit be, 0, zero_

/* _find_first_bit_be and _find_next_bit_be */
                find_bit be, 1

#endif

/*
 * One or more bits in the LSB of r3 are assumed to be set.
 */
.L_found_swab:
        UNWIND( .fnstart)
                rev_l   r3, ip
.L_found:
#if __LINUX_ARM_ARCH__ >= 7
                rbit    r3, r3                  @ reverse bits
                clz     r3, r3                  @ count high zero bits
                add     r0, r2, r3              @ add offset of first set bit
#elif __LINUX_ARM_ARCH__ >= 5
                rsb     r0, r3, #0
                and     r3, r3, r0              @ mask out lowest bit set
                clz     r3, r3                  @ count high zero bits
                rsb     r3, r3, #31             @ offset of first set bit
                add     r0, r2, r3              @ add offset of first set bit
#else
                mov     ip, #~0
                tst     r3, ip, lsr #16         @ test bits 0-15
                addeq   r2, r2, #16
                moveq   r3, r3, lsr #16
                tst     r3, #0x00ff
                addeq   r2, r2, #8
                moveq   r3, r3, lsr #8
                tst     r3, #0x000f
                addeq   r2, r2, #4
                moveq   r3, r3, lsr #4
                tst     r3, #0x0003
                addeq   r2, r2, #2
                moveq   r3, r3, lsr #2
                tst     r3, #0x0001
                addeq   r2, r2, #1
                mov     r0, r2
#endif
                cmp     r1, r0                  @ Clamp to maxbit
                movlo   r0, r1
                ret     lr
        UNWIND( .fnend)