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