summaryrefslogtreecommitdiffstats
path: root/arch/arm/lib/findbit.S
blob: b7ac2d3c0748652c1094a2b648323f971c5042e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* 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)