xref: /openbmc/u-boot/arch/powerpc/include/asm/bitops.h (revision b994efbd)
1 /*
2  * bitops.h: Bit string operations on the ppc
3  */
4 
5 #ifndef _PPC_BITOPS_H
6 #define _PPC_BITOPS_H
7 
8 #include <asm/byteorder.h>
9 
10 extern void set_bit(int nr, volatile void *addr);
11 extern void clear_bit(int nr, volatile void *addr);
12 extern void change_bit(int nr, volatile void *addr);
13 extern int test_and_set_bit(int nr, volatile void *addr);
14 extern int test_and_clear_bit(int nr, volatile void *addr);
15 extern int test_and_change_bit(int nr, volatile void *addr);
16 
17 /*
18  * Arguably these bit operations don't imply any memory barrier or
19  * SMP ordering, but in fact a lot of drivers expect them to imply
20  * both, since they do on x86 cpus.
21  */
22 #ifdef CONFIG_SMP
23 #define SMP_WMB		"eieio\n"
24 #define SMP_MB		"\nsync"
25 #else
26 #define SMP_WMB
27 #define SMP_MB
28 #endif /* CONFIG_SMP */
29 
30 #define __INLINE_BITOPS	1
31 
32 #if __INLINE_BITOPS
33 /*
34  * These used to be if'd out here because using : "cc" as a constraint
35  * resulted in errors from egcs.  Things may be OK with gcc-2.95.
36  */
37 extern __inline__ void set_bit(int nr, volatile void * addr)
38 {
39 	unsigned long old;
40 	unsigned long mask = 1 << (nr & 0x1f);
41 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
42 
43 	__asm__ __volatile__(SMP_WMB "\
44 1:	lwarx	%0,0,%3\n\
45 	or	%0,%0,%2\n\
46 	stwcx.	%0,0,%3\n\
47 	bne	1b"
48 	SMP_MB
49 	: "=&r" (old), "=m" (*p)
50 	: "r" (mask), "r" (p), "m" (*p)
51 	: "cc" );
52 }
53 
54 extern __inline__ void clear_bit(int nr, volatile void *addr)
55 {
56 	unsigned long old;
57 	unsigned long mask = 1 << (nr & 0x1f);
58 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
59 
60 	__asm__ __volatile__(SMP_WMB "\
61 1:	lwarx	%0,0,%3\n\
62 	andc	%0,%0,%2\n\
63 	stwcx.	%0,0,%3\n\
64 	bne	1b"
65 	SMP_MB
66 	: "=&r" (old), "=m" (*p)
67 	: "r" (mask), "r" (p), "m" (*p)
68 	: "cc");
69 }
70 
71 extern __inline__ void change_bit(int nr, volatile void *addr)
72 {
73 	unsigned long old;
74 	unsigned long mask = 1 << (nr & 0x1f);
75 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
76 
77 	__asm__ __volatile__(SMP_WMB "\
78 1:	lwarx	%0,0,%3\n\
79 	xor	%0,%0,%2\n\
80 	stwcx.	%0,0,%3\n\
81 	bne	1b"
82 	SMP_MB
83 	: "=&r" (old), "=m" (*p)
84 	: "r" (mask), "r" (p), "m" (*p)
85 	: "cc");
86 }
87 
88 extern __inline__ int test_and_set_bit(int nr, volatile void *addr)
89 {
90 	unsigned int old, t;
91 	unsigned int mask = 1 << (nr & 0x1f);
92 	volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5);
93 
94 	__asm__ __volatile__(SMP_WMB "\
95 1:	lwarx	%0,0,%4\n\
96 	or	%1,%0,%3\n\
97 	stwcx.	%1,0,%4\n\
98 	bne	1b"
99 	SMP_MB
100 	: "=&r" (old), "=&r" (t), "=m" (*p)
101 	: "r" (mask), "r" (p), "m" (*p)
102 	: "cc");
103 
104 	return (old & mask) != 0;
105 }
106 
107 extern __inline__ int test_and_clear_bit(int nr, volatile void *addr)
108 {
109 	unsigned int old, t;
110 	unsigned int mask = 1 << (nr & 0x1f);
111 	volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5);
112 
113 	__asm__ __volatile__(SMP_WMB "\
114 1:	lwarx	%0,0,%4\n\
115 	andc	%1,%0,%3\n\
116 	stwcx.	%1,0,%4\n\
117 	bne	1b"
118 	SMP_MB
119 	: "=&r" (old), "=&r" (t), "=m" (*p)
120 	: "r" (mask), "r" (p), "m" (*p)
121 	: "cc");
122 
123 	return (old & mask) != 0;
124 }
125 
126 extern __inline__ int test_and_change_bit(int nr, volatile void *addr)
127 {
128 	unsigned int old, t;
129 	unsigned int mask = 1 << (nr & 0x1f);
130 	volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5);
131 
132 	__asm__ __volatile__(SMP_WMB "\
133 1:	lwarx	%0,0,%4\n\
134 	xor	%1,%0,%3\n\
135 	stwcx.	%1,0,%4\n\
136 	bne	1b"
137 	SMP_MB
138 	: "=&r" (old), "=&r" (t), "=m" (*p)
139 	: "r" (mask), "r" (p), "m" (*p)
140 	: "cc");
141 
142 	return (old & mask) != 0;
143 }
144 #endif /* __INLINE_BITOPS */
145 
146 extern __inline__ int test_bit(int nr, __const__ volatile void *addr)
147 {
148 	__const__ unsigned int *p = (__const__ unsigned int *) addr;
149 
150 	return ((p[nr >> 5] >> (nr & 0x1f)) & 1) != 0;
151 }
152 
153 /* Return the bit position of the most significant 1 bit in a word */
154 /* - the result is undefined when x == 0 */
155 extern __inline__ int __ilog2(unsigned int x)
156 {
157 	int lz;
158 
159 	asm ("cntlzw %0,%1" : "=r" (lz) : "r" (x));
160 	return 31 - lz;
161 }
162 
163 extern __inline__ int ffz(unsigned int x)
164 {
165 	if ((x = ~x) == 0)
166 		return 32;
167 	return __ilog2(x & -x);
168 }
169 
170 /*
171  * fls: find last (most-significant) bit set.
172  * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
173  *
174  * On powerpc, __ilog2(0) returns -1, but this is not safe in general
175  */
176 static __inline__ int fls(unsigned int x)
177 {
178 	return __ilog2(x) + 1;
179 }
180 #define PLATFORM_FLS
181 
182 /**
183  * fls64 - find last set bit in a 64-bit word
184  * @x: the word to search
185  *
186  * This is defined in a similar way as the libc and compiler builtin
187  * ffsll, but returns the position of the most significant set bit.
188  *
189  * fls64(value) returns 0 if value is 0 or the position of the last
190  * set bit if value is nonzero. The last (most significant) bit is
191  * at position 64.
192  */
193 #if BITS_PER_LONG == 32
194 static inline int fls64(__u64 x)
195 {
196 	__u32 h = x >> 32;
197 	if (h)
198 		return fls(h) + 32;
199 	return fls(x);
200 }
201 #elif BITS_PER_LONG == 64
202 static inline int fls64(__u64 x)
203 {
204 	if (x == 0)
205 		return 0;
206 	return __ilog2(x) + 1;
207 }
208 #else
209 #error BITS_PER_LONG not 32 or 64
210 #endif
211 
212 static inline int __ilog2_u64(u64 n)
213 {
214 	return fls64(n) - 1;
215 }
216 
217 static inline int ffs64(u64 x)
218 {
219 	return __ilog2_u64(x & -x) + 1ull;
220 }
221 
222 #ifdef __KERNEL__
223 
224 /*
225  * ffs: find first bit set. This is defined the same way as
226  * the libc and compiler builtin ffs routines, therefore
227  * differs in spirit from the above ffz (man ffs).
228  */
229 extern __inline__ int ffs(int x)
230 {
231 	return __ilog2(x & -x) + 1;
232 }
233 #define PLATFORM_FFS
234 
235 /*
236  * hweightN: returns the hamming weight (i.e. the number
237  * of bits set) of a N-bit word
238  */
239 
240 #define hweight32(x) generic_hweight32(x)
241 #define hweight16(x) generic_hweight16(x)
242 #define hweight8(x) generic_hweight8(x)
243 
244 #endif /* __KERNEL__ */
245 
246 /*
247  * This implementation of find_{first,next}_zero_bit was stolen from
248  * Linus' asm-alpha/bitops.h.
249  */
250 #define find_first_zero_bit(addr, size) \
251 	find_next_zero_bit((addr), (size), 0)
252 
253 extern __inline__ unsigned long find_next_zero_bit(void * addr,
254 	unsigned long size, unsigned long offset)
255 {
256 	unsigned int * p = ((unsigned int *) addr) + (offset >> 5);
257 	unsigned int result = offset & ~31UL;
258 	unsigned int tmp;
259 
260 	if (offset >= size)
261 		return size;
262 	size -= result;
263 	offset &= 31UL;
264 	if (offset) {
265 		tmp = *p++;
266 		tmp |= ~0UL >> (32-offset);
267 		if (size < 32)
268 			goto found_first;
269 		if (tmp != ~0U)
270 			goto found_middle;
271 		size -= 32;
272 		result += 32;
273 	}
274 	while (size >= 32) {
275 		if ((tmp = *p++) != ~0U)
276 			goto found_middle;
277 		result += 32;
278 		size -= 32;
279 	}
280 	if (!size)
281 		return result;
282 	tmp = *p;
283 found_first:
284 	tmp |= ~0UL << size;
285 found_middle:
286 	return result + ffz(tmp);
287 }
288 
289 
290 #define _EXT2_HAVE_ASM_BITOPS_
291 
292 #ifdef __KERNEL__
293 /*
294  * test_and_{set,clear}_bit guarantee atomicity without
295  * disabling interrupts.
296  */
297 #define ext2_set_bit(nr, addr)		test_and_set_bit((nr) ^ 0x18, addr)
298 #define ext2_clear_bit(nr, addr)	test_and_clear_bit((nr) ^ 0x18, addr)
299 
300 #else
301 extern __inline__ int ext2_set_bit(int nr, void * addr)
302 {
303 	int		mask;
304 	unsigned char	*ADDR = (unsigned char *) addr;
305 	int oldbit;
306 
307 	ADDR += nr >> 3;
308 	mask = 1 << (nr & 0x07);
309 	oldbit = (*ADDR & mask) ? 1 : 0;
310 	*ADDR |= mask;
311 	return oldbit;
312 }
313 
314 extern __inline__ int ext2_clear_bit(int nr, void * addr)
315 {
316 	int		mask;
317 	unsigned char	*ADDR = (unsigned char *) addr;
318 	int oldbit;
319 
320 	ADDR += nr >> 3;
321 	mask = 1 << (nr & 0x07);
322 	oldbit = (*ADDR & mask) ? 1 : 0;
323 	*ADDR = *ADDR & ~mask;
324 	return oldbit;
325 }
326 #endif	/* __KERNEL__ */
327 
328 extern __inline__ int ext2_test_bit(int nr, __const__ void * addr)
329 {
330 	__const__ unsigned char	*ADDR = (__const__ unsigned char *) addr;
331 
332 	return (ADDR[nr >> 3] >> (nr & 7)) & 1;
333 }
334 
335 /*
336  * This implementation of ext2_find_{first,next}_zero_bit was stolen from
337  * Linus' asm-alpha/bitops.h and modified for a big-endian machine.
338  */
339 
340 #define ext2_find_first_zero_bit(addr, size) \
341 	ext2_find_next_zero_bit((addr), (size), 0)
342 
343 static __inline__ unsigned long ext2_find_next_zero_bit(void *addr,
344 	unsigned long size, unsigned long offset)
345 {
346 	unsigned int *p = ((unsigned int *) addr) + (offset >> 5);
347 	unsigned int result = offset & ~31UL;
348 	unsigned int tmp;
349 
350 	if (offset >= size)
351 		return size;
352 	size -= result;
353 	offset &= 31UL;
354 	if (offset) {
355 		tmp = cpu_to_le32p(p++);
356 		tmp |= ~0UL >> (32-offset);
357 		if (size < 32)
358 			goto found_first;
359 		if (tmp != ~0U)
360 			goto found_middle;
361 		size -= 32;
362 		result += 32;
363 	}
364 	while (size >= 32) {
365 		if ((tmp = cpu_to_le32p(p++)) != ~0U)
366 			goto found_middle;
367 		result += 32;
368 		size -= 32;
369 	}
370 	if (!size)
371 		return result;
372 	tmp = cpu_to_le32p(p);
373 found_first:
374 	tmp |= ~0U << size;
375 found_middle:
376 	return result + ffz(tmp);
377 }
378 
379 /* Bitmap functions for the minix filesystem.  */
380 #define minix_test_and_set_bit(nr,addr) ext2_set_bit(nr,addr)
381 #define minix_set_bit(nr,addr) ((void)ext2_set_bit(nr,addr))
382 #define minix_test_and_clear_bit(nr,addr) ext2_clear_bit(nr,addr)
383 #define minix_test_bit(nr,addr) ext2_test_bit(nr,addr)
384 #define minix_find_first_zero_bit(addr,size) ext2_find_first_zero_bit(addr,size)
385 
386 #endif /* _PPC_BITOPS_H */
387