patch-2.4.19 linux-2.4.19/include/asm-ppc64/bitops.h
Next file: linux-2.4.19/include/asm-ppc64/bootinfo.h
Previous file: linux-2.4.19/include/asm-ppc64/atomic.h
Back to the patch index
Back to the overall index
- Lines: 432
- Date:
Fri Aug 2 17:39:45 2002
- Orig file:
linux-2.4.18/include/asm-ppc64/bitops.h
- Orig date:
Wed Dec 31 16:00:00 1969
diff -urN linux-2.4.18/include/asm-ppc64/bitops.h linux-2.4.19/include/asm-ppc64/bitops.h
@@ -0,0 +1,431 @@
+/*
+ * PowerPC64 atomic bit operations.
+ * Dave Engebretsen, Todd Inglett, Don Reed, Pat McCarthy, Peter Bergner,
+ * Anton Blanchard
+ *
+ * Originally taken from the 32b PPC code. Modified to use 64b values for
+ * the various counters & memory references.
+ *
+ * Bitops are odd when viewed on big-endian systems. They were designed
+ * on little endian so the size of the bitset doesn't matter (low order bytes
+ * come first) as long as the bit in question is valid.
+ *
+ * Bits are "tested" often using the C expression (val & (1<<nr)) so we do
+ * our best to stay compatible with that. The assumption is that val will
+ * be unsigned long for such tests. As such, we assume the bits are stored
+ * as an array of unsigned long (the usual case is a single unsigned long,
+ * of course). Here's an example bitset with bit numbering:
+ *
+ * |63..........0|127........64|195.......128|255.......196|
+ *
+ * This leads to a problem. If an int, short or char is passed as a bitset
+ * it will be a bad memory reference since we want to store in chunks
+ * of unsigned long (64 bits here) size.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _PPC64_BITOPS_H
+#define _PPC64_BITOPS_H
+
+#ifdef __KERNEL__
+
+#include <asm/byteorder.h>
+#include <asm/memory.h>
+
+/*
+ * clear_bit doesn't imply a memory barrier
+ */
+#define smp_mb__before_clear_bit() smp_mb()
+#define smp_mb__after_clear_bit() smp_mb()
+
+static __inline__ int test_bit(unsigned long nr, __const__ volatile void *addr)
+{
+ return (1UL & (((__const__ long *) addr)[nr >> 6] >> (nr & 63)));
+}
+
+static __inline__ void set_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long old;
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ __asm__ __volatile__(
+"1: ldarx %0,0,%3 # set_bit\n\
+ or %0,%0,%2\n\
+ stdcx. %0,0,%3\n\
+ bne- 1b"
+ : "=&r" (old), "=m" (*p)
+ : "r" (mask), "r" (p), "m" (*p)
+ : "cc");
+}
+
+static __inline__ void clear_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long old;
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ __asm__ __volatile__(
+"1: ldarx %0,0,%3 # clear_bit\n\
+ andc %0,%0,%2\n\
+ stdcx. %0,0,%3\n\
+ bne- 1b"
+ : "=&r" (old), "=m" (*p)
+ : "r" (mask), "r" (p), "m" (*p)
+ : "cc");
+}
+
+static __inline__ void change_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long old;
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ __asm__ __volatile__(
+"1: ldarx %0,0,%3 # change_bit\n\
+ xor %0,%0,%2\n\
+ stdcx. %0,0,%3\n\
+ bne- 1b"
+ : "=&r" (old), "=m" (*p)
+ : "r" (mask), "r" (p), "m" (*p)
+ : "cc");
+}
+
+static __inline__ int test_and_set_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long old, t;
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ __asm__ __volatile__(
+ EIEIO_ON_SMP
+"1: ldarx %0,0,%3 # test_and_set_bit\n\
+ or %1,%0,%2 \n\
+ stdcx. %1,0,%3 \n\
+ bne- 1b"
+ ISYNC_ON_SMP
+ : "=&r" (old), "=&r" (t)
+ : "r" (mask), "r" (p)
+ : "cc", "memory");
+
+ return (old & mask) != 0;
+}
+
+static __inline__ int test_and_clear_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long old, t;
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ __asm__ __volatile__(
+ EIEIO_ON_SMP
+"1: ldarx %0,0,%3 # test_and_clear_bit\n\
+ andc %1,%0,%2\n\
+ stdcx. %1,0,%3\n\
+ bne- 1b"
+ ISYNC_ON_SMP
+ : "=&r" (old), "=&r" (t)
+ : "r" (mask), "r" (p)
+ : "cc", "memory");
+
+ return (old & mask) != 0;
+}
+
+static __inline__ int test_and_change_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long old, t;
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ __asm__ __volatile__(
+ EIEIO_ON_SMP
+"1: ldarx %0,0,%3 # test_and_change_bit\n\
+ xor %1,%0,%2\n\
+ stdcx. %1,0,%3\n\
+ bne- 1b"
+ ISYNC_ON_SMP
+ : "=&r" (old), "=&r" (t)
+ : "r" (mask), "r" (p)
+ : "cc", "memory");
+
+ return (old & mask) != 0;
+}
+
+/*
+ * non-atomic versions
+ */
+static __inline__ void __set_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ *p |= mask;
+}
+
+static __inline__ void __clear_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ *p &= ~mask;
+}
+
+static __inline__ void __change_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+
+ *p ^= mask;
+}
+
+static __inline__ int __test_and_set_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+ unsigned long old = *p;
+
+ *p = old | mask;
+ return (old & mask) != 0;
+}
+
+static __inline__ int __test_and_clear_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+ unsigned long old = *p;
+
+ *p = old & ~mask;
+ return (old & mask) != 0;
+}
+
+static __inline__ int __test_and_change_bit(unsigned long nr, volatile void *addr)
+{
+ unsigned long mask = 1UL << (nr & 0x3f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 6);
+ unsigned long old = *p;
+
+ *p = old ^ mask;
+ return (old & mask) != 0;
+}
+
+/*
+ * Return the zero-based bit position (from RIGHT TO LEFT, 63 -> 0) of the
+ * most significant (left-most) 1-bit in a double word.
+ */
+static __inline__ int __ilog2(unsigned long x)
+{
+ int lz;
+
+ asm ("cntlzd %0,%1" : "=r" (lz) : "r" (x));
+ return 63 - lz;
+}
+
+/* Return the zero-based bit position
+ * from RIGHT TO LEFT 63 --> 0
+ * of the most significant (left-most) 1-bit in an 8-byte area.
+ */
+static __inline__ long cnt_trailing_zeros(unsigned long mask)
+{
+ long cnt;
+
+ asm(
+" addi %0,%1,-1 \n\
+ andc %0,%0,%1 \n\
+ cntlzd %0,%0 \n\
+ subfic %0,%0,64"
+ : "=r" (cnt)
+ : "r" (mask));
+ return cnt;
+}
+
+
+
+/*
+ * ffz = Find First Zero in word. Undefined if no zero exists,
+ * Determines the bit position of the LEAST significant
+ * (rightmost) 0 bit in the specified DOUBLE-WORD.
+ * The returned bit position will be zero-based, starting
+ * from the right side (63 - 0).
+ * the code should check against ~0UL first..
+ */
+static __inline__ unsigned long ffz(unsigned long x)
+{
+ u32 tempRC;
+
+ /* Change all of x's 1s to 0s and 0s to 1s in x.
+ * And insure at least 1 zero exists in the 8 byte area.
+ */
+ if ((x = ~x) == 0)
+ /* no zero exists anywhere in the 8 byte area. */
+ return 64;
+
+ /* Calculate the bit position of the least significant '1' bit in x
+ * (since x has been changed this will actually be the least
+ * significant '0' bit in the original x).
+ * Note: (x & -x) gives us a mask that is the LEAST significant
+ * (RIGHT-most) 1-bit of the value in x.
+ */
+ tempRC = __ilog2(x & -x);
+
+ return tempRC;
+}
+
+/*
+ * ffs: find first bit set. This is defined the same way as
+ * the libc and compiler builtin ffs routines, therefore
+ * differs in spirit from the above ffz (man ffs).
+ */
+static __inline__ int ffs(int x)
+{
+ int result = ffz(~x);
+ return x ? result+1 : 0;
+}
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
+extern unsigned long find_next_zero_bit(void * addr, unsigned long size,
+ unsigned long offset);
+/*
+ * The optimizer actually does good code for this case..
+ */
+#define find_first_zero_bit(addr, size) find_next_zero_bit((addr), (size), 0)
+
+/* Bitmap functions for the ext2 filesystem. */
+#define _EXT2_HAVE_ASM_BITOPS_
+
+static __inline__ int ext2_set_bit(int nr, void* addr)
+{
+ /* This method needs to take into account the fact that the ext2 file system represents
+ * it's bitmaps as "little endian" unsigned integers.
+ * Note: this method is not atomic, but ext2 does not need it to be.
+ */
+ int mask;
+ int oldbit;
+ unsigned char* ADDR = (unsigned char*) addr;
+
+ /* Determine the BYTE containing the specified bit
+ * (nr) - important as if we go to a byte there are no
+ * little endian concerns.
+ */
+ ADDR += nr >> 3;
+ mask = 1 << (nr & 0x07); /* Create a mask to the bit within this byte. */
+ oldbit = *ADDR & mask; /* Save the bit's previous value. */
+ *ADDR |= mask; /* Turn the bit on. */
+ return oldbit; /* Return the bit's previous value. */
+}
+
+static __inline__ int ext2_clear_bit(int nr, void* addr)
+{
+ /* This method needs to take into account the fact that the ext2 file system represents
+ * | it's bitmaps as "little endian" unsigned integers.
+ * Note: this method is not atomic, but ext2 does not need it to be.
+ */
+ int mask;
+ int oldbit;
+ unsigned char* ADDR = (unsigned char*) addr;
+
+ /* Determine the BYTE containing the specified bit (nr)
+ * - important as if we go to a byte there are no little endian concerns.
+ */
+ ADDR += nr >> 3;
+ mask = 1 << (nr & 0x07); /* Create a mask to the bit within this byte. */
+ oldbit = *ADDR & mask; /* Save the bit's previous value. */
+ *ADDR = *ADDR & ~mask; /* Turn the bit off. */
+ return oldbit; /* Return the bit's previous value. */
+}
+
+static __inline__ int ext2_test_bit(int nr, __const__ void * addr)
+{
+ /* This method needs to take into account the fact that the ext2 file system represents
+ * | it's bitmaps as "little endian" unsigned integers.
+ * Determine the BYTE containing the specified bit (nr),
+ * then shift to the right the correct number of bits and return that bit's value.
+ */
+ __const__ unsigned char *ADDR = (__const__ unsigned char *) addr;
+ return (ADDR[nr >> 3] >> (nr & 7)) & 1;
+}
+
+/* Returns the bit position of the most significant 1 bit in a WORD. */
+static __inline__ int ext2_ilog2(unsigned int x)
+{
+ int lz;
+
+ asm ("cntlzw %0,%1" : "=r" (lz) : "r" (x));
+ return 31 - lz;
+}
+
+/* ext2_ffz = ext2's Find First Zero.
+ * Determines the bit position of the LEAST significant (rightmost) 0 bit in the specified WORD.
+ * The returned bit position will be zero-based, starting from the right side (31 - 0).
+ */
+static __inline__ int ext2_ffz(unsigned int x)
+{
+ u32 tempRC;
+ /* Change all of x's 1s to 0s and 0s to 1s in x. And insure at least 1 zero exists in the word. */
+ if ((x = ~x) == 0)
+ /* no zero exists anywhere in the 4 byte area. */
+ return 32;
+ /* Calculate the bit position of the least significant '1' bit in x
+ * (since x has been changed this will actually be the least
+ * significant '0' bit in the original x).
+ * Note: (x & -x) gives us a mask that is the LEAST significant
+ * (RIGHT-most) 1-bit of the value in x.
+ */
+ tempRC = ext2_ilog2(x & -x);
+ return tempRC;
+}
+
+static __inline__ u32 ext2_find_next_zero_bit(void* addr, u32 size, u32 offset)
+{
+ /* This method needs to take into account the fact that the ext2 file system represents
+ * | it's bitmaps as "little endian" unsigned integers.
+ */
+ unsigned int *p = ((unsigned int *) addr) + (offset >> 5);
+ unsigned int result = offset & ~31;
+ unsigned int tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= 31;
+ if (offset) {
+ tmp = cpu_to_le32p(p++);
+ tmp |= ~0U >> (32-offset); /* bug or feature ? */
+ if (size < 32)
+ goto found_first;
+ if (tmp != ~0)
+ goto found_middle;
+ size -= 32;
+ result += 32;
+ }
+ while (size >= 32) {
+ if ((tmp = cpu_to_le32p(p++)) != ~0)
+ goto found_middle;
+ result += 32;
+ size -= 32;
+ }
+ if (!size)
+ return result;
+ tmp = cpu_to_le32p(p);
+found_first:
+ tmp |= ~0 << size;
+ if (tmp == ~0) /* Are any bits zero? */
+ return result + size; /* Nope. */
+found_middle:
+ return result + ext2_ffz(tmp);
+}
+
+#define ext2_find_first_zero_bit(addr, size) ext2_find_next_zero_bit((addr), (size), 0)
+
+#endif /* __KERNEL__ */
+#endif /* _PPC64_BITOPS_H */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)